Permalink
Browse files

Added Query.Any(), improved ID accessing for cases when ID is not wri…

…te accessible

Signed-off-by: gburgett <gordon.burgett@gmail.com>
  • Loading branch information...
gburgett committed Jan 31, 2013
1 parent 6d95e3c commit 37c3a4d9bf151bc8023b5019b46025ed8653dd0e
@@ -29,7 +29,7 @@ endorsed.classpath=
excludes=
includes=**
jar.archive.disabled=${jnlp.enabled}
-jar.compress=true
+jar.compress=false
jar.index=${jnlp.enabled}
javac.classpath=\
${libs.Apache_Commons_Logging.classpath}:\
@@ -69,7 +69,8 @@ public void insert(T row)
//UPDATE
/**
- * Replaces a value with the new value by ID.
+ * Replaces a value with the new value by ID. This is the same as "Save"
+ * in some other document databases.
* @param newValue The new value to replace the old value.
*/
public void replace(T newValue)
@@ -101,11 +101,7 @@ else if(this.idMap != null){
private void setId(Element rowData, String sId){
rowData.setAttribute("id", sId, XFlatDatabase.xFlatNs);
}
-
- private Element convert(T data){
- return convert(data, getId(data));
- }
-
+
private Element convert(T data, String id){
Element ret;
try {
@@ -136,61 +132,64 @@ private T convert(Element rowData){
return ret;
}
- if(this.accessor.hasId()){
- Object id = this.getIdGenerator().stringToId(sId, this.accessor.getIdType());
- try {
- this.accessor.setIdValue(ret, id);
- } catch (IllegalAccessException | InvocationTargetException ex) {
- throw new XFlatException("Cannot set ID", ex);
- }
- }
- else if(this.idMap != null){
+ if(!this.accessor.hasId() && this.idMap != null){
//cache the ID
this.idMap.put(ret, sId);
}
return ret;
}
- private String generateId(T rowData){
+ private String getOrGenerateId(T rowData){
if(this.accessor.hasId()){
- Object id = this.getIdGenerator().generateNewId(this.accessor.getIdType());
-
try{
+ Object id = this.accessor.getIdValue(rowData);
+ if(id != null){
+ //already have an ID
+ return this.getIdGenerator().idToString(id);
+ }
+
+ id = this.getIdGenerator().generateNewId(this.accessor.getIdType());
+
this.accessor.setIdValue(rowData, id);
+
+ return this.getIdGenerator().idToString(id);
+
} catch (IllegalAccessException | InvocationTargetException ex) {
- throw new XFlatException("Cannot set newly-generated ID", ex);
+ throw new XFlatException("Cannot generate ID", ex);
}
- return this.getIdGenerator().idToString(id);
}
else{
//if no ID property, always use string
- String id = (String)this.getIdGenerator().generateNewId(String.class);
if(this.idMap != null){
- this.idMap.put(rowData, id);
+ //doublecheck in the ID map
+ String id;
+ synchronized(this.idMap){
+ id = this.idMap.get(rowData);
+ if(id == null){
+ id = (String)this.getIdGenerator().generateNewId(String.class);
+ this.idMap.put(rowData, id);
+ }
+ }
+ return id;
}
- return id;
+
+ return (String)this.getIdGenerator().generateNewId(String.class);
}
}
//</editor-fold>
@Override
public void insert(T row) throws DuplicateKeyException {
- final Element e = convert(row);
- String id = getId(e);
- if(id == null){
- //generate new ID
- id = generateId(row);
- setId(e, id);
- }
-
- final String sId = id;
+ //generate new ID
+ final String id = getOrGenerateId(row);
+ final Element e = convert(row, id);
this.doWithEngine(new EngineAction(){
@Override
public Object act(Engine engine) {
- engine.insertRow(sId, e);
+ engine.insertRow(id, e);
return null;
}
});
@@ -305,7 +304,7 @@ public boolean replaceOne(XPathQuery query, T newValue) {
return false;
}
- Element data = convert(newValue);
+ Element data = convert(newValue, null);
String replacedId = recursiveReplaceOne(query, data, existing);
if(replacedId == null){
return false;
@@ -347,32 +346,15 @@ public Object act(Engine engine) {
@Override
public boolean upsert(T newValue) {
- final Element data = convert(newValue);
- final String id = getId(data);
+ final String id = getOrGenerateId(newValue);
+ final Element data = convert(newValue, id);
- if(id == null){
- //insert
- final String nId = generateId(newValue);
- setId(data, nId);
-
- this.doWithEngine(new EngineAction(){
- @Override
- public Object act(Engine engine) {
- engine.insertRow(nId, data);
- return null;
- }
- });
-
- return true; //inserted
- }
- else{
- return this.doWithEngine(new EngineAction<Boolean>(){
- @Override
- public Boolean act(Engine engine) {
- return engine.upsertRow(id, data);
- }
- });
- }
+ return this.doWithEngine(new EngineAction<Boolean>(){
+ @Override
+ public Boolean act(Engine engine) {
+ return engine.upsertRow(id, data);
+ }
+ });
}
@Override
@@ -152,9 +152,6 @@ private static PropertyDescriptor getIdProperty(Class<?> pojoType) throws Intros
if(p.getReadMethod() == null || Object.class.equals(p.getReadMethod().getDeclaringClass()))
continue;
- if(p.getWriteMethod() == null || Object.class.equals(p.getWriteMethod().getDeclaringClass()))
- continue;
-
descriptors.add(p);
}
@@ -295,7 +292,8 @@ public void setIdValue(T pojo, Object id)
throws IllegalAccessException, InvocationTargetException
{
if(this.idProperty != null){
- this.idProperty.getWriteMethod().invoke(pojo, id);
+ if(this.idProperty.getWriteMethod() != null)
+ this.idProperty.getWriteMethod().invoke(pojo, id);
return;
}
@@ -17,6 +17,7 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.apache.commons.logging.Log;
@@ -53,30 +54,65 @@
//<editor-fold desc="properties" >
private XPathExpression<?> selector;
+ /**
+ * Gets the XPath expression that is the selector for this query.
+ * AND and OR queries have null here, their subqueries have this populated.
+ * @return the selecting expression for this query.
+ */
public XPathExpression<?> getSelector(){
return selector;
}
private Matcher<Element> rowMatcher;
+ /**
+ * Gets the Hamcrest matcher that matches database rows. This is used to determine
+ * if a row is a match for a query.
+ * @return The database row hamcrest matcher.
+ */
public Matcher<Element> getRowMatcher(){
return rowMatcher;
}
private Object value;
+ /**
+ * Gets the value to which the element selected by the {@link #getSelector() selector}
+ * should be compared, according to this query's type. <br/>
+ * AND and OR queries have null here, their subqueries have this populated.
+ * @return the value that will be compared to the selected element.
+ */
public Object getValue(){
return value;
}
private Class<?> valueType;
+ /**
+ * Gets the type of {@link #getValue() the value}, to which the selected element
+ * should be convertible so it can be compared to the value.
+ * @return The type of the value, or null if the value is null.
+ */
public Class<?> getValueType(){
return valueType;
}
private QueryType queryType;
+ /**
+ * Gets the type of this query, ie. EQ, NE, AND, OR, etc.
+ * @return The type of query.
+ */
public QueryType getQueryType(){
return queryType;
}
+ /**
+ * Gets the sub queries of this query. This is only populated for
+ * AND and OR queries.
+ * @return A list of all the sub queries, or an empty list if this is not an AND
+ * or OR query.
+ */
+ public List<XPathQuery> getSubQueries(){
+ return queryChain == null ? Collections.EMPTY_LIST : Collections.unmodifiableList(queryChain);
+ }
+
private List<XPathQuery> queryChain;
//</editor-fold>
@@ -232,6 +268,7 @@ private XPathQuery(){
case EXISTS:
case MATCHES:
+ case ANY:
//always a full table scan
return IntervalSet.all();
@@ -461,6 +498,15 @@ public static XPathQuery exists(XPathExpression<?> selector){
return new XPathQuery(selector, QueryType.EXISTS, true, Object.class, m);
}
+
+ /**
+ * An XPathQuery that matches any and all rows regardless of content.
+ * useful for find all or delete all.
+ * @return A new XPathQuery that will match all rows.
+ */
+ public static XPathQuery any(){
+ return new XPathQuery(QueryType.ANY, Matchers.anyOf(Matchers.nullValue(), Matchers.any(Element.class)));
+ }
//</editor-fold>
@@ -491,7 +537,9 @@ public static XPathQuery exists(XPathExpression<?> selector){
/** Represents an exists query. */
EXISTS,
/** Represents a matches query. */
- MATCHES
+ MATCHES,
+ /** Represents an Any query, which matches all rows. */
+ ANY
}
/**
@@ -23,6 +23,7 @@
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
+import java.util.Set;
import org.xflatdb.xflat.Cursor;
import org.xflatdb.xflat.KeyNotFoundException;
import org.xflatdb.xflat.convert.ConversionService;
@@ -224,7 +225,7 @@ public void testFind_Foo_GetsElement_DeserializesAndSetsId() throws Exception {
when(idGenerator.stringToId(any(String.class), eq(String.class)))
.thenAnswer(byReturningFirstParam());
- Element inDb = new Element("foo");
+ Element inDb = new Element("foo").setAttribute("id", fooId, Foo.FooIdNs);
setId(inDb, fooId);
inDb.addContent(new Element("fooInt").setText("32"));
when(engine.readRow(fooId))
@@ -300,9 +301,13 @@ public void testFind_Foo_ReturnsConvertingCursor() throws Exception {
when(idGenerator.stringToId(any(String.class), eq(String.class)))
.thenAnswer(byReturningFirstParam());
- Element inDb1 = new Element("foo").addContent(new Element("fooInt").setText("32"));
+ Element inDb1 = new Element("foo")
+ .setAttribute("id", "id 1", Foo.FooIdNs)
+ .addContent(new Element("fooInt").setText("32"));
setId(inDb1, "id 1");
- Element inDb2 = new Element("foo").addContent(new Element("fooInt").setText("33"));
+ Element inDb2 = new Element("foo")
+ .setAttribute("id", "id 2", Foo.FooIdNs)
+ .addContent(new Element("fooInt").setText("33"));
setId(inDb2, "id 2");
XPathQuery query = XPathQuery.gte(xpath.compile("foo/fooInt"), 32);
@@ -342,9 +347,13 @@ public void testFindAll_Foo_ConvertsAllToList() throws Exception {
when(idGenerator.stringToId(any(String.class), eq(String.class)))
.thenAnswer(byReturningFirstParam());
- Element inDb1 = new Element("foo").addContent(new Element("fooInt").setText("32"));
+ Element inDb1 = new Element("foo")
+ .setAttribute("id", "id 1", Foo.FooIdNs)
+ .addContent(new Element("fooInt").setText("32"));
setId(inDb1, "id 1");
- Element inDb2 = new Element("foo").addContent(new Element("fooInt").setText("33"));
+ Element inDb2 = new Element("foo")
+ .setAttribute("id", "id 2", Foo.FooIdNs)
+ .addContent(new Element("fooInt").setText("33"));
setId(inDb2, "id 2");
XPathQuery query = XPathQuery.gte(xpath.compile("foo/fooInt"), 32);
@@ -556,6 +565,10 @@ public void testUpsert_Foo_NoId_InsertsWithNewId() throws Exception {
when(idGenerator.generateNewId(String.class))
.thenReturn(id);
+ when(engine.upsertRow(eq(id), any(Element.class)))
+ .thenReturn(true);
+
+
Foo toUpsert = new Foo();
toUpsert.fooInt = 51;
@@ -566,7 +579,7 @@ public void testUpsert_Foo_NoId_InsertsWithNewId() throws Exception {
assertTrue("Should have inserted new foo", didInsert);
ArgumentCaptor<Element> data = ArgumentCaptor.forClass(Element.class);
- verify(engine).insertRow(eq(id), data.capture());
+ verify(engine).upsertRow(eq(id), data.capture());
assertEquals("Should have inserted correct data", "51",
data.getValue().getChild("fooInt").getText());
@@ -614,15 +627,18 @@ public void testUpsert_Bar_NewInstance_GeneratesNewIdAndRemembersIt() throws Exc
when(idGenerator.generateNewId(String.class))
.thenReturn(id, "second invocation");
+ when(engine.upsertRow(eq(id), any(Element.class)))
+ .thenReturn(true);
+
//ACT
ConvertingTable<Bar> instance = this.getBarInstance();
boolean didInsert = instance.upsert(toUpsert);
//ASSERT
- assertTrue("Should have inserted new foo", didInsert);
+ assertTrue("Should have inserted new bar", didInsert);
ArgumentCaptor<Element> data = ArgumentCaptor.forClass(Element.class);
- verify(engine).insertRow(eq(id), data.capture());
+ verify(engine).upsertRow(eq(id), data.capture());
assertEquals("Should have inserted correct data", "test data",
data.getValue().getChild("barString").getText());
Oops, something went wrong.

0 comments on commit 37c3a4d

Please sign in to comment.