You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tamaya.apache.org by an...@apache.org on 2017/12/17 08:00:32 UTC
[4/6] incubator-tamaya git commit: Added full JSR implementation.
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/base/filter/FilterManager.java
----------------------------------------------------------------------
diff --git a/code/base/src/main/java/org/apache/tamaya/base/filter/FilterManager.java b/code/base/src/main/java/org/apache/tamaya/base/filter/FilterManager.java
new file mode 100644
index 0000000..32c8f3f
--- /dev/null
+++ b/code/base/src/main/java/org/apache/tamaya/base/filter/FilterManager.java
@@ -0,0 +1,273 @@
+/*
+ * 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.tamaya.base.filter;
+
+import org.apache.tamaya.base.FormatUtils;
+import org.apache.tamaya.spi.Filter;
+import org.apache.tamaya.spi.ConfigValue;
+import org.apache.tamaya.spi.ServiceContext;
+import org.apache.tamaya.spi.ServiceContextManager;
+
+import javax.config.Config;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Implementation of the Configuration API. This class uses the current {@link javax.config.Config} to evaluate the
+ * chain of {@link javax.config.spi.ConfigSource} and {@link Filter}
+ * instance to evaluate the current Configuration.
+ */
+public class FilterManager {
+ /**
+ * The logger.
+ */
+ private static final Logger LOG = Logger.getLogger(FilterManager.class.getName());
+ /**
+ * The maximal number of filter cycles performed before aborting.
+ */
+ private static final int MAX_FILTER_LOOPS = 10;
+
+ private List<Filter> filters = new ArrayList<>();
+
+ private ClassLoader classloader = ServiceContext.defaultClassLoader();
+
+ /**
+ * Create a new filter manager.
+ */
+ public FilterManager(){
+ }
+
+ /**
+ * Create a new filter manager.
+ * @param filters the filters to be used, not null.
+ */
+ public FilterManager(List<Filter> filters){
+ this.filters.addAll(filters);
+ LOG.info("Registered " + filters.size() + " config filter: " + filters);
+ }
+
+ /**
+ * Get the classloader used for instance creation.
+ * @return the classloader, never null.
+ */
+ public ClassLoader getClassloader(){
+ return classloader;
+ }
+
+ /**
+ * Sets the classloader to use for loading of instances.
+ * @param ClassLoader the classloader, not null.
+ * @return this instance for chaining.
+ */
+ public FilterManager setClassloader(ClassLoader ClassLoader){
+ this.classloader = Objects.requireNonNull(classloader);
+ return this;
+ }
+
+ /**
+ * Get the current list of filters.
+ * @return the list of filters.
+ */
+ public List<Filter> getFilters(){
+ return Collections.unmodifiableList(filters);
+ }
+
+ /**
+ * Adds the given Filter instances, hereby the instances are added
+ * to the end of the list with highest priority. The ordering of existing
+ * property filters remains unchanged. To sort the
+ * filters call {@link #sortFilter}.
+ *
+ * @param filters the filters to add
+ * @return this instance, for chaining, never null.
+ */
+ public FilterManager addFilter(Filter... filters) {
+ return addFilter(Arrays.asList(filters));
+ }
+
+ /**
+ * Adds the given Filter instances, hereby the instances are added
+ * to the end of the list with highest priority. The ordering of existing
+ * property filters remains unchanged. To sort the
+ * filters call {@link #sortFilter}.
+ *
+ * @param filters the filters to add
+ * @return this instance, for chaining, never null.
+ */
+ public FilterManager addFilter(Collection<Filter> filters) {
+ Objects.requireNonNull(filters);
+ for(Filter filter:filters) {
+ if (!this.filters.contains(filter)) {
+ this.filters.add(filter);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Removes the given PropertyFilter instances, if existing. The order of the remaining
+ * filters is preserved.
+ *
+ * @param filters the filter to remove
+ * @return this builder, for chaining, never null.
+ */
+ public FilterManager removeFilters(Filter... filters){
+ return removeFilters(Arrays.asList(filters));
+ }
+
+ /**
+ * Removes the given PropertyFilter instances, if existing. The order of the remaining
+ * filters is preserved.
+ *
+ * @param filters the filter to remove
+ * @return this builder, for chaining, never null.
+ */
+ public FilterManager removeFilters(Collection<Filter> filters){
+ Objects.requireNonNull(filters);
+ this.filters.removeAll(filters);
+ return this;
+ }
+
+ /**
+ * Add all registered (default) property filters to the context built.
+ * @return this builder, for chaining, never null.
+ */
+ public FilterManager addDefaultFilters() {
+ for(Filter pf: ServiceContextManager.getServiceContext().getServices(Filter.class, classloader)){
+ addFilter(pf);
+ }
+ return this;
+ }
+
+ public FilterManager sortFilter(Comparator<Filter> comparator) {
+ Collections.sort(filters, comparator);
+ return this;
+ }
+
+ /**
+ * Removes all contained items.
+ * @return this instance for chaining.
+ */
+ public FilterManager clear() {
+ this.filters.clear();
+ return this;
+ }
+
+ /**
+ * Filters a single value.
+ * @param value the raw value, not {@code null}.
+ * @return the filtered value, including {@code null}.
+ */
+ public ConfigValue filterValue(ConfigValue value) {
+ FilterContext filterContext = new FilterContext(value, null);
+ return filterValue(filterContext);
+ }
+
+ /**
+ * Filters a single value.
+ * @param value the raw value, not {@code null}.
+ * @param config the config
+ * @return the filtered value, including {@code null}.
+ */
+ public ConfigValue filterValue(ConfigValue value, Config config) {
+ FilterContext filterContext = new FilterContext(value, config);
+ return filterValue(filterContext);
+ }
+
+ /**
+ * Filters all properties.
+ * @param rawProperties the unfiltered properties, not {@code null}.
+ * @param config the config
+ * @return the filtered value, inclusing null.
+ */
+ public Map<String, String> applyFilters(Map<String, String> rawProperties, Config config) {
+ Map<String, String> result = new HashMap<>();
+ // Apply filters to values, prevent values filtered to null!
+ for (Map.Entry<String, String> entry : rawProperties.entrySet()) {
+ FilterContext filterContext = new FilterContext(ConfigValue.of(entry.getKey(), rawProperties), config);
+ ConfigValue filtered = filterValue(filterContext);
+ if(filtered!=null){
+ result.putAll(filtered.asMap());
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Basic filter logic.
+ * @param context the filter context, not {@code null}.
+ * @return the filtered value.
+ */
+ private ConfigValue filterValue(FilterContext context) {
+ ConfigValue inputValue = context.getProperty();
+ ConfigValue filteredValue = inputValue;
+ for (int i = 0; i < MAX_FILTER_LOOPS; i++) {
+ int changes = 0;
+ for (Filter filter :filters) {
+ filteredValue = filter.filterProperty(inputValue);
+ if (filteredValue != null && !filteredValue.equals(inputValue)) {
+ changes++;
+ LOG.finest("Filter - " + inputValue + " -> " + filteredValue + " by " + filter);
+ }
+ if(filteredValue==null){
+ LOG.finest("Filter removed entry - " + inputValue + ": " + filter);
+ break;
+ }else{
+ inputValue = filteredValue;
+ }
+ }
+ if (changes == 0) {
+ LOG.finest("Finishing filter loop, no changes detected.");
+ break;
+ } else if (filteredValue == null) {
+ break;
+ } else {
+ if (i == (MAX_FILTER_LOOPS - 1)) {
+ if (LOG.isLoggable(Level.WARNING)) {
+ LOG.warning("Maximal filter loop count reached, aborting filter evaluation after cycles: " + i);
+ }
+ } else {
+ LOG.finest("Repeating filter loop, changes detected: " + changes);
+ }
+ }
+ }
+ return filteredValue;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder b = new StringBuilder();
+ b.append("Filters\n");
+ b.append("-------\n");
+ if(filters.isEmpty()){
+ b.append(" No filters loaded.\n\n");
+ }else {
+ b.append(" CLASS INFO\n\n");
+ for (Filter filter : filters) {
+ b.append(" ");
+ FormatUtils.appendFormatted(b, filter.getClass().getSimpleName(), 30);
+ b.append(FormatUtils.removeNewLines(filter.toString()));
+ b.append('\n');
+ }
+ b.append("\n");
+ }
+ return b.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/base/filter/package-info.java
----------------------------------------------------------------------
diff --git a/code/base/src/main/java/org/apache/tamaya/base/filter/package-info.java b/code/base/src/main/java/org/apache/tamaya/base/filter/package-info.java
new file mode 100644
index 0000000..7880500
--- /dev/null
+++ b/code/base/src/main/java/org/apache/tamaya/base/filter/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+/**
+ * Default implementations for filter management and evaluation.
+ */
+package org.apache.tamaya.base.filter;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/base/package-info.java
----------------------------------------------------------------------
diff --git a/code/base/src/main/java/org/apache/tamaya/base/package-info.java b/code/base/src/main/java/org/apache/tamaya/base/package-info.java
new file mode 100644
index 0000000..820476e
--- /dev/null
+++ b/code/base/src/main/java/org/apache/tamaya/base/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+/**
+ * Default implementations for config and related artifacts.
+ */
+package org.apache.tamaya.base;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/spi/ConfigContext.java
----------------------------------------------------------------------
diff --git a/code/base/src/main/java/org/apache/tamaya/spi/ConfigContext.java b/code/base/src/main/java/org/apache/tamaya/spi/ConfigContext.java
new file mode 100644
index 0000000..7c77e12
--- /dev/null
+++ b/code/base/src/main/java/org/apache/tamaya/spi/ConfigContext.java
@@ -0,0 +1,154 @@
+/*
+ * 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.tamaya.spi;
+
+import javax.config.spi.ConfigSource;
+import javax.config.spi.Converter;
+import java.lang.reflect.Type;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * Central SPI for programmatically dealing with the setup of the configuration system.
+ * This includes adding and enlisting {@link javax.config.spi.ConfigSource}s,
+ * managing {@link javax.config.spi.Converter}s, ConfigFilters, etc.
+ */
+public interface ConfigContext {
+
+ /**
+ * This method returns the current list of registered PropertySources ordered via their ordinal.
+ * PropertySources with a lower ordinal come last. The PropertySource with the
+ * highest ordinal comes first.
+ * If two PropertySources have the same ordinal number they will get sorted
+ * using their class name just to ensure the user at least gets the same ordering
+ * after a JVM restart, hereby names before are added last.
+ * PropertySources are loaded when this method is called the first time, which basically is
+ * when the first time configuration is accessed.
+ *
+ * @return a sorted list of registered PropertySources. The returned list need not be modifiable
+ */
+ List<ConfigSource> getSources();
+
+// /**
+// * Access a {@link ConfigSource} using its (unique) name.
+// * @param name the propoerty source's name, not {@code null}.
+// * @return the propoerty source found, or {@code null}.
+// */
+// default ConfigSource getSource(String name) {
+// for(ConfigSource ps: getSources()){
+// if(name.equals(ps.getName())){
+// return ps;
+// }
+// }
+// return null;
+// }
+
+ /**
+ * Access the current PropertyFilter instances.
+ * @return the list of registered PropertyFilters, never null.
+ */
+ List<Filter> getFilters();
+
+ /**
+ * <p>
+ * This method returns the Map of registered PropertyConverters
+ * per type.
+ * The List for each type is ordered via their {@link javax.annotation.Priority} and
+ * cladd name.
+ * </p>
+ *
+ * <p>A simplified scenario could be like:</p>
+ * <pre>
+ * {
+ * Date.class -> {StandardDateConverter, TimezoneDateConverter, MyCustomDateConverter }
+ * Boolean.class -> {StandardBooleanConverter, FrenchBooleanConverter}
+ * Integer.class -> {DynamicDefaultConverter}
+ * }
+ * </pre>
+ *
+ * @return map with sorted list of registered PropertySources per type.
+ */
+ Map<Type, List<Converter>> getConverters();
+
+ /**
+ * <p>
+ * This method returns the registered PropertyConverters for a given type.
+ * The List for each type is ordered via their {@link javax.annotation.Priority}.
+ * </p>
+ *
+ * <p>
+ * PropertyConverters with a higher Priority come first. The PropertyConverter with the
+ * lowest Priority comes last.
+ * If two PropertyConverter have the same ordinal number they will get sorted
+ * using their class name just to ensure the user at least gets the same ordering
+ * after a JVM restart.
+ * </p>
+ *
+ * <p>
+ * Additionally if a PropertyProvider is accessed, which is not registered the implementation
+ * should try to figure out, if there could be a default implementation as follows:</p>
+ * <ol>
+ * <li>Look for static factory methods: {@code of(String), valueOf(String), getInstance(String),
+ * instanceOf(String), fomr(String)}</li>
+ * <li>Look for a matching constructor: {@code T(String)}.</li>
+ * </ol>
+ *
+ * <p>
+ * If a correspoding factory method or constructor could be found, a corresponding
+ * PropertyConverter should be created and registered automatically for the given
+ * type.
+ * </p>
+ *
+ * <p> The scenario could be like:</p>
+ *
+ * <pre>
+ * {
+ * Date.class -> {MyCustomDateConverter,StandardDateConverter, TimezoneDateConverter}
+ * Boolean.class -> {StandardBooleanConverter, FrenchBooleanConverter}
+ * Integer.class -> {DynamicDefaultConverter}
+ * }
+ * </pre>
+ *
+ * <p>
+ * The converters returned for a type should be used as a chain, whereas the result of the
+ * first converters that is able to convert the configured value, is taken as the chain's result.
+ * No more converters are called after a converters has successfully converted the input into
+ * the required target type.
+ * </p>
+ *
+ * @param type type of the desired converters
+ * @return a sorted list of registered PropertySources per type, or null.
+ */
+ default List<Converter> getConverters(Type type){
+ return Optional.ofNullable(getConverters().get(type)).orElse(Collections.emptyList());
+ }
+
+ /**
+ * Access the {@link ConfigValueCombinationPolicy} used to evaluate the final
+ * property values.
+ * @return the {@link ConfigValueCombinationPolicy} used, never null.
+ */
+ default ConfigValueCombinationPolicy getConfigValueCombinationPolicy(){
+ return ConfigValueCombinationPolicy.DEFAULT_OVERRIDING_POLICY;
+ }
+
+}
+
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/spi/ConfigValue.java
----------------------------------------------------------------------
diff --git a/code/base/src/main/java/org/apache/tamaya/spi/ConfigValue.java b/code/base/src/main/java/org/apache/tamaya/spi/ConfigValue.java
new file mode 100644
index 0000000..fedcef5
--- /dev/null
+++ b/code/base/src/main/java/org/apache/tamaya/spi/ConfigValue.java
@@ -0,0 +1,209 @@
+/*
+ * 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.tamaya.spi;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Class modelling the result of a request for a property value. A property value is basically identified by its key.
+ * There might be reasons, where one want to further analyze, which PropertySources provided a value and which not, so
+ * it is possible to create a PropertyValue with a null value. Nevertheless in all cases the provider source (typically
+ * the name of the PropertySource) must be set.
+ */
+public final class ConfigValue implements Serializable{
+ private static final long serialVersionUID = 1L;
+ /** The requested key. */
+ private String key;
+ /** The value. */
+ private String value;
+ /** Additional metadata provided by the provider. */
+ private String metaEntry;
+
+ ConfigValue(ConfigValueBuilder builder){
+ this.key = Objects.requireNonNull(builder.key);
+ this.value = Objects.requireNonNull(builder.value);
+ this.metaEntry = builder.metaEntry;
+ }
+
+ /**
+ * Creates a new instance
+ * @param key the key, not {@code null}.
+ * @param value the value, not {@code null}.
+ * @param metaEntry the source, typically the name of the {@link javax.config.spi.ConfigSource} providing
+ * the value, not {@code null}.
+ */
+ private ConfigValue(String key, String value, String metaEntry){
+ this.key = Objects.requireNonNull(key, "Key is required.");
+ this.value = Objects.requireNonNull(value, "Value is required.");
+ this.metaEntry = metaEntry;
+ }
+
+ /**
+ * The requested key.
+ * @return the, key never {@code null}.
+ */
+ public String getKey() {
+ return key;
+ }
+
+ /**
+ * The value.
+ * @return the value, in case a value is null it is valid to return {#code null} as result for
+ * {@link javax.config.spi.ConfigSource#getValue(String)}}.
+ */
+ public String getValue() {
+ return this.value;
+ }
+
+ /**
+ * Creates a full configuration map for this key, value pair and all its meta context data. This map
+ * is also used for subsequent processing, like value filtering.
+ * @return the property value entry map.
+ */
+ public String getMetaEntry() {
+ return metaEntry;
+ }
+
+ /**
+ * Creates a new builder instance.
+ * @param key the key, not {@code null}.
+ * @return a new builder instance.
+ */
+ public static ConfigValueBuilder builder(String key){
+ Objects.requireNonNull(key, "Key must be given.");
+ return new ConfigValueBuilder(key);
+ }
+
+ /**
+ * Creates a new builder instance.
+ * @param key the key, not {@code null}.
+ * @param value the property value, not {@code null}.
+ * @return a new builder instance.
+ */
+ public static ConfigValueBuilder builder(String key, String value) {
+ Objects.requireNonNull(key, "Key must be given.");
+ Objects.requireNonNull(value, "Value must be given");
+ return new ConfigValueBuilder(key, value);
+ }
+
+
+ /**
+ * Creates a new PropertyValue without any metadata..
+ * @param key the key, not {@code null}.
+ * @param value the value.
+ * @param metaEntry the metaEntry, typically the name of the {@link javax.config.spi.ConfigSource}
+ * providing the value, not {@code null}.
+ * @return a new property value instance, or {@code null},
+ * if the value passed is {@code null}..
+ */
+ public static ConfigValue of(String key, String value, String metaEntry) {
+ if (value==null) {
+ return null;
+ }
+ return new ConfigValue(key, value, metaEntry);
+ }
+
+ /**
+ * Creates a new builder instance based on this item.
+ * @return a new builder, never null.
+ */
+ public ConfigValueBuilder toBuilder() {
+ return new ConfigValueBuilder(this.getKey())
+ .setValue(this.getValue())
+ .setMetaEntry(this.metaEntry);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ConfigValue)) return false;
+ ConfigValue that = (ConfigValue) o;
+ return Objects.equals(getKey(), that.getKey()) &&
+ Objects.equals(getValue(), that.getValue()) &&
+ Objects.equals(getMetaEntry(), that.getMetaEntry());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getKey(), getValue(),
+ getMetaEntry());
+ }
+
+ @Override
+ public String toString() {
+ return "PropertyValue{" +
+ "key='" + key + '\'' +
+ ", value='" + value + '\'' +
+ ", metaEntry='" + metaEntry + '\'' +
+ '}';
+ }
+
+ /**
+ * Maps a map of {@code Map<String,String>} to a {@code Map<String,PropertyValue>}.
+ * @param config the String based map, not {@code null}.
+ * @return the corresponding value based map.
+ */
+ public static Map<String,ConfigValue> map(Map<String, String> config) {
+ Map<String,ConfigValue> result = new HashMap<>(config.size());
+ for(Map.Entry<String,String> en:config.entrySet()){
+ result.put(en.getKey(), ConfigValue.of(en.getKey(), en.getValue(), config.get(en.getKey()+"[meta]")));
+ }
+ return result;
+ }
+
+ /**
+ * Maps a map of {@code Map<String,String>} to a {@code Map<String,PropertyValue>}.
+ *
+ * @param config the String based map, not {@code null}.
+ * @param source the source name, not {@code null}.
+ * @param metaData additional metadata, not {@code null}.
+ * @return the corresponding value based map.
+ */
+ public static Map<String,ConfigValue> map(Map<String, String> config, String source,
+ Map<String,String> metaData) {
+ Objects.requireNonNull(config, "Config must be given.");
+ Objects.requireNonNull(source, "Source must be given.");
+ Objects.requireNonNull(metaData, "Meta data must be given.");
+
+ Map<String,ConfigValue> result = new HashMap<>(config.size());
+
+ for(Map.Entry<String,String> en:config.entrySet()){
+ ConfigValue value = new ConfigValueBuilder(en.getKey(), source).setValue(en.getValue())
+ .addMetaEntries(metaData).build();
+ result.put(en.getKey(), value);
+ }
+ return result;
+ }
+
+ public Map<? extends String, ? extends String> asMap() {
+ Map<String,String> map = new HashMap<>();
+ map.put(key, value);
+ map.put(key+"[meta]", this.metaEntry);
+ return map;
+ }
+
+ public static ConfigValue of(String key, Map<String, String> rawProperties) {
+ String value = rawProperties.get(key);
+ String meta = rawProperties.get(key+"[meta]");
+ return new ConfigValue(key, value, meta);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/spi/ConfigValueBuilder.java
----------------------------------------------------------------------
diff --git a/code/base/src/main/java/org/apache/tamaya/spi/ConfigValueBuilder.java b/code/base/src/main/java/org/apache/tamaya/spi/ConfigValueBuilder.java
new file mode 100644
index 0000000..d1c2d60
--- /dev/null
+++ b/code/base/src/main/java/org/apache/tamaya/spi/ConfigValueBuilder.java
@@ -0,0 +1,179 @@
+/*
+ * 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.tamaya.spi;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.*;
+
+/**
+ * Builder to create a {@link ConfigValue} instance.
+ */
+public class ConfigValueBuilder {
+ /** The key accessed. */
+ String key;
+ /** The property value. */
+ String value;
+ /** additional metadata entries (optional). */
+ String metaEntry;
+
+ /**
+ * Create a new builder instance, for a given set of parameters.
+ * Before calling build at least a {@link #value} and its {@link #metaEntry}
+ * must be set.
+ */
+ ConfigValueBuilder(String key){
+ this.key = Objects.requireNonNull(key);
+ }
+
+ /**
+ * Create a new builder instance, for a given set of parameters.
+ * @param key to access a property value, not {@code null}.
+ * @param value property value.
+ */
+ ConfigValueBuilder(String key, String value) {
+ this.key = Objects.requireNonNull(key);
+ this.value = Objects.requireNonNull(value);
+ }
+
+ /**
+ * Create a new builder instance, for a given set of parameters.
+ *
+ * @param key to access a property value.
+ * @param value the value, not {@code null}. If a value is {@code null}
+ * {@link javax.config.spi.ConfigSource#getValue(String)} should return {@code null}.
+ * @param metaEntry property metaEntry.
+ */
+ ConfigValueBuilder(String key, String value, String metaEntry) {
+ this.key = Objects.requireNonNull(key);
+ this.value = value;
+ this.metaEntry = Objects.requireNonNull(metaEntry);
+ }
+
+ /**
+ * Replaces/sets the context data.
+ * @param metaEntry the context data to be applied.
+ * @return the builder for chaining.
+ */
+ public ConfigValueBuilder setMetaEntry(String metaEntry) {
+ this.metaEntry = metaEntry;
+ return this;
+ }
+
+ /**
+ * Add an additional context data information.
+ * @param key the context data key, not {@code null}.
+ * @param value the context value, not {@code null} (will be converted to String).
+ * @return the builder for chaining.
+ */
+ public ConfigValueBuilder addMetaEntry(String key, String value) {
+ Objects.requireNonNull(key, "Meta key must be given.");
+ Objects.requireNonNull(value, "Meta value must be given.");
+ if(metaEntry==null){
+ metaEntry = key+"="+value;
+ }else{
+ metaEntry = "\n" + key+"="+value;
+ }
+ return this;
+ }
+
+ /**
+ * Adds the context data given.
+ * @param metaEntries the context data to be applied, not {@code null}.
+ * @return the builder for chaining.
+ */
+ public ConfigValueBuilder addMetaEntries(Map<String, String> metaEntries) {
+ Properties props = new Properties();
+ props.putAll(metaEntries);
+ StringWriter stringWriter = new StringWriter();
+ try {
+ props.store(stringWriter, null);
+ stringWriter.flush();
+ if(metaEntry==null){
+ metaEntry = stringWriter.toString();
+ }else{
+ metaEntry += '\n' + stringWriter.toString();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return this;
+ }
+
+ /**
+ * Adds the context data given as JSON object.
+ * @param meta the context data in JSON format, not {@code null}.
+ * @return the builder for chaining.
+ */
+ public ConfigValueBuilder addMetaEntry(String meta) {
+ if(metaEntry==null){
+ metaEntry = meta;
+ }else{
+ metaEntry += '\n' + meta;
+ }
+ return this;
+ }
+
+ /**
+ * Get the value's context data.
+ * @return the context data, not {@code null}.
+ */
+ public String getMetaEntry() {
+ return metaEntry;
+ }
+
+ /**
+ * Sets a new key.
+ * @param key the new key, not {@code null}.
+ * @return the builder for chaining.
+ */
+ public ConfigValueBuilder setKey(String key) {
+ this.key = Objects.requireNonNull(key);
+ return this;
+ }
+
+ /**
+ * Sets a new value.
+ * @param value the new value, not {@code null}.
+ * @return the builder for chaining.
+ */
+ public ConfigValueBuilder setValue(String value) {
+ this.value = Objects.requireNonNull(value, "Value must be given.");
+
+ return this;
+ }
+
+ /**
+ * Creates a new immutable {@link ConfigValue}.
+ * @return a new immutable {@link ConfigValue}, never {@code null}.
+ */
+ public ConfigValue build(){
+ return new ConfigValue(this);
+ }
+
+ @Override
+ public String toString() {
+ return "PropertyValueBuilder{" +
+ "key='" + key + '\'' +
+ "value='" + value + '\'' +
+ ", metaEntry=" + metaEntry +
+ '}';
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/spi/Experimental.java
----------------------------------------------------------------------
diff --git a/code/base/src/main/java/org/apache/tamaya/spi/Experimental.java b/code/base/src/main/java/org/apache/tamaya/spi/Experimental.java
new file mode 100644
index 0000000..9831820
--- /dev/null
+++ b/code/base/src/main/java/org/apache/tamaya/spi/Experimental.java
@@ -0,0 +1,32 @@
+/*
+ * 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.tamaya.spi;
+
+import java.lang.annotation.*;
+
+/**
+ * This is a simple annotation for flaging out functionality or features the Tamaya team is not sure if it is already
+ * stabilized, so use it with some caution.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD,
+ ElementType.TYPE})
+public @interface Experimental {
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/spi/Filter.java
----------------------------------------------------------------------
diff --git a/code/base/src/main/java/org/apache/tamaya/spi/Filter.java b/code/base/src/main/java/org/apache/tamaya/spi/Filter.java
new file mode 100644
index 0000000..9fdad86
--- /dev/null
+++ b/code/base/src/main/java/org/apache/tamaya/spi/Filter.java
@@ -0,0 +1,50 @@
+/*
+ * 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.tamaya.spi;
+
+
+/**
+ * <p>Interface for filtering the current map of properties during the evaluation of the chain of PropertySources.
+ * Filters can be registered using the {@link org.apache.tamaya.spi.ServiceContext}. The ordinal
+ * hereby is defined by the corresponding {@code @Priority} annotation.</p>
+ * <p>Filters </p>
+ */
+@FunctionalInterface
+public interface Filter {
+
+ /**
+ * <p>Maps the current {@code valueToBeFiltered} value to a new value. The resulting value will be used as the result
+ * passed to the user.</p>
+ * <p>If a filter is currently not available, it should just pass the input map to the method's
+ * output.</p>
+ * <p>Returning {@code null} will remove the entry.</p>
+ * <h3>Implementation specification</h3>
+ * Implementations of this class must be
+ * <ul>
+ * <li>reentrant</li>
+ * <li>thread-safe</li>
+ * </ul>
+ * @param value the value to be filtered, which also can be {@code null} if removed by another filter.
+ * @return the filtered value, or {@code null} if the value should be removed alltogether.
+ * @see ConfigValue
+ * @see ConfigValueBuilder
+ */
+ ConfigValue filterProperty(ConfigValue value);
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/spi/ServiceContext.java
----------------------------------------------------------------------
diff --git a/code/base/src/main/java/org/apache/tamaya/spi/ServiceContext.java b/code/base/src/main/java/org/apache/tamaya/spi/ServiceContext.java
new file mode 100644
index 0000000..ff0bb9f
--- /dev/null
+++ b/code/base/src/main/java/org/apache/tamaya/spi/ServiceContext.java
@@ -0,0 +1,147 @@
+/*
+ * 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.tamaya.spi;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.List;
+
+
+/**
+ * This class models the component that is managing the lifecycle current the
+ * services used by the Configuration API.
+ */
+public interface ServiceContext {
+
+ /**
+ * @return ordinal of the ServiceContext. The one with the highest ordinal will be taken.
+ */
+ int ordinal();
+
+ static ClassLoader defaultClassLoader(){
+ ClassLoader classloader = Thread.currentThread().getContextClassLoader();
+ if(classloader==null){
+ classloader = ServiceContextManager.class.getClassLoader();
+ }
+ return classloader;
+ }
+
+ /**
+ * Access a service singleton via its type.
+ * If multiple implementations for the very serviceType exist then
+ * the one with the highest {@link javax.annotation.Priority} will be used.
+ *
+ * @param <T> the type of the service type.
+ * @param serviceType the service type.
+ * @return The instance to be used, or {@code null}
+ * @throws IllegalArgumentException if there are multiple service implementations with the maximum priority.
+ */
+ default <T> T getService(Class<T> serviceType){
+ return getService(serviceType, defaultClassLoader());
+ }
+
+ /**
+ * Access a service singleton via its type.
+ * If multiple implementations for the very serviceType exist then
+ * the one with the highest {@link javax.annotation.Priority} will be used.
+ *
+ * @param <T> the type of the service type.
+ * @param serviceType the service type.
+ * @param classLoader the class loader to be considered.
+ * @return The instance to be used, or {@code null}
+ * @throws IllegalArgumentException if there are multiple service implementations with the maximum priority.
+ */
+ <T> T getService(Class<T> serviceType, ClassLoader classLoader);
+
+ /**
+ * Factory method to create a type, hereby a new instance is created on each access.
+ * If multiple implementations for the very serviceType exist then
+ * the one with the highest {@link javax.annotation.Priority} will be used as the base
+ * for creating subsequent instances.
+ *
+ * @param <T> the type of the service type.
+ * @param serviceType the service type.
+ * @return The new instance to be used, or {@code null}
+ * @throws IllegalArgumentException if there are multiple service implementations with the maximum priority.
+ */
+ default <T> T create(Class<T> serviceType){
+ return create(serviceType, defaultClassLoader());
+ }
+
+ /**
+ * Factory method to create a type, hereby a new instance is created on each access.
+ * If multiple implementations for the very serviceType exist then
+ * the one with the highest {@link javax.annotation.Priority} will be used as the base
+ * for creating subsequent instances.
+ *
+ * @param <T> the type of the service type.
+ * @param serviceType the service type.
+ * @param classLoader the class loader to be considered.
+ * @return The new instance to be used, or {@code null}
+ * @throws IllegalArgumentException if there are multiple service implementations with the maximum priority.
+ */
+ <T> T create(Class<T> serviceType, ClassLoader classLoader);
+
+
+ /**
+ * Access a list current services, given its type. The bootstrap mechanism should
+ * order the instance for precedence, hereby the most significant should be
+ * first in order.
+ *
+ * @param serviceType
+ * the service type.
+ * @param <T> the type of the list element returned by this method
+ * @return The instance to be used, never {@code null}
+ */
+ default <T> List<T> getServices(Class<T> serviceType){
+ return getServices(serviceType, defaultClassLoader());
+ }
+
+ /**
+ * Access a list current services, given its type. The bootstrap mechanism should
+ * order the instance for precedence, hereby the most significant should be
+ * first in order.
+ *
+ * @param serviceType
+ * the service type.
+ * @param <T> the type of the list element returned by this method
+ * @return The instance to be used, never {@code null}
+ */
+ <T> List<T> getServices(Class<T> serviceType, ClassLoader classLoader);
+
+ /**
+ * Loads resources from the current runtime context. This method allows to use runtime
+ * specific code to load resources, e.g. within OSGI environments.
+ * @param resource the resource, not {@code null}.
+ * @param cl the desired classloader context, if null, the current thread context classloader is used.
+ * @return the resources found
+ * @throws IOException if load fails.
+ */
+ Enumeration<URL> getResources(String resource, ClassLoader cl) throws IOException;
+
+ /**
+ * Loads a resource from the current runtime context. This method allows to use runtime
+ * specific code to load a resource, e.g. within OSGI environments.
+ * @param resource the resource, not {@code null}.
+ * @param cl the desired classloader context, if null, the current thread context classloader is used.
+ * @return the resource found, or {@code null}.
+ */
+ URL getResource(String resource, ClassLoader cl);
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/spi/ServiceContextManager.java
----------------------------------------------------------------------
diff --git a/code/base/src/main/java/org/apache/tamaya/spi/ServiceContextManager.java b/code/base/src/main/java/org/apache/tamaya/spi/ServiceContextManager.java
new file mode 100644
index 0000000..ddfd425
--- /dev/null
+++ b/code/base/src/main/java/org/apache/tamaya/spi/ServiceContextManager.java
@@ -0,0 +1,118 @@
+/*
+ * 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.tamaya.spi;
+
+import java.util.Objects;
+import java.util.ServiceLoader;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * This singleton provides access to the services available in the current {@link ServiceContext}. The
+ * behaviour can be adapted, by calling {@link ServiceContextManager#set(ServiceContext)} before accessing any
+ * services.
+ */
+public final class ServiceContextManager {
+
+ /**
+ * The logger used.
+ */
+ private static final Logger LOG = Logger.getLogger(ServiceContextManager.class.getName());
+
+ /**
+ * The ServiceProvider used.
+ */
+ private static volatile ServiceContext serviceContextProviderDelegate;
+
+ /**
+ * Private singletons constructor.
+ */
+ private ServiceContextManager() {
+ }
+
+ /**
+ * Load the {@link ServiceContext} to be used.
+ *
+ * @return {@link ServiceContext} to be used for loading the services.
+ */
+ private static ServiceContext loadDefaultServiceProvider() {
+ ServiceContext highestServiceContext = null;
+ try {
+ int highestOrdinal = 0;
+ for (ServiceContext serviceContext : ServiceLoader.load(ServiceContext.class)) {
+ if (highestServiceContext == null) {
+ highestServiceContext = serviceContext;
+ } else if (serviceContext.ordinal() > highestOrdinal) {
+ highestServiceContext = serviceContext;
+ highestOrdinal = serviceContext.ordinal();
+ }
+ }
+ } catch (Exception e) {
+ throw new IllegalStateException("ServiceContext not loadable", e);
+ }
+ if (highestServiceContext == null) {
+ throw new IllegalStateException("No ServiceContext found");
+ }
+ LOG.info("Using Service Context of type: " + highestServiceContext.getClass().getName());
+ return highestServiceContext;
+ }
+
+ /**
+ * Replace the current {@link ServiceContext} in use.
+ *
+ * @param serviceContextProvider the new {@link ServiceContext}, not {@code null}.
+ * @return the currently used context after setting it.
+ */
+ public static ServiceContext set(ServiceContext serviceContextProvider) {
+ Objects.requireNonNull(serviceContextProvider);
+ ServiceContext currentContext = ServiceContextManager.serviceContextProviderDelegate;
+
+ synchronized (ServiceContextManager.class) {
+ if (ServiceContextManager.serviceContextProviderDelegate == null) {
+ ServiceContextManager.serviceContextProviderDelegate = serviceContextProvider;
+ LOG.log(Level.INFO, "Using ServiceProvider: " + serviceContextProvider.getClass().getName());
+ } else {
+ LOG.log(Level.WARNING, "Replacing ServiceProvider " +
+ ServiceContextManager.serviceContextProviderDelegate.getClass().getName() +
+ " with: " + serviceContextProvider.getClass().getName());
+ ServiceContextManager.serviceContextProviderDelegate = serviceContextProvider;
+ }
+ }
+
+ return currentContext;
+ }
+
+ /**
+ * Ge {@link ServiceContext}. If necessary the {@link ServiceContext} will be laziliy loaded.
+ *
+ * @return the {@link ServiceContext} used.
+ */
+ public static ServiceContext getServiceContext(){
+ if (serviceContextProviderDelegate == null) {
+ synchronized (ServiceContextManager.class) {
+ if (serviceContextProviderDelegate == null) {
+ serviceContextProviderDelegate = loadDefaultServiceProvider();
+ }
+ }
+ }
+ return serviceContextProviderDelegate;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/spi/StandaloneConfigContextBuilder.java
----------------------------------------------------------------------
diff --git a/code/base/src/main/java/org/apache/tamaya/spi/StandaloneConfigContextBuilder.java b/code/base/src/main/java/org/apache/tamaya/spi/StandaloneConfigContextBuilder.java
new file mode 100644
index 0000000..f8cd841
--- /dev/null
+++ b/code/base/src/main/java/org/apache/tamaya/spi/StandaloneConfigContextBuilder.java
@@ -0,0 +1,407 @@
+///*
+// * 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.tamaya.spi;
+//
+//import org.apache.tamaya.base.configsource.ConfigSourceComparator;
+//import org.apache.tamaya.base.configsource.CLIConfigSource;
+//import org.apache.tamaya.base.configsource.EnvironmentConfigSource;
+//import org.apache.tamaya.base.configsource.JavaConfigurationConfigSource;
+//import org.apache.tamaya.base.configsource.SystemConfigSource;
+//
+//import javax.config.spi.ConfigSource;
+//import javax.config.spi.ConfigSourceProvider;
+//import javax.config.spi.Converter;
+//import java.lang.reflect.ParameterizedType;
+//import java.lang.reflect.Type;
+//import java.util.*;
+//import java.util.logging.Logger;
+//
+///**
+// * Default implementation of {@link StandaloneConfigContextBuilder}.
+// */
+//public final class StandaloneConfigContextBuilder {
+//
+// private static final Logger LOG = Logger.getLogger(StandaloneConfigContextBuilder.class.getName());
+//
+// protected List<Filter> filters = new ArrayList<>();
+// protected List<ConfigSource> propertySources = new ArrayList<>();
+// protected ConfigValueCombinationPolicy combinationPolicy = ConfigValueCombinationPolicy.DEFAULT_OVERRIDING_POLICY;
+// protected Map<Type, Collection<Converter>> propertyConverters = new HashMap<>();
+//
+// /**
+// * Flag if the config has already been built.
+// * Configuration can be built only once
+// */
+// private boolean built;
+// private ClassLoader classLoader;
+//
+// /**
+// * Creates a new builder instance.
+// */
+// public StandaloneConfigContextBuilder() {
+// }
+//
+// /**
+// * Creates a new builder instance initializing it with the given context.
+// * @param context the context to be used, not null.
+// */
+// public StandaloneConfigContextBuilder(ConfigContext context) {
+// this.propertyConverters.putAll(context.getConverters());
+// this.filters.addAll(context.getFilters());
+// for(ConfigSource ps:context.getSources()) {
+// withSources(ps);
+// }
+// this.combinationPolicy = context.getConfigValueCombinationPolicy();
+// }
+//
+// /**
+// * Allows to reset configuration context during unit tests.
+// */
+// public final StandaloneConfigContextBuilder reset() {
+// checkBuilderState();
+// this.filters.clear();
+// this.propertySources.clear();
+// this.propertyConverters.clear();
+// this.combinationPolicy = ConfigValueCombinationPolicy.DEFAULT_OVERRIDING_POLICY;
+// return this;
+// }
+//
+//
+// public StandaloneConfigContextBuilder withContext(ConfigContext context) {
+// checkBuilderState();
+// this.propertyConverters.putAll(context.getConverters());
+// for(ConfigSource ps:context.getSources()){
+// this.propertySources.add(ps);
+// }
+// this.filters.addAll(context.getFilters());
+// this.combinationPolicy = context.getConfigValueCombinationPolicy();
+// return this;
+// }
+//
+// public final StandaloneConfigContextBuilder withSources(ConfigSource... sources){
+// return withSources(Arrays.asList(sources));
+// }
+//
+// public StandaloneConfigContextBuilder withSources(Collection<ConfigSource> sources){
+// checkBuilderState();
+// for(ConfigSource source:sources) {
+// if (!this.propertySources.contains(source)) {
+// this.propertySources.add(source);
+// }
+// }
+// return this;
+// }
+//
+//
+// public StandaloneConfigContextBuilder addDiscoveredSources() {
+// checkBuilderState();
+// List<ConfigSource> propertySources = new ArrayList<>();
+//// addDiscoveredSources(propertySources);
+// for(ConfigSource ps: ServiceContextManager.getServiceContext().getServices(ConfigSource.class, classLoader)) {
+// if(!propertySources.contains(ps)){
+// propertySources.add(ps);
+// }
+// }
+//
+// for(ConfigSourceProvider provider:
+// ServiceContextManager.getServiceContext().getServices(ConfigSourceProvider.class, classLoader)){
+// for(ConfigSource src: provider.getConfigSources(classLoader)){
+// propertySources.add(src);
+// }
+// }
+// Collections.sort(propertySources, ConfigSourceComparator.getInstance());
+// return withSources(propertySources);
+// }
+//
+// private StandaloneConfigContextBuilder addDiscoveredSources(List<ConfigSource> propertySources) {
+// for(ConfigSource ps: new ConfigSource[]{
+// new EnvironmentConfigSource(),
+// new JavaConfigurationConfigSource(),
+// new CLIConfigSource(),
+// new SystemConfigSource()
+// }){
+// if(!propertySources.contains(ps)){
+// propertySources.add(ps);
+// }
+// }
+// return this;
+// }
+//
+// public StandaloneConfigContextBuilder addDiscoveredPropertyFilters() {
+// checkBuilderState();
+// for(Filter pf:ServiceContextManager.getServiceContext().getServices(Filter.class, classLoader)){
+// withFilters(pf);
+// }
+// return this;
+// }
+//
+// public StandaloneConfigContextBuilder addDiscoveredConverters() {
+// checkBuilderState();
+// addDiscoveredConverters();
+// for(Map.Entry<Type, Collection<Converter>> en: getDefaultConverters().entrySet()){
+// for(Converter pc: en.getValue()) {
+// withConverters(en.getKey(), pc);
+// }
+// }
+// return this;
+// }
+//
+// @SuppressWarnings("unchecked")
+// public void addDiscoveredConverters() {
+// // should be overridden by subclasses.
+// }
+//
+// public final StandaloneConfigContextBuilder removeSources(ConfigSource... propertySources) {
+// return removeSources(Arrays.asList(propertySources));
+// }
+//
+// public StandaloneConfigContextBuilder removeSources(Collection<ConfigSource> propertySources) {
+// checkBuilderState();
+// this.propertySources.removeAll(propertySources);
+// return this;
+// }
+//
+// protected ConfigSource getSource(String name) {
+// for(ConfigSource ps:propertySources){
+// if(ps.getName().equals(name)){
+// return ps;
+// }
+// }
+// throw new IllegalArgumentException("No such PropertySource: "+name);
+// }
+//
+// public List<ConfigSource> getSources() {
+// return Collections.unmodifiableList(this.propertySources);
+// }
+//
+// public StandaloneConfigContextBuilder increasePriority(ConfigSource propertySource) {
+// checkBuilderState();
+// int index = propertySources.indexOf(propertySource);
+// if(index<0){
+// throw new IllegalArgumentException("No such PropertySource: " + propertySource);
+// }
+// if(index<(propertySources.size()-1)){
+// propertySources.remove(propertySource);
+// propertySources.add(index+1, propertySource);
+// }
+// return this;
+// }
+//
+// public StandaloneConfigContextBuilder decreasePriority(ConfigSource propertySource) {
+// checkBuilderState();
+// int index = propertySources.indexOf(propertySource);
+// if(index<0){
+// throw new IllegalArgumentException("No such PropertySource: " + propertySource);
+// }
+// if(index>0){
+// propertySources.remove(propertySource);
+// propertySources.add(index-1, propertySource);
+// }
+// return this;
+// }
+//
+// public StandaloneConfigContextBuilder highestPriority(ConfigSource propertySource) {
+// checkBuilderState();
+// int index = propertySources.indexOf(propertySource);
+// if(index<0){
+// throw new IllegalArgumentException("No such PropertySource: " + propertySource);
+// }
+// if(index<(propertySources.size()-1)){
+// propertySources.remove(propertySource);
+// propertySources.add(propertySource);
+// }
+// return this;
+// }
+//
+// public StandaloneConfigContextBuilder lowestPriority(ConfigSource propertySource) {
+// checkBuilderState();
+// int index = propertySources.indexOf(propertySource);
+// if(index<0){
+// throw new IllegalArgumentException("No such PropertySource: " + propertySource);
+// }
+// if(index>0){
+// propertySources.remove(propertySource);
+// propertySources.add(0, propertySource);
+// }
+// return this;
+// }
+//
+// public final StandaloneConfigContextBuilder withFilters(Filter... filters){
+// return withFilters(Arrays.asList(filters));
+// }
+//
+// public final StandaloneConfigContextBuilder withFilters(Collection<Filter> filters){
+// checkBuilderState();
+// for(Filter f:filters) {
+// if (!this.filters.contains(f)) {
+// this.filters.add(f);
+// }
+// }
+// return this;
+// }
+//
+// public final StandaloneConfigContextBuilder removeFilters(Filter... filters) {
+// return removeFilters(Arrays.asList(filters));
+// }
+//
+// public final StandaloneConfigContextBuilder removeFilters(Collection<Filter> filters) {
+// checkBuilderState();
+// this.filters.removeAll(filters);
+// return this;
+// }
+//
+//
+// public final <T> StandaloneConfigContextBuilder removeConverters(Type typeToConvert,
+// @SuppressWarnings("unchecked") Converter<T>... converters) {
+// return removeConverters(typeToConvert, Arrays.asList(converters));
+// }
+//
+// public final <T> StandaloneConfigContextBuilder removeConverters(Type typeToConvert,
+// Collection<Converter<T>> converters) {
+// Collection<Converter> subConverters = this.propertyConverters.get(typeToConvert);
+// if(subConverters!=null) {
+// subConverters.removeAll(converters);
+// }
+// return this;
+// }
+//
+// public final StandaloneConfigContextBuilder removeConverters(TypeLiteral<?> typeToConvert) {
+// this.propertyConverters.remove(typeToConvert);
+// return this;
+// }
+//
+//
+// public final StandaloneConfigContextBuilder withPropertyValueCombinationPolicy(ConfigValueCombinationPolicy combinationPolicy){
+// checkBuilderState();
+// this.combinationPolicy = Objects.requireNonNull(combinationPolicy);
+// return this;
+// }
+//
+//
+// public <T> StandaloneConfigContextBuilder withConverters(Type type, Converter<T>... propertyConverters){
+// checkBuilderState();
+// Objects.requireNonNull(type);
+// Objects.requireNonNull(propertyConverters);
+// Collection<Converter> converters = this.propertyConverters.get(type);
+// if(converters==null){
+// converters = new ArrayList<>();
+// this.propertyConverters.put(type, converters);
+// }
+// for(Converter<T> propertyConverter:propertyConverters) {
+// if (!converters.contains(propertyConverter)) {
+// converters.add(propertyConverter);
+// } else {
+// LOG.warning("Converter ignored, already registered: " + propertyConverter);
+// }
+// }
+// return this;
+// }
+//
+// public <T> StandaloneConfigContextBuilder withConverters(Type type, Collection<Converter<T>> propertyConverters){
+// checkBuilderState();
+// Objects.requireNonNull(type);
+// Objects.requireNonNull(propertyConverters);
+// Collection<Converter> converters = this.propertyConverters.get(type);
+// if(converters==null){
+// converters = new ArrayList<>();
+// this.propertyConverters.put(type, converters);
+// }
+// for(Converter<T> propertyConverter:propertyConverters) {
+// if (!converters.contains(propertyConverter)) {
+// converters.add(propertyConverter);
+// } else {
+// LOG.warning("Converter ignored, already registered: " + propertyConverter);
+// }
+// }
+// return this;
+// }
+//
+// protected StandaloneConfigContextBuilder loadDefaults() {
+// checkBuilderState();
+// this.combinationPolicy = ConfigValueCombinationPolicy.DEFAULT_OVERRIDING_COLLECTOR;
+// addDiscoveredSources();
+// addDiscoveredPropertyFilters();
+// addDiscoveredConverters();
+// return this;
+// }
+//
+//
+// protected Map<Type, Collection<Converter>> getDefaultConverters() {
+// Map<Type, Collection<Converter>> result = new HashMap<>();
+// for (Converter conv : ServiceContextManager.getServiceContext().getServices(
+// Converter.class, classLoader)) {
+// for(Type type:conv.getClass().getGenericInterfaces()){
+// if(type instanceof ParameterizedType){
+// ParameterizedType pt = (ParameterizedType)type;
+// if(Converter.class.equals(((ParameterizedType) type).getRawType())){
+// Type target = pt.getActualTypeArguments()[0];
+// Collection<Converter> convList = result.get(target);
+// if (convList == null) {
+// convList = new ArrayList<>();
+// result.put(target, convList);
+// }
+// convList.add(conv);
+// }
+// }
+// }
+// }
+// return result;
+// }
+//
+//
+// /**
+// * Builds a new configuration based on the configuration of this builder instance.
+// *
+// * @return a new {@link javax.config.Config configuration instance},
+// * never {@code null}.
+// */
+// public ConfigContext build() {
+// checkBuilderState();
+// built = true;
+// return new StandaloneConfigContext(this);
+// }
+//
+// public StandaloneConfigContextBuilder sortFilter(Comparator<Filter> comparator) {
+// Collections.sort(filters, comparator);
+// return this;
+// }
+//
+// public StandaloneConfigContextBuilder sortSources(Comparator<ConfigSource> comparator) {
+// Collections.sort(propertySources, comparator);
+// return this;
+// }
+//
+// private void checkBuilderState() {
+// if (built) {
+// throw new IllegalStateException("Configuration has already been build.");
+// }
+// }
+//
+// public List<Filter> getFilters() {
+// return filters;
+// }
+//
+// public Map<Type, Collection<Converter>> getConverter() {
+// return Collections.unmodifiableMap(this.propertyConverters);
+// }
+//
+// public void setClassLoader(ClassLoader classLoader) {
+// this.classLoader = classLoader;
+// }
+//}
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/spi/TamayaConfigBuilder.java
----------------------------------------------------------------------
diff --git a/code/base/src/main/java/org/apache/tamaya/spi/TamayaConfigBuilder.java b/code/base/src/main/java/org/apache/tamaya/spi/TamayaConfigBuilder.java
new file mode 100644
index 0000000..e48a6bd
--- /dev/null
+++ b/code/base/src/main/java/org/apache/tamaya/spi/TamayaConfigBuilder.java
@@ -0,0 +1,333 @@
+/*
+ * 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.tamaya.spi;
+
+import org.apache.tamaya.base.DefaultConfigBuilder;
+
+import javax.config.spi.ConfigBuilder;
+import javax.config.spi.ConfigSource;
+import javax.config.spi.Converter;
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A builder for creating new or adapting instances of {@link javax.config.Config}.
+ * Builders can be obtained in exactly two ways:
+ * <ol>
+ * <li>By accessing an empty builder instance from
+ * {@link javax.config.spi.ConfigProviderResolver#getBuilder()}.</li>
+ * </ol>
+ */
+public interface TamayaConfigBuilder extends ConfigBuilder, ConfigContextSupplier{
+
+ /**
+ * Create a new empty configuration builder.
+ * @return
+ */
+ static TamayaConfigBuilder create() {
+ return new DefaultConfigBuilder();
+ }
+
+ static TamayaConfigBuilder create(ConfigContextSupplier contextSupplier){
+ return new DefaultConfigBuilder(contextSupplier.getConfigContext());
+ }
+
+ static TamayaConfigBuilder from(ConfigBuilder configBuilder){
+ if(configBuilder instanceof TamayaConfigBuilder) {
+ return (TamayaConfigBuilder) configBuilder;
+ }else if(configBuilder instanceof ConfigContextSupplier){
+ return create((ConfigContextSupplier)configBuilder);
+ }
+ throw new IllegalArgumentException("Builder must implement ConfigContextSupplier.");
+ }
+
+ /**
+ * This method can be used for programmatically adding {@link ConfigSource}s.
+ * Hereby the property source is added to the tail of property sources with
+ * lowest priority regardless of its current ordinal value. To sort the property
+ * sources based on their ordinals call {@link #sortSources}.
+ *
+ * @param propertySources the PropertySources to add
+ * @return this builder, for chaining, never null.
+ * @throws IllegalArgumentException If a property source with a given name already
+ * exists.
+ */
+ TamayaConfigBuilder withSources(Collection<ConfigSource> propertySources);
+
+ /**
+ * Removes the given property sources, if existing. The existing order of property
+ * sources is preserved.
+ *
+ * @param propertySources the property sources to remove, not {@code null}.
+ * @return the builder for chaining.
+ */
+ TamayaConfigBuilder removeSources(ConfigSource... propertySources);
+
+ /**
+ * Removes the given property sources, if existing. The existing order of property
+ * sources is preserved.
+ *
+ * @param propertySources the property sources to remove, not {@code null}.
+ * @return the builder for chaining.
+ */
+ TamayaConfigBuilder removeSources(Collection<ConfigSource> propertySources);
+
+ /**
+ * Increases the priority of the given property source, by moving it towards the end
+ * of the chain of property sources. If the property source given is already at the end
+ * this method has no effect. This operation does not change any ordinal values.
+ *
+ * @param propertySource the property source to be incresed regarding its significance.
+ * @return the builder for chaining.
+ * @throws IllegalArgumentException If no such property source exists in the current
+ * chain.
+ */
+ TamayaConfigBuilder increasePriority(ConfigSource propertySource);
+
+ /**
+ * Decreases the priority of the given property source, by moving it towards the start
+ * of the chain of property sources. If the property source given is already the first
+ * this method has no effect. This operation does not change any ordinal values.
+ *
+ * @param propertySource the property source to be decresed regarding its significance.
+ * @return the builder for chaining.
+ * @throws IllegalArgumentException If no such property source exists in the current
+ * chain.
+ */
+ TamayaConfigBuilder decreasePriority(ConfigSource propertySource);
+
+ /**
+ * Increases the priority of the given property source to be maximal, by moving it to
+ * the tail of the of property source chain. If the property source given is
+ * already the last item this method has no effect. This operation does not change
+ * any ordinal values.
+ *
+ * @param propertySource the property source to be maximized regarding its significance.
+ * @return the builder for chaining.
+ * @throws IllegalArgumentException If no such property source exists in the current
+ * chain.
+ */
+ TamayaConfigBuilder highestPriority(ConfigSource propertySource);
+
+ /**
+ * Decreases the priority of the given property source to be minimal, by moving it to
+ * the start of the chain of property source chain. If the property source given is
+ * already the first item this method has no effect. This operation does not change
+ * any ordinal values.
+ *
+ * @param propertySource the property source to be minimized regarding its significance.
+ * @return the builder for chaining.
+ * @throws IllegalArgumentException If no such property source exists in the current
+ * chain.
+ */
+ TamayaConfigBuilder lowestPriority(ConfigSource propertySource);
+
+ /**
+ * Access the current chain of property sources. Items at the end of the list have
+ * precedence/more significance.
+ *
+ * @return the property source chain, never {@code null}.
+ */
+ List<ConfigSource> getSources();
+
+ /**
+ * Sorts the current registered property sources using the given comparator.
+ *
+ * NOTE: property sources at the beginning have minimal significance.
+ *
+ * @param comparator the comparator to be used, not {@code null}.
+ * @return this instance for chaining.
+ */
+ TamayaConfigBuilder sortSources(Comparator<ConfigSource> comparator);
+
+ /**
+ * Adds the given PropertyFilter instances, hereby the instances are added
+ * to the end of the list with highest priority. The ordering of existing
+ * property filters remains unchanged. To sort the property
+ * filters call {@link #sortFilter}.
+ *
+ * @param filters the filters to add
+ * @return this builder, for chaining, never null.
+ */
+ TamayaConfigBuilder withFilters(Filter... filters);
+
+ /**
+ * Adds the given PropertyFilter instances, hereby the instances are added
+ * to the end of the list with highest priority. The ordering of existing
+ * property filters remains unchanged. To sort the property
+ * filters call {@link #sortFilter}.
+ *
+ * @param filters the filters to add
+ * @return this builder, for chaining, never null.
+ */
+ TamayaConfigBuilder withFilters(Collection<Filter> filters);
+
+ /**
+ * Add all registered (default) property filters to the context built.
+ * @return this builder, for chaining, never null.
+ */
+ TamayaConfigBuilder addDiscoveredFilters();
+
+ /**
+ * Removes the given PropertyFilter instances, if existing. The order of the remaining
+ * filters is preserved.
+ *
+ * @param filters the filter to remove
+ * @return this builder, for chaining, never null.
+ */
+ TamayaConfigBuilder removeFilters(Filter... filters);
+
+ /**
+ * Removes the given PropertyFilter instances, if existing. The order of the remaining
+ * filters is preserved.
+ *
+ * @param filters the filter to remove
+ * @return this builder, for chaining, never null.
+ */
+ TamayaConfigBuilder removeFilters(Collection<Filter> filters);
+
+ /**
+ * Access the current chain of property filters. Items at the end of the list have
+ * precedence/more significance.
+ *
+ * @return the property source chain, never {@code null}.
+ */
+ List<Filter> getFilters();
+
+ /**
+ * This method can be used for adding {@link Converter}s.
+ * Converters are added at the end after any existing converters.
+ * For converters already registered for the current target type the
+ * method has no effect.
+ *
+ * @param converters the converters to add for this type
+ * @return this builder, for chaining, never null.
+ */
+ TamayaConfigBuilder withConverters(Collection<Converter<?>> converters);
+
+ /**
+ * This method can be used for adding {@link Converter}s.
+ * Converters are added at the end after any existing converters.
+ * For converters already registered for the current target type the
+ * method has no effect.
+ *
+ * @param typeToConvert the type for which the converters is for
+ * @param converters the converters to add for this type
+ * @param <T> the target type.
+ * @return this builder, for chaining, never null.
+ */
+ <T> TamayaConfigBuilder withConverters(Class<T> typeToConvert, Converter<T>... converters);
+
+ /**
+ * This method can be used for adding {@link Converter}s.
+ * Converters are added at the end after any existing converters.
+ * For converters already registered for the current target type the
+ * method has no effect.
+ *
+ * @param typeToConvert the type for which the converters is for
+ * @param converters the converters to add for this type
+ * @param <T> the target type.
+ * @return this builder, for chaining, never null.
+ */
+ <T> TamayaConfigBuilder withConverters(Class<T> typeToConvert, Collection<Converter<T>> converters);
+
+
+ /**
+ * Removes the given PropertyConverter instances for the given type,
+ * if existing.
+ *
+ * @param typeToConvert the type which the converters is for
+ * @param converters the converters to remove
+ * @param <T> the target type.
+ * @return this builder, for chaining, never null.
+ */
+ <T> TamayaConfigBuilder removeConverters(Class<T> typeToConvert, Converter<T>... converters);
+
+ /**
+ * Removes the given PropertyConverter instances for the given type,
+ * if existing.
+ *
+ * @param typeToConvert the type which the converters is for
+ * @param converters the converters to remove
+ * @param <T> the target type.
+ * @return this builder, for chaining, never null.
+ */
+ <T> TamayaConfigBuilder removeConverters(Class<T> typeToConvert, Collection<Converter<T>> converters);
+
+ /**
+ * Removes all converters for the given type, which actually renders a given type
+ * unsupported for type conversion.
+ *
+ * @param typeToConvert the type which the converters is for
+ * @return this builder, for chaining, never null.
+ */
+ <T> TamayaConfigBuilder removeConverters(Class<T> typeToConvert);
+
+ /**
+ * Access the current registered property converters.
+ *
+ * @return the current registered property converters.
+ */
+ Map<Type, List<Converter>> getConverter();
+
+ /**
+ * Sets the {@link ConfigValueCombinationPolicy} used to evaluate the final
+ * property values.
+ *
+ * @param policy the {@link ConfigValueCombinationPolicy} used, not {@code null}.
+ * @return this builder, for chaining, never null.
+ */
+ TamayaConfigBuilder withPropertyValueCombinationPolicy(ConfigValueCombinationPolicy policy);
+
+ /**
+ * Sorts the current registered property filters using the given comparator.
+ *
+ * NOTE: property filters at the beginning have minimal significance.
+ *
+ * @param comparator the comparator to be used, not {@code null}.
+ * @return this instance for chaining.
+ */
+ TamayaConfigBuilder sortFilter(Comparator<Filter> comparator);
+
+ @Override
+ TamayaConfigBuilder addDefaultSources();
+
+ @Override
+ TamayaConfigBuilder addDiscoveredSources();
+
+ @Override
+ TamayaConfigBuilder addDiscoveredConverters();
+
+ @Override
+ TamayaConfigBuilder forClassLoader(ClassLoader loader);
+
+ @Override
+ TamayaConfigBuilder withSources(ConfigSource... sources);
+
+ @Override
+ TamayaConfigBuilder withConverters(Converter<?>... converters);
+
+ @Override
+ <T> TamayaConfigBuilder withConverter(Class<T> type, Converter<T> converter);
+
+}
+
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/spi/TypeLiteral.java
----------------------------------------------------------------------
diff --git a/code/base/src/main/java/org/apache/tamaya/spi/TypeLiteral.java b/code/base/src/main/java/org/apache/tamaya/spi/TypeLiteral.java
new file mode 100644
index 0000000..0139e85
--- /dev/null
+++ b/code/base/src/main/java/org/apache/tamaya/spi/TypeLiteral.java
@@ -0,0 +1,225 @@
+/*
+ * 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.tamaya.spi;
+
+import java.io.Serializable;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Objects;
+
+
+/**
+ * <p>Class for instantiation of objects that represent parameterized types
+ * with current parameters.</p>
+ *
+ * <p>An object that represents a parameterized type may be obtained by
+ * subclassing <tt>TypeLiteral</tt>.</p>
+ *
+ * <pre>
+ * TypeLiteral<List<Integer>> stringListType = new TypeLiteral<List<Integer>>() {};
+ * </pre>
+ *
+ * @param <T> the type, including all type parameters
+ */
+public class TypeLiteral<T> implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+ private static final Type[] EMPTY_TYPE_ARRAY = new Type[0];
+ /** The current defined type. */
+ private final Type definedType;
+
+ /**
+ * Constructor.
+ * @param definedType the defined type.
+ */
+ public TypeLiteral(Type definedType) {
+ Objects.requireNonNull(definedType, "Type must be given");
+
+ this.definedType = definedType;
+ }
+
+ /**
+ * Constructor only for directly implemeting a TypeLiteral hereby dynamically implementing a generic interface.
+ */
+ public TypeLiteral() {
+ this.definedType = getDefinedType(this.getClass());
+ }
+
+ /**
+ * Creates a new TypeLiteral based on a given type.
+ *
+ * @param type the type , not {@code null}.
+ * @param <R> the literal generic type.
+ * @return the corresponding TypeLiteral, never {@code null}.
+ */
+ public static <R> TypeLiteral<R> of(Type type) {
+ Objects.requireNonNull(type, "Type must be given.");
+
+ return new TypeLiteral<R>(type);
+ }
+
+ /**
+ * Checks the current implemented generic interfaces and evaluates the given single type parameter.
+ *
+ * @param clazz the class to check, not {@code null}.
+ * @param interfaceType the interface type to be checked, not {@code null}.
+ * @return the generic type parameters, or an empty array, if it cannot be evaluated.
+ */
+ public static Type[] getGenericInterfaceTypeParameters(Class<?> clazz, Class<?> interfaceType) {
+ Objects.requireNonNull(clazz, "Class parameter must be given.");
+ Objects.requireNonNull(interfaceType, "Interface parameter must be given.");
+
+ for (Type type : clazz.getGenericInterfaces()) {
+ if (type instanceof ParameterizedType) {
+ ParameterizedType parameterizedType = (ParameterizedType) type;
+ if(parameterizedType.getRawType().equals(interfaceType)){
+ return parameterizedType.getActualTypeArguments();
+ }
+ }
+ }
+ return EMPTY_TYPE_ARRAY;
+ }
+
+ /**
+ * Method that checks the class's type for a generic interface implementation type.
+ *
+ * @param type the type, not {@code null}.
+ * @return the generic type parameter of the given single type generic interfaceType, or an empty array.
+ */
+ public static Type[] getTypeParameters(Type type) {
+ Objects.requireNonNull(type, "Type must be given.");
+
+ if (type instanceof ParameterizedType) {
+ ParameterizedType parameterizedType = (ParameterizedType) type;
+ return parameterizedType.getActualTypeArguments();
+ }
+ return EMPTY_TYPE_ARRAY;
+ }
+
+ public final Type getType() {
+ return definedType;
+ }
+
+ /**
+ * Returns basic raw Java type.
+ *
+ * @return the actual type represented by this object
+ */
+ @SuppressWarnings("unchecked")
+ public final Class<T> getRawType() {
+ Class<T> rawType;
+
+ if (this.definedType instanceof ParameterizedType) {
+ ParameterizedType pt = (ParameterizedType) this.definedType;
+ rawType = (Class<T>) pt.getRawType();
+ } else if (this.definedType instanceof GenericArrayType) {
+ rawType = (Class<T>) Object[].class;
+ } else if (this.definedType instanceof Class) {
+ rawType = (Class<T>) this.definedType;
+ } else {
+ throw new RuntimeException("Illegal type for the Type Literal Class");
+ }
+
+ return rawType;
+ }
+
+ /**
+ * Returns actual type arguments, if present.
+ *
+ * @return the actual type represented by defined class, or an empty array.
+ */
+ public final Type[] getActualTypeArguments() {
+ if (this.definedType instanceof ParameterizedType) {
+ ParameterizedType pt = (ParameterizedType) this.definedType;
+ return pt.getActualTypeArguments();
+ }
+ return new Type[0];
+ }
+
+
+ protected Type getDefinedType(Class<?> clazz) {
+ Type type;
+
+ if (clazz == null) {
+ throw new RuntimeException("Class parameter clazz can not be null");
+ }
+
+ Type superClazz = clazz.getGenericSuperclass();
+
+ if (superClazz instanceof ParameterizedType) {
+ ParameterizedType pt = (ParameterizedType) superClazz;
+ Type[] actualArgs = pt.getActualTypeArguments();
+
+ if (actualArgs.length == 1) {
+ type = actualArgs[0];
+
+ } else {
+ throw new RuntimeException("More than one parametric type");
+ }
+
+ } else if (superClazz.equals(Object.class)) {
+ throw new RuntimeException("Super class must be parametrized type");
+ } else {
+ type = getDefinedType((Class<?>) superClazz);
+ }
+
+ return type;
+ }
+
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((definedType == null) ? 0 : definedType.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ TypeLiteral<?> other = (TypeLiteral<?>) obj;
+ if (definedType == null) {
+ if (other.definedType != null) {
+ return false;
+ }
+ } else if (!definedType.equals(other.definedType)) {
+ return false;
+ }
+ return true;
+ }
+
+
+ @Override
+ public String toString() {
+ return "TypeLiteral{" +
+ "type=" + definedType +
+ '}';
+ }
+
+}