Resolve ROO-1452. Allows RequestFactory service methods to return null values. Patch by: bobv Review by: rjrjr git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8867 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/PersonEditorWorkflow.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/PersonEditorWorkflow.java index 0cf012c..290f801 100644 --- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/PersonEditorWorkflow.java +++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/PersonEditorWorkflow.java
@@ -59,8 +59,8 @@ final DynaTableRequestFactory requestFactory, final FavoritesManager manager) { eventBus.addHandler(EditPersonEvent.TYPE, new EditPersonEvent.Handler() { - public void startEdit(PersonProxy person) { - new PersonEditorWorkflow(requestFactory, manager, person).edit(); + public void startEdit(PersonProxy person, Request<?> request) { + new PersonEditorWorkflow(requestFactory, manager, person).edit(request); } }); } @@ -79,7 +79,7 @@ private Driver editorDriver; private final FavoritesManager manager; - private final PersonProxy person; + private PersonProxy person; private final DynaTableRequestFactory requestFactory; private PersonEditorWorkflow(DynaTableRequestFactory requestFactory, @@ -98,12 +98,12 @@ } @UiHandler("cancel") - void onCancel(@SuppressWarnings("unused") ClickEvent e) { + void onCancel(ClickEvent e) { dialog.hide(); } @UiHandler("save") - void onSave(@SuppressWarnings("unused") ClickEvent e) { + void onSave(ClickEvent e) { // MOVE TO ACTIVITY END final Request<Void> request = editorDriver.<Void> flush(); if (editorDriver.hasErrors()) { @@ -125,18 +125,28 @@ } @UiHandler("favorite") - void onValueChanged(@SuppressWarnings("unused") ValueChangeEvent<Boolean> event) { + void onValueChanged(ValueChangeEvent<Boolean> event) { manager.setFavorite(person, favorite.getValue()); } - private void edit() { - // The request is configured arbitrarily - ProxyRequest<PersonProxy> fetchRequest = requestFactory.personRequest().findPerson( - person.getId()); - + private void edit(Request<?> request) { editorDriver = GWT.create(Driver.class); - editorDriver.initialize(null, requestFactory, personEditor); + editorDriver.initialize(requestFactory, personEditor); + if (request == null) { + fetchAndEdit(); + return; + } + + editorDriver.edit(person, request); + personEditor.focus(); + favorite.setValue(manager.isFavorite(person), false); + dialog.center(); + } + + private void fetchAndEdit() { + // The request is configured arbitrarily + ProxyRequest<PersonProxy> fetchRequest = requestFactory.find(person.stableId()); // Add the paths that the EditorDelegate computes are necessary fetchRequest.with(editorDriver.getPaths()); @@ -144,15 +154,11 @@ fetchRequest.fire(new Receiver<PersonProxy>() { @Override public void onSuccess(PersonProxy person) { + PersonEditorWorkflow.this.person = person; // Start the edit process - editorDriver.edit(person, - requestFactory.personRequest().persist(person)); - personEditor.focus(); + Request<Void> request = requestFactory.personRequest().persist(person); + edit(request); } }); - - // Set up UI while waiting for data - favorite.setValue(manager.isFavorite(person), false); - dialog.center(); } }
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/events/EditPersonEvent.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/events/EditPersonEvent.java index 744cddd..737ff85 100644 --- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/events/EditPersonEvent.java +++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/events/EditPersonEvent.java
@@ -17,6 +17,7 @@ import com.google.gwt.event.shared.EventHandler; import com.google.gwt.event.shared.GwtEvent; +import com.google.gwt.requestfactory.shared.Request; import com.google.gwt.sample.dynatablerf.shared.PersonProxy; /** @@ -31,18 +32,24 @@ * Handles {@link EditPersonEvent}. */ public interface Handler extends EventHandler { - void startEdit(PersonProxy person); + void startEdit(PersonProxy person, Request<?> request); } private final PersonProxy person; + private final Request<?> request; public EditPersonEvent(PersonProxy person) { + this(person, null); + } + + public EditPersonEvent(PersonProxy person, Request<?> request) { this.person = person; + this.request = request; } @Override protected void dispatch(Handler handler) { - handler.startEdit(person); + handler.startEdit(person, request); } @Override
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/events/FilterChangeEvent.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/events/FilterChangeEvent.java index b9bcf98..5cb4864 100644 --- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/events/FilterChangeEvent.java +++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/events/FilterChangeEvent.java
@@ -15,8 +15,10 @@ */ package com.google.gwt.sample.dynatablerf.client.events; +import com.google.gwt.event.shared.EventBus; import com.google.gwt.event.shared.EventHandler; import com.google.gwt.event.shared.GwtEvent; +import com.google.gwt.event.shared.HandlerRegistration; /** * An event to indicate a change in the filter options. @@ -30,6 +32,11 @@ } public static final Type<Handler> TYPE = new Type<Handler>(); + + public static HandlerRegistration register(EventBus eventBus, Handler handler) { + return eventBus.addHandler(TYPE, handler); + } + private final int day; private final boolean selected;
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/DayCheckBox.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/DayCheckBox.java index 128b82c..79a1fc4 100644 --- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/DayCheckBox.java +++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/DayCheckBox.java
@@ -35,7 +35,7 @@ public DayCheckBox(EventBus eventBus) { this.eventBus = eventBus; initWidget(cb); - cb.setEnabled(false); + cb.setValue(true); cb.addValueChangeHandler(new ValueChangeHandler<Boolean>() { public void onValueChange(ValueChangeEvent<Boolean> event) { DayCheckBox.this.eventBus.fireEvent(new FilterChangeEvent(getDay(),
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/FavoritesWidget.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/FavoritesWidget.java index 580d410..62e87d6 100644 --- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/FavoritesWidget.java +++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/FavoritesWidget.java
@@ -86,7 +86,7 @@ @UiField Style style; - private final List<PersonProxy> displayed; + private final List<PersonProxy> displayedList; private final EventBus eventBus; private final RequestFactory factory; private FavoritesManager manager; @@ -117,7 +117,7 @@ driver.display(new ArrayList<PersonProxy>()); // Modifying this list triggers widget creation and destruction - displayed = listEditor.getList(); + displayedList = listEditor.getList(); } @Override @@ -149,16 +149,32 @@ return; } - if (event.isFavorite()) { - displayed.add(person); + // EntityProxies should be compared by stable id + EntityProxyId<PersonProxy> lookFor = person.stableId(); + PersonProxy found = null; + for (PersonProxy displayed : displayedList) { + if (displayed.stableId().equals(lookFor)) { + found = displayed; + break; + } + } + + if (event.isFavorite() && found == null) { + displayedList.add(person); + } else if (!event.isFavorite() && found != null) { + displayedList.remove(person); } else { - displayed.remove(person); + // No change + return; } // Sorting the list of PersonProxies will also change the UI display - Collections.sort(displayed, new Comparator<PersonProxy>() { + Collections.sort(displayedList, new Comparator<PersonProxy>() { public int compare(PersonProxy o1, PersonProxy o2) { - return o1.getName().compareTo(o2.getName()); + // Newly-created records may have null names + String name1 = o1.getName() == null ? "" : o1.getName(); + String name2 = o2.getName() == null ? "" : o2.getName(); + return name1.compareToIgnoreCase(name2); } }); }
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/SummaryWidget.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/SummaryWidget.java index f7a8c31..4e2dbcf 100644 --- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/SummaryWidget.java +++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/SummaryWidget.java
@@ -17,13 +17,17 @@ import com.google.gwt.cell.client.TextCell; import com.google.gwt.core.client.GWT; +import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.shared.EventBus; import com.google.gwt.requestfactory.shared.EntityProxyChange; import com.google.gwt.requestfactory.shared.EntityProxyId; import com.google.gwt.requestfactory.shared.Receiver; +import com.google.gwt.requestfactory.shared.Request; import com.google.gwt.requestfactory.shared.WriteOperation; import com.google.gwt.resources.client.CssResource; import com.google.gwt.sample.dynatablerf.client.events.EditPersonEvent; +import com.google.gwt.sample.dynatablerf.client.events.FilterChangeEvent; +import com.google.gwt.sample.dynatablerf.shared.AddressProxy; import com.google.gwt.sample.dynatablerf.shared.DynaTableRequestFactory; import com.google.gwt.sample.dynatablerf.shared.PersonProxy; import com.google.gwt.uibinder.client.UiBinder; @@ -111,6 +115,9 @@ private final DynaTableRequestFactory requestFactory; private final SingleSelectionModel<PersonProxy> selectionModel = new SingleSelectionModel<PersonProxy>(); + private boolean[] filter = new boolean[] { + true, true, true, true, true, true, true}; + public SummaryWidget(EventBus eventBus, DynaTableRequestFactory requestFactory, int numRows) { this.eventBus = eventBus; @@ -139,6 +146,13 @@ } }); + FilterChangeEvent.register(eventBus, new FilterChangeEvent.Handler() { + public void onFilterChanged(FilterChangeEvent e) { + filter[e.getDay()] = e.isSelected(); + fetch(0); + } + }); + selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() { public void onSelectionChange(SelectionChangeEvent event) { SummaryWidget.this.refreshSelection(); @@ -148,6 +162,16 @@ fetch(0); } + @UiHandler("create") + void onCreate(ClickEvent event) { + AddressProxy address = requestFactory.create(AddressProxy.class); + PersonProxy person = requestFactory.create(PersonProxy.class); + Request<Void> request = requestFactory.personRequest().persist(person); + person = request.edit(person); + person.setAddress(address); + eventBus.fireEvent(new EditPersonEvent(person, request)); + } + void onPersonChanged(EntityProxyChange<PersonProxy> event) { if (WriteOperation.UPDATE.equals(event.getWriteOperation())) { EntityProxyId<PersonProxy> personId = event.getProxyId(); @@ -156,18 +180,17 @@ int displayOffset = offsetOf(personId); if (displayOffset != -1) { // Record is onscreen and may differ from our data - requestFactory.find(personId).fire( - new Receiver<PersonProxy>() { - @Override - public void onSuccess(PersonProxy person) { - // Re-check offset in case of changes while waiting for data - int offset = offsetOf(person.stableId()); - if (offset != -1) { - table.setRowData(table.getPageStart() + offset, - Collections.singletonList(person)); - } - } - }); + requestFactory.find(personId).fire(new Receiver<PersonProxy>() { + public void onSuccess(PersonProxy response) { + PersonProxy person = (PersonProxy) response; + // Re-check offset in case of changes while waiting for data + int offset = offsetOf(person.stableId()); + if (offset != -1) { + table.setRowData(table.getPageStart() + offset, + Collections.singletonList(person)); + } + } + }); } } } @@ -185,11 +208,12 @@ if (person == null) { return; } - SummaryWidget.this.eventBus.fireEvent(new EditPersonEvent(person)); + eventBus.fireEvent(new EditPersonEvent(person)); selectionModel.setSelected(person, false); } private void fetch(final int start) { + // XXX add back filter requestFactory.schoolCalendarRequest().getPeople(start, numRows).fire( new Receiver<List<PersonProxy>>() { @Override
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/SummaryWidget.ui.xml b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/SummaryWidget.ui.xml index 411a374..f7e7269 100644 --- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/SummaryWidget.ui.xml +++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/SummaryWidget.ui.xml
@@ -3,21 +3,30 @@ <ui:style src="../common.css" type="com.google.gwt.sample.dynatablerf.client.widgets.SummaryWidget.Style"> .displayInline { - display: inline; + display: inline; } .thirty { - width: 30% + width: 30% } </ui:style> <g:DockLayoutPanel ui:field="dock" unit="EX"> <g:north size="6"> - <g:HTMLPanel> - <div class="{style.rightAlign}"> - <cv:SimplePager ui:field="pager" stylePrimaryName="{style.displayInline}" - display="{table}" /> - </div> - </g:HTMLPanel> + <g:DockLayoutPanel unit="PCT"> + <g:east size="45"> + <g:SimplePanel stylePrimaryName="{style.rightAlign}"> + <cv:SimplePager ui:field="pager" stylePrimaryName="{style.displayInline}" + display="{table}" /> + </g:SimplePanel> + </g:east> + <g:west size="45"> + <g:SimplePanel> + <g:Button stylePrimaryName="{style.displayInline}" + ui:field="create">New + Person</g:Button> + </g:SimplePanel> + </g:west> + </g:DockLayoutPanel> </g:north> <g:center> <cv:CellTable ui:field="table" />
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Address.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Address.java index bed0090..b87ee48 100644 --- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Address.java +++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Address.java
@@ -37,7 +37,7 @@ @Size(min = 1) private String city; - @NotNull + // May be null if Address is newly-created private String id; @NotNull
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Person.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Person.java index b7b2dda..7e5caca 100644 --- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Person.java +++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Person.java
@@ -24,7 +24,15 @@ /** * Hold relevant data for Person. */ -public abstract class Person { +public class Person { + /** + * New instances could be created on the client, but it's a better demo to + * send back a Person with a bunch of dummy data. + */ + public static Person createPerson() { + return SchoolCalendarService.createPerson(); + } + /** * The RequestFactory requires a static finder method for each proxied type. * Soon it should allow you to customize how instances are found. @@ -41,13 +49,16 @@ private final Address address = new Address(); @NotNull + private Schedule classSchedule = new Schedule(); + + @NotNull private String description = "DESC"; @NotNull @Size(min = 2, message = "Persons aren't just characters") private String name; - @NotNull + // May be null if the person is newly-created private String id; @NotNull @@ -56,6 +67,9 @@ private String note; + private final boolean[] daysFilters = new boolean[] { + true, true, true, true, true, true, true}; + public Person() { } @@ -65,6 +79,7 @@ public void copyFrom(Person copyFrom) { address.copyFrom(copyFrom.address); + classSchedule = copyFrom.classSchedule; description = copyFrom.description; name = copyFrom.name; id = copyFrom.id; @@ -76,6 +91,10 @@ return address; } + public Schedule getClassSchedule() { + return classSchedule; + } + public String getDescription() { return description; } @@ -100,10 +119,12 @@ } public String getSchedule() { - return getSchedule(new boolean[] {true, true, true, true, true, true, true}); + return getSchedule(daysFilters); } - public abstract String getSchedule(boolean[] daysFilter); + public String getSchedule(boolean[] daysFilter) { + return classSchedule.getDescription(daysFilter); + } /** * The RequestFactory requires an Integer version property for each proxied @@ -113,7 +134,9 @@ return version; } - public abstract Person makeCopy(); + public Person makeCopy() { + return new Person(this); + } /** * When this was written the RequestFactory required a persist method per @@ -124,6 +147,16 @@ SchoolCalendarService.persist(this); } + public void setAddress(Address address) { + this.address.copyFrom(address); + } + + public void setDaysFilter(boolean[] daysFilter) { + assert daysFilter.length == this.daysFilters.length; + System.arraycopy(daysFilter, 0, this.daysFilters, 0, + this.daysFilters.length); + } + public void setDescription(String description) { this.description = description; }
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Professor.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Professor.java deleted file mode 100644 index 3e3a296..0000000 --- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Professor.java +++ /dev/null
@@ -1,46 +0,0 @@ -/* - * Copyright 2007 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.google.gwt.sample.dynatablerf.domain; - -/** - * Holds relevant data for a Professor type Person. - */ -public class Professor extends Person { - - private Schedule teachingSchedule = new Schedule(); - - public Professor() { - } - - private Professor(Professor copyFrom) { - super(copyFrom); - teachingSchedule = copyFrom.teachingSchedule; - } - - @Override - public String getSchedule(boolean[] daysFilter) { - return teachingSchedule.getDescription(daysFilter); - } - - public Schedule getTeachingSchedule() { - return teachingSchedule; - } - - @Override - public Professor makeCopy() { - return new Professor(this); - } -}
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Student.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Student.java deleted file mode 100644 index c7be880..0000000 --- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Student.java +++ /dev/null
@@ -1,46 +0,0 @@ -/* - * Copyright 2007 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.google.gwt.sample.dynatablerf.domain; - -/** - * Holds relevant data for a Student type Person. - */ -public class Student extends Person { - - private Schedule classSchedule = new Schedule(); - - public Student() { - } - - private Student(Student copyFrom) { - super(copyFrom); - classSchedule = copyFrom.classSchedule; - } - - public Schedule getClassSchedule() { - return classSchedule; - } - - @Override - public String getSchedule(boolean[] daysFilter) { - return classSchedule.getDescription(daysFilter); - } - - @Override - public Student makeCopy() { - return new Student(this); - } -}
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/PersonFuzzer.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/PersonFuzzer.java index 9bbfaba..3a0c79b 100644 --- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/PersonFuzzer.java +++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/PersonFuzzer.java
@@ -16,9 +16,7 @@ package com.google.gwt.sample.dynatablerf.server; import com.google.gwt.sample.dynatablerf.domain.Person; -import com.google.gwt.sample.dynatablerf.domain.Professor; import com.google.gwt.sample.dynatablerf.domain.Schedule; -import com.google.gwt.sample.dynatablerf.domain.Student; import com.google.gwt.sample.dynatablerf.domain.TimeSlot; import java.util.ArrayList; @@ -54,6 +52,13 @@ private static final int STUDENTS_PER_PROF = 5; + public static Person generatePerson() { + Random rnd = new Random(); + Person toReturn = generateRandomPerson(rnd); + AddressFuzzer.fuzz(rnd, toReturn.getAddress()); + return toReturn; + } + public static List<Person> generateRandomPeople() { List<Person> toReturn = new ArrayList<Person>(MAX_PEOPLE); Random rnd = new Random(3); @@ -76,7 +81,7 @@ } private static Person generateRandomProfessor(Random rnd) { - Professor prof = new Professor(); + Person prof = new Person(); String firstName = pickRandomString(rnd, FIRST_NAMES); String lastName = pickRandomString(rnd, LAST_NAMES); @@ -85,7 +90,7 @@ String subject = pickRandomString(rnd, SUBJECTS); prof.setDescription("Professor of " + subject); - generateRandomSchedule(rnd, prof.getTeachingSchedule()); + generateRandomSchedule(rnd, prof.getClassSchedule()); return prof; } @@ -115,7 +120,7 @@ } private static Person generateRandomStudent(Random rnd) { - Student student = new Student(); + Person student = new Person(); String firstName = pickRandomString(rnd, FIRST_NAMES); String lastName = pickRandomString(rnd, LAST_NAMES); @@ -133,5 +138,4 @@ int i = rnd.nextInt(a.length); return a[i]; } - }
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/PersonSource.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/PersonSource.java index 5db0d1c..08ec81c 100644 --- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/PersonSource.java +++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/PersonSource.java
@@ -42,7 +42,8 @@ } @Override - public List<Person> getPeople(int startIndex, int maxCount) { + public List<Person> getPeople(int startIndex, int maxCount, + boolean[] daysFilter) { int peopleCount = countPeople(); int start = startIndex; @@ -54,6 +55,7 @@ if (start == end) { return Collections.emptyList(); } + // This is ugly, but a real backend would have a skip mechanism return new ArrayList<Person>(people.values()).subList(start, end); } @@ -104,10 +106,14 @@ } @Override - public List<Person> getPeople(int startIndex, int maxCount) { + public List<Person> getPeople(int startIndex, int maxCount, + boolean[] daysFilter) { List<Person> toReturn = new ArrayList<Person>(maxCount); - for (Person person : backingStore.getPeople(startIndex, maxCount)) { - toReturn.add(findPerson(person.getId())); + for (Person person : backingStore.getPeople(startIndex, maxCount, + daysFilter)) { + Person copy = findPerson(person.getId()); + toReturn.add(copy); + copy.setDaysFilter(daysFilter); } return toReturn; } @@ -148,7 +154,8 @@ public abstract Person findPerson(String id); - public abstract List<Person> getPeople(int startIndex, int maxCount); + public abstract List<Person> getPeople(int startIndex, int maxCount, + boolean[] daysFilter); public abstract void persist(Address address);
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/SchoolCalendarService.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/SchoolCalendarService.java index 61170f2..3cdc9a8 100644 --- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/SchoolCalendarService.java +++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/SchoolCalendarService.java
@@ -34,14 +34,24 @@ public class SchoolCalendarService implements Filter { private static final ThreadLocal<PersonSource> PERSON_SOURCE = new ThreadLocal<PersonSource>(); + public static Person createPerson() { + checkPersonSource(); + Person person = PersonFuzzer.generatePerson(); + PERSON_SOURCE.get().persist(person); + return person; + } + public static Person findPerson(String id) { checkPersonSource(); return PERSON_SOURCE.get().findPerson(id); } + /** + * XXX Remove this once primitive lists work. + */ public static List<Person> getPeople(int startIndex, int maxCount) { - checkPersonSource(); - return PERSON_SOURCE.get().getPeople(startIndex, maxCount); + return getPeople(startIndex, maxCount, new boolean[] { + true, true, true, true, true, true, true}); } public static void persist(Address address) { @@ -61,6 +71,15 @@ } } + /** + * XXX private due to inability to add method overloads. + */ + private static List<Person> getPeople(int startIndex, int maxCount, + boolean[] filter) { + checkPersonSource(); + return PERSON_SOURCE.get().getPeople(startIndex, maxCount, filter); + } + private PersonSource backingStore; public void destroy() {
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/DynaTableRequestFactory.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/DynaTableRequestFactory.java index c76996c..a08e09a 100644 --- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/DynaTableRequestFactory.java +++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/DynaTableRequestFactory.java
@@ -18,7 +18,6 @@ import com.google.gwt.requestfactory.shared.Instance; import com.google.gwt.requestfactory.shared.LoggingRequest; import com.google.gwt.requestfactory.shared.ProxyListRequest; -import com.google.gwt.requestfactory.shared.ProxyRequest; import com.google.gwt.requestfactory.shared.Request; import com.google.gwt.requestfactory.shared.RequestFactory; import com.google.gwt.requestfactory.shared.Service; @@ -37,8 +36,6 @@ */ @Service(Address.class) interface AddressRequest { - ProxyRequest<AddressProxy> findAddress(String id); - @Instance Request<Void> persist(AddressProxy person); } @@ -48,8 +45,6 @@ */ @Service(Person.class) interface PersonRequest { - ProxyRequest<PersonProxy> findPerson(String id); - @Instance Request<Void> persist(PersonProxy person); } @@ -59,7 +54,6 @@ */ @Service(SchoolCalendarService.class) interface SchoolCalendarRequest { - // TODO(amitmanjhi, cromwellian) Request<List<PersonProxy>> ProxyListRequest<PersonProxy> getPeople(int startIndex, int maxCount); }
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/PersonProxy.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/PersonProxy.java index 31413a5..deae1b3 100644 --- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/PersonProxy.java +++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/PersonProxy.java
@@ -38,6 +38,8 @@ String getSchedule(); + void setAddress(AddressProxy address); + void setDescription(String description); void setName(String name);
diff --git a/user/src/com/google/gwt/editor/client/EditorDelegate.java b/user/src/com/google/gwt/editor/client/EditorDelegate.java index 1e56034..d51c32b 100644 --- a/user/src/com/google/gwt/editor/client/EditorDelegate.java +++ b/user/src/com/google/gwt/editor/client/EditorDelegate.java
@@ -33,15 +33,6 @@ */ public interface EditorDelegate<T> { /** - * If a {@link ValueAwareEditor} chooses to modify the object passed into - * {@link ValueAwareEditor#setValue} directly, as opposed to manipulating its - * sub-Editors, the Editor must only call setter methods on the object - * returned from this method. This allows the backing service to optimize the - * read-only use case. - */ - T ensureMutable(T object); - - /** * Returns the Editor's path, relative to the root object. */ String getPath();
diff --git a/user/src/com/google/gwt/editor/client/ValueAwareEditor.java b/user/src/com/google/gwt/editor/client/ValueAwareEditor.java index 1d846bc..84da806 100644 --- a/user/src/com/google/gwt/editor/client/ValueAwareEditor.java +++ b/user/src/com/google/gwt/editor/client/ValueAwareEditor.java
@@ -39,11 +39,7 @@ * Called by the EditorDriver to set the object the Editor is peered with * <p> * ValueAwareEditors should preferentially use sub-editors to alter the - * properties of the object being edited. If this is not feasible, the value - * provided to this method must not be mutated directly without calling - * {@link EditorDelegate#ensureMutable()} to obtain a guaranteed-mutable - * instance. - * <p> + * properties of the object being edited. */ void setValue(T value); }
diff --git a/user/src/com/google/gwt/editor/client/adapters/TakesValueEditor.java b/user/src/com/google/gwt/editor/client/adapters/TakesValueEditor.java index 1cb1b0f..2472e85 100644 --- a/user/src/com/google/gwt/editor/client/adapters/TakesValueEditor.java +++ b/user/src/com/google/gwt/editor/client/adapters/TakesValueEditor.java
@@ -15,8 +15,6 @@ */ package com.google.gwt.editor.client.adapters; -import com.google.gwt.editor.client.EditorDelegate; -import com.google.gwt.editor.client.HasEditorDelegate; import com.google.gwt.editor.client.LeafValueEditor; import com.google.gwt.user.client.TakesValue; @@ -25,14 +23,12 @@ * * @param <T> the type of value to be edited */ -public class TakesValueEditor<T> implements LeafValueEditor<T>, - HasEditorDelegate<T> { +public class TakesValueEditor<T> implements LeafValueEditor<T> { public static <T> TakesValueEditor<T> of(TakesValue<T> peer) { return new TakesValueEditor<T>(peer); } - private EditorDelegate<T> delegate; private final TakesValue<T> peer; protected TakesValueEditor(TakesValue<T> peer) { @@ -43,19 +39,7 @@ return peer.getValue(); } - public void setDelegate(EditorDelegate<T> delegate) { - this.delegate = delegate; - } - public void setValue(T value) { - peer.setValue(delegate.ensureMutable(value)); - } - - protected EditorDelegate<T> getDelegate() { - return delegate; - } - - protected TakesValue<T> getPeer() { - return peer; + peer.setValue(value); } }
diff --git a/user/src/com/google/gwt/editor/client/adapters/ValueBoxEditor.java b/user/src/com/google/gwt/editor/client/adapters/ValueBoxEditor.java index d34d270..0433197 100644 --- a/user/src/com/google/gwt/editor/client/adapters/ValueBoxEditor.java +++ b/user/src/com/google/gwt/editor/client/adapters/ValueBoxEditor.java
@@ -15,6 +15,8 @@ */ package com.google.gwt.editor.client.adapters; +import com.google.gwt.editor.client.EditorDelegate; +import com.google.gwt.editor.client.HasEditorDelegate; import com.google.gwt.user.client.ui.ValueBoxBase; import java.text.ParseException; @@ -26,12 +28,14 @@ * * @param <T> the type of value to be edited */ -public class ValueBoxEditor<T> extends TakesValueEditor<T> { +public class ValueBoxEditor<T> extends TakesValueEditor<T> implements + HasEditorDelegate<T> { public static <T> ValueBoxEditor<T> of(ValueBoxBase<T> valueBox) { return new ValueBoxEditor<T>(valueBox); } + private EditorDelegate<T> delegate; private final ValueBoxBase<T> peer; private T value; @@ -40,6 +44,10 @@ this.peer = peer; } + public EditorDelegate<T> getDelegate() { + return delegate; + } + /** * Calls {@link ValueBoxBase#getValueOrThrow()}. If a {@link ParseException} * is thrown, it will be available through @@ -58,6 +66,10 @@ return value; } + public void setDelegate(EditorDelegate<T> delegate) { + this.delegate = delegate; + } + @Override public void setValue(T value) { peer.setValue(this.value = value);
diff --git a/user/src/com/google/gwt/editor/client/impl/AbstractEditorDelegate.java b/user/src/com/google/gwt/editor/client/impl/AbstractEditorDelegate.java index 5af1c44..911d743 100644 --- a/user/src/com/google/gwt/editor/client/impl/AbstractEditorDelegate.java +++ b/user/src/com/google/gwt/editor/client/impl/AbstractEditorDelegate.java
@@ -123,8 +123,6 @@ super(); } - public abstract T ensureMutable(T object); - /** * Flushes both data and errors. */ @@ -143,7 +141,6 @@ if (getObject() == null) { return; } - setObject(ensureMutable(getObject())); flushSubEditors(errors); if (editorChain != null) { @@ -178,7 +175,7 @@ } public void refresh(T object) { - setObject(object); + setObject(ensureMutable(object)); if (leafValueEditor != null) { leafValueEditor.setValue(object); } else if (valueAwareEditor != null) { @@ -202,6 +199,10 @@ throw new IllegalStateException(); } + protected <Q> Q ensureMutable(Q object) { + return object; + } + protected abstract void flushSubEditorErrors( List<EditorError> errorAccumulator); @@ -217,7 +218,7 @@ DelegateMap map) { this.path = pathSoFar; setEditor(editor); - setObject(object); + setObject(ensureMutable(object)); errors = new ArrayList<EditorError>(); simpleEditors = new HashMap<String, Editor<?>>();
diff --git a/user/src/com/google/gwt/editor/client/impl/SimpleBeanEditorDelegate.java b/user/src/com/google/gwt/editor/client/impl/SimpleBeanEditorDelegate.java index 5638879..5fb855f 100644 --- a/user/src/com/google/gwt/editor/client/impl/SimpleBeanEditorDelegate.java +++ b/user/src/com/google/gwt/editor/client/impl/SimpleBeanEditorDelegate.java
@@ -28,11 +28,6 @@ AbstractEditorDelegate<T, E> { @Override - public T ensureMutable(T object) { - return object; - } - - @Override public void initialize(String pathSoFar, T object, E editor, DelegateMap map) { super.initialize(pathSoFar, object, editor, map); }
diff --git a/user/src/com/google/gwt/editor/client/testing/MockEditorDelegate.java b/user/src/com/google/gwt/editor/client/testing/MockEditorDelegate.java index 3600b79..3706468 100644 --- a/user/src/com/google/gwt/editor/client/testing/MockEditorDelegate.java +++ b/user/src/com/google/gwt/editor/client/testing/MockEditorDelegate.java
@@ -32,13 +32,6 @@ private String path = ""; /** - * Returns <code>object</code>. - */ - public T ensureMutable(T object) { - return object; - } - - /** * Returns a zero-length string or the last value passed to {@link #setPath}. */ public String getPath() {
diff --git a/user/src/com/google/gwt/requestfactory/client/impl/AbstractRequest.java b/user/src/com/google/gwt/requestfactory/client/impl/AbstractRequest.java index 1841abf..78c8c4b 100644 --- a/user/src/com/google/gwt/requestfactory/client/impl/AbstractRequest.java +++ b/user/src/com/google/gwt/requestfactory/client/impl/AbstractRequest.java
@@ -67,16 +67,32 @@ public <P extends EntityProxy> P edit(P record) { P returnRecordImpl = (P) ((ProxyImpl) record).schema().create( ((ProxyImpl) record).asJso(), ((ProxyImpl) record).unpersisted()); - ((ProxyImpl) returnRecordImpl).putDeltaValueStore(deltaValueStore); + ((ProxyImpl) returnRecordImpl).putDeltaValueStore(deltaValueStore, this); return returnRecordImpl; } + /** + * Utility method to call {@link #edit} only on immutable proxy objects. Any + * other type of data will be returned as-is. + */ + public <Q> Q ensureMutable(Q o) { + if (o instanceof ProxyImpl) { + ProxyImpl proxy = (ProxyImpl) o; + if (!proxy.mutable()) { + @SuppressWarnings("unchecked") + Q editable = (Q) edit(proxy); + return editable; + } + } + return o; + } + public void fire(Receiver<? super T> receiver) { assert null != receiver : "receiver cannot be null"; this.receiver = receiver; requestFactory.fire(this); } - + /** * @deprecated use {@link #with(String...)} instead. * @param properties @@ -88,7 +104,7 @@ } return getThis(); } - + /** * @return the properties */ @@ -102,19 +118,23 @@ JsonResults results = JsonResults.fromResults(responseText); JsonServerException cause = results.getException(); if (cause != null) { - fail(new ServerFailure( - cause.getMessage(), cause.getType(), cause.getTrace())); + fail(new ServerFailure(cause.getMessage(), cause.getType(), + cause.getTrace())); return; } - processRelated(results.getRelated()); - // handle violations JsArray<DeltaValueStoreJsonImpl.ReturnRecord> violationsArray = results.getViolations(); if (violationsArray != null) { processViolations(violationsArray); } else { deltaValueStore.commit(results.getSideEffects()); - handleResult(results.getResult()); + processRelated(results.getRelated()); + if (results.isNullResult()) { + // Indicates the server explicitly meant to send a null value + succeed(null); + } else { + handleResult(results.getResult()); + } } } @@ -186,7 +206,7 @@ } else { id = violationRecord.getEncodedId(); } - final EntityProxyIdImpl key = new EntityProxyIdImpl(id, + final EntityProxyIdImpl<?> key = new EntityProxyIdImpl<EntityProxy>(id, requestFactory.getSchema(violationRecord.getSchema()), violationRecord.hasFutureId(), null); assert violationRecord.hasViolations(); @@ -206,7 +226,7 @@ return path; } - public EntityProxyId getProxyId() { + public EntityProxyId<?> getProxyId() { return key; } });
diff --git a/user/src/com/google/gwt/requestfactory/client/impl/AbstractRequestFactoryEditorDriver.java b/user/src/com/google/gwt/requestfactory/client/impl/AbstractRequestFactoryEditorDriver.java index bd6ff87..ce47bb4 100644 --- a/user/src/com/google/gwt/requestfactory/client/impl/AbstractRequestFactoryEditorDriver.java +++ b/user/src/com/google/gwt/requestfactory/client/impl/AbstractRequestFactoryEditorDriver.java
@@ -100,9 +100,8 @@ traverseEditors(paths); } - - public void initialize(RequestFactory requestFactory, - E editor) { + + public void initialize(RequestFactory requestFactory, E editor) { initialize(requestFactory.getEventBus(), requestFactory, editor); } @@ -171,8 +170,7 @@ private void checkSaveRequest() { if (saveRequest == null) { - throw new IllegalStateException( - "edit() was called with a null Request"); + throw new IllegalStateException("edit() was called with a null Request"); } } }
diff --git a/user/src/com/google/gwt/requestfactory/client/impl/DeltaValueStoreJsonImpl.java b/user/src/com/google/gwt/requestfactory/client/impl/DeltaValueStoreJsonImpl.java index 14252a5..17ab45a 100644 --- a/user/src/com/google/gwt/requestfactory/client/impl/DeltaValueStoreJsonImpl.java +++ b/user/src/com/google/gwt/requestfactory/client/impl/DeltaValueStoreJsonImpl.java
@@ -105,13 +105,13 @@ private final RequestFactoryJsonImpl requestFactory; // track C-U-D of CRUD operations - private final Map<EntityProxyId, ProxyJsoImpl> creates = new HashMap<EntityProxyId, ProxyJsoImpl>(); + private final Map<EntityProxyId<?>, ProxyJsoImpl> creates = new HashMap<EntityProxyId<?>, ProxyJsoImpl>(); - private final Map<EntityProxyId, ProxyJsoImpl> updates = new HashMap<EntityProxyId, ProxyJsoImpl>(); + private final Map<EntityProxyId<?>, ProxyJsoImpl> updates = new HashMap<EntityProxyId<?>, ProxyJsoImpl>(); // nothing for deletes because DeltaValueStore is not involved in deletes. The // operation alone suffices. - private final Map<EntityProxyId, WriteOperation> operations = new HashMap<EntityProxyId, WriteOperation>(); + private final Map<EntityProxyId<?>, WriteOperation> operations = new HashMap<EntityProxyId<?>, WriteOperation>(); private boolean used = false; @@ -136,17 +136,17 @@ int length = newRecords.length(); for (int i = 0; i < length; i++) { ReturnRecord newRecord = newRecords.get(i); - final EntityProxyIdImpl<?> futureKey = new EntityProxyIdImpl<EntityProxy>( - newRecord.getFutureId(), - requestFactory.getSchema(newRecord.getSchema()), - RequestFactoryJsonImpl.IS_FUTURE, null); - ProxyJsoImpl copy = ProxyJsoImpl.create( - newRecord.getFutureId(), 1, futureKey.schema, - requestFactory); + ProxySchema<?> schema = requestFactory.getSchema(newRecord.getSchema()); + EntityProxyIdImpl<?> futureKey = new EntityProxyIdImpl<EntityProxy>( + newRecord.getFutureId(), schema, RequestFactoryJsonImpl.IS_FUTURE, + null); + ProxyJsoImpl copy = ProxyJsoImpl.create(newRecord.getFutureId(), 1, + schema, requestFactory); toRemove.add(futureKey); requestFactory.datastoreToFutureMap.put(newRecord.getEncodedId(), futureKey.schema, futureKey.encodedId); - requestFactory.futureToDatastoreMap.put(futureKey.encodedId, newRecord.getEncodedId()); + requestFactory.futureToDatastoreMap.put(futureKey.encodedId, + newRecord.getEncodedId()); /* * TODO (amitmanjhi): get all the data from the server. make a copy of @@ -161,9 +161,7 @@ copy.merge(value); copy.putEncodedId(newRecord.getEncodedId()); } - ProxyJsoImpl masterRecord = master.records.get(futureKey); - assert masterRecord == null; - requestFactory.postChangeEvent(copy, WriteOperation.CREATE); + assert master.records.containsKey(futureKey); } } processToRemove(toRemove, WriteOperation.CREATE); @@ -175,11 +173,11 @@ int length = deletedRecords.length(); for (int i = 0; i < length; i++) { ReturnRecord deletedRecord = deletedRecords.get(i); - final EntityProxyIdImpl key = getPersistedProxyId( + EntityProxyIdImpl<?> key = getPersistedProxyId( deletedRecord.getEncodedId(), requestFactory.getSchema(deletedRecord.getSchema())); - ProxyJsoImpl copy = ProxyJsoImpl.create((String) key.encodedId, 1, key.schema, - requestFactory); + ProxyJsoImpl copy = ProxyJsoImpl.create((String) key.encodedId, 1, + key.schema, requestFactory); requestFactory.postChangeEvent(copy, WriteOperation.DELETE); master.records.remove(key); } @@ -191,10 +189,11 @@ int length = updatedRecords.length(); for (int i = 0; i < length; i++) { ReturnRecord updatedRecord = updatedRecords.get(i); - final EntityProxyIdImpl key = getPersistedProxyId(updatedRecord.getEncodedId(), + EntityProxyIdImpl<?> key = getPersistedProxyId( + updatedRecord.getEncodedId(), requestFactory.getSchema(updatedRecord.getSchema())); - ProxyJsoImpl copy = ProxyJsoImpl.create((String) key.encodedId, 1, key.schema, - requestFactory); + ProxyJsoImpl copy = ProxyJsoImpl.create((String) key.encodedId, 1, + key.schema, requestFactory); requestFactory.postChangeEvent(copy, WriteOperation.UPDATE); ProxyJsoImpl masterRecord = master.records.get(key); ProxyJsoImpl value = updates.get(key); @@ -213,11 +212,37 @@ processToRemove(toRemove, WriteOperation.UPDATE); } + public <V> V get(Property<V> property, EntityProxy record) { + ProxyJsoImpl proxy = creates.get(record.stableId()); + if (proxy == null) { + proxy = updates.get(record.stableId()); + } + if (proxy == null) { + return null; + } + return proxy.get(property); + } + public boolean isChanged() { return !operations.isEmpty(); } /** + * Returns <code>true</code> if a call to {@link #set} was made with the given + * property and record. + */ + public <V> boolean isPropertySet(Property<V> property, EntityProxy record) { + ProxyJsoImpl proxy = creates.get(record.stableId()); + if (proxy == null) { + proxy = updates.get(record.stableId()); + } + if (proxy == null) { + return false; + } + return proxy.isDefined(property.getName()); + } + + /** * Reset the used flag. To be called only when an unsuccessful reponse has * been received after a {@link #toJson()} string has been sent to the server. */ @@ -230,6 +255,8 @@ ProxyImpl recordImpl = (ProxyImpl) record; EntityProxyId<?> recordKey = recordImpl.stableId(); + retainValue(value); + ProxyJsoImpl rawMasterRecord = master.records.get(recordKey); WriteOperation priorOperation = operations.get(recordKey); if (rawMasterRecord == null && priorOperation == null) { @@ -277,13 +304,49 @@ } /** - * Has side effect of setting the used flag, meaning further - * sets will fail until clearUsed is called. Cannot be called - * while used. + * Clean-up logic to ensure that any values referenced in the payload will be + * transmitted to the server. Specifically, this ensures that all referenced, + * future ProxyImpls will be transmitted to the server, even if they have no + * associated property changes. + */ + void retainValue(Object value) { + if (value instanceof Iterable<?>) { + // Retain values in collections + for (Object o : (Iterable<?>) value) { + retainValue(o); + return; + } + } + + if (!(value instanceof ProxyImpl)) { + // Ignore anything that's not a proxy + return; + } + + ProxyImpl proxy = (ProxyImpl) value; + if (!proxy.unpersisted()) { + // A persisted proxy doesn't need to be retained + return; + } + + EntityProxyId<?> id = proxy.stableId(); + if (operations.containsKey(id)) { + // Already retained + return; + } + + // Retain + creates.put(id, proxy.asJso()); + operations.put(id, WriteOperation.CREATE); + } + + /** + * Has side effect of setting the used flag, meaning further sets will fail + * until clearUsed is called. Cannot be called while used. */ String toJson() { assertNotUsed(); - + used = true; StringBuffer jsonData = new StringBuffer("{"); for (WriteOperation writeOperation : new WriteOperation[] { @@ -304,14 +367,19 @@ /** * returns true if a new change record has been added. */ - private <V> boolean addNewChangeRecord(EntityProxyId recordKey, + private <V> boolean addNewChangeRecord(EntityProxyId<?> recordKey, ProxyImpl recordImpl, Property<V> property, V value) { ProxyJsoImpl rawMasterRecord = master.records.get(recordKey); ProxyJsoImpl changeRecord = newChangeRecord(recordImpl); if (isRealChange(property, value, rawMasterRecord)) { changeRecord.set(property, value); - updates.put(recordKey, changeRecord); - operations.put(recordKey, WriteOperation.UPDATE); + if (recordImpl.unpersisted()) { + creates.put(recordKey, changeRecord); + operations.put(recordKey, WriteOperation.CREATE); + } else { + updates.put(recordKey, changeRecord); + operations.put(recordKey, WriteOperation.UPDATE); + } return true; } return false; @@ -326,7 +394,7 @@ private void assertNotUsedAndCorrectType(EntityProxy record) { assertNotUsed(); - + if (!(record instanceof ProxyImpl)) { throw new IllegalArgumentException(record + " + must be an instance of " + ProxyImpl.class); @@ -335,22 +403,22 @@ private String getJsonForOperation(WriteOperation writeOperation) { assert (writeOperation == WriteOperation.CREATE || writeOperation == WriteOperation.UPDATE); - Map<EntityProxyId, ProxyJsoImpl> recordsMap = getRecordsMap(writeOperation); + Map<EntityProxyId<?>, ProxyJsoImpl> recordsMap = getRecordsMap(writeOperation); if (recordsMap.size() == 0) { return ""; } - StringBuffer requestData = new StringBuffer("\"" + writeOperation.getUnObfuscatedEnumName() - + "\":["); + StringBuffer requestData = new StringBuffer("\"" + + writeOperation.getUnObfuscatedEnumName() + "\":["); boolean first = true; - for (Map.Entry<EntityProxyId, ProxyJsoImpl> entry : recordsMap.entrySet()) { + for (Map.Entry<EntityProxyId<?>, ProxyJsoImpl> entry : recordsMap.entrySet()) { ProxyJsoImpl impl = entry.getValue(); if (first) { first = false; } else { requestData.append(","); } - requestData.append("{\"" - + entry.getValue().getSchema().getToken() + "\":"); + requestData.append("{\"" + entry.getValue().getSchema().getToken() + + "\":"); requestData.append(impl.toJson()); requestData.append("}"); } @@ -358,14 +426,14 @@ return requestData.toString(); } - private EntityProxyIdImpl getPersistedProxyId(String encodedId, + private EntityProxyIdImpl<?> getPersistedProxyId(String encodedId, ProxySchema<?> schema) { - return new EntityProxyIdImpl(encodedId, schema, + return new EntityProxyIdImpl<EntityProxy>(encodedId, schema, RequestFactoryJsonImpl.NOT_FUTURE, requestFactory.datastoreToFutureMap.get(encodedId, schema)); } - private Map<EntityProxyId, ProxyJsoImpl> getRecordsMap( + private Map<EntityProxyId<?>, ProxyJsoImpl> getRecordsMap( WriteOperation writeOperation) { switch (writeOperation) { case CREATE: @@ -386,10 +454,10 @@ return true; } - if (property instanceof CollectionProperty) { + if (property instanceof CollectionProperty<?, ?>) { return true; } - + masterRecord = rawMasterRecord.cast(); if (!masterRecord.isDefined(property.getName())) {
diff --git a/user/src/com/google/gwt/requestfactory/client/impl/JsoCollection.java b/user/src/com/google/gwt/requestfactory/client/impl/JsoCollection.java index 3c89f2f..4196c8b 100644 --- a/user/src/com/google/gwt/requestfactory/client/impl/JsoCollection.java +++ b/user/src/com/google/gwt/requestfactory/client/impl/JsoCollection.java
@@ -1,12 +1,12 @@ /* * Copyright 2010 Google Inc. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -26,12 +26,11 @@ /** * Inject dependencies needed by jso collections to commit mutations to * {@link DeltaValueStoreJsonImpl}. - * @param dvs DeltaValueStoreImpl obtained by editing a Proxy + * * @param property the Property corresponding to the contained type * @param proxy the Proxy on which this collection resides */ - void setDependencies(DeltaValueStoreJsonImpl dvs, Property property, - ProxyImpl proxy); + void setDependencies(Property<?> property, ProxyImpl proxy); /** * Returns the JavaScriptObject (usually a raw JS Array) backing this
diff --git a/user/src/com/google/gwt/requestfactory/client/impl/JsoList.java b/user/src/com/google/gwt/requestfactory/client/impl/JsoList.java index 5cc62a8..60b8829 100644 --- a/user/src/com/google/gwt/requestfactory/client/impl/JsoList.java +++ b/user/src/com/google/gwt/requestfactory/client/impl/JsoList.java
@@ -41,9 +41,7 @@ private final JavaScriptObject array; - private DeltaValueStoreJsonImpl deltaValueStore; - - private CollectionProperty property; + private CollectionProperty<JsoList<T>, T> property; private ProxyImpl record; @@ -69,10 +67,17 @@ return array; } + /** + * Ensures that any EntityProxy returned is mutable if the enclosing proxy + * is mutable. + */ @Override - @SuppressWarnings("unchecked") public T get(int i) { - return get0(i); + T toReturn = get0(i); + if (record.mutable()) { + toReturn = record.request().ensureMutable(toReturn); + } + return toReturn; } @SuppressWarnings("unchecked") @@ -190,10 +195,9 @@ return old; } - public void setDependencies(DeltaValueStoreJsonImpl dvs, Property property, - ProxyImpl proxy) { - this.deltaValueStore = dvs; - this.property = (CollectionProperty) property; + @SuppressWarnings("unchecked") + public void setDependencies(Property<?> property, ProxyImpl proxy) { + this.property = (CollectionProperty<JsoList<T>, T>) property; this.record = proxy; } @@ -203,8 +207,8 @@ } private void dvsUpdate() { - if (deltaValueStore != null) { - deltaValueStore.set(property, record, this); + if (record.mutable()) { + record.<JsoList<T>> set(property, record, this); } } @@ -212,11 +216,12 @@ return jso[i]; }-*/; + @SuppressWarnings("unchecked") private T get0(int i) { if (TypeLibrary.isProxyType(property.getLeafType())) { String key[] = ((JsArrayString) array).get(i).split("@", 3); - return (T) rf.getValueStore().getRecordBySchemaAndId(rf.getSchema(key[2]), - key[0], rf); + return (T) rf.getValueStore().getRecordBySchemaAndId( + rf.getSchema(key[2]), key[0], "IS".equals(key[1]), rf); } else { return (T) get(array, i, property.getLeafType()); }
diff --git a/user/src/com/google/gwt/requestfactory/client/impl/JsoSet.java b/user/src/com/google/gwt/requestfactory/client/impl/JsoSet.java index 424c066..dd804ad 100644 --- a/user/src/com/google/gwt/requestfactory/client/impl/JsoSet.java +++ b/user/src/com/google/gwt/requestfactory/client/impl/JsoSet.java
@@ -90,10 +90,9 @@ return false; } - public void setDependencies(DeltaValueStoreJsonImpl dvs, Property property, - ProxyImpl proxy) { + public void setDependencies(Property<?> property, ProxyImpl proxy) { list = new JsoList<T>(rf, array); - list.setDependencies(dvs, property, proxy); + list.setDependencies(property, proxy); for (T t : list) { set.add(key(t)); }
diff --git a/user/src/com/google/gwt/requestfactory/client/impl/JsonResults.java b/user/src/com/google/gwt/requestfactory/client/impl/JsonResults.java index ccc18a8..5030427 100644 --- a/user/src/com/google/gwt/requestfactory/client/impl/JsonResults.java +++ b/user/src/com/google/gwt/requestfactory/client/impl/JsonResults.java
@@ -51,4 +51,11 @@ final native JsArray<DeltaValueStoreJsonImpl.ReturnRecord> getViolations()/*-{ return this.violations || null; }-*/; + + /** + * Looks for an explicit <code>{result: null}</code> in the payload. + */ + final native boolean isNullResult() /*-{ + return this.result === null; + }-*/; } \ No newline at end of file
diff --git a/user/src/com/google/gwt/requestfactory/client/impl/ProxyImpl.java b/user/src/com/google/gwt/requestfactory/client/impl/ProxyImpl.java index 13a4c65..c7062fd 100644 --- a/user/src/com/google/gwt/requestfactory/client/impl/ProxyImpl.java +++ b/user/src/com/google/gwt/requestfactory/client/impl/ProxyImpl.java
@@ -43,9 +43,9 @@ private final ProxyJsoImpl jso; private final boolean isFuture; - private DeltaValueStoreJsonImpl deltaValueStore; - + private AbstractRequest<?, ?> request; + /** * For use by generated subclasses only. Other code should use * {@link ProxySchema#create(ProxyJsoImpl, boolean)}, typically: @@ -61,7 +61,6 @@ assert jso.getSchema() != null; this.jso = jso; this.isFuture = isFuture; - deltaValueStore = null; } public ProxyJsoImpl asJso() { @@ -73,10 +72,28 @@ } /** - * Get this proxy's value for the given property. Behavior is undefined if - * the proxy has no such property, or if the property has never been set. It - * is unusual to call this method directly. Rather it is expected to be called - * by bean-style getter methods provided by implementing classes. + * A ProxyImpl is equal to another ProxyImpl if they have equal EntityProxyIds + * and they are from the same Request object. + */ + @Override + public boolean equals(Object o) { + if (!(o instanceof ProxyImpl)) { + return false; + } + ProxyImpl other = (ProxyImpl) o; + return stableId().equals(other.stableId()) + && (request == other.request || request != null + && request.equals(other.request)); + } + + /** + * Get this proxy's value for the given property. Behavior is undefined if the + * proxy has no such property, or if the property has never been set. It is + * unusual to call this method directly. Rather it is expected to be called by + * bean-style getter methods provided by implementing classes. + * <p> + * If the ProxyImpl is mutable, any EntityProxies reachable from the return + * value will have already been made mutable. * * @param <V> the type of the property's value * @param property the property to fetch @@ -84,17 +101,29 @@ */ @SuppressWarnings("unchecked") public <V> V get(Property<V> property) { - if (property instanceof CollectionProperty) { - V toReturn = (V) jso.getCollection((CollectionProperty) property); - if (toReturn != null) { - ((JsoCollection) toReturn).setDependencies(deltaValueStore, property, - this); + // Read through to the DeltaValueStore to see if the entity has been mutated + V toReturn = null; + if (deltaValueStore != null + && deltaValueStore.isPropertySet(property, this)) { + toReturn = deltaValueStore.get(property, this); + } else { + if (property instanceof CollectionProperty) { + // Possibly create a new JsoCollection if one does not exist + toReturn = (V) jso.getCollection((CollectionProperty) property); + } else { + // Return a scalar property from the backing object + toReturn = jso.<V> get(property); } - return toReturn; } - return jso.<V> get(property); + // Ensure mutability + if (toReturn instanceof JsoCollection) { + ((JsoCollection) toReturn).setDependencies(property, this); + } else if (mutable()) { + toReturn = request.ensureMutable(toReturn); + } + return toReturn; } - + public <V> V get(String propertyName, Class<?> propertyType) { // javac 1.6.0_20 on mac has problems without the explicit parameterization return jso.<V> get(propertyName, propertyType); @@ -107,6 +136,20 @@ return deltaValueStore.isChanged(); } + @Override + public int hashCode() { + return stableId().hashCode() * 13 + + (request == null ? 0 : request.hashCode()) * 5; + } + + public boolean mutable() { + return deltaValueStore != null; + } + + public AbstractRequest<?, ?> request() { + return request; + } + public ProxySchema<?> schema() { return jso.getSchema(); } @@ -119,17 +162,17 @@ deltaValueStore.set(property, record, value); } - // Allow the generated subclass to return the specific type its public interface probably demands - @SuppressWarnings({"unchecked", "rawtypes"}) + /** + * Allow the generated subclass to return the specific type its public + * interface probably demands. + */ + @SuppressWarnings(value = {"unchecked", "rawtypes"}) public EntityProxyId stableId() { - if (!isFuture) { - return new EntityProxyIdImpl<ProxyImpl>( - encodedId(), - schema(), - false, - jso.getRequestFactory().datastoreToFutureMap.get(encodedId(), schema())); + if (isFuture) { + return new EntityProxyIdImpl(encodedId(), schema(), isFuture, null); } - return new EntityProxyIdImpl(encodedId(), schema(), isFuture, null); + return new EntityProxyIdImpl<ProxyImpl>(encodedId(), schema(), false, + jso.getRequestFactory().datastoreToFutureMap.get(encodedId(), schema())); } public boolean unpersisted() { @@ -147,8 +190,10 @@ protected ValueStoreJsonImpl valueStore() { return jso.getRequestFactory().getValueStore(); } - - void putDeltaValueStore(DeltaValueStoreJsonImpl deltaValueStore) { + + void putDeltaValueStore(DeltaValueStoreJsonImpl deltaValueStore, + AbstractRequest<?, ?> request) { this.deltaValueStore = deltaValueStore; + this.request = request; } }
diff --git a/user/src/com/google/gwt/requestfactory/client/impl/ProxyJsoImpl.java b/user/src/com/google/gwt/requestfactory/client/impl/ProxyJsoImpl.java index f7c9520..7f66fba 100644 --- a/user/src/com/google/gwt/requestfactory/client/impl/ProxyJsoImpl.java +++ b/user/src/com/google/gwt/requestfactory/client/impl/ProxyJsoImpl.java
@@ -154,7 +154,7 @@ assert type == List.class || type == Set.class; JsoCollection col = (JsoCollection) getCollection( (CollectionProperty) property); - col.setDependencies(null, property, null); + col.setDependencies(property, null); return (V) col; } // javac 1.6.0_20 on mac has problems without the explicit parameterization @@ -246,8 +246,9 @@ String schemaAndId[] = relatedId.split("@"); assert schemaAndId.length == 3; ProxySchema<?> schema = getRequestFactory().getSchema(schemaAndId[2]); - return (V) getRequestFactory().getValueStore().getRecordBySchemaAndId(schema, - schemaAndId[0], getRequestFactory()); + EntityProxy toReturn = getRequestFactory().getValueStore().getRecordBySchemaAndId(schema, + schemaAndId[0], schemaAndId[1].equals("IS"), getRequestFactory()); + return (V) toReturn; } }
diff --git a/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryEditorDelegate.java b/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryEditorDelegate.java index 56c346c..8229081 100644 --- a/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryEditorDelegate.java +++ b/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryEditorDelegate.java
@@ -46,17 +46,15 @@ public void onProxyChange(EntityProxyChange<EntityProxy> event) { if (event.getWriteOperation().equals(WriteOperation.UPDATE) - && event.getProxyId().equals( - ((EntityProxy) getObject()).stableId())) { + && event.getProxyId().equals(((EntityProxy) getObject()).stableId())) { List<String> paths = new ArrayList<String>(); traverse(paths); - @SuppressWarnings("rawtypes") - EntityProxyId id = event.getProxyId(); + EntityProxyId<?> id = event.getProxyId(); doFind(paths, id); } } - @SuppressWarnings({"rawtypes", "unchecked"}) + @SuppressWarnings( {"rawtypes", "unchecked"}) private void doFind(List<String> paths, EntityProxyId id) { factory.find(id).with(paths.toArray(new String[paths.size()])).fire( new SubscriptionReceiver()); @@ -76,20 +74,6 @@ protected RequestFactory factory; protected Request<?> request; - @Override - public P ensureMutable(P object) { - if (request == null) { - throw new IllegalStateException("No Request"); - } else if (object instanceof EntityProxy) { - @SuppressWarnings("unchecked") - P toReturn = (P) request.edit(((EntityProxy) object)); - return toReturn; - } else { - // Likely a value object - return object; - } - } - public void initialize(EventBus eventBus, RequestFactory factory, String pathSoFar, P object, E editor, DelegateMap delegateMap, Request<?> editRequest) { @@ -120,6 +104,15 @@ } @Override + protected <T> T ensureMutable(T object) { + if (request == null) { + // Read-only mode + return object; + } + return ((AbstractRequest<?, ?>) request).<T> ensureMutable(object); + } + + @Override protected <R, S extends Editor<R>> void initializeSubDelegate( AbstractEditorDelegate<R, S> subDelegate, String path, R object, S subEditor, DelegateMap delegateMap) {
diff --git a/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java b/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java index 8a92f1f..f6758f2 100644 --- a/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java +++ b/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java
@@ -112,6 +112,15 @@ public void fire(Request<?> requestObject) { final AbstractRequest<?, ?> abstractRequest = (AbstractRequest<?, ?>) requestObject; RequestData requestData = ((AbstractRequest<?, ?>) requestObject).getRequestData(); + + /* + * Prevent the "dummy object" case for parameters with no associated + * property updates. + */ + for (Object proxy : requestData.getProxyParameters()) { + abstractRequest.deltaValueStore.retainValue(proxy); + } + Map<String, String> requestMap = requestData.getRequestMap(abstractRequest.deltaValueStore.toJson()); String payload = ClientRequestHelper.getRequestString(requestMap); @@ -240,6 +249,10 @@ } ValueStoreJsonImpl getValueStore() { + if (valueStore == null) { + throw new IllegalStateException( + "must call RequestFactory.initialize() first"); + } return valueStore; } @@ -263,6 +276,9 @@ Long futureId = ++currentFutureId; ProxyJsoImpl newRecord = ProxyJsoImpl.create(Long.toString(futureId), initialVersion, schema, this); - return schema.create(newRecord, IS_FUTURE); + R created = schema.create(newRecord, IS_FUTURE); + // Need to be able to find the future object to hook up future-to-future ref + getValueStore().putFutureInValueStore(newRecord); + return created; } }
diff --git a/user/src/com/google/gwt/requestfactory/client/impl/ValueStoreJsonImpl.java b/user/src/com/google/gwt/requestfactory/client/impl/ValueStoreJsonImpl.java index 07fe41b..ffa3572 100644 --- a/user/src/com/google/gwt/requestfactory/client/impl/ValueStoreJsonImpl.java +++ b/user/src/com/google/gwt/requestfactory/client/impl/ValueStoreJsonImpl.java
@@ -32,17 +32,30 @@ class ValueStoreJsonImpl { // package protected fields for use by DeltaValueStoreJsonImpl - final Map<EntityProxyIdImpl, ProxyJsoImpl> records = new HashMap<EntityProxyIdImpl, ProxyJsoImpl>(); + final Map<EntityProxyIdImpl<?>, ProxyJsoImpl> records = new HashMap<EntityProxyIdImpl<?>, ProxyJsoImpl>(); EntityProxy getRecordBySchemaAndId(ProxySchema<?> schema, String encodedId, - RequestFactoryJsonImpl requestFactory) { + boolean isFuture, RequestFactoryJsonImpl requestFactory) { if (encodedId == null) { return null; } - // TODO: pass isFuture to this method from decoding ID string - EntityProxyIdImpl key = new EntityProxyIdImpl(encodedId, schema, false, - requestFactory.datastoreToFutureMap.get(encodedId, schema)); - return schema.create(records.get(key)); + + EntityProxyIdImpl<?> key = new EntityProxyIdImpl<EntityProxy>(encodedId, + schema, isFuture, requestFactory.datastoreToFutureMap.get(encodedId, + schema)); + return schema.create(records.get(key), isFuture); + } + + /** + * Puts a newly-created {@link ProxyJsoImpl} newJsoRecord in the valueStore + * following a create, and sends appropriate events. If the valuestore had a + * Jso that was the same or a superset of newJsoRecord, returns the valuestore + * jso. Otherwise, puts newJsoRecord in the valuestore and returns null. + * <p> + * package-protected for testing purposes. + */ + ProxyJsoImpl putFutureInValueStore(ProxyJsoImpl newJsoRecord) { + return putInValueStore(newJsoRecord, RequestFactoryJsonImpl.IS_FUTURE); } /** @@ -54,26 +67,7 @@ * package-protected for testing purposes. */ ProxyJsoImpl putInValueStore(ProxyJsoImpl newJsoRecord) { - EntityProxyIdImpl recordKey = new EntityProxyIdImpl(newJsoRecord.encodedId(), - newJsoRecord.getSchema(), RequestFactoryJsonImpl.NOT_FUTURE, - newJsoRecord.getRequestFactory().datastoreToFutureMap.get( - newJsoRecord.encodedId(), newJsoRecord.getSchema())); - - ProxyJsoImpl oldRecord = records.get(recordKey); - if (oldRecord == null) { - records.put(recordKey, newJsoRecord); - newJsoRecord.assertValid(); - newJsoRecord.getRequestFactory().postChangeEvent(newJsoRecord, - WriteOperation.ACQUIRE); - return null; - } - if (oldRecord.hasChanged(newJsoRecord)) { - records.put(recordKey, newJsoRecord); - newJsoRecord.getRequestFactory().postChangeEvent(newJsoRecord, - WriteOperation.UPDATE); - return null; - } - return oldRecord; + return putInValueStore(newJsoRecord, RequestFactoryJsonImpl.NOT_FUTURE); } void setRecords(JsArray<ProxyJsoImpl> newRecords) { @@ -84,4 +78,29 @@ } } } + + private ProxyJsoImpl putInValueStore(ProxyJsoImpl newJsoRecord, + boolean isFuture) { + RequestFactoryJsonImpl factory = newJsoRecord.getRequestFactory(); + EntityProxyIdImpl<?> recordKey = new EntityProxyIdImpl<EntityProxy>( + newJsoRecord.encodedId(), newJsoRecord.getSchema(), isFuture, + factory.datastoreToFutureMap.get(newJsoRecord.encodedId(), + newJsoRecord.getSchema())); + + ProxyJsoImpl oldRecord = records.get(recordKey); + if (oldRecord == null) { + records.put(recordKey, newJsoRecord); + newJsoRecord.assertValid(); + factory.postChangeEvent(newJsoRecord, isFuture ? WriteOperation.CREATE + : WriteOperation.ACQUIRE); + return null; + } + + if (oldRecord.hasChanged(newJsoRecord)) { + records.put(recordKey, newJsoRecord); + factory.postChangeEvent(newJsoRecord, WriteOperation.UPDATE); + return null; + } + return oldRecord; + } }
diff --git a/user/src/com/google/gwt/requestfactory/rebind/RequestFactoryGenerator.java b/user/src/com/google/gwt/requestfactory/rebind/RequestFactoryGenerator.java index 75e75db..76483ed 100644 --- a/user/src/com/google/gwt/requestfactory/rebind/RequestFactoryGenerator.java +++ b/user/src/com/google/gwt/requestfactory/rebind/RequestFactoryGenerator.java
@@ -92,17 +92,16 @@ */ public class RequestFactoryGenerator extends Generator { - private enum CollectionType { - SCALAR("ObjectRequestImpl", AbstractJsonObjectRequest.class), - LIST("ListRequestImpl", AbstractJsonProxyListRequest.class), - SET("SetRequestImpl", AbstractJsonProxySetRequest.class); + private enum CollectionType { + SCALAR("ObjectRequestImpl", AbstractJsonObjectRequest.class), LIST( + "ListRequestImpl", AbstractJsonProxyListRequest.class), SET( + "SetRequestImpl", AbstractJsonProxySetRequest.class); private String implName; private Class<?> requestClass; - CollectionType(String implName, - Class<?> requestClass) { + CollectionType(String implName, Class<?> requestClass) { this.implName = implName; this.requestClass = requestClass; } @@ -149,7 +148,7 @@ listType = typeOracle.findType(List.class.getName()); setType = typeOracle.findType(Set.class.getName()); entityProxyType = typeOracle.findType(EntityProxy.class.getName()); - + // Get a reference to the type that the generator should implement JClassType interfaceType = typeOracle.findType(interfaceName); @@ -208,7 +207,8 @@ continue; } - EntityProperty entityProperty = maybeComputePropertyFromMethod(method, logger); + EntityProperty entityProperty = maybeComputePropertyFromMethod(method, + logger); if (entityProperty != null) { String propertyName = entityProperty.getName(); if (!propertyNames.contains(propertyName)) { @@ -220,7 +220,7 @@ return entityProperties; }; - + private void ensureProxyType(TreeLogger logger, GeneratorContext generatorContext, JClassType publicProxyType) throws UnableToCompleteException { @@ -245,21 +245,21 @@ Set<JClassType> transitiveDeps = new LinkedHashSet<JClassType>(); if (pw != null) { - logger = logger.branch(TreeLogger.DEBUG, - "Generating " + publicProxyType.getName()); + logger = logger.branch(TreeLogger.DEBUG, "Generating " + + publicProxyType.getName()); ClassSourceFileComposerFactory f = new ClassSourceFileComposerFactory( packageName, proxyImplTypeName); - f.addImport(AbstractJsonProxySetRequest.class.getName()); + f.addImport(AbstractJsonProxySetRequest.class.getName()); f.addImport(AbstractJsonProxyListRequest.class.getName()); - f.addImport(AbstractJsonValueListRequest.class.getName()); + f.addImport(AbstractJsonValueListRequest.class.getName()); f.addImport(AbstractJsonObjectRequest.class.getName()); f.addImport(RequestFactoryJsonImpl.class.getName()); f.addImport(Property.class.getName()); f.addImport(EnumProperty.class.getName()); f.addImport(CollectionProperty.class.getName()); - + f.addImport(EntityProxy.class.getName()); f.addImport(ProxyImpl.class.getName()); f.addImport(ProxyJsoImpl.class.getName()); @@ -273,7 +273,8 @@ f.setSuperclass(ProxyImpl.class.getSimpleName()); f.addImplementedInterface(publicProxyType.getName()); - List<EntityProperty> entityProperties = computeEntityPropertiesFromProxyType(publicProxyType, logger); + List<EntityProperty> entityProperties = computeEntityPropertiesFromProxyType( + publicProxyType, logger); for (EntityProperty entityProperty : entityProperties) { JType type = entityProperty.getType(); if (type.isPrimitive() == null) { @@ -291,15 +292,13 @@ if (entityProperty.getType().isEnum() != null) { sw.println(String.format( "private static final Property<%1$s> %2$s = new EnumProperty<%1$s>(\"%2$s\", %1$s.class, %1$s.values());", - entityProperty.getType().getSimpleSourceName(), - name)); + entityProperty.getType().getSimpleSourceName(), name)); } else if (isCollection(typeOracle, entityProperty.getType())) { sw.println(String.format( "private static final Property<%1$s> %2$s = new CollectionProperty<%1$s, %3$s>(\"%2$s\", %1$s.class, %3$s.class);", entityProperty.getType().getSimpleSourceName(), name, - entityProperty.getType().isParameterized().getTypeArgs()[0].getQualifiedSourceName() - )); + entityProperty.getType().isParameterized().getTypeArgs()[0].getQualifiedSourceName())); } else { sw.println(String.format( "private static final Property<%1$s> %2$s = new Property<%1$s>(\"%2$s\", \"%3$s\", %1$s.class);", @@ -312,9 +311,12 @@ sw.println(); String simpleImplName = publicProxyType.getSimpleSourceName() + "Impl"; - printRequestImplClass(sw, publicProxyType, simpleImplName, CollectionType.LIST); - printRequestImplClass(sw, publicProxyType, simpleImplName, CollectionType.SET); - printRequestImplClass(sw, publicProxyType, simpleImplName, CollectionType.SCALAR); + printRequestImplClass(sw, publicProxyType, simpleImplName, + CollectionType.LIST); + printRequestImplClass(sw, publicProxyType, simpleImplName, + CollectionType.SET); + printRequestImplClass(sw, publicProxyType, simpleImplName, + CollectionType.SCALAR); sw.println(); sw.println(String.format( @@ -336,13 +338,12 @@ JClassType collectionType = returnType.isClassOrInterface(); if (collectionType != null && collectionType.isParameterized() != null) { - returnType = collectionType.isParameterized().getTypeArgs()[0]; - returnTypeString = collectionType.isParameterized().getParameterizedQualifiedSourceName(); + returnType = collectionType.isParameterized().getTypeArgs()[0]; + returnTypeString = collectionType.isParameterized().getParameterizedQualifiedSourceName(); } sw.println(); - sw.println(String.format("public %s get%s() {", - returnTypeString, + sw.println(String.format("public %s get%s() {", returnTypeString, capitalize(entityProperty.getName()))); sw.indent(); sw.println(String.format("return get(%s);", entityProperty.getName())); @@ -383,10 +384,8 @@ JClassType interfaceType, String packageName, String implName) throws UnableToCompleteException { - logger = logger.branch( - TreeLogger.DEBUG, - String.format("Generating implementation of %s", - interfaceType.getName())); + logger = logger.branch(TreeLogger.DEBUG, String.format( + "Generating implementation of %s", interfaceType.getName())); ClassSourceFileComposerFactory f = new ClassSourceFileComposerFactory( packageName, implName); @@ -414,8 +413,10 @@ } JType returnType = method.getReturnType(); if (null == returnType) { - logger.log(TreeLogger.ERROR, String.format( - "Illegal return type for %s. Methods of %s must return interfaces, found void", + logger.log( + TreeLogger.ERROR, + String.format( + "Illegal return type for %s. Methods of %s must return interfaces, found void", method.getName(), interfaceType.getName())); throw new UnableToCompleteException(); } @@ -428,7 +429,7 @@ } requestBuilders.add(method); } - + /* * Hard-code the requestSelectors specified in RequestFactory. */ @@ -461,11 +462,12 @@ sw.outdent(); sw.println("}"); sw.println(); - + // write getHistoryToken(proxyId) sw.println("public String getHistoryToken(EntityProxyId proxyId) {"); sw.indent(); - sw.println("return getHistoryToken(proxyId, new " + proxyToTypeMapName + "());"); + sw.println("return getHistoryToken(proxyId, new " + proxyToTypeMapName + + "());"); sw.outdent(); sw.println("}"); sw.println(); @@ -537,10 +539,8 @@ private void generateProxyToTypeMap(TreeLogger logger, GeneratorContext generatorContext, PrintWriter out, JClassType interfaceType, String packageName, String implName) { - logger = logger.branch( - TreeLogger.DEBUG, - String.format("Generating implementation of %s", - interfaceType.getName())); + logger = logger.branch(TreeLogger.DEBUG, String.format( + "Generating implementation of %s", interfaceType.getName())); ClassSourceFileComposerFactory f = new ClassSourceFileComposerFactory( packageName, implName); @@ -616,10 +616,8 @@ JMethod selectorMethod, JClassType mainType, String packageName, String implName) throws UnableToCompleteException { JClassType selectorInterface = selectorMethod.getReturnType().isInterface(); - logger = logger.branch( - TreeLogger.DEBUG, - String.format("Generating implementation of %s", - selectorInterface.getName())); + logger = logger.branch(TreeLogger.DEBUG, String.format( + "Generating implementation of %s", selectorInterface.getName())); ClassSourceFileComposerFactory f = new ClassSourceFileComposerFactory( packageName, implName); @@ -643,12 +641,13 @@ // write each method. for (JMethod method : selectorInterface.getOverridableMethods()) { - JParameterizedType parameterizedType = method.getReturnType() - .isParameterized(); + JParameterizedType parameterizedType = method.getReturnType().isParameterized(); if (parameterizedType == null) { - logger.log(TreeLogger.ERROR, String.format( - "Illegal return type for %s. Methods of %s must return Request<T>.", - method.getName(), selectorInterface.getName())); + logger.log( + TreeLogger.ERROR, + String.format( + "Illegal return type for %s. Methods of %s must return Request<T>.", + method.getName(), selectorInterface.getName())); throw new UnableToCompleteException(); } JClassType returnType = parameterizedType.getTypeArgs()[0]; @@ -673,17 +672,20 @@ } for (JParameter param : method.getParameters()) { if (param.getType().isArray() != null) { - logger.log(TreeLogger.ERROR, String.format( - "Illegal param type %s for %s. Methods of %s cannot take array parameters.", - param.getName(), method.getName(), selectorInterface.getName())); + logger.log( + TreeLogger.ERROR, + String.format( + "Illegal param type %s for %s. Methods of %s cannot take array parameters.", + param.getName(), method.getName(), + selectorInterface.getName())); throw new UnableToCompleteException(); } } if (isProxyCollectionRequest(typeOracle, requestType)) { Class<?> colType = getCollectionType(typeOracle, requestType); assert colType != null; - requestClassName = asInnerImplClass(colType == List.class ? - "ListRequestImpl" : "SetRequestImpl", returnType); + requestClassName = asInnerImplClass(colType == List.class + ? "ListRequestImpl" : "SetRequestImpl", returnType); } else if (isProxyRequest(typeOracle, requestType)) { if (selectorInterface.isAssignableTo(typeOracle.findType(FindRequest.class.getName()))) { extraArgs = ", proxyId"; @@ -692,17 +694,17 @@ requestClassName = asInnerImplClass("ObjectRequestImpl", returnType); } } else if (isValueListRequest(typeOracle, requestType)) { - requestClassName = AbstractJsonValueListRequest.class.getName(); - // generate argument list for AbstractJsonValueListRequest constructor - JClassType colType = requestType.isParameterized().getTypeArgs()[0]; - extraArgs = ", " + colType.isAssignableTo(setType); - // extraArgs = ", isSet" true if elementType of collection is Set - JClassType leafType = colType.isParameterized().getTypeArgs()[0]; - extraArgs += ", " + leafType.getQualifiedSourceName() + ".class"; - // extraArgs = ", isSet, collectionElementType.class" + requestClassName = AbstractJsonValueListRequest.class.getName(); + // generate argument list for AbstractJsonValueListRequest constructor + JClassType colType = requestType.isParameterized().getTypeArgs()[0]; + extraArgs = ", " + colType.isAssignableTo(setType); + // extraArgs = ", isSet" true if elementType of collection is Set + JClassType leafType = colType.isParameterized().getTypeArgs()[0]; + extraArgs += ", " + leafType.getQualifiedSourceName() + ".class"; + // extraArgs = ", isSet, collectionElementType.class" if (leafType.isAssignableTo(typeOracle.findType(Enum.class.getName()))) { // if contained type is Enum, pass Enum.values() - extraArgs += ", " + leafType.getQualifiedSourceName() + ".values()"; + extraArgs += ", " + leafType.getQualifiedSourceName() + ".values()"; } else { // else for all other types, pass null extraArgs += ", null"; @@ -734,8 +736,7 @@ } else if (isEnumRequest(typeOracle, requestType)) { requestClassName = AbstractEnumRequest.class.getName(); JClassType enumType = requestType.isParameterized().getTypeArgs()[0]; - extraArgs = ", " + enumType.getQualifiedSourceName() - + ".values()"; + extraArgs = ", " + enumType.getQualifiedSourceName() + ".values()"; } else if (isVoidRequest(typeOracle, requestType)) { requestClassName = AbstractVoidRequest.class.getName(); } else { @@ -746,15 +747,16 @@ sw.println(getMethodDeclaration(method) + " {"); sw.indent(); - sw.println( - "return new " + requestClassName + "(factory" + extraArgs + ") {"); + sw.println("return new " + requestClassName + "(factory" + extraArgs + + ") {"); sw.indent(); String requestDataName = RequestData.class.getSimpleName(); sw.println("public " + requestDataName + " getRequestData() {"); sw.indent(); - sw.println("return new " + requestDataName + "(\"" + operationName - + "\", " + getParametersAsString(method, typeOracle) + ", " - + "getPropertyRefs());"); + sw.println("return new %s(\"%s\", %s, %s, getPropertyRefs());", + RequestData.class.getSimpleName(), operationName, + getParametersAsString(method, typeOracle), + getEntityParameters(method)); sw.outdent(); sw.println("}"); sw.outdent(); @@ -775,12 +777,10 @@ */ private Class<?> getCollectionType(TypeOracle typeOracle, JClassType requestType) { - if (requestType.isAssignableTo( - typeOracle.findType(ProxyListRequest.class.getName()))) { + if (requestType.isAssignableTo(typeOracle.findType(ProxyListRequest.class.getName()))) { return List.class; } - if (requestType.isAssignableTo(typeOracle.findType( - ProxySetRequest.class.getName()))) { + if (requestType.isAssignableTo(typeOracle.findType(ProxySetRequest.class.getName()))) { return Set.class; } JClassType retType = requestType.isParameterized().getTypeArgs()[0]; @@ -797,6 +797,18 @@ return null; } + private String getEntityParameters(JMethod method) { + StringBuilder sb = new StringBuilder(); + sb.append("new Object[] {"); + for (JParameter param : method.getParameters()) { + if (mayContainEntityProxy(param.getType())) { + sb.append(param.getName()).append(","); + } + } + sb.append("}"); + return sb.toString(); + } + /** * This method is very similar to {@link * com.google.gwt.core.ext.typeinfo.JMethod.getReadableDeclaration()}. The @@ -838,11 +850,12 @@ JClassType classType = parameter.getType().isClassOrInterface(); JType paramType = parameter.getType(); - if (paramType.getQualifiedSourceName().equals(EntityProxyId.class.getName())) { + if (paramType.getQualifiedSourceName().equals( + EntityProxyId.class.getName())) { sb.append("factory.getWireFormat(" + parameter.getName() + ")"); continue; } - + if (classType != null && classType.isAssignableTo(entityProxyType)) { sb.append("((" + classType.getQualifiedBinaryName() + "Impl" + ")"); } @@ -877,16 +890,14 @@ return requestType.isParameterized().getTypeArgs()[0].isAssignableTo(typeOracle.findType(Character.class.getName())); } - private boolean isCollection(TypeOracle typeOracle, - JType requestType) { + private boolean isCollection(TypeOracle typeOracle, JType requestType) { return requestType.isParameterized() != null - && requestType.isParameterized().isAssignableTo( - typeOracle.findType(Collection.class.getName())); + && requestType.isParameterized().isAssignableTo( + typeOracle.findType(Collection.class.getName())); } private boolean isDateRequest(TypeOracle typeOracle, JClassType requestType) { - return requestType.isParameterized().getTypeArgs()[0].isAssignableTo(typeOracle.findType( - Date.class.getName())); + return requestType.isParameterized().getTypeArgs()[0].isAssignableTo(typeOracle.findType(Date.class.getName())); } private boolean isDoubleRequest(TypeOracle typeOracle, JClassType requestType) { @@ -911,12 +922,10 @@ private boolean isProxyCollectionRequest(TypeOracle typeOracle, JClassType requestType) { - return requestType.isAssignableTo(typeOracle.findType( - ProxyListRequest.class.getName())) - || requestType.isAssignableTo(typeOracle.findType( - ProxySetRequest.class.getName())); + return requestType.isAssignableTo(typeOracle.findType(ProxyListRequest.class.getName())) + || requestType.isAssignableTo(typeOracle.findType(ProxySetRequest.class.getName())); } - + private boolean isProxyRequest(TypeOracle typeOracle, JClassType requestType) { return requestType.isAssignableTo(typeOracle.findType(ProxyRequest.class.getName())); } @@ -974,9 +983,9 @@ propertyName = Introspector.decapitalize(methodName.substring(3)); propertyType = method.getReturnType(); if (propertyType.isArray() != null) { - logger.log(TreeLogger.ERROR, "Method " + methodName - + " on " + method.getEnclosingType().getQualifiedSourceName() - + " may not return a Java array."); + logger.log(TreeLogger.ERROR, "Method " + methodName + " on " + + method.getEnclosingType().getQualifiedSourceName() + + " may not return a Java array."); throw new UnableToCompleteException(); } } else if (methodName.startsWith("set")) { @@ -985,9 +994,9 @@ if (parameters.length > 0) { propertyType = parameters[parameters.length - 1].getType(); if (propertyType.isArray() != null) { - logger.log(TreeLogger.ERROR, "Method " + methodName - + " on " + method.getEnclosingType().getQualifiedSourceName() - + " may accept a Java array as a parameter."); + logger.log(TreeLogger.ERROR, "Method " + methodName + " on " + + method.getEnclosingType().getQualifiedSourceName() + + " may accept a Java array as a parameter."); throw new UnableToCompleteException(); } } @@ -1002,6 +1011,16 @@ return null; } + private boolean mayContainEntityProxy(JType paramType) { + JClassType paramClass = paramType.isClassOrInterface(); + if (paramClass == null) { + return false; + } + return entityProxyType.isAssignableFrom(paramClass) + || listType.isAssignableFrom(paramClass) + || setType.isAssignableFrom(paramClass); + } + /** * Prints a ListRequestImpl or ObjectRequestImpl class. */ @@ -1068,8 +1087,8 @@ throw new RuntimeException(e); } - List<EntityProperty> entityProperties = computeEntityPropertiesFromProxyType(publicProxyType, - logger); + List<EntityProperty> entityProperties = computeEntityPropertiesFromProxyType( + publicProxyType, logger); for (EntityProperty entityProperty : entityProperties) { sw.println(String.format("set.add(%s);", entityProperty.getName())); }
diff --git a/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java b/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java index 1b20eac..6c95f8a 100644 --- a/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java +++ b/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java
@@ -157,8 +157,8 @@ try { decodedBytes = Base64Utils.fromBase64(encoded); } catch (Exception e) { - throw new IllegalArgumentException( - "EntityKeyId was not Base64 encoded: " + encoded); + throw new IllegalArgumentException("EntityKeyId was not Base64 encoded: " + + encoded); } return new String(decodedBytes); } @@ -210,7 +210,7 @@ private Map<EntityKey, EntityData> afterDvsDataMap = new HashMap<EntityKey, EntityData>(); - @SuppressWarnings({"unchecked", "rawtypes"}) + @SuppressWarnings(value = {"unchecked", "rawtypes"}) public Collection<Property<?>> allProperties( Class<? extends EntityProxy> clazz) throws IllegalArgumentException { return getPropertiesFromRecordProxyType(clazz).values(); @@ -403,9 +403,10 @@ String encodedEntityId = isEntityReference(returnValue, proxyPropertyType); if (returnValue == null) { - return JSONObject.NULL; + return JSONObject.NULL; } else if (encodedEntityId != null) { - String keyRef = encodeRelated(proxyPropertyType, propertyName, propertyContext, returnValue); + String keyRef = encodeRelated(proxyPropertyType, propertyName, + propertyContext, returnValue); return keyRef; } else if (property instanceof CollectionProperty) { Class<?> colType = ((CollectionProperty) property).getType(); @@ -461,9 +462,11 @@ validateKeys(recordObject, propertiesInDomain.keySet()); // get entityInstance - Object entityInstance = getEntityInstance(writeOperation, entityType, - entityKey.decodedId(propertiesInProxy.get(ENTITY_ID_PROPERTY).getType()), propertiesInProxy.get( - ENTITY_ID_PROPERTY).getType()); + Object entityInstance = getEntityInstance( + writeOperation, + entityType, + entityKey.decodedId(propertiesInProxy.get(ENTITY_ID_PROPERTY).getType()), + propertiesInProxy.get(ENTITY_ID_PROPERTY).getType()); cachedEntityLookup.put(entityKey, entityInstance); @@ -607,18 +610,22 @@ return jsonArray; } - public JSONObject getJsonObject(Object entityElement, + public Object getJsonObject(Object entityElement, Class<? extends EntityProxy> entityKeyClass, RequestProperty propertyContext) throws JSONException, NoSuchMethodException, IllegalAccessException, InvocationTargetException { JSONObject jsonObject = new JSONObject(); - if (entityElement == null || !EntityProxy.class.isAssignableFrom(entityKeyClass)) { - return jsonObject; + if (entityElement == null + || !EntityProxy.class.isAssignableFrom(entityKeyClass)) { + // JSONObject.NULL isn't a JSONObject + return JSONObject.NULL; } - jsonObject.put(ENCODED_ID_PROPERTY, isEntityReference(entityElement, entityKeyClass)); - jsonObject.put(ENCODED_VERSION_PROPERTY, encodePropertyValueFromDataStore(entityElement, - ENTITY_VERSION_PROPERTY, ENTITY_VERSION_PROPERTY.getName(), propertyContext)); + jsonObject.put(ENCODED_ID_PROPERTY, isEntityReference(entityElement, + entityKeyClass)); + jsonObject.put(ENCODED_VERSION_PROPERTY, encodePropertyValueFromDataStore( + entityElement, ENTITY_VERSION_PROPERTY, + ENTITY_VERSION_PROPERTY.getName(), propertyContext)); for (Property<?> p : allProperties(entityKeyClass)) { if (requestedProperty(p, propertyContext)) { String propertyName = p.getName(); @@ -866,7 +873,8 @@ JSONObject sideEffects = getSideEffects(); - if ((result instanceof List<?>) != operation.isReturnTypeList()) { + if (result != null + && (result instanceof List<?>) != operation.isReturnTypeList()) { throw new IllegalArgumentException( String.format("Type mismatch, expected %s%s, but %s returns %s", operation.isReturnTypeList() ? "list of " : "", @@ -887,11 +895,13 @@ // HACK. if (involvedKeys.size() == 1) { returnType = involvedKeys.iterator().next().proxyType; + } else { + System.out.println("null find"); } } else { returnType = (Class<? extends EntityProxy>) operation.getReturnType(); } - JSONObject jsonObject = getJsonObject(result, returnType, propertyRefs); + Object jsonObject = getJsonObject(result, returnType, propertyRefs); envelop.put(RESULT_TOKEN, jsonObject); } envelop.put(SIDE_EFFECTS_TOKEN, sideEffects); @@ -1012,10 +1022,12 @@ if (!relatedObjects.containsKey(keyRef)) { // put temporary value to prevent infinite recursion relatedObjects.put(keyRef, null); - JSONObject jsonObject = getJsonObject(returnValue, propertyType, + Object jsonObject = getJsonObject(returnValue, propertyType, propertyContext); - // put real value - relatedObjects.put(keyRef, jsonObject); + if (jsonObject != JSONObject.NULL) { + // put real value + relatedObjects.put(keyRef, (JSONObject) jsonObject); + } } } @@ -1064,24 +1076,16 @@ // TODO: assert that the id is null for entityData.entityInstance } afterDvsDataMap.put(entityKey, entityData); + } else if (entityKey.isFuture) { + // The client-side DVS failed to include a CREATE operation. + throw new RuntimeException("Future object with no dvsData"); } else { - if (entityKey.isFuture) { - /* - * dummy create, i.e., an entity for which RequestFactory#create was - * called, but for which no values were set, so it is not listed in - * the dvs TODO(rjrjr) silly to work around this on the server. Fix - * the client. - */ - JSONObject dummyJson = new JSONObject(); - dummyJson.put(ENCODED_ID_PROPERTY, entityKey.encodedId); - afterDvsDataMap.put(entityKey, - getEntityDataForRecordWithSettersApplied(entityKey, dummyJson, - WriteOperation.CREATE)); - } else { - // Involved, but not in the deltaValueStore -- param ref to an - // unedited existing object - SerializedEntity serializedEntity = beforeDataMap.get(entityKey); - assert serializedEntity.entityInstance != null; + /* + * Involved, but not in the deltaValueStore -- param ref to an unedited, + * existing object. + */ + SerializedEntity serializedEntity = beforeDataMap.get(entityKey); + if (serializedEntity.entityInstance != null) { afterDvsDataMap.put(entityKey, new EntityData( serializedEntity.entityInstance, null)); } @@ -1206,7 +1210,8 @@ Object entityInstance = entityData.entityInstance; assert entityInstance != null; JSONObject returnObject = new JSONObject(); - returnObject.put(ENCODED_FUTUREID_PROPERTY, originalEntityKey.encodedId + ""); + returnObject.put(ENCODED_FUTUREID_PROPERTY, originalEntityKey.encodedId + + ""); // violations have already been taken care of. Object newId = getRawPropertyValueFromDatastore(entityInstance, ENTITY_ID_PROPERTY, propertyRefs); @@ -1217,10 +1222,12 @@ } newId = encodeId(newId); - returnObject.put(ENCODED_ID_PROPERTY, getSchemaAndId(originalEntityKey.proxyType, newId)); - returnObject.put(ENCODED_VERSION_PROPERTY, encodePropertyValueFromDataStore( - entityInstance, ENTITY_VERSION_PROPERTY, - ENTITY_VERSION_PROPERTY.getName(), propertyRefs)); + returnObject.put(ENCODED_ID_PROPERTY, getSchemaAndId( + originalEntityKey.proxyType, newId)); + returnObject.put(ENCODED_VERSION_PROPERTY, + encodePropertyValueFromDataStore(entityInstance, + ENTITY_VERSION_PROPERTY, ENTITY_VERSION_PROPERTY.getName(), + propertyRefs)); return returnObject; } @@ -1347,14 +1354,14 @@ entityData); if (writeOperation == WriteOperation.DELETE) { JSONObject deleteRecord = new JSONObject(); - deleteRecord.put(ENCODED_ID_PROPERTY, getSchemaAndId(entityKey.proxyType, - entityKey.encodedId)); + deleteRecord.put(ENCODED_ID_PROPERTY, getSchemaAndId( + entityKey.proxyType, entityKey.encodedId)); deleteArray.put(deleteRecord); } if (writeOperation == WriteOperation.UPDATE) { JSONObject updateRecord = new JSONObject(); - updateRecord.put(ENCODED_ID_PROPERTY, getSchemaAndId(entityKey.proxyType, - entityKey.encodedId)); + updateRecord.put(ENCODED_ID_PROPERTY, getSchemaAndId( + entityKey.proxyType, entityKey.encodedId)); updateArray.put(updateRecord); } } @@ -1384,10 +1391,11 @@ returnObject.put(VIOLATIONS_TOKEN, entityData.violations); if (entityKey.isFuture) { returnObject.put(ENCODED_FUTUREID_PROPERTY, entityKey.encodedId); - returnObject.put(ENCODED_ID_PROPERTY, getSchemaAndId(entityKey.proxyType, null)); + returnObject.put(ENCODED_ID_PROPERTY, getSchemaAndId( + entityKey.proxyType, null)); } else { - returnObject.put(ENCODED_ID_PROPERTY, getSchemaAndId(entityKey.proxyType, - entityKey.encodedId)); + returnObject.put(ENCODED_ID_PROPERTY, getSchemaAndId( + entityKey.proxyType, entityKey.encodedId)); } violations.put(returnObject); } @@ -1453,7 +1461,7 @@ Object propertyValue; String encodedEntityId = isEntityReference(returnValue, p.getType()); if (returnValue == null) { - propertyValue = JSONObject.NULL; + propertyValue = JSONObject.NULL; } else if (encodedEntityId != null) { propertyValue = encodedEntityId + "@NO@"
diff --git a/user/src/com/google/gwt/requestfactory/shared/Receiver.java b/user/src/com/google/gwt/requestfactory/shared/Receiver.java index 7d89ca4..8ba90ae 100644 --- a/user/src/com/google/gwt/requestfactory/shared/Receiver.java +++ b/user/src/com/google/gwt/requestfactory/shared/Receiver.java
@@ -33,13 +33,7 @@ * RuntimeException with the provided error message. */ public void onFailure(ServerFailure error) { - String exceptionType = error.getExceptionType(); - String message = error.getMessage(); - throw new RuntimeException(exceptionType - + ((exceptionType.length() != 0 && message.length() != 0) ? ": " : "") - + error.getMessage() - + ((exceptionType.length() != 0 || message.length() != 0) ? ": " : "") - + error.getStackTraceString()); + throw new RuntimeException(error.getMessage()); } /**
diff --git a/user/src/com/google/gwt/requestfactory/shared/impl/RequestData.java b/user/src/com/google/gwt/requestfactory/shared/impl/RequestData.java index 46f3d43..f369ce8 100644 --- a/user/src/com/google/gwt/requestfactory/shared/impl/RequestData.java +++ b/user/src/com/google/gwt/requestfactory/shared/impl/RequestData.java
@@ -73,14 +73,25 @@ private final String operation; private final Object[] parameters; - private final Set<String> propertyRefs; + // Could be EntityProxy or Collection instances + private final Object[] proxies; - public RequestData(String operation, Object[] parameters, + public RequestData(String operation, Object[] parameters, Object[] proxies, Set<String> propertyRefs) { this.operation = operation; this.parameters = parameters; this.propertyRefs = propertyRefs; + this.proxies = proxies; + } + + /** + * Returns any EntityProxies being passed as method arguments. Prevents the + * "dummy object" case. + * @return EntityProxies or Collections + */ + public Object[] getProxyParameters() { + return proxies; } /** @@ -119,11 +130,11 @@ return "null"; } - if (value instanceof Iterable) { + if (value instanceof Iterable<?>) { StringBuffer toReturn = new StringBuffer(); toReturn.append('['); boolean first = true; - for (Object val : ((Iterable) value)) { + for (Object val : ((Iterable<?>) value)) { if (!first) { toReturn.append(','); } else { @@ -147,8 +158,8 @@ return asJsonString(((Date) value).getTime()); } - if (value instanceof Enum) { - return asJsonString(((Enum) value).ordinal()); + if (value instanceof Enum<?>) { + return asJsonString(((Enum<?>) value).ordinal()); } return value.toString();
diff --git a/user/test/com/google/gwt/requestfactory/client/FindServiceTest.java b/user/test/com/google/gwt/requestfactory/client/FindServiceTest.java index 0a59821..adb0efe 100644 --- a/user/test/com/google/gwt/requestfactory/client/FindServiceTest.java +++ b/user/test/com/google/gwt/requestfactory/client/FindServiceTest.java
@@ -15,10 +15,15 @@ */ package com.google.gwt.requestfactory.client; +import com.google.gwt.core.client.GWT; +import com.google.gwt.event.shared.SimpleEventBus; import com.google.gwt.requestfactory.shared.EntityProxyId; +import com.google.gwt.requestfactory.shared.ProxyRequest; import com.google.gwt.requestfactory.shared.Receiver; +import com.google.gwt.requestfactory.shared.Request; import com.google.gwt.requestfactory.shared.SimpleBarProxy; import com.google.gwt.requestfactory.shared.SimpleFooProxy; +import com.google.gwt.requestfactory.shared.SimpleRequestFactory; /** * Tests for {@link com.google.gwt.requestfactory.shared.RequestFactory}. @@ -28,14 +33,46 @@ * DO NOT USE finishTest(). Instead, call finishTestAndReset(); */ + private static final int TEST_DELAY = 5000; + @Override public String getModuleName() { return "com.google.gwt.requestfactory.RequestFactorySuite"; } + public void testFetchDeletedEntity() { + delayTestFinish(TEST_DELAY); + SimpleBarProxy willDelete = req.create(SimpleBarProxy.class); + req.simpleBarRequest().persistAndReturnSelf(willDelete).fire( + new Receiver<SimpleBarProxy>() { + @Override + public void onSuccess(SimpleBarProxy response) { + final EntityProxyId<SimpleBarProxy> id = response.stableId(); + + // Make the entity behave as though it's been deleted + Request<Void> persist = req.simpleBarRequest().persist(response); + persist.edit(response).setFindFails(true); + persist.fire(new Receiver<Void>() { + + @Override + public void onSuccess(Void response) { + // Now try fetching the deleted instance + req.find(id).fire(new Receiver<SimpleBarProxy>() { + @Override + public void onSuccess(SimpleBarProxy response) { + assertNull(response); + finishTestAndReset(); + } + }); + } + }); + } + }); + } + public void testFetchEntityWithLongId() { final boolean relationsAbsent = false; - delayTestFinish(5000); + delayTestFinish(TEST_DELAY); req.simpleFooRequest().findSimpleFooById(999L).fire( new Receiver<SimpleFooProxy>() { @Override @@ -58,7 +95,7 @@ public void testFetchEntityWithRelation() { final boolean relationsPresent = true; - delayTestFinish(5000); + delayTestFinish(TEST_DELAY); req.simpleFooRequest().findSimpleFooById(999L).with("barField").fire( new Receiver<SimpleFooProxy>() { @Override @@ -79,9 +116,9 @@ } }); } - + public void testFetchEntityWithStringId() { - delayTestFinish(5000); + delayTestFinish(TEST_DELAY); req.simpleBarRequest().findSimpleBarById("999L").fire( new Receiver<SimpleBarProxy>() { @Override @@ -100,6 +137,36 @@ }); } + /** + * Demonstrates behavior when fetching an unpersisted id. The setup is + * analagous to saving a future id into a cookie and then trying to fetch it + * later. + */ + public void testFetchUnpersistedFutureId() { + String historyToken; + + // Here's the factory from the "previous invocation" of the client + { + SimpleRequestFactory oldFactory = GWT.create(SimpleRequestFactory.class); + oldFactory.initialize(new SimpleEventBus()); + EntityProxyId<SimpleBarProxy> id = oldFactory.create(SimpleBarProxy.class).stableId(); + historyToken = oldFactory.getHistoryToken(id); + } + + EntityProxyId<SimpleBarProxy> id = req.getProxyId(historyToken); + assertNotNull(id); + ProxyRequest<SimpleBarProxy> find = req.find(id); + try { + find.fire(new Receiver<SimpleBarProxy>() { + @Override + public void onSuccess(SimpleBarProxy response) { + fail("Request should never have been made"); + } + }); + } catch (IllegalArgumentException expected) { + } + } + private void checkReturnedProxy(SimpleFooProxy response, boolean checkForRelations) { assertEquals(42, (int) response.getIntId()); @@ -114,4 +181,3 @@ } } } -
diff --git a/user/test/com/google/gwt/requestfactory/client/RequestFactoryStringTest.java b/user/test/com/google/gwt/requestfactory/client/RequestFactoryStringTest.java index 86361f4..d31efff 100644 --- a/user/test/com/google/gwt/requestfactory/client/RequestFactoryStringTest.java +++ b/user/test/com/google/gwt/requestfactory/client/RequestFactoryStringTest.java
@@ -126,8 +126,9 @@ assertEquals(futureId, foo.getId()); assertTrue(((ProxyImpl) foo).unpersisted()); - assertEquals(1, handler.acquireEventCount); + assertEquals(0, handler.acquireEventCount); assertEquals(1, handler.createEventCount); + assertEquals(1, handler.updateEventCount); assertEquals(2, handler.totalEventCount); checkStableIdEquals(foo, returned);
diff --git a/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java b/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java index d31f951..8319d2d 100644 --- a/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java +++ b/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java
@@ -27,8 +27,8 @@ import com.google.gwt.requestfactory.shared.SimpleFooProxy; import com.google.gwt.requestfactory.shared.Violation; -import java.util.Arrays; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -47,8 +47,8 @@ private SimpleFooProxy mutableFoo; private Request<SimpleFooProxy> persistRequest; private String expectedException; - - public FooReciever(SimpleFooProxy mutableFoo, + + public FooReciever(SimpleFooProxy mutableFoo, Request<SimpleFooProxy> persistRequest, String exception) { this.mutableFoo = mutableFoo; this.persistRequest = persistRequest; @@ -60,8 +60,7 @@ assertEquals(expectedException, error.getExceptionType()); if (expectedException.length() > 0) { assertFalse(error.getStackTraceString().length() == 0); - assertEquals("THIS EXCEPTION IS EXPECTED BY A TEST", - error.getMessage()); + assertEquals("THIS EXCEPTION IS EXPECTED BY A TEST", error.getMessage()); } else { assertEquals("", error.getStackTraceString()); assertEquals("Server Error: THIS EXCEPTION IS EXPECTED BY A TEST", @@ -91,6 +90,14 @@ } } + class NullReceiver extends Receiver<Object> { + @Override + public void onSuccess(Object response) { + assertNull(response); + finishTestAndReset(); + } + } + private class FailFixAndRefire<T> extends Receiver<T> { private final SimpleFooProxy proxy; @@ -155,14 +162,14 @@ } } - public <T extends EntityProxy> void assertContains(Collection<T> col, - T value) { + public <T extends EntityProxy> void assertContains(Collection<T> col, T value) { for (T x : col) { if (x.stableId().equals(value.stableId())) { return; } } - assertTrue(("Value " + value + " not found in collection ") + col.toString(), false); + assertTrue(("Value " + value + " not found in collection ") + + col.toString(), false); } public <T extends EntityProxy> void assertNotContains(Collection<T> col, @@ -185,13 +192,12 @@ assertEquals(SimpleFooProxy.class, foo.stableId().getProxyClass()); } - public void testDummyCreate() { + public void testDummyCreate() { delayTestFinish(5000); - final SimpleFooEventHandler<SimpleFooProxy> handler = - new SimpleFooEventHandler<SimpleFooProxy>(); - EntityProxyChange.registerForProxyType( - req.getEventBus(), SimpleFooProxy.class, handler); + final SimpleFooEventHandler<SimpleFooProxy> handler = new SimpleFooEventHandler<SimpleFooProxy>(); + EntityProxyChange.registerForProxyType(req.getEventBus(), + SimpleFooProxy.class, handler); final SimpleFooProxy foo = req.create(SimpleFooProxy.class); Object futureId = foo.getId(); @@ -207,8 +213,9 @@ assertEquals(futureId, foo.getId()); assertTrue(((ProxyImpl) foo).unpersisted()); - assertEquals(1, handler.acquireEventCount); + assertEquals(0, handler.acquireEventCount); assertEquals(1, handler.createEventCount); + assertEquals(1, handler.updateEventCount); assertEquals(2, handler.totalEventCount); checkStableIdEquals(foo, returned); @@ -240,6 +247,30 @@ }); } + public void testDummyCreateList() { + delayTestFinish(500000); + + final SimpleBarProxy bar = req.create(SimpleBarProxy.class); + Object futureId = bar.getId(); + assertEquals(futureId, bar.getId()); + assertTrue(((ProxyImpl) bar).unpersisted()); + Request<SimpleBarProxy> fooReq = req.simpleBarRequest().returnFirst( + Collections.singletonList(bar)); + fooReq.fire(new Receiver<SimpleBarProxy>() { + + @Override + public void onSuccess(final SimpleBarProxy returned) { + Object futureId = bar.getId(); + assertEquals(futureId, bar.getId()); + assertTrue(((ProxyImpl) bar).unpersisted()); + assertFalse(((ProxyImpl) returned).unpersisted()); + + checkStableIdEquals(bar, returned); + finishTestAndReset(); + } + }); + } + public void testFetchEntity() { delayTestFinish(5000); req.simpleFooRequest().findSimpleFooById(999L).fire( @@ -276,19 +307,18 @@ public void testFetchList() { delayTestFinish(5000); - req.simpleFooRequest().findAll().fire( - new Receiver<List<SimpleFooProxy>>() { - @Override - public void onSuccess(List<SimpleFooProxy> responseList) { - SimpleFooProxy response = responseList.get(0); - assertEquals(42, (int) response.getIntId()); - assertEquals("GWT", response.getUserName()); - assertEquals(8L, (long) response.getLongField()); - assertEquals(com.google.gwt.requestfactory.shared.SimpleEnum.FOO, - response.getEnumField()); - finishTestAndReset(); - } - }); + req.simpleFooRequest().findAll().fire(new Receiver<List<SimpleFooProxy>>() { + @Override + public void onSuccess(List<SimpleFooProxy> responseList) { + SimpleFooProxy response = responseList.get(0); + assertEquals(42, (int) response.getIntId()); + assertEquals("GWT", response.getUserName()); + assertEquals(8L, (long) response.getLongField()); + assertEquals(com.google.gwt.requestfactory.shared.SimpleEnum.FOO, + response.getEnumField()); + finishTestAndReset(); + } + }); } public void testFetchSet() { @@ -369,17 +399,43 @@ // Check that the persisted object can be found with future token assertEquals(futureId, req.getProxyId(futureToken)); assertEquals(futureId, req.getProxyId(persistedToken)); - assertEquals(futureId.getProxyClass(), req.getProxyClass(persistedToken)); + assertEquals(futureId.getProxyClass(), + req.getProxyClass(persistedToken)); assertEquals(persistedId, req.getProxyId(futureToken)); assertEquals(persistedId, req.getProxyId(persistedToken)); - assertEquals(persistedId.getProxyClass(), req.getProxyClass(futureToken)); + assertEquals(persistedId.getProxyClass(), + req.getProxyClass(futureToken)); finishTestAndReset(); } }); } + /** + * Ensures that a service method can respond with a null value. + */ + public void testNullListResult() { + delayTestFinish(5000); + req.simpleFooRequest().returnNullList().fire(new NullReceiver()); + } + + /** + * Ensures that a service method can respond with a null value. + */ + public void testNullEntityProxyResult() { + delayTestFinish(5000); + req.simpleFooRequest().returnNullSimpleFoo().fire(new NullReceiver()); + } + + /** + * Ensures that a service method can respond with a null value. + */ + public void testNullStringResult() { + delayTestFinish(5000); + req.simpleFooRequest().returnNullString().fire(new NullReceiver()); + } + /* * tests that (a) any method can have a side effect that is handled correctly. * (b) instance methods are handled correctly and (c) a request cannot be @@ -388,10 +444,9 @@ public void testMethodWithSideEffects() { delayTestFinish(5000); - final SimpleFooEventHandler<SimpleFooProxy> handler = - new SimpleFooEventHandler<SimpleFooProxy>(); - EntityProxyChange.registerForProxyType( - req.getEventBus(), SimpleFooProxy.class, handler); + final SimpleFooEventHandler<SimpleFooProxy> handler = new SimpleFooEventHandler<SimpleFooProxy>(); + EntityProxyChange.registerForProxyType(req.getEventBus(), + SimpleFooProxy.class, handler); req.simpleFooRequest().findSimpleFooById(999L).fire( new Receiver<SimpleFooProxy>() { @@ -471,7 +526,7 @@ } }); } - + /* * Find Entity Create Entity2 Relate Entity2 to Entity Persist Entity */ @@ -495,8 +550,7 @@ public void onSuccess(SimpleFooProxy response) { // Found the foo, edit it - Request<Void> fooReq = req.simpleFooRequest().persist( - response); + Request<Void> fooReq = req.simpleFooRequest().persist(response); response = fooReq.edit(response); response.setBarField(persistedBar); fooReq.fire(new Receiver<Void>() { @@ -562,6 +616,29 @@ }); } + /** + * Ensure that a relationship can be set up between two newly-created objects. + */ + public void testPersistFutureToFuture() { + delayTestFinish(500000); + SimpleFooProxy newFoo = req.create(SimpleFooProxy.class); + final SimpleBarProxy newBar = req.create(SimpleBarProxy.class); + + Request<SimpleFooProxy> fooReq = req.simpleFooRequest().persistAndReturnSelf( + newFoo).with("barField"); + newFoo = fooReq.edit(newFoo); + newFoo.setBarField(newBar); + + fooReq.fire(new Receiver<SimpleFooProxy>() { + @Override + public void onSuccess(SimpleFooProxy response) { + assertNotNull(response.getBarField()); + assertEquals(newBar.stableId(), response.getBarField().stableId()); + finishTestAndReset(); + } + }); + } + /* * Create Entity, Persist Entity Create Entity2, Perist Entity2 relate Entity2 * to Entity Persist @@ -624,30 +701,28 @@ new Receiver<SimpleBarProxy>() { @Override public void onSuccess(final SimpleBarProxy barProxy) { - req.simpleFooRequest().findSimpleFooById(999L).with("oneToManyField").fire( - new Receiver<SimpleFooProxy>() { - @Override - public void onSuccess(SimpleFooProxy fooProxy) { - Request<SimpleFooProxy> updReq = - req.simpleFooRequest().persistAndReturnSelf( - fooProxy).with("oneToManyField"); - fooProxy = updReq.edit(fooProxy); + req.simpleFooRequest().findSimpleFooById(999L).with( + "oneToManyField").fire(new Receiver<SimpleFooProxy>() { + @Override + public void onSuccess(SimpleFooProxy fooProxy) { + Request<SimpleFooProxy> updReq = req.simpleFooRequest().persistAndReturnSelf( + fooProxy).with("oneToManyField"); + fooProxy = updReq.edit(fooProxy); - List<SimpleBarProxy> barProxyList = - fooProxy.getOneToManyField(); - final int listCount = barProxyList.size(); - barProxyList.add(barProxy); - updReq.fire(new Receiver<SimpleFooProxy>() { - @Override - public void onSuccess(SimpleFooProxy response) { - assertEquals(response.getOneToManyField().size(), - listCount + 1); - assertContains(response.getOneToManyField(), barProxy); - finishTestAndReset(); - } - }); + List<SimpleBarProxy> barProxyList = fooProxy.getOneToManyField(); + final int listCount = barProxyList.size(); + barProxyList.add(barProxy); + updReq.fire(new Receiver<SimpleFooProxy>() { + @Override + public void onSuccess(SimpleFooProxy response) { + assertEquals(response.getOneToManyField().size(), + listCount + 1); + assertContains(response.getOneToManyField(), barProxy); + finishTestAndReset(); } }); + } + }); } }); } @@ -678,8 +753,8 @@ delayTestFinish(5000); SimpleFooProxy rayFoo = req.create(SimpleFooProxy.class); - final Request<SimpleFooProxy> persistRay = req.simpleFooRequest() - .persistAndReturnSelf(rayFoo); + final Request<SimpleFooProxy> persistRay = req.simpleFooRequest().persistAndReturnSelf( + rayFoo); rayFoo = persistRay.edit(rayFoo); rayFoo.setUserName("Ray"); @@ -687,8 +762,8 @@ @Override public void onSuccess(final SimpleFooProxy persistedRay) { SimpleBarProxy amitBar = req.create(SimpleBarProxy.class); - final Request<SimpleBarProxy> persistAmit = req.simpleBarRequest() - .persistAndReturnSelf(amitBar); + final Request<SimpleBarProxy> persistAmit = req.simpleBarRequest().persistAndReturnSelf( + amitBar); amitBar = persistAmit.edit(amitBar); amitBar.setUserName("Amit"); @@ -696,9 +771,8 @@ @Override public void onSuccess(SimpleBarProxy persistedAmit) { - final Request<SimpleFooProxy> persistRelationship = req - .simpleFooRequest().persistAndReturnSelf(persistedRay) - .with("barField"); + final Request<SimpleFooProxy> persistRelationship = req.simpleFooRequest().persistAndReturnSelf( + persistedRay).with("barField"); SimpleFooProxy newRec = persistRelationship.edit(persistedRay); newRec.setBarField(persistedAmit); @@ -716,20 +790,19 @@ } /* - * TODO: all these tests should check the final values. It will be easy when - * we have better persistence than the singleton pattern. - */ + * TODO: all these tests should check the final values. It will be easy when + * we have better persistence than the singleton pattern. + */ public void testPersistSelfOneToManyExistingEntityExistingRelation() { delayTestFinish(5000); - req.simpleFooRequest().findSimpleFooById(999L).with("selfOneToManyField") - .fire(new Receiver<SimpleFooProxy>() { + req.simpleFooRequest().findSimpleFooById(999L).with("selfOneToManyField").fire( + new Receiver<SimpleFooProxy>() { @Override public void onSuccess(SimpleFooProxy fooProxy) { - Request<SimpleFooProxy> updReq = - req.simpleFooRequest().persistAndReturnSelf(fooProxy).with( - "selfOneToManyField"); + Request<SimpleFooProxy> updReq = req.simpleFooRequest().persistAndReturnSelf( + fooProxy).with("selfOneToManyField"); fooProxy = updReq.edit(fooProxy); List<SimpleFooProxy> fooProxyList = fooProxy.getSelfOneToManyField(); final int listCount = fooProxyList.size(); @@ -749,12 +822,12 @@ public void testPersistValueList() { delayTestFinish(5000); - req.simpleFooRequest().findSimpleFooById(999L) - .fire(new Receiver<SimpleFooProxy>() { + req.simpleFooRequest().findSimpleFooById(999L).fire( + new Receiver<SimpleFooProxy>() { @Override public void onSuccess(SimpleFooProxy fooProxy) { - Request<SimpleFooProxy> updReq = - req.simpleFooRequest().persistAndReturnSelf(fooProxy); + Request<SimpleFooProxy> updReq = req.simpleFooRequest().persistAndReturnSelf( + fooProxy); fooProxy = updReq.edit(fooProxy); fooProxy.getNumberListField().add(100); updReq.fire(new Receiver<SimpleFooProxy>() { @@ -769,17 +842,17 @@ } /* - * TODO: all these tests should check the final values. It will be easy when - * we have better persistence than the singleton pattern. - */ + * TODO: all these tests should check the final values. It will be easy when + * we have better persistence than the singleton pattern. + */ public void testPersistValueListNull() { delayTestFinish(500000); - req.simpleFooRequest().findSimpleFooById(999L) - .fire(new Receiver<SimpleFooProxy>() { + req.simpleFooRequest().findSimpleFooById(999L).fire( + new Receiver<SimpleFooProxy>() { @Override public void onSuccess(SimpleFooProxy fooProxy) { - Request<SimpleFooProxy> updReq = - req.simpleFooRequest().persistAndReturnSelf(fooProxy); + Request<SimpleFooProxy> updReq = req.simpleFooRequest().persistAndReturnSelf( + fooProxy); fooProxy = updReq.edit(fooProxy); fooProxy.setNumberListField(null); @@ -787,7 +860,7 @@ @Override public void onSuccess(SimpleFooProxy response) { List<Integer> list = response.getNumberListField(); - assertNull(list); + assertNull(list); finishTestAndReset(); } }); @@ -796,17 +869,17 @@ } /* - * TODO: all these tests should check the final values. It will be easy when - * we have better persistence than the singleton pattern. - */ + * TODO: all these tests should check the final values. It will be easy when + * we have better persistence than the singleton pattern. + */ public void testPersistValueListRemove() { delayTestFinish(5000); - req.simpleFooRequest().findSimpleFooById(999L) - .fire(new Receiver<SimpleFooProxy>() { + req.simpleFooRequest().findSimpleFooById(999L).fire( + new Receiver<SimpleFooProxy>() { @Override public void onSuccess(SimpleFooProxy fooProxy) { - Request<SimpleFooProxy> updReq = - req.simpleFooRequest().persistAndReturnSelf(fooProxy); + Request<SimpleFooProxy> updReq = req.simpleFooRequest().persistAndReturnSelf( + fooProxy); fooProxy = updReq.edit(fooProxy); final int oldValue = fooProxy.getNumberListField().remove(0); updReq.fire(new Receiver<SimpleFooProxy>() { @@ -821,17 +894,17 @@ } /* - * TODO: all these tests should check the final values. It will be easy when - * we have better persistence than the singleton pattern. - */ + * TODO: all these tests should check the final values. It will be easy when + * we have better persistence than the singleton pattern. + */ public void testPersistValueListReplace() { delayTestFinish(5000); - req.simpleFooRequest().findSimpleFooById(999L) - .fire(new Receiver<SimpleFooProxy>() { + req.simpleFooRequest().findSimpleFooById(999L).fire( + new Receiver<SimpleFooProxy>() { @Override public void onSuccess(SimpleFooProxy fooProxy) { - Request<SimpleFooProxy> updReq = - req.simpleFooRequest().persistAndReturnSelf(fooProxy); + Request<SimpleFooProxy> updReq = req.simpleFooRequest().persistAndReturnSelf( + fooProxy); fooProxy = updReq.edit(fooProxy); final ArrayList<Integer> al = new ArrayList<Integer>(); al.add(5); @@ -851,19 +924,19 @@ } }); } - + /* - * TODO: all these tests should check the final values. It will be easy when - * we have better persistence than the singleton pattern. - */ + * TODO: all these tests should check the final values. It will be easy when + * we have better persistence than the singleton pattern. + */ public void testPersistValueListReverse() { delayTestFinish(5000); - req.simpleFooRequest().findSimpleFooById(999L) - .fire(new Receiver<SimpleFooProxy>() { + req.simpleFooRequest().findSimpleFooById(999L).fire( + new Receiver<SimpleFooProxy>() { @Override public void onSuccess(SimpleFooProxy fooProxy) { - Request<SimpleFooProxy> updReq = - req.simpleFooRequest().persistAndReturnSelf(fooProxy); + Request<SimpleFooProxy> updReq = req.simpleFooRequest().persistAndReturnSelf( + fooProxy); fooProxy = updReq.edit(fooProxy); final ArrayList<Integer> al = new ArrayList<Integer>(); List<Integer> listField = fooProxy.getNumberListField(); @@ -882,17 +955,17 @@ } /* - * TODO: all these tests should check the final values. It will be easy when - * we have better persistence than the singleton pattern. - */ + * TODO: all these tests should check the final values. It will be easy when + * we have better persistence than the singleton pattern. + */ public void testPersistValueListSetIndex() { delayTestFinish(5000); - req.simpleFooRequest().findSimpleFooById(999L) - .fire(new Receiver<SimpleFooProxy>() { + req.simpleFooRequest().findSimpleFooById(999L).fire( + new Receiver<SimpleFooProxy>() { @Override public void onSuccess(SimpleFooProxy fooProxy) { - Request<SimpleFooProxy> updReq = - req.simpleFooRequest().persistAndReturnSelf(fooProxy); + Request<SimpleFooProxy> updReq = req.simpleFooRequest().persistAndReturnSelf( + fooProxy); fooProxy = updReq.edit(fooProxy); fooProxy.getNumberListField().set(0, 10); updReq.fire(new Receiver<SimpleFooProxy>() { @@ -907,9 +980,9 @@ } /* - * TODO: all these tests should check the final values. It will be easy when - * we have better persistence than the singleton pattern. - */ + * TODO: all these tests should check the final values. It will be easy when + * we have better persistence than the singleton pattern. + */ public void testPersistValueSetAddNew() { delayTestFinish(5000); SimpleBarProxy newBar = req.create(SimpleBarProxy.class); @@ -918,39 +991,36 @@ new Receiver<SimpleBarProxy>() { @Override public void onSuccess(final SimpleBarProxy barProxy) { - req.simpleFooRequest().findSimpleFooById(999L).with("oneToManySetField").fire( - new Receiver<SimpleFooProxy>() { - @Override - public void onSuccess(SimpleFooProxy fooProxy) { - Request<SimpleFooProxy> updReq = - req.simpleFooRequest().persistAndReturnSelf( - fooProxy).with("oneToManySetField"); - fooProxy = updReq.edit(fooProxy); + req.simpleFooRequest().findSimpleFooById(999L).with( + "oneToManySetField").fire(new Receiver<SimpleFooProxy>() { + @Override + public void onSuccess(SimpleFooProxy fooProxy) { + Request<SimpleFooProxy> updReq = req.simpleFooRequest().persistAndReturnSelf( + fooProxy).with("oneToManySetField"); + fooProxy = updReq.edit(fooProxy); - Set<SimpleBarProxy> setField = - fooProxy.getOneToManySetField(); - final int listCount = setField.size(); - setField.add(barProxy); - updReq.fire(new Receiver<SimpleFooProxy>() { - @Override - public void onSuccess(SimpleFooProxy response) { - assertEquals(listCount + 1, - response.getOneToManySetField().size()); - assertContains(response.getOneToManySetField(), - barProxy); - finishTestAndReset(); - } - }); + Set<SimpleBarProxy> setField = fooProxy.getOneToManySetField(); + final int listCount = setField.size(); + setField.add(barProxy); + updReq.fire(new Receiver<SimpleFooProxy>() { + @Override + public void onSuccess(SimpleFooProxy response) { + assertEquals(listCount + 1, + response.getOneToManySetField().size()); + assertContains(response.getOneToManySetField(), barProxy); + finishTestAndReset(); } }); + } + }); } }); } /* - * TODO: all these tests should check the final values. It will be easy when - * we have better persistence than the singleton pattern. - */ + * TODO: all these tests should check the final values. It will be easy when + * we have better persistence than the singleton pattern. + */ public void testPersistValueSetAlreadyExists() { delayTestFinish(5000); @@ -958,40 +1028,37 @@ new Receiver<SimpleBarProxy>() { @Override public void onSuccess(final SimpleBarProxy barProxy) { - req.simpleFooRequest().findSimpleFooById(999L).with("oneToManySetField").fire( - new Receiver<SimpleFooProxy>() { - @Override - public void onSuccess(SimpleFooProxy fooProxy) { - Request<SimpleFooProxy> updReq = - req.simpleFooRequest().persistAndReturnSelf( - fooProxy).with("oneToManySetField"); - fooProxy = updReq.edit(fooProxy); + req.simpleFooRequest().findSimpleFooById(999L).with( + "oneToManySetField").fire(new Receiver<SimpleFooProxy>() { + @Override + public void onSuccess(SimpleFooProxy fooProxy) { + Request<SimpleFooProxy> updReq = req.simpleFooRequest().persistAndReturnSelf( + fooProxy).with("oneToManySetField"); + fooProxy = updReq.edit(fooProxy); - Set<SimpleBarProxy> setField = - fooProxy.getOneToManySetField(); - final int listCount = setField.size(); - assertContains(setField, barProxy); - setField.add(barProxy); - updReq.fire(new Receiver<SimpleFooProxy>() { - @Override - public void onSuccess(SimpleFooProxy response) { - assertEquals(response.getOneToManySetField().size(), - listCount); - assertContains(response.getOneToManySetField(), - barProxy); - finishTestAndReset(); - } - }); + Set<SimpleBarProxy> setField = fooProxy.getOneToManySetField(); + final int listCount = setField.size(); + assertContains(setField, barProxy); + setField.add(barProxy); + updReq.fire(new Receiver<SimpleFooProxy>() { + @Override + public void onSuccess(SimpleFooProxy response) { + assertEquals(response.getOneToManySetField().size(), + listCount); + assertContains(response.getOneToManySetField(), barProxy); + finishTestAndReset(); } }); + } + }); } }); } /* - * TODO: all these tests should check the final values. It will be easy when - * we have better persistence than the singleton pattern. - */ + * TODO: all these tests should check the final values. It will be easy when + * we have better persistence than the singleton pattern. + */ public void testPersistValueSetRemove() { delayTestFinish(5000); @@ -999,33 +1066,30 @@ new Receiver<SimpleBarProxy>() { @Override public void onSuccess(final SimpleBarProxy barProxy) { - req.simpleFooRequest().findSimpleFooById(999L).with("oneToManySetField").fire( - new Receiver<SimpleFooProxy>() { - @Override - public void onSuccess(SimpleFooProxy fooProxy) { - Request<SimpleFooProxy> updReq = - req.simpleFooRequest().persistAndReturnSelf( - fooProxy).with("oneToManySetField"); - fooProxy = updReq.edit(fooProxy); + req.simpleFooRequest().findSimpleFooById(999L).with( + "oneToManySetField").fire(new Receiver<SimpleFooProxy>() { + @Override + public void onSuccess(SimpleFooProxy fooProxy) { + Request<SimpleFooProxy> updReq = req.simpleFooRequest().persistAndReturnSelf( + fooProxy).with("oneToManySetField"); + fooProxy = updReq.edit(fooProxy); - Set<SimpleBarProxy> setField = - fooProxy.getOneToManySetField(); - final int listCount = setField.size(); - assertContains(setField, barProxy); - setField.remove(barProxy); - assertNotContains(setField, barProxy); - updReq.fire(new Receiver<SimpleFooProxy>() { - @Override - public void onSuccess(SimpleFooProxy response) { - assertEquals(listCount - 1, - response.getOneToManySetField().size()); - assertNotContains(response.getOneToManySetField(), - barProxy); - finishTestAndReset(); - } - }); + Set<SimpleBarProxy> setField = fooProxy.getOneToManySetField(); + final int listCount = setField.size(); + assertContains(setField, barProxy); + setField.remove(barProxy); + assertNotContains(setField, barProxy); + updReq.fire(new Receiver<SimpleFooProxy>() { + @Override + public void onSuccess(SimpleFooProxy response) { + assertEquals(listCount - 1, + response.getOneToManySetField().size()); + assertNotContains(response.getOneToManySetField(), barProxy); + finishTestAndReset(); } }); + } + }); } }); } @@ -1047,19 +1111,19 @@ public void testPrimitiveListAsParameter() { delayTestFinish(5000); - final Request<SimpleFooProxy> fooReq = - req.simpleFooRequest().findSimpleFooById(999L); + final Request<SimpleFooProxy> fooReq = req.simpleFooRequest().findSimpleFooById( + 999L); fooReq.fire(new Receiver<SimpleFooProxy>() { public void onSuccess(SimpleFooProxy response) { - final Request<Integer> sumReq = req.simpleFooRequest().sum( - response, Arrays.asList(1,2,3)); - sumReq.fire(new Receiver<Integer>() { - @Override - public void onSuccess(Integer response) { - assertEquals(6, response.intValue()); - finishTestAndReset(); - } - }); + final Request<Integer> sumReq = req.simpleFooRequest().sum(response, + Arrays.asList(1, 2, 3)); + sumReq.fire(new Receiver<Integer>() { + @Override + public void onSuccess(Integer response) { + assertEquals(6, response.intValue()); + finishTestAndReset(); + } + }); } }); } @@ -1084,7 +1148,7 @@ final Date date = new Date(90, 0, 1); Request<Date> procReq = req.simpleFooRequest().processDateList( - Arrays.asList(date)); + Arrays.asList(date)); procReq.fire(new Receiver<Date>() { @Override public void onSuccess(Date response) { @@ -1106,7 +1170,7 @@ assertEquals(SimpleEnum.BAR, response); finishTestAndReset(); } - }); + }); } public void testPrimitiveSet() { @@ -1127,7 +1191,8 @@ public void testPrimitiveString() { delayTestFinish(5000); final String testString = "test\"string\'with\nstring\u2060characters\t"; - final Request<String> fooReq = req.simpleFooRequest().processString(testString); + final Request<String> fooReq = req.simpleFooRequest().processString( + testString); fooReq.fire(new Receiver<String>() { @Override public void onSuccess(String response) { @@ -1139,7 +1204,8 @@ public void testProxyList() { delayTestFinish(5000); - final Request<SimpleFooProxy> fooReq = req.simpleFooRequest().findSimpleFooById(999L).with("oneToManyField"); + final Request<SimpleFooProxy> fooReq = req.simpleFooRequest().findSimpleFooById( + 999L).with("oneToManyField"); fooReq.fire(new Receiver<SimpleFooProxy>() { @Override public void onSuccess(SimpleFooProxy response) { @@ -1151,24 +1217,23 @@ public void testProxyListAsParameter() { delayTestFinish(5000); - final Request<SimpleFooProxy> fooReq = - req.simpleFooRequest().findSimpleFooById(999L).with("selfOneToManyField"); + final Request<SimpleFooProxy> fooReq = req.simpleFooRequest().findSimpleFooById( + 999L).with("selfOneToManyField"); fooReq.fire(new Receiver<SimpleFooProxy>() { public void onSuccess(final SimpleFooProxy fooProxy) { - final Request<String> procReq = req.simpleFooRequest().processList( - fooProxy, fooProxy.getSelfOneToManyField()); - procReq.fire(new Receiver<String>() { - @Override - public void onSuccess(String response) { - assertEquals(fooProxy.getUserName(), response); - finishTestAndReset(); - } - }); + final Request<String> procReq = req.simpleFooRequest().processList( + fooProxy, fooProxy.getSelfOneToManyField()); + procReq.fire(new Receiver<String>() { + @Override + public void onSuccess(String response) { + assertEquals(fooProxy.getUserName(), response); + finishTestAndReset(); + } + }); } }); } - public void testProxysAsInstanceMethodParams() { delayTestFinish(5000); req.simpleFooRequest().findSimpleFooById(999L).fire( @@ -1176,8 +1241,8 @@ @Override public void onSuccess(SimpleFooProxy response) { SimpleBarProxy bar = req.create(SimpleBarProxy.class); - Request<String> helloReq = req.simpleFooRequest().hello( - response, bar); + Request<String> helloReq = req.simpleFooRequest().hello(response, + bar); bar = helloReq.edit(bar); bar.setUserName("BAR"); helloReq.fire(new Receiver<String>() { @@ -1190,28 +1255,116 @@ } }); } - + public void testServerFailureCheckedException() { SimpleFooProxy newFoo = req.create(SimpleFooProxy.class); - final Request<SimpleFooProxy> persistRequest = - req.simpleFooRequest().persistAndReturnSelf(newFoo); + final Request<SimpleFooProxy> persistRequest = req.simpleFooRequest().persistAndReturnSelf( + newFoo); final SimpleFooProxy mutableFoo = persistRequest.edit(newFoo); // 43 is the crash causing magic number for a checked exception mutableFoo.setPleaseCrash(43); persistRequest.fire(new FooReciever(mutableFoo, persistRequest, "")); } - + public void testServerFailureRuntimeException() { delayTestFinish(5000); SimpleFooProxy newFoo = req.create(SimpleFooProxy.class); - final Request<SimpleFooProxy> persistRequest = - req.simpleFooRequest().persistAndReturnSelf(newFoo); + final Request<SimpleFooProxy> persistRequest = req.simpleFooRequest().persistAndReturnSelf( + newFoo); final SimpleFooProxy mutableFoo = persistRequest.edit(newFoo); // 42 is the crash causing magic number for a runtime exception - mutableFoo.setPleaseCrash(42); + mutableFoo.setPleaseCrash(42); persistRequest.fire(new FooReciever(mutableFoo, persistRequest, "")); - } - + } + + /** + * Tests the behaviors of setters and their effects on getters. + */ + public void testSetters() { + SimpleFooProxy foo = req.create(SimpleFooProxy.class); + SimpleBarProxy bar = req.create(SimpleBarProxy.class); + + // Assert that uninitalize references are null + assertNull(foo.getBarField()); + + // Assert that objects must be made mutable before calling setters. + try { + foo.setBarField(bar); + fail("Must require request to call setters"); + } catch (UnsupportedOperationException expected) { + } + + Request<SimpleFooProxy> r = req.simpleFooRequest().persistAndReturnSelf(foo); + foo = r.edit(foo); + foo.setBarField(bar); + + // Assert that the set value is retained + SimpleBarProxy returnedBarField = foo.getBarField(); + assertNotNull(returnedBarField); + assertEquals(bar.stableId(), returnedBarField.stableId()); + assertEquals(returnedBarField, foo.getBarField()); + assertNotSame(returnedBarField, foo.getBarField()); + + // Returned not equal to original because it's associated with a request + assertFalse(bar.equals(returnedBarField)); + + // Getters called on mutable objects are also mutable + returnedBarField.setUserName("userName"); + assertEquals("userName", returnedBarField.getUserName()); + } + + /** + * There's plenty of special-case code for Collection properties, so they need + * to be tested as well. + */ + public void testSettersWithCollections() { + SimpleFooProxy foo = req.create(SimpleFooProxy.class); + SimpleBarProxy bar = req.create(SimpleBarProxy.class); + List<SimpleBarProxy> originalList = Collections.singletonList(bar); + + // Assert that uninitalize references are null + assertNull(foo.getOneToManyField()); + + // Assert that objects must be made mutable before calling setters. + try { + foo.setOneToManyField(null); + fail("Must require request to call setters"); + } catch (UnsupportedOperationException expected) { + } + + Request<SimpleFooProxy> r = req.simpleFooRequest().persistAndReturnSelf(foo); + foo = r.edit(foo); + foo.setOneToManyField(originalList); + // There's a "dummy" create case here; AbstractRequest, DVS is untestable + + // Assert that the value is retained, but a defensive copy + List<SimpleBarProxy> list = foo.getOneToManyField(); + assertNotSame(originalList, list); + // Not equal because list is associated with a request now + assertFalse(originalList.equals(list)); + assertEquals(1, list.size()); + assertEquals(bar.stableId(), list.get(0).stableId()); + assertEquals(list, foo.getOneToManyField()); + + // Assert that entities returned from editable list are mutable + list.get(0).setUserName("userName"); + } + + public void testSettersWithMutableObject() { + SimpleFooProxy foo = req.create(SimpleFooProxy.class); + Request<SimpleFooProxy> r = req.simpleFooRequest().persistAndReturnSelf(foo); + foo = r.edit(foo); + + SimpleBarProxy immutableBar = req.create(SimpleBarProxy.class); + SimpleBarProxy mutableBar = r.edit(immutableBar); + mutableBar.setUserName("userName"); + foo.setBarField(mutableBar); + + // Creating a new editable object in the same request should read through + r.edit(immutableBar).setUserName("Reset"); + assertEquals("Reset", foo.getBarField().getUserName()); + } + public void testStableId() { delayTestFinish(5000); @@ -1311,8 +1464,7 @@ fooCreationRequest().fire(new Receiver<SimpleFooProxy>() { @Override public void onSuccess(SimpleFooProxy returned) { - Request<Void> editRequest = req.simpleFooRequest().persist( - returned); + Request<Void> editRequest = req.simpleFooRequest().persist(returned); new FailFixAndRefire<Void>(returned, editRequest).doVoidTest(); } });
diff --git a/user/test/com/google/gwt/requestfactory/client/SimpleRequestFactoryInstance.java b/user/test/com/google/gwt/requestfactory/client/SimpleRequestFactoryInstance.java index 2dded39..2889db8 100644 --- a/user/test/com/google/gwt/requestfactory/client/SimpleRequestFactoryInstance.java +++ b/user/test/com/google/gwt/requestfactory/client/SimpleRequestFactoryInstance.java
@@ -16,6 +16,7 @@ package com.google.gwt.requestfactory.client; import com.google.gwt.core.client.GWT; +import com.google.gwt.event.shared.SimpleEventBus; import com.google.gwt.requestfactory.client.impl.ProxyImpl; import com.google.gwt.requestfactory.client.impl.ProxySchema; import com.google.gwt.requestfactory.client.impl.RequestFactoryJsonImpl; @@ -31,6 +32,7 @@ public static SimpleRequestFactory factory() { if (factory == null) { factory = GWT.create(SimpleRequestFactory.class); + factory.initialize(new SimpleEventBus()); } return factory;
diff --git a/user/test/com/google/gwt/requestfactory/server/SimpleBar.java b/user/test/com/google/gwt/requestfactory/server/SimpleBar.java index 3b6ebc2..f6e2fd6 100644 --- a/user/test/com/google/gwt/requestfactory/server/SimpleBar.java +++ b/user/test/com/google/gwt/requestfactory/server/SimpleBar.java
@@ -34,11 +34,15 @@ * DO NOT USE THIS UGLY HACK DIRECTLY! Call {@link #get} instead. */ private static Map<String, SimpleBar> jreTestSingleton = new HashMap<String, SimpleBar>(); - + private static long nextId = 2L; + static { + reset(); + } + public static Long countSimpleBar() { - return (long) get().size(); + return (long) get().size(); } public static List<SimpleBar> findAll() { @@ -53,8 +57,12 @@ return findSimpleBarById(id); } + /** + * Returns <code>null</code> if {@link #findFails} is <code>true</code>. + */ public static SimpleBar findSimpleBarById(String id) { - return get().get(id); + SimpleBar toReturn = get().get(id); + return toReturn.findFails ? null : toReturn; } @SuppressWarnings("unchecked") @@ -82,6 +90,11 @@ return findSimpleBar("1L"); } + public static SimpleBar returnFirst(List<SimpleBar> list) { + SimpleBar toReturn = list.get(0); + return toReturn; + } + public static synchronized Map<String, SimpleBar> reset() { Map<String, SimpleBar> instance = new HashMap<String, SimpleBar>(); // fixtures @@ -105,24 +118,23 @@ return instance; } - static { - reset(); - } - Integer version = 1; @Id private String id = "999L"; - - private String userName; - + private boolean findFails; private boolean isNew = true; + private String userName; public SimpleBar() { version = 1; userName = "FOO"; } + public Boolean getFindFails() { + return findFails; + } + public String getId() { return id; } @@ -148,6 +160,10 @@ return this; } + public void setFindFails(Boolean fails) { + this.findFails = fails; + } + public void setId(String id) { this.id = id; }
diff --git a/user/test/com/google/gwt/requestfactory/server/SimpleFoo.java b/user/test/com/google/gwt/requestfactory/server/SimpleFoo.java index 15f8edf..23c56d2 100644 --- a/user/test/com/google/gwt/requestfactory/server/SimpleFoo.java +++ b/user/test/com/google/gwt/requestfactory/server/SimpleFoo.java
@@ -105,7 +105,7 @@ public static Date processDateList(List<Date> values) { return values.get(0); } - + public static SimpleEnum processEnumList(List<SimpleEnum> values) { return values.get(0); } @@ -126,6 +126,18 @@ return instance; } + public static List<SimpleFoo> returnNullList() { + return null; + } + + public static SimpleFoo returnNullSimpleFoo() { + return null; + } + + public static String returnNullString() { + return null; + } + @SuppressWarnings("unused") private static Integer privateMethod() { return 0; @@ -171,7 +183,7 @@ private List<SimpleBar> oneToManyField; private List<SimpleFoo> selfOneToManyField; private Set<SimpleBar> oneToManySetField; - + private List<Integer> numberListField; public SimpleFoo() { @@ -284,7 +296,7 @@ public String getNullField() { return nullField; } - + public List<Integer> getNumberListField() { return numberListField; } @@ -351,7 +363,7 @@ } return result; } - + public void setBarField(SimpleBar barField) { this.barField = barField; } @@ -459,7 +471,8 @@ public void setPleaseCrash(Integer crashIf42or43) throws Exception { if (crashIf42or43 == 42) { - throw new UnsupportedOperationException("THIS EXCEPTION IS EXPECTED BY A TEST"); + throw new UnsupportedOperationException( + "THIS EXCEPTION IS EXPECTED BY A TEST"); } if (crashIf42or43 == 43) { throw new Exception("THIS EXCEPTION IS EXPECTED BY A TEST");
diff --git a/user/test/com/google/gwt/requestfactory/shared/SimpleBarProxy.java b/user/test/com/google/gwt/requestfactory/shared/SimpleBarProxy.java index 892f926..782ea66 100644 --- a/user/test/com/google/gwt/requestfactory/shared/SimpleBarProxy.java +++ b/user/test/com/google/gwt/requestfactory/shared/SimpleBarProxy.java
@@ -18,15 +18,19 @@ import com.google.gwt.requestfactory.server.SimpleBar; /** - * A simple entity used for testing. Has an int field and date field. Add other data types as their - * support gets built in. + * A simple entity used for testing. Has an int field and date field. Add other + * data types as their support gets built in. */ @ProxyFor(SimpleBar.class) public interface SimpleBarProxy extends EntityProxy { + Boolean getFindFails(); + String getId(); - + String getUserName(); + void setFindFails(Boolean fails); + void setUserName(String userName); EntityProxyId<SimpleBarProxy> stableId();
diff --git a/user/test/com/google/gwt/requestfactory/shared/SimpleBarRequest.java b/user/test/com/google/gwt/requestfactory/shared/SimpleBarRequest.java index 19888eb..51d4879 100644 --- a/user/test/com/google/gwt/requestfactory/shared/SimpleBarRequest.java +++ b/user/test/com/google/gwt/requestfactory/shared/SimpleBarRequest.java
@@ -15,6 +15,8 @@ */ package com.google.gwt.requestfactory.shared; +import java.util.List; + /** * Do nothing test interface. */ @@ -36,4 +38,6 @@ ProxyRequest<SimpleBarProxy> persistAndReturnSelf(SimpleBarProxy proxy); Request<Void> reset(); + + ProxyRequest<SimpleBarProxy> returnFirst(List<SimpleBarProxy> proxy); }
diff --git a/user/test/com/google/gwt/requestfactory/shared/SimpleFooRequest.java b/user/test/com/google/gwt/requestfactory/shared/SimpleFooRequest.java index d482096..c9aa77b 100644 --- a/user/test/com/google/gwt/requestfactory/shared/SimpleFooRequest.java +++ b/user/test/com/google/gwt/requestfactory/shared/SimpleFooRequest.java
@@ -61,6 +61,12 @@ Request<Void> reset(); + ProxyListRequest<SimpleFooProxy> returnNullList(); + + ProxyRequest<SimpleFooProxy> returnNullSimpleFoo(); + + Request<String> returnNullString(); + @Instance Request<Integer> sum(SimpleFooProxy instance, List<Integer> values); }