/* * 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); } } } }