/testing/library/src/com/google/appengine/library/DataStoreBookDataService.java

http://datanucleus-appengine.googlecode.com/ · Java · 225 lines · 176 code · 36 blank · 13 comment · 24 complexity · 29b056b1322c2a2b39de1b9eb5984bc5 MD5 · raw file

  1. // Copyright 2008 Google Inc. All Rights Reserved.
  2. package com.google.appengine.library;
  3. import static com.google.appengine.api.datastore.FetchOptions.Builder.withLimit;
  4. import com.google.appengine.api.datastore.DatastoreService;
  5. import com.google.appengine.api.datastore.DatastoreServiceFactory;
  6. import com.google.appengine.api.datastore.Entity;
  7. import com.google.appengine.api.datastore.FetchOptions;
  8. import com.google.appengine.api.datastore.Query;
  9. import com.google.appengine.api.datastore.Query.FilterOperator;
  10. import com.google.appengine.api.datastore.Query.SortDirection;
  11. import java.util.Date;
  12. import java.util.HashMap;
  13. import java.util.Iterator;
  14. import java.util.Map;
  15. import java.util.StringTokenizer;
  16. /**
  17. * @author kjin@google.com (Kevin Jin)
  18. */
  19. final class DataStoreBookDataService implements BookDataService {
  20. private final DatastoreService datastoreService = DatastoreServiceFactory.getDatastoreService();
  21. public Iterable<Book> asIterable(String jpqlQuery) {
  22. return new BookIterable(datastoreService.prepare(new QueryConverter().convertQuery(jpqlQuery))
  23. .asIterable());
  24. }
  25. public Iterable<Book> asIterable(String jpqlQuery, int limit, int offset) {
  26. FetchOptions fo = withLimit(limit).offset(offset);
  27. return new BookIterable(datastoreService.prepare(new QueryConverter().convertQuery(jpqlQuery))
  28. .asIterable(fo));
  29. }
  30. public int countEntities(String jpqlQuery) {
  31. return datastoreService.prepare(new QueryConverter().convertQuery(jpqlQuery)).countEntities();
  32. }
  33. public void delete(Book book) {
  34. datastoreService.delete(((BookWithEntity) book).e.getKey());
  35. }
  36. public void put(Book book) {
  37. final Entity e;
  38. if (book instanceof BookWithEntity) {
  39. e = ((BookWithEntity) book).e;
  40. } else {
  41. e = new Entity("Book");
  42. }
  43. e.setProperty("category", book.getCategory());
  44. e.setProperty("lastname", book.getLastname());
  45. e.setProperty("firstname", book.getFirstname());
  46. e.setProperty("title", book.getTitle());
  47. e.setProperty("created", book.getCreated());
  48. e.setProperty("year", book.getYear());
  49. datastoreService.put(e);
  50. }
  51. private static final class BookWithEntity extends Book {
  52. private final Entity e;
  53. private BookWithEntity(Entity e) {
  54. super((String) e.getProperty("category"), (Date) e.getProperty("created"),
  55. (String) e.getProperty("firstname"), (String) e.getProperty("lastname"),
  56. (String) e.getProperty("title"), ((Long) e.getProperty("year")).intValue());
  57. this.e = e;
  58. }
  59. }
  60. private static final class BookIterable implements Iterable<Book> {
  61. public Iterator<Book> iterator() {
  62. return new BookIterator(it.iterator());
  63. }
  64. private BookIterable(Iterable<Entity> it) {
  65. super();
  66. this.it = it;
  67. }
  68. private final Iterable<Entity> it;
  69. }
  70. private static final class BookIterator implements Iterator<Book> {
  71. public boolean hasNext() {
  72. return it.hasNext();
  73. }
  74. public Book next() {
  75. Book book = null;
  76. final Entity e = it.next();
  77. if (e != null) {
  78. book =
  79. new BookWithEntity(e);
  80. }
  81. return book;
  82. }
  83. public void remove() {
  84. it.remove();
  85. }
  86. private BookIterator(Iterator<Entity> it) {
  87. super();
  88. this.it = it;
  89. }
  90. private final Iterator<Entity> it;
  91. }
  92. private static final class QueryConverter {
  93. /**
  94. * Converts a JPQL string into {@code Query}. Only parses the format output
  95. * by {@code Library}, e.g. WHERE category='test' AND year>1800 ORDER BY
  96. * year.
  97. */
  98. private Query convertQuery(String jpqlQuery) {
  99. query = new Query("Book");
  100. tokenizer = new StringTokenizer(jpqlQuery);
  101. if (tokenizer.hasMoreTokens()) {
  102. String lastToken = tokenizer.nextToken();
  103. if (lastToken.equalsIgnoreCase("WHERE")) {
  104. do {
  105. parseFilter();
  106. if (!tokenizer.hasMoreTokens()) {
  107. break;
  108. }
  109. lastToken = tokenizer.nextToken();
  110. } while (lastToken.equals("AND"));
  111. }
  112. if (lastToken.equals("ORDER")) {
  113. lastToken = tokenizer.nextToken(); // skip "BY"
  114. assert lastToken.equals("BY");
  115. while (parseSort()) {
  116. }
  117. }
  118. }
  119. return query;
  120. }
  121. private boolean parseSort() {
  122. String propName = tokenizer.nextToken();
  123. String direction = tokenizer.nextToken();
  124. boolean moreSort = direction.endsWith(",");
  125. if (moreSort) {
  126. direction = direction.substring(0, direction.length() - 1);
  127. }
  128. SortDirection sd = null;
  129. if (direction.equals("ASC")) {
  130. sd = SortDirection.ASCENDING;
  131. } else if (direction.equals("DESC")) {
  132. sd = SortDirection.DESCENDING;
  133. } else {
  134. assert false : direction + " is not ASC or DESC";
  135. }
  136. query.addSort(propName, sd);
  137. return moreSort;
  138. }
  139. private void parseFilter() {
  140. String filter = tokenizer.nextToken();
  141. int opIndex = -1;
  142. for (String op : OPERATORS) {
  143. opIndex = filter.indexOf(op);
  144. if (opIndex != -1) {
  145. String propName = filter.substring(0, opIndex);
  146. FilterOperator operator = OPERATOR_MAP.get(op);
  147. String propValue = filter.substring(opIndex + op.length());
  148. propValue = parseValue(propValue);
  149. // special case: year has int value
  150. if (propName.equals("year")) {
  151. query.addFilter(propName, operator, Integer.parseInt(propValue));
  152. } else {
  153. query.addFilter(propName, operator, propValue);
  154. }
  155. break;
  156. }
  157. }
  158. assert opIndex != -1 : filter + "missing comparison operator";
  159. }
  160. private String parseValue(String propValue) {
  161. // not quoted
  162. if (!propValue.startsWith("'")) {
  163. return propValue;
  164. }
  165. while (!propValue.endsWith("'")) {
  166. // an incomplete quoted literal
  167. propValue += " " + tokenizer.nextToken();
  168. }
  169. propValue = propValue.substring(1, propValue.length() - 1);
  170. return propValue;
  171. }
  172. private StringTokenizer tokenizer;
  173. private Query query;
  174. private static final String[] OPERATORS = {">=", ">", "<=", "<", "="};
  175. private static final FilterOperator[] OPERATOR_ENUMS =
  176. {FilterOperator.GREATER_THAN_OR_EQUAL, FilterOperator.GREATER_THAN,
  177. FilterOperator.LESS_THAN_OR_EQUAL, FilterOperator.LESS_THAN, FilterOperator.EQUAL};
  178. private static final Map<String, FilterOperator> OPERATOR_MAP =
  179. new HashMap<String, FilterOperator>();
  180. static {
  181. for (int ii = 0; ii < OPERATORS.length; ii++) {
  182. OPERATOR_MAP.put(OPERATORS[ii], OPERATOR_ENUMS[ii]);
  183. }
  184. }
  185. }
  186. public void close() {
  187. // no clean-up needed for DataStore.
  188. }
  189. }