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
028  // The following fields have been copied from the DN class in the SDK
029  /** RDN separator for normalized byte string of a DN. */
030  private static final byte NORMALIZED_RDN_SEPARATOR = 0x00;
031  /** AVA separator for normalized byte string of a DN. */
032  private static final byte NORMALIZED_AVA_SEPARATOR = 0x01;
033  /** Escape byte for normalized byte string of a DN. */
034  private static final byte NORMALIZED_ESC_BYTE = 0x02;
035
036  /**
037   * Find the length of bytes that represents the superior DN of the given DN
038   * key. The superior DN is represented by the initial bytes of the DN key.
039   *
040   * @param dnKey
041   *          The key value of the DN.
042   * @return The length of the superior DN or -1 if the given dn is the root DN
043   *         or 0 if the superior DN is removed.
044   */
045  static int findDNKeyParent(ByteSequence dnKey)
046  {
047    if (dnKey.length() == 0)
048    {
049      // This is the root or base DN
050      return -1;
051    }
052
053    // We will walk backwards through the buffer
054    // and find the first unescaped NORMALIZED_RDN_SEPARATOR
055    for (int i = dnKey.length() - 1; i >= 0; i--)
056    {
057      if (positionIsRDNSeparator(dnKey, i))
058      {
059        return i;
060      }
061    }
062    return 0;
063  }
064
065  /**
066   * Create a DN key from an entry DN.
067   *
068   * @param dn The entry DN.
069   * @param prefixRDNs The number of prefix RDNs to remove from the encoded
070   *                   representation.
071   * @return A ByteString containing the key.
072   */
073  static ByteString dnToDNKey(DN dn, int prefixRDNs)
074  {
075    return dn.localName(dn.size() - prefixRDNs).toNormalizedByteString();
076  }
077
078  /**
079   * Returns a best effort conversion from key to a human readable DN.
080   * @param key the index key
081   * @return a best effort conversion from key to a human readable DN.
082   */
083  static String keyToDNString(ByteString key)
084  {
085    return key.toByteString().toASCIIString();
086  }
087
088  private static boolean positionIsRDNSeparator(ByteSequence key, int index)
089  {
090    return index > 0
091        && key.byteAt(index) == NORMALIZED_RDN_SEPARATOR && key.byteAt(index - 1) != NORMALIZED_ESC_BYTE;
092  }
093
094  static ByteStringBuilder beforeFirstChildOf(final ByteSequence key)
095  {
096    final ByteStringBuilder beforeKey = new ByteStringBuilder(key.length() + 1);
097    beforeKey.appendBytes(key);
098    beforeKey.appendByte(NORMALIZED_RDN_SEPARATOR);
099    return beforeKey;
100  }
101
102  static ByteStringBuilder afterLastChildOf(final ByteSequence key)
103  {
104    final ByteStringBuilder afterKey = new ByteStringBuilder(key.length() + 1);
105    afterKey.appendBytes(key);
106    afterKey.appendByte(NORMALIZED_AVA_SEPARATOR);
107    return afterKey;
108  }
109
110  /**
111   * Check if two DN have a parent-child relationship.
112   *
113   * @param parent
114   *          The potential parent
115   * @param child
116   *          The potential child of parent
117   * @return true if child is a direct children of parent, false otherwise.
118   */
119  static boolean isChild(ByteSequence parent, ByteSequence child)
120  {
121    if (child.length() <= parent.length()
122        || child.byteAt(parent.length()) != NORMALIZED_RDN_SEPARATOR
123        || !child.startsWith(parent))
124    {
125      return false;
126    }
127    // Immediate children should only have one RDN separator past the parent length
128    boolean childSeparatorDetected = false;
129    for (int i = parent.length() ; i < child.length(); i++)
130    {
131      if (child.byteAt(i) == NORMALIZED_RDN_SEPARATOR)
132      {
133        if (childSeparatorDetected)
134        {
135          return false;
136        }
137        childSeparatorDetected = true;
138      }
139    }
140    return childSeparatorDetected;
141  }
142}