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.util;
021    
022    
023    import java.util.ArrayList;
024    import java.util.List;
025    
026    import org.apache.directory.shared.i18n.I18n;
027    import org.apache.directory.shared.ldap.exception.LdapInvalidDnException;
028    import org.apache.directory.shared.ldap.name.DN;
029    
030    
031    /**
032     * Tools dealing with common Naming operations.
033     * 
034     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
035     * @version $Revision: 926996 $
036     */
037    public class NamespaceTools
038    {
039        private static final String[] EMPTY_STRING_ARRAY = new String[0];
040    
041        
042        /**
043         * Gets the attribute of a single attribute rdn or name component.
044         * 
045         * @param rdn the name component
046         * @return the attribute name TODO the name rdn is misused rename refactor
047         *         this method
048         */
049        public static String getRdnAttribute( String rdn )
050        {
051            int index = rdn.indexOf( '=' );
052            return rdn.substring( 0, index );
053        }
054    
055    
056        /**
057         * Gets the value of a single name component of a distinguished name.
058         * 
059         * @param rdn the name component to get the value from
060         * @return the value of the single name component TODO the name rdn is
061         *         misused rename refactor this method
062         */
063        public static String getRdnValue( String rdn )
064        {
065            int index = rdn.indexOf( '=' );
066            return rdn.substring( index + 1, rdn.length() );
067        }
068    
069    
070        /**
071         * Checks to see if two names are siblings.
072         * 
073         * @param name1 the first name
074         * @param name2 the second name
075         * @return true if the names are siblings, false otherwise.
076         */
077        public static boolean isSibling( DN name1, DN name2 ) throws LdapInvalidDnException
078        {
079            if ( name1.size() == name2.size() )
080            {
081                DN parentDn = ( DN ) name1.clone();
082                parentDn.remove( name1.size() - 1 );
083                return name2.isChildOf( parentDn );
084            }
085    
086            return false;
087        }
088    
089    
090        /**
091         * Tests to see if a candidate entry is a descendant of a base.
092         * 
093         * @param ancestor the base ancestor
094         * @param descendant the candidate to test for descendancy
095         * @return true if the candidate is a descendant
096         */
097        public static boolean isDescendant( DN ancestor, DN descendant )
098        {
099            return descendant.isChildOf( ancestor );
100        }
101    
102    
103        /**
104         * Gets the relative name between an ancestor and a potential descendant.
105         * Both name arguments must be normalized. The returned name is also
106         * normalized.
107         * 
108         * @param ancestor the normalized distinguished name of the ancestor context
109         * @param descendant the normalized distinguished name of the descendant context
110         * @return the relative normalized name between the ancestor and the
111         *         descendant contexts
112         * @throws LdapInvalidDnException if the contexts are not related in the ancestual sense
113         */
114        public static DN getRelativeName( DN ancestor, DN descendant ) throws LdapInvalidDnException
115        {
116            DN rdn = null;
117            
118            if ( descendant instanceof DN )
119            {
120                rdn = ( DN ) descendant.clone();
121            }
122            else
123            {
124                rdn = new DN( descendant.toString() );
125            }
126    
127            if ( rdn.isChildOf( ancestor ) )
128            {
129                for ( int ii = 0; ii < ancestor.size(); ii++ )
130                {
131                    rdn.remove( 0 );
132                }
133            }
134            else
135            {
136                LdapInvalidDnException e = new LdapInvalidDnException( I18n.err( I18n.ERR_04417, descendant, ancestor ) );
137    
138                throw e;
139            }
140    
141            return rdn;
142        }
143    
144    
145        /**
146         * Uses the algorithm in <a href="http://www.faqs.org/rfcs/rfc2247.html">RFC
147         * 2247</a> to infer an LDAP name from a Kerberos realm name or a DNS
148         * domain name.
149         * 
150         * @param realm the realm or domain name
151         * @return the LDAP name for the realm or domain
152         */
153        public static String inferLdapName( String realm )
154        {
155            if ( StringTools.isEmpty( realm ) )
156            {
157                return "";
158            }
159    
160            StringBuffer buf = new StringBuffer( realm.length() );
161            buf.append( "dc=" );
162    
163            int start = 0, end = 0;
164    
165            // Replace all the '.' by ",dc=". The comma is added because
166            // the string is not supposed to start with a dot, so another
167            // dc=XXXX already exists in any cases.
168            // The realm is also not supposed to finish with a '.'
169            while ( ( end = realm.indexOf( '.', start ) ) != -1 )
170            {
171                buf.append( realm.substring( start, end ) ).append( ",dc=" );
172                start = end + 1;
173    
174            }
175    
176            buf.append( realm.substring( start ) );
177            return buf.toString();
178        }
179    
180    
181        /**
182         * Gets the '+' appended components of a composite name component.
183         * 
184         * @param compositeNameComponent a single name component not a whole name
185         * @return the components of the complex name component in order
186         * @throws LdapInvalidDnException
187         *             if nameComponent is invalid (starts with a +)
188         */
189        public static String[] getCompositeComponents( String compositeNameComponent ) throws LdapInvalidDnException
190        {
191            int lastIndex = compositeNameComponent.length() - 1;
192            List<String> comps = new ArrayList<String>();
193    
194            for ( int ii = compositeNameComponent.length() - 1; ii >= 0; ii-- )
195            {
196                if ( compositeNameComponent.charAt( ii ) == '+' )
197                {
198                    if ( ii == 0 )
199                    {
200                        throw new LdapInvalidDnException( I18n.err( I18n.ERR_04418, compositeNameComponent ) );
201                    }
202                    
203                    if ( compositeNameComponent.charAt( ii - 1 ) != '\\' )
204                    {
205                        if ( lastIndex == compositeNameComponent.length() - 1 )
206                        {
207                            comps.add( 0, compositeNameComponent.substring( ii + 1, lastIndex + 1 ) );
208                        }
209                        else
210                        {
211                            comps.add( 0, compositeNameComponent.substring( ii + 1, lastIndex ) );
212                        }
213    
214                        lastIndex = ii;
215                    }
216                }
217                
218                if ( ii == 0 )
219                {
220                    if ( lastIndex == compositeNameComponent.length() - 1 )
221                    {
222                        comps.add( 0, compositeNameComponent );
223                    }
224                    else
225                    {
226                        comps.add( 0, compositeNameComponent.substring( ii, lastIndex ) );
227                    }
228    
229                    lastIndex = 0;
230                }
231            }
232    
233            if ( comps.size() == 0 )
234            {
235                comps.add( compositeNameComponent );
236            }
237    
238            return comps.toArray( EMPTY_STRING_ARRAY );
239        }
240    
241    
242        /**
243         * Checks to see if a name has name complex name components in it.
244         * 
245         * @param name The name to check 
246         * @return <code>true</code> if the name has composite components
247         * @throws LdapInvalidDnException If the name is invalid
248         */
249        public static boolean hasCompositeComponents( String name ) throws LdapInvalidDnException
250        {
251            for ( int ii = name.length() - 1; ii >= 0; ii-- )
252            {
253                if ( name.charAt( ii ) == '+' )
254                {
255                    if ( ii == 0 )
256                    {
257                        throw new LdapInvalidDnException( I18n.err( I18n.ERR_04418, name ) );
258                    }
259                    if ( name.charAt( ii - 1 ) != '\\' )
260                    {
261                        return true;
262                    }
263                }
264            }
265    
266            return false;
267        }
268    }