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.shared.ldap.model.schema.normalizers;
021
022
023import java.io.UnsupportedEncodingException;
024
025import org.apache.directory.shared.i18n.I18n;
026import org.apache.directory.shared.ldap.model.entry.BinaryValue;
027import org.apache.directory.shared.ldap.model.exception.LdapException;
028import org.apache.directory.shared.ldap.model.schema.AttributeType;
029import org.apache.directory.shared.ldap.model.schema.MatchingRule;
030import org.apache.directory.shared.ldap.model.schema.Normalizer;
031import org.apache.directory.shared.ldap.model.schema.SchemaManager;
032import org.apache.directory.shared.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     * {@inheritDoc}
108     */
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            try
120            {
121                String unescaped = unescape( value );
122                byte[] valBytes = unescaped.getBytes( "UTF-8" );
123                
124                return lookup( name ).normalize( new BinaryValue( valBytes ) ); 
125            }
126            catch ( UnsupportedEncodingException uee )
127            {
128                String message = I18n.err( I18n.ERR_04222 );
129                LOG.error( message );
130                throw new LdapException( message, uee );
131            }
132        }
133        
134    }
135
136
137    /**
138     * {@inheritDoc}
139     */
140    public Object normalizeByName( String name, byte[] value ) throws LdapException
141    {
142        AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( name );
143        
144        if ( !attributeType.getSyntax().isHumanReadable() )
145        {
146            return lookup( name ).normalize( new BinaryValue( value ) );
147        }
148        else
149        {
150            try
151            {
152                String valStr = new String( value, "UTF-8" );
153                return lookup( name ).normalize( valStr ); 
154            }
155            catch ( UnsupportedEncodingException uee )
156            {
157                String message = I18n.err( I18n.ERR_04223 );
158                LOG.error( message );
159                throw new LdapException( message, uee );
160            }
161        }
162    }
163
164
165    /**
166     * {@inheritDoc}
167     */
168    public Object normalizeByOid( String oid, String value ) throws LdapException
169    {
170        return lookup( oid ).normalize( value );
171    }
172
173
174    /**
175     * {@inheritDoc}
176     */
177    public Object normalizeByOid( String oid, byte[] value ) throws LdapException
178    {
179        return lookup( oid ).normalize( new BinaryValue( value ) );
180    }
181
182
183    /**
184     * Looks up the Normalizer to use for a name component using the attributeId
185     * for the name component.  First the attribute is resolved, then its
186     * equality matching rule is looked up.  The normalizer of that matching
187     * rule is returned.
188     *
189     * @param id the name or oid of the attribute in the name component to
190     * normalize the value of
191     * @return the Normalizer to use for normalizing the value of the attribute
192     * @throws LdapException if there are failures resolving the Normalizer
193     */
194    private Normalizer lookup( String id ) throws LdapException
195    {
196        AttributeType type = schemaManager.lookupAttributeTypeRegistry( id );
197        MatchingRule mrule = type.getEquality();
198        
199        if ( mrule == null )
200        {
201            return new NoOpNormalizer( id );
202        }
203        
204        return mrule.getNormalizer();
205    }
206
207
208    /**
209     * @see NameComponentNormalizer#isDefined(String)
210     */
211    public boolean isDefined( String id )
212    {
213        return schemaManager.getAttributeTypeRegistry().contains( id );
214    }
215
216
217    public String normalizeName( String attributeName ) throws LdapException
218    {
219        return schemaManager.getAttributeTypeRegistry().getOidByName( attributeName );
220    }
221}