Add more tests
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/AttributeableEntity.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/AttributeableEntity.java
index e1dc59e..7ca4b67 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/AttributeableEntity.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/AttributeableEntity.java
@@ -32,13 +32,6 @@
 	
 	/** The additional attributes */
 	private final Map<String, JsonValue> attributes = new LinkedHashMap<>();
-	
-	/**
-	 * Create a new instance and set defaults.
-	 */
-	public AttributeableEntity() {
-		this.clear();
-	}
 
     /**
      * Clear the object and remove all metadata
@@ -128,7 +121,25 @@
 		return null;
 	}
 
-	void setString(final JsonObjectBuilder builder, final String attributeName, final String value) {
+	/**
+	 * Helper method to get a number value from an attribute
+	 * @param attributeName The attribute name
+	 * @return The string value or {@code null}.
+     * @throws IOException If the attribute value is not of type boolean
+	 */
+	Number getNumber(final String attributeName) throws IOException {
+		final JsonValue val = this.getAttributes().remove(attributeName);
+		if ( val != null ) {
+			final Object obj = Configurations.convertToObject(val);
+			if ( obj instanceof Number ) {
+				return (Number)obj;
+			}
+			throw new IOException("Invalid type for number value " + attributeName + " : " + val.getValueType().name());
+		}
+		return null;
+	}
+
+    void setString(final JsonObjectBuilder builder, final String attributeName, final String value) {
 		if ( value != null ) {
 			builder.add(attributeName, value);
 		}
@@ -153,6 +164,7 @@
 	 * @param attributeName The attribute name
 	 * @param defaultValue default value
 	 * @return The boolean value or the default value
+     * @throws IOException If the attribute value is not of type boolean
 	 */
 	boolean getBoolean(final String attributeName, final boolean defaultValue) throws IOException {
 		final JsonValue val = this.getAttributes().remove(attributeName);
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurableEntity.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurableEntity.java
index 936ea1c..b7edfe6 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurableEntity.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurableEntity.java
@@ -51,7 +51,7 @@
 	public void fromJSONObject(final JsonObject jsonObj) throws IOException {
         super.fromJSONObject(jsonObj);
         try {
-            final JsonValue val = this.getAttributes().remove(Constants.KEY_PROPERTIES);
+            final JsonValue val = this.getAttributes().remove(InternalConstants.KEY_PROPERTIES);
             if ( val != null ) {
                 for(final Map.Entry<String, JsonValue> innerEntry : val.asJsonObject().entrySet()) {
 					final Property prop = new Property();
@@ -86,7 +86,7 @@
 			for(final Map.Entry<String, Property> entry : this.getProperties().entrySet()) {
 				propBuilder.add(entry.getKey(), entry.getValue().createJson());
 			}
-			objBuilder.add(Constants.KEY_PROPERTIES, propBuilder);
+			objBuilder.add(InternalConstants.KEY_PROPERTIES, propBuilder);
 		}
 
 		return objBuilder;
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurationApi.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurationApi.java
index b6ce23f..c97040a 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurationApi.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurationApi.java
@@ -119,7 +119,7 @@
         super.fromJSONObject(jsonObj);
         try {
             JsonValue val;
-            val = this.getAttributes().remove(Constants.KEY_CONFIGURATIONS);
+            val = this.getAttributes().remove(InternalConstants.KEY_CONFIGURATIONS);
             if ( val != null ) {
                 for(final Map.Entry<String, JsonValue> innerEntry : val.asJsonObject().entrySet()) {
                     final Configuration cfg = new Configuration();
@@ -128,7 +128,7 @@
                 }
             }
             
-            val = this.getAttributes().remove(Constants.KEY_FACTORIES);
+            val = this.getAttributes().remove(InternalConstants.KEY_FACTORIES);
             if ( val != null ) {
                 for(final Map.Entry<String, JsonValue> innerEntry : val.asJsonObject().entrySet()) {
                     final FactoryConfiguration cfg = new FactoryConfiguration();
@@ -137,7 +137,7 @@
                 }
             }
 
-            val = this.getAttributes().remove(Constants.KEY_FWK_PROPERTIES);
+            val = this.getAttributes().remove(InternalConstants.KEY_FWK_PROPERTIES);
             if ( val != null ) {
                 for(final Map.Entry<String, JsonValue> innerEntry : val.asJsonObject().entrySet()) {
                     final FrameworkProperty cfg = new FrameworkProperty();
@@ -146,21 +146,21 @@
                 }
             }
 
-            val = this.getAttributes().remove(Constants.KEY_INTERNAL_CONFIGURATIONS);
+            val = this.getAttributes().remove(InternalConstants.KEY_INTERNAL_CONFIGURATIONS);
             if ( val != null ) {
                 for(final JsonValue innerVal : val.asJsonArray()) {
                     this.getInternalConfigurations().add(getString(innerVal));
                 }
             }
 
-            val = this.getAttributes().remove(Constants.KEY_INTERNAL_FACTORIES);
+            val = this.getAttributes().remove(InternalConstants.KEY_INTERNAL_FACTORIES);
             if ( val != null ) {
                 for(final JsonValue innerVal : val.asJsonArray()) {
                     this.getInternalFactories().add(getString(innerVal));
                 }
             }
 
-            val = this.getAttributes().remove(Constants.KEY_INTERNAL_FWK_PROPERTIES);
+            val = this.getAttributes().remove(InternalConstants.KEY_INTERNAL_FWK_PROPERTIES);
             if ( val != null ) {
                 for(final JsonValue innerVal : val.asJsonArray()) {
                     this.getInternalFrameworkProperties().add(getString(innerVal));
@@ -233,42 +233,42 @@
             for(final Map.Entry<String, Configuration> entry : this.getConfigurations().entrySet()) {
                 propBuilder.add(entry.getKey(), entry.getValue().createJson());
             }
-            objBuilder.add(Constants.KEY_CONFIGURATIONS, propBuilder);
+            objBuilder.add(InternalConstants.KEY_CONFIGURATIONS, propBuilder);
         }
         if ( !this.getFactories().isEmpty() ) {
             final JsonObjectBuilder propBuilder = Json.createObjectBuilder();
             for(final Map.Entry<String, FactoryConfiguration> entry : this.getFactories().entrySet()) {
                 propBuilder.add(entry.getKey(), entry.getValue().createJson());
             }
-            objBuilder.add(Constants.KEY_FACTORIES, propBuilder);
+            objBuilder.add(InternalConstants.KEY_FACTORIES, propBuilder);
         }
         if ( !this.getFrameworkProperties().isEmpty() ) {
             final JsonObjectBuilder propBuilder = Json.createObjectBuilder();
             for(final Map.Entry<String, FrameworkProperty> entry : this.getFrameworkProperties().entrySet()) {
                 propBuilder.add(entry.getKey(), entry.getValue().createJson());
             }
-            objBuilder.add(Constants.KEY_FWK_PROPERTIES, propBuilder);
+            objBuilder.add(InternalConstants.KEY_FWK_PROPERTIES, propBuilder);
         }
         if ( !this.getInternalConfigurations().isEmpty() ) {
             final JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
             for(final String n : this.getInternalConfigurations()) {
                 arrayBuilder.add(n);
             }
-			objBuilder.add(Constants.KEY_INTERNAL_CONFIGURATIONS, arrayBuilder);
+			objBuilder.add(InternalConstants.KEY_INTERNAL_CONFIGURATIONS, arrayBuilder);
 		}
 		if ( !this.getInternalFactories().isEmpty() ) {
             final JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
             for(final String n : this.getInternalFactories()) {
                 arrayBuilder.add(n);
             }
-			objBuilder.add(Constants.KEY_INTERNAL_FACTORIES, arrayBuilder);
+			objBuilder.add(InternalConstants.KEY_INTERNAL_FACTORIES, arrayBuilder);
 		}
 		if ( !this.getInternalFrameworkProperties().isEmpty() ) {
             final JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
             for(final String n : this.getInternalFrameworkProperties()) {
                 arrayBuilder.add(n);
             }
-			objBuilder.add(Constants.KEY_INTERNAL_FWK_PROPERTIES, arrayBuilder);
+			objBuilder.add(InternalConstants.KEY_INTERNAL_FWK_PROPERTIES, arrayBuilder);
 		}
 
 		return objBuilder;
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/DescribableEntity.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/DescribableEntity.java
index 69c9e3a..0f36d8a 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/DescribableEntity.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/DescribableEntity.java
@@ -52,9 +52,9 @@
 	public void fromJSONObject(final JsonObject jsonObj) throws IOException {
 		super.fromJSONObject(jsonObj);
         try {
-			this.setTitle(this.getString(Constants.KEY_TITLE));
-			this.setDescription(this.getString(Constants.KEY_DESCRIPTION));
-			this.setDeprecated(this.getString(Constants.KEY_DEPRECATED));
+			this.setTitle(this.getString(InternalConstants.KEY_TITLE));
+			this.setDescription(this.getString(InternalConstants.KEY_DESCRIPTION));
+			this.setDeprecated(this.getString(InternalConstants.KEY_DEPRECATED));
         } catch (final JsonException | IllegalArgumentException e) {
             throw new IOException(e);
 		}
@@ -117,9 +117,9 @@
     JsonObjectBuilder createJson() throws IOException {
 		final JsonObjectBuilder objectBuilder = super.createJson();
 
-		this.setString(objectBuilder, Constants.KEY_TITLE, this.getTitle());
-		this.setString(objectBuilder, Constants.KEY_DESCRIPTION, this.getDescription());
-		this.setString(objectBuilder, Constants.KEY_DEPRECATED, this.getDeprecated());
+		this.setString(objectBuilder, InternalConstants.KEY_TITLE, this.getTitle());
+		this.setString(objectBuilder, InternalConstants.KEY_DESCRIPTION, this.getDescription());
+		this.setString(objectBuilder, InternalConstants.KEY_DEPRECATED, this.getDeprecated());
 
 		return objectBuilder;
 	}
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/FactoryConfiguration.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/FactoryConfiguration.java
index c2345bb..ea41555 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/FactoryConfiguration.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/FactoryConfiguration.java
@@ -31,10 +31,6 @@
 
 public class FactoryConfiguration extends ConfigurableEntity {
     
-    private static final String KEY_OPERATIONS = "operations";
-
-    private static final String KEY_INTERNAL_NAMES = "internal-names";
-    
     private final Set<Operation> operations = new HashSet<>();
 
     private final List<String> internalNames = new ArrayList<>();
@@ -58,7 +54,7 @@
         super.fromJSONObject(jsonObj);
         try {
             JsonValue val;
-            val = this.getAttributes().remove(KEY_OPERATIONS);
+            val = this.getAttributes().remove(InternalConstants.KEY_OPERATIONS);
             if ( val != null ) {
                 for(final JsonValue innerVal : val.asJsonArray()) {
                     final String v = getString(innerVal).toUpperCase();
@@ -66,7 +62,7 @@
                 }
             }
             
-            val = this.getAttributes().remove(KEY_INTERNAL_NAMES);
+            val = this.getAttributes().remove(InternalConstants.KEY_INTERNAL_NAMES);
             if ( val != null ) {
                 for(final JsonValue innerVal : val.asJsonArray()) {
                     this.getInternalNames().add(getString(innerVal));
@@ -106,14 +102,14 @@
             for(final Operation op : this.getOperations()) {
                 arrayBuilder.add(op.name());
             }
-			objBuilder.add(KEY_OPERATIONS, arrayBuilder);
+			objBuilder.add(InternalConstants.KEY_OPERATIONS, arrayBuilder);
 		}
 		if ( !this.getInternalNames().isEmpty() ) {
             final JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
             for(final String n : this.getInternalNames()) {
                 arrayBuilder.add(n);
             }
-			objBuilder.add(KEY_INTERNAL_NAMES, arrayBuilder);
+			objBuilder.add(InternalConstants.KEY_INTERNAL_NAMES, arrayBuilder);
 		}
 		return objBuilder;
    }
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Constants.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/InternalConstants.java
similarity index 93%
rename from src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Constants.java
rename to src/main/java/org/apache/sling/feature/extension/apiregions/api/config/InternalConstants.java
index d253419..5ba22ec 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Constants.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/InternalConstants.java
@@ -19,7 +19,7 @@
 /**
  * Constants used in this implementation
  */
-public abstract class Constants {
+abstract class InternalConstants {
     
     static final String KEY_TITLE = "title";
 
@@ -64,4 +64,8 @@
     static final String KEY_VALUE = "value";
 
     static final String KEY_REQUIRED = "required";
+
+    static final String KEY_OPERATIONS = "operations";
+
+    static final String KEY_INTERNAL_NAMES = "internal-names";
 }
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Option.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Option.java
index 9ebd153..4efeecf 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Option.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Option.java
@@ -30,22 +30,22 @@
     /**
      * Clear the object and remove all metadata
      */
-	  public void clear() {
+    public void clear() {
         super.clear();
-		    this.setValue(null);
+        this.setValue(null);
     }
 
-	  /**
-	   * Extract the metadata from the JSON object.
-	   * This method first calls {@link #clear()}
-	   * @param jsonObj The JSON Object
-	   * @throws IOException If JSON parsing fails
-	   */
-	  public void fromJSONObject(final JsonObject jsonObj) throws IOException {
+    /**
+     * Extract the metadata from the JSON object.
+     * This method first calls {@link #clear()}
+     * @param jsonObj The JSON Object
+     * @throws IOException If JSON parsing fails
+     */
+    public void fromJSONObject(final JsonObject jsonObj) throws IOException {
         super.fromJSONObject(jsonObj);
         try {
-		      	this.setValue(this.getString(Constants.KEY_VALUE));
- 		    } catch (final JsonException | IllegalArgumentException e) {
+          	this.setValue(this.getString(InternalConstants.KEY_VALUE));
+        } catch (final JsonException | IllegalArgumentException e) {
             throw new IOException(e);
         }
     }
@@ -53,17 +53,17 @@
     /**
      * Get the value for the option
   	 * @return the value
-	   */
-	  public String getValue() {
-		    return value;
-	  }
+     */
+    public String getValue() {
+        return value;
+    }
 
-	  /**
+    /**
      * Set the value for the option
-	   * @param value the value to set
-	   */
-	  public void setValue(final String value) {
-		    this.value = value;
+     * @param value the value to set
+     */
+    public void setValue(final String value) {
+        this.value = value;
     }
 
     /**
@@ -73,10 +73,10 @@
      * @throws IOException If generating the JSON fails
      */
     JsonObjectBuilder createJson() throws IOException {
-		    final JsonObjectBuilder objectBuilder = super.createJson();
+        final JsonObjectBuilder objectBuilder = super.createJson();
 
-		    this.setString(objectBuilder, Constants.KEY_VALUE, this.getValue());
+        this.setString(objectBuilder, InternalConstants.KEY_VALUE, this.getValue());
 
-		    return objectBuilder;
+        return objectBuilder;
     }
 }
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Property.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Property.java
index 3579029..bebccfd 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Property.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Property.java
@@ -19,6 +19,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.regex.Pattern;
 
 import javax.json.Json;
 import javax.json.JsonArrayBuilder;
@@ -54,19 +55,27 @@
 	private List<Option> options;
 	
 	/** The optional regex */
-	private String regex;
+	private Pattern pattern;
 
 	/** Required? */
 	private boolean required;
-	
+    
+    public Property() {
+        this.setDefaults();
+    }
+
+    void setDefaults() {
+		this.setType(PropertyType.STRING);
+        this.setCardinality(1);
+        this.setRequired(false);
+    }
+
     /**
      * Clear the object and remove all metadata
      */
 	public void clear() {
         super.clear();
-		this.setType(PropertyType.STRING);
-		this.setCardinality(1);
-		this.setRequired(false);
+        this.setDefaults();
 		this.setVariable(null);
 		this.setRange(null);
 		this.setIncludes(null);
@@ -84,21 +93,21 @@
 	public void fromJSONObject(final JsonObject jsonObj) throws IOException {
         super.fromJSONObject(jsonObj);
         try {
-			this.setVariable(this.getString(Constants.KEY_VARIABLE));
-			this.setCardinality(this.getInteger(Constants.KEY_CARDINALITY, this.getCardinality()));
-			this.setRequired(this.getBoolean(Constants.KEY_REQUIRED, this.isRequired()));
+			this.setVariable(this.getString(InternalConstants.KEY_VARIABLE));
+			this.setCardinality(this.getInteger(InternalConstants.KEY_CARDINALITY, this.getCardinality()));
+			this.setRequired(this.getBoolean(InternalConstants.KEY_REQUIRED, this.isRequired()));
 			
-			final String typeVal = this.getString(Constants.KEY_TYPE);
+			final String typeVal = this.getString(InternalConstants.KEY_TYPE);
 			if ( typeVal != null ) {
                 this.setType(PropertyType.valueOf(typeVal.toUpperCase()));				
 			}
-			final JsonValue rangeVal = this.getAttributes().remove(Constants.KEY_RANGE);
+			final JsonValue rangeVal = this.getAttributes().remove(InternalConstants.KEY_RANGE);
 			if ( rangeVal != null ) {
 				final Range range = new Range();
 				range.fromJSONObject(rangeVal.asJsonObject());
 				this.setRange(range);
 			}
-			final JsonValue incs = this.getAttributes().remove(Constants.KEY_INCLUDES);
+			final JsonValue incs = this.getAttributes().remove(InternalConstants.KEY_INCLUDES);
 			if ( incs != null ) {
 				final List<String> list = new ArrayList<>();
 				for(final JsonValue innerVal : incs.asJsonArray()) {
@@ -106,7 +115,7 @@
                 }
                 this.setIncludes(list.toArray(new String[list.size()]));
 			}
-			final JsonValue excs = this.getAttributes().remove(Constants.KEY_EXCLUDES);
+			final JsonValue excs = this.getAttributes().remove(InternalConstants.KEY_EXCLUDES);
 			if ( excs != null ) {
 				final List<String> list = new ArrayList<>();
 				for(final JsonValue innerVal : excs.asJsonArray()) {
@@ -114,7 +123,7 @@
                 }
                 this.setExcludes(list.toArray(new String[list.size()]));
 			}
-			final JsonValue opts = this.getAttributes().remove(Constants.KEY_OPTIONS);
+			final JsonValue opts = this.getAttributes().remove(InternalConstants.KEY_OPTIONS);
 			if ( opts != null ) {
 				final List<Option> list = new ArrayList<>();
 				for(final JsonValue innerVal : opts.asJsonArray()) {
@@ -124,7 +133,7 @@
                 }
 				this.setOptions(list);
 			}
-			this.setRegex(this.getString(Constants.KEY_REGEX));
+			this.setRegex(this.getString(InternalConstants.KEY_REGEX));
  		} catch (final JsonException | IllegalArgumentException e) {
             throw new IOException(e);
         }
@@ -140,41 +149,41 @@
 		final JsonObjectBuilder objectBuilder = super.createJson();
 
 		if ( this.getType() != null ) {
-			this.setString(objectBuilder, Constants.KEY_TYPE, this.getType().name());
+			this.setString(objectBuilder, InternalConstants.KEY_TYPE, this.getType().name());
 	    }
 		if ( this.getCardinality() != 1 ) {
-			objectBuilder.add(Constants.KEY_CARDINALITY, this.getCardinality());
+			objectBuilder.add(InternalConstants.KEY_CARDINALITY, this.getCardinality());
 		}
 		if ( this.isRequired() ) {
-			objectBuilder.add(Constants.KEY_REQUIRED, this.isRequired());
+			objectBuilder.add(InternalConstants.KEY_REQUIRED, this.isRequired());
 		}
-	    this.setString(objectBuilder, Constants.KEY_VARIABLE, this.getVariable());
+	    this.setString(objectBuilder, InternalConstants.KEY_VARIABLE, this.getVariable());
 		
 		if ( this.range != null ) {
-			objectBuilder.add(Constants.KEY_RANGE, this.range.toJSONObject());
+			objectBuilder.add(InternalConstants.KEY_RANGE, this.range.toJSONObject());
 		}
 		if ( this.includes != null && this.includes.length > 0 ) {
 			final JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
 			for(final String v : this.includes) {
 				arrayBuilder.add(v);
 			}
-			objectBuilder.add(Constants.KEY_INCLUDES, arrayBuilder);
+			objectBuilder.add(InternalConstants.KEY_INCLUDES, arrayBuilder);
 		}
 		if ( this.excludes != null && this.excludes.length > 0 ) {
 			final JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
 			for(final String v : this.excludes) {
 				arrayBuilder.add(v);
 			}
-			objectBuilder.add(Constants.KEY_EXCLUDES, arrayBuilder);
+			objectBuilder.add(InternalConstants.KEY_EXCLUDES, arrayBuilder);
 		}
 		if ( this.options != null && !this.options.isEmpty()) {
 			final JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
             for(final Option o : this.options) {
 				arrayBuilder.add(o.toJSONObject());
 			}
-			objectBuilder.add(Constants.KEY_OPTIONS, arrayBuilder);
+			objectBuilder.add(InternalConstants.KEY_OPTIONS, arrayBuilder);
 		}
-		this.setString(objectBuilder, Constants.KEY_REGEX, this.getRegex());
+		this.setString(objectBuilder, InternalConstants.KEY_REGEX, this.getRegex());
 		
 		return objectBuilder;
 	}
@@ -192,7 +201,7 @@
 	 * @param type the type to set
 	 */
 	public void setType(final PropertyType type) {
-		this.type = type;
+		this.type = type == null ? PropertyType.STRING : type;
 	}
 
 	/**
@@ -296,17 +305,30 @@
 	 * @return the regex
 	 */
 	public String getRegex() {
-		return regex;
+		return pattern == null ? null : pattern.pattern();
 	}
 
 	/**
 	 * Set the regex
 	 * @param regex the regex to set
+     * @throws IllegalArgumentException If the pattern is not valid
 	 */
 	public void setRegex(final String regex) {
-		this.regex = regex;
+        if ( regex == null ) {
+            this.pattern = null;
+        } else {
+           this.pattern = Pattern.compile(regex);
+        }
 	}
 
+    /**
+     * Get the regex pattern
+     * @return The pattern or {@code null}
+     */
+    public Pattern getRegexPattern() {
+        return this.pattern;
+    }
+
 	/**
 	 * Is this property required?
 	 * @return {@code true} if it is required
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Range.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Range.java
index 01a474f..3acac02 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Range.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Range.java
@@ -22,13 +22,15 @@
 import javax.json.JsonObject;
 import javax.json.JsonObjectBuilder;
 
+import org.apache.felix.cm.json.Configurations;
+
 public class Range extends AttributeableEntity {
 
     /** The optional min value */
-    private String min;
+    private Number min;
 
     /** The optional max value */
-    private String max;
+    private Number max;
 
     /**
      * Clear the object and remove all metadata
@@ -48,8 +50,8 @@
 	public void fromJSONObject(final JsonObject jsonObj) throws IOException {
         super.fromJSONObject(jsonObj);
         try {
-			this.setMin(this.getString(Constants.KEY_MIN));
-			this.setMax(this.getString(Constants.KEY_MAX));
+			this.setMin(this.getNumber(InternalConstants.KEY_MIN));
+			this.setMax(this.getNumber(InternalConstants.KEY_MAX));
  		} catch (final JsonException | IllegalArgumentException e) {
             throw new IOException(e);
         }
@@ -59,7 +61,7 @@
      * Get the min value
 	 * @return the min
 	 */
-	public String getMin() {
+	public Number getMin() {
 		return min;
 	}
 
@@ -67,7 +69,7 @@
      * Set the min value
 	 * @param min the min to set
 	 */
-	public void setMin(final String min) {
+	public void setMin(final Number min) {
 		this.min = min;
 	}
 
@@ -75,7 +77,7 @@
      * Get the max value
 	 * @return the max
 	 */
-	public String getMax() {
+	public Number getMax() {
 		return max;
 	}
 
@@ -83,7 +85,7 @@
      * Set the max value
 	 * @param max the max to set
 	 */
-	public void setMax(final String max) {
+	public void setMax(final Number max) {
 		this.max = max;
     }
 
@@ -96,8 +98,12 @@
     JsonObjectBuilder createJson() throws IOException {
 		final JsonObjectBuilder objectBuilder = super.createJson();
 
-		this.setString(objectBuilder, Constants.KEY_MIN, this.getMin());
-		this.setString(objectBuilder, Constants.KEY_MAX, this.getMax());
+        if ( this.getMin() != null ) {
+            objectBuilder.add(InternalConstants.KEY_MIN, Configurations.convertToJsonValue(this.getMin()));
+        }
+        if ( this.getMax() != null ) {
+            objectBuilder.add(InternalConstants.KEY_MAX, Configurations.convertToJsonValue(this.getMax()));
+        }
 
 		return objectBuilder;
 	}
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Validator.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Validator.java
index 546fa1c..6a3eda6 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Validator.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Validator.java
@@ -22,7 +22,6 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
-import java.util.regex.Pattern;
 
 /**
  * Validate values
@@ -144,9 +143,8 @@
 							break;
 				default : messages.add("Unable to validate value - unknown property type : " + prop.getType());
 			}
-			if ( prop.getRegex() != null ) {
-				final Pattern p = Pattern.compile(prop.getRegex());
-				if ( !p.matcher(value.toString()).matches() ) {
+			if ( prop.getRegexPattern() != null ) {
+				if ( !prop.getRegexPattern().matcher(value.toString()).matches() ) {
                     messages.add("Value " + value + " does not match regex " + prop.getRegex());
 				}
 			}
@@ -321,16 +319,30 @@
 	    if ( prop.getRange() != null ) {
 			try {
                 if ( prop.getRange().getMin() != null ) {
-					final long min = Long.valueOf(prop.getRange().getMin());
-					if ( value.longValue() < min ) {
-                         messages.add("Value " + value + " is too low; should not be lower than " + prop.getRange().getMin());
-					}
+                    if ( value instanceof Float || value instanceof Double ) {
+                        final double min = prop.getRange().getMin().doubleValue();
+                        if ( value.doubleValue() < min ) {
+                             messages.add("Value " + value + " is too low; should not be lower than " + prop.getRange().getMin());
+                        }    
+                    } else {
+                        final long min = prop.getRange().getMin().longValue();
+                        if ( value.longValue() < min ) {
+                             messages.add("Value " + value + " is too low; should not be lower than " + prop.getRange().getMin());
+                        }    
+                    }
 				}
                 if ( prop.getRange().getMax() != null ) {
-					final long max = Long.valueOf(prop.getRange().getMax());
-					if ( value.longValue() > max ) {
-                         messages.add("Value " + value + " is too high; should not be higher than " + prop.getRange().getMax());
-					}
+                    if ( value instanceof Float || value instanceof Double ) {
+                        final double max = prop.getRange().getMax().doubleValue();
+                        if ( value.doubleValue() > max ) {
+                            messages.add("Value " + value + " is too high; should not be higher than " + prop.getRange().getMax());
+                        }    
+                    } else {
+                        final long max = prop.getRange().getMax().longValue();
+                        if ( value.longValue() > max ) {
+                            messages.add("Value " + value + " is too high; should not be higher than " + prop.getRange().getMax());
+                        }    
+                    }
 				}
 			} catch ( final NumberFormatException nfe) {
 				messages.add("Invalid range specified in " + prop.getRange());
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/package-info.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/package-info.java
index f2582d7..b67f74d 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/package-info.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/package-info.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-@org.osgi.annotation.versioning.Version("1.ü.0")
+@org.osgi.annotation.versioning.Version("1.0.0")
 package org.apache.sling.feature.extension.apiregions.api.config;
 
 
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/AttributeableEntityTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/AttributeableEntityTest.java
index 5152867..165a5ed 100644
--- a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/AttributeableEntityTest.java
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/AttributeableEntityTest.java
@@ -104,4 +104,22 @@
         assertEquals(9, entity.getInteger("foo", 7));
         assertTrue(entity.getAttributes().isEmpty());
     }
+
+    @Test public void testGetNumber() throws IOException {
+        final AE entity = new AE();
+        assertNull(entity.getNumber("foo"));
+
+        entity.getAttributes().put("foo", Json.createValue(5));
+        assertEquals(5L, entity.getNumber("foo"));
+        assertTrue(entity.getAttributes().isEmpty());
+
+
+        try {
+            entity.getAttributes().put("foo", Json.createValue("a"));
+            entity.getNumber("foo");
+            fail();
+        } catch ( final IOException expected) {
+            // this is expected
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurableEntityTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurableEntityTest.java
new file mode 100644
index 0000000..79efb74
--- /dev/null
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurableEntityTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.sling.feature.extension.apiregions.api.config;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import javax.json.Json;
+
+import org.apache.sling.feature.Extension;
+import org.apache.sling.feature.ExtensionState;
+import org.apache.sling.feature.ExtensionType;
+import org.junit.Test;
+
+public class ConfigurableEntityTest {
+
+    public static class CE extends ConfigurableEntity {
+        // ConfigurableEntity is abstract, therefore subclassing for testing
+    }
+
+    @Test public void testClear() {
+        final CE entity = new CE();
+        entity.getAttributes().put("a", Json.createValue(5));
+        entity.setDeprecated("d");
+        entity.setTitle("t");
+        entity.setDescription("x");
+        entity.getProperties().put("a", new Property());
+        entity.clear();
+        assertTrue(entity.getAttributes().isEmpty());
+        assertNull(entity.getDeprecated());
+        assertNull(entity.getTitle());
+        assertNull(entity.getDescription());
+        assertTrue(entity.getProperties().isEmpty());
+    }
+
+    @Test public void testFromJSONObject() throws IOException {
+        final Extension ext = new Extension(ExtensionType.JSON, "a", ExtensionState.OPTIONAL);
+        ext.setJSON("{ \"properties\" : { \"a\" : {}, \"b\" : {}}}");
+
+        final CE entity = new CE();
+        entity.fromJSONObject(ext.getJSONStructure().asJsonObject());
+        assertEquals(2, entity.getProperties().size());
+        assertNotNull(entity.getProperties().get("a"));
+        assertNotNull(entity.getProperties().get("b"));
+    }
+
+    @Test public void testToJSONObject() throws IOException {
+        final CE entity = new CE();
+        entity.getProperties().put("a", new Property());
+        entity.getProperties().put("b", new Property());
+
+        final Extension ext = new Extension(ExtensionType.JSON, "a", ExtensionState.OPTIONAL);
+        ext.setJSON("{ \"properties\" : { \"a\" : {\"type\":\"STRING\"}, \"b\" : {\"type\":\"STRING\"}}}");
+
+        assertEquals(ext.getJSONStructure().asJsonObject(), entity.toJSONObject());
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurationApiTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurationApiTest.java
index 525797e..cdb2f94 100644
--- a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurationApiTest.java
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurationApiTest.java
@@ -16,7 +16,13 @@
  */
 package org.apache.sling.feature.extension.apiregions.api.config;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import javax.json.Json;
 
 import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.Extension;
@@ -44,4 +50,69 @@
         f.getExtensions().add(e);
         ConfigurationApi.getConfigurationApi(f);
     }
+
+    @Test public void testClear() {
+        final ConfigurationApi entity = new ConfigurationApi();
+        entity.getAttributes().put("a", Json.createValue(5));
+        entity.getConfigurations().put("pid", new Configuration());
+        entity.getFactories().put("factory", new FactoryConfiguration());
+        entity.getFrameworkProperties().put("prop", new FrameworkProperty());
+        entity.getInternalConfigurations().add("ipid");
+        entity.getInternalFactories().add("ifactory");
+        entity.getInternalFrameworkProperties().add("iprop");
+        entity.clear();
+        assertTrue(entity.getAttributes().isEmpty());
+        assertTrue(entity.getConfigurations().isEmpty());
+        assertTrue(entity.getFactories().isEmpty());
+        assertTrue(entity.getFrameworkProperties().isEmpty());
+        assertTrue(entity.getInternalConfigurations().isEmpty());
+        assertTrue(entity.getInternalFactories().isEmpty());
+        assertTrue(entity.getInternalFrameworkProperties().isEmpty());
+    }
+
+    @Test public void testFromJSONObject() throws IOException {
+        final Extension ext = new Extension(ExtensionType.JSON, "a", ExtensionState.OPTIONAL);
+        ext.setJSON("{ \"a\" : 5, \"configurations\" : { \"pid\": {}}, " +
+            "\"factories\" : { \"factory\" : {}}," +
+            "\"framework-properties\" : { \"prop\" : { \"type\" : \"STRING\"}}," +
+            "\"internal-configurations\" : [\"ipid\"],"+
+            "\"internal-factories\" : [\"ifactory\"],"+
+            "\"internal-framework-properties\" : [\"iprop\"]}");
+
+        final ConfigurationApi entity = new ConfigurationApi();
+        entity.fromJSONObject(ext.getJSONStructure().asJsonObject());
+        assertEquals(1, entity.getConfigurations().size());
+        assertEquals(1, entity.getFactories().size());
+        assertEquals(1, entity.getFrameworkProperties().size());
+        assertEquals(1, entity.getInternalConfigurations().size());
+        assertEquals(1, entity.getInternalFactories().size());
+        assertEquals(1, entity.getInternalFrameworkProperties().size());
+        assertTrue(entity.getConfigurations().containsKey("pid"));
+        assertTrue(entity.getFactories().containsKey("factory"));
+        assertTrue(entity.getFrameworkProperties().containsKey("prop"));
+        assertTrue(entity.getInternalConfigurations().contains("ipid"));
+        assertTrue(entity.getInternalFactories().contains("ifactory"));
+        assertTrue(entity.getInternalFrameworkProperties().contains("iprop"));
+    }
+
+    @Test public void testToJSONObject() throws IOException {
+        final ConfigurationApi entity = new ConfigurationApi();
+        entity.getAttributes().put("a", Json.createValue(5));
+        entity.getConfigurations().put("pid", new Configuration());
+        entity.getFactories().put("factory", new FactoryConfiguration());
+        entity.getFrameworkProperties().put("prop", new FrameworkProperty());
+        entity.getInternalConfigurations().add("ipid");
+        entity.getInternalFactories().add("ifactory");
+        entity.getInternalFrameworkProperties().add("iprop");
+
+        final Extension ext = new Extension(ExtensionType.JSON, "a", ExtensionState.OPTIONAL);
+        ext.setJSON("{ \"a\" : 5, \"configurations\" : { \"pid\": {}}, " +
+            "\"factories\" : { \"factory\" : {}}," +
+            "\"framework-properties\" : { \"prop\" : { \"type\" : \"STRING\"}}," +
+            "\"internal-configurations\" : [\"ipid\"],"+
+            "\"internal-factories\" : [\"ifactory\"],"+
+            "\"internal-framework-properties\" : [\"iprop\"]}");
+
+        assertEquals(ext.getJSONStructure().asJsonObject(), entity.toJSONObject());
+    }
 }
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/DescribableEntityTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/DescribableEntityTest.java
new file mode 100644
index 0000000..f2ad633
--- /dev/null
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/DescribableEntityTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.sling.feature.extension.apiregions.api.config;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import javax.json.Json;
+
+import org.apache.sling.feature.Extension;
+import org.apache.sling.feature.ExtensionState;
+import org.apache.sling.feature.ExtensionType;
+import org.junit.Test;
+
+public class DescribableEntityTest {
+
+    public static class DE extends DescribableEntity {
+        // DescribableEntity is abstract, therefore subclassing for testing
+    }
+
+    @Test public void testClear() {
+        final DE entity = new DE();
+        entity.getAttributes().put("a", Json.createValue(5));
+        entity.setDeprecated("d");
+        entity.setTitle("t");
+        entity.setDescription("x");
+        entity.clear();
+        assertTrue(entity.getAttributes().isEmpty());
+        assertNull(entity.getDeprecated());
+        assertNull(entity.getTitle());
+        assertNull(entity.getDescription());
+    }
+
+    @Test public void testFromJSONObject() throws IOException {
+        final Extension ext = new Extension(ExtensionType.JSON, "a", ExtensionState.OPTIONAL);
+        ext.setJSON("{ \"a\" : 1, \"b\" : \"2\", \"title\" : \"t\", \"description\" : \"desc\", \"deprecated\" : \"depr\"}");
+
+        final DE entity = new DE();
+        entity.fromJSONObject(ext.getJSONStructure().asJsonObject());
+        assertEquals(2, entity.getAttributes().size());
+        assertEquals(Json.createValue(1), entity.getAttributes().get("a"));
+        assertEquals(Json.createValue("2"), entity.getAttributes().get("b"));
+        assertEquals("t", entity.getTitle());
+        assertEquals("desc", entity.getDescription());
+        assertEquals("depr", entity.getDeprecated());
+    }
+
+    @Test public void testToJSONObject() throws IOException {
+        final DE entity = new DE();
+        entity.getAttributes().put("a", Json.createValue(1));
+        entity.getAttributes().put("b", Json.createValue("2"));
+        entity.setTitle("t");
+        entity.setDescription("desc");
+        entity.setDeprecated("depr");
+
+        final Extension ext = new Extension(ExtensionType.JSON, "a", ExtensionState.OPTIONAL);
+        ext.setJSON("{ \"a\" : 1, \"b\" : \"2\", \"title\" : \"t\", \"description\" : \"desc\", \"deprecated\" : \"depr\"}");
+
+        assertEquals(ext.getJSONStructure().asJsonObject(), entity.toJSONObject());
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/FactoryConfigurationTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/FactoryConfigurationTest.java
new file mode 100644
index 0000000..37f09bf
--- /dev/null
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/FactoryConfigurationTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.sling.feature.extension.apiregions.api.config;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import javax.json.Json;
+
+import org.apache.sling.feature.Extension;
+import org.apache.sling.feature.ExtensionState;
+import org.apache.sling.feature.ExtensionType;
+import org.junit.Test;
+
+public class FactoryConfigurationTest {
+
+    @Test public void testClear() {
+        final FactoryConfiguration entity = new FactoryConfiguration();
+        entity.getAttributes().put("a", Json.createValue(5));
+        entity.setDeprecated("d");
+        entity.setTitle("t");
+        entity.setDescription("x");
+        entity.getProperties().put("a", new Property());
+        entity.getOperations().add(Operation.CREATE);
+        entity.getInternalNames().add("internal");
+        entity.clear();
+        assertTrue(entity.getAttributes().isEmpty());
+        assertNull(entity.getDeprecated());
+        assertNull(entity.getTitle());
+        assertNull(entity.getDescription());
+        assertTrue(entity.getProperties().isEmpty());
+        assertTrue(entity.getOperations().isEmpty());
+        assertTrue(entity.getInternalNames().isEmpty());
+    }
+
+    @Test public void testFromJSONObject() throws IOException {
+        final Extension ext = new Extension(ExtensionType.JSON, "a", ExtensionState.OPTIONAL);
+        ext.setJSON("{ \"internal-names\" : [ \"a\", \"b\"], \"operations\" : [\"create\"]}");
+
+        final FactoryConfiguration entity = new FactoryConfiguration();
+        entity.fromJSONObject(ext.getJSONStructure().asJsonObject());
+        assertEquals(2, entity.getInternalNames().size());
+        assertTrue(entity.getInternalNames().contains("a"));
+        assertTrue(entity.getInternalNames().contains("b"));
+        assertEquals(1, entity.getOperations().size());
+        assertEquals(Operation.CREATE, entity.getOperations().iterator().next());
+    }
+
+    @Test public void testToJSONObject() throws IOException {
+        final FactoryConfiguration entity = new FactoryConfiguration();
+        entity.getInternalNames().add("a");
+        entity.getInternalNames().add("b");
+        entity.getOperations().add(Operation.UPDATE);
+
+        final Extension ext = new Extension(ExtensionType.JSON, "a", ExtensionState.OPTIONAL);
+        ext.setJSON("{ \"internal-names\" : [ \"a\", \"b\"], \"operations\" : [\"UPDATE\"]}");
+
+        assertEquals(ext.getJSONStructure().asJsonObject(), entity.toJSONObject());
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/OptionTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/OptionTest.java
new file mode 100644
index 0000000..924c2b0
--- /dev/null
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/OptionTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.sling.feature.extension.apiregions.api.config;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import javax.json.Json;
+
+import org.apache.sling.feature.Extension;
+import org.apache.sling.feature.ExtensionState;
+import org.apache.sling.feature.ExtensionType;
+import org.junit.Test;
+
+public class OptionTest {
+
+    @Test public void testClear() {
+        final Option entity = new Option();
+        entity.getAttributes().put("a", Json.createValue(5));
+        entity.setDeprecated("d");
+        entity.setTitle("t");
+        entity.setDescription("x");
+        entity.setValue("foo");
+        entity.clear();
+        assertTrue(entity.getAttributes().isEmpty());
+        assertNull(entity.getDeprecated());
+        assertNull(entity.getTitle());
+        assertNull(entity.getDescription());
+        assertNull(entity.getValue());
+    }
+
+    @Test public void testFromJSONObject() throws IOException {
+        final Extension ext = new Extension(ExtensionType.JSON, "a", ExtensionState.OPTIONAL);
+        ext.setJSON("{ \"a\" : 1, \"b\" : \"2\", \"title\" : \"t\", \"description\" : \"desc\", \"value\" : \"v\" }");
+
+        final Option entity = new Option();
+        entity.fromJSONObject(ext.getJSONStructure().asJsonObject());
+        assertEquals(2, entity.getAttributes().size());
+        assertEquals(Json.createValue(1), entity.getAttributes().get("a"));
+        assertEquals(Json.createValue("2"), entity.getAttributes().get("b"));
+        assertEquals("t", entity.getTitle());
+        assertEquals("desc", entity.getDescription());
+        assertEquals("v", entity.getValue());
+    }
+
+    @Test public void testToJSONObject() throws IOException {
+        final Option entity = new Option();
+        entity.getAttributes().put("a", Json.createValue(1));
+        entity.getAttributes().put("b", Json.createValue("2"));
+        entity.setTitle("t");
+        entity.setDescription("desc");
+        entity.setValue("v");
+
+        final Extension ext = new Extension(ExtensionType.JSON, "a", ExtensionState.OPTIONAL);
+        ext.setJSON("{ \"a\" : 1, \"b\" : \"2\", \"title\" : \"t\", \"description\" : \"desc\", \"value\" : \"v\" }");
+
+        assertEquals(ext.getJSONStructure().asJsonObject(), entity.toJSONObject());
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyTest.java
new file mode 100644
index 0000000..b007391
--- /dev/null
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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.sling.feature.extension.apiregions.api.config;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import javax.json.Json;
+
+import org.apache.sling.feature.Extension;
+import org.apache.sling.feature.ExtensionState;
+import org.apache.sling.feature.ExtensionType;
+import org.junit.Test;
+
+public class PropertyTest {
+
+    @Test public void testClear() {
+        final Property entity = new Property();
+        entity.getAttributes().put("a", Json.createValue(5));
+        entity.setDeprecated("d");
+        entity.setTitle("t");
+        entity.setDescription("x");
+        entity.setCardinality(5);
+        entity.setExcludes(new String[] {"ex"});
+        entity.setIncludes(new String[] {"in"});
+        entity.setOptions(Arrays.asList(new Option()));
+        entity.setRange(new Range());
+        entity.setRegex(".");
+        entity.setRequired(true);
+        entity.setVariable("var");
+        entity.setType(PropertyType.BYTE);
+        entity.clear();
+        assertTrue(entity.getAttributes().isEmpty());
+        assertNull(entity.getDeprecated());
+        assertNull(entity.getTitle());
+        assertNull(entity.getDescription());
+        assertEquals(1, entity.getCardinality());
+        assertNull(entity.getExcludes());
+        assertNull(entity.getIncludes());
+        assertNull(entity.getOptions());
+        assertNull(entity.getRange());
+        assertNull(entity.getRegex());
+        assertNull(entity.getRegexPattern());
+        assertNull(entity.getVariable());
+        assertFalse(entity.isRequired());
+        assertEquals(PropertyType.STRING, entity.getType());
+    }
+
+    @Test public void testFromJSONObject() throws IOException {
+        final Extension ext = new Extension(ExtensionType.JSON, "a", ExtensionState.OPTIONAL);
+        ext.setJSON("{ \"type\" : \"BYTE\", \"cardinality\": 5, \"required\" : true, \"variable\" : \"var\"," +
+        "\"range\" : {}, \"includes\" : [\"in\"], \"excludes\" : [\"ex\"] , \"options\": [{}], \"regex\": \".\"}");
+
+        final Property entity = new Property();
+        entity.fromJSONObject(ext.getJSONStructure().asJsonObject());
+
+        assertEquals(5, entity.getCardinality());
+        assertEquals(PropertyType.BYTE, entity.getType());
+        assertTrue(entity.isRequired());
+        assertEquals("var", entity.getVariable());
+        assertNotNull(entity.getRange());
+        assertArrayEquals(new String[] {"ex"}, entity.getExcludes());
+        assertArrayEquals(new String[] {"in"}, entity.getIncludes());
+        assertEquals(1, entity.getOptions().size());
+        assertEquals(".", entity.getRegex());
+        assertNotNull(entity.getRegexPattern());
+
+        // test defaults and empty values
+        ext.setJSON("{ \"variable\" : \"var\", \"regex\": \".\"}");
+        entity.fromJSONObject(ext.getJSONStructure().asJsonObject());
+
+        assertEquals(1, entity.getCardinality());
+        assertEquals(PropertyType.STRING, entity.getType());
+        assertFalse(entity.isRequired());
+        assertEquals("var", entity.getVariable());
+        assertNull(entity.getRange());
+        assertNull(entity.getExcludes());
+        assertNull(entity.getIncludes());
+        assertNull(entity.getOptions());
+        assertEquals(".", entity.getRegex());
+        assertNotNull(entity.getRegexPattern());
+   }
+
+    @Test public void testToJSONObject() throws IOException {
+        final Property entity = new Property();
+        entity.setCardinality(5);
+        entity.setExcludes(new String[] {"ex"});
+        entity.setIncludes(new String[] {"in"});
+        entity.setOptions(Arrays.asList(new Option()));
+        entity.setRange(new Range());
+        entity.setRegex(".");
+        entity.setRequired(true);
+        entity.setVariable("var");
+        entity.setType(PropertyType.BYTE);
+
+        final Extension ext = new Extension(ExtensionType.JSON, "a", ExtensionState.OPTIONAL);
+        ext.setJSON("{ \"type\" : \"BYTE\", \"cardinality\": 5, \"required\" : true, \"variable\" : \"var\"," +
+            "\"range\" : {}, \"includes\" : [\"in\"], \"excludes\" : [\"ex\"] , \"options\": [{}], \"regex\": \".\"}");
+
+        assertEquals(ext.getJSONStructure().asJsonObject(), entity.toJSONObject());
+
+        // test defaults and empty values
+        entity.setCardinality(1);
+        entity.setType(null);
+        entity.setRequired(false);
+        entity.setRange(null);
+        entity.setOptions(null);
+        entity.setExcludes(null);
+        entity.setIncludes(null);
+
+        ext.setJSON("{ \"type\" : \"STRING\", \"variable\" : \"var\", \"regex\": \".\"}");
+
+        assertEquals(ext.getJSONStructure().asJsonObject(), entity.toJSONObject());
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/RangeTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/RangeTest.java
new file mode 100644
index 0000000..b894daf
--- /dev/null
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/RangeTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.sling.feature.extension.apiregions.api.config;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import javax.json.Json;
+
+import org.apache.sling.feature.Extension;
+import org.apache.sling.feature.ExtensionState;
+import org.apache.sling.feature.ExtensionType;
+import org.junit.Test;
+
+public class RangeTest {
+
+    @Test public void testClear() {
+        final Range entity = new Range();
+        entity.getAttributes().put("a", Json.createValue(5));
+        entity.setMax(5);
+        entity.setMin(20.1);
+        entity.clear();
+        assertTrue(entity.getAttributes().isEmpty());
+        assertNull(entity.getMax());
+        assertNull(entity.getMin());
+    }
+
+    @Test public void testFromJSONObject() throws IOException {
+        final Extension ext = new Extension(ExtensionType.JSON, "a", ExtensionState.OPTIONAL);
+        ext.setJSON("{ \"min\" : 5, \"max\" : 20.1 }");
+
+        final Range entity = new Range();
+        entity.fromJSONObject(ext.getJSONStructure().asJsonObject());
+        assertEquals(5L, entity.getMin());
+        assertEquals(20.1, entity.getMax());
+    }
+
+    @Test public void testToJSONObject() throws IOException {
+        final Range entity = new Range();
+        entity.setMin(5);
+        entity.setMax(20.1);
+
+        final Extension ext = new Extension(ExtensionType.JSON, "a", ExtensionState.OPTIONAL);
+        ext.setJSON("{ \"min\" : 5, \"max\" : 20.1 }");
+
+        assertEquals(ext.getJSONStructure().asJsonObject(), entity.toJSONObject());
+    }
+}
\ No newline at end of file