001/*
002 * Copyright 2009-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2009-2024 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2009-2024 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.ldap.sdk.migrate.jndi;
037
038
039
040import java.io.Serializable;
041import java.util.Collection;
042import javax.naming.NamingEnumeration;
043import javax.naming.NamingException;
044import javax.naming.directory.Attributes;
045import javax.naming.directory.BasicAttribute;
046import javax.naming.directory.BasicAttributes;
047import javax.naming.directory.DirContext;
048import javax.naming.directory.ModificationItem;
049import javax.naming.directory.SearchResult;
050import javax.naming.ldap.BasicControl;
051import javax.naming.ldap.ExtendedResponse;
052
053import com.unboundid.asn1.ASN1Exception;
054import com.unboundid.asn1.ASN1OctetString;
055import com.unboundid.ldap.sdk.Attribute;
056import com.unboundid.ldap.sdk.Control;
057import com.unboundid.ldap.sdk.DN;
058import com.unboundid.ldap.sdk.Entry;
059import com.unboundid.ldap.sdk.ExtendedRequest;
060import com.unboundid.ldap.sdk.ExtendedResult;
061import com.unboundid.ldap.sdk.Modification;
062import com.unboundid.ldap.sdk.ModificationType;
063import com.unboundid.ldap.sdk.RDN;
064import com.unboundid.util.Debug;
065import com.unboundid.util.NotMutable;
066import com.unboundid.util.NotNull;
067import com.unboundid.util.Nullable;
068import com.unboundid.util.PropertyManager;
069import com.unboundid.util.StaticUtils;
070import com.unboundid.util.ThreadSafety;
071import com.unboundid.util.ThreadSafetyLevel;
072
073
074
075/**
076 * This utility class provides a set of methods that may be used to convert
077 * between data structures in the Java Naming and Directory Interface (JNDI)
078 * and the corresponding data structures in the UnboundID LDAP SDK for Java.
079 */
080@NotMutable()
081@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
082public final class JNDIConverter
083       implements Serializable
084{
085  /**
086   * An empty array of attributes.
087   */
088  @NotNull private static final Attribute[] NO_ATTRIBUTES = new Attribute[0];
089
090
091
092  /**
093   * An empty array of JNDI controls.
094   */
095  @NotNull private static final javax.naming.ldap.Control[] NO_JNDI_CONTROLS =
096       new javax.naming.ldap.Control[0];
097
098
099
100  /**
101   * An empty array of SDK modifications.
102   */
103  @NotNull private static final Modification[] NO_MODIFICATIONS =
104       new Modification[0];
105
106
107
108  /**
109   * An empty array of JNDI modification items.
110   */
111  @NotNull private static final ModificationItem[] NO_MODIFICATION_ITEMS =
112       new ModificationItem[0];
113
114
115
116  /**
117   * The name of a system property that can be used to indicate whether the
118   * encoded value in a JNDI control (as used by the {@link #convertControl}
119   * methods) should include the BER type and length in addition to the bytes of
120   * the value.
121   */
122  @NotNull private static final String
123       PROPERTY_INCLUDE_TYPE_AND_LENGTH_IN_CONTROL_VALUES =
124            JNDIConverter.class.getName() +
125                 ".includeTypeAndLengthInExtendedOpValues";
126
127
128
129  /**
130   * Indicates whether the encoded value in a JNDI control (as used by the
131   * {@link #convertControl} methods) should include the BER type and length in
132   * addition to the bytes of the value.  Although the JNDI Javadoc states that
133   * the encoded value "includes the BER tag and length for the control's
134   * value", testing with the JNDI API indicates that is not actually the case,
135   * and the array should contain only the value and not the type (also known as
136   * the tag) and the length.
137   */
138  private static volatile boolean includeTypeAndLengthInControlValues =
139       PropertyManager.getBoolean(
140            PROPERTY_INCLUDE_TYPE_AND_LENGTH_IN_CONTROL_VALUES, false);
141
142
143
144  /**
145   * The name of a system property that can be used to indicate whether the byte
146   * arrays returned by {@link JNDIExtendedRequest#getEncodedValue()} and
147   * {@link JNDIExtendedResponse#getEncodedValue()} should include the BER type
148   * and length in addition to the bytes of the value.
149   */
150  @NotNull private static final String
151       PROPERTY_INCLUDE_TYPE_AND_LENGTH_IN_EXTENDED_OP_VALUES =
152            JNDIConverter.class.getName() +
153                 ".includeTypeAndLengthInExtendedOpValues";
154
155
156
157  /**
158   * Indicates whether the byte arrays returned by
159   * {@link JNDIExtendedRequest#getEncodedValue()} and
160   * {@link JNDIExtendedResponse#getEncodedValue()} should include the BER type
161   * and length in addition to the type bytes of the value.  Although the JNDI
162   * Javadoc states that the byte arrays should be "the raw BER bytes including
163   * the tag and length", testing with the JNDI API indicates that is not
164   * actually the case, and the array should contain only the value and not
165   * the type (also known as the tag) and the length.
166   */
167  private static volatile boolean includeTypeAndLengthInExtendedOpValues =
168       PropertyManager.getBoolean(
169            PROPERTY_INCLUDE_TYPE_AND_LENGTH_IN_EXTENDED_OP_VALUES, false);
170
171
172
173  /**
174   * The serial version UID for this serializable class.
175   */
176  private static final long serialVersionUID = -6719122549948024736L;
177
178
179
180  /**
181   * Prevent this utility class from being instantiated.
182   */
183  private JNDIConverter()
184  {
185    // No implementation required.
186  }
187
188
189
190  /**
191   * Converts the provided JNDI attribute to an LDAP SDK attribute.
192   *
193   * @param  a  The attribute to be converted.
194   *
195   * @return  The LDAP SDK attribute that corresponds to the provided JNDI
196   *          attribute.
197   *
198   * @throws  NamingException  If a problem is encountered during the conversion
199   *                           process.
200   */
201  @Nullable()
202  public static Attribute convertAttribute(
203                     @Nullable final javax.naming.directory.Attribute a)
204         throws NamingException
205  {
206    if (a == null)
207    {
208      return null;
209    }
210
211    final String name = a.getID();
212    final ASN1OctetString[] values = new ASN1OctetString[a.size()];
213
214    for (int i=0; i < values.length; i++)
215    {
216      final Object value = a.get(i);
217      if (value instanceof byte[])
218      {
219        values[i] = new ASN1OctetString((byte[]) value);
220      }
221      else
222      {
223        values[i] = new ASN1OctetString(String.valueOf(value));
224      }
225    }
226
227    return new Attribute(name, values);
228  }
229
230
231
232  /**
233   * Converts the provided LDAP SDK attribute to a JNDI attribute.
234   *
235   * @param  a  The attribute to be converted.
236   *
237   * @return  The JNDI attribute that corresponds to the provided LDAP SDK
238   *          attribute.
239   */
240  @Nullable()
241  public static javax.naming.directory.Attribute convertAttribute(
242                     @Nullable final Attribute a)
243  {
244    if (a == null)
245    {
246      return null;
247    }
248
249    final BasicAttribute attr = new BasicAttribute(a.getName(), true);
250    for (final String v : a.getValues())
251    {
252      attr.add(v);
253    }
254
255    return attr;
256  }
257
258
259
260  /**
261   * Converts the provided JNDI attributes to an array of LDAP SDK attributes.
262   *
263   * @param  a  The attributes to be converted.
264   *
265   * @return  The array of LDAP SDK attributes that corresponds to the
266   *          provided JNDI attributes.
267   *
268   * @throws  NamingException  If a problem is encountered during the conversion
269   *                           process.
270   */
271  @NotNull()
272  public static Attribute[] convertAttributes(@Nullable final Attributes a)
273         throws NamingException
274  {
275    if (a == null)
276    {
277      return NO_ATTRIBUTES;
278    }
279
280    int i=0;
281    final Attribute[] attributes = new Attribute[a.size()];
282    final NamingEnumeration<? extends javax.naming.directory.Attribute> e =
283         a.getAll();
284
285    try
286    {
287      while (e.hasMoreElements())
288      {
289        attributes[i++] = convertAttribute(e.next());
290      }
291    }
292    finally
293    {
294      e.close();
295    }
296
297    return attributes;
298  }
299
300
301
302  /**
303   * Converts the provided array of LDAP SDK attributes to a set of JNDI
304   * attributes.
305   *
306   * @param  a  The array of attributes to be converted.
307   *
308   * @return  The JNDI attributes that corresponds to the provided LDAP SDK
309   *          attributes.
310   */
311  @NotNull()
312  public static Attributes convertAttributes(@Nullable final Attribute... a)
313  {
314    final BasicAttributes attrs = new BasicAttributes(true);
315    if (a == null)
316    {
317      return attrs;
318    }
319
320    for (final Attribute attr : a)
321    {
322      attrs.put(convertAttribute(attr));
323    }
324
325    return attrs;
326  }
327
328
329
330  /**
331   * Converts the provided collection of LDAP SDK attributes to a set of JNDI
332   * attributes.
333   *
334   * @param  a  The collection of attributes to be converted.
335   *
336   * @return  The JNDI attributes that corresponds to the provided LDAP SDK
337   *          attributes.
338   */
339  @NotNull()
340  public static Attributes convertAttributes(
341                                @Nullable final Collection<Attribute> a)
342  {
343    final BasicAttributes attrs = new BasicAttributes(true);
344    if (a == null)
345    {
346      return attrs;
347    }
348
349    for (final Attribute attr : a)
350    {
351      attrs.put(convertAttribute(attr));
352    }
353
354    return attrs;
355  }
356
357
358
359  /**
360   * Converts the provided JNDI control to an LDAP SDK control.
361   *
362   * @param  c  The control to be converted.
363   *
364   * @return  The LDAP SDK control that corresponds to the provided JNDI
365   *          control.
366   *
367   * @throws  NamingException  If a problem is encountered during the conversion
368   *                           process.
369   */
370  @Nullable
371  public static Control convertControl(
372                             @Nullable final javax.naming.ldap.Control c)
373         throws NamingException
374  {
375    if (c == null)
376    {
377      return null;
378    }
379
380    final ASN1OctetString value;
381    final byte[] valueBytes = c.getEncodedValue();
382    if ((valueBytes == null) || (valueBytes.length == 0))
383    {
384      value = null;
385    }
386    else if (JNDIConverter.includeTypeAndLengthInControlValues)
387    {
388      try
389      {
390        value = ASN1OctetString.decodeAsOctetString(valueBytes);
391      }
392      catch (final ASN1Exception ae)
393      {
394        throw new NamingException(StaticUtils.getExceptionMessage(ae));
395      }
396    }
397    else
398    {
399      value = new ASN1OctetString(valueBytes);
400    }
401
402    return new Control(c.getID(), c.isCritical(), value);
403  }
404
405
406
407  /**
408   * Converts the provided LDAP SDK control to a JNDI control.
409   *
410   * @param  c  The control to be converted.
411   *
412   * @return  The JNDI control that corresponds to the provided LDAP SDK
413   *          control.
414   */
415  @Nullable()
416  public static javax.naming.ldap.Control convertControl(
417                                               @Nullable final Control c)
418  {
419    if (c == null)
420    {
421      return null;
422    }
423
424    final ASN1OctetString value = c.getValue();
425    if (value == null)
426    {
427      return new BasicControl(c.getOID(), c.isCritical(), null);
428    }
429    else if (JNDIConverter.includeTypeAndLengthInControlValues)
430    {
431      return new BasicControl(c.getOID(), c.isCritical(), value.encode());
432    }
433    else
434    {
435      return new BasicControl(c.getOID(), c.isCritical(), value.getValue());
436    }
437  }
438
439
440
441  /**
442   * Converts the provided array of JNDI controls to an array of LDAP SDK
443   * controls.
444   *
445   * @param  c  The array of JNDI controls to be converted.
446   *
447   * @return  The array of LDAP SDK controls that corresponds to the provided
448   *          array of JNDI controls.
449   *
450   * @throws  NamingException  If a problem is encountered during the conversion
451   *                           process.
452   */
453  @NotNull()
454  public static Control[] convertControls(
455                               @Nullable final javax.naming.ldap.Control... c)
456         throws NamingException
457  {
458    if (c == null)
459    {
460      return StaticUtils.NO_CONTROLS;
461    }
462
463    final Control[] controls = new Control[c.length];
464    for (int i=0; i < controls.length; i++)
465    {
466      controls[i] = convertControl(c[i]);
467    }
468
469    return controls;
470  }
471
472
473
474  /**
475   * Converts the provided array of LDAP SDK controls to an array of JNDI
476   * controls.
477   *
478   * @param  c  The array of LDAP SDK controls to be converted.
479   *
480   * @return  The array of JNDI controls that corresponds to the provided array
481   *          of LDAP SDK controls.
482   */
483  @NotNull()
484  public static javax.naming.ldap.Control[] convertControls(
485                                                 @Nullable final Control... c)
486  {
487    if (c == null)
488    {
489      return NO_JNDI_CONTROLS;
490    }
491
492    final javax.naming.ldap.Control[] controls =
493         new javax.naming.ldap.Control[c.length];
494    for (int i=0; i < controls.length; i++)
495    {
496      controls[i] = convertControl(c[i]);
497    }
498
499    return controls;
500  }
501
502
503
504  /**
505   * Converts the provided JNDI extended request to an LDAP SDK extended
506   * request.
507   *
508   * @param  r  The request to be converted.
509   *
510   * @return  The LDAP SDK extended request that corresponds to the provided
511   *          JNDI extended request.
512   *
513   * @throws  NamingException  If a problem is encountered during the conversion
514   *                           process.
515   */
516  @Nullable()
517  public static ExtendedRequest convertExtendedRequest(
518                     @Nullable final javax.naming.ldap.ExtendedRequest r)
519         throws NamingException
520  {
521    if (r == null)
522    {
523      return null;
524    }
525
526    return JNDIExtendedRequest.toSDKExtendedRequest(r);
527  }
528
529
530
531  /**
532   * Converts the provided LDAP SDK extended request to a JNDI extended request.
533   *
534   * @param  r  The request to be converted.
535   *
536   * @return  The JNDI extended request that corresponds to the provided LDAP
537   *          SDK extended request.
538   */
539  @Nullable()
540  public static javax.naming.ldap.ExtendedRequest convertExtendedRequest(
541                     @Nullable final ExtendedRequest r)
542  {
543    if (r == null)
544    {
545      return null;
546    }
547
548    return new JNDIExtendedRequest(r);
549  }
550
551
552
553  /**
554   * Converts the provided JNDI extended response to an LDAP SDK extended
555   * result.
556   *
557   * @param  r  The response to be converted.
558   *
559   * @return  The LDAP SDK extended result that corresponds to the provided
560   *          JNDI extended response.
561   *
562   * @throws  NamingException  If a problem is encountered during the conversion
563   *                           process.
564   */
565  @Nullable()
566  public static ExtendedResult convertExtendedResponse(
567                                    @Nullable final ExtendedResponse r)
568         throws NamingException
569  {
570    if (r == null)
571    {
572      return null;
573    }
574
575    return JNDIExtendedResponse.toSDKExtendedResult(r);
576  }
577
578
579
580  /**
581   * Converts the provided LDAP SDK extended result to a JNDI extended response.
582   *
583   * @param  r  The result to be converted.
584   *
585   * @return  The JNDI extended response that corresponds to the provided LDAP
586   *          SDK extended result.
587   */
588  @Nullable()
589  public static ExtendedResponse convertExtendedResult(
590                                      @Nullable final ExtendedResult r)
591  {
592    if (r == null)
593    {
594      return null;
595    }
596
597    return new JNDIExtendedResponse(r);
598  }
599
600
601
602  /**
603   * Converts the provided JNDI modification item to an LDAP SDK modification.
604   *
605   * @param  m  The JNDI modification item to be converted.
606   *
607   * @return  The LDAP SDK modification that corresponds to the provided JNDI
608   *          modification item.
609   *
610   * @throws  NamingException  If a problem is encountered during the conversion
611   *                           process.
612   */
613  @Nullable()
614  public static Modification convertModification(
615                                  @Nullable final ModificationItem m)
616         throws NamingException
617  {
618    if (m == null)
619    {
620      return null;
621    }
622
623    final ModificationType modType;
624    switch (m.getModificationOp())
625    {
626      case DirContext.ADD_ATTRIBUTE:
627        modType = ModificationType.ADD;
628        break;
629      case DirContext.REMOVE_ATTRIBUTE:
630        modType = ModificationType.DELETE;
631        break;
632      case DirContext.REPLACE_ATTRIBUTE:
633        modType = ModificationType.REPLACE;
634        break;
635      default:
636        throw new NamingException("Unsupported modification type " + m);
637    }
638
639    final Attribute a = convertAttribute(m.getAttribute());
640
641    return new Modification(modType, a.getName(), a.getRawValues());
642  }
643
644
645
646  /**
647   * Converts the provided LDAP SDK modification to a JNDI modification item.
648   *
649   * @param  m  The LDAP SDK modification to be converted.
650   *
651   * @return  The JNDI modification item that corresponds to the provided LDAP
652   *          SDK modification.
653   *
654   * @throws  NamingException  If a problem is encountered during the conversion
655   *                           process.
656   */
657  @Nullable()
658  public static ModificationItem convertModification(
659                                      @Nullable final Modification m)
660         throws NamingException
661  {
662    if (m == null)
663    {
664      return null;
665    }
666
667    final int modType;
668    switch (m.getModificationType().intValue())
669    {
670      case ModificationType.ADD_INT_VALUE:
671        modType = DirContext.ADD_ATTRIBUTE;
672        break;
673      case ModificationType.DELETE_INT_VALUE:
674        modType = DirContext.REMOVE_ATTRIBUTE;
675        break;
676      case ModificationType.REPLACE_INT_VALUE:
677        modType = DirContext.REPLACE_ATTRIBUTE;
678        break;
679      default:
680        throw new NamingException("Unsupported modification type " + m);
681    }
682
683    return new ModificationItem(modType, convertAttribute(m.getAttribute()));
684  }
685
686
687
688  /**
689   * Converts the provided array of JNDI modification items to an array of LDAP
690   * SDK modifications.
691   *
692   * @param  m  The array of JNDI modification items to be converted.
693   *
694   * @return  The array of LDAP SDK modifications that corresponds to the
695   *          provided array of JNDI modification items.
696   *
697   * @throws  NamingException  If a problem is encountered during the conversion
698   *                           process.
699   */
700  @NotNull()
701  public static Modification[] convertModifications(
702                                    @Nullable final ModificationItem... m)
703         throws NamingException
704  {
705    if (m == null)
706    {
707      return NO_MODIFICATIONS;
708    }
709
710    final Modification[] mods = new Modification[m.length];
711    for (int i=0; i < m.length; i++)
712    {
713      mods[i] = convertModification(m[i]);
714    }
715
716    return mods;
717  }
718
719
720
721  /**
722   * Converts the provided array of LDAP SDK modifications to an array of JNDI
723   * modification items.
724   *
725   * @param  m  The array of LDAP SDK modifications to be converted.
726   *
727   * @return  The array of JNDI modification items that corresponds to the
728   *          provided array of LDAP SDK modifications.
729   *
730   * @throws  NamingException  If a problem is encountered during the conversion
731   *                           process.
732   */
733  @NotNull()
734  public static ModificationItem[] convertModifications(
735                                        @Nullable final Modification... m)
736         throws NamingException
737  {
738    if (m == null)
739    {
740      return NO_MODIFICATION_ITEMS;
741    }
742
743    final ModificationItem[] mods = new ModificationItem[m.length];
744    for (int i=0; i < m.length; i++)
745    {
746      mods[i] = convertModification(m[i]);
747    }
748
749    return mods;
750  }
751
752
753
754  /**
755   * Converts the provided JNDI search result object to an LDAP SDK entry.
756   *
757   * @param  r  The JNDI search result object to be converted.
758   *
759   * @return  The LDAP SDK entry that corresponds to the provided JNDI search
760   *          result.
761   *
762   * @throws  NamingException  If a problem is encountered during the conversion
763   *                           process.
764   */
765  @Nullable()
766  public static Entry convertSearchEntry(@Nullable final SearchResult r)
767         throws NamingException
768  {
769    return convertSearchEntry(r, null);
770  }
771
772
773
774  /**
775   * Converts the provided JNDI search result object to an LDAP SDK entry.
776   *
777   * @param  r              The JNDI search result object to be converted.
778   * @param  contextBaseDN  The base DN for the JNDI context over which the
779   *                        search result was retrieved.  If it is
780   *                        non-{@code null} and non-empty, then it will be
781   *                        appended to the result of the {@code getName} method
782   *                        to obtain the entry's full DN.
783   *
784   * @return  The LDAP SDK entry that corresponds to the provided JNDI search
785   *          result.
786   *
787   * @throws  NamingException  If a problem is encountered during the conversion
788   *                           process.
789   */
790  @Nullable
791  public static Entry convertSearchEntry(@Nullable final SearchResult r,
792                                         @Nullable final String contextBaseDN)
793         throws NamingException
794  {
795    if (r == null)
796    {
797      return null;
798    }
799
800    final String dn;
801    if ((contextBaseDN == null) || contextBaseDN.isEmpty())
802    {
803      dn = r.getName();
804    }
805    else
806    {
807      final String name = r.getName();
808      if ((name == null) || name.isEmpty())
809      {
810        dn = contextBaseDN;
811      }
812      else
813      {
814        dn = r.getName() + ',' + contextBaseDN;
815      }
816    }
817
818    return new Entry(dn, convertAttributes(r.getAttributes()));
819  }
820
821
822
823  /**
824   * Converts the provided LDAP SDK entry to a JNDI search result.
825   *
826   * @param  e  The entry to be converted to a JNDI search result.
827   *
828   * @return  The JNDI search result that corresponds to the provided LDAP SDK
829   *          entry.
830   */
831  @Nullable()
832  public static SearchResult convertSearchEntry(@Nullable final Entry e)
833  {
834    return convertSearchEntry(e, null);
835  }
836
837
838
839  /**
840   * Converts the provided LDAP SDK entry to a JNDI search result.
841   *
842   * @param  e              The entry to be converted to a JNDI search result.
843   * @param  contextBaseDN  The base DN for the JNDI context over which the
844   *                        search result was retrieved.  If it is
845   *                        non-{@code null} and non-empty, then it will be
846   *                        removed from the end of the entry's DN in order to
847   *                        obtain the name for the {@code SearchResult} that is
848   *                        returned.
849   *
850   * @return  The JNDI search result that corresponds to the provided LDAP SDK
851   *          entry.
852   */
853  @Nullable()
854  public static SearchResult convertSearchEntry(@Nullable final Entry e,
855                                  @Nullable final String contextBaseDN)
856  {
857    if (e == null)
858    {
859      return null;
860    }
861
862    String name = e.getDN();
863    if ((contextBaseDN != null) && (! contextBaseDN.isEmpty()))
864    {
865      try
866      {
867        final DN parsedEntryDN = e.getParsedDN();
868        final DN parsedBaseDN = new DN(contextBaseDN);
869        if (parsedEntryDN.equals(parsedBaseDN))
870        {
871          name = "";
872        }
873        else if (parsedEntryDN.isDescendantOf(parsedBaseDN, false))
874        {
875          final RDN[] entryRDNs = parsedEntryDN.getRDNs();
876          final RDN[] baseRDNs = parsedBaseDN.getRDNs();
877          final RDN[] remainingRDNs =
878               new RDN[entryRDNs.length - baseRDNs.length];
879          System.arraycopy(entryRDNs, 0, remainingRDNs, 0,
880               remainingRDNs.length);
881          name = new DN(remainingRDNs).toString();
882        }
883      }
884      catch (final Exception ex)
885      {
886        Debug.debugException(ex);
887      }
888    }
889
890    final Collection<Attribute> attrs = e.getAttributes();
891    final Attribute[] attributes = new Attribute[attrs.size()];
892    attrs.toArray(attributes);
893
894    return new SearchResult(name, null, convertAttributes(attributes));
895  }
896
897
898
899  /**
900   * Indicates whether the encoded value in a JNDI control should include the
901   * BER type and length in addition to the bytes of the value.
902   *
903   * @return  {@code true} if the encoded value of a JNDI control should include
904   *          the BER type and length in addition to type bytes of the value, or
905   *          {@code false} if not.
906   */
907  static boolean includeTypeAndLengthInControlValues()
908  {
909    return includeTypeAndLengthInControlValues;
910  }
911
912
913
914  /**
915   * Specifies whether the encoded value in a JNDI control should include the
916   * BER type and length in addition to the bytes of the value.
917   *
918   * @param  includeTypeAndLengthInControlValues  Indicates whether the encoded
919   *                                              value in a JNDI control should
920   *                                              include the BER type and
921   *                                              length in addition to the
922   *                                              bytes of the value.
923   */
924  static void setIncludeTypeAndLengthInControlValues(
925       final boolean includeTypeAndLengthInControlValues)
926  {
927    JNDIConverter.includeTypeAndLengthInControlValues =
928    includeTypeAndLengthInControlValues;
929  }
930
931
932
933  /**
934   * Indicates whether the encoded value in a JNDI extended request or response
935   * should include the BER type and length in addition to the bytes of the
936   * value.
937   *
938   * @return  {@code true} if the encoded value of a JNDI extended request or
939   *          response should include the BER type and length in addition to
940   *          type bytes of the value, or {@code false} if not.
941   */
942  static boolean includeTypeAndLengthInExtendedOpValues()
943  {
944    return includeTypeAndLengthInExtendedOpValues;
945  }
946
947
948
949  /**
950   * Specifies whether the encoded value in a JNDI extended request or response
951   * should include the BER type and length in addition to the bytes of the
952   * value.
953   *
954   * @param  includeTypeAndLengthInExtendedOpValues  Indicates whether the
955   *                                                 encoded value in a JNDI
956   *                                                 extended request or
957   *                                                 response should include the
958   *                                                 BER type and length in
959   *                                                 addition to the bytes of
960   *                                                 the value.
961   */
962  static void setIncludeTypeAndLengthInExtendedOpValues(
963       final boolean includeTypeAndLengthInExtendedOpValues)
964  {
965    JNDIConverter.includeTypeAndLengthInExtendedOpValues =
966    includeTypeAndLengthInExtendedOpValues;
967  }
968}