Skip to content

Commit

Permalink
added QueryParser and unit test
Browse files Browse the repository at this point in the history
  • Loading branch information
jdavo committed Jun 13, 2012
1 parent cc1257d commit c1c592b
Show file tree
Hide file tree
Showing 2 changed files with 279 additions and 0 deletions.
133 changes: 133 additions & 0 deletions src/com/perfectworldprogramming/mobile/orm/reflection/QueryParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package com.perfectworldprogramming.mobile.orm.reflection;

import java.lang.reflect.Field;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.perfectworldprogramming.mobile.orm.annotations.Column;
import com.perfectworldprogramming.mobile.orm.annotations.ForeignKey;
import com.perfectworldprogramming.mobile.orm.annotations.PrimaryKey;
import com.perfectworldprogramming.mobile.orm.exception.DataAccessException;
import com.perfectworldprogramming.mobile.orm.exception.FieldNotFoundException;
/**
* Allows SQL to refer to domain classes and fields, which are then converted to SQL.
* Queries place matchable tokens in square brackets and refer to either a direct field or a qualified field
* using the (case insensitive) {@code class.getSimpleName()}. Duplicate fields will return the first match.
* In the special case where only the simple class name is entered, the primary key field will be substituted.
* This is to simplify queries using foreign keys.
* eg [fieldName] or [MyClass.myField] <br/>
* {@code "select * from Person where [age] = ?"}<br />
* {@code "select * from Person where [dateOfBirth] < ?"}<br />
* {@code "select * from Person where [Person.height] > ?"}<br />
* {@code "select * from Person p, Address a where p.[Person.height] > ? and p.[Person] = a.[Address.person]"}<br />
* @author David O'Meara <[email protected]>
* @since 13/06/2012
*
*/
public class QueryParser
{
private QueryParser()
{
// prevent external instantiation
}

public static String parse(String input, List<Class<?>> domainClasses)
{
StringBuilder result = new StringBuilder();
int start = 0;
Matcher m = p.matcher(input);
while (m.find())
{
result.append(input.substring(start, m.start()));

if (m.groupCount() == 2 && m.group(2).length()>0)
{
for (Class<?> clazz : domainClasses)
{
final String className = m.group(1);
final String fieldName = m.group(2);
if(clazz.getSimpleName().equalsIgnoreCase(className))
{
String replace = getColumnName(clazz, fieldName);
if(replace==null || replace.length()==0)
{
throw new FieldNotFoundException("Could not find field '"+fieldName+"' in class "+clazz.getName());
}
result.append(replace);
break;
}
}
}
else
{
final String matchName = m.group(1);
String replace = null;
for (Class<?> clazz : domainClasses)
{
if(clazz.getSimpleName().equalsIgnoreCase(matchName))
{
PrimaryKey pk = new DomainClassAnalyzer().getPrimaryKey(clazz);
replace = pk.value();
break;
}
}
if(replace==null)
{
for (Class<?> clazz : domainClasses)
{
replace = getColumnName(clazz, matchName);
if(replace.length()>0)
{
break;
}
}
}
if(replace==null || replace.length()==0)
{
throw new FieldNotFoundException("Could not find field '"+matchName+"' in domain class(es)");
}
result.append(replace);
}
start = m.end();
}
result.append(input.substring(start));
return result.toString();
}

private static String getColumnName(Class<?> clazz, String fieldName)
{
Field field;
try
{
field = clazz.getDeclaredField(fieldName);
}
catch (SecurityException e)
{
throw new DataAccessException("Failed to view field "+fieldName+" in class "+clazz.getName(), e);
}
catch (NoSuchFieldException e)
{
return "";
}
if (field.isAnnotationPresent(Column.class))
{
Column setter = field.getAnnotation(Column.class);
return setter.value();
}
else if (field.isAnnotationPresent(PrimaryKey.class))
{
PrimaryKey setter = field.getAnnotation(PrimaryKey.class);
return setter.value();
}
else if (field.isAnnotationPresent(ForeignKey.class))
{
ForeignKey setter = field.getAnnotation(ForeignKey.class);
return setter.value();
}
return "";
}

private static final String REGEX_SUBST = "\\[([a-zA-Z0-9]+)\\.?([a-zA-Z0-9]*)\\]";
private static final Pattern p = Pattern.compile(REGEX_SUBST);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package com.perfectworldprogramming.mobile.orm.test.reflection;

import java.util.ArrayList;
import java.util.List;

import android.test.ActivityInstrumentationTestCase2;

import com.perfectworldprogramming.mobile.orm.exception.FieldNotFoundException;
import com.perfectworldprogramming.mobile.orm.reflection.QueryParser;
import com.perfectworldprogramming.mobile.orm.test.Main;
import com.perfectworldprogramming.mobile.orm.test.domain.Address;
import com.perfectworldprogramming.mobile.orm.test.domain.Person;

public class QueryParserTest extends ActivityInstrumentationTestCase2<Main>
{
public QueryParserTest()
{
super("org.springframework.mobile.orm.test", Main.class);
}

public void testUnchanged()
{
final String input = "select * from Person";
List<Class<?>> singleton = new ArrayList<Class<?>>();
singleton.add(Person.class);
assertEquals(input, QueryParser.parse(input, singleton));
}

public void testUnchanged2()
{
final String input = "select * from Person where FIRST_NAME = ?";
List<Class<?>> singleton = new ArrayList<Class<?>>();
singleton.add(Person.class);
assertEquals(input, QueryParser.parse(input, singleton));
}

public void testSingleSimple()
{
final String input = "select * from Person where [firstName] = ?";
final String expected = "select * from Person where FIRST_NAME = ?";
List<Class<?>> singleton = new ArrayList<Class<?>>();
singleton.add(Person.class);
final String actual = QueryParser.parse(input, singleton);
assertEquals(expected, actual);
}

public void testMultipleSimple()
{
final String input = "select * from Person where [firstName] = ? and [age] < ?";
final String expected = "select * from Person where FIRST_NAME = ? and AGE < ?";
List<Class<?>> singleton = new ArrayList<Class<?>>();
singleton.add(Person.class);
final String actual = QueryParser.parse(input, singleton);
assertEquals(expected, actual);
}

public void testSingleComplex()
{
final String input = "select * from Person where [Person.firstName] = ?";
final String expected = "select * from Person where FIRST_NAME = ?";
List<Class<?>> singleton = new ArrayList<Class<?>>();
singleton.add(Person.class);
final String actual = QueryParser.parse(input, singleton);
assertEquals(expected, actual);
}

public void testMultipleComplex()
{
final String input = "select * from Person where [Person.firstName] = ? and [Person.age] < ?";
final String expected = "select * from Person where FIRST_NAME = ? and AGE < ?";
List<Class<?>> singleton = new ArrayList<Class<?>>();
singleton.add(Person.class);
final String actual = QueryParser.parse(input, singleton);
assertEquals(expected, actual);
}

public void testFailSimpleMatchInvalidField()
{
final String input = "select * from Person where [abcdef] = ?";
List<Class<?>> singleton = new ArrayList<Class<?>>();
singleton.add(Person.class);
try
{
QueryParser.parse(input, singleton);
fail("FieldNotFound expected");
}
catch(FieldNotFoundException e)
{
// expected
}
}
public void testFailSimpleMatchInvalidClass()
{
final String input = "select * from Person where [age] = ?";
List<Class<?>> singleton = new ArrayList<Class<?>>();
singleton.add(Address.class);
try
{
QueryParser.parse(input, singleton);
fail("FieldNotFound expected");
}
catch(FieldNotFoundException e)
{
// expected
}
}

public void testFailSimpleMatchInvalidClassAndField()
{
final String input = "select * from Person where [Address.age] = ?";
List<Class<?>> singleton = new ArrayList<Class<?>>();
singleton.add(Person.class);
singleton.add(Address.class);
try
{
QueryParser.parse(input, singleton);
fail("FieldNotFound expected");
}
catch(FieldNotFoundException e)
{
// expected
}
}

public void testMultipleClassesAndJoin()
{
final String input = "select * from Person p, Address a where p.[lastName] = ? and p.[Person.id]=a.[Address.person]";
final String expected = "select * from Person p, Address a where p.LAST_NAME = ? and p.PERSON_ID=a.PERSON_ID";
List<Class<?>> list = new ArrayList<Class<?>>();
list.add(Person.class);
list.add(Address.class);
final String actual = QueryParser.parse(input, list);
assertEquals(expected, actual);
}

public void testForeignKeyJoin()
{
final String input = "select * from Person p, Address a where p.[Person]=a.[Address.person]";
final String expected = "select * from Person p, Address a where p.PERSON_ID=a.PERSON_ID";
List<Class<?>> list = new ArrayList<Class<?>>();
list.add(Person.class);
list.add(Address.class);
final String actual = QueryParser.parse(input, list);
assertEquals(expected, actual);
}
}

0 comments on commit c1c592b

Please sign in to comment.