SLING-522 Verify the correctness of the key according to symbolic-name
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@641241 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/configadmin/src/main/java/org/apache/felix/cm/impl/CaseInsensitiveDictionary.java b/configadmin/src/main/java/org/apache/felix/cm/impl/CaseInsensitiveDictionary.java
index 39b992d..e254838 100644
--- a/configadmin/src/main/java/org/apache/felix/cm/impl/CaseInsensitiveDictionary.java
+++ b/configadmin/src/main/java/org/apache/felix/cm/impl/CaseInsensitiveDictionary.java
@@ -31,7 +31,7 @@
* <code>java.util.Dictionary</code> which conforms to the requirements laid
* out by the Configuration Admin Service Specification requiring the property
* names to keep case but to ignore case when accessing the properties.
- *
+ *
* @author fmeschbe
*/
class CaseInsensitiveDictionary extends Dictionary
@@ -43,8 +43,8 @@
private Hashtable internalMap;
/**
- * Mapping of lower case keys to original case keys as last used to set
- * a property value.
+ * Mapping of lower case keys to original case keys as last used to set a
+ * property value.
*/
private Hashtable originalKeys;
@@ -64,10 +64,9 @@
while ( keys.hasMoreElements() )
{
Object key = keys.nextElement();
- if ( !( key instanceof String ) )
- {
- throw new IllegalArgumentException( "Key [" + key + "] must be a String" );
- }
+
+ // check the correct syntax of the key
+ checkKey( key );
// check uniqueness of key
String lowerCase = ( ( String ) key ).toLowerCase();
@@ -94,7 +93,9 @@
}
- /* (non-Javadoc)
+ /*
+ * (non-Javadoc)
+ *
* @see java.util.Dictionary#elements()
*/
public Enumeration elements()
@@ -103,7 +104,9 @@
}
- /* (non-Javadoc)
+ /*
+ * (non-Javadoc)
+ *
* @see java.util.Dictionary#get(java.lang.Object)
*/
public Object get( Object key )
@@ -118,7 +121,9 @@
}
- /* (non-Javadoc)
+ /*
+ * (non-Javadoc)
+ *
* @see java.util.Dictionary#isEmpty()
*/
public boolean isEmpty()
@@ -127,7 +132,9 @@
}
- /* (non-Javadoc)
+ /*
+ * (non-Javadoc)
+ *
* @see java.util.Dictionary#keys()
*/
public Enumeration keys()
@@ -136,7 +143,9 @@
}
- /* (non-Javadoc)
+ /*
+ * (non-Javadoc)
+ *
* @see java.util.Dictionary#put(java.lang.Object, java.lang.Object)
*/
public Object put( Object key, Object value )
@@ -146,11 +155,7 @@
throw new NullPointerException( "key or value" );
}
- if ( !( key instanceof String ) )
- {
- throw new IllegalArgumentException( "Key [" + key + "] must be a String" );
- }
-
+ checkKey( key );
checkValue( value );
String lowerCase = String.valueOf( key ).toLowerCase();
@@ -159,7 +164,9 @@
}
- /* (non-Javadoc)
+ /*
+ * (non-Javadoc)
+ *
* @see java.util.Dictionary#remove(java.lang.Object)
*/
public Object remove( Object key )
@@ -175,7 +182,9 @@
}
- /* (non-Javadoc)
+ /*
+ * (non-Javadoc)
+ *
* @see java.util.Dictionary#size()
*/
public int size()
@@ -184,7 +193,61 @@
}
- //---------- internal -----------------------------------------------------
+ // ---------- internal -----------------------------------------------------
+
+ /**
+ * Ensures the <code>key</code> complies with the <em>symbolic-name</em>
+ * production of the OSGi core specification (1.3.2):
+ *
+ * <pre>
+ * symbolic-name :: = token('.'token)*
+ * digit ::= [0..9]
+ * alpha ::= [a..zA..Z]
+ * alphanum ::= alpha | digit
+ * token ::= ( alphanum | ’_’ | ’-’ )+
+ * </pre>
+ *
+ * If the key does not comply an <code>IllegalArgumentException</code> is
+ * thrown.
+ *
+ * @param key
+ * The configuration property key to check.
+ * @throws IllegalArgumentException
+ * if the key does not comply with the symbolic-name production.
+ */
+ static void checkKey( Object keyObject )
+ {
+ if ( !( keyObject instanceof String ) )
+ {
+ throw new IllegalArgumentException( "Key [" + keyObject + "] must be a String" );
+ }
+
+ String key = ( String ) keyObject;
+ if ( key.startsWith( "." ) || key.endsWith( "." ) )
+ {
+ throw new IllegalArgumentException( "Key [" + key + "] must not start or end with a dot" );
+ }
+
+ int lastDot = Integer.MIN_VALUE;
+ for ( int i = 0; i < key.length(); i++ )
+ {
+ char c = key.charAt( i );
+ if ( c == '.' )
+ {
+ if ( lastDot == i - 1 )
+ {
+ throw new IllegalArgumentException( "Key [" + key + "] must not have consecutive dots" );
+ }
+ lastDot = i;
+ }
+ else if ( ( c < '0' || c > '9' ) && ( c < 'a' || c > 'z' ) && ( c < 'A' || c > 'Z' ) && c != '_'
+ && c != '-' )
+ {
+ throw new IllegalArgumentException( "Key [" + key + "] contains illegal character" );
+ }
+ }
+ }
+
static void checkValue( Object value )
{
@@ -256,7 +319,7 @@
}
- //---------- Object Overwrites --------------------------------------------
+ // ---------- Object Overwrites --------------------------------------------
public String toString()
{
diff --git a/configadmin/src/test/java/org/apache/felix/cm/impl/CaseInsensitiveDictionaryTest.java b/configadmin/src/test/java/org/apache/felix/cm/impl/CaseInsensitiveDictionaryTest.java
new file mode 100644
index 0000000..71aa288
--- /dev/null
+++ b/configadmin/src/test/java/org/apache/felix/cm/impl/CaseInsensitiveDictionaryTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.felix.cm.impl;
+
+import junit.framework.TestCase;
+
+public class CaseInsensitiveDictionaryTest extends TestCase
+{
+
+ public void testValidKeys() {
+ CaseInsensitiveDictionary.checkKey( "a" );
+ CaseInsensitiveDictionary.checkKey( "1" );
+ CaseInsensitiveDictionary.checkKey( "-" );
+ CaseInsensitiveDictionary.checkKey( "_" );
+ CaseInsensitiveDictionary.checkKey( "A" );
+ CaseInsensitiveDictionary.checkKey( "a.b.c" );
+ CaseInsensitiveDictionary.checkKey( "a.1.c" );
+ CaseInsensitiveDictionary.checkKey( "a-sample.dotted_key.end" );
+ }
+
+ public void testKeyDots() {
+ testFailingKey( "." );
+ testFailingKey( ".a.b.c" );
+ testFailingKey( "a.b.c." );
+ testFailingKey( ".a.b.c." );
+ testFailingKey( "a..b" );
+ }
+
+ public void testKeyIllegalCharacters() {
+ testFailingKey( " " );
+ testFailingKey( "§" );
+ testFailingKey( "${yikes}" );
+ testFailingKey( "a key with spaces" );
+ testFailingKey( "fail:key" );
+ }
+
+ private void testFailingKey(String key) {
+ try {
+ CaseInsensitiveDictionary.checkKey( key );
+ fail("Expected IllegalArgumentException for key [" + key + "]");
+ } catch (IllegalArgumentException iae) {
+ // expected
+ }
+ }
+}