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