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 2018/12/17 19:54:44 UTC
[incubator-tamaya-sandbox] 01/09: Added experimental documentation
generator tool.
This is an automated email from the ASF dual-hosted git repository.
anatole pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-tamaya-sandbox.git
commit 9f6fda17dfdf7e776c3291e712b0eb89bf1a909b
Author: Anatole Tresch <at...@gmail.com>
AuthorDate: Sat Dec 1 01:34:47 2018 +0100
Added experimental documentation generator tool.
---
documentation/bnd.bnd | 32 +++
documentation/pom.xml | 72 ++++++
.../org/apache/tamaya/doc/ConfigDocumenter.java | 174 ++++++++++++++
.../main/java/org/apache/tamaya/doc/DocFormat.java | 25 ++
.../java/org/apache/tamaya/doc/DocumentedArea.java | 264 +++++++++++++++++++++
.../apache/tamaya/doc/DocumentedConfiguration.java | 186 +++++++++++++++
.../org/apache/tamaya/doc/DocumentedProperty.java | 188 +++++++++++++++
.../apache/tamaya/doc/annot/ConfigAreaSpec.java | 94 ++++++++
.../apache/tamaya/doc/annot/ConfigAreaSpecs.java | 38 +++
.../tamaya/doc/annot/ConfigPropertySpec.java | 63 +++++
.../tamaya/doc/annot/ConfigPropertySpecs.java | 38 +++
.../org/apache/tamaya/doc/annot/ConfigSpec.java | 48 ++++
.../apache/tamaya/doc/formats/HtmlDocFormat.java | 127 ++++++++++
.../apache/tamaya/doc/formats/TextDocFormat.java | 124 ++++++++++
.../apache/tamaya/validation/ConfigValidator.java | 26 ++
.../java/org/apache/tamaya/validation/Finding.java | 54 +++++
.../apache/tamaya/validation/ValidationCheck.java | 194 +++++++++++++++
.../apache/tamaya/validation/ValidationResult.java | 29 +++
documentation/src/main/resources/img/tamaya.png | Bin 0 -> 2015 bytes
.../AnnotBasedStandaloneConfigDocumentation.java | 62 +++++
.../apache/tamaya/doc/AnnotatedDocConfigBean.java | 95 ++++++++
.../apache/tamaya/doc/ConfigDocumenterTest.java | 46 ++++
pom.xml | 1 +
23 files changed, 1980 insertions(+)
diff --git a/documentation/bnd.bnd b/documentation/bnd.bnd
new file mode 100644
index 0000000..ef18c20
--- /dev/null
+++ b/documentation/bnd.bnd
@@ -0,0 +1,32 @@
+-buildpath: \
+ osgi.annotation; version=6.0.0,\
+ osgi.core; version=6.0,\
+ osgi.cmpn; version=6.0
+
+-testpath: \
+ ${junit}
+
+javac.source: 1.8
+javac.target: 1.8
+
+Automatic-Module-Name: org.apache.tamaya.doc
+Bundle-Version: ${version}.${tstamp}
+Bundle-Name: Apache Tamaya - Documentation Generator
+Bundle-SymbolicName: org.apache.tamaya.doc
+Bundle-Description: Apacha Tamaya Configuration - Documentation Generator
+Bundle-Category: Implementation
+Bundle-Copyright: (C) Apache Foundation
+Bundle-License: Apache Licence version 2
+Bundle-Vendor: Apache Software Foundation
+Bundle-ContactAddress: dev-tamaya@incubator.apache.org
+Bundle-DocURL: http://tamaya.apache.org
+Export-Package: \
+ org.apache.tamaya.doc,\
+ org.apache.tamaya.doc.spi
+Import-Package: \
+ org.apache.tamaya,\
+ org.apache.tamaya.spi
+Export-Service: \
+ org.apache.tamaya.doc.spi.ConfigDocumentationMBean,\
+ org.apache.tamaya.doc.spi.ModelProviderSpi
+
diff --git a/documentation/pom.xml b/documentation/pom.xml
new file mode 100644
index 0000000..8ac628f
--- /dev/null
+++ b/documentation/pom.xml
@@ -0,0 +1,72 @@
+<!--
+ ~ 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 createObject 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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.tamaya.ext</groupId>
+ <artifactId>tamaya-sandbox</artifactId>
+ <version>0.4-incubating-SNAPSHOT</version>
+ <relativePath>..</relativePath>
+ </parent>
+
+ <artifactId>tamaya-doc_alpha</artifactId>
+ <name>Apache Tamaya Modules - Documentation</name>
+ <description>This extension module provides functionality to document your configuration options easily.
+ </description>
+ <packaging>jar</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.reflections</groupId>
+ <artifactId>reflections</artifactId>
+ <version>0.9.9-RC1</version>
+ </dependency>
+ <dependency>
+ <groupId>com.j2html</groupId>
+ <artifactId>j2html</artifactId>
+ <version>1.3.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.tamaya</groupId>
+ <artifactId>tamaya-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.tamaya</groupId>
+ <artifactId>tamaya-core</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.tamaya.ext</groupId>
+ <artifactId>tamaya-injection-api</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>java-hamcrest</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/documentation/src/main/java/org/apache/tamaya/doc/ConfigDocumenter.java b/documentation/src/main/java/org/apache/tamaya/doc/ConfigDocumenter.java
new file mode 100644
index 0000000..6c00d16
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/doc/ConfigDocumenter.java
@@ -0,0 +1,174 @@
+/*
+ * 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 createObject 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.doc;
+
+import org.apache.tamaya.doc.annot.*;
+import org.reflections.Reflections;
+import org.reflections.scanners.FieldAnnotationsScanner;
+import org.reflections.scanners.MethodAnnotationsScanner;
+import org.reflections.scanners.TypeAnnotationsScanner;
+import org.reflections.util.ClasspathHelper;
+import org.reflections.util.ConfigurationBuilder;
+import org.reflections.util.FilterBuilder;
+
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Class to read and store the current configuration documentation.
+ */
+public final class ConfigDocumenter {
+
+ private DocumentedConfiguration docs = new DocumentedConfiguration();
+
+ /**
+ * Read documentation from the given classes.
+ * @param clazzes the classes to read, not null.
+ */
+ public void readClasses(Class... clazzes){
+ FilterBuilder filterBuilder = new FilterBuilder();
+ List<URL> urls = new ArrayList<>(clazzes.length);
+ for(Class clazz:clazzes){
+// filterBuilder.exclude(".*");
+ filterBuilder.include(clazz.getName()+".*");
+ urls.add(ClasspathHelper.forClass(clazz));
+ }
+ ConfigurationBuilder configBuilder = new ConfigurationBuilder()
+ .setScanners(new TypeAnnotationsScanner(), new MethodAnnotationsScanner(), new FieldAnnotationsScanner())
+ .setUrls(urls)
+ .filterInputsBy(filterBuilder);
+ Reflections reflections = new Reflections(configBuilder);
+ readSpecs(reflections);
+ }
+
+ /**
+ * Read documentation from the classes managed by the given classloader.
+ * @param classLoader the classloader, not null.
+ */
+ public void readClasses(ClassLoader classLoader){
+ ConfigurationBuilder configBuilder = new ConfigurationBuilder()
+ .setScanners(new TypeAnnotationsScanner(), new MethodAnnotationsScanner(), new FieldAnnotationsScanner())
+ .setUrls(ClasspathHelper.forClassLoader(classLoader));
+ Reflections reflections = new Reflections(configBuilder);
+ readSpecs(reflections);
+ }
+
+ /**
+ * Read documentation from the given packages.
+ * @param packages the package names, not null.
+ */
+ public void readPackages(String... packages) {
+ ConfigurationBuilder configBuilder = new ConfigurationBuilder();
+ FilterBuilder filterBuilder = new FilterBuilder();
+ for (String p : packages) {
+ filterBuilder.includePackage(p);
+ }
+ configBuilder.filterInputsBy(filterBuilder);
+ configBuilder.setUrls(ClasspathHelper.forJavaClassPath());
+ configBuilder.setScanners(new TypeAnnotationsScanner(), new MethodAnnotationsScanner(), new FieldAnnotationsScanner());
+ Reflections reflections = new Reflections(configBuilder);
+ readSpecs(reflections);
+ }
+
+ /**
+ * Access the collected configuration documentation.
+ * @return the documentation, not null.
+ */
+ public DocumentedConfiguration getDocumentation(){
+ return docs;
+ }
+
+ private void readSpecs(Reflections reflections){
+ // types
+ for(Class type:reflections.getTypesAnnotatedWith(ConfigSpec.class)){
+ readConfigSpec(type);
+ }
+ for(Class type:reflections.getTypesAnnotatedWith(ConfigAreaSpec.class)){
+ readAreaSpec(type);
+ }
+ for(Class type:reflections.getTypesAnnotatedWith(ConfigAreaSpecs.class)){
+ readAreaSpecs(type);
+ }
+ for(Class type:reflections.getTypesAnnotatedWith(ConfigPropertySpec.class)){
+ readPropertySpec(type);
+ }
+ for(Class type:reflections.getTypesAnnotatedWith(ConfigPropertySpecs.class)){
+ readPropertySpecs(type);
+ }
+ // Fields
+ for(Field f:reflections.getFieldsAnnotatedWith(ConfigAreaSpec.class)){
+ readAreaSpec(f);
+ }
+ for(Field f:reflections.getFieldsAnnotatedWith(ConfigAreaSpecs.class)){
+ readAreaSpecs(f);
+ }
+ for(Field f:reflections.getFieldsAnnotatedWith(ConfigPropertySpec.class)){
+ readPropertySpec(f);
+ }
+ for(Field f:reflections.getFieldsAnnotatedWith(ConfigPropertySpecs.class)){
+ readPropertySpecs(f);
+ }
+ // Methods
+ for(Method m:reflections.getMethodsAnnotatedWith(ConfigAreaSpec.class)){
+ readAreaSpec(m);
+ }
+ for(Method m:reflections.getMethodsAnnotatedWith(ConfigAreaSpecs.class)){
+ readAreaSpecs(m);
+ }
+ for(Method m:reflections.getMethodsAnnotatedWith(ConfigPropertySpec.class)){
+ readPropertySpec(m);
+ }
+ for(Method m:reflections.getMethodsAnnotatedWith(ConfigPropertySpecs.class)){
+ readPropertySpecs(m);
+ }
+ }
+
+ private void readConfigSpec(Class type){
+ docs.init((ConfigSpec)type.getAnnotation(ConfigSpec.class), type);
+ }
+
+ private void readAreaSpecs(AnnotatedElement elem){
+ ConfigAreaSpecs areaSpecs = elem.getAnnotation(ConfigAreaSpecs.class);
+ for (ConfigAreaSpec areaSpec:areaSpecs.value()){
+ docs.addGroup(new DocumentedArea(areaSpec, elem));
+ }
+ }
+
+ private void readPropertySpecs(AnnotatedElement elem){
+ ConfigPropertySpecs propertySpecs = elem.getAnnotation(ConfigPropertySpecs.class);
+ for (ConfigPropertySpec propertySpec:propertySpecs.value()){
+ docs.addProperty(new DocumentedProperty(propertySpec, elem));
+ }
+ }
+
+ private void readAreaSpec(AnnotatedElement elem){
+ docs.addGroup(new DocumentedArea(elem.getAnnotation(ConfigAreaSpec.class), elem));
+ }
+
+ private void readPropertySpec(AnnotatedElement elem){
+ docs.addProperty(new DocumentedProperty(elem.getAnnotation(ConfigPropertySpec.class), elem));
+ }
+
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/doc/DocFormat.java b/documentation/src/main/java/org/apache/tamaya/doc/DocFormat.java
new file mode 100644
index 0000000..0f5e14a
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/doc/DocFormat.java
@@ -0,0 +1,25 @@
+/*
+ * 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 createObject 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.doc;
+
+import java.util.function.Function;
+
+public interface DocFormat<T> extends Function<DocumentedConfiguration, T> {
+
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/doc/DocumentedArea.java b/documentation/src/main/java/org/apache/tamaya/doc/DocumentedArea.java
new file mode 100644
index 0000000..e2215b4
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/doc/DocumentedArea.java
@@ -0,0 +1,264 @@
+/*
+ * 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 createObject 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.doc;
+
+import org.apache.tamaya.doc.annot.ConfigAreaSpec;
+import org.apache.tamaya.doc.annot.ConfigPropertySpec;
+import org.apache.tamaya.inject.api.ConfigDefaultSections;
+import org.apache.tamaya.inject.spi.InjectionUtils;
+import org.apache.tamaya.spi.PropertyValue;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+public final class DocumentedArea {
+
+ private final AnnotatedElement owner;
+ private ConfigAreaSpec configArea;
+ private String path;
+ private String description;
+ private PropertyValue.ValueType groupType;
+ private Map<String, DocumentedProperty> properties = new HashMap<>();
+ private Map<String, DocumentedArea> areas = new HashMap<>();
+ private Class<?> valueType;
+
+ private int minCardinality;
+ private int maxCardinality;
+ private Set<DocumentedArea> dependsOnGroups = new TreeSet<>();
+ private Set<DocumentedProperty> dependsOnProperties = new TreeSet<>();
+
+ public DocumentedArea(ConfigAreaSpec areaSpec, AnnotatedElement owner){
+ this.owner = owner;
+ this.configArea = areaSpec;
+ if(!areaSpec.path().isEmpty()) {
+ this.path = areaSpec.path();
+ }else{
+ this.path = evaluatePath(owner);
+ }
+ if(!areaSpec.description().isEmpty()) {
+ this.description = areaSpec.description();
+ }
+ this.groupType = areaSpec.areaType();
+ if(areaSpec.max()>0) {
+ this.maxCardinality = areaSpec.max();
+ }
+ if(areaSpec.min()>0) {
+ this.minCardinality = areaSpec.min();
+ }
+ if(Object.class!=areaSpec.valueType()) {
+ this.valueType = areaSpec.valueType();
+ }else{
+ if(owner instanceof Class){
+ this.valueType = ((Class)owner);
+ }
+ }
+ for(ConfigPropertySpec ps:areaSpec.properties()) {
+ this.properties.put(ps.name(), new DocumentedProperty(ps, owner));
+ }
+ }
+
+ private String evaluatePath(AnnotatedElement owner) {
+ if(owner instanceof Field) {
+ return String.join(", ", InjectionUtils.getKeys((Field) owner));
+ }else if(owner instanceof Method) {
+ return String.join(", ", InjectionUtils.getKeys((Method) owner));
+ }else if(owner instanceof Class) {
+ ConfigDefaultSections sectionsAnnot = owner.getAnnotation(ConfigDefaultSections.class);
+ if(sectionsAnnot!=null){
+ return String.join(", ", sectionsAnnot.value());
+ }
+ return ((Class)owner).getName()+", "+((Class)owner).getSimpleName();
+ }
+ return "<root>";
+ }
+
+ void resolve(DocumentedConfiguration documentation){
+ if(configArea !=null){
+ for(String key: configArea.dependsOnAreas()){
+ this.dependsOnGroups.add(documentation.getGroup(key));
+ }
+ for(String key: configArea.dependsOnProperties()){
+ this.dependsOnProperties.add(documentation.getProperty(key));
+ }
+ }
+ }
+
+ public DocumentedArea addGroup(DocumentedArea group){
+ this.areas.put(group.path, group);
+ return this;
+ }
+
+ public DocumentedArea addProperty(DocumentedProperty property){
+ this.properties.put(property.getName(), property);
+ return this;
+ }
+
+ public AnnotatedElement getOwner() {
+ return owner;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public PropertyValue.ValueType getGroupType() {
+ return groupType;
+ }
+
+ public Map<String, DocumentedProperty> getProperties() {
+ return properties;
+ }
+
+ public List<DocumentedProperty> getPropertiesSorted() {
+ List<DocumentedProperty> result = new ArrayList<>(properties.values());
+ result.sort(Comparator.comparing(DocumentedProperty::getName));
+ return result;
+ }
+
+ public Map<String, DocumentedArea> getAreas() {
+ return areas;
+ }
+
+ public List<DocumentedArea> getAreasSorted() {
+ List<DocumentedArea> result = new ArrayList<>(areas.values());
+ result.sort(Comparator.comparing(DocumentedArea::getPath));
+ return result;
+ }
+
+ public Class<?> getValueType() {
+ return valueType;
+ }
+
+ public int getMinCardinality() {
+ return minCardinality;
+ }
+
+ public int getMaxCardinality() {
+ return maxCardinality;
+ }
+
+ public List<DocumentedArea> getDependsOnAreas() {
+ List<DocumentedArea> result = new ArrayList<>(dependsOnGroups);
+ result.sort(Comparator.comparing(DocumentedArea::getPath));
+ return result;
+ }
+
+ public List<DocumentedProperty> getDependsOnProperties() {
+ List<DocumentedProperty> result = new ArrayList<>(dependsOnProperties);
+ result.sort(Comparator.comparing(DocumentedProperty::getName));
+ return result;
+ }
+
+ public DocumentedArea path(String path) {
+ this.path = path;
+ return this;
+ }
+
+ public DocumentedArea description(String description) {
+ this.description = description;
+ return this;
+ }
+
+ public DocumentedArea groupType(PropertyValue.ValueType groupType) {
+ this.groupType = groupType;
+ return this;
+ }
+
+ public DocumentedArea properties(Map<String, DocumentedProperty> properties) {
+ this.properties = properties;
+ return this;
+ }
+
+ public DocumentedArea groups(Map<String, DocumentedArea> groups) {
+ this.areas = groups;
+ return this;
+ }
+
+ public DocumentedArea valueType(Class<?> valueType) {
+ this.valueType = valueType;
+ return this;
+ }
+
+ public DocumentedArea minCardinality(int minCardinality) {
+ this.minCardinality = minCardinality;
+ return this;
+ }
+
+ public DocumentedArea maxCardinality(int maxCardinality) {
+ this.maxCardinality = maxCardinality;
+ return this;
+ }
+
+ public DocumentedArea dependsOnGroups(Set<DocumentedArea> dependsOnGroups) {
+ this.dependsOnGroups = dependsOnGroups;
+ return this;
+ }
+
+ public DocumentedArea dependsOnProperties(Set<DocumentedProperty> dependsOnProperties) {
+ this.dependsOnProperties = dependsOnProperties;
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ DocumentedArea that = (DocumentedArea) o;
+
+ return Objects.equals(this.dependsOnGroups, that.dependsOnGroups) &&
+ Objects.equals(this.dependsOnProperties, that.dependsOnProperties) &&
+ Objects.equals(this.description, that.description) &&
+ Objects.equals(this.areas, that.areas) &&
+ Objects.equals(this.groupType, that.groupType) &&
+ Objects.equals(this.maxCardinality, that.maxCardinality) &&
+ Objects.equals(this.minCardinality, that.minCardinality) &&
+ Objects.equals(this.path, that.path) &&
+ Objects.equals(this.properties, that.properties) &&
+ Objects.equals(this.valueType, that.valueType);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(dependsOnGroups, dependsOnProperties, description, areas, groupType, maxCardinality,
+ minCardinality, path, properties, valueType);
+ }
+
+ @Override
+ public String toString() {
+ return "ConfigGroup{" +
+ "path='" + path + '\'' +
+ ", description='" + description + '\'' +
+ ", areaType=" + groupType +
+ ", properties=" + properties +
+ ", areas=" + areas +
+ ", valueType=" + valueType +
+ ", minCardinality=" + minCardinality +
+ ", maxCardinality=" + maxCardinality +
+ ", dependsOnAreas=" + dependsOnGroups +
+ ", dependsOnProperties=" + dependsOnProperties +
+ '}';
+ }
+
+
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/doc/DocumentedConfiguration.java b/documentation/src/main/java/org/apache/tamaya/doc/DocumentedConfiguration.java
new file mode 100644
index 0000000..2596d84
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/doc/DocumentedConfiguration.java
@@ -0,0 +1,186 @@
+/*
+ * 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 createObject 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.doc;
+
+import org.apache.tamaya.doc.annot.ConfigSpec;
+
+import java.util.*;
+
+/**
+ * Documentation of an application configuration.
+ */
+public final class DocumentedConfiguration {
+
+ private String name;
+ private String version;
+ private Class ownerClass;
+
+ private Map<String, DocumentedProperty> properties = new TreeMap<>();
+ private Map<String, DocumentedArea> groups = new TreeMap<>();
+
+ /**
+ * Creates a new empty configuration documentation.
+ */
+ public DocumentedConfiguration(){}
+
+ /**
+ * Creates a new configuration documentation and initializes it with the values from the annotation given.
+ * @param annotation the spec annotation, not null.
+ */
+ public DocumentedConfiguration(ConfigSpec annotation){
+ init(annotation, null);
+ }
+
+ /**
+ * Initializes the instance with the given annotation.
+ * @param annotation the annotation , not null.
+ * @param ownerClass the annotaed class.
+ */
+ public void init(ConfigSpec annotation, Class ownerClass) {
+ this.name = annotation.name();
+ this.version = annotation.version();
+ this.ownerClass = ownerClass;
+ }
+
+ /**
+ * Get the current configuration name.
+ * @return the name, or '<undefined>'.
+ */
+ public String getName() {
+ if(name==null){
+ return "<undefined>";
+ }
+ return name;
+ }
+
+ /**
+ * Get the current configuration version.
+ * @return the version, or '<undefined>'.
+ */
+ public String getVersion() {
+ if(version==null){
+ return "<undefined>";
+ }
+ return version;
+ }
+
+ /**
+ * Get the annotated class.
+ * @return the class, or null.
+ */
+ public Class getOwnerClass() {
+ return ownerClass;
+ }
+
+ /**
+ * Get the documented properties.
+ * @return the properties, never null.
+ */
+ public Map<String, DocumentedProperty> getProperties() {
+ return properties;
+ }
+
+ /**
+ * Get the documented properties.
+ * @return the properties, never null.
+ */
+ public List<DocumentedArea> getAllAreasSorted() {
+ List<DocumentedArea> areas = new ArrayList<>();
+ areas.addAll(getAreas().values());
+ areas.sort(this::compareAreas);
+ return areas;
+ }
+
+ private int compareAreas(DocumentedArea area1, DocumentedArea area2) {
+ return area1.getPath().compareTo(area2.getPath());
+ }
+
+ /**
+ * Get the documented properties.
+ * @return the properties, never null.
+ */
+ public List<DocumentedProperty> getAllPropertiesSorted() {
+ List<DocumentedProperty> props = new ArrayList<>();
+ props.addAll(getProperties().values());
+ for(DocumentedArea area:getAreas().values()) {
+ props.addAll(area.getProperties().values());
+ }
+ props.sort(this::compareProperties);
+ return props;
+ }
+
+ private int compareProperties(DocumentedProperty property, DocumentedProperty property1) {
+ return property.getName().compareTo(property1.getName());
+ }
+
+ /**
+ * Get the documented areas.
+ * @return the areas, never null.
+ */
+ public Map<String, DocumentedArea> getAreas() {
+ return groups;
+ }
+
+ public DocumentedArea getGroup(String path) {
+ return null;
+ }
+
+ public DocumentedProperty getProperty(String path) {
+ return null;
+ }
+
+ public DocumentedConfiguration addProperty(DocumentedProperty property){
+ this.properties.put(property.getName(), property);
+ return this;
+ }
+
+ public DocumentedConfiguration addGroup(DocumentedArea group){
+ this.groups.put(group.getPath(), group);
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof DocumentedConfiguration)) return false;
+
+ DocumentedConfiguration that = (DocumentedConfiguration) o;
+
+ if (!name.equals(that.name)) return false;
+ return version.equals(that.version);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = name.hashCode();
+ result = 31 * result + version.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "DocumentedConfiguration{" +
+ "name='" + name + '\'' +
+ ", version='" + version + '\'' +
+ ", properties=" + properties +
+ ", groups=" + groups +
+ '}';
+ }
+
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/doc/DocumentedProperty.java b/documentation/src/main/java/org/apache/tamaya/doc/DocumentedProperty.java
new file mode 100644
index 0000000..b270aea
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/doc/DocumentedProperty.java
@@ -0,0 +1,188 @@
+/*
+ * 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 createObject 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.doc;
+
+import org.apache.tamaya.doc.annot.ConfigPropertySpec;
+import org.apache.tamaya.inject.api.Config;
+import org.apache.tamaya.inject.spi.InjectionUtils;
+
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.*;
+
+public final class DocumentedProperty {
+
+ private final AnnotatedElement owner;
+ private ConfigPropertySpec propertySpec;
+ private String name;
+ private String defaultValue;
+ private boolean required;
+ private String description;
+ private Class<?> valueType;
+
+ private Set<DocumentedArea> dependsOnGroups = new TreeSet<>();
+ private Set<DocumentedProperty> dependsOnProperties = new TreeSet<>();
+
+
+ public DocumentedProperty(ConfigPropertySpec annot, AnnotatedElement owner){
+ this.owner = owner;
+ this.propertySpec = annot;
+ if(!annot.name().isEmpty()) {
+ this.name = annot.name();
+ }else{
+ if(owner instanceof Field) {
+ this.name = String.join(", ", InjectionUtils.getKeys((Field) owner));
+ }else if(owner instanceof Method) {
+ this.name = String.join(", ", InjectionUtils.getKeys((Method) owner));
+ }
+ }
+ this.description = annot.description();
+ this.valueType = annot.valueType();
+ if(String.class.equals(this.valueType)){
+ if(owner instanceof Field){
+ Field f = (Field)owner;
+ this.valueType = f.getType();
+ }else if(owner instanceof Method){
+ Method m = (Method)owner;
+ this.valueType = m.getParameterTypes()[0];
+ }
+ }
+ Config configAnnot = owner.getAnnotation(Config.class);
+ if(configAnnot!=null){
+ this.required = configAnnot.required();
+ this.defaultValue = configAnnot.defaultValue();
+ }
+ }
+
+ void resolve(DocumentedConfiguration documentation){
+ if(propertySpec !=null){
+ for(String key: propertySpec.dependsOnAreas()){
+ this.dependsOnGroups.add(documentation.getGroup(key));
+ }
+ for(String key: propertySpec.dependsOnProperties()){
+ this.dependsOnProperties.add(documentation.getProperty(key));
+ }
+ }
+ }
+
+ public AnnotatedElement getOwner() {
+ return owner;
+ }
+
+ public String getDefaultValue() {
+ if(defaultValue==null){
+ return "";
+ }
+ return defaultValue;
+ }
+
+ public boolean isRequired() {
+ return required;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getDescription() {
+ if(description==null){
+ return "";
+ }
+ return description;
+ }
+
+ public Class<?> getValueType() {
+ return valueType;
+ }
+
+ public Set<DocumentedArea> getDependsOnGroups() {
+ return dependsOnGroups;
+ }
+
+ public Set<DocumentedProperty> getDependsOnProperties() {
+ return dependsOnProperties;
+ }
+
+ public DocumentedProperty path(String path) {
+ this.name = path;
+ return this;
+ }
+
+ public DocumentedProperty description(String description) {
+ this.description = description;
+ return this;
+ }
+
+ public DocumentedProperty valueType(Class<?> valueType) {
+ this.valueType = valueType;
+ return this;
+ }
+
+ public DocumentedProperty dependsOnGroups(Collection<DocumentedArea> dependsOnGroups) {
+ this.dependsOnGroups.addAll(dependsOnGroups);
+ return this;
+ }
+
+ public DocumentedProperty dependsOnGroups(DocumentedArea... dependsOnGroups) {
+ this.dependsOnGroups.addAll(Arrays.asList(dependsOnGroups));
+ return this;
+ }
+
+ public DocumentedProperty dependsOnProperties(Collection<DocumentedProperty> dependsOnProperties) {
+ this.dependsOnProperties.addAll(dependsOnProperties);
+ return this;
+ }
+
+ public DocumentedProperty dependsOnProperties(DocumentedProperty... dependsOnProperties) {
+ this.dependsOnProperties.addAll(Arrays.asList(dependsOnProperties));
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ DocumentedProperty that = (DocumentedProperty) o;
+
+ return Objects.equals(this.dependsOnGroups, that.dependsOnGroups) &&
+ Objects.equals(this.dependsOnProperties, that.dependsOnProperties) &&
+ Objects.equals(this.description, that.description) &&
+ Objects.equals(this.name, that.name) &&
+ Objects.equals(this.valueType, that.valueType);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(dependsOnGroups, dependsOnProperties, description, name, valueType);
+ }
+
+ @Override
+ public String toString() {
+ return "ConfigGroup{" +
+ "name='" + name + '\'' +
+ ", description='" + description + '\'' +
+ ", valueType=" + valueType +
+ ", dependsOnAreas=" + dependsOnGroups +
+ ", dependsOnProperties=" + dependsOnProperties +
+ '}';
+ }
+
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigAreaSpec.java b/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigAreaSpec.java
new file mode 100644
index 0000000..589cc6c
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigAreaSpec.java
@@ -0,0 +1,94 @@
+/*
+ * 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 createObject 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.doc.annot;
+
+import org.apache.tamaya.spi.PropertyValue;
+
+import java.lang.annotation.*;
+
+/**
+ * This annotation allows to specify a configuration area.
+ */
+@Repeatable(ConfigAreaSpecs.class)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
+public @interface ConfigAreaSpec {
+ /**
+ * The areas configuration area path, e.g. {@code foo.bar}. An empty String means the root
+ * of the configuration tree.
+ * @return the path, not null.
+ */
+ String path()default"";
+
+ /**
+ * Define a description of the area.
+ * @return the description.
+ */
+ String description() default "";
+
+ /**
+ * Defines the value type that is represented by a given area path, e.g. all properties on level
+ * {@code foo.bar.Server} are mapped to some client classes {@code a.b.ServerConfig}.
+ * @return the type mapping, if any.
+ */
+ Class<?> valueType() default Object.class;
+
+ /**
+ * Allows to specifiy the node type of an area:
+ * <ul>
+ * <li><b>ARRAY: </b> is an array of child items.</li>
+ * <li><b>MAP: </b> is an map of named child items.</li>
+ * </ul>
+ * @return the area group type, default is {@link org.apache.tamaya.spi.PropertyValue.ValueType#MAP}.
+ */
+ PropertyValue.ValueType areaType() default PropertyValue.ValueType.MAP;
+
+ /**
+ * The minimal cardinality required. A cardinality > 0 means that the corresponding area must be present
+ * in your configuration. This can be ensured/checked by a configuration validation system.
+ * @return the minimal cardinality.
+ */
+ int min() default 0;
+
+ /**
+ * The maximal cardinality allowed. A cardinality > 0 means that the corresponding area must not be present
+ * in your configuration more than the configured times. This can be ensured/checked by a configuration validation
+ * system.
+ * @return the maximal cardinality.
+ */
+ int max() default 0;
+
+ /**
+ * The properties managed in this area.
+ * @return the properties managed within this area.
+ */
+ ConfigPropertySpec[] properties() default {};
+
+ /**
+ * Allows to define that this area is only required, if any of these configured areas are present in your config.
+ * @return the area dependencies. This can be ensured/checked by a configuration validation
+ */
+ String[] dependsOnAreas() default {};
+
+ /**
+ * Allows to define that this area is only required, if any of these configured properties are present in your config.
+ * @return the area dependencies. This can be ensured/checked by a configuration validation
+ */
+ String[] dependsOnProperties() default {};
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigAreaSpecs.java b/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigAreaSpecs.java
new file mode 100644
index 0000000..5735c16
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigAreaSpecs.java
@@ -0,0 +1,38 @@
+/*
+ * 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 createObject 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.doc.annot;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This is the container annotation for the repeatable {@link ConfigAreaSpec} annotation.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
+public @interface ConfigAreaSpecs {
+
+ /**
+ * The contained child annotations.
+ * @return the contained annotations.
+ */
+ ConfigAreaSpec[] value()default{};
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigPropertySpec.java b/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigPropertySpec.java
new file mode 100644
index 0000000..0e77e05
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigPropertySpec.java
@@ -0,0 +1,63 @@
+/*
+ * 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 createObject 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.doc.annot;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
+public @interface ConfigPropertySpec {
+
+// boolean id() default false;
+
+ /**
+ * The property name. The full property key is a combination of the parent
+ * @return
+ */
+ String name() default "";
+
+ /**
+ * Define a description of the property.
+ * @return the description.
+ */
+ String description() default "";
+
+ /**
+ * The property's value type, by default {@link String}.
+ * @return the property's value type.
+ */
+ Class<?> valueType() default String.class;
+
+ /**
+ * Allows to define that this property is only required, if any of these configured areas are present in your config.
+ * @return the area dependencies. This can be ensured/checked by a configuration validation
+ */
+ String[] dependsOnAreas() default {};
+
+ /**
+ * Allows to define that this property is only required, if any of these configured properties are present in your config.
+ * @return the area dependencies. This can be ensured/checked by a configuration validation
+ */
+ String[] dependsOnProperties() default {};
+
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigPropertySpecs.java b/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigPropertySpecs.java
new file mode 100644
index 0000000..a8fd4f0
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigPropertySpecs.java
@@ -0,0 +1,38 @@
+/*
+ * 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 createObject 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.doc.annot;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This is the container annotation for the repeatable {@link ConfigPropertySpec} annotation.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
+public @interface ConfigPropertySpecs {
+
+ /**
+ * The contained child annotations.
+ * @return the contained annotations.
+ */
+ ConfigPropertySpec[] value()default{};
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigSpec.java b/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigSpec.java
new file mode 100644
index 0000000..5f0c798
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/doc/annot/ConfigSpec.java
@@ -0,0 +1,48 @@
+/*
+ * 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 createObject 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.doc.annot;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to specify the main configuration properties.
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ConfigSpec {
+ String name() default "";
+ String version() default "";
+
+ /**
+ * Define a description of the configuration.
+ * @return the description.
+ */
+ String description() default "";
+
+ /**
+ * Should loading of the configuration fail, if a validation failed, default is {@code false}, where
+ * a warning is logged only.
+ * @return true to fail config load on configuration validation failures.
+ */
+ boolean failOnErrors() default false;
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/doc/formats/HtmlDocFormat.java b/documentation/src/main/java/org/apache/tamaya/doc/formats/HtmlDocFormat.java
new file mode 100644
index 0000000..b37bf80
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/doc/formats/HtmlDocFormat.java
@@ -0,0 +1,127 @@
+/*
+ * 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 createObject 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.doc.formats;
+
+import j2html.tags.ContainerTag;
+import org.apache.tamaya.doc.DocFormat;
+import org.apache.tamaya.doc.DocumentedArea;
+import org.apache.tamaya.doc.DocumentedConfiguration;
+import org.apache.tamaya.doc.DocumentedProperty;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static j2html.TagCreator.*;
+
+public class HtmlDocFormat implements DocFormat<String> {
+ @Override
+ public String apply(DocumentedConfiguration documentedConfiguration) {
+ List<ContainerTag> areaTags = new ArrayList<>();
+ for(DocumentedArea area:documentedConfiguration.getAllAreasSorted()) {
+ areaTags.addAll(createAreaEntries(area, null));
+ }
+ ContainerTag propertiesTable = createPropertiesTable(documentedConfiguration.getAllPropertiesSorted(), null);
+
+ ContainerTag head = createHead(documentedConfiguration);
+ ContainerTag body = body()
+ .with(
+ h1("Configuration Documentation (" + documentedConfiguration.getName() + ")"),
+ p("Version: " + documentedConfiguration.getVersion()),
+ h2("Documented Areas")).with(areaTags)
+ .with(
+ h2("Documented Properties"),
+ propertiesTable);
+ String result = html(head, body).render();
+ writeResultToFile(result);
+ return result;
+ }
+
+ private void writeResultToFile(String result) {
+ File file = new File("./doc.html");
+ FileWriter w;
+ try {
+ w = new FileWriter(file);
+ w.append(result);
+ w.flush();
+ w.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private ContainerTag createHead(DocumentedConfiguration config) {
+ return head(title("Tamaya Configuration - " + config.getName() + " " +
+ config.getVersion()),
+ meta().withCharset("utf-8"),
+ meta().withName("viewport").withContent("width=device-width, initial-scale=0.9, shrink-to-fit=yes"),
+ link().withRel("stylesheet")
+ .withHref("https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css")
+ .attr("integrity","sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm")
+ .attr("crossorigin","anonymous"));
+ }
+
+ private ContainerTag createPropertiesTable(List<DocumentedProperty> properties, String parentArea) {
+ List<ContainerTag> propertyRows = new ArrayList<>();
+ for (DocumentedProperty prop : properties) {
+ propertyRows.add(tr(
+ td(pre(prop.getName().replace(", ", "\n"))).attr("scope","row"),
+ td(Object.class==prop.getValueType()?"":prop.getValueType().getName()),
+ td(prop.getDescription()),
+ td(i(prop.getDefaultValue()))
+ ));
+ }
+ ContainerTag propertiesTable = table(
+ thead(tr(
+ th("Key(s)").attr("scope","col"),
+ th("Valuetype").attr("scope","col"),
+ th("Description").attr("width", "75%").attr("scope","col"),
+ th("Default").attr("scope","col").attr("width", "15%")
+ )).withClass("thead-dark")
+ ).with(propertyRows.toArray(new ContainerTag[propertyRows.size()]))
+ .withClass("table table-striped table-hover table-bordered table-sm")
+ .attr("width", "90%");
+ return propertiesTable;
+ }
+
+ private List<ContainerTag> createAreaEntries(DocumentedArea area, String parentArea) {
+ List<ContainerTag> result = new ArrayList<>();
+ if(parentArea==null){
+ result.add(h4(area.getPath()));
+ }else{
+ result.add(h4(parentArea + "."+ area.getPath()));
+ }
+ result.add(ul(
+ li(b("Group Type: "), text(area.getGroupType().toString()),
+ li(b("Valuetype: "), text(area.getValueType().getName()))
+ .withCondHidden(Object.class==area.getValueType()),
+ li(b("Description: "), text(area.getDescription()))
+ .withCondHidden(area.getDescription()==null),
+ li(b("Properties: ")).with(createPropertiesTable(area.getPropertiesSorted(), area.getPath()))
+ .withCondHidden(area.getProperties().isEmpty())
+ )));
+ for(DocumentedArea subArea:area.getAreasSorted()){
+ result.addAll(createAreaEntries(subArea, area.getPath()));
+ }
+ return result;
+ }
+
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/doc/formats/TextDocFormat.java b/documentation/src/main/java/org/apache/tamaya/doc/formats/TextDocFormat.java
new file mode 100644
index 0000000..4172cb9
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/doc/formats/TextDocFormat.java
@@ -0,0 +1,124 @@
+/*
+ * 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 createObject 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.doc.formats;
+
+import org.apache.tamaya.doc.DocFormat;
+import org.apache.tamaya.doc.DocumentedConfiguration;
+import org.apache.tamaya.doc.DocumentedArea;
+import org.apache.tamaya.doc.DocumentedProperty;
+
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class TextDocFormat implements DocFormat<String> {
+ @Override
+ public String apply(DocumentedConfiguration documentedConfiguration) {
+ StringBuilder b = new StringBuilder();
+ b.append("Configuration:\n");
+ b.append(" Spec : ").append(documentedConfiguration.getName()).append('\n');
+ b.append(" Version : ").append(documentedConfiguration.getVersion()).append('\n');
+ if(documentedConfiguration.getOwnerClass()!=null){
+ b.append(" Owner : ").append(documentedConfiguration.getOwnerClass().getName()).append('\n');
+ }
+ if(!documentedConfiguration.getAreas().isEmpty()) {
+ b.append("Areas: ").append('\n');
+ for (DocumentedArea area : documentedConfiguration.getAreas().values()) {
+ printArea(" ", area, b);
+ }
+ }
+ if(!documentedConfiguration.getProperties().isEmpty()) {
+ b.append("Properties: ").append('\n');
+ for (DocumentedProperty prop : documentedConfiguration.getProperties().values()) {
+ printProperty(" ", prop, b);
+ }
+ }
+ return b.toString();
+ }
+
+ private void printArea(String inset, DocumentedArea area, StringBuilder b) {
+ if(!area.getPath().isEmpty()) {
+ b.append(inset).append("- name : ").append(area.getPath()).append("\n");
+ }else{
+ b.append(inset).append("- name : NONE\n");
+ }
+// b.append(inset). append(" Type : area\n");
+ if(area.getOwner()!=null){
+ b.append(inset).append(" Owner : ").append(printOwner(area.getOwner())).append('\n');
+ }
+ if(area.getDescription()!=null) {
+ b.append(inset).append(" Descr : ").append(area.getDescription()).append('\n');
+ }
+ b.append(inset). append(" Areatype : ").append(area.getGroupType()).append('\n');
+ if(area.getMinCardinality()!=0) {
+ b.append(inset).append(" Min : ").append(area.getMinCardinality()).append('\n');
+ }
+ if(area.getMaxCardinality()!=0) {
+ b.append(inset).append(" Max : ").append(area.getMaxCardinality()).append('\n');
+ }
+ if(area.getValueType()!=Object.class) {
+ b.append(inset).append(" Value : ").append(area.getValueType().getName()).append('\n');
+ }
+ if(!area.getProperties().isEmpty()) {
+ b.append(inset).append(" Properties : ").append('\n');
+ for (DocumentedProperty prop : area.getProperties().values()) {
+ printProperty(inset + " ", prop, b);
+ }
+ }
+ if(!area.getAreas().isEmpty()) {
+ b.append(inset).append(" Areas : ").append('\n');
+ for (DocumentedArea childArea : area.getAreas().values()) {
+ printArea(inset + " ", childArea, b);
+ }
+ }
+ }
+
+ private void printProperty(String inset, DocumentedProperty prop, StringBuilder b) {
+ b.append(inset).append("- Name : ").append(prop.getName()).append("\n");
+// b.append(inset).append(" Type : property\n");
+ if(prop.getOwner()!=null){
+ b.append(inset).append(" Owner : ").append(printOwner(prop.getOwner())).append('\n');
+ }
+ if(prop.getDescription()!=null) {
+ b.append(inset).append(" Descr : ").append(prop.getDescription()).append('\n');
+ }
+ b.append(inset).append(" Value : ").append(prop.getValueType().getName()).append('\n');
+ }
+
+ private String printOwner(AnnotatedElement owner) {
+ if(owner instanceof Type){
+ return ((Type)owner).getTypeName();
+ }else if(owner instanceof Field){
+ Field f = (Field)owner;
+ return f.getDeclaringClass().getName()+ '#' + f.getName()+": " + f.getType().getName();
+ }
+ else if(owner instanceof Method){
+ Method m = (Method)owner;
+ return m.getDeclaringClass().getName()+ '#' + m.getName()+
+ "("+String.join(", ", Stream.of(m.getParameterTypes()).map(c -> c.getName())
+ .collect(Collectors.toList())) + "): " +
+ m.getReturnType().getName();
+ }else{
+ return String.valueOf(owner);
+ }
+ }
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/validation/ConfigValidator.java b/documentation/src/main/java/org/apache/tamaya/validation/ConfigValidator.java
new file mode 100644
index 0000000..9a6b548
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/validation/ConfigValidator.java
@@ -0,0 +1,26 @@
+/*
+ * 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 createObject 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.validation;
+
+import org.apache.tamaya.Configuration;
+
+import java.util.function.Function;
+
+public interface ConfigValidator extends Function<Configuration, ValidationResult> {
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/validation/Finding.java b/documentation/src/main/java/org/apache/tamaya/validation/Finding.java
new file mode 100644
index 0000000..d75c677
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/validation/Finding.java
@@ -0,0 +1,54 @@
+/*
+ * 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 createObject 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.validation;
+
+/**
+ * Enum type describing the different validation results supported.
+ */
+public enum Finding {
+ /**
+ * The validated item is valid
+ */
+ VALID,
+ /**
+ * The validated item is deprecated.
+ */
+ DEPRECATED,
+ /**
+ * The validated item is correct, but the createValue is worth a warning.
+ */
+ WARNING,
+ /**
+ * A required parameter or section is missing.
+ */
+ MISSING,
+ /**
+ * The validated item has an invalid createValue.
+ */
+ ERROR;
+
+ /**
+ * Method to quickly evaluate if the current state is an error state.
+ *
+ * @return true, if the state is not ERROR or MISSING.
+ */
+ boolean isError() {
+ return this.ordinal() == MISSING.ordinal() || this.ordinal() == ERROR.ordinal();
+ }
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/validation/ValidationCheck.java b/documentation/src/main/java/org/apache/tamaya/validation/ValidationCheck.java
new file mode 100644
index 0000000..2fb40a8
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/validation/ValidationCheck.java
@@ -0,0 +1,194 @@
+/*
+ * 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 createObject 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.validation;
+
+import org.apache.tamaya.doc.annot.ConfigAreaSpec;
+import org.apache.tamaya.doc.annot.ConfigPropertySpec;
+
+import java.lang.annotation.Annotation;
+import java.util.Objects;
+
+/**
+ * Models a partial configuration configModel result.
+ */
+public final class ValidationCheck {
+ /**
+ * The configModel result.
+ */
+ private final Finding result;
+ /**
+ * The configModel message.
+ */
+ private final String message;
+
+ private final Annotation spec;
+
+ /**
+ * Get the checks annotation.
+ * @return the annotation.
+ */
+ public Class getSpecType(){
+ return spec.getClass();
+ }
+
+ /**
+ * Creates a new ValidationResult.
+ *
+ * @param spec the specification item, not null.
+ * @return a new validation result containing valid parts createObject the given model.
+ */
+ public static ValidationCheck createValid(Annotation spec) {
+ return new ValidationCheck(spec, Finding.VALID, null);
+ }
+
+ /**
+ * Creates a new ValidationResult.
+ *
+ * @param spec the validated specification, not null.
+ * @return a new validation result containing missing parts createObject the given model.
+ */
+ public static ValidationCheck createMissing(Annotation spec) {
+ return new ValidationCheck(spec, Finding.MISSING, null);
+ }
+
+ /**
+ * Creates a new ValidationResult.
+ *
+ * @param spec the validated specification, not null.
+ * @param message Additional message to be shown (optional).
+ * @return a new validation result containing missing parts createObject the given model with a message.
+ */
+ public static ValidationCheck createMissing(Annotation spec, String message) {
+ return new ValidationCheck(spec, Finding.MISSING, message);
+ }
+
+ /**
+ * Creates a new ValidationResult.
+ *
+ * @param spec the validated specification, not null.
+ * @param error error message to addNode.
+ * @return a new validation result containing erroneous parts createObject the given model with the given error message.
+ */
+ public static ValidationCheck createError(Annotation spec, String error) {
+ return new ValidationCheck(spec, Finding.ERROR, error);
+ }
+
+ /**
+ * Creates a new ValidationResult.
+ *
+ * @param spec the validated specification, not null.
+ * @param warning warning message to addNode.
+ * @return a new validation result containing warning parts createObject the given model with the given warning message.
+ */
+ public static ValidationCheck createWarning(Annotation spec, String warning) {
+ return new ValidationCheck(spec, Finding.WARNING, warning);
+ }
+
+ /**
+ * Creates a new ValidationResult.
+ *
+ * @param spec the validated specification, not null.
+ * @param alternativeUsage allows setting a message to indicate non-deprecated replacement, maybe null.
+ * @return a new validation result containing deprecated parts createObject the given model with an optional message.
+ */
+ public static ValidationCheck createDeprecated(Annotation spec, String alternativeUsage) {
+ return new ValidationCheck(spec, Finding.DEPRECATED, alternativeUsage != null ? "Use instead: " + alternativeUsage : null);
+ }
+
+ /**
+ * Creates a new ValidationResult.
+ *
+ * @param spec the validated specification, not null.
+ * @return a new validation result containing deprecated parts createObject the given model.
+ */
+ public static ValidationCheck createDeprecated(Annotation spec) {
+ return new ValidationCheck(spec, Finding.DEPRECATED, null);
+ }
+
+
+ /**
+ * Constructor.
+ *
+ * @param spec the validated specification, not null.
+ * @param result the configModel result, not null.
+ * @param message the detail message.
+ * @return new validation result.
+ */
+ public static ValidationCheck create(Annotation spec, Finding result, String message) {
+ return new ValidationCheck(spec, result, message);
+ }
+
+
+ /**
+ * Constructor.
+ *
+ * @param spec the validated specification, not null.
+ * @param result the configModel result, not null.
+ * @param message the detail message.
+ */
+ private ValidationCheck(Annotation spec, Finding result, String message) {
+ this.message = message;
+ this.spec = Objects.requireNonNull(spec);
+ this.result = Objects.requireNonNull(result);
+ }
+
+ /**
+ * Get the configModel section.
+ *
+ * @return the section, never null.
+ */
+ public Annotation getSpec() {
+ return spec;
+ }
+
+ /**
+ * Get the configModel result.
+ *
+ * @return the result, never null.
+ */
+ public Finding getResult() {
+ return result;
+ }
+
+ /**
+ * Get the detail message.
+ *
+ * @return the detail message, or null.
+ */
+ public String getMessage() {
+ return message;
+ }
+
+ @Override
+ public String toString() {
+ String finalMessage = "";
+ if (message != null) {
+ finalMessage = " -> " + message;
+ }
+ if(spec instanceof ConfigPropertySpec){
+ ConfigPropertySpec pspec = (ConfigPropertySpec)spec;
+ return result + ": " + pspec.name() + " (property)"+finalMessage + '\n';
+ }
+ else if(spec instanceof ConfigAreaSpec){
+ ConfigAreaSpec gspec = (ConfigAreaSpec)spec;
+ return result + ": " + gspec.path() + " (group)"+finalMessage + '\n';
+ }
+ return result + ": " + spec + ")"+finalMessage + '\n';
+ }
+}
diff --git a/documentation/src/main/java/org/apache/tamaya/validation/ValidationResult.java b/documentation/src/main/java/org/apache/tamaya/validation/ValidationResult.java
new file mode 100644
index 0000000..6ff8a4c
--- /dev/null
+++ b/documentation/src/main/java/org/apache/tamaya/validation/ValidationResult.java
@@ -0,0 +1,29 @@
+/*
+ * 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 createObject 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.validation;
+
+import java.util.Collection;
+import java.util.Collections;
+
+public class ValidationResult {
+
+ public Collection<ValidationCheck> getRules(){
+ return Collections.emptyList();
+ }
+}
diff --git a/documentation/src/main/resources/img/tamaya.png b/documentation/src/main/resources/img/tamaya.png
new file mode 100644
index 0000000..16df5d7
Binary files /dev/null and b/documentation/src/main/resources/img/tamaya.png differ
diff --git a/documentation/src/test/java/org/apache/tamaya/doc/AnnotBasedStandaloneConfigDocumentation.java b/documentation/src/test/java/org/apache/tamaya/doc/AnnotBasedStandaloneConfigDocumentation.java
new file mode 100644
index 0000000..223b3d4
--- /dev/null
+++ b/documentation/src/test/java/org/apache/tamaya/doc/AnnotBasedStandaloneConfigDocumentation.java
@@ -0,0 +1,62 @@
+package org.apache.tamaya.doc;
+
+import org.apache.tamaya.doc.annot.ConfigAreaSpec;
+import org.apache.tamaya.doc.annot.ConfigPropertySpec;
+import org.apache.tamaya.doc.annot.ConfigSpec;
+import org.apache.tamaya.inject.api.Config;
+import org.apache.tamaya.inject.api.ConfigDefaultSections;
+import org.apache.tamaya.spi.PropertyValue;
+
+import java.net.URL;
+
+@ConfigSpec(
+ name="Test",
+ version="0.1.0",
+ description = "Tomcat Configuration based on Tamaya"
+)
+@ConfigAreaSpec(
+ path = "kubernetes",
+ description = "Kubernetes Settings",
+ areaType = PropertyValue.ValueType.MAP,
+ max = 1
+)
+@ConfigAreaSpec(
+ path = "kubernetes.security",
+ description = "Kubernetes Security Settings",
+ areaType = PropertyValue.ValueType.MAP,
+ max = 1
+)
+@ConfigAreaSpec(
+ path = "kubernetes.cluster",
+ description = "Kubernetes Cluster Options",
+ areaType = PropertyValue.ValueType.MAP,
+ max = 1
+)
+@ConfigAreaSpec(
+ path = "<root>",
+ description = "Main Options",
+ areaType = PropertyValue.ValueType.MAP,
+ properties = {
+ @ConfigPropertySpec(name="log", description ="Log the server startup in detail, default: false.",
+ valueType = Boolean.class),
+ @ConfigPropertySpec(name="refresh", description = "Refresh interval in millis, default: 1000ms",
+ valueType = Long.class),
+ }
+)
+public interface AnnotBasedStandaloneConfigDocumentation {
+
+ @ConfigAreaSpec(
+ description = "Tomcat Server Endpoints",
+ min = 1,
+ areaType = PropertyValue.ValueType.ARRAY)
+ @ConfigDefaultSections("servers")
+ class Server{
+ @ConfigPropertySpec(description = "The server name.")
+ @Config(required = true)
+ private String name;
+ @ConfigPropertySpec(description = "The server url.")
+ @Config(required = true)
+ private URL url;
+ }
+
+}
diff --git a/documentation/src/test/java/org/apache/tamaya/doc/AnnotatedDocConfigBean.java b/documentation/src/test/java/org/apache/tamaya/doc/AnnotatedDocConfigBean.java
new file mode 100644
index 0000000..184c4ce
--- /dev/null
+++ b/documentation/src/test/java/org/apache/tamaya/doc/AnnotatedDocConfigBean.java
@@ -0,0 +1,95 @@
+/*
+ * 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.doc;
+
+import org.apache.tamaya.doc.annot.ConfigAreaSpec;
+import org.apache.tamaya.doc.annot.ConfigPropertySpec;
+import org.apache.tamaya.doc.annot.ConfigSpec;
+import org.apache.tamaya.inject.api.Config;
+import org.apache.tamaya.inject.api.DynamicValue;
+import org.apache.tamaya.inject.api.NoConfig;
+import org.apache.tamaya.spi.PropertyValue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An example showing some basic annotations, using an interface to be proxied by the
+ * configuration system, nevertheless extending the overall Configuration interface.
+ * Created by Anatole on 15.02.14.
+ */
+@ConfigSpec(
+ name="Test",
+ version="0.1.0"
+)
+@ConfigAreaSpec(
+ path="",
+ description = "Default root configuration",
+ areaType = PropertyValue.ValueType.MAP
+)
+public class AnnotatedDocConfigBean {
+
+ @ConfigPropertySpec(description = "Tests a multi key resolution")
+ @Config(value = {"foo.bar.myprop", "mp", "common.testdata.myProperty"}, defaultValue = "ET")
+ public String myParameter;
+
+ @ConfigPropertySpec(description = "Tests a simple String description")
+ @Config("simple_value")
+ public String simpleValue;
+
+ @ConfigPropertySpec(description = "Another test value without any explicit definition")
+ @Config
+ String anotherValue;
+
+ @ConfigPropertySpec(description = "An explicit config parameter value.")
+ @Config("[host.name]")
+ private String hostName;
+
+ @ConfigPropertySpec(description = "An non String typed instance.")
+ @Config("host.name")
+ private DynamicValue<String> dynamicHostname;
+
+ @NoConfig
+ public String javaVersion;
+
+ public String getAnotherValue(){
+ return anotherValue;
+ }
+
+ public String getHostName(){
+ return hostName;
+ }
+
+ public DynamicValue<String> getDynamicValue(){
+ return dynamicHostname;
+ }
+
+ @NoConfig
+ private List<String> events = new ArrayList<>();
+
+ // verify we don't try to inject final fields
+ public static final String CONSTANT = "a constant";
+
+
+ @Config("java.version")
+ void setJavaVersion(String version){
+ this.javaVersion = version;
+ }
+
+}
diff --git a/documentation/src/test/java/org/apache/tamaya/doc/ConfigDocumenterTest.java b/documentation/src/test/java/org/apache/tamaya/doc/ConfigDocumenterTest.java
new file mode 100644
index 0000000..3b6bdcd
--- /dev/null
+++ b/documentation/src/test/java/org/apache/tamaya/doc/ConfigDocumenterTest.java
@@ -0,0 +1,46 @@
+package org.apache.tamaya.doc;
+
+import org.apache.tamaya.doc.formats.HtmlDocFormat;
+import org.apache.tamaya.doc.formats.TextDocFormat;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class ConfigDocumenterTest {
+
+ @Test
+ public void getDocumentationAndPrint_ConfigBean() {
+ ConfigDocumenter reader = new ConfigDocumenter();
+ reader.readClasses(AnnotatedDocConfigBean.class);
+ DocumentedConfiguration documentation = reader.getDocumentation();
+ assertNotNull(documentation);
+ System.out.println(new TextDocFormat().apply(documentation));
+ }
+
+ @Test
+ public void getDocumentationAndPrint_AnnotationType() {
+ ConfigDocumenter reader = new ConfigDocumenter();
+ reader.readClasses(AnnotBasedStandaloneConfigDocumentation.class);
+ DocumentedConfiguration documentation = reader.getDocumentation();
+ assertNotNull(documentation);
+ System.out.println(new TextDocFormat().apply(documentation));
+ }
+
+ @Test
+ public void getDocumentationAndPrint_Package() {
+ ConfigDocumenter reader = new ConfigDocumenter();
+ reader.readPackages("org.apache.tamaya.doc");
+ DocumentedConfiguration documentation = reader.getDocumentation();
+ assertNotNull(documentation);
+ System.out.println(new TextDocFormat().apply(documentation));
+ }
+
+ @Test
+ public void getDocumentationAndPrint_Package_html() {
+ ConfigDocumenter reader = new ConfigDocumenter();
+ reader.readPackages("org.apache.tamaya.doc");
+ DocumentedConfiguration documentation = reader.getDocumentation();
+ assertNotNull(documentation);
+ System.out.println(new HtmlDocFormat().apply(documentation));
+ }
+}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 715ca1d..167366d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -809,6 +809,7 @@ under the License.
<module>uom</module>
<module>vertx</module>
<module>configjsr</module>
+ <module>documentation</module>
<!-- Once the API is officially available ... -->
<!-- module>configjsr</module-->
</modules>