2
0
Fork 0
mirror of https://github.com/ethauvin/rife2.git synced 2025-05-01 02:58:12 -07:00

Added support for hierarchical properties and field injection for properties.

This commit is contained in:
Geert Bevin 2023-01-02 10:31:55 -05:00
parent eb872bdf4c
commit 426104ff0b
25 changed files with 2005 additions and 18 deletions

View file

@ -9,6 +9,7 @@ import rife.continuations.ContinuationConfigRuntime;
import rife.continuations.ContinuationContext; import rife.continuations.ContinuationContext;
import rife.continuations.exceptions.*; import rife.continuations.exceptions.*;
import rife.engine.exceptions.*; import rife.engine.exceptions.*;
import rife.ioc.HierarchicalProperties;
import rife.template.Template; import rife.template.Template;
import rife.template.TemplateFactory; import rife.template.TemplateFactory;
import rife.template.exceptions.TemplateException; import rife.template.exceptions.TemplateException;
@ -204,6 +205,64 @@ public class Context {
return routeMatch_.route(); return routeMatch_.route();
} }
public Router router() {
var route = route();
if (route == null) {
return null;
}
return route.router();
}
public HierarchicalProperties properties() {
var router = router();
if (router == null) {
return null;
}
return router.properties();
}
/**
* Retrieve a property from this context's {@code HierarchicalProperties}.
*
* @return the requested property; or {@code null} if it doesn't exist
* @since 1.0
*/
public Object property(String name) {
var properties = properties();
if (null == properties) {
return null;
}
return properties.getValue(name);
}
/**
* Checks for the existence of a property in this context's {@code HierarchicalProperties}.
*
* @return {@code true} if the property exists; or {@code false} otherwise
* @since 1.0
*/
public boolean hasProperty(String name) {
var properties = properties();
if (null == properties) {
return false;
}
return properties.contains(name);
}
/**
* Returns a collection of the names in this context's {@code HierarchicalProperties}.
*
* @return the requested collection of names
* @since 1.0
*/
public Collection<String> propertyNames() {
var properties = properties();
if (null == properties) {
return Collections.emptyList();
}
return properties.getNames();
}
/** /**
* Pauses the execution of the element and creates a new continuation. * Pauses the execution of the element and creates a new continuation.
* <p>The next request will resume exactly at the same location with a * <p>The next request will resume exactly at the same location with a

View file

@ -6,15 +6,13 @@ package rife.engine;
import rife.Version; import rife.Version;
import rife.config.RifeConfig; import rife.config.RifeConfig;
import rife.continuations.*;
import rife.continuations.exceptions.PauseException;
import rife.engine.exceptions.DeferException; import rife.engine.exceptions.DeferException;
import rife.engine.exceptions.RedirectException; import rife.engine.exceptions.RedirectException;
import rife.ioc.HierarchicalProperties;
import rife.template.TemplateFactory; import rife.template.TemplateFactory;
import rife.tools.ExceptionFormattingUtils; import rife.tools.ExceptionFormattingUtils;
import rife.tools.ExceptionUtils; import rife.tools.ExceptionUtils;
import java.io.IOException;
import java.util.logging.Logger; import java.util.logging.Logger;
/** /**
@ -25,6 +23,7 @@ import java.util.logging.Logger;
* @since 1.0 * @since 1.0
*/ */
public class Gate { public class Gate {
private HierarchicalProperties properties_ = null;
private Site site_ = null; private Site site_ = null;
private Throwable initException_ = null; private Throwable initException_ = null;
@ -34,7 +33,10 @@ public class Gate {
* @param site the site that will handle the requests * @param site the site that will handle the requests
* @since 1.0 * @since 1.0
*/ */
public void setup(Site site) { public void setup(HierarchicalProperties properties, Site site) {
var system_properties = new HierarchicalProperties().putAll(System.getProperties());
properties_ = properties.parent(system_properties);
site.properties_.setParent(properties_);
site_ = site; site_ = site;
try { try {

View file

@ -101,12 +101,13 @@ public class RouteClass implements Route {
} }
if (field.isAnnotationPresent(ActiveSite.class) || if (field.isAnnotationPresent(ActiveSite.class) ||
field.isAnnotationPresent(Parameter.class) ||
field.isAnnotationPresent(Header.class) ||
field.isAnnotationPresent(Body.class) || field.isAnnotationPresent(Body.class) ||
field.isAnnotationPresent(PathInfo.class) ||
field.isAnnotationPresent(FileUpload.class) ||
field.isAnnotationPresent(Cookie.class) || field.isAnnotationPresent(Cookie.class) ||
field.isAnnotationPresent(FileUpload.class) ||
field.isAnnotationPresent(Header.class) ||
field.isAnnotationPresent(Parameter.class) ||
field.isAnnotationPresent(PathInfo.class) ||
field.isAnnotationPresent(Property.class) ||
field.isAnnotationPresent(RequestAttribute.class) || field.isAnnotationPresent(RequestAttribute.class) ||
field.isAnnotationPresent(SessionAttribute.class)) { field.isAnnotationPresent(SessionAttribute.class)) {
fields.add(field); fields.add(field);
@ -227,6 +228,22 @@ public class RouteClass implements Route {
} }
field.set(element, value); field.set(element, value);
} }
} else if (field.isAnnotationPresent(Property.class)) {
var properties = context.properties();
var annotation_name = field.getAnnotation(Property.class).value();
if (annotation_name != null && !annotation_name.isEmpty()) {
name = annotation_name;
}
var values = properties.get(name);
if (values != null) {
Object value;
try {
value = Convert.toType(values, type);
} catch (ConversionException e) {
value = Convert.getDefaultValue(type);
}
field.set(element, value);
}
} else if (field.isAnnotationPresent(Header.class) && } else if (field.isAnnotationPresent(Header.class) &&
shouldProcessInFlow(field.getAnnotation(Header.class).flow())) { shouldProcessInFlow(field.getAnnotation(Header.class).flow())) {
var annotation_name = field.getAnnotation(Header.class).value(); var annotation_name = field.getAnnotation(Header.class).value();

View file

@ -79,12 +79,13 @@ public class RouteInstance implements Route {
} }
if (field.isAnnotationPresent(ActiveSite.class) || if (field.isAnnotationPresent(ActiveSite.class) ||
field.isAnnotationPresent(Parameter.class) ||
field.isAnnotationPresent(Header.class) ||
field.isAnnotationPresent(Body.class) || field.isAnnotationPresent(Body.class) ||
field.isAnnotationPresent(PathInfo.class) ||
field.isAnnotationPresent(FileUpload.class) ||
field.isAnnotationPresent(Cookie.class) || field.isAnnotationPresent(Cookie.class) ||
field.isAnnotationPresent(FileUpload.class) ||
field.isAnnotationPresent(Header.class) ||
field.isAnnotationPresent(Parameter.class) ||
field.isAnnotationPresent(PathInfo.class) ||
field.isAnnotationPresent(Property.class) ||
field.isAnnotationPresent(RequestAttribute.class) || field.isAnnotationPresent(RequestAttribute.class) ||
field.isAnnotationPresent(SessionAttribute.class)) { field.isAnnotationPresent(SessionAttribute.class)) {
throw new AnnotatedElementInstanceFieldException(this, element_, field.getName()); throw new AnnotatedElementInstanceFieldException(this, element_, field.getName());

View file

@ -4,11 +4,14 @@
*/ */
package rife.engine; package rife.engine;
import rife.ioc.HierarchicalProperties;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.*; import java.util.*;
public class Router { public class Router {
final HierarchicalProperties properties_ = new HierarchicalProperties();
final List<Route> before_ = new ArrayList<>(); final List<Route> before_ = new ArrayList<>();
final List<Route> after_ = new ArrayList<>(); final List<Route> after_ = new ArrayList<>();
final Map<String, List<Route>> routes_ = new HashMap<>(); final Map<String, List<Route>> routes_ = new HashMap<>();
@ -64,6 +67,7 @@ public class Router {
} }
public final <T extends Router> T group(String path, T router) { public final <T extends Router> T group(String path, T router) {
router.properties_.setParent(this.properties_);
router.parent_ = this; router.parent_ = this;
groups_.add(router); groups_.add(router);
@ -317,6 +321,10 @@ public class Router {
return route; return route;
} }
public HierarchicalProperties properties() {
return properties_;
}
public Site site() { public Site site() {
Router router = this; Router router = this;
while (router.parent_ != null) { while (router.parent_ != null) {

View file

@ -8,11 +8,13 @@ import org.eclipse.jetty.server.*;
import org.eclipse.jetty.server.session.*; import org.eclipse.jetty.server.session.*;
import org.eclipse.jetty.servlet.*; import org.eclipse.jetty.servlet.*;
import rife.config.RifeConfig; import rife.config.RifeConfig;
import rife.ioc.HierarchicalProperties;
import rife.servlet.RifeFilter; import rife.servlet.RifeFilter;
import java.util.EnumSet; import java.util.EnumSet;
public class Server { public class Server {
private final HierarchicalProperties properties_ = new HierarchicalProperties();
private final org.eclipse.jetty.server.Server server_ = new org.eclipse.jetty.server.Server(); private final org.eclipse.jetty.server.Server server_ = new org.eclipse.jetty.server.Server();
private final SessionIdManager sessions_ = new DefaultSessionIdManager(server_); private final SessionIdManager sessions_ = new DefaultSessionIdManager(server_);
private final ServletContextHandler handler_ = new ServletContextHandler(); private final ServletContextHandler handler_ = new ServletContextHandler();
@ -27,6 +29,10 @@ public class Server {
return this; return this;
} }
public HierarchicalProperties properties() {
return properties_;
}
public Server start(Site site) { public Server start(Site site) {
try (var connector = new ServerConnector(server_)) { try (var connector = new ServerConnector(server_)) {
connector.setPort(RifeConfig.server().getPort()); connector.setPort(RifeConfig.server().getPort());
@ -41,7 +47,7 @@ public class Server {
handler_.setSessionHandler(session_handler); handler_.setSessionHandler(session_handler);
var rife_filter = new RifeFilter(); var rife_filter = new RifeFilter();
rife_filter.site(site); rife_filter.site(properties_, site);
var filter_holder = new FilterHolder(rife_filter); var filter_holder = new FilterHolder(rife_filter);
var ctx = new ServletContextHandler(); var ctx = new ServletContextHandler();

View file

@ -0,0 +1,25 @@
/*
* Copyright 2001-2022 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.engine.annotations;
import java.lang.annotation.*;
/**
* Declares a property.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
@Documented
public @interface Property {
/**
* The name of the parameter.
*
* @since 1.0
*/
String value() default "";
}

View file

@ -0,0 +1,676 @@
/*
* Copyright 2001-2022 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.ioc;
import java.util.*;
import rife.ioc.exceptions.IncompatiblePropertyValueTypeException;
import rife.ioc.exceptions.PropertyValueException;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
/**
* This class allows the creation of a hierarchical tree of named {@link
* PropertyValue} instances.
* <p>When a property is looked up in a child
* <code>HierarchicalProperties</code> instance, the lookup will be propagated
* to its parent when it couldn't be found in the child. A single hierarchical
* line is thus considered to be one collection that groups all involved
* <code>HierarchicalProperties</code> instances. Retrieving the names and the
* size will recursively take all the properties of the parents into account
* and return the consolidated result. To offer these features, intelligent
* caching has been implemented to ensure optimal performance.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.0
*/
public class HierarchicalProperties {
private LinkedHashMap<String, PropertyValue> properties_;
private HierarchicalProperties parent_;
private LinkedHashSet<HierarchicalProperties> children_;
private Set<String> cachedNames_;
private Set<String> cachedInjectableNames_;
public HierarchicalProperties() {
}
private HierarchicalProperties(HierarchicalProperties shadow) {
properties_ = shadow.properties_;
}
/**
* Creates a copy of this <code>HierarchicalProperties</code> hierarchy
* until a certain instance is reached.
* <p>
* Each copied instance will share the datastructure in which the
* properties are stored with the original. Creating a shadow is for
* changing the hierarchical structure but maintaining a centralized
* management of the properties.
*
* @param limit the <code>HierarchicalProperties</code> instance that will
* not be part of the shadow copy and interrupt the copying process; or
* <code>null</code> if the entire hierachy should be copied.
* @return the shadow copy of this <code>HierarchicalProperties</code>
* hierarchy
* @since 1.0
*/
public HierarchicalProperties createShadow(HierarchicalProperties limit) {
var result = new HierarchicalProperties(this);
var original = this;
var shadow = result;
while (original.getParent() != null &&
original.getParent() != limit) {
shadow.setParent(new HierarchicalProperties(original.getParent()));
original = original.getParent();
shadow = shadow.getParent();
}
return result;
}
/**
* Retrieves the first parent of this <code>HierarchicalProperties</code>
* hierarchy.
*
* @return the root of this <code>HierarchicalProperties</code>
* hierarchy
* @since 1.0
*/
public HierarchicalProperties getRoot() {
var root = this;
while (root.getParent() != null) {
root = root.getParent();
}
return root;
}
/**
* Retrieves the <code>Map</code> with only the properties that are
* locally present in this <code>HierarchicalProperties</code> instance.
*
* @return the local <code>Map</code> of this
* <code>HierarchicalProperties</code> instance
* @since 1.0
*/
public Map<String, PropertyValue> getLocalMap() {
if (null == properties_) {
return Collections.EMPTY_MAP;
}
return properties_;
}
/**
* Sets the parent of this <code>HierarchicalProperties</code> instance.
*
* @param parent the parent of this instance; or <code>null</code> if this
* instance should be isolated
* @see #getParent
* @since 1.0
*/
public void setParent(HierarchicalProperties parent) {
clearCaches();
if (parent_ != null) {
parent_.removeChild(this);
}
parent_ = parent;
if (parent_ != null) {
parent_.addChild(this);
}
}
/**
* Sets the parent of this <code>HierarchicalProperties</code> instance.
*
* @param parent the parent of this instance; or <code>null</code> if this
* instance should be isolated
* @return this <code>HierarchicalProperties</code> instance
* @see #getParent
* @since 1.0
*/
public HierarchicalProperties parent(HierarchicalProperties parent) {
setParent(parent);
return this;
}
/**
* Retrieves the parent of this <code>HierarchicalProperties</code>
* instance.
*
* @return the parent of this <code>HierarchicalProperties</code>
* instance; or
* <p><code>null</code> if this instance is isolated
* @see #parent
* @since 1.0
*/
public HierarchicalProperties getParent() {
return parent_;
}
/**
* Associates the specified value with the specified name in this
* <code>HierarchicalProperties</code> instance. If it previously
* contained a mapping for this name, the old value is replaced by the
* specified value.
*
* @param name the name that will be associated with the property
* @param value the property value that will be associated with the
* specified name
* @return this <code>HierarchicalProperties</code> instance
* @see #put(String, Object)
* @see #putAll
* @since 1.0
*/
public HierarchicalProperties put(String name, PropertyValue value) {
clearCaches();
if (null == properties_) {
properties_ = new LinkedHashMap<>();
}
properties_.put(name, value);
return this;
}
/**
* Associates the specified fixed object value with the specified name
* in this <code>HierarchicalProperties</code> instance. If it previously
* contained a mapping for this name, the old value is replaced by the
* specified value.
*
* @param name the name that will be associated with the property
* @param value the property value that will be associated with the
* specified name, note that this method will create a {@link PropertyValueObject}
* instance that will contain the value in a fixed manner
* @return this <code>HierarchicalProperties</code> instance
* @see #put(String, PropertyValue)
* @see #putAll
* @since 1.0
*/
public HierarchicalProperties put(String name, Object value) {
put(name, new PropertyValueObject(value));
return this;
}
/**
* Removes the mapping for this name from this
* <code>HierarchicalProperties</code> instance, if it is present.
*
* @param name the name that will be removed
* @return the previously associated value; or
* <p><code>null</code> if the name wasn't found in this
* <code>HierarchicalProperties</code> instance
* @since 1.0
*/
public PropertyValue remove(String name) {
if (null == properties_) {
return null;
}
clearCaches();
return properties_.remove(name);
}
/**
* Copies all the named properties from the specified
* <code>HierarchicalProperties</code> instance to this
* <code>HierarchicalProperties</code> instance. The effect of this call
* is equivalent to that of calling {@link #put} on this
* <code>HierarchicalProperties</code> once for each mapping from the
* specified <code>HierarchicalProperties</code> instance.
*
* @param source the properties that will be stored in this
* <code>HierarchicalProperties</code> instance
* @return this <code>HierarchicalProperties</code> instance
* @see #put
* @since 1.0
*/
public HierarchicalProperties putAll(HierarchicalProperties source) {
clearCaches();
if (source.properties_ != null) {
if (null == properties_) {
properties_ = new LinkedHashMap<>();
}
properties_.putAll(source.properties_);
}
return this;
}
/**
* Copies all the named properties from the specified
* <code>HierarchicalProperties</code> instance to this
* <code>HierarchicalProperties</code> instance, without replacing existing
* properties. The effect of this call
* is equivalent to that of calling {@link #put} on this
* <code>HierarchicalProperties</code> once for each mapping from the
* specified <code>HierarchicalProperties</code> instance that doesn't
* have a key in this instance yet.
*
* @param source the properties that will be stored in this
* <code>HierarchicalProperties</code> instance
* @return this <code>HierarchicalProperties</code> instance
* @see #put
* @since 1.0
*/
public HierarchicalProperties putAllWithoutReplacing(HierarchicalProperties source) {
clearCaches();
if (source.properties_ != null) {
if (null == properties_) {
properties_ = new LinkedHashMap<>();
}
for (var entry : source.properties_.entrySet()) {
if (!properties_.containsKey(entry.getKey())) {
properties_.put(entry.getKey(), entry.getValue());
}
}
}
return this;
}
/**
* Copies all the entries for a <code>Map</code> instance to this
* <code>HierarchicalProperties</code> instance.
*
* @param source the map entries that will be stored in this
* <code>HierarchicalProperties</code> instance
* @return this <code>HierarchicalProperties</code> instance
* @since 1.0
*/
public HierarchicalProperties putAll(Map source) {
if (null == source) {
return this;
}
clearCaches();
if (null == properties_) {
properties_ = new LinkedHashMap<>();
}
for (var entry : ((Set<Map.Entry>) source.entrySet())) {
properties_.put(String.valueOf(entry.getKey()), new PropertyValueObject(entry.getValue()));
}
return this;
}
/**
* Checks the <code>HierarchicalProperties</code> hierarchy for the
* presence of the specified name.
*
* @param name the name whose presence will be checked
* @return <code>true</code> if the name was found; or
* <p><code>false</code> otherwise
* @see #get
* @since 1.0
*/
public boolean contains(String name) {
var current = this;
LinkedHashMap<String, PropertyValue> properties = null;
while (true) {
properties = current.properties_;
if (properties != null) {
if (properties.containsKey(name)) {
return true;
}
}
if (null == current.parent_) {
break;
}
current = current.parent_;
}
return false;
}
/**
* Retrieves the <code>PropertyValue</code> for a specific name from the
* <code>HierarchicalProperties</code> hierarchy.
*
* @param name the name whose associated value will be returned
* @return the associated <code>PropertyValue</code>; or
* <p><code>null</code> if the name could not be found
* @see #contains
* @since 1.0
*/
public PropertyValue get(String name) {
var current = this;
PropertyValue result;
LinkedHashMap<String, PropertyValue> properties = null;
while (true) {
properties = current.properties_;
if (properties != null) {
result = properties.get(name);
if (result != null) {
return result;
}
}
if (null == current.parent_) {
break;
}
current = current.parent_;
}
return null;
}
/**
* Retrieves the value of <code>PropertyValue</code> for a specific name from
* the <code>HierarchicalProperties</code> hierarchy.
*
* @param name the name whose associated value will be returned
* @return the associated <code>PropertyValue</code>; or
* <p><code>null</code> if the name could not be found
* @throws PropertyValueException when an error occurred while retrieving the
* property value
* @see #get
* @see #getValue(String, Object)
* @since 1.0
*/
public Object getValue(String name)
throws PropertyValueException {
return getValue(name, null);
}
/**
* Retrieves the value of <code>PropertyValue</code> for a specific name from
* the <code>HierarchicalProperties</code> hierarchy. If the property couldn't
* be found or if the value was <code>null</code>, the default value will be
* returned.
*
* @param name the name whose associated value will be returned
* @param defaultValue the value that should be used as a fallback
* @return the associated <code>PropertyValue</code>; or
* <p>the <code>defaultValue</code> if the property couldn't be found or if
* the value was <code>null</code>
* @throws PropertyValueException when an error occurred while retrieving the
* property value
* @see #get
* @see #getValue(String)
* @since 1.0
*/
public Object getValue(String name, Object defaultValue)
throws PropertyValueException {
Object result = null;
var property = get(name);
if (property != null) {
result = property.getValue();
}
if (null == result) {
return defaultValue;
}
return result;
}
/**
* Retrieves the string value of <code>PropertyValue</code> for a specific name from
* the <code>HierarchicalProperties</code> hierarchy.
*
* @param name the name whose associated value will be returned
* @return the string value of the retrieved <code>PropertyValue</code>; or
* <p><code>null</code> if the name could not be found
* @throws PropertyValueException when an error occurred while retrieving the
* property value
* @see #get
* @see #getValueString(String, String)
* @see #getValueTyped
* @since 1.0
*/
public String getValueString(String name)
throws PropertyValueException {
return getValueString(name, null);
}
/**
* Retrieves the string value of <code>PropertyValue</code> for a specific name from
* the <code>HierarchicalProperties</code> hierarchy. If the property couldn't
* be found, if the value was <code>null</code> or if the value was empty, the
* default value will be returned.
*
* @param name the name whose associated value will be returned
* @param defaultValue the value that should be used as a fallback
* @return the string value of the retrieved <code>PropertyValue</code>; or
* <p>the <code>defaultValue</code> if the property couldn't be found or if
* the value was <code>null</code> or an empty string
* @throws PropertyValueException when an error occurred while retrieving the
* property value
* @see #get
* @see #getValueString(String)
* @see #getValueTyped
* @since 1.0
*/
public String getValueString(String name, String defaultValue)
throws PropertyValueException {
String result = null;
var property = get(name);
if (property != null) {
result = property.getValueString();
}
if (null == result ||
0 == result.length()) {
return defaultValue;
}
return result;
}
/**
* Retrieves the typed value of <code>PropertyValue</code> for a specific name from
* the <code>HierarchicalProperties</code> hierarchy.
* <p>
* Note that no conversion will occurr, the value is simple verified to be
* assignable to the requested type and then cast to it.
*
* @param name the name whose associated value will be returned
* @param type the class that the value has to be retrieved as
* @return the associated <code>PropertyValue</code> as an instance of the
* provided type; or
* <p><code>null</code> if the name could not be found
* @throws IncompatiblePropertyValueTypeException when the type of the property
* value wasn't compatible with the requested type
* @throws PropertyValueException when an error occurred while retrieving the
* property value
* @see #get
* @see #getValueString
* @see #getValueTyped(String, Class)
* @since 1.0
*/
public <T> T getValueTyped(String name, Class<T> type)
throws PropertyValueException {
return (T) getValueTyped(name, type, null);
}
/**
* Retrieves the typed value of <code>PropertyValue</code> for a specific name from
* the <code>HierarchicalProperties</code> hierarchy.
* <p>
* Note that no conversion will occur, the value is simple verified to be
* assignable to the requested type and then cast to it.
*
* @param name the name whose associated value will be returned
* @param type the class that the value has to be retrieved as
* @param defaultValue the value that should be used as a fallback
* @return the associated <code>PropertyValue</code> as an instance of the
* provided type; or
* <p>the <code>defaultValue</code> if the property couldn't be found or if
* the value was <code>null</code>
* @throws IncompatiblePropertyValueTypeException when the type of the property
* value wasn't compatible with the requested type
* @throws PropertyValueException when an error occurred while retrieving the
* property value
* @see #get
* @see #getValueString
* @see #getValueTyped(String, Class)
* @since 1.0
*/
public <T> T getValueTyped(String name, Class<T> type, T defaultValue)
throws PropertyValueException {
if (null == name ||
null == type ||
0 == name.length()) {
return defaultValue;
}
Object result = null;
var property = get(name);
if (property != null) {
result = property.getValue();
}
if (null == result) {
return defaultValue;
}
if (!type.isAssignableFrom(result.getClass())) {
throw new IncompatiblePropertyValueTypeException(name, type, result.getClass(), null);
}
return (T) result;
}
/**
* Retrieves the number of unique names in the
* <code>HierarchicalProperties</code> hierarchy.
*
* @return the amount of unique names
* @since 1.0
*/
public int size() {
return getNames().size();
}
/**
* Retrieves a <code>Set</code> with the unique names that are present in
* the <code>HierarchicalProperties</code> hierarchy.
*
* @return a collection with the unique names
* @see #getInjectableNames
* @since 1.0
*/
public Collection<String> getNames() {
if (cachedNames_ != null) {
return cachedNames_;
}
var current = this;
Set<String> names = new LinkedHashSet<>();
LinkedHashMap<String, PropertyValue> properties = null;
while (true) {
properties = current.properties_;
if (properties != null) {
names.addAll(properties.keySet());
}
if (null == current.parent_) {
break;
}
current = current.parent_;
}
cachedNames_ = names;
return names;
}
/**
* Retrieves a <code>Set</code> with the unique names that are present in
* the <code>HierarchicalProperties</code> hierarchy and that conform to
* the <a
* href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.8">Java
* rules for valid identifiers</a>. The names in this set are thus usable
* for injection through bean setters.
*
* @return a <code>Set</code> with the unique injectable names
* @see #getNames
* @since 1.0
*/
public Collection<String> getInjectableNames() {
if (cachedInjectableNames_ != null) {
return cachedInjectableNames_;
}
Set<String> injectable_names = new LinkedHashSet<>();
var names = getNames();
for (var name : names) {
var injectable = true;
CharacterIterator it = new StringCharacterIterator(name);
for (var c = it.first(); c != CharacterIterator.DONE; c = it.next()) {
if (!Character.isJavaIdentifierPart(c)) {
injectable = false;
break;
}
}
if (injectable) {
injectable_names.add(name);
}
}
cachedInjectableNames_ = injectable_names;
return injectable_names;
}
private void clearCaches() {
cachedNames_ = null;
cachedInjectableNames_ = null;
if (null == children_) {
return;
}
for (var child : children_) {
child.clearCaches();
}
}
private void addChild(HierarchicalProperties child) {
if (null == children_) {
children_ = new LinkedHashSet<>();
}
children_.add(child);
}
private void removeChild(HierarchicalProperties child) {
if (null == children_) {
return;
}
children_.remove(child);
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright 2001-2022 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.ioc;
import rife.ioc.exceptions.PropertyValueException;
/**
* This interface defines the methods that need to be implemented by classes
* that are able to provide values to properties.
* <p>These classes should make all value retrieval as lazy as possible and
* store only the parameters that are required to obtain the actual data
* dynamically at runtime.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.0
*/
public interface PropertyValue {
/**
* Retrieves a property value.
*
* @return the requested property value; or
* <p><code>null</code> if the property value couldn't be found
* @throws PropertyValueException When something went wrong during the
* retrieval of the property value.
* @since 1.0
*/
Object getValue()
throws PropertyValueException;
/**
* Retrieves a string representation of the property value.
*
* @return the requested string representation of the property value; or
* <p><code>null</code> if the property value couldn't be found
* @throws PropertyValueException When something went wrong during the
* retrieval of the property value.
* @since 1.0
*/
String getValueString()
throws PropertyValueException;
/**
* Indicates whether the value provided by this instance is negligible in
* a textual context. This is for instance applicable to pure whitespace
* values that when trimmed, have zero length. The property construction
* logic will check this state to determine if it has to concatenate
* several property values together as one text result of only use one and
* discard all other negligible ones.
*
* @return <code>true</code> if the value is negligible in a textual
* context; or
* <p><code>false</code> otherwise
* @since 1.0
*/
boolean isNegligible();
/**
* Indicates whether the value is statically fixed an not dynamically
* retrieved at runtime.
*
* @return <code>true</code> if the value is static; or
* <p><code>false</code> if the value is dynamically retrieved at runtime
* @since 1.0
*/
boolean isStatic();
}

View file

@ -0,0 +1,65 @@
/*
* Copyright 2001-2022 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.ioc;
import rife.ioc.exceptions.PropertyValueException;
import java.io.Serial;
import java.util.ArrayList;
/**
* An ordered list of property values.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.0
*/
public class PropertyValueList extends ArrayList<PropertyValue> {
@Serial private static final long serialVersionUID = -7791482346118685259L;
/**
* Interprets the list of property values and make one new property value
* out of it.
*
* @return the new <code>PropertyValue</code> instance
* @since 1.0
*/
public PropertyValue makePropertyValue()
throws PropertyValueException {
// evaluate the current property values series and check if this should be
// interpreted as a text result or as a participant value
PropertyValue result = null;
PropertyValue non_negligible_prop_val = null;
for (var prop_val : this) {
if (!prop_val.isNegligible()) {
if (non_negligible_prop_val != null) {
non_negligible_prop_val = null;
break;
}
non_negligible_prop_val = prop_val;
}
}
if (non_negligible_prop_val != null) {
if (non_negligible_prop_val instanceof PropertyValueObject ||
!non_negligible_prop_val.isStatic()) {
result = non_negligible_prop_val;
} else {
result = new PropertyValueObject(non_negligible_prop_val.getValueString().trim());
}
}
if (null == result) {
var key_text = new StringBuilder();
for (var prop_val : this) {
key_text.append(prop_val.getValueString());
}
result = new PropertyValueObject(key_text.toString().trim());
}
return result;
}
}

View file

@ -0,0 +1,51 @@
/*
* Copyright 2001-2022 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.ioc;
/**
* Holds a single static object property value that doesn't change at runtime.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.0
*/
public class PropertyValueObject implements PropertyValue {
private final Object value_;
/**
* The constructor that stores the static object instance.
*
* @param value the static object instance
* @since 1.0
*/
public PropertyValueObject(Object value) {
value_ = value;
}
public Object getValue() {
return value_;
}
public String getValueString() {
return String.valueOf(value_);
}
public String toString() {
return getValueString();
}
public boolean isNegligible() {
if (null == value_) {
return true;
}
return 0 == String.valueOf(value_).trim().length();
}
public boolean isStatic() {
return true;
}
}

View file

@ -0,0 +1,74 @@
/*
* Copyright 2001-2022 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.ioc;
import rife.ioc.exceptions.PropertyValueException;
import rife.ioc.exceptions.TemplateFactoryUnknownException;
import rife.template.Template;
import rife.template.TemplateFactory;
/**
* Retrieves a property value as template instance of a particular type.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.0
*/
public class PropertyValueTemplate implements PropertyValue {
private final String type_;
private final String name_;
/**
* The constructor that stores the retrieval parameters.
* The template type will be set to "html"
*
* @param name the template name
* @since 1.0
*/
public PropertyValueTemplate(String name) {
this(null, name);
}
/**
* The constructor that stores the retrieval parameters.
*
* @param type the template factory type; if this argument is <code>null</code>
* the template type will be "html"
* @param name the template name
* @since 1.0
*/
public PropertyValueTemplate(String type, String name) {
if (null == type) {
type = "html";
}
type_ = type;
name_ = name;
}
public Template getValue()
throws PropertyValueException {
TemplateFactory factory = TemplateFactory.getFactory(type_);
if (null == factory) {
throw new TemplateFactoryUnknownException(type_);
}
return factory.get(name_);
}
public String getValueString()
throws PropertyValueException {
return getValue().getContent();
}
public String toString() {
return getValueString();
}
public boolean isNegligible() {
return false;
}
public boolean isStatic() {
return false;
}
}

View file

@ -0,0 +1,35 @@
/*
* Copyright 2001-2022 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.ioc.exceptions;
import java.io.Serial;
public class IncompatiblePropertyValueTypeException extends PropertyValueException {
@Serial private static final long serialVersionUID = 6336950082309925343L;
private final String propertyName_;
private final Class expectedType_;
private final Class actualType_;
public IncompatiblePropertyValueTypeException(String propertyName, Class expectedType, Class actualType, Throwable e) {
super("The property '" + propertyName + "' was expected to have the type '" + expectedType.getName() + "', however it's actual type '" + actualType.getName() + "' couldn't be cast to it.", e);
propertyName_ = propertyName;
expectedType_ = expectedType;
actualType_ = actualType;
}
public String getPropertyName() {
return propertyName_;
}
public Class getExpectedType() {
return expectedType_;
}
public Class getActualType() {
return actualType_;
}
}

View file

@ -0,0 +1,27 @@
/*
* Copyright 2001-2022 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.ioc.exceptions;
import java.io.Serial;
public class PropertyValueException extends RuntimeException {
@Serial private static final long serialVersionUID = 1712906301485959756L;
public PropertyValueException() {
super();
}
public PropertyValueException(String message) {
super(message);
}
public PropertyValueException(String message, Throwable cause) {
super(message, cause);
}
public PropertyValueException(Throwable cause) {
super(cause);
}
}

View file

@ -0,0 +1,23 @@
/*
* Copyright 2001-2022 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.ioc.exceptions;
import java.io.Serial;
public class TemplateFactoryUnknownException extends PropertyValueException {
@Serial private static final long serialVersionUID = 7268695167543687153L;
private final String type_;
public TemplateFactoryUnknownException(String type) {
super("The template factory with type '" + type + "' isn't known by the system.");
type_ = type;
}
public String getType() {
return type_;
}
}

View file

@ -0,0 +1,7 @@
<html>
<body>
Provides exception classes for the inversion of control support.
</body>
</html>

View file

@ -0,0 +1,7 @@
<html>
<body>
Provides interfaces and classes for IoC capable properties that resolve and obtain their values at run-time.
</body>
</html>

View file

@ -10,6 +10,7 @@ import jakarta.servlet.http.HttpServletResponse;
import rife.config.RifeConfig; import rife.config.RifeConfig;
import rife.engine.Gate; import rife.engine.Gate;
import rife.engine.Site; import rife.engine.Site;
import rife.ioc.HierarchicalProperties;
import rife.tools.FileUtils; import rife.tools.FileUtils;
import java.io.IOException; import java.io.IOException;
@ -20,8 +21,8 @@ public class RifeFilter implements Filter {
private final Gate gate_ = new Gate(); private final Gate gate_ = new Gate();
private String gateUrl_ = null; private String gateUrl_ = null;
public void site(Site site) { public void site(HierarchicalProperties properties, Site site) {
gate_.setup(site); gate_.setup(properties, site);
} }
@Override @Override
@ -29,11 +30,29 @@ public class RifeFilter implements Filter {
throws ServletException { throws ServletException {
var classloader = getClass().getClassLoader(); var classloader = getClass().getClassLoader();
// set up the properties
var properties = new HierarchicalProperties();
var context = config.getServletContext();
var names = context.getInitParameterNames();
String name;
while (names.hasMoreElements()) {
name = names.nextElement();
properties.put(name, context.getInitParameter(name));
}
names = config.getInitParameterNames();
while (names.hasMoreElements()) {
name = names.nextElement();
properties.put(name, config.getInitParameter(name));
}
// create the site instance
var site_classname = config.getInitParameter(RIFE_SITE_CLASS_NAME); var site_classname = config.getInitParameter(RIFE_SITE_CLASS_NAME);
if (site_classname != null) { if (site_classname != null) {
try { try {
var site_class = classloader.loadClass(site_classname); var site_class = classloader.loadClass(site_classname);
gate_.setup((Site) site_class.getDeclaredConstructor().newInstance()); gate_.setup(properties, (Site) site_class.getDeclaredConstructor().newInstance());
} catch (Throwable e) { } catch (Throwable e) {
throw new ServletException(e); throw new ServletException(e);
} }
@ -51,7 +70,7 @@ public class RifeFilter implements Filter {
// check if the url matches one of the pass-through suffixes // check if the url matches one of the pass-through suffixes
var pass_through = extension != null && var pass_through = extension != null &&
RifeConfig.engine().getPassThroughSuffixes().contains(extension); RifeConfig.engine().getPassThroughSuffixes().contains(extension);
// if not passed through, handle the request // if not passed through, handle the request
if (!pass_through) { if (!pass_through) {

View file

@ -8,6 +8,7 @@ import jakarta.servlet.http.Cookie;
import rife.engine.Gate; import rife.engine.Gate;
import rife.engine.Site; import rife.engine.Site;
import rife.engine.exceptions.EngineException; import rife.engine.exceptions.EngineException;
import rife.ioc.HierarchicalProperties;
import rife.tools.ArrayUtils; import rife.tools.ArrayUtils;
import rife.tools.StringUtils; import rife.tools.StringUtils;
@ -31,6 +32,7 @@ public class MockConversation {
private Gate gate_ = null; private Gate gate_ = null;
private final HierarchicalProperties properties_ = new HierarchicalProperties();
private final HashMap<String, MockCookie> cookies_ = new HashMap<>(); private final HashMap<String, MockCookie> cookies_ = new HashMap<>();
private final HashMap<String, MockSession> sessions_ = new HashMap<>(); private final HashMap<String, MockSession> sessions_ = new HashMap<>();
private String scheme_ = "http"; private String scheme_ = "http";
@ -48,7 +50,8 @@ public class MockConversation {
public MockConversation(Site site) public MockConversation(Site site)
throws EngineException { throws EngineException {
gate_ = new Gate(); gate_ = new Gate();
gate_.setup(site);
gate_.setup(properties_, site);
} }
/** /**
@ -292,6 +295,17 @@ public class MockConversation {
return this; return this;
} }
/**
* Returns the properties uses by this conversation.
*
* @return the instance of <code>HierarchicalProperties</code> that is used
* by this conversation
* @since 1.0
*/
public HierarchicalProperties properties() {
return properties_;
}
/** /**
* Checks whether a cookie is present. * Checks whether a cookie is present.
* *

View file

@ -25,6 +25,9 @@ public class AnnotationInSite extends Site {
@RequestAttribute int intRequestAttribute = -7; @RequestAttribute int intRequestAttribute = -7;
@SessionAttribute int intSessionAttribute = -9; @SessionAttribute int intSessionAttribute = -9;
@Header("header2") String stringHeader2 = "defaultHeader2"; @Header("header2") String stringHeader2 = "defaultHeader2";
@Property String prop1 = "defaultProp1";
@Property String prop2 = "defaultProp2";
@Property("prop1") String prop3 = "defaultProp3";
public void process(Context c) public void process(Context c)
throws Exception { throws Exception {
@ -66,6 +69,10 @@ public class AnnotationInSite extends Site {
@Header int intHeader = -11; @Header int intHeader = -11;
@Header("header3") int intHeader2 = -12; @Header("header3") int intHeader2 = -12;
@Property String prop1 = "defaultProp1";
@Property String prop2 = "defaultProp2";
@Property("prop1") String prop3 = "defaultProp3";
public void process(Context c) public void process(Context c)
throws Exception { throws Exception {
super.process(c); super.process(c);
@ -133,10 +140,15 @@ public class AnnotationInSite extends Site {
c.print(intHeader + "\n"); c.print(intHeader + "\n");
c.print(stringHeader2 + "\n"); c.print(stringHeader2 + "\n");
c.print(intHeader2 + "\n"); c.print(intHeader2 + "\n");
c.print(prop1 + "\n");
c.print(prop2 + "\n");
c.print(prop3 + "\n");
} }
} }
public void setup() { public void setup() {
properties().put("prop1", "propval1");
before(c -> { before(c -> {
if (c.parameterBoolean("generate")) { if (c.parameterBoolean("generate")) {
c.setAttribute("stringRequestAttribute", "value9"); c.setAttribute("stringRequestAttribute", "value9");

View file

@ -54,6 +54,9 @@ public class TestAnnotations {
-11 -11
defaultHeader2 defaultHeader2
-12 -12
propval1
defaultProp2
propval1
""", webClient.getPage(new WebRequest(new URL("http://localhost:8181/get"), HttpMethod.GET)).getWebResponse().getContentAsString()); """, webClient.getPage(new WebRequest(new URL("http://localhost:8181/get"), HttpMethod.GET)).getWebResponse().getContentAsString());
} }
} }
@ -110,6 +113,9 @@ public class TestAnnotations {
18 18
value19 value19
20 20
propval1
defaultProp2
propval1
""", webClient.getPage(request).getWebResponse().getContentAsString()); """, webClient.getPage(request).getWebResponse().getContentAsString());
} }
} }
@ -155,6 +161,9 @@ public class TestAnnotations {
-11 -11
defaultHeader2 defaultHeader2
-12 -12
propval1
defaultProp2
propval1
""", webClient.getPage(request).getWebResponse().getContentAsString()); """, webClient.getPage(request).getWebResponse().getContentAsString());
request.setRequestBody("836"); request.setRequestBody("836");
@ -189,6 +198,9 @@ public class TestAnnotations {
-11 -11
defaultHeader2 defaultHeader2
-12 -12
propval1
defaultProp2
propval1
""", webClient.getPage(request).getWebResponse().getContentAsString()); """, webClient.getPage(request).getWebResponse().getContentAsString());
} }
} }
@ -265,6 +277,9 @@ public class TestAnnotations {
-11 -11
defaultHeader2 defaultHeader2
-12 -12
propval1
defaultProp2
propval1
""", page.getWebResponse().getContentAsString()); """, page.getWebResponse().getContentAsString());
} }
} }

View file

@ -0,0 +1,603 @@
/*
* Copyright 2001-2022 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.ioc;
import org.junit.jupiter.api.Test;
import rife.ioc.exceptions.IncompatiblePropertyValueTypeException;
import rife.ioc.exceptions.PropertyValueException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
public class TestHierarchicalProperties {
@Test
void testInstantiation() {
var properties = new HierarchicalProperties();
assertNotNull(properties);
assertEquals(0, properties.size());
assertNotNull(properties.getNames());
assertEquals(0, properties.getNames().size());
assertNotNull(properties.getInjectableNames());
assertEquals(0, properties.getInjectableNames().size());
assertNotNull(properties.getLocalMap());
assertEquals(0, properties.getLocalMap().size());
}
@Test
void testSingleInstance() {
Iterator<String> names_it;
PropertyValue property1 = new PropertyValueObject("value1");
PropertyValue property2 = new PropertyValueObject("value2");
PropertyValue property3 = new PropertyValueObject("value3");
PropertyValue property4 = new PropertyValueObject("value4");
PropertyValue property5 = new PropertyValueObject("value5");
var properties = new HierarchicalProperties();
assertSame(properties, properties.put("name1", property1));
assertSame(properties, properties.put("name2", property2));
assertSame(properties, properties.put("non.identifier.name3", property3));
assertEquals(3, properties.size());
assertNotNull(properties.getLocalMap());
assertEquals(3, properties.getLocalMap().size());
assertTrue(properties.contains("name1"));
assertTrue(properties.contains("name2"));
assertTrue(properties.contains("non.identifier.name3"));
assertSame(property1, properties.get("name1"));
assertSame(property2, properties.get("name2"));
assertSame(property3, properties.get("non.identifier.name3"));
assertEquals("value1", properties.getValue("name1"));
assertEquals("value2", properties.getValue("name2"));
assertEquals("value3", properties.getValue("non.identifier.name3"));
assertEquals("value1", properties.getValueString("name1"));
assertEquals("value2", properties.getValueString("name2"));
assertEquals("value3", properties.getValueString("non.identifier.name3"));
names_it = properties.getNames().iterator();
assertEquals("name1", names_it.next());
assertEquals("name2", names_it.next());
assertEquals("non.identifier.name3", names_it.next());
assertFalse(names_it.hasNext());
names_it = properties.getInjectableNames().iterator();
assertEquals("name1", names_it.next());
assertEquals("name2", names_it.next());
assertFalse(names_it.hasNext());
assertSame(property2, properties.remove("name2"));
assertEquals(2, properties.size());
assertTrue(properties.contains("name1"));
assertFalse(properties.contains("name2"));
assertTrue(properties.contains("non.identifier.name3"));
names_it = properties.getNames().iterator();
assertEquals("name1", names_it.next());
assertEquals("non.identifier.name3", names_it.next());
assertFalse(names_it.hasNext());
names_it = properties.getInjectableNames().iterator();
assertEquals("name1", names_it.next());
assertFalse(names_it.hasNext());
var properties_alternative = new HierarchicalProperties();
assertSame(properties_alternative, properties_alternative.put("name4", property4));
assertSame(properties_alternative, properties_alternative.put("non.identifier.name5", property5));
assertEquals(2, properties_alternative.size());
assertSame(properties, properties.putAll(properties_alternative));
assertEquals(4, properties.size());
assertTrue(properties.contains("name1"));
assertTrue(properties.contains("non.identifier.name3"));
assertTrue(properties.contains("name4"));
assertTrue(properties.contains("non.identifier.name5"));
assertSame(property1, properties.get("name1"));
assertSame(property3, properties.get("non.identifier.name3"));
assertSame(property4, properties.get("name4"));
assertSame(property5, properties.get("non.identifier.name5"));
assertEquals("value1", properties.getValue("name1"));
assertNull(properties.getValue("name2"));
var default_value = 34;
assertSame(default_value, properties.getValue("name2", default_value));
assertEquals("value3", properties.getValue("non.identifier.name3"));
assertEquals("value4", properties.getValue("name4"));
assertEquals("value5", properties.getValue("non.identifier.name5"));
assertEquals("value1", properties.getValueString("name1"));
assertNull(properties.getValueString("name2"));
assertEquals("somevalue", properties.getValueString("name2", "somevalue"));
assertEquals("value3", properties.getValueString("non.identifier.name3"));
assertEquals("value4", properties.getValueString("name4"));
assertEquals("value5", properties.getValueString("non.identifier.name5"));
names_it = properties.getNames().iterator();
assertEquals("name1", names_it.next());
assertEquals("non.identifier.name3", names_it.next());
assertEquals("name4", names_it.next());
assertEquals("non.identifier.name5", names_it.next());
assertFalse(names_it.hasNext());
names_it = properties.getInjectableNames().iterator();
assertEquals("name1", names_it.next());
assertEquals("name4", names_it.next());
assertFalse(names_it.hasNext());
}
@Test
void testHierarchy() {
/*
* This is the hierarchy that's being built.
*
* grandparent
* -----------
* name1 (property1d)
* name2 (property2d)
* name2_grandparent (property2d)
* non.identifier.name3 (property3d)
* |
* ______________|___________________________
* / \
* / \
* parent1 parent2
* ------- -------
* name1 (property1c) name2 (property2c)
* name2_parent (property2c) non.identifier.name3_parent (property3c)
* | |
* ___________|___________________ |
* / \ |
* / \ |
* child1 child2 child3
* ------ ------ ------
* name1 (property1) name1 (property1b) non.identifier.name3 (property3b)
* name2 (property2) name2 (property2b)
* non.identifier.name3 (property3)
*/
Iterator<String> names_it;
PropertyValue property1 = new PropertyValueObject("value1");
PropertyValue property2 = new PropertyValueObject("value2");
PropertyValue property3 = new PropertyValueObject("value3");
PropertyValue property1b = new PropertyValueObject("value1b");
PropertyValue property2b = new PropertyValueObject("value2b");
PropertyValue property3b = new PropertyValueObject("value3b");
PropertyValue property1c = new PropertyValueObject("value1c");
PropertyValue property2c = new PropertyValueObject("value2c");
PropertyValue property3c = new PropertyValueObject("value3c");
PropertyValue property1d = new PropertyValueObject("value1d");
PropertyValue property2d = new PropertyValueObject("value2d");
PropertyValue property3d = new PropertyValueObject("value3d");
var properties_child1 = new HierarchicalProperties();
assertSame(properties_child1, properties_child1.put("name1", property1));
assertSame(properties_child1, properties_child1.put("name2", property2));
assertSame(properties_child1, properties_child1.put("non.identifier.name3", property3));
assertEquals(3, properties_child1.size());
assertSame(property1, properties_child1.get("name1"));
assertSame(property2, properties_child1.get("name2"));
assertSame(property3, properties_child1.get("non.identifier.name3"));
names_it = properties_child1.getNames().iterator();
assertEquals("name1", names_it.next());
assertEquals("name2", names_it.next());
assertEquals("non.identifier.name3", names_it.next());
assertFalse(names_it.hasNext());
names_it = properties_child1.getInjectableNames().iterator();
assertEquals("name1", names_it.next());
assertEquals("name2", names_it.next());
assertFalse(names_it.hasNext());
var properties_child2 = new HierarchicalProperties();
assertSame(properties_child2, properties_child2.put("name1", property1b));
assertSame(properties_child2, properties_child2.put("name2", property2b));
assertEquals(2, properties_child2.size());
assertSame(property1b, properties_child2.get("name1"));
assertSame(property2b, properties_child2.get("name2"));
names_it = properties_child2.getNames().iterator();
assertEquals("name1", names_it.next());
assertEquals("name2", names_it.next());
assertFalse(names_it.hasNext());
names_it = properties_child2.getInjectableNames().iterator();
assertEquals("name1", names_it.next());
assertEquals("name2", names_it.next());
assertFalse(names_it.hasNext());
var properties_child3 = new HierarchicalProperties();
assertSame(properties_child3, properties_child3.put("non.identifier.name3", property3b));
assertEquals(1, properties_child3.size());
assertSame(property3b, properties_child3.get("non.identifier.name3"));
names_it = properties_child3.getNames().iterator();
assertEquals("non.identifier.name3", names_it.next());
assertFalse(names_it.hasNext());
names_it = properties_child3.getInjectableNames().iterator();
assertFalse(names_it.hasNext());
var properties_parent1 = new HierarchicalProperties();
assertSame(properties_parent1, properties_parent1.put("name1", property1c));
assertSame(properties_parent1, properties_parent1.put("name2_parent", property2c));
assertEquals(2, properties_parent1.size());
assertSame(property1c, properties_parent1.get("name1"));
assertSame(property2c, properties_parent1.get("name2_parent"));
names_it = properties_parent1.getNames().iterator();
assertEquals("name1", names_it.next());
assertEquals("name2_parent", names_it.next());
assertFalse(names_it.hasNext());
names_it = properties_parent1.getInjectableNames().iterator();
assertEquals("name1", names_it.next());
assertEquals("name2_parent", names_it.next());
assertFalse(names_it.hasNext());
var properties_parent2 = new HierarchicalProperties();
assertSame(properties_parent2, properties_parent2.put("name2", property2c));
assertSame(properties_parent2, properties_parent2.put("non.identifier.name3_parent", property3c));
assertEquals(2, properties_parent2.size());
assertSame(property2c, properties_parent2.get("name2"));
assertSame(property3c, properties_parent2.get("non.identifier.name3_parent"));
names_it = properties_parent2.getNames().iterator();
assertEquals("name2", names_it.next());
assertEquals("non.identifier.name3_parent", names_it.next());
assertFalse(names_it.hasNext());
names_it = properties_parent2.getInjectableNames().iterator();
assertEquals("name2", names_it.next());
assertFalse(names_it.hasNext());
var properties_grandparent = new HierarchicalProperties();
assertSame(properties_grandparent, properties_grandparent.put("name1", property1d));
assertSame(properties_grandparent, properties_grandparent.put("name2", property2d));
assertSame(properties_grandparent, properties_grandparent.put("name2_grandparent", property2d));
assertSame(properties_grandparent, properties_grandparent.put("non.identifier.name3", property3d));
assertEquals(4, properties_grandparent.size());
assertSame(property1d, properties_grandparent.get("name1"));
assertSame(property2d, properties_grandparent.get("name2"));
assertSame(property2d, properties_grandparent.get("name2_grandparent"));
assertSame(property3d, properties_grandparent.get("non.identifier.name3"));
names_it = properties_grandparent.getNames().iterator();
assertEquals("name1", names_it.next());
assertEquals("name2", names_it.next());
assertEquals("name2_grandparent", names_it.next());
assertEquals("non.identifier.name3", names_it.next());
assertFalse(names_it.hasNext());
names_it = properties_grandparent.getInjectableNames().iterator();
assertEquals("name1", names_it.next());
assertEquals("name2", names_it.next());
assertEquals("name2_grandparent", names_it.next());
assertFalse(names_it.hasNext());
// set the first level parents
properties_child1.parent(properties_parent1);
assertSame(properties_parent1, properties_child1.getRoot());
assertEquals(4, properties_child1.size());
assertSame(property1, properties_child1.get("name1"));
assertSame(property2, properties_child1.get("name2"));
assertSame(property2c, properties_child1.get("name2_parent"));
assertSame(property3, properties_child1.get("non.identifier.name3"));
names_it = properties_child1.getNames().iterator();
assertEquals("name1", names_it.next());
assertEquals("name2", names_it.next());
assertEquals("non.identifier.name3", names_it.next());
assertEquals("name2_parent", names_it.next());
assertFalse(names_it.hasNext());
names_it = properties_child1.getInjectableNames().iterator();
assertEquals("name1", names_it.next());
assertEquals("name2", names_it.next());
assertEquals("name2_parent", names_it.next());
assertFalse(names_it.hasNext());
properties_child2.parent(properties_parent1);
assertSame(properties_parent1, properties_child2.getRoot());
assertEquals(3, properties_child2.size());
assertSame(property1b, properties_child2.get("name1"));
assertSame(property2b, properties_child2.get("name2"));
names_it = properties_child2.getNames().iterator();
assertEquals("name1", names_it.next());
assertEquals("name2", names_it.next());
assertEquals("name2_parent", names_it.next());
assertFalse(names_it.hasNext());
names_it = properties_child2.getInjectableNames().iterator();
assertEquals("name1", names_it.next());
assertEquals("name2", names_it.next());
assertEquals("name2_parent", names_it.next());
assertFalse(names_it.hasNext());
properties_child3.parent(properties_parent2);
assertSame(properties_parent2, properties_child3.getRoot());
assertEquals(3, properties_child3.size());
assertSame(property3b, properties_child3.get("non.identifier.name3"));
assertSame(property2c, properties_child3.get("name2"));
assertSame(property3c, properties_child3.get("non.identifier.name3_parent"));
names_it = properties_child3.getNames().iterator();
assertEquals("non.identifier.name3", names_it.next());
assertEquals("name2", names_it.next());
assertEquals("non.identifier.name3_parent", names_it.next());
assertFalse(names_it.hasNext());
names_it = properties_child3.getInjectableNames().iterator();
assertEquals("name2", names_it.next());
assertFalse(names_it.hasNext());
// set the second level parents
properties_parent1.parent(properties_grandparent);
assertEquals(5, properties_child1.size());
assertSame(property1, properties_child1.get("name1"));
assertSame(property2, properties_child1.get("name2"));
assertSame(property2c, properties_child1.get("name2_parent"));
assertSame(property3, properties_child1.get("non.identifier.name3"));
assertSame(property2d, properties_child1.get("name2_grandparent"));
names_it = properties_child1.getNames().iterator();
assertEquals("name1", names_it.next());
assertEquals("name2", names_it.next());
assertEquals("non.identifier.name3", names_it.next());
assertEquals("name2_parent", names_it.next());
assertEquals("name2_grandparent", names_it.next());
assertFalse(names_it.hasNext());
names_it = properties_child1.getInjectableNames().iterator();
assertEquals("name1", names_it.next());
assertEquals("name2", names_it.next());
assertEquals("name2_parent", names_it.next());
assertEquals("name2_grandparent", names_it.next());
assertFalse(names_it.hasNext());
properties_parent2.parent(properties_grandparent);
assertEquals(5, properties_child3.size());
assertSame(property3b, properties_child3.get("non.identifier.name3"));
assertSame(property2c, properties_child3.get("name2"));
assertSame(property3c, properties_child3.get("non.identifier.name3_parent"));
assertSame(property1d, properties_child3.get("name1"));
assertSame(property2d, properties_child3.get("name2_grandparent"));
names_it = properties_child3.getNames().iterator();
assertEquals("non.identifier.name3", names_it.next());
assertEquals("name2", names_it.next());
assertEquals("non.identifier.name3_parent", names_it.next());
assertEquals("name1", names_it.next());
assertEquals("name2_grandparent", names_it.next());
assertFalse(names_it.hasNext());
names_it = properties_child3.getInjectableNames().iterator();
assertEquals("name2", names_it.next());
assertEquals("name1", names_it.next());
assertEquals("name2_grandparent", names_it.next());
assertFalse(names_it.hasNext());
assertSame(properties_grandparent, properties_child1.getRoot());
assertSame(properties_grandparent, properties_child2.getRoot());
assertSame(properties_grandparent, properties_child3.getRoot());
// manipulate the hierarchy
assertSame(property1, properties_child1.remove("name1"));
assertNull(properties_child1.remove("name1"));
assertSame(property1c, properties_child1.get("name1"));
assertSame(property2c, properties_parent1.remove("name2_parent"));
assertNull(properties_parent1.remove("name2_parent"));
assertNull(properties_parent1.get("name2_parent"));
assertSame(property1c, properties_parent1.remove("name1"));
assertNull(properties_child1.remove("name1"));
assertSame(property1d, properties_child1.get("name1"));
assertSame(property1d, properties_parent1.get("name1"));
assertSame(property3b, properties_child3.remove("non.identifier.name3"));
assertNull(properties_child3.remove("non.identifier.name3"));
assertSame(property3d, properties_child3.get("non.identifier.name3"));
assertSame(property2c, properties_parent2.remove("name2"));
assertNull(properties_parent2.remove("name2"));
assertSame(property2d, properties_child3.get("name2"));
assertSame(property2d, properties_grandparent.remove("name2"));
assertNull(properties_grandparent.remove("name2"));
assertNull(properties_child3.get("name2"));
names_it = properties_child1.getNames().iterator();
assertEquals("name2", names_it.next());
assertEquals("non.identifier.name3", names_it.next());
assertEquals("name1", names_it.next());
assertEquals("name2_grandparent", names_it.next());
assertFalse(names_it.hasNext());
names_it = properties_child1.getInjectableNames().iterator();
assertEquals("name2", names_it.next());
assertEquals("name1", names_it.next());
assertEquals("name2_grandparent", names_it.next());
assertFalse(names_it.hasNext());
names_it = properties_child2.getNames().iterator();
assertEquals("name1", names_it.next());
assertEquals("name2", names_it.next());
assertEquals("name2_grandparent", names_it.next());
assertEquals("non.identifier.name3", names_it.next());
assertFalse(names_it.hasNext());
names_it = properties_child2.getInjectableNames().iterator();
assertEquals("name1", names_it.next());
assertEquals("name2", names_it.next());
assertEquals("name2_grandparent", names_it.next());
assertFalse(names_it.hasNext());
names_it = properties_child3.getNames().iterator();
assertEquals("non.identifier.name3_parent", names_it.next());
assertEquals("name1", names_it.next());
assertEquals("name2_grandparent", names_it.next());
assertEquals("non.identifier.name3", names_it.next());
assertFalse(names_it.hasNext());
names_it = properties_child3.getInjectableNames().iterator();
assertEquals("name1", names_it.next());
assertEquals("name2_grandparent", names_it.next());
assertFalse(names_it.hasNext());
PropertyValue property2e = new PropertyValueObject("value1e");
assertSame(properties_parent1, properties_parent1.put("new_name2", property2e));
PropertyValue property3e = new PropertyValueObject("value3e");
assertSame(properties_grandparent, properties_grandparent.put("new_non.identifier.name3", property3e));
names_it = properties_child1.getNames().iterator();
assertEquals("name2", names_it.next());
assertEquals("non.identifier.name3", names_it.next());
assertEquals("new_name2", names_it.next());
assertEquals("name1", names_it.next());
assertEquals("name2_grandparent", names_it.next());
assertEquals("new_non.identifier.name3", names_it.next());
assertFalse(names_it.hasNext());
names_it = properties_child1.getInjectableNames().iterator();
assertEquals("name2", names_it.next());
assertEquals("new_name2", names_it.next());
assertEquals("name1", names_it.next());
assertEquals("name2_grandparent", names_it.next());
assertFalse(names_it.hasNext());
names_it = properties_child2.getNames().iterator();
assertEquals("name1", names_it.next());
assertEquals("name2", names_it.next());
assertEquals("new_name2", names_it.next());
assertEquals("name2_grandparent", names_it.next());
assertEquals("non.identifier.name3", names_it.next());
assertEquals("new_non.identifier.name3", names_it.next());
assertFalse(names_it.hasNext());
names_it = properties_child2.getInjectableNames().iterator();
assertEquals("name1", names_it.next());
assertEquals("name2", names_it.next());
assertEquals("new_name2", names_it.next());
assertEquals("name2_grandparent", names_it.next());
assertFalse(names_it.hasNext());
names_it = properties_child3.getNames().iterator();
assertEquals("non.identifier.name3_parent", names_it.next());
assertEquals("name1", names_it.next());
assertEquals("name2_grandparent", names_it.next());
assertEquals("non.identifier.name3", names_it.next());
assertEquals("new_non.identifier.name3", names_it.next());
assertFalse(names_it.hasNext());
names_it = properties_child3.getInjectableNames().iterator();
assertEquals("name1", names_it.next());
assertEquals("name2_grandparent", names_it.next());
assertFalse(names_it.hasNext());
}
@Test
void testPutAll() {
var properties1 = new HierarchicalProperties();
assertEquals(0, properties1.size());
var properties2 = new HierarchicalProperties();
assertEquals(0, properties2.size());
Object value1 = new StringBuffer("test");
Object value2 = new Date();
properties2.put("24", value1);
properties2.put("test", value2);
properties1.putAll(properties2);
assertEquals(2, properties1.size());
Iterator<String> names_it = properties1.getNames().iterator();
assertEquals("24", names_it.next());
assertEquals("test", names_it.next());
assertSame(value1, properties1.get("24").getValue());
assertSame(value2, properties1.get("test").getValue());
}
@Test
void testPutAllMap() {
var properties = new HierarchicalProperties();
assertEquals(0, properties.size());
Map<Object, Object> map = new LinkedHashMap<>();
Object value1 = new StringBuffer("test");
Object value2 = new Date();
map.put(24, value1);
map.put("test", value2);
properties.putAll(map);
assertEquals(2, properties.size());
Iterator<String> names_it = properties.getNames().iterator();
assertEquals("24", names_it.next());
assertEquals("test", names_it.next());
assertSame(value1, properties.get("24").getValue());
assertSame(value2, properties.get("test").getValue());
}
@Test
void testPutAllWithoutReplacing() {
var properties1 = new HierarchicalProperties();
assertEquals(0, properties1.size());
var properties2 = new HierarchicalProperties();
assertEquals(0, properties2.size());
Object value1 = new StringBuffer("test");
Object value2 = new Date();
Object value3 = 38746387L;
properties1.put("test", value2);
properties2.put("24", value1);
properties2.put("test", value3);
properties1.putAllWithoutReplacing(properties2);
assertEquals(2, properties1.size());
Iterator<String> names_it = properties1.getNames().iterator();
assertEquals("test", names_it.next());
assertEquals("24", names_it.next());
assertSame(value1, properties1.get("24").getValue());
assertSame(value2, properties1.get("test").getValue());
}
@Test
void testGetValueString() {
var properties = new HierarchicalProperties();
Object value1 = new StringBuffer("test");
Object value2 = new BigDecimal("12682861E+10");
properties.put("value1", value1);
properties.put("value2", value2);
properties.put("value3", null);
properties.put("value4", "");
assertEquals("test", properties.getValueString("value1"));
assertEquals("1.2682861E+17", properties.getValueString("value2"));
assertNull(properties.getValueString("value3"));
assertNull(properties.getValueString("value4"));
assertNull(properties.getValueString("inexistent"));
assertEquals("test", properties.getValueString("value1", "default1"));
assertEquals("1.2682861E+17", properties.getValueString("value2", "default2"));
assertEquals("default3", properties.getValueString("value3", "default3"));
assertEquals("default4", properties.getValueString("value4", "default4"));
assertEquals("default5", properties.getValueString("inexistent", "default5"));
}
@Test
void testGetValueTyped() {
var properties = new HierarchicalProperties();
Object value1 = new StringBuffer("test");
Object value2 = new BigDecimal("12682861E+10");
Object value4 = "";
properties.put("value1", value1);
properties.put("value2", value2);
properties.put("value3", null);
properties.put("value4", value4);
assertSame(value1, properties.getValueTyped("value1", StringBuffer.class));
assertSame(value2, properties.getValueTyped("value2", BigDecimal.class));
assertNull(properties.getValueTyped("value3", String.class));
assertSame(value4, properties.getValueTyped("value4", String.class));
assertNull(properties.getValueTyped("inexistent", String.class));
var default3 = new BigDecimal("5718620E+6");
Integer default5 = 97586;
assertSame(value1, properties.getValueTyped("value1", StringBuffer.class, new StringBuffer("default1")));
assertSame(value2, properties.getValueTyped("value2", BigDecimal.class, new BigDecimal(1268)));
assertSame(default3, properties.getValueTyped("value3", BigDecimal.class, default3));
assertSame(value4, properties.getValueTyped("value4", String.class, "default4"));
assertSame(default5, properties.getValueTyped("inexistent", Integer.class, default5));
try {
properties.getValueTyped("value2", Date.class);
fail("Expected exception");
} catch (PropertyValueException e) {
assertTrue(e instanceof IncompatiblePropertyValueTypeException);
assertEquals("value2", ((IncompatiblePropertyValueTypeException) e).getPropertyName());
assertSame(Date.class, ((IncompatiblePropertyValueTypeException) e).getExpectedType());
assertSame(BigDecimal.class, ((IncompatiblePropertyValueTypeException) e).getActualType());
}
}
}

View file

@ -0,0 +1,54 @@
/*
* Copyright 2001-2022 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.ioc;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class TestPropertyValueList {
@Test
void testInstantiation() {
PropertyValueList list = new PropertyValueList();
assertNotNull(list);
assertEquals(0, list.size());
}
@Test
void testSingleNonNegligible() {
var list = new PropertyValueList();
var value = new PropertyValueObject("Not Negligible");
list.add(value);
assertEquals(1, list.size());
assertSame(value, list.makePropertyValue());
}
@Test
void testOneNoneNegligibleOtherNegligibles() {
var value = new PropertyValueObject("Not Negligible");
var list = new PropertyValueList();
list.add(new PropertyValueObject(" "));
list.add(value);
list.add(new PropertyValueObject(" "));
list.add(new PropertyValueObject(""));
assertEquals(4, list.size());
assertSame(value, list.makePropertyValue());
}
@Test
void testOneNoneNegligibleOtherNonNegligible() {
var value = new PropertyValueObject("Not Negligible");
var list = new PropertyValueList();
list.add(new PropertyValueObject(" "));
list.add(value);
list.add(new PropertyValueObject(" "));
list.add(new PropertyValueObject("testing"));
assertEquals(4, list.size());
PropertyValue result = list.makePropertyValue();
assertNotSame(value, result);
assertEquals(value + " testing", result.getValueString());
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright 2001-2022 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.ioc;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class TestPropertyValueObject {
@Test
void testInstantiation() {
Integer value = 25;
var object = new PropertyValueObject(value);
assertNotNull(object);
assertTrue(object.isStatic());
}
@Test
void testGetValue() {
Integer value = 25;
var object = new PropertyValueObject(value);
assertSame(value, object.getValue());
}
@Test
void testGetValueString() {
Integer value = 25;
var object = new PropertyValueObject(value);
assertEquals("25", object.getValueString());
}
@Test
void testToString() {
Integer value = 25;
var object = new PropertyValueObject(value);
assertEquals("25", object.toString());
}
@Test
void testisNegligible() {
assertFalse(new PropertyValueObject("lhkjkj").isNegligible());
assertTrue(new PropertyValueObject(" ").isNegligible());
assertTrue(new PropertyValueObject("").isNegligible());
assertTrue(new PropertyValueObject(null).isNegligible());
}
}

View file

@ -0,0 +1,67 @@
/*
* Copyright 2001-2022 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.ioc;
import org.junit.jupiter.api.Test;
import rife.ioc.exceptions.TemplateFactoryUnknownException;
import rife.template.Template;
import rife.template.exceptions.TemplateNotFoundException;
import static org.junit.jupiter.api.Assertions.*;
public class TestPropertyValueTemplate {
@Test
void testInstantiation() {
PropertyValueTemplate object = new PropertyValueTemplate("html", "values");
assertNotNull(object);
assertFalse(object.isStatic());
}
@Test
void testGetValue() {
PropertyValueTemplate object = new PropertyValueTemplate("html", "values");
assertNotNull(object.getValue());
assertTrue(object.getValue() instanceof Template);
}
@Test
void testGetValueUnknownFactory() {
PropertyValueTemplate object = new PropertyValueTemplate("blah", "values");
try {
object.getValue();
fail("TemplateFactoryUnknownException wasn't thrown");
} catch (TemplateFactoryUnknownException e) {
assertEquals("blah", e.getType());
}
}
@Test
void testGetValueUnknownTemplate() {
PropertyValueTemplate object = new PropertyValueTemplate("html", "blahblihbloh");
try {
object.getValue();
fail("template 'blahblihbloh' shouldn't have been found");
} catch (TemplateNotFoundException e) {
assertEquals("blahblihbloh", e.getName());
}
}
@Test
void testGetValueString() {
PropertyValueTemplate object = new PropertyValueTemplate("html", "values");
assertEquals("{{v VALUE1/}}<!--v VALUE2/-->{{v VALUE3/}}\n", object.getValueString());
}
@Test
void testToString() {
PropertyValueTemplate object = new PropertyValueTemplate("html", "values");
assertEquals("{{v VALUE1/}}<!--v VALUE2/-->{{v VALUE3/}}\n", object.toString());
}
@Test
void testisNegligible() {
assertFalse(new PropertyValueTemplate("html", "values").isNegligible());
}
}