001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2006-2010 Sun Microsystems, Inc.
015 * Portions Copyright 2014-2016 ForgeRock AS.
016 */
017package org.opends.server.backends.pluggable;
018
019import org.forgerock.opendj.ldap.ByteSequence;
020import org.forgerock.opendj.ldap.ByteString;
021import org.forgerock.opendj.ldap.ByteStringBuilder;
022import org.forgerock.opendj.ldap.DN;
023
024/** Handles the disk representation of LDAP data. */
025public class DnKeyFormat
026{
027  /** The format version used by this class to encode and decode a ByteString. */
028  static final byte FORMAT_VERSION = 0x01;
029
030  // The following fields have been copied from the DN class in the SDK
031  /** RDN separator for normalized byte string of a DN. */
032  private static final byte NORMALIZED_RDN_SEPARATOR = 0x00;
033  /** AVA separator for normalized byte string of a DN. */
034  private static final byte NORMALIZED_AVA_SEPARATOR = 0x01;
035  /** Escape byte for normalized byte string of a DN. */
036  private static final byte NORMALIZED_ESC_BYTE = 0x02;
037
038  /**
039   * Find the length of bytes that represents the superior DN of the given DN
040   * key. The superior DN is represented by the initial bytes of the DN key.
041   *
042   * @param dnKey
043   *          The key value of the DN.
044   * @return The length of the superior DN or -1 if the given dn is the root DN
045   *         or 0 if the superior DN is removed.
046   */
047  static int findDNKeyParent(ByteSequence dnKey)
048  {
049    if (dnKey.length() == 0)
050    {
051      // This is the root or base DN
052      return -1;
053    }
054
055    // We will walk backwards through the buffer
056    // and find the first unescaped NORMALIZED_RDN_SEPARATOR
057    for (int i = dnKey.length() - 1; i >= 0; i--)
058    {
059      if (positionIsRDNSeparator(dnKey, i))
060      {
061        return i;
062      }
063    }
064    return 0;
065  }
066
067  /**
068   * Create a DN key from an entry DN.
069   *
070   * @param dn The entry DN.
071   * @param prefixRDNs The number of prefix RDNs to remove from the encoded
072   *                   representation.
073   * @return A ByteString containing the key.
074   */
075  static ByteString dnToDNKey(DN dn, int prefixRDNs)
076  {
077    return dn.localName(dn.size() - prefixRDNs).toNormalizedByteString();
078  }
079
080  /**
081   * Returns a best effort conversion from key to a human readable DN.
082   * @param key the index key
083   * @return a best effort conversion from key to a human readable DN.
084   */
085  static String keyToDNString(ByteString key)
086  {
087    return key.toByteString().toASCIIString();
088  }
089
090  private static boolean positionIsRDNSeparator(ByteSequence key, int index)
091  {
092    return index > 0
093        && key.byteAt(index) == NORMALIZED_RDN_SEPARATOR && key.byteAt(index - 1) != NORMALIZED_ESC_BYTE;
094  }
095
096  static ByteStringBuilder beforeFirstChildOf(final ByteSequence key)
097  {
098    final ByteStringBuilder beforeKey = new ByteStringBuilder(key.length() + 1);
099    beforeKey.appendBytes(key);
100    beforeKey.appendByte(NORMALIZED_RDN_SEPARATOR);
101    return beforeKey;
102  }
103
104  static ByteStringBuilder afterLastChildOf(final ByteSequence key)
105  {
106    final ByteStringBuilder afterKey = new ByteStringBuilder(key.length() + 1);
107    afterKey.appendBytes(key);
108    afterKey.appendByte(NORMALIZED_AVA_SEPARATOR);
109    return afterKey;
110  }
111
112  /**
113   * Check if two DN have a parent-child relationship.
114   *
115   * @param parent
116   *          The potential parent
117   * @param child
118   *          The potential child of parent
119   * @return true if child is a direct children of parent, false otherwise.
120   */
121  static boolean isChild(ByteSequence parent, ByteSequence child)
122  {
123    if (!child.startsWith(parent))
124    {
125      return false;
126    }
127    // Immediate children should only have one RDN separator past the parent length
128    int nbSeparator = 0;
129    for (int i = parent.length() ; i < child.length(); i++)
130    {
131      if (child.byteAt(i) == NORMALIZED_RDN_SEPARATOR)
132      {
133        nbSeparator++;
134        if (nbSeparator > 1)
135        {
136          return false;
137        }
138      }
139    }
140    return nbSeparator == 1;
141  }
142}