SLING-10086 : Validate configuration properties case-insensitive
diff --git a/pom.xml b/pom.xml
index 20b2365..4b101f8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -54,6 +54,12 @@
<scope>provided</scope>
</dependency>
<dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.cm.json</artifactId>
+ <version>1.0.4</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.feature</artifactId>
<version>1.2.18</version>
@@ -83,12 +89,6 @@
<version>1.2</version>
<scope>provided</scope>
</dependency>
- <dependency>
- <groupId>org.apache.felix</groupId>
- <artifactId>org.apache.felix.cm.json</artifactId>
- <version>1.0.2</version>
- <scope>provided</scope>
- </dependency>
<!-- Testing -->
<dependency>
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/CaseInsensitiveMap.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/CaseInsensitiveMap.java
deleted file mode 100644
index d7af7ad..0000000
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/CaseInsensitiveMap.java
+++ /dev/null
@@ -1,281 +0,0 @@
-/*
- * 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 java.util.AbstractSet;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-
-class CaseInsensitiveMap<T> implements Map<String, T> {
-
- private final Map<CaseInsensitiveKey, T> map = new LinkedHashMap<>();
-
- @Override
- public void clear() {
- this.map.clear();
- }
-
- @Override
- public boolean containsKey(final Object key) {
- return this.map.containsKey(new CaseInsensitiveKey(key.toString()));
- }
-
- @Override
- public boolean containsValue(final Object value) {
- return this.map.containsValue(value);
- }
-
- @Override
- public Set<Entry<String, T>> entrySet() {
- return new EntrySet();
- }
-
- @Override
- public T get(final Object key) {
- final CaseInsensitiveKey k = new CaseInsensitiveKey(key.toString());
- return this.map.get(k);
- }
-
- @Override
- public boolean isEmpty() {
- return this.map.isEmpty();
- }
-
- @Override
- public Set<String> keySet() {
- return new KeySet();
- }
-
- @Override
- public T put(final String key, final T value) {
- final CaseInsensitiveKey k = new CaseInsensitiveKey(key.toString());
- final T old = this.map.remove(k);
- this.map.put(k, value);
- return old;
- }
-
- @Override
- public void putAll(final Map<? extends String, ? extends T> m) {
- for(final Map.Entry<? extends String, ? extends T> entry : m.entrySet()) {
- this.put(entry.getKey(), entry.getValue());
- }
- }
-
- @Override
- public T remove(final Object key) {
- final CaseInsensitiveKey k = new CaseInsensitiveKey(key.toString());
- return this.map.remove(k);
- }
-
- @Override
- public int size() {
- return this.map.size();
- }
-
- @Override
- public Collection<T> values() {
- return this.map.values();
- }
-
- private static final class CaseInsensitiveKey {
-
- private final String value;
-
- private final int hashCode;
-
- public CaseInsensitiveKey(final String v) {
- this.value = v;
- this.hashCode = v.toUpperCase().hashCode();
- }
-
- /* (non-Javadoc)
- * @see java.lang.Object#hashCode()
- */
- @Override
- public int hashCode() {
- return this.hashCode;
- }
-
- /* (non-Javadoc)
- * @see java.lang.Object#equals(java.lang.Object)
- */
- @Override
- public boolean equals(final Object obj) {
- if (this == obj) {
- return true;
- }
- if (!(obj instanceof CaseInsensitiveKey)) {
- return false;
- }
- final CaseInsensitiveKey other = (CaseInsensitiveKey) obj;
- if ( value == null ) {
- if ( other.value == null ) {
- return true;
- }
- return false;
- }
- if ( other.value == null ) {
- return false;
- }
- return value.equalsIgnoreCase(other.value);
- }
- }
-
- private final class KeySet extends AbstractSet<String> {
-
- @Override
- public int size() {
- return CaseInsensitiveMap.this.size();
- }
-
- @Override
- public boolean isEmpty() {
- return CaseInsensitiveMap.this.isEmpty();
- }
-
- @Override
- public boolean contains(Object o) {
- return CaseInsensitiveMap.this.containsKey(o);
- }
-
- @Override
- public Iterator<String> iterator() {
- return new KeyIterator(CaseInsensitiveMap.this.map.keySet());
- }
-
- @Override
- public boolean remove(Object o) {
- return CaseInsensitiveMap.this.remove(o) != null;
- }
-
- @Override
- public void clear() {
- CaseInsensitiveMap.this.clear();
- }
- }
-
- private static final class KeyIterator implements Iterator<String> {
- private final Iterator<CaseInsensitiveKey> i;
-
- KeyIterator(final Collection<CaseInsensitiveKey> c) {
- this.i = c.iterator();
- }
-
- @Override
- public boolean hasNext() {
- return i.hasNext();
- }
-
- @Override
- public String next() {
- final CaseInsensitiveKey k = i.next();
- return k.value;
- }
-
- @Override
- public void remove() {
- i.remove();
- }
- }
-
- private final class EntrySet extends AbstractSet<Entry<String, T>> {
-
- @Override
- public int size() {
- return CaseInsensitiveMap.this.size();
- }
-
- @Override
- public boolean isEmpty() {
- return CaseInsensitiveMap.this.isEmpty();
- }
-
- @Override
- public Iterator<Entry<String, T>> iterator() {
- return new EntryIterator<>(CaseInsensitiveMap.this.map.entrySet());
- }
-
- @Override
- public void clear() {
- CaseInsensitiveMap.this.clear();
- }
- }
-
- private static final class EntryIterator<T> implements Iterator<Entry<String, T>> {
- private final Iterator<Entry<CaseInsensitiveKey, T>> i;
-
- EntryIterator(final Collection<Entry<CaseInsensitiveKey, T>> c) {
- this.i = c.iterator();
- }
-
- @Override
- public boolean hasNext() {
- return i.hasNext();
- }
-
- @Override
- public Entry<String, T> next() {
- return new CaseInsentiveEntry<>(i.next());
- }
-
- @Override
- public void remove() {
- i.remove();
- }
- }
-
- private static final class CaseInsentiveEntry<T> implements Entry<String, T> {
- private final Entry<CaseInsensitiveKey, T> entry;
-
- CaseInsentiveEntry(final Entry<CaseInsensitiveKey, T> entry) {
- this.entry = entry;
- }
-
- @Override
- public String getKey() {
- return entry.getKey().value;
- }
-
- @Override
- public T getValue() {
- return entry.getValue();
- }
-
- @Override
- public T setValue(final T value) {
- return entry.setValue(value);
- }
-
- @Override
- public int hashCode() {
- return entry.hashCode();
- }
-
- @Override
- public boolean equals(final Object obj) {
- if (obj instanceof CaseInsentiveEntry) {
- final CaseInsentiveEntry<?> other = (CaseInsentiveEntry<?>) obj;
- return Objects.equals(other.entry.getKey(), this.entry.getKey()) && Objects.equals(other.entry.getValue(), this.entry.getValue());
- }
- return false;
- }
- }
-}
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 c7c9f31..15e1319 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
@@ -25,14 +25,16 @@
import javax.json.JsonObjectBuilder;
import javax.json.JsonValue;
+import org.apache.felix.cm.json.Configurations;
+
/**
* A configurable entity has properties
* This class is not thread safe.
*/
public abstract class ConfigurableEntity extends DescribableEntity {
- /** The properties */
- private final Map<String, PropertyDescription> properties = new CaseInsensitiveMap<>();
+ /** The properties */
+ private final Map<String, PropertyDescription> properties = (Map)Configurations.newConfiguration();
/**
* Clear the object and reset to defaults
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/ConfigurationValidator.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/ConfigurationValidator.java
index a0e9bc7..f0315b3 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/ConfigurationValidator.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/validation/ConfigurationValidator.java
@@ -89,8 +89,7 @@
final Dictionary<String, Object> properties = configuration.getConfigurationProperties();
// validate the described properties
for(final Map.Entry<String, PropertyDescription> propEntry : desc.getPropertyDescriptions().entrySet()) {
- // TODO - we need to make a case-insensitive lookup (see SLING-10084)
- final Object value = properties.get(propEntry.getKey());
+ final Object value = properties.get(propEntry.getKey());
final PropertyValidationResult result = propertyValidator.validate(value, propEntry.getValue());
results.put(propEntry.getKey(), result);
}
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/CaseInsensitiveMapTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/CaseInsensitiveMapTest.java
deleted file mode 100644
index ff120b3..0000000
--- a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/CaseInsensitiveMapTest.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import org.junit.Test;
-
-public class CaseInsensitiveMapTest {
-
- @Test public void testKeys() {
- final Map<String, String> map = new CaseInsensitiveMap<>();
- assertNull(map.put("hello", "helloV"));
- assertNull(map.put("world", "worldV"));
-
- assertEquals(2, map.size());
- assertEquals(2, map.values().size());
- assertTrue(map.values().contains("helloV"));
- assertTrue(map.values().contains("worldV"));
-
- assertEquals(2, map.entrySet().size());
- final Map<String, String> m = map.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
- assertEquals("helloV", m.get("hello"));
- assertEquals("worldV", m.get("world"));
-
- Set<String> keys = new HashSet<>(map.keySet());
- assertEquals(2, keys.size());
- assertTrue(keys.contains("hello"));
- assertTrue(keys.contains("world"));
-
- assertEquals("helloV", map.get("hello"));
- assertEquals("worldV", map.get("world"));
- assertNull(map.get("foo"));
-
- assertEquals("helloV", map.get("HELLO"));
- assertEquals("worldV", map.get("WORLD"));
-
- assertEquals("helloV", map.put("heLLo", "bar"));
- assertEquals(2, map.size());
-
- keys = new HashSet<>(map.keySet());
- assertEquals(2, keys.size());
- assertTrue(keys.contains("heLLo"));
- assertTrue(keys.contains("world"));
-
- assertEquals("bar", map.get("hello"));
- assertEquals("worldV", map.get("world"));
-
- assertEquals("bar", map.remove("HellO"));
- assertEquals("worldV", map.remove("WoRlD"));
- assertTrue(map.isEmpty());
- }
-
- @Test public void testClear() {
- final Map<String, String> map = new CaseInsensitiveMap<>();
- map.put("hello", "hello");
- map.put("world", "world");
-
- map.clear();
- assertTrue(map.isEmpty());
- assertEquals(0, map.size());
- assertTrue(map.values().isEmpty());
- assertTrue(map.keySet().isEmpty());
- assertTrue(map.entrySet().isEmpty());
- }
-
- @Test public void testContains() {
- final Map<String, String> map = new CaseInsensitiveMap<>();
- map.put("hello", "helloV");
-
- assertTrue(map.containsKey("hello"));
- assertTrue(map.containsKey("heLLo"));
- assertFalse(map.containsKey("hell o"));
-
- assertTrue(map.containsValue("helloV"));
- assertFalse(map.containsValue("heLLoV"));
- assertFalse(map.containsValue("hello"));
- }
-}