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 }