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