/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE
* or https://OpenDS.dev.java.net/OpenDS.LICENSE.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
* add the following below this CDDL HEADER, with the fields enclosed
* by brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2009 Sun Microsystems, Inc.
*/
package org.opends.server.schema;
import java.util.HashMap;
import java.util.HashSet;
import org.opends.server.types.ByteSequence;
import static org.opends.server.util.Validator.*;
import org.opends.server.util.Platform;
/**
* This class defines the "stringprep" profile as defined in RFC 4518.
* It must be used by all the matching rules that support unicode
* characters. For a complete list of such rules, refer to Section
* 4.2, RFC 4517.
*/
public final class StringPrepProfile
{
/**
* Defines SPACE character.
*/
private static final char SPACE_CHAR = '\u0020';
/**
* Indicates whether case should be folded during string preparation.
*/
public static final boolean CASE_FOLD = true;
/**
* Indicates whether case should not be folded during string preparation.
*/
public static final boolean NO_CASE_FOLD =false;
/**
* Indicates whether leading and trailing spaces should be trimmed during
* string preparation.
*/
public static final boolean TRIM = true;
/**
* Prepares an attribute or assertion value as per stringprep algorithm
* defined in RFC 4518.
*
* @param buffer The buffer to which the prepared form of the string should
* be appended.
* @param sequence The {@link ByteSequence} that needs preparation.
* @param trim Indicates whether leading and trailing spaces should be
* omitted from the string representation.
* @param foldCase Indicates whether the case will be folded during mapping.
* @see
* Internationalized String Preparation
*/
public static void prepareUnicode(StringBuilder buffer,
ByteSequence sequence,
boolean trim,
boolean foldCase)
{
ensureNotNull(buffer);
ensureNotNull(sequence);
//Optimize in the case of purely ascii characters which is the most common
//case.
int length = sequence.length();
for (int i=0; i < length; i++)
{
if((sequence.byteAt(i) & 0x7F) != sequence.byteAt(i))
{
//Map the attribute value.
map(buffer,sequence.subSequence(i, length),trim,foldCase);
//Normalize the attribute value.
normalize(buffer);
break;
}
int buffLen = buffer.length();
switch(sequence.byteAt(i))
{
case ' ':
if ((trim && (buffLen == 0)) ||
(buffLen > 0 && buffer.charAt(buffLen-1)==SPACE_CHAR))
{
break;
}
buffer.append(' ');
break;
default:
byte b = sequence.byteAt(i);
//Perform mapping.
if(b >='\u0009' && b<'\u000E')
{
//These characters are mapped to a SPACE.
buffLen = buffer.length();
if((trim && ( (buffLen ==0) ))
|| (buffLen > 0 && buffer.charAt(buffLen-1) == ' '))
{
/** Do not map this character into a space if:
* a . trimming is desired and this was the leading char.
* b. The last character was a space. **/
break;
}
else
{
buffer.append(SPACE_CHAR);
}
}
else if((b>='\u0000' && b<='\u0008') || (b>='\u000E' && b<='\u001F')
|| b == '\u007F')
{
//These characters are mapped to nothing and hence not copied over..
break;
}
else if (foldCase && b >=65 && b<=90)
{
//If case-folding is allowed then map to the lower case.
buffer.append((char)(b+32));
}
else
{
buffer.append((char)b);
}
break;
}
}
if (trim)
{
// Strip off any trailing spaces.
for (int i=buffer.length()-1; i > 0; i--)
{
if (buffer.charAt(i) == SPACE_CHAR)
{
buffer.delete(i, i+1);
}
else
{
break;
}
}
}
}
//Checks each character and replaces it with its mapping.
private static void map(StringBuilder buffer,
ByteSequence value,
boolean trim,
boolean foldCase)
{
MappingTable.map(buffer,value,trim,foldCase);
}
//Normalizes the input string with NFKC Form.
private static void normalize(StringBuilder buffer)
{
Platform.normalize(buffer);
}
/**
* A Table defining the mapped code-points as per RFC 3454.
*/
private static class MappingTable
{
//Set of chars which are deleted from the incoming value.
private final static HashSet map2null =
new HashSet();
//Set of chars which are replaced by a SPACE when found.
private final static HashSet map2space =
new HashSet();
//Table for case-folding. Map of Character and String containing uppercase
//and lowercase value as the key-value pair.
private final static HashMapcaseMappingTable =
new HashMap();
static
{
//Appendix B.1 RFC 3454.
char[][] mapped2null = new char[][]
{
{'\u0000','\u0008'},{'\u000E','\u001F'},{'\u007F','\u0084'},
{'\u0086','\u009F'},{'\u00AD'}, {'\u034F'},{'\u06DD'},{'\u070F'},
{'\u1806'},{'\u180B','\u180E'},{'\u200C', '\u200F'}, {'\u202A',
'\u202E'},{'\u2060','\u2063'}, {'\u206A','\u206F'},{'\uFE00','\uFE0F'},
{'\uFEFF'},{'\uFFF9','\uFFFC'}
};
for(int i=0;i 0 && buffer.charAt(buffLen-1) == SPACE_CHAR))
{
/** Do not map this character into a space if:
* a . trimming is wanted and this was the first char.
* b. The last character was a space. **/
continue;
}
buffer.append(SPACE_CHAR);
continue;
}
if(foldCase)
{
String mapping = caseMappingTable.get(c);
if(mapping !=null)
{
buffer.append(mapping);
continue;
}
}
//It came here so no match was found.
buffer.append(c);
}
}
}
}