Index: trunk/fmgVen/src/com/fmguler/ven/QueryMapper.java
===================================================================
--- trunk/fmgVen/src/com/fmguler/ven/QueryMapper.java	(revision 23)
+++ trunk/fmgVen/src/com/fmguler/ven/QueryMapper.java	(revision 28)
@@ -18,6 +18,21 @@
 package com.fmguler.ven;
 
+import com.fmguler.ven.util.Convert;
+import com.fmguler.ven.util.VenList;
+import java.beans.PropertyDescriptor;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import javax.sql.DataSource;
+import org.springframework.beans.BeanWrapper;
+import org.springframework.beans.BeanWrapperImpl;
+import org.springframework.jdbc.core.RowCallbackHandler;
 import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
 
@@ -28,10 +43,140 @@
 public class QueryMapper {
     private NamedParameterJdbcTemplate template;
+    private Set domainPackages;
+    private Set dbClasses;
+    private boolean debug = true;
 
-    public List list() {
-        return null;
+    public QueryMapper() {
+        domainPackages = new HashSet();
+        dbClasses = new HashSet();
+        //the predefined database classes;
+        this.dbClasses.add(Integer.class);
+        this.dbClasses.add(String.class);
+        this.dbClasses.add(Date.class);
+        this.dbClasses.add(Double.class);
+        this.dbClasses.add(Boolean.class);
     }
 
-    //SETTERS-------------------------------------------------------------------
+    /**
+     * Executes the specified query setting the specified parameters,
+     * and maps the results to the instances of the specified objectClass.
+     * @param query select SQL query in the conventional format
+     * @param parameters named query parameter values
+     * @param objectClass the type of the object to be mapped
+     * @return the list of mapped objects
+     */
+    public List list(String query, Map parameters, final Class objectClass) {
+        long t1 = System.currentTimeMillis();
+        final List results = new LinkedList();
+        final String tableName = Convert.toDB(Convert.toSimpleName(objectClass.getName()));
+        final Set columns = new HashSet();
+
+        template.query(query, parameters, new RowCallbackHandler() {
+            public void processRow(ResultSet rs) throws SQLException {
+                enumerateColumns(columns, rs);
+                mapRecursively(rs, columns, tableName, objectClass, results);
+            }
+        });
+
+        System.out.println("Ven - list time = " + (System.currentTimeMillis() - t1));
+        return results;
+    }
+
+    //--------------------------------------------------------------------------
+    //PRIVATE METHODS
+    //recursively map the resultSet to the object and add to the list
+    protected void mapRecursively(ResultSet rs, Set columns, String tableName, Class objectClass, List parentList) {
+        try {
+            if (!columns.contains(tableName + "_id")) return; //this object does not exist in the columns
+            Object id = rs.getObject(tableName + "_id");
+            if (id == null) return; //this object exists in the columns but null, probably because of left join
+
+            //create bean wrapper for the object class
+            BeanWrapper wr = new BeanWrapperImpl(objectClass); //already caches class introspection (CachedIntrospectionResults.forClass())
+            wr.setPropertyValue("id", id); //set the id property
+            Object object = wr.getWrappedInstance();
+            boolean map = true;
+
+            //check if this object exists in the parent list (since SQL joins are cartesian products, do not create new object if this row is just the same as previous)
+            for (Iterator it = parentList.iterator(); it.hasNext();) {
+                Object objectInList = (Object)it.next();
+                if (objectIdEquals(objectInList, id)) {
+                    wr.setWrappedInstance(objectInList); //already exists in the list, use that instance
+                    map = false; // and do not map again
+                    break;
+                }
+            }
+            if (map) parentList.add(object); //could not find in the parent list, add the new object
+
+            PropertyDescriptor[] pdArr = wr.getPropertyDescriptors();
+            for (int i = 0; i < pdArr.length; i++) {
+                PropertyDescriptor pd = pdArr[i];
+                Class fieldClass = pd.getPropertyType(); //field class
+                String fieldName = Convert.toDB(pd.getName()); //field name
+                Object fieldValue = wr.getPropertyValue(pd.getName());
+                String columnName = tableName + "_" + fieldName;
+
+                //database class (primitive property)
+                if (map && dbClasses.contains(fieldClass)) {
+                    if (columns.contains(columnName)) {
+                        if (debug) System.out.println(">>field is found: " + columnName);
+                        wr.setPropertyValue(pd.getName(), rs.getObject(columnName));
+                    } else {
+                        if (debug) System.out.println("--field not found: " + columnName);
+                    }
+                }
+
+                //many to one association (object property)
+                if (map && fieldClass.getPackage() != null && domainPackages.contains(fieldClass.getPackage().getName())) {
+                    if (columns.contains(columnName + "_id")) {
+                        if (debug) System.out.println(">>object is found " + columnName);
+                        List list = new ArrayList(1); //we know there will be single result
+                        mapRecursively(rs, columns, columnName, fieldClass, list);
+                        if (list.size() > 0) wr.setPropertyValue(pd.getName(), list.get(0));
+                    } else {
+                        if (debug) System.out.println("--object not found: " + columnName);
+                    }
+                }
+
+                //one to many association (list property)
+                if (fieldValue instanceof List) { //Note: here recurring row's list property is mapped and add to parent's list
+                    if (columns.contains(columnName + "_id")) {
+                        Class elementClass = VenList.findElementClass((List)fieldValue);
+                        if (debug) System.out.println(">>list is found " + columnName);
+                        mapRecursively(rs, columns, columnName, elementClass, (List)fieldValue);
+                    } else {
+                        if (debug) System.out.println("--list not found: " + columnName);
+                    }
+                }
+            }
+        } catch (Exception ex) {
+            if (debug) {
+                System.out.println("Ven - error while mapping row; ");
+                ex.printStackTrace();
+            }
+        }
+    }
+
+    //enumerate columns from the resultset
+    private void enumerateColumns(Set columns, ResultSet rs) throws SQLException {
+        if (!columns.isEmpty()) return;
+        for (int i = 1; i < rs.getMetaData().getColumnCount() + 1; i++) {
+            columns.add(rs.getMetaData().getColumnName(i));
+        }
+    }
+
+    //check if the specified objects are the same entity (according to id fields)
+    private boolean objectIdEquals(Object object, Object id) {
+        //return obj1.equals(obj2); //objects need to implement equals()
+        //TODO: more efficient (invoke getId method)
+        BeanWrapper wr = new BeanWrapperImpl(object);
+        return id.equals(wr.getPropertyValue("id"));
+    }
+
+    //--------------------------------------------------------------------------
+    //SETTERS
+    /**
+     * @param dataSource used for accessing database
+     */
     public void setDataSource(DataSource dataSource) {
         if (dataSource == null) throw new RuntimeException("fmgVen - DataSource cannot be null");
@@ -39,6 +184,10 @@
     }
 
-    public void addDomainPackage(String domainPackage){
-
+    /**
+     * Add the domain packages that will be considered persistent
+     * @param domainPackage the domain package
+     */
+    public void addDomainPackage(String domainPackage) {
+        domainPackages.add(domainPackage);
     }
 }
