From 6a76f80824cb0a6b3671d4051982ec06f63e91ce Mon Sep 17 00:00:00 2001
From: Glenn Van Lint <glenn.vanlint@is4u.be>
Date: Tue, 21 May 2013 22:58:38 +0000
Subject: [PATCH] Fixed bug in ConvertSearchFilter() when there was only 1 subfilter present. Added documentation.

---
 opendj-sdk/opendj3/opendj-virtual/src/main/java/org/forgerock/opendj/virtual/JDBCConnection.java |  681 +++++++++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 522 insertions(+), 159 deletions(-)

diff --git a/opendj-sdk/opendj3/opendj-virtual/src/main/java/org/forgerock/opendj/virtual/JDBCConnection.java b/opendj-sdk/opendj3/opendj-virtual/src/main/java/org/forgerock/opendj/virtual/JDBCConnection.java
index 00d7134..3d26b0a 100644
--- a/opendj-sdk/opendj3/opendj-virtual/src/main/java/org/forgerock/opendj/virtual/JDBCConnection.java
+++ b/opendj-sdk/opendj3/opendj-virtual/src/main/java/org/forgerock/opendj/virtual/JDBCConnection.java
@@ -65,8 +65,11 @@
 import org.forgerock.opendj.ldap.responses.Responses;
 import org.forgerock.opendj.ldap.responses.Result;
 
+/**
+ * JDBC connection implementation.
+ */
 public final class JDBCConnection extends AbstractSynchronousConnection {
-  // For connection to h2 database, use driverName = "org.h2.driver";
+  //For connection to h2 database, use driverName = "org.h2.driver";
   private final String driverName = "com.mysql.jdbc.Driver";
   private java.sql.Connection connection;
   private String connectionUrl;
@@ -74,88 +77,139 @@
   private MappingConfigurationManager mcm;
   private List<Entry> searchEntries = new ArrayList<Entry>();
 
+  /**
+   * Creates a new JDBC connection.
+   *
+   * @param connectionURL
+   *            The URL to connect to the SQL Database Server consisting of the 
+   *            host name, port number and name of the database.
+   */
   JDBCConnection(final String connectionURL) 
   {
     this.connectionUrl = connectionURL;
   }   
 
-  public void initializeMapper(JDBCMapper jdbcmapper) throws SQLException, ErrorResultException, IOException
+  /**
+   * Initializes the mapping component for this connection.
+   *
+   * @param jdbcMapper
+   *            The JDBCMapper object used to map the directory and database 
+   *            structure.
+   */
+  public void initializeMapper(final JDBCMapper jdbcMapper) throws SQLException, ErrorResultException, IOException
   {
-    jdbcm = jdbcmapper;
+    //Load the jdbc mapper with the mapping from the properties file using the Mapping Configuration Manager.
+    jdbcm = jdbcMapper;
     jdbcm.fillMaps();
     mcm = new MappingConfigurationManager(jdbcm);
     jdbcm.loadMappingConfig(mcm.loadMapping());
   }
 
+  /**
+   * Returns the SQL connection used by the SQL driver to connect with the database.
+   *
+   * @return A connection to the database used by the SQL driver.
+   */
   public java.sql.Connection getSqlConnection()
   {
     return connection;
   }
 
+  /**
+   * Adds a record to the Database Server using the provided add request.
+   *       
+   * @param request
+   *            The add request.
+   * @return The result of the operation.
+   * @throws ErrorResultException
+   *            If the result code indicates that the request failed for some
+   *            reason.
+   * @throws SQLException
+   *            If the SQL query has an invalid format.
+   * @throws NullPointerException
+   *            If {@code request} was {@code null}, or if 
+   *            a corresponding mapping value could not be found in the mapping component.
+   */
   @Override
-  public Result add(AddRequest request) throws ErrorResultException
+  public Result add(final AddRequest request) throws ErrorResultException
   {
     Result r;
     try {
+      //Split up the DN the of the request.
       final DN DN = request.getName();
+      final RDN rDN = DN.rdn();
+      final String rDNName = rDN.getFirstAVA().getAttributeType().getNameOrOID();
+      final String rDNValue = rDN.getFirstAVA().getAttributeValue().toString();
       final RDN OU = DN.parent().rdn();
       final String organizationalUnitName = OU.getFirstAVA().getAttributeValue().toString();
       final String baseDN = DN.parent(2).toString();
+
+      //Search mapping for the corresponding table and column names. 
       final String tableName = jdbcm.getTableNameFromMapping(baseDN, organizationalUnitName);
+
+      if(tableName == null) throw new NullPointerException("JDBC Error: Could not find matching table name for OU: '" + organizationalUnitName + "' in DN: '" + baseDN + "'. Please check if mapping was succesful.");            
+      final String rDNColumnName = jdbcm.getColumnNameFromMapping(tableName, baseDN, organizationalUnitName, rDNName); 
+
+      if(rDNColumnName == null) throw new NullPointerException("JDBC Error: Could not find matching column name for attribute: '" + rDNName + "'. Please check if mapping was succesful.");
       final Map<String, Object> columnValuesMap = new HashMap<String, Object>();
       final Iterable<Attribute> attributesCollection = request.getAllAttributes();
       final Iterator<Attribute> attributeIter = attributesCollection.iterator();
 
+      //Search mapping for the corresponding column name for each attribute.
       while(attributeIter.hasNext()){
         final Attribute att = attributeIter.next();
         final Iterator<ByteString> valueIter = att.iterator();
         final String attributeName = att.getAttributeDescriptionAsString();
-        final String columnName = jdbcm.getColumnNameFromMapping(tableName, baseDN, organizationalUnitName, attributeName);
-        String columnValue = "";
 
-        if (columnName == null) continue;
+        if(attributeName == "objectClass") continue;      
+        final String columnName = jdbcm.getColumnNameFromMapping(tableName, baseDN, organizationalUnitName, attributeName);
+
+        if(columnName == null) throw new NullPointerException("JDBC Error: Could not find matching column name for attribute: '" + attributeName + "'. Please check if mapping was succesful.");
+        String columnValue = "";
 
         while(valueIter.hasNext()){
           columnValue = columnValue.concat(valueIter.next().toString());
         }
         columnValuesMap.put(columnName, columnValue);
       }
+
+      //Build the SQL values String.
       final ArrayList<String>columnList = jdbcm.getTableColumns(tableName);
-      String columnNamesString = " (";
-      String columnValuesString = " (";
+      String columnNamesString = " (" + rDNColumnName;
+      String columnValuesString = " ('" + rDNValue + "'";
 
       for(int i = 0; i < columnList.size(); i++){
-
-        if (i > 0){
-          columnNamesString = columnNamesString.concat(", ");
-          columnValuesString = columnValuesString.concat(", ");
-        }
         final String columnName = columnList.get(i);
+
+        if(columnNamesString.contains(columnName)) continue;
         final Object columnDataType = jdbcm.getTableColumnDataType(tableName, columnName);
         Object columnValue = columnValuesMap.get(columnName);
 
         if(columnValue == null){
-
           if(columnDataType.equals(Integer.class)) columnValue = "0";
           else columnValue = "Default Value";
         }
 
         if(columnDataType.equals(Integer.class)) columnValue = Integer.parseInt(columnValue.toString());
-
-        columnNamesString = columnNamesString.concat(columnName);
-        columnValuesString = columnValuesString.concat("'" + columnValue + "'");
+        columnNamesString = columnNamesString.concat(", " + columnName);
+        columnValuesString = columnValuesString.concat(", '" + columnValue + "'");
       }
       columnNamesString = columnNamesString.concat(")");
       columnValuesString = columnValuesString.concat(")");
 
+      //Build the SQL query.
       final Statement st = connection.createStatement();
       final String sql = "INSERT INTO " + tableName + columnNamesString + " VALUES" + columnValuesString;
       st.executeUpdate(sql);
       r = Responses.newResult(ResultCode.SUCCESS);
-
-    } catch (SQLException e) {
+    }catch (SQLException e) {
       System.out.println(e.toString());
       r = Responses.newResult(ResultCode.OPERATIONS_ERROR);
+      r.setCause(e);
+    }catch (NullPointerException e){
+      System.out.println(e.toString());
+      r = Responses.newResult(ResultCode.OPERATIONS_ERROR);
+      r.setCause(e);
     }
     return r;
   }
@@ -166,10 +220,27 @@
     // TODO Auto-generated method stub
   }
 
+  /**
+   * Authenticates to the Database Server using the provided bind request.
+   *       
+   * @param request
+   *            The bind request.
+   * @return The result of the operation.
+   * @throws ErrorResultException
+   *            If the result code indicates that the request failed for some
+   *            reason.
+   * @throws ClassNotFoundException
+   *            If no definition for the class with the specified driver name could be found. 
+   * @throws SQLException
+   *            If the driver could not establish a connection with the 
+   *            Database Server.
+   */
   @Override
-  public BindResult bind(BindRequest request) throws ErrorResultException
+  public BindResult bind(final BindRequest request) throws ErrorResultException
   {
     BindResult r;
+
+    //Extract user name and password from the bind request.
     final String userName = request.getName();
     final String userPass;
     byte[] password = null;
@@ -182,15 +253,16 @@
     }
     userPass = new String(password);
 
+    //Establish SQL connection to the Database Server.
     try {
       Class.forName(driverName);
       this.connection = DriverManager
           .getConnection(this.connectionUrl,userName , userPass);
-    } catch (ClassNotFoundException e) {
+    }catch (ClassNotFoundException e) {
       System.out.println(e.toString());
       r = Responses.newBindResult(ResultCode.OTHER);
       return r;
-    } catch (SQLException e) {
+    }catch (SQLException e) {
       System.out.println(e.toString());
       r = Responses.newBindResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR);
       return r;
@@ -199,8 +271,11 @@
     return r;
   }
 
+  /**
+   * {@inheritDoc}
+   */
   @Override
-  public void close(UnbindRequest request, String reason)
+  public void close(final UnbindRequest request, final String reason)
   {
     if(connection != null){
       try
@@ -214,12 +289,28 @@
     }
   }
 
+  /**
+   * Compares a record in the Database Server using the provided compare request.
+   *       
+   * @param request
+   *            The compare request.
+   * @return The result of the operation.
+   * @throws ErrorResultException
+   *            If the result code indicates that the request failed for some
+   *            reason.
+   * @throws SQLException
+   *            If the SQL query has an invalid format.
+   * @throws NullPointerException
+   *            If {@code request} was {@code null}, or if 
+   *            a corresponding mapping value could not be found in the mapping component.
+   */
   @Override
-  public CompareResult compare(CompareRequest request) throws ErrorResultException
+  public CompareResult compare(final CompareRequest request) throws ErrorResultException
   {
     CompareResult cr;
     try
     {
+      //Split up the DN the of the request.
       final DN DN = request.getName();
       final RDN rDN = DN.rdn();
       final String filterAttributeName = rDN.getFirstAVA().getAttributeType().getNameOrOID();
@@ -227,31 +318,61 @@
       final RDN OU = DN.parent(1).rdn();
       final String OUName = OU.getFirstAVA().getAttributeValue().toString();
       final String baseDN = DN.parent(2).toString();
+
+      //Search mapping for the corresponding table and column names. 
       final String tableName = jdbcm.getTableNameFromMapping(baseDN, OUName);
+
+      if(tableName == null) throw new NullPointerException("JDBC Error: Could not find matching table name for OU: '" + OUName + "' in DN: '" + baseDN + "'. Please check if mapping was succesful.");            
       final String columnName = jdbcm.getColumnNameFromMapping(tableName, baseDN, OUName, filterAttributeName);  
+
+      if(columnName == null) throw new NullPointerException("JDBC Error: Could not find matching column name for attribute: '" + filterAttributeName + "'. Please check if mapping was succesful.");
+
+      //Search mapping for the corresponding column name for the comparing attribute.
       final String compareAttributeName = request.getAttributeDescription().toString();
       final String compareAttributeValue = request.getAssertionValueAsString();
       final String compareColumnName = jdbcm.getColumnNameFromMapping(tableName, baseDN, OUName, compareAttributeName);
 
+      if(compareColumnName == null) throw new NullPointerException("JDBC Error: Could not find matching column name for attribute: '" + compareAttributeName + "'. Please check if mapping was succesful.");    
+
+      //Build the SQL query.
       final Statement st = connection.createStatement();
       final String sql = "SELECT * FROM " + tableName + " WHERE " + columnName + "='" + filterAttributeValue + "' AND " + compareColumnName + "='" +  compareAttributeValue + "'";
       final ResultSet rs = st.executeQuery(sql);
       if(rs.first()) cr = Responses.newCompareResult(ResultCode.COMPARE_TRUE);
       else cr = Responses.newCompareResult(ResultCode.COMPARE_FALSE);
-    }
-    catch (SQLException e)
-    {
+    }catch (SQLException e) {
+      System.out.println(e.toString());
       cr = Responses.newCompareResult(ResultCode.OPERATIONS_ERROR);
-      e.printStackTrace();
+      cr.setCause(e);
+    }catch (NullPointerException e){
+      System.out.println(e.toString());
+      cr = Responses.newCompareResult(ResultCode.OPERATIONS_ERROR);
+      cr.setCause(e);
     }
     return cr;
   }
 
+  /**
+   * Deletes a record from the Database Server using the provided delete request.
+   *       
+   * @param request
+   *            The delete request.
+   * @return The result of the operation.
+   * @throws ErrorResultException
+   *            If the result code indicates that the request failed for some
+   *            reason.
+   * @throws SQLException
+   *            If the SQL query has an invalid format.
+   * @throws NullPointerException
+   *            If {@code request} was {@code null}, or if 
+   *            a corresponding mapping value could not be found in the mapping component.
+   */
   @Override
   public Result delete(DeleteRequest request) throws ErrorResultException
   {
     Result r;
     try{
+      //Split up the DN the of the request.
       final DN DN = request.getName();
       final RDN rDN = DN.rdn();
       final String filterAttributeName = rDN.getFirstAVA().getAttributeType().getNameOrOID();
@@ -259,16 +380,28 @@
       final RDN OU = DN.parent(1).rdn();
       final String OUName = OU.getFirstAVA().getAttributeValue().toString();
       final String baseDN = DN.parent(2).toString();
+
+      //Search mapping for the corresponding table and column names.
       final String tableName = jdbcm.getTableNameFromMapping(baseDN, OUName);
+
+      if(tableName == null) throw new NullPointerException("JDBC Error: Could not find matching table name for OU: '" + OUName + "' in DN: '" + baseDN + "'. Please check if mapping was succesful.");            
       final String columnName = jdbcm.getColumnNameFromMapping(tableName, baseDN, OUName, filterAttributeName);
 
+      if(columnName == null) throw new NullPointerException("JDBC Error: Could not find matching column name for attribute: '" + filterAttributeName + "'. Please check if mapping was succesful.");
+
+      //Build the SQL query.
       final Statement st = connection.createStatement();
       final String sql = "DELETE FROM " + tableName + " WHERE " + columnName + "='" + filterAttributeValue + "'";
       st.executeUpdate(sql);
       r = Responses.newResult(ResultCode.SUCCESS);
-    } catch (SQLException e) {
+    }catch (SQLException e) {
       System.out.println(e.toString());
       r = Responses.newResult(ResultCode.OPERATIONS_ERROR);
+      r.setCause(e);
+    }catch (NullPointerException e){
+      System.out.println(e.toString());
+      r = Responses.newCompareResult(ResultCode.OPERATIONS_ERROR);
+      r.setCause(e);
     }
     return r;
   }
@@ -282,6 +415,9 @@
     return null;
           }
 
+  /**
+   * {@inheritDoc}
+   */
   @Override
   public boolean isClosed()
   {
@@ -296,6 +432,9 @@
     }
   }
 
+  /**
+   * {@inheritDoc}
+   */
   @Override
   public boolean isValid()
   {
@@ -310,11 +449,27 @@
     }
   }
 
+  /**
+   * Modifies a record in the Database Server using the provided modify request.
+   *       
+   * @param request
+   *            The modify request.
+   * @return The result of the operation.
+   * @throws ErrorResultException
+   *            If the result code indicates that the request failed for some
+   *            reason.
+   * @throws SQLException
+   *            If the SQL query has an invalid format.
+   * @throws NullPointerException
+   *            If {@code request} was {@code null}, or if 
+   *            a corresponding mapping value could not be found in the mapping component.
+   */
   @Override
-  public Result modify(ModifyRequest request)
+  public Result modify(final ModifyRequest request)
   { 
     Result r;
     try{
+      //Split up the DN the of the request.
       final DN DN = request.getName();
       final RDN rDN = DN.rdn();
       final String filterAttributeName = rDN.getFirstAVA().getAttributeType().getNameOrOID();
@@ -322,8 +477,17 @@
       final RDN OU = DN.parent(1).rdn();
       final String OUName = OU.getFirstAVA().getAttributeValue().toString();
       final String baseDN = DN.parent(2).toString();
+
+      //Search mapping for the corresponding table and column names. 
       final String tableName = jdbcm.getTableNameFromMapping(baseDN, OUName);
+
+      if(tableName == null) throw new NullPointerException("JDBC Error: Could not find matching table name for OU: '" + OUName + "' in DN: '" + baseDN + "'. Please check if mapping was succesful.");            
       final String columnName = jdbcm.getColumnNameFromMapping(tableName, baseDN, OUName, filterAttributeName);
+
+      if(columnName == null) throw new NullPointerException("JDBC Error: Could not find matching column name for attribute: '" + filterAttributeName + "'. Please check if mapping was succesful.");
+
+      //Get attribute and modificationtype for each modification.
+      //Search mapping for the corresponding column name for the modification attribute.
       final List<Modification> modificationList = request.getModifications();
       final ListIterator<Modification> listIter = modificationList.listIterator();
       String modificationString = "";
@@ -334,6 +498,8 @@
         final Attribute modificationAttribute = modification.getAttribute();
         final String modificationAttributeName = modificationAttribute.getAttributeDescription().toString();
         final String modificationColumnName = jdbcm.getColumnNameFromMapping(tableName, baseDN, OUName, modificationAttributeName);
+
+        if(modificationColumnName == null) throw new NullPointerException("JDBC Error: Could not find matching column name for attribute: '" + modificationAttributeName + "'. Please check if mapping was succesful.");     
         String modificationAttributeValue = "";
 
         if(modificationType == ModificationType.ADD){
@@ -346,10 +512,11 @@
             counter++;
           }
           final Object classType = jdbcm.getTableColumnDataType(tableName, modificationColumnName);
-          if(classType == Integer.class) modificationAttributeValue = "(case when (" + modificationAttributeName + " = 0) THEN ' "
-              + modificationAttributeValue + "' ELSE concat(" + modificationAttributeName + ", ', " + modificationAttributeValue + "') END)";
-          else modificationAttributeValue = "(case when (" + modificationAttributeName + " = 'Default Value') THEN ' "
-              + modificationAttributeValue + "' ELSE concat(" + modificationAttributeName + ", ', " + modificationAttributeValue + "') END)";
+
+          if(classType == Integer.class) modificationAttributeValue = "(CASE WHEN (" + modificationColumnName + " = 0) THEN ' "
+              + modificationAttributeValue + "' ELSE concat(" + modificationColumnName + ", ', " + modificationAttributeValue + "') END)";
+          else modificationAttributeValue = "(CASE WHEN (" + modificationColumnName + " = 'Default Value') THEN ' "
+              + modificationAttributeValue + "' ELSE concat(" + modificationColumnName + ", ', " + modificationAttributeValue + "') END)";
         }        
         else if(modificationType == ModificationType.REPLACE){
           final Iterator<ByteString> iter = modificationAttribute.iterator();
@@ -370,14 +537,20 @@
         modificationString = modificationString.concat(modificationColumnName + "=" + modificationAttributeValue + ", ");
       }
       modificationString = modificationString.substring(0, modificationString.length() - 2);
-      final String sql = "UPDATE " + tableName + " SET " + modificationString + " WHERE " + columnName + "='" + filterAttributeValue + "'";
 
+      //Build the SQL query.
       final Statement st = connection.createStatement();
+      final String sql = "UPDATE " + tableName + " SET " + modificationString + " WHERE " + columnName + "='" + filterAttributeValue + "'";
       st.executeUpdate(sql);
       r = Responses.newResult(ResultCode.SUCCESS);
-    }catch (SQLException e){
+    }catch (SQLException e) {
       System.out.println(e.toString());
       r = Responses.newResult(ResultCode.OPERATIONS_ERROR);
+      r.setCause(e);
+    }catch (NullPointerException e){
+      System.out.println(e.toString());
+      r = Responses.newCompareResult(ResultCode.OPERATIONS_ERROR);
+      r.setCause(e);
     }
     return r;
   }
@@ -395,123 +568,92 @@
     // TODO Auto-generated method stub
   }
 
+  /**
+   * Converts the LDAP filter to an SQL filter.
+   *       
+   * @param filter
+   *            The filter of the search request.
+   * @param baseDN
+   *            The top domain name.
+   * @param OUName
+   *            The name of the organizational unit.            
+   * @return The converted SQL filter.
+   * @throws NullPointerException
+   *            If the result code indicates that the request failed for some
+   *            reason.
+   * @throws SQLException
+   *            If the SQL query has an invalid format.
+   * @throws NullPointerException
+   *            If {@code filter}, {@code baseDN} or {@code OUName}  was {@code null}, or if 
+   *            a corresponding mapping value could not be found in the mapping component.
+   */
   private String convertSearchFilter(final Filter filter, final String baseDN, String OUName)
   {
-    String filterString = filter.toString();
-    int stringIndex = 0;
-    int subStringCount = 0;
+    try{
+      String filterString = filter.toString();
+      String convertedFilterString = "";
 
-    while(filterString.charAt(stringIndex) == '('){
-      if(filterString.charAt(stringIndex + 2) == '('){
-        subStringCount++;
-      }
-      stringIndex += 2;
-    }
-    int subStringStartIndex = 2 * subStringCount;
-    int subStringEndIndex = 0;
-    final String[] subStrings = new String[subStringCount];
-    final char[] operationChars = new char[subStringCount];
+      if(filterString.isEmpty() || filterString.contains("objectClass=*")) return "";
+      int stringIndex = 0;
+      int subStringCount = 0;
 
-    while(subStringStartIndex > 0){
-      final char operationChar = filterString.charAt(subStringStartIndex - 1);
-      subStringEndIndex = filterString.indexOf("))") + 1;
-      String subString = filterString.substring(subStringStartIndex, subStringEndIndex);
-      subString = subString.replace("()","");
 
-      operationChars[subStringCount - subStringStartIndex / 2] = operationChar;
-      subStrings[subStringCount - subStringStartIndex / 2] = subString;
-
-      final String replaceString = filterString.substring(subStringStartIndex - 1, subStringEndIndex);
-      filterString = filterString.replace(replaceString, "");
-
-      subStringStartIndex-=2;
-    }
-    String convertedFilterString = "";
-
-    for(int i = 0; i < subStringCount; i++){
-      final char operationChar = operationChars[i];
-
-      if(operationChar == '!'){
-        String subString = subStrings[i];
-
-        if(subString.isEmpty()){
-          subString = subStrings[i-1];
-          subString = subString.replace(">=", ">");
-          subString = subString.replace("<=", "<");
-          subString = subString.replace("=", "!=");
-          subString = subString.replace(">", ">=");
-          subString = subString.replace("<", "<=");
-          subStrings[i-1] = subString;
+      //Set the amount of subfilters if operators are present.
+      while(filterString.charAt(stringIndex) == '('){
+        if(filterString.charAt(stringIndex + 2) == '('){
+          subStringCount++;
         }
-        else{
-          subString = subString.replace(">=", ">");
-          subString = subString.replace("<=", "<");
-          subString = subString.replace("=", "!=");
-          subString = subString.replace(">", ">=");
-          subString = subString.replace("<", "<=");
-          subStrings[i] = subString;
-        }
-      }
-    }
-    boolean multipleSubStrings = false;
-
-    if(subStringCount > 1) multipleSubStrings = true;
-
-    for(int i = 0; i < subStringCount; i++){
-      final char operationChar = operationChars[i];
-      String operationString = "";
-
-      if(operationChar == '&'){
-        operationString = " AND ";
-      }
-      else if (operationChar == '|'){
-        operationString = " OR ";
+        stringIndex += 2;
       }
 
-      String subString = subStrings[i];
-      if (subString.isEmpty()) continue;
+      if(subStringCount == 0){
+        //Replace escape characters.
+        String subString = filterString.substring(1, filterString.length()-1);
+        subString = subString.replace("\\02a", "*");
+        subString = subString.replace("\\028", "(");
+        subString = subString.replace("\\029", ")");
 
-      subString = subString.substring(1, subString.length() - 1);
-      String[] subStringSplitter = subString.split("\\)\\(");
+        String[] subStringSplitter;
 
-      for(int j = 0; j < subStringSplitter.length; j++){
-        String subStringFilter = subStringSplitter[j];
-        final String[] subStringFilterSplitter;
+        if(subString.contains("<=")) subStringSplitter = subString.split("<=");
+        else if(subString.contains(">=")) subStringSplitter = subString.split(">=");
+        else subStringSplitter = subString.split("=");
 
-        subStringFilter = subStringFilter.replace("\\02a", "*");
-        subStringFilter = subStringFilter.replace("\\028", "(");
-        subStringFilter = subStringFilter.replace("\\029", ")");
-
-        if(subStringFilter.contains("!=")) subStringFilterSplitter = subStringFilter.split("!=");
-        else if(subStringFilter.contains("<=")) subStringFilterSplitter = subStringFilter.split("<=");
-        else if(subStringFilter.contains(">=")) subStringFilterSplitter = subStringFilter.split(">=");
-        else subStringFilterSplitter = subStringFilter.split("=");
-
-        final String filterAttributeName = subStringFilterSplitter[0];
+        final String filterAttributeName = subStringSplitter[0];
         String filterColumnName = null;;
         Object columnDataType = null;
 
-        if(OUName == null){
+        //Search mapping for the corresponding table and column names. 
+        if(OUName.isEmpty()){
           final List<String> OUList = jdbcm.getOrganizationalUnits(baseDN);
 
           for(Iterator<String> iter = OUList.iterator(); iter.hasNext();){
             OUName = iter.next();
             final String tableName = jdbcm.getTableNameFromMapping(baseDN, OUName);
+
+            if(tableName == null) continue;            
             filterColumnName = jdbcm.getColumnNameFromMapping(tableName, baseDN, OUName, filterAttributeName);
 
             if(filterColumnName == null) continue;
             else columnDataType = jdbcm.getTableColumnDataType(tableName, filterColumnName);
             break;
           }
+
+          if(filterColumnName == null) throw new NullPointerException("JDBC Error: Could not find matching column name for attribute: '" + filterAttributeName + "'. Please check if mapping was succesful.");  
         }
         else{
           final String tableName = jdbcm.getTableNameFromMapping(baseDN, OUName);
-          filterColumnName = jdbcm.getColumnNameFromMapping(tableName, baseDN, OUName, filterAttributeName);     
+
+          if(tableName == null) throw new NullPointerException("JDBC Error: Could not find matching table name for OU: '" + OUName + "' in DN: '" + baseDN + "'. Please check if mapping was succesful.");            
+          filterColumnName = jdbcm.getColumnNameFromMapping(tableName, baseDN, OUName, filterAttributeName);  
+
+          if(filterColumnName == null) throw new NullPointerException("JDBC Error: Could not find matching column name for attribute: '" + filterAttributeName + "'. Please check if mapping was succesful.");  
           columnDataType = jdbcm.getTableColumnDataType(tableName, filterColumnName);
         }
 
+        //Set values to right format.
         if(columnDataType != Integer.class){
-          String filterAttributeValue = subStringFilterSplitter[1];
+          String filterAttributeValue = subStringSplitter[1];
           String filterColumnValue = "'" + filterAttributeValue + "'";
           filterAttributeValue = filterAttributeValue.replace("*", "\\*");
           filterAttributeValue = filterAttributeValue.replace("(", "\\(");
@@ -520,34 +662,194 @@
           if(filterColumnValue.length() > 3 && filterColumnValue.contains("*")){
             filterColumnValue = filterColumnValue.replace("*", "%");
 
-            if(subStringFilter.contains("!=")) subStringFilter = subStringFilter.replace("!=", " not like ");
-            else subStringFilter = subStringFilter.replace("=", " like ");
+            subString = subString.replace("=", " like ");
           }
-          subStringFilter = subStringFilter.replaceFirst(filterAttributeValue, filterColumnValue);
+          subString = subString.replaceFirst(filterAttributeValue, filterColumnValue);
+        }
+        subString = subString.replaceFirst(filterAttributeName, filterColumnName);
+        convertedFilterString = convertedFilterString.concat(subString);
+      }
+      else
+      {
+        int subStringStartIndex = 2 * subStringCount;
+        int subStringEndIndex = 0;
+        final String[] subStrings = new String[subStringCount];
+        final char[] operationChars = new char[subStringCount];
+
+        //Separate operation characters and subfilters.
+        while(subStringStartIndex > 0){
+          final char operationChar = filterString.charAt(subStringStartIndex - 1);
+          subStringEndIndex = filterString.indexOf("))") + 1;
+          String subString = filterString.substring(subStringStartIndex, subStringEndIndex);
+          subString = subString.replace("()","");
+
+          operationChars[subStringCount - subStringStartIndex / 2] = operationChar;
+          subStrings[subStringCount - subStringStartIndex / 2] = subString;
+
+          final String replaceString = filterString.substring(subStringStartIndex - 1, subStringEndIndex);
+          filterString = filterString.replace(replaceString, "");
+
+          subStringStartIndex-=2;
         }
 
-        if(filterColumnName == null) continue;
-        subStringFilter = subStringFilter.replaceFirst(filterAttributeName, filterColumnName);
+        //Handle the not operator.
+        for(int i = 0; i < subStringCount; i++){
+          final char operationChar = operationChars[i];
 
-        if(j != 0 || i != 0) convertedFilterString = convertedFilterString.concat(operationString);
-        convertedFilterString = convertedFilterString.concat(subStringFilter);
+          if(operationChar == '!'){
+            String subString = subStrings[i];
+
+            if(subString.isEmpty()){
+              subString = subStrings[i-1];
+              subString = subString.replace(">=", ">");
+              subString = subString.replace("<=", "<");
+              subString = subString.replace("=", "!=");
+              subString = subString.replace(">", ">=");
+              subString = subString.replace("<", "<=");
+              subStrings[i-1] = subString;
+            }
+            else{
+              subString = subString.replace(">=", ">");
+              subString = subString.replace("<=", "<");
+              subString = subString.replace("=", "!=");
+              subString = subString.replace(">", ">=");
+              subString = subString.replace("<", "<=");
+              subStrings[i] = subString;
+            }
+          }
+        }
+        boolean multipleSubStrings = false;
+
+        if(subStringCount > 1) multipleSubStrings = true;
+
+        //Convert each subfilter with corresponding operator.
+        for(int i = 0; i < subStringCount; i++){
+          final char operationChar = operationChars[i];
+          String operationString = "";
+
+          if(operationChar == '&'){
+            operationString = " AND ";
+          }
+          else if (operationChar == '|'){
+            operationString = " OR ";
+          }
+
+          String subString = subStrings[i];
+          if (subString.isEmpty()) continue;
+
+          subString = subString.substring(1, subString.length() - 1);
+          String[] subStringSplitter = subString.split("\\)\\(");
+
+          for(int j = 0; j < subStringSplitter.length; j++){
+            String subStringFilter = subStringSplitter[j];
+            final String[] subStringFilterSplitter;
+
+            subStringFilter = subStringFilter.replace("\\02a", "*");
+            subStringFilter = subStringFilter.replace("\\028", "(");
+            subStringFilter = subStringFilter.replace("\\029", ")");
+
+            if(subStringFilter.contains("!=")) subStringFilterSplitter = subStringFilter.split("!=");
+            else if(subStringFilter.contains("<=")) subStringFilterSplitter = subStringFilter.split("<=");
+            else if(subStringFilter.contains(">=")) subStringFilterSplitter = subStringFilter.split(">=");
+            else subStringFilterSplitter = subStringFilter.split("=");
+
+            final String filterAttributeName = subStringFilterSplitter[0];
+            String filterColumnName = null;;
+            Object columnDataType = null;
+
+            //Search mapping for the corresponding column name for each filter attribute.
+            if(OUName.isEmpty()){
+              final List<String> OUList = jdbcm.getOrganizationalUnits(baseDN);
+
+              for(Iterator<String> iter = OUList.iterator(); iter.hasNext();){
+                OUName = iter.next();
+                final String tableName = jdbcm.getTableNameFromMapping(baseDN, OUName);
+
+                if(tableName == null) continue;
+                filterColumnName = jdbcm.getColumnNameFromMapping(tableName, baseDN, OUName, filterAttributeName);
+
+                if(filterColumnName == null) continue;
+                else columnDataType = jdbcm.getTableColumnDataType(tableName, filterColumnName);
+                break;
+              }
+              if(filterColumnName == null) throw new NullPointerException("JDBC Error: Could not find matching column name for attribute: '" + filterAttributeName + "'. Please check if mapping was succesful.");  
+            }
+            else{
+              final String tableName = jdbcm.getTableNameFromMapping(baseDN, OUName);
+
+              if(tableName == null) throw new NullPointerException("JDBC Error: Could not find matching table name for OU: '" + OUName + "' in DN: '" + baseDN + "'. Please check if mapping was succesful.");            
+              filterColumnName = jdbcm.getColumnNameFromMapping(tableName, baseDN, OUName, filterAttributeName);   
+
+              if(filterColumnName == null) throw new NullPointerException("JDBC Error: Could not find matching column name for attribute: '" + filterAttributeName + "'. Please check if mapping was succesful.");  
+              columnDataType = jdbcm.getTableColumnDataType(tableName, filterColumnName);
+            }
+
+            //Handle the like operator.
+            if(columnDataType != Integer.class){
+              String filterAttributeValue = subStringFilterSplitter[1];
+              String filterColumnValue = "'" + filterAttributeValue + "'";
+              filterAttributeValue = filterAttributeValue.replace("*", "\\*");
+              filterAttributeValue = filterAttributeValue.replace("(", "\\(");
+              filterAttributeValue = filterAttributeValue.replace(")", "\\)");
+
+              if(filterColumnValue.length() > 3 && filterColumnValue.contains("*")){
+                filterColumnValue = filterColumnValue.replace("*", "%");
+
+                if(subStringFilter.contains("!=")) subStringFilter = subStringFilter.replace("!=", " not like ");
+                else subStringFilter = subStringFilter.replace("=", " like ");
+              }
+              subStringFilter = subStringFilter.replaceFirst(filterAttributeValue, filterColumnValue);
+            }
+            subStringFilter = subStringFilter.replaceFirst(filterAttributeName, filterColumnName);
+
+            if(j != 0 || i != 0) convertedFilterString = convertedFilterString.concat(operationString);
+            convertedFilterString = convertedFilterString.concat(subStringFilter);
+          }
+
+          if(multipleSubStrings && i < subStringCount -1) convertedFilterString = "(" + convertedFilterString + ")";
+        }
       }
-
-      if(multipleSubStrings && i < subStringCount -1) convertedFilterString = "(" + convertedFilterString + ")";
+      return convertedFilterString;
     }
-    return convertedFilterString;
+    catch(NullPointerException e){
+      return "Err:" + e.getMessage();
+    }
   }
 
+  /**
+   * Returns the search entries of the last search request.
+   *       
+   * @return A list of entries of the last search request.
+   */
   public List<Entry> getSearchEntries()
   {
     return searchEntries;
   }
 
+  /**
+   * Search the Database Server using the provided search request.
+   *       
+   * @param request
+   *            The search request.
+   * @param handler
+   *            A search result handler which can be used to asynchronously process the 
+   *            search result entries and references as they are received, may be null.
+   * @return The result of the operation.
+   * @throws ErrorResultException
+   *            If the result code indicates that the request failed for some
+   *            reason.
+   * @throws SQLException
+   *            If the SQL query has an invalid format.
+   * @throws NullPointerException
+   *            If {@code request} was {@code null}, or if 
+   *            a corresponding mapping value could not be found in the mapping component.
+   */
   @Override
   public Result search(SearchRequest request, SearchResultHandler handler) throws ErrorResultException
   {
     Result r;
     try{
+      //Split up the DN the of the request.
       final DN DN = request.getName();
       final int DNSize = DN.size();
       final String baseDN = DN.parent(DNSize - 2).toString();
@@ -568,49 +870,98 @@
         OU = DN.parent(0).rdn();
         OUName = OU.getFirstAVA().getAttributeValue().toString();
       }
+      List<String> returnAttributeNames = request.getAttributes();
+      final List<String> removeAttributesList = new ArrayList<String>();
 
+      //Get all column names to be returned to return full records.
+      if(returnAttributeNames == null){
+        if(OU != null){
+          returnAttributeNames = jdbcm.getOrganizationalUnitAttributes(baseDN, OUName);
 
-      final List<String> returnAttributeNames = request.getAttributes();
-      final List<String> returnColumnNames = new ArrayList<String>();
+          if(returnAttributeNames == null) throw new NullPointerException("JDBC Error: Could not find any matching attributes in OU: '" + OUName + "'.");   
+          final String tableName = jdbcm.getTableNameFromMapping(baseDN, OUName);
 
-      final Filter searchFilter = request.getFilter();
-      final String convertedFilterString = convertSearchFilter(searchFilter, baseDN, OUName);
+          for(Iterator<String> iterAttributes = returnAttributeNames.iterator(); iterAttributes.hasNext();){
+            String returnAttributeName = iterAttributes.next();
+            final String returnColumnName = jdbcm.getColumnNameFromMapping(tableName, baseDN, OUName, returnAttributeName);
 
-      if(returnAttributeNames != null){
+            if(returnColumnName == null) removeAttributesList.add(returnAttributeName);
+          }
+          returnAttributeNames.removeAll(removeAttributesList);
+        }
+        else{
+          final List<String> OUList = jdbcm.getOrganizationalUnits(baseDN);
 
-        for(Iterator<String> iter = returnAttributeNames.iterator();iter.hasNext();){
+          if(OUList == null) throw new NullPointerException("JDBC Error: Could not find any matching organizational units in DN: '" + baseDN + "'.");
 
-          if(OU == null){
-            final String returnAttributeName = iter.next();
-            final List<String> OUList = jdbcm.getOrganizationalUnits(baseDN);
+          for(Iterator<String> iterOU = OUList.iterator(); iterOU.hasNext();){
+            OUName = iterOU.next();
+            final String tableName = jdbcm.getTableNameFromMapping(baseDN, OUName);
+            returnAttributeNames = jdbcm.getOrganizationalUnitAttributes(baseDN, OUName);
 
-            for(Iterator<String> iterOU = OUList.iterator(); iterOU.hasNext();){
-              OUName = iterOU.next();
-              final String tableName = jdbcm.getTableNameFromMapping(baseDN, OUName);
+            for(Iterator<String> iterAttributes = returnAttributeNames.iterator(); iterAttributes.hasNext();){
+              String returnAttributeName = iterAttributes.next();
               final String returnColumnName = jdbcm.getColumnNameFromMapping(tableName, baseDN, OUName, returnAttributeName);
 
-              if(returnColumnName == null) continue;
-              else returnColumnNames.add(returnColumnName);
-              break;
+              if(returnColumnName == null) removeAttributesList.add(returnAttributeName);
             }
-          }
-          else {
-            final String returnAttributeName = iter.next();
-            final String tableName = jdbcm.getTableNameFromMapping(baseDN, OUName);
-            final String returnColumnName = jdbcm.getColumnNameFromMapping(tableName, baseDN, OUName, returnAttributeName);
-            returnColumnNames.add(returnColumnName);
+            returnAttributeNames.removeAll(removeAttributesList);
           }
         }
       }
+      //Convert the search filter.
+      final Filter searchFilter = request.getFilter();
+      final String convertedFilterString = convertSearchFilter(searchFilter, baseDN, OUName);
+
+      if(convertedFilterString.startsWith("Err:")){
+        String[] errorStringSplitter = convertedFilterString.split("Err:");
+        throw new NullPointerException(errorStringSplitter[1]);
+      }
+      final List<String> returnColumnNames = new ArrayList<String>();
+
+      //Search mapping for the corresponding column name for each return attribute.
+      for(Iterator<String> iter = returnAttributeNames.iterator();iter.hasNext();){
+
+        if(OU == null){
+          final String returnAttributeName = iter.next();
+          final List<String> OUList = jdbcm.getOrganizationalUnits(baseDN);
+
+          if(OUList == null) throw new NullPointerException("JDBC Error: Could not find any matching organizational units in DN: '" + baseDN + "'.");
+
+          for(Iterator<String> iterOU = OUList.iterator(); iterOU.hasNext();){
+            OUName = iterOU.next();
+            final String tableName = jdbcm.getTableNameFromMapping(baseDN, OUName);
+            final String returnColumnName = jdbcm.getColumnNameFromMapping(tableName, baseDN, OUName, returnAttributeName);
+
+            if(returnColumnName == null) continue;
+            else returnColumnNames.add(returnColumnName);
+            break;
+          }
+        }
+        else {
+          final String returnAttributeName = iter.next();
+          final String tableName = jdbcm.getTableNameFromMapping(baseDN, OUName);
+
+          if(tableName == null) throw new NullPointerException("JDBC Error: Could not find matching table name for OU: '" + OUName + "' in DN: '" + baseDN + "'. Please check if mapping was succesful.");            
+          final String returnColumnName = jdbcm.getColumnNameFromMapping(tableName, baseDN, OUName, returnAttributeName);
+
+          if(returnColumnName == null) throw new NullPointerException("JDBC Error: Could not find matching column name for attribute: '" + returnAttributeName + "'. Please check if mapping was succesful.");
+          returnColumnNames.add(returnColumnName);
+        }
+      }
+
       String selectString = "";
       String fromString = "";
       String whereString = convertedFilterString;
 
-      if(returnColumnNames.isEmpty()) selectString = "*";
+      if(returnAttributeNames.isEmpty()) selectString = "*";
 
+      //Build the SQL SELECT string.
       if(OU == null){
         final List<String> OUList = jdbcm.getOrganizationalUnits(baseDN);
 
+        if(OUList == null) throw new NullPointerException("JDBC Error: Could not find any matching organizational units in DN: '" + baseDN + "'.");
+
         for(int i = 0; i < OUList.size(); i++){
           final String currentOU = OUList.get(i);
           final String currentTable = jdbcm.getTableNameFromMapping(baseDN, currentOU);
@@ -634,6 +985,8 @@
       }
       else{
         final String selectTable = jdbcm.getTableNameFromMapping(baseDN, OUName);
+        if(selectTable == null) throw new NullPointerException("JDBC Error: Could not find matching table name for OU: '" + OUName + "' in DN: '" + baseDN + "'. Please check if mapping was succesful.");            
+
         fromString = fromString.concat(selectTable);
 
         for(int j = 0; j < returnColumnNames.size(); j++){
@@ -646,20 +999,22 @@
         }
 
         if(rDN != null){
-          final String tableName = jdbcm.getTableNameFromMapping(baseDN, OUName);
-          final String columnName = jdbcm.getColumnNameFromMapping(tableName, baseDN, OUName, rDNAttributeName);
+          final String columnName = jdbcm.getColumnNameFromMapping(selectTable, baseDN, OUName, rDNAttributeName);
+
+          if(columnName == null) throw new NullPointerException("JDBC Error: Could not find matching column name for attribute: '" + rDNAttributeName + "'. Please check if mapping was succesful.");          
           whereString = columnName + "='" + rDNAttributeValue + "'";
         }
       }
 
+      //Build the SQL query.
+      final Statement st = connection.createStatement();
       String sql = "SELECT " + selectString + " FROM " + fromString;
 
       if(!whereString.isEmpty()) sql = sql.concat(" WHERE " + whereString);
-
-      final Statement st = connection.createStatement();
       ResultSet rs = st.executeQuery(sql);
       ResultSetMetaData rsmd = rs.getMetaData();
 
+      //Fill the entries result for this query.
       searchEntries.clear();
       while (rs.next()) {
         Entry e = new LinkedHashMapEntry();
@@ -670,13 +1025,21 @@
         searchEntries.add(e);
       }
       r = Responses.newResult(ResultCode.SUCCESS);
-    }catch (SQLException e){
+    }catch (SQLException e) {
       System.out.println(e.toString());
       r = Responses.newResult(ResultCode.OPERATIONS_ERROR);
+      r.setCause(e);
+    }catch (NullPointerException e){
+      System.out.println(e.toString());
+      r = Responses.newCompareResult(ResultCode.OPERATIONS_ERROR);
+      r.setCause(e);
     }
     return r;
   }
 
+  /**
+   * {@inheritDoc}
+   */
   @Override
   public String toString()
   {

--
Gitblit v1.10.0