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 }