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}