001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *  
010 *    https://www.apache.org/licenses/LICENSE-2.0
011 *  
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License. 
018 *  
019 */
020package org.apache.directory.api.ldap.model.schema.normalizers;
021
022
023import org.apache.directory.api.i18n.I18n;
024import org.apache.directory.api.ldap.model.entry.Value;
025import org.apache.directory.api.ldap.model.exception.LdapException;
026import org.apache.directory.api.ldap.model.schema.AttributeType;
027import org.apache.directory.api.ldap.model.schema.MatchingRule;
028import org.apache.directory.api.ldap.model.schema.Normalizer;
029import org.apache.directory.api.ldap.model.schema.SchemaManager;
030import org.apache.directory.api.util.Hex;
031import org.apache.directory.api.util.Strings;
032
033
034/**
035 * A Dn Name component Normalizer which uses the bootstrap registries to find
036 * the appropriate normalizer for the attribute of the name component with which
037 * to normalize the name component value.
038 *
039 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
040 */
041public class ConcreteNameComponentNormalizer implements NameComponentNormalizer
042{
043    /** the schemaManager used to dynamically resolve Normalizers */
044    private final SchemaManager schemaManager;
045
046
047    /**
048     * Creates a Dn Name component Normalizer which uses the bootstrap
049     * registries to find the appropriate normalizer for the attribute of the
050     * name component with which to normalize the name component value.
051     *
052     * @param schemaManager the schemaManager used to dynamically resolve Normalizers
053     */
054    public ConcreteNameComponentNormalizer( SchemaManager schemaManager )
055    {
056        this.schemaManager = schemaManager;
057    }
058
059
060    private String unescape( String value )
061    {
062        char[] newVal = new char[value.length()];
063        int escaped = 0;
064        char high = 0;
065        char low;
066        int pos = 0;
067
068        for ( int index = 0; index < value.length(); index++  )
069        {
070            char c = value.charAt( index );
071            
072            switch ( escaped )
073            {
074                case 0:
075                    if ( c == '\\' )
076                    {
077                        escaped = 1;
078                    }
079                    else
080                    {
081                        newVal[pos++] = c;
082                    }
083
084                    break;
085
086                case 1:
087                    escaped++;
088                    high = c;
089                    break;
090
091                case 2:
092                    escaped = 0;
093                    low = c;
094                    newVal[pos++] = ( char ) Hex.getHexValue( high, low );
095                    break;
096
097                default:
098                    throw new IllegalStateException( I18n.err( I18n.ERR_13713_ESCAPED_WRONG_VALUE, value ) );
099            }
100        }
101
102        return new String( newVal, 0, pos );
103    }
104
105
106    /**
107     * {@inheritDoc}
108     */
109    @Override
110    public Object normalizeByName( String name, String value ) throws LdapException
111    {
112        AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( name );
113        Normalizer normalizer = lookup( name );
114
115        if ( attributeType.getSyntax().isHumanReadable() )
116        {
117            return normalizer.normalize( value );
118        }
119        else
120        {
121            String unescaped = unescape( value );
122
123            return normalizer.normalize( unescaped );
124        }
125    }
126
127
128    /**
129     * {@inheritDoc}
130     */
131    @Override
132    public Object normalizeByName( AttributeType attributeType, String value ) throws LdapException
133    {
134        MatchingRule mrule = attributeType.getEquality();
135        Normalizer normalizer;
136            
137        if ( mrule == null )
138        {
139            return new NoOpNormalizer( attributeType.getOid() );
140        }
141        else
142        {
143            normalizer = attributeType.getEquality().getNormalizer();
144        }
145
146        if ( attributeType.getSyntax().isHumanReadable() )
147        {
148            return normalizer.normalize( value );
149        }
150        else
151        {
152            String unescaped = unescape( value );
153
154            return normalizer.normalize( unescaped );
155        }
156    }
157
158
159    /**
160     * {@inheritDoc}
161     */
162    @Override
163    public Object normalizeByName( String name, byte[] value ) throws LdapException
164    {
165        AttributeType attributeType = schemaManager.getAttributeType( name );
166        
167        return new Value( attributeType, value );
168    }
169
170
171    /**
172     * {@inheritDoc}
173     */
174    @Override
175    public Object normalizeByOid( String oid, String value ) throws LdapException
176    {
177        return lookup( oid ).normalize( value );
178    }
179
180
181    /**
182     * {@inheritDoc}
183     */
184    @Override
185    public Object normalizeByOid( String oid, byte[] value ) throws LdapException
186    {
187        return lookup( oid ).normalize( Strings.utf8ToString( value ) );
188    }
189
190
191    /**
192     * Looks up the Normalizer to use for a name component using the attributeId
193     * for the name component.  First the attribute is resolved, then its
194     * equality matching rule is looked up.  The normalizer of that matching
195     * rule is returned.
196     *
197     * @param id the name or oid of the attribute in the name component to
198     * normalize the value of
199     * @return the Normalizer to use for normalizing the value of the attribute
200     * @throws LdapException if there are failures resolving the Normalizer
201     */
202    private Normalizer lookup( String id ) throws LdapException
203    {
204        AttributeType type = schemaManager.lookupAttributeTypeRegistry( id );
205        MatchingRule mrule = type.getEquality();
206
207        if ( mrule == null )
208        {
209            return new NoOpNormalizer( id );
210        }
211
212        return mrule.getNormalizer();
213    }
214
215
216    /**
217     * @see NameComponentNormalizer#isDefined(String)
218     */
219    @Override
220    public boolean isDefined( String id )
221    {
222        return schemaManager.getAttributeTypeRegistry().contains( id );
223    }
224
225
226    /**
227     * {@inheritDoc}
228     */
229    @Override
230    public String normalizeName( String attributeName ) throws LdapException
231    {
232        return schemaManager.getAttributeTypeRegistry().getOidByName( attributeName );
233    }
234}