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 *    http://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.ldap.model.entry.BinaryValue;
024import org.apache.directory.api.ldap.model.exception.LdapException;
025import org.apache.directory.api.ldap.model.schema.AttributeType;
026import org.apache.directory.api.ldap.model.schema.MatchingRule;
027import org.apache.directory.api.ldap.model.schema.Normalizer;
028import org.apache.directory.api.ldap.model.schema.SchemaManager;
029import org.apache.directory.api.util.Hex;
030import org.apache.directory.api.util.Strings;
031
032
033/**
034 * A Dn Name component Normalizer which uses the bootstrap registries to find
035 * the appropriate normalizer for the attribute of the name component with which
036 * to normalize the name component value.
037 *
038 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
039 */
040public class ConcreteNameComponentNormalizer implements NameComponentNormalizer
041{
042    /** the schemaManager used to dynamically resolve Normalizers */
043    private final SchemaManager schemaManager;
044
045
046    /**
047     * Creates a Dn Name component Normalizer which uses the bootstrap
048     * registries to find the appropriate normalizer for the attribute of the
049     * name component with which to normalize the name component value.
050     *
051     * @param schemaManager the schemaManager used to dynamically resolve Normalizers
052     */
053    public ConcreteNameComponentNormalizer( SchemaManager schemaManager )
054    {
055        this.schemaManager = schemaManager;
056    }
057
058
059    private String unescape( String value )
060    {
061        char[] newVal = new char[value.length()];
062        int escaped = 0;
063        char high = 0;
064        char low;
065        int pos = 0;
066
067        for ( int index = 0; index < value.length(); index++  )
068        {
069            char c = value.charAt( index );
070            
071            switch ( escaped )
072            {
073                case 0:
074                    if ( c == '\\' )
075                    {
076                        escaped = 1;
077                    }
078                    else
079                    {
080                        newVal[pos++] = c;
081                    }
082
083                    break;
084
085                case 1:
086                    escaped++;
087                    high = c;
088                    break;
089
090                case 2:
091                    escaped = 0;
092                    low = c;
093                    newVal[pos++] = ( char ) Hex.getHexValue( high, low );
094                    break;
095
096                default:
097                    throw new IllegalStateException( "escaped can never have such a value: " + value );
098            }
099        }
100
101        return new String( newVal, 0, pos );
102    }
103
104
105    /**
106     * {@inheritDoc}
107     */
108    @Override
109    public Object normalizeByName( String name, String value ) throws LdapException
110    {
111        AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( name );
112
113        if ( attributeType.getSyntax().isHumanReadable() )
114        {
115            return lookup( name ).normalize( value );
116        }
117        else
118        {
119            String unescaped = unescape( value );
120            byte[] valBytes = Strings.getBytesUtf8( unescaped );
121
122            return lookup( name ).normalize( new BinaryValue( valBytes ) );
123        }
124
125    }
126
127
128    /**
129     * {@inheritDoc}
130     */
131    @Override
132    public Object normalizeByName( String name, byte[] value ) throws LdapException
133    {
134        AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( name );
135
136        if ( !attributeType.getSyntax().isHumanReadable() )
137        {
138            return lookup( name ).normalize( new BinaryValue( value ) );
139        }
140        else
141        {
142            String valStr = Strings.utf8ToString( value );
143            return lookup( name ).normalize( valStr );
144        }
145    }
146
147
148    /**
149     * {@inheritDoc}
150     */
151    @Override
152    public Object normalizeByOid( String oid, String value ) throws LdapException
153    {
154        return lookup( oid ).normalize( value );
155    }
156
157
158    /**
159     * {@inheritDoc}
160     */
161    @Override
162    public Object normalizeByOid( String oid, byte[] value ) throws LdapException
163    {
164        return lookup( oid ).normalize( new BinaryValue( value ) );
165    }
166
167
168    /**
169     * Looks up the Normalizer to use for a name component using the attributeId
170     * for the name component.  First the attribute is resolved, then its
171     * equality matching rule is looked up.  The normalizer of that matching
172     * rule is returned.
173     *
174     * @param id the name or oid of the attribute in the name component to
175     * normalize the value of
176     * @return the Normalizer to use for normalizing the value of the attribute
177     * @throws LdapException if there are failures resolving the Normalizer
178     */
179    private Normalizer lookup( String id ) throws LdapException
180    {
181        AttributeType type = schemaManager.lookupAttributeTypeRegistry( id );
182        MatchingRule mrule = type.getEquality();
183
184        if ( mrule == null )
185        {
186            return new NoOpNormalizer( id );
187        }
188
189        return mrule.getNormalizer();
190    }
191
192
193    /**
194     * @see NameComponentNormalizer#isDefined(String)
195     */
196    @Override
197    public boolean isDefined( String id )
198    {
199        return schemaManager.getAttributeTypeRegistry().contains( id );
200    }
201
202
203    @Override
204    public String normalizeName( String attributeName ) throws LdapException
205    {
206        return schemaManager.getAttributeTypeRegistry().getOidByName( attributeName );
207    }
208}