/**
   * Search for entry that matches the specific <code>DirectoryQuery</code> conditions
   *
   * @param q DirectoryQuery
   * @return List<DirectoryEntry>
   * @exception LDAPException
   */
  public List<Identity> search(final LDAPDirectoryQuery q) throws LDAPException {
    List<Identity> results = new ArrayList<Identity>();
    try {
      DirContext ctx = connection.connect();
      if (ctx == null) {
        throw new LDAPException("directory service not available");
      }
      SearchControls ctls = new SearchControls();
      List<String> _aux = new ArrayList<String>();
      _aux.add("modifyTimestamp");
      _aux.add("*");
      ctls.setReturningAttributes(_aux.toArray(new String[_aux.size()]));
      if (connection.hasCountLimit()) {
        ctls.setCountLimit(connection.getCountLimit());
      }
      ctls.setSearchScope(connection.getScope());

      String filter = getQueryString(ctx, q);
      NamingEnumeration<SearchResult> answer = ctx.search(baseDN, filter, ctls);
      while (answer.hasMoreElements()) {
        SearchResult sr = answer.nextElement();
        LDAPDirectoryEntry _e = null;
        if (sr.getName().isEmpty()) {
          _e = new LDAPDirectoryEntry(baseDN);
        } else {
          _e = new LDAPDirectoryEntry(sr.getNameInNamespace());
          /*
           * _e = new LDAPEntry(sr.getName() + "," + this.baseDN); if(_e.getID().matches(
           * "^(ldap|ldaps)\\://[a-zA-Z0-9\\-\\.]+\\.[a-zA-Z0-9\\-]+(:[a-zA-Z0-9]*)?/?([a-zA-Z0-9\\-\\._\\?\\,\\'/\\\\\\+&amp;%\\$#\\=~])*[^\\.\\,\\)\\(\\s]$"
           * )) { URL _url = new URL(_e.getID()); _e.setID(_url.getPath()); }
           */
        }
        @SuppressWarnings("unchecked")
        NamingEnumeration<Attribute> ne =
            (NamingEnumeration<Attribute>) sr.getAttributes().getAll();
        while (ne.hasMore()) {
          Attribute att = ne.next();
          Object[] attrs = new Object[att.size()];
          @SuppressWarnings("unchecked")
          NamingEnumeration<Object> nea = (NamingEnumeration<Object>) att.getAll();
          for (int i = 0; nea.hasMore(); i++) {
            attrs[i] = nea.next();
          }
          _e.setAttribute(att.getID(), attrs);
        }
        results.add(_e);
      }
    } catch (NullPointerException e) {
      _log.log(java.util.logging.Level.ALL, "search() null pointer");
      throw new LDAPException("search null pointer");
    } catch (NamingException e) {
      _log.log(java.util.logging.Level.ALL, "search() - " + e.getMessage());
      throw new LDAPException(e.getMessage());
    } finally {
      connection.disconnect();
    }
    return results;
  }
  /**
   * Search for entry that matches the specific <code>DirectoryQuery</code> conditions. Results will
   * be order using the values of a specific attribute
   *
   * @param q DirectoryQuery
   * @param attribute Name of the attribute that determines the order
   * @return java.util.List<DirectoryEntry>
   * @exception LDAPException
   */
  public List<Identity> sortedSearch(final LDAPDirectoryQuery q, final String attribute)
      throws LDAPException {
    TreeMap<String, Identity> results =
        new TreeMap<String, Identity>(Collator.getInstance(new Locale("es")));
    try {
      LdapContext ctx = connection.connect();
      if (ctx == null) {
        throw new LDAPException("Directory service not available");
      }
      SearchControls ctls = new SearchControls();
      if (connection.hasCountLimit()) {
        ctls.setCountLimit(connection.getCountLimit());
      }
      ctls.setSearchScope(connection.getScope());
      ctx.setRequestControls(new Control[] {new SortControl(attribute, Control.NONCRITICAL)});

      String filter = getQueryString(ctx, q);
      NamingEnumeration<SearchResult> answer = ctx.search(baseDN, filter, ctls);
      while (answer.hasMoreElements()) {
        SearchResult sr = answer.nextElement();
        LDAPDirectoryEntry _e = new LDAPDirectoryEntry(sr.getNameInNamespace());
        @SuppressWarnings("unchecked")
        NamingEnumeration<Attribute> ne =
            (NamingEnumeration<Attribute>) sr.getAttributes().getAll();
        while (ne.hasMore()) {
          Attribute att = ne.next();
          Object[] attrs = new Object[att.size()];
          @SuppressWarnings("unchecked")
          NamingEnumeration<Object> nea = (NamingEnumeration<Object>) att.getAll();
          for (int i = 0; nea.hasMore(); i++) {
            attrs[i] = nea.next();
          }
          _e.setAttribute(att.getID(), attrs);
        }
        String _value = String.valueOf(_e.getAttribute(attribute)[0]);
        while (results.containsKey(_value)) {
          _value = _value.concat("0");
        }
        results.put(_value, _e);
      }
    } catch (NullPointerException e) {
      _log.log(java.util.logging.Level.ALL, "sortedSearch() null pointer");
      throw new LDAPException("sorted search null pointer");
    } catch (NamingException e) {
      _log.log(java.util.logging.Level.ALL, "sortedSearch() - " + e.getMessage());
      throw new LDAPException(e.getMessage());
    } catch (IOException e) {
      _log.log(java.util.logging.Level.ALL, "sortedSearch() - " + e.getMessage());
      throw new LDAPException(e.getMessage());
    } finally {
      connection.disconnect();
    }
    return new ArrayList<Identity>(results.values());
  }
 /**
  * Gets an <code>LDAPDirectoryEntry</code> object that represent an entry on directory. You can
  * provide a list of attributes to be ignored when load the entry data. Look for attribute matches
  * using a map of values
  *
  * @param DN Distinguished Name of the entry
  * @param ignore_attributes You can indicate here a list of attribute to be ignored when load all
  *     entry data. this is useful if you have some big data in some attributes and do you want to
  *     ignore that
  * @param attribute_matches Map with attribute names and values to match
  * @return LDAPDirectoryEntry
  * @exception LDAPException
  */
 public LDAPDirectoryEntry getEntry(
     final String DN,
     final List<String> ignore_attributes,
     final Map<String, String> attribute_matches)
     throws LDAPException {
   LDAPDirectoryEntry _e = null;
   try {
     _e = new LDAPDirectoryEntry(DN);
     DirContext ctx = connection.connect();
     if (ctx == null) {
       throw new LDAPException("directory service not available");
     }
     Attributes atts = ctx.getAttributes(DN);
     if (atts == null) {
       return null;
     }
     @SuppressWarnings("unchecked")
     NamingEnumeration<Attribute> ne = (NamingEnumeration<Attribute>) atts.getAll();
     while (ne.hasMore()) {
       Attribute att = ne.next();
       if (ignore_attributes == null || !ignore_attributes.contains(att.getID())) {
         List<Object> _values = new ArrayList<Object>();
         @SuppressWarnings("unchecked")
         NamingEnumeration<Object> nea = (NamingEnumeration<Object>) att.getAll();
         while (nea.hasMore()) {
           Object _value = nea.next();
           if (attribute_matches == null || !attribute_matches.containsKey(att.getID())) {
             _values.add(_value);
           } else if (attribute_matches.get(att.getID()) != null
               && String.valueOf(_value).contains(attribute_matches.get(att.getID()))) {
             _values.add(_value);
           }
         }
         _e.setAttribute(att.getID(), _values.toArray());
       }
     }
   } catch (NullPointerException e) {
     _log.log(java.util.logging.Level.ALL, "getEntry() null pointer");
     throw new LDAPException("get entry null pointer");
   } catch (NamingException e) {
     _log.log(java.util.logging.Level.ALL, "getEntry() - " + e.getMessage());
     throw new LDAPException(e.getMessage());
   } finally {
     connection.disconnect();
   }
   return _e;
 }