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