Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
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...
commit 37c3a4d9bf151bc8023b5019b46025ed8653dd0e 1 parent 6d95e3c
@gburgett authored
View
2  java/XFlat/nbproject/project.properties
@@ -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}:\
View
3  java/XFlat/src/org/xflatdb/xflat/Table.java
@@ -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)
View
98 java/XFlat/src/org/xflatdb/xflat/db/ConvertingTable.java
@@ -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,15 +132,7 @@ 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);
}
@@ -152,45 +140,56 @@ else if(this.idMap != null){
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
View
6 java/XFlat/src/org/xflatdb/xflat/db/IdAccessor.java
@@ -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;
}
View
50 java/XFlat/src/org/xflatdb/xflat/query/XPathQuery.java
@@ -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
}
/**
View
32 java/XFlat/test/org/xflatdb/xflat/db/ConvertingTableTest.java
@@ -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());
View
6 java/XFlat/test/org/xflatdb/xflat/db/DatabaseIntegrationTest.java
@@ -174,10 +174,10 @@ public void testInsertMany_QueriesAll() throws Exception {
try(Cursor<Foo> fooCursor = fooTable.find(XPathQuery.gte(expression, 100))){
int i = 100;
- for(Foo f : fooCursor){
- assertThat("Expected items 100 to 499", f.fooInt,
+ for(Foo f2 : fooCursor){
+ assertThat("Expected items 100 to 499", f2.fooInt,
Matchers.allOf(Matchers.greaterThanOrEqualTo(100), Matchers.lessThan(500)));
- assertThat("Expected to use integer ID generator", Integer.parseInt(f.getId()),
+ assertThat("Expected to use integer ID generator", Integer.parseInt(f2.getId()),
Matchers.allOf(Matchers.greaterThan(0), Matchers.lessThanOrEqualTo(500)));
i++;
}
View
45 java/XFlat/test/org/xflatdb/xflat/query/XPathQueryTest.java
@@ -865,6 +865,51 @@ public void testExists_DoesntExist_DoesntMatch() throws Exception {
}//end testExists_DoesntExist_DoesntMatch
@Test
+ public void testAny_MatchesNull() throws Exception {
+ System.out.println("testAny_MatchesNull");
+
+ XPathQuery query = XPathQuery.any();
+
+ //act
+ boolean matches = query.getRowMatcher().matches(null);
+
+ //assert
+ assertTrue(matches);
+ }
+
+ @Test
+ public void testAny_MatchesEmptyRow() throws Exception {
+ System.out.println("testAny_MatchesEmptyRow");
+
+ XPathQuery query = XPathQuery.any();
+
+ //act
+ boolean matches = query.getRowMatcher().matches(new Element("row", XFlatDatabase.xFlatNs));
+
+ //assert
+ assertTrue(matches);
+ }
+
+ @Test
+ public void testAny_MatchesFullRow() throws Exception {
+ System.out.println("testAny_MatchesEmptyRow");
+
+ XPathQuery query = XPathQuery.any();
+
+ //act
+ boolean matches = query.getRowMatcher().matches(
+ new Element("row", XFlatDatabase.xFlatNs)
+ .addContent(new Element("data").setAttribute("val", "value").setText("some data"))
+ .addContent("some more text")
+ );
+
+ //assert
+ assertTrue(matches);
+ }
+
+
+
+ @Test
public void testEq_MatchesSecondOfTwoSelected_Matches() throws Exception {
System.out.println("testEq_MatchesSecondOfTwoSelected_Matches");
View
2  java/XFlat/test/test/Foo.java
@@ -82,8 +82,6 @@ public Element convert(Foo source) {
public static class FromElementConverter implements Converter<Element, Foo>{
@Override
public Foo convert(Element source) throws ConversionException {
- if(!"foo".equals(source.getName() ))
- throw new ConversionException("Expected element named 'Foo'");
Foo ret = new Foo();
ret.id = source.getAttributeValue("id", FooIdNs);
Please sign in to comment.
Something went wrong with that request. Please try again.