![JAR search and dependency download from the Maven repository](/logo.png)
org.apache.juneau.config.Section 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.common.internal.ArgUtils.*;
import static org.apache.juneau.internal.CollectionUtils.*;
import java.beans.*;
import java.lang.reflect.*;
import java.util.*;
import org.apache.juneau.*;
import org.apache.juneau.collections.*;
import org.apache.juneau.config.internal.*;
import org.apache.juneau.parser.*;
/**
* A single section in a config file.
*/
public class Section {
final Config config;
private final ConfigMap configMap;
final String name;
/**
* Constructor.
*
* @param config The config that this entry belongs to.
* @param configMap The map that this belongs to.
* @param name The section name of this entry.
*/
protected Section(Config config, ConfigMap configMap, String name) {
this.config = config;
this.configMap = configMap;
this.name = name;
}
/**
* Returns true if this section exists.
*
* @return true if this section exists.
*/
public boolean isPresent() {
return configMap.hasSection(name);
}
/**
* Shortcut for calling asBean(sectionName, c, false )
.
*
* @param The bean class to create.
* @param c The bean class to create.
* @return A new bean instance, or {@link Optional#empty()} if this section does not exist.
* @throws ParseException Malformed input encountered.
*/
public Optional asBean(Class c) throws ParseException {
return asBean(c, false);
}
/**
* Converts this config file section to the specified bean instance.
*
*
* Key/value pairs in the config file section get copied as bean property values to the specified bean class.
*
*
Example config file
*
* [MyAddress]
* name = John Smith
* street = 123 Main Street
* city = Anywhere
* state = NY
* zip = 12345
*
*
* Example bean
*
* public class Address {
* public String name , street , city ;
* public StateEnum state ;
* public int zip ;
* }
*
*
* Example usage
*
* Config config = Config.create ().name("MyConfig.cfg" ).build();
* Address address = config .getSection("MySection" ).asBean(Address.class ).orElse(null );
*
*
* @param The bean class to create.
* @param c The bean class to create.
* @param ignoreUnknownProperties
* If false , throws a {@link ParseException} if the section contains an entry that isn't a bean property
* name.
* @return A new bean instance, or null if this section doesn't exist.
* @throws ParseException Unknown property was encountered in section.
*/
public Optional asBean(Class c, boolean ignoreUnknownProperties) throws ParseException {
assertArgNotNull("c", c);
if (! isPresent())
return empty();
Set keys = configMap.getKeys(name);
BeanMap bm = config.beanSession.newBeanMap(c);
for (String k : keys) {
BeanPropertyMeta bpm = bm.getPropertyMeta(k);
if (bpm == null) {
if (! ignoreUnknownProperties)
throw new ParseException("Unknown property ''{0}'' encountered in configuration section ''{1}''.", k, name);
} else {
bm.put(k, config.get(name + '/' + k).as(bpm.getClassMeta().getInnerClass()).orElse(null));
}
}
return optional(bm.getBean());
}
/**
* Returns this section as a map.
*
* @return A new {@link JsonMap}, or {@link Optional#empty()} if this section doesn't exist.
*/
public Optional asMap() {
if (! isPresent())
return empty();
Set keys = configMap.getKeys(name);
JsonMap m = new JsonMap();
for (String k : keys)
m.put(k, config.get(name + '/' + k).as(Object.class).orElse(null));
return optional(m);
}
/**
* Wraps this section inside a Java interface so that values in the section can be read and
* write using getters and setters.
*
* Example config file
*
* [MySection]
* string = foo
* int = 123
* enum = ONE
* bean = {foo:'bar',baz:123}
* int3dArray = [[[123,null],null],null]
* bean1d3dListMap = {key:[[[[{foo:'bar',baz:123}]]]]}
*
*
* Example interface
*
* public interface MyConfigInterface {
*
* String getString();
* void setString(String value );
*
* int getInt();
* void setInt(int value );
*
* MyEnum getEnum();
* void setEnum(MyEnum value );
*
* MyBean getBean();
* void setBean(MyBean value );
*
* int [][][] getInt3dArray();
* void setInt3dArray(int [][][] value );
*
* Map<String,List<MyBean[][][]>> getBean1d3dListMap();
* void setBean1d3dListMap(Map<String,List<MyBean[][][]>> value );
* }
*
*
* Example usage
*
* Config config = Config.create ().name("MyConfig.cfg" ).build();
*
* MyConfigInterface ci = config .get("MySection" ).asInterface(MyConfigInterface.class ).orElse(null );
*
* int myInt = ci .getInt();
*
* ci .setBean(new MyBean());
*
* ci .save();
*
*
* Notes:
* - Calls to setters when the configuration is read-only will cause {@link UnsupportedOperationException} to be thrown.
*
*
* @param The proxy interface class.
* @param c The proxy interface class.
* @return The proxy interface.
*/
@SuppressWarnings("unchecked")
public Optional asInterface(final Class c) {
assertArgNotNull("c", c);
if (!c.isInterface())
throw new IllegalArgumentException("Class '" + c.getName() + "' passed to toInterface() is not an interface.");
return optional((T) Proxy.newProxyInstance(c.getClassLoader(), new Class[] { c }, (InvocationHandler) (proxy, method, args) -> {
BeanInfo bi = Introspector.getBeanInfo(c, null);
for (PropertyDescriptor pd : bi.getPropertyDescriptors()) {
Method rm = pd.getReadMethod(), wm = pd.getWriteMethod();
if (method.equals(rm))
return config.get(name + '/' + pd.getName()).as(rm.getGenericReturnType()).orElse(null);
if (method.equals(wm))
return config.set(name + '/' + pd.getName(), args[0]);
}
throw new UnsupportedOperationException("Unsupported interface method. method='" + method + "'");
}));
}
/**
* Copies the entries in this section to the specified bean by calling the public setters on that bean.
*
* @param bean The bean to set the properties on.
* @param ignoreUnknownProperties
* If true , don't throw an {@link IllegalArgumentException} if this section contains a key that doesn't
* correspond to a setter method.
* @return An object map of the changes made to the bean.
* @throws ParseException If parser was not set on this config file or invalid properties were found in the section.
* @throws UnsupportedOperationException If configuration is read only.
*/
public Section writeToBean(Object bean, boolean ignoreUnknownProperties) throws ParseException {
assertArgNotNull("bean", bean);
if (! isPresent()) throw new IllegalArgumentException("Section '"+name+"' not found in configuration.");
Set keys = configMap.getKeys(name);
BeanMap> bm = config.beanSession.toBeanMap(bean);
for (String k : keys) {
BeanPropertyMeta bpm = bm.getPropertyMeta(k);
if (bpm == null) {
if (! ignoreUnknownProperties)
throw new ParseException("Unknown property ''{0}'' encountered in configuration section ''{1}''.", k, name);
} else {
bm.put(k, config.get(name + '/' + k).as(bpm.getClassMeta().getInnerClass()).orElse(null));
}
}
return this;
}
}