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