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 */ 020package org.apache.directory.server.xdbm; 021 022 023import java.io.Externalizable; 024import java.io.IOException; 025import java.io.ObjectInput; 026import java.io.ObjectOutput; 027import java.util.Arrays; 028import java.util.List; 029 030import org.apache.directory.api.ldap.model.name.Rdn; 031 032 033/** 034 * A wrapper for the tuple of parentId and Rdn, used for the Rdn index. 035 * 036 * If the refered entry is a ContextEntry, we may have more than one Rdn stored 037 * 038 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 039 */ 040public class ParentIdAndRdn implements Externalizable, Comparable<ParentIdAndRdn> 041{ 042 /** The entry ID */ 043 protected String parentId; 044 045 /** The list of Rdn for this instance */ 046 protected Rdn[] rdns; 047 048 /** Number of direct children */ 049 protected int nbChildren; 050 051 /** Number of global descendant */ 052 protected int nbDescendants; 053 054 055 /** 056 * Serializable constructor. 057 */ 058 public ParentIdAndRdn() 059 { 060 } 061 062 063 /** 064 * Creates a new instance of ParentIdAndRdn. 065 * 066 * @param parentId the parent ID 067 * @param rdns the RDNs 068 */ 069 public ParentIdAndRdn( String parentId, Rdn... rdns ) 070 { 071 this.parentId = parentId; 072 this.rdns = rdns; 073 } 074 075 076 /** 077 * Creates a new instance of ParentIdAndRdn. 078 * 079 * @param parentId the parent ID 080 * @param rdns the RDNs 081 */ 082 public ParentIdAndRdn( String parentId, List<Rdn> rdns ) 083 { 084 this.parentId = parentId; 085 this.rdns = rdns.toArray( new Rdn[rdns.size()] ); 086 nbChildren = 0; 087 nbDescendants = 0; 088 } 089 090 091 /** 092 * Gets the parent ID. 093 * 094 * @return the parent ID 095 */ 096 public String getParentId() 097 { 098 return parentId; 099 } 100 101 102 /** 103 * Sets the parent ID. 104 * 105 * @param parentId the new parent ID 106 */ 107 public void setParentId( String parentId ) 108 { 109 this.parentId = parentId; 110 } 111 112 113 /** 114 * Gets the RDNs. 115 * 116 * @return the RDNs 117 */ 118 public Rdn[] getRdns() 119 { 120 return rdns; 121 } 122 123 124 /** 125 * Sets the Rdn. 126 * 127 * @param rdns the new Rdn 128 */ 129 public void setRdns( Rdn... rdns ) 130 { 131 this.rdns = rdns; 132 } 133 134 135 @Override 136 public int hashCode() 137 { 138 int h = 37; 139 h = h * 17 + ( ( parentId == null ) ? 0 : parentId.hashCode() ); 140 h = h * 17 + Arrays.hashCode( rdns ); 141 142 return h; 143 } 144 145 146 @Override 147 @SuppressWarnings("unchecked") 148 public boolean equals( Object obj ) 149 { 150 // Shortcut 151 if ( this == obj ) 152 { 153 return true; 154 } 155 156 if ( !( obj instanceof ParentIdAndRdn ) ) 157 { 158 return false; 159 } 160 161 ParentIdAndRdn that = ( ParentIdAndRdn ) obj; 162 163 if ( rdns == null ) 164 { 165 return that.rdns == null; 166 } 167 else if ( that.rdns == null ) 168 { 169 return false; 170 } 171 172 if ( rdns.length != that.rdns.length ) 173 { 174 return false; 175 } 176 177 for ( int i = 0; i < rdns.length; i++ ) 178 { 179 if ( !rdns[i].equals( that.rdns[i] ) ) 180 { 181 return false; 182 } 183 } 184 185 return true; 186 } 187 188 189 /** 190 * {@inheritDoc} 191 */ 192 @Override 193 public int compareTo( ParentIdAndRdn that ) 194 { 195 // Special case when that.rdns = null : we are searching for oneLevel or subLevel scope 196 if ( that.rdns == null ) 197 { 198 int val = parentId.compareTo( that.parentId ); 199 200 if ( val != 0 ) 201 { 202 return val; 203 } 204 else 205 { 206 // The current value is necessarily superior 207 return 1; 208 } 209 } 210 211 if ( rdns == null ) 212 { 213 int res = parentId.compareTo( that.parentId ); 214 215 if ( res == 0 ) 216 { 217 return -1; 218 } 219 else 220 { 221 return res; 222 } 223 } 224 225 int val = parentId.compareTo( that.getParentId() ); 226 227 if ( val != 0 ) 228 { 229 return val; 230 } 231 232 // The ID is the same, check the RDNs now 233 val = rdns.length - that.rdns.length; 234 235 if ( val != 0 ) 236 { 237 return val; 238 } 239 240 if ( rdns.length == 1 ) 241 { 242 // Special case : we only have one rdn. 243 // first try with the normalized name 244 if ( rdns[0].getNormName() != null ) 245 { 246 return rdns[0].getNormName().compareTo( that.rdns[0].getNormName() ); 247 } 248 249 val = rdns[0].compareTo( that.rdns[0] ); 250 251 return val; 252 } 253 else 254 { 255 // We need to compare the Rdns in the order they are given. 256 // Actually, this is a Dn, not a Rdn. 257 for ( int i = 0; i < rdns.length; i++ ) 258 { 259 // first try with the normalized name 260 if ( rdns[i].getNormName() != null ) 261 { 262 return rdns[i].getNormName().compareTo( that.rdns[i].getNormName() ); 263 } 264 265 val = rdns[i].compareTo( that.rdns[i] ); 266 267 if ( val != 0 ) 268 { 269 return val; 270 } 271 } 272 273 return 0; 274 } 275 } 276 277 278 /** 279 * {@inheritDoc} 280 */ 281 @Override 282 public void writeExternal( ObjectOutput out ) throws IOException 283 { 284 out.writeUTF( parentId ); 285 out.writeInt( nbChildren ); 286 out.writeInt( nbDescendants ); 287 out.writeInt( rdns.length ); 288 289 for ( Rdn rdn : rdns ) 290 { 291 rdn.writeExternal( out ); 292 } 293 } 294 295 296 /** 297 * {@inheritDoc} 298 */ 299 @SuppressWarnings("unchecked") 300 @Override 301 public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException 302 { 303 parentId = in.readUTF(); 304 nbChildren = in.readInt(); 305 nbDescendants = in.readInt(); 306 int size = in.readInt(); 307 rdns = new Rdn[size]; 308 309 for ( int i = 0; i < size; i++ ) 310 { 311 Rdn rdn = new Rdn(); 312 rdn.readExternal( in ); 313 rdns[i] = rdn; 314 } 315 } 316 317 318 /** 319 * @return The number of children this entry has 320 */ 321 public int getNbChildren() 322 { 323 return nbChildren; 324 } 325 326 327 /** 328 * Sets the number of children this entry has 329 * @param nbChildren The number of children 330 */ 331 public void setNbChildren( int nbChildren ) 332 { 333 this.nbChildren = nbChildren; 334 } 335 336 337 /** 338 * @return The number of descendants this entry has 339 */ 340 public int getNbDescendants() 341 { 342 return nbDescendants; 343 } 344 345 346 /** 347 * Sets the number of descendants this entry has 348 * 349 * @param nbDescendants The number of descendants 350 */ 351 public void setNbDescendants( int nbDescendants ) 352 { 353 this.nbDescendants = nbDescendants; 354 } 355 356 357 /** 358 * {@inheritDoc} 359 */ 360 @Override 361 public String toString() 362 { 363 StringBuilder sb = new StringBuilder(); 364 365 sb.append( "ParentIdAndRdn<" ); 366 sb.append( parentId ).append( ", '" ); 367 368 if ( rdns == null ) 369 { 370 sb.append( "*'>" ); 371 } 372 else 373 { 374 boolean isFirst = true; 375 376 for ( Rdn rdn : rdns ) 377 { 378 if ( isFirst ) 379 { 380 isFirst = false; 381 } 382 else 383 { 384 sb.append( "," ); 385 } 386 387 sb.append( rdn ); 388 } 389 390 sb.append( "'>" ); 391 392 sb.append( "[nbC:" ).append( nbChildren ).append( ", nbD:" ).append( nbDescendants ).append( "]" ); 393 } 394 395 return sb.toString(); 396 } 397}