All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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()); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy