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