![JAR search and dependency download from the Maven repository](/logo.png)
org.apache.juneau.config.Entry Maven / Gradle / Ivy
// ***************************************************************************************************************************
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
// * to you 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.apache.juneau.config;
import static org.apache.juneau.internal.CollectionUtils.*;
import static org.apache.juneau.BinaryFormat.*;
import static org.apache.juneau.common.internal.StringUtils.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.function.*;
import org.apache.juneau.*;
import org.apache.juneau.collections.*;
import org.apache.juneau.common.internal.*;
import org.apache.juneau.config.internal.*;
import org.apache.juneau.json.*;
import org.apache.juneau.parser.*;
/**
* A single entry in a {@link Config} file.
*/
public class Entry {
private final ConfigMapEntry configEntry;
private final Config config;
private final String value;
/**
* Constructor.
*
* @param config The config that this entry belongs to.
* @param configMap The map that this belongs to.
* @param sectionName The section name of this entry.
* @param entryName The name of this entry.
*/
protected Entry(Config config, ConfigMap configMap, String sectionName, String entryName) {
this.configEntry = configMap.getEntry(sectionName, entryName);
this.config = config;
this.value = configEntry == null ? null : config.removeMods(configEntry.getModifiers(), configEntry.getValue());
}
//-----------------------------------------------------------------------------------------------------------------
// Value retrievers
//-----------------------------------------------------------------------------------------------------------------
/**
* Returns true if this entry exists in the config.
*
* @return true if this entry exists in the config.
*/
public boolean isPresent() {
return ! isNull();
}
/**
* Returns true if this entry exists in the config and is not empty.
*
* @return true if this entry exists in the config and is not empty.
*/
public boolean isNotEmpty() {
return ! isEmpty();
}
/**
* Returns this entry as a string.
*
* @return true if this entry exists in the config and is not empty.
* @throws NullPointerException if value was null .
*/
public String get() {
if (isNull())
throw new NullPointerException("Value was null");
return toString();
}
/**
* Returns this entry converted to the specified type or returns the default value.
*
*
* This is equivalent to calling as(def .getClass()).orElse(def ) but is simpler and
* avoids the creation of an {@link Optional} object.
*
* @param def The default value to return if value does not exist.
* @return This entry converted to the specified type or returns the default value.
*/
public String orElse(String def) {
return isNull() ? def : get();
}
/**
* Returns this entry converted to the specified type or returns the default value.
*
*
* This is equivalent to calling as(def .getClass()).orElse(def ) but is simpler and
* avoids the creation of an {@link Optional} object.
*
* @param def The default value to return if value does not exist.
* @return This entry converted to the specified type or returns the default value.
*/
public String orElseGet(Supplier def) {
return isNull() ? def.get() : get();
}
/**
* Returns this entry converted to the specified type.
*
* @param The type to convert the value to.
* @param type The type to convert the value to.
* @return This entry converted to the specified type.
*/
public Optional as(Class type) {
return as((Type)type);
}
/**
* Returns this entry converted to the specified value.
*
*
* The type can be a simple type (e.g. beans, strings, numbers) or parameterized type (collections/maps).
*
*
Examples:
*
* Config config = Config.create ().name("MyConfig.cfg" ).build();
*
* // Parse into a linked-list of strings.
* List list = config .get("MySection/myListOfStrings" ).to(LinkedList.class , String.class );
*
* // Parse into a linked-list of beans.
* List list = config .get("MySection/myListOfBeans" ).to(LinkedList.class , MyBean.class );
*
* // Parse into a linked-list of linked-lists of strings.
* List list = config .get("MySection/my2dListOfStrings" ).to(LinkedList.class ,
* LinkedList.class , String.class );
*
* // Parse into a map of string keys/values.
* Map map = config .get("MySection/myMap" ).to(TreeMap.class , String.class ,
* String.class );
*
* // Parse into a map containing string keys and values of lists containing beans.
* Map map = config .get("MySection/myMapOfListsOfBeans" ).to(TreeMap.class , String.class ,
* List.class , MyBean.class );
*
*
*
* Collection classes are assumed to be followed by zero or one objects indicating the element type.
*
*
* Map classes are assumed to be followed by zero or two meta objects indicating the key and value
* types.
*
*
* The array can be arbitrarily long to indicate arbitrarily complex data structures.
*
*
Notes:
* -
* Use the {@link #as(Class)} method instead if you don't need a parameterized map/collection.
*
*
* @param The object type to create.
* @param type
* The object type to create.
*
Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
* @param args
* The type arguments of the class if it's a collection or map.
*
Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
*
Ignored if the main type is not a map or collection.
* @return The value, or {@link Optional#empty()} if the section or key does not exist.
*/
public Optional as(Type type, Type...args) {
return as(config.parser, type, args);
}
/**
* Same as {@link #as(Type, Type...)} but specifies the parser to use to parse the entry.
*
* @param The object type to create.
* @param parser
* The parser to use to parse the entry.
* @param type
* The object type to create.
*
Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
* @param args
* The type arguments of the class if it's a collection or map.
*
Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
*
Ignored if the main type is not a map or collection.
* @return The value, or {@link Optional#empty()} if the section or key does not exist.
*/
@SuppressWarnings("unchecked")
public Optional as(Parser parser, Type type, Type...args) {
if (isNull())
return empty();
try {
String v = toString();
if (type == String.class) return (Optional)asString();
if (type == String[].class) return (Optional)asStringArray();
if (type == byte[].class) return (Optional)asBytes();
if (type == int.class || type == int.class || type == Integer.class) return (Optional)asInteger();
if (type == long.class || type == Long.class) return (Optional)asLong();
if (type == JsonMap.class) return (Optional)asMap();
if (type == JsonList.class) return (Optional)asList();
if (isEmpty()) return empty();
if (isSimpleType(type)) return optional((T)config.beanSession.convertToType(v, (Class>)type));
if (parser instanceof JsonParser) {
char s1 = firstNonWhitespaceChar(v);
if (isArray(type) && s1 != '[')
v = '[' + v + ']';
else if (s1 != '[' && s1 != '{' && ! "null".equals(v))
v = '\'' + v + '\'';
}
return optional(parser.parse(v, type, args));
} catch (ParseException e) {
throw new BeanRuntimeException(e, null, "Value could not be parsed.");
}
}
/**
* Returns this entry converted to the specified type.
*
* @param The type to convert the value to.
* @param parser The parser to use to parse the entry value.
* @param type The type to convert the value to.
* @return This entry converted to the specified type, or {@link Optional#empty()} if the entry does not exist.
*/
public Optional as(Parser parser, Class type) {
return as(parser, (Type)type);
}
/**
* Returns this entry as a string.
*
* @return This entry as a string, or null if the entry does not exist.
*/
@Override
public String toString() {
return isPresent() ? config.varSession.resolve(value) : null;
}
/**
* Returns this entry as a string.
*
* @return This entry as a string, or {@link Optional#empty()} if the entry does not exist.
*/
public Optional asString() {
return optional(isPresent() ? config.varSession.resolve(value) : null);
}
/**
* Returns this entry as a string array.
*
*
* If the value exists, splits the value on commas and returns the values as trimmed strings.
*
* @return This entry as a string array, or {@link Optional#empty()} if the entry does not exist.
*/
public Optional asStringArray() {
if (! isPresent())
return empty();
String v = toString();
char s1 = firstNonWhitespaceChar(v), s2 = lastNonWhitespaceChar(v);
if (s1 == '[' && s2 == ']' && config.parser instanceof JsonParser) {
try {
return optional(config.parser.parse(v, String[].class));
} catch (ParseException e) {
throw new BeanRuntimeException(e);
}
}
return optional(split(v));
}
/**
* Returns this entry as an integer.
*
*
* "K" , "M" , and "G" can be used to identify kilo, mega, and giga in base 2.
*
"k" , "m" , and "g" can be used to identify kilo, mega, and giga in base 10.
*
*
Example:
*
* -
*
"100K" -> 1024000
* -
*
"100M" -> 104857600
* -
*
"100k" -> 1000000
* -
*
"100m" -> 100000000
*
*
*
* Uses {@link Integer#decode(String)} underneath, so any of the following integer formats are supported:
*
* "0x..."
* "0X..."
* "#..."
* "0..."
*
*
* @return The value, or {@link Optional#empty()} if the value does not exist or the value is empty.
*/
public Optional asInteger() {
return optional(isEmpty() ? null : parseIntWithSuffix(toString()));
}
/**
* Returns this entry as a parsed boolean.
*
*
* Uses {@link Boolean#parseBoolean(String)} to parse value.
*
* @return The value, or {@link Optional#empty()} if the value does not exist or the value is empty.
*/
public Optional asBoolean() {
return optional(isEmpty() ? null : Boolean.parseBoolean(toString()));
}
/**
* Returns this entry as a long.
*
*
* "K" , "M" , "G" , "T" , and "P" can be used to identify kilo, mega, giga, tera, and penta in base 2.
*
"k" , "m" , "g" , "t" , and "p" can be used to identify kilo, mega, giga, tera, and p in base 10.
*
*
Example:
*
* -
*
"100K" -> 1024000
* -
*
"100M" -> 104857600
* -
*
"100k" -> 1000000
* -
*
"100m" -> 100000000
*
*
*
* Uses {@link Long#decode(String)} underneath, so any of the following integer formats are supported:
*
* "0x..."
* "0X..."
* "#..."
* "0..."
*
*
* @return The value, or {@link Optional#empty()} if the value does not exist or the value is empty.
*/
public Optional asLong() {
return optional(isEmpty() ? null : parseLongWithSuffix(toString()));
}
/**
* Returns this entry as a double.
*
*
* Uses {@link Double#valueOf(String)} underneath, so any of the following number formats are supported:
*
* "0x..."
* "0X..."
* "#..."
* "0..."
*
*
* @return The value, or {@link Optional#empty()} if the value does not exist or the value is empty.
*/
public Optional asDouble() {
return optional(isEmpty() ? null : Double.valueOf(toString()));
}
/**
* Returns this entry as a float.
*
*
* Uses {@link Float#valueOf(String)} underneath, so any of the following number formats are supported:
*
* "0x..."
* "0X..."
* "#..."
* "0..."
*
*
* @return The value, or {@link Optional#empty()} if the value does not exist or the value is empty.
*/
public Optional asFloat() {
return optional(isEmpty() ? null : Float.valueOf(toString()));
}
/**
* Returns this entry as a byte array.
*
*
* Byte arrays are stored as encoded strings, typically BASE64, but dependent on the {@link Config.Builder#binaryFormat(BinaryFormat)} setting.
*
* @return The value, or {@link Optional#empty()} if the section or key does not exist.
*/
public Optional asBytes() {
if (isNull())
return empty();
String s = toString();
if (s.indexOf('\n') != -1)
s = s.replaceAll("\n", "");
try {
if (config.binaryFormat == HEX)
return optional(fromHex(s));
if (config.binaryFormat == SPACED_HEX)
return optional(fromSpacedHex(s));
return optional(base64Decode(s));
} catch (Exception e) {
throw new BeanRuntimeException(e, null, "Value could not be converted to a byte array.");
}
}
/**
* Returns this entry as a parsed map.
*
*
* Uses the parser registered on the {@link Config} to parse the entry.
*
*
* If the parser is a JSON parser, the starting/trailing "{" /"}" in the value are optional.
*
* @return The value, or {@link Optional#empty()} if the section or key does not exist.
* @throws ParseException If value could not be parsed.
*/
public Optional asMap() throws ParseException {
return asMap(config.parser);
}
/**
* Returns this entry as a parsed map.
*
*
* If the parser is a JSON parser, the starting/trailing "{" /"}" in the value are optional.
*
* @param parser The parser to use to parse the value, or {@link Optional#empty()} to use the parser defined on the config.
* @return The value, or null if the section or key does not exist.
* @throws ParseException If value could not be parsed.
*/
public Optional asMap(Parser parser) throws ParseException {
if (isNull())
return empty();
if (parser == null)
parser = config.parser;
String s = toString();
if (parser instanceof JsonParser) {
char s1 = firstNonWhitespaceChar(s);
if (s1 != '{' && ! "null".equals(s))
s = '{' + s + '}';
}
return optional(JsonMap.ofText(s, parser));
}
/**
* Returns this entry as a parsed list.
*
*
* Uses the parser registered on the {@link Config} to parse the entry.
*
*
* If the parser is a JSON parser, the starting/trailing "[" /"]" in the value are optional.
*
* @return The value, or {@link Optional#empty()} if the section or key does not exist.
* @throws ParseException If value could not be parsed.
*/
public Optional asList() throws ParseException {
return asList(config.parser);
}
/**
* Returns this entry as a parsed list.
*
*
* If the parser is a JSON parser, the starting/trailing "[" /"]" in the value are optional.
*
* @param parser The parser to use to parse the value, or {@link Optional#empty()} to use the parser defined on the config.
* @return The value, or {@link Optional#empty()} if the section or key does not exist.
* @throws ParseException If value could not be parsed.
*/
public Optional asList(Parser parser) throws ParseException {
if (isNull())
return empty();
if (parser == null)
parser = config.parser;
String s = toString();
if (parser instanceof JsonParser) {
char s1 = firstNonWhitespaceChar(s);
if (s1 != '[' && ! "null".equals(s))
s = '[' + s + ']';
}
return optional(JsonList.ofText(s, parser));
}
//-----------------------------------------------------------------------------------------------------------------
// Metadata retrievers
//-----------------------------------------------------------------------------------------------------------------
/**
* Returns the name of this entry.
*
* @return The name of this entry.
*/
public String getKey() {
return configEntry.getKey();
}
/**
* Returns the raw value of this entry.
*
* @return The raw value of this entry.
*/
public String getValue() {
return configEntry.getValue();
}
/**
* Returns the same-line comment of this entry.
*
* @return The same-line comment of this entry.
*/
public String getComment() {
return configEntry.getComment();
}
/**
* Returns the pre-lines of this entry.
*
* @return The pre-lines of this entry as an unmodifiable list.
*/
public List getPreLines() {
return configEntry.getPreLines();
}
/**
* Returns the modifiers for this entry.
*
* @return The modifiers for this entry, or null if it has no modifiers.
*/
public String getModifiers() {
return configEntry.getModifiers();
}
//-----------------------------------------------------------------------------------------------------------------
// Helper methods
//-----------------------------------------------------------------------------------------------------------------
private boolean isEmpty() {
return StringUtils.isEmpty(value);
}
private boolean isNull() {
return value == null;
}
private boolean isArray(Type t) {
if (! (t instanceof Class))
return false;
Class> c = (Class>)t;
return (c.isArray());
}
private boolean isSimpleType(Type t) {
if (! (t instanceof Class))
return false;
Class> c = (Class>)t;
return (c == String.class || c.isPrimitive() || c.isAssignableFrom(Number.class) || c == Boolean.class || c.isEnum());
}
}