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