PageRenderTime 43ms CodeModel.GetById 5ms app.highlight 33ms RepoModel.GetById 1ms app.codeStats 0ms

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