Revision: 9609
Author: rchan...@google.com
Date: Tue Jan 25 08:05:30 2011
Log: Updates DTRF to show the use of ServiceLocators and Locators.
- Added editors to modify person schedules
- Schedule is now a RF Entity and TimeSlot is now a VO
- ScheduleLocator interfaces with "non standard" getKey and getRevision methods
- ScheduleService provides a service method

Review at http://gwt-code-reviews.appspot.com/1299801

Review by: rj...@google.com
http://code.google.com/p/google-web-toolkit/source/detail?r=9609

Added:
/trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/ScheduleEditor.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/ScheduleEditor.ui.xml /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/TimeSlotListWidget.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/TimeSlotListWidget.ui.xml /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/ScheduleFuzzer.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/ScheduleLocator.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/ScheduleService.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/ScheduleServiceLocator.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/ScheduleSource.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/ScheduleProxy.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/TimeSlotProxy.java
Modified:
/trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/PersonEditorWorkflow.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/PersonEditor.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/PersonEditor.ui.xml /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/SummaryWidget.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Person.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Schedule.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/TimeSlot.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/PersonFuzzer.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/PersonSource.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/SchoolCalendarService.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/DynaTableRequestFactory.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/PersonProxy.java

=======================================
--- /dev/null
+++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/ScheduleEditor.java Tue Jan 25 08:05:30 2011
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2011 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.client.widgets;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.editor.client.Editor;
+import com.google.gwt.sample.dynatablerf.shared.ScheduleProxy;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * Edits a persons's schedule.
+ */
+public class ScheduleEditor extends Composite implements Editor<ScheduleProxy> {
+
+ interface ScheduleEditorUiBinder extends UiBinder<Widget, ScheduleEditor> {
+  }
+
+  private static ScheduleEditorUiBinder uiBinder = GWT.create(
+      ScheduleEditorUiBinder.class);
+
+  @UiField(provided = true)
+  TimeSlotListWidget timeSlots;
+
+  public ScheduleEditor(TimeSlotListWidget timeSlotEditor) {
+    timeSlots = timeSlotEditor;
+    initWidget(uiBinder.createAndBindUi(this));
+  }
+
+}
=======================================
--- /dev/null
+++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/ScheduleEditor.ui.xml Tue Jan 25 08:05:30 2011
@@ -0,0 +1,13 @@
+<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent";>
+<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
+  xmlns:g="urn:import:com.google.gwt.user.client.ui"
+  xmlns:dt='urn:import:com.google.gwt.sample.dynatablerf.client.widgets' >
+  <ui:style>
+    .table {
+      width: 100%;
+    }
+  </ui:style>
+  <g:HTMLPanel>
+ <dt:TimeSlotListWidget ui:field="timeSlots" stylePrimaryName="{style.table}" />
+  </g:HTMLPanel>
+</ui:UiBinder>
=======================================
--- /dev/null
+++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/TimeSlotListWidget.java Tue Jan 25 08:05:30 2011
@@ -0,0 +1,267 @@
+/*
+ * Copyright 2011 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.client.widgets;
+
+import com.google.gwt.cell.client.ClickableTextCell;
+import com.google.gwt.cell.client.FieldUpdater;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.editor.client.EditorDelegate;
+import com.google.gwt.editor.client.ValueAwareEditor;
+import com.google.gwt.requestfactory.shared.Receiver;
+import com.google.gwt.sample.dynatablerf.client.widgets.SummaryWidget.TableResources;
+import com.google.gwt.sample.dynatablerf.shared.DynaTableRequestFactory;
+import com.google.gwt.sample.dynatablerf.shared.DynaTableRequestFactory.ScheduleRequest;
+import com.google.gwt.sample.dynatablerf.shared.TimeSlotProxy;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.user.cellview.client.CellTable;
+import com.google.gwt.user.cellview.client.Column;
+import com.google.gwt.user.cellview.client.HasKeyboardSelectionPolicy.KeyboardSelectionPolicy;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Widget;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Edits a list of time slots.
+ */
+public class TimeSlotListWidget extends Composite implements ValueAwareEditor<List<TimeSlotProxy>> {
+
+ interface TimeSlotListWidgetUiBinder extends UiBinder<Widget, TimeSlotListWidget> {
+  }
+
+  private class ScheduleRow {
+    int hour;
+
+    ScheduleRow(int hour) {
+      this.hour = hour;
+    }
+
+    public int getHour() {
+      return hour;
+    }
+
+    public boolean isInUse(WeekDay day) {
+      return currentSchedule.contains(new TimeSlotKey(day, hour));
+    }
+
+    public void toggleInUse(WeekDay day) {
+      final TimeSlotKey key = new TimeSlotKey(day, hour);
+      if (currentSchedule.contains(key)) {
+        currentSchedule.remove(key);
+        table.redraw();
+      } else if (!existingSlots.containsKey(key)) {
+        acceptClicks = false;
+        ScheduleRequest context = factory.scheduleRequest();
+ context.createTimeSlot(day.ordinal(), hour * 60, hour * 60 + 50).fire(
+            new Receiver<TimeSlotProxy>() {
+              public void onSuccess(TimeSlotProxy slot) {
+                existingSlots.put(key, slot);
+                backing.add(slot);
+                currentSchedule.add(key);
+                table.redraw();
+                acceptClicks = true;
+              }
+        });
+      } else {
+        currentSchedule.add(key);
+        table.redraw();
+      }
+    }
+  }
+
+  private static class TimeSlotKey {
+    private int hour;
+    private WeekDay day;
+
+    TimeSlotKey(WeekDay day, int hour) {
+      this.day = day;
+      this.hour = hour;
+    }
+
+    TimeSlotKey(TimeSlotProxy slot) {
+      day = WeekDay.fromInt(slot.getDayOfWeek());
+      hour = slot.getStartMinutes() / 60;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (this == obj) {
+        return true;
+      }
+      if (obj == null) {
+        return false;
+      }
+      if (getClass() != obj.getClass()) {
+        return false;
+      }
+      TimeSlotKey other = (TimeSlotKey) obj;
+      if (day == null) {
+        if (other.day != null) {
+          return false;
+        }
+      } else if (!day.equals(other.day)) {
+        return false;
+      }
+      if (hour != other.hour) {
+        return false;
+      }
+      return true;
+    }
+
+    @Override
+    public int hashCode() {
+      return 31 * (31 + ((day == null) ? 0 : day.hashCode())) + hour;
+    }
+  }
+
+  private class WeekDayColumn extends Column<ScheduleRow, String> {
+    private WeekDay day;
+
+    public WeekDayColumn(WeekDay day) {
+      super(new ClickableTextCell());
+      this.day = day;
+    }
+
+    @Override
+    public String getValue(ScheduleRow row) {
+      if (day == null) {
+        int hour = row.getHour();
+        return Integer.toString(hour <= 12 ? hour : hour - 12) + ":00"
+            + ((hour < 12) ? "AM" : "PM");
+      }
+      return row.isInUse(day) ? "X" : ".";
+    }
+  }
+
+  private static final int ROWS_IN_A_DAY = 9;
+  private static final int FIRST_HOUR = 8;
+
+  private static TimeSlotListWidgetUiBinder uiBinder = GWT.create(
+      TimeSlotListWidgetUiBinder.class);
+
+  @UiField(provided = true)
+  CellTable<ScheduleRow> table;
+
+  private enum WeekDay {
+    SUNDAY("Su"), MONDAY("Mo"), TUESDAY("Tu"), WEDNESDAY("We"),
+    THURSDAY("Th"), FRIDAY("Fr"), SATURDAY("Sa");
+
+    public static WeekDay fromInt(int ordinal) {
+      return values()[ordinal];
+    }
+
+    private String shortName;
+
+    WeekDay(String shortName) {
+      this.shortName = shortName;
+    }
+
+    public String getShortName() {
+      return shortName;
+    }
+  }
+
+  private List<TimeSlotProxy> backing;
+  private HashSet<TimeSlotKey> currentSchedule;
+  private HashMap<TimeSlotKey, TimeSlotProxy> existingSlots;
+  private DynaTableRequestFactory factory;
+  private HashSet<TimeSlotKey> initialSchedule;
+  private boolean acceptClicks = true;
+
+  public TimeSlotListWidget(DynaTableRequestFactory factory) {
+    this.factory = factory;
+    table = new CellTable<TimeSlotListWidget.ScheduleRow>(ROWS_IN_A_DAY,
+        GWT.<TableResources> create(TableResources.class));
+    table.addColumn(new WeekDayColumn(null), "Hour");
+    for (WeekDay day : WeekDay.values()) {
+      WeekDayColumn col = new WeekDayColumn(day);
+
+      class Updater implements FieldUpdater<ScheduleRow, String> {
+        private WeekDay columnDay;
+
+        public Updater(WeekDay day) {
+          columnDay = day;
+        }
+
+        public void update(int index, ScheduleRow row, String value) {
+          if (acceptClicks) {
+            row.toggleInUse(columnDay);
+          }
+        }
+      }
+
+      FieldUpdater<ScheduleRow, String> fieldUpdater = new Updater(day);
+      col.setFieldUpdater(fieldUpdater);
+      table.addColumn(col, day.getShortName());
+    }
+
+    table.setRowCount(ROWS_IN_A_DAY, false);
+    table.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.DISABLED);
+    initWidget(uiBinder.createAndBindUi(this));
+  }
+
+  public void flush() {
+ HashMap<TimeSlotProxy, TimeSlotKey> index = new HashMap<TimeSlotProxy, TimeSlotKey>();
+
+    for (TimeSlotProxy slot : backing) {
+      index.put(slot, new TimeSlotKey(slot));
+    }
+
+    // Compute slots that need to be removed from the backing
+    initialSchedule.removeAll(currentSchedule);
+
+ for (Iterator<TimeSlotProxy> iterator = backing.iterator(); iterator.hasNext();) {
+      TimeSlotProxy slot = iterator.next();
+      TimeSlotKey key = index.get(slot);
+      if (initialSchedule.contains(key)) {
+        iterator.remove();
+      }
+    }
+  }
+
+  public void onPropertyChange(String... paths) {
+  }
+
+  public void setDelegate(EditorDelegate<List<TimeSlotProxy>> delegate) {
+  }
+
+  public void setValue(List<TimeSlotProxy> value) {
+    backing = value;
+    currentSchedule = new HashSet<TimeSlotKey>();
+    existingSlots = new HashMap<TimeSlotKey, TimeSlotProxy>();
+
+    initialSchedule = new HashSet<TimeSlotKey>();
+
+    for (TimeSlotProxy slot : backing) {
+      TimeSlotKey key = new TimeSlotKey(slot);
+      currentSchedule.add(key);
+      existingSlots.put(key, slot);
+      initialSchedule.add(new TimeSlotKey(slot));
+    }
+
+ ArrayList<ScheduleRow> rows = new ArrayList<ScheduleRow>(ROWS_IN_A_DAY);
+    for (int i = 0; i < ROWS_IN_A_DAY; i++) {
+      rows.add(new ScheduleRow(FIRST_HOUR + i));
+    }
+    table.setRowData(rows);
+  }
+
+}
=======================================
--- /dev/null
+++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/TimeSlotListWidget.ui.xml Tue Jan 25 08:05:30 2011
@@ -0,0 +1,13 @@
+<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent";>
+<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
+  xmlns:g="urn:import:com.google.gwt.user.client.ui"
+  xmlns:cv='urn:import:com.google.gwt.user.cellview.client'>
+  <ui:style>
+    .table {
+      width: 100%;
+    }
+  </ui:style>
+  <g:HTMLPanel>
+    <cv:CellTable ui:field="table" stylePrimaryName="{style.table}" />
+  </g:HTMLPanel>
+</ui:UiBinder>
=======================================
--- /dev/null
+++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/ScheduleFuzzer.java Tue Jan 25 08:05:30 2011
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2011 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.server;
+
+import com.google.gwt.sample.dynatablerf.domain.Schedule;
+import com.google.gwt.sample.dynatablerf.domain.TimeSlot;
+
+import java.util.ArrayList;
+import java.util.Random;
+import java.util.TreeSet;
+
+/**
+ * Generates a list of random schedules. To facilitate the UI
+ * TimeSlots are defined in whole hour blocks.
+ */
+public class ScheduleFuzzer {
+
+  private static final int MAX_SCHED_ENTRIES = 5;
+
+  private static final int MIN_SCHED_ENTRIES = 1;
+
+  private static final int CLASS_LENGTH_MINS = 50;
+
+  private static final int NUMBER_OF_SCHEDULES = PersonFuzzer.MAX_PEOPLE;
+
+  public static Schedule generateRandomSchedule(Random rnd) {
+    int range = MAX_SCHED_ENTRIES - MIN_SCHED_ENTRIES + 1;
+    int howMany = MIN_SCHED_ENTRIES + rnd.nextInt(range);
+
+    ArrayList<TimeSlot> timeSlots = generateTimeSlots(rnd, howMany);
+
+    Schedule sched = new Schedule();
+    for (TimeSlot timeSlot : timeSlots) {
+      sched.addTimeSlot(timeSlot);
+    }
+    return sched;
+  }
+
+  public static Schedule[] generateSchedules() {
+    Random rnd = new Random();
+    Schedule[] toReturn = new Schedule[NUMBER_OF_SCHEDULES];
+    for (int i = 0; i < NUMBER_OF_SCHEDULES; i++) {
+      Schedule sched = generateRandomSchedule(rnd);
+      toReturn[i] = sched;
+    }
+    return toReturn;
+  }
+
+ private static ArrayList<TimeSlot> generateTimeSlots(Random rnd, int howMany) {
+    TreeSet<TimeSlot> timeSlots = new TreeSet<TimeSlot>();
+
+    for (int i = 0; i < howMany; ++i) {
+      int startHrs = 8 + rnd.nextInt(9); // 8 am - 5 pm
+      int dayOfWeek = 1 + rnd.nextInt(5); // Mon - Fri
+
+      int absStartMins = 60 * startHrs; // convert to minutes
+      int absStopMins = absStartMins + CLASS_LENGTH_MINS;
+
+      timeSlots.add(new TimeSlot(dayOfWeek, absStartMins, absStopMins));
+    }
+
+    return new ArrayList<TimeSlot>(timeSlots);
+  }
+
+}
=======================================
--- /dev/null
+++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/ScheduleLocator.java Tue Jan 25 08:05:30 2011
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2011 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.server;
+
+import com.google.gwt.requestfactory.shared.Locator;
+import com.google.gwt.sample.dynatablerf.domain.Schedule;
+
+/**
+ * This class serves as an example of implementing a Locator to allow
+ * RequestFactory to work with entities that don't conform to its expectations of + * static find*() methods, and getId() and getVersion() methods. In a production + * application such a Locator might be the bridge to your dependency injection
+ * framework, or a data access object.
+ * <p>
+ * There is a reference to this class in a {@literal @}Service annotation in
+ * {@link com.google.gwt.sample.dynatablerf.shared.DynaTableRequestFactory}
+ */
+public class ScheduleLocator extends Locator<Schedule, String> {
+
+  public static ScheduleSource getThreadLocalObject() {
+    return SchoolCalendarService.SCHEDULE_SOURCE.get();
+  }
+
+  @Override
+  public Schedule create(Class<? extends Schedule> clazz) {
+    return getThreadLocalObject().create(clazz);
+  }
+
+  @Override
+  public Schedule find(Class<? extends Schedule> clazz, String id) {
+    return getThreadLocalObject().find(clazz, id);
+  }
+
+  @Override
+  public Class<Schedule> getDomainType() {
+    return getThreadLocalObject().getDomainType();
+  }
+
+  @Override
+  public String getId(Schedule domainObject) {
+    return getThreadLocalObject().getId(domainObject);
+  }
+
+  @Override
+  public Class<String> getIdType() {
+    return getThreadLocalObject().getIdType();
+  }
+
+  @Override
+  public Object getVersion(Schedule domainObject) {
+    return getThreadLocalObject().getVersion(domainObject);
+  }
+
+  public void persist(Schedule domainObject) {
+    getThreadLocalObject().persist(domainObject);
+  }
+}
=======================================
--- /dev/null
+++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/ScheduleService.java Tue Jan 25 08:05:30 2011
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2011 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.server;
+
+import com.google.gwt.sample.dynatablerf.domain.TimeSlot;
+
+/**
+ * Service object for Schedule entities, used to demonstrate the use of non-static + * service objects with RequestFactory. RequestFactory finds this service via the
+ * {@link ScheduleServiceLocator}.
+ */
+public class ScheduleService {
+
+ public TimeSlot createTimeSlot(int zeroBasedDayOfWeek, int startMinutes, int endMinutes) {
+    return new TimeSlot(zeroBasedDayOfWeek, startMinutes, endMinutes);
+  }
+
+}
=======================================
--- /dev/null
+++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/ScheduleServiceLocator.java Tue Jan 25 08:05:30 2011
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2011 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.server;
+
+import com.google.gwt.requestfactory.shared.ServiceLocator;
+
+/**
+ * This class provides an example of implementing a ServiceLocator to allow
+ * RequestFactory to work with instances of service objects, instead of its default
+ * behavior of mapping service calls to static methods.
+ * <p>
+ * There is a reference to this class in an {@literal @}Service annotation in
+ * {@link com.google.gwt.sample.dynatablerf.shared.DynaTableRequestFactory}
+ */
+public class ScheduleServiceLocator implements ServiceLocator {
+
+  public Object getInstance(Class<?> clazz) {
+ return clazz.equals(ScheduleService.class) ? new ScheduleService() : null;
+  }
+
+}
=======================================
--- /dev/null
+++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/ScheduleSource.java Tue Jan 25 08:05:30 2011
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2011 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.server;
+
+import com.google.gwt.sample.dynatablerf.domain.Schedule;
+import com.google.gwt.sample.dynatablerf.domain.TimeSlot;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Provides a number of Schedule objects as a demonstration datasource. Many of + * the operations in this implementation would be much more efficient in a real + * database, but are implemented is a straightforward fashion because they're
+ * not really important for understanding the RequestFactory framework.
+ */
+public abstract class ScheduleSource {
+
+  private static int serial = 0;
+
+  /**
+   * Backing store of schedule entities.
+   */
+  static class Backing extends ScheduleSource {
+
+    @Override
+    public Schedule create(Class<? extends Schedule> clazz) {
+      return new Schedule();
+    }
+
+    @Override
+    public Schedule find(Class<? extends Schedule> clazz, String id) {
+      if (!Schedule.class.equals(clazz)) {
+        return null;
+      }
+      return schedules.get(makeKey(id));
+    }
+
+    @Override
+    public Class<Schedule> getDomainType() {
+      return Schedule.class;
+    }
+
+    @Override
+    public String getId(Schedule domainObject) {
+      return Integer.toString(domainObject.getKey());
+    }
+
+    @Override
+    public Class<String> getIdType() {
+      return String.class;
+    }
+
+    @Override
+    public Object getVersion(Schedule domainObject) {
+      return domainObject.getRevision();
+    }
+
+    @Override
+    public void persist(Schedule domainObject) {
+      if (domainObject.getKey() == null) {
+        domainObject.setKey(newSerial());
+      }
+      domainObject.setRevision(newSerial());
+      Schedule existing = schedules.get(domainObject.getKey());
+      if (existing == null) {
+        schedules.put(domainObject.getKey(), domainObject);
+      } else {
+        copyScheduleFields(domainObject, existing);
+      }
+    }
+  }
+
+  /**
+   * Provides copy-on-read access to a ScheduleLocator.
+   */
+  public static class CopyOnRead extends Backing {
+    private final ScheduleSource backingStore;
+
+    public CopyOnRead(ScheduleSource backingStore) {
+      this.backingStore = backingStore;
+    }
+
+    @Override
+    public Schedule find(Class<? extends Schedule> clazz, String id) {
+      if (!Schedule.class.equals(clazz)) {
+        return null;
+      }
+      Integer key = makeKey(id);
+      Schedule toReturn = schedules.get(key);
+      if (toReturn == null) {
+        Schedule original = backingStore.find(clazz, id);
+        if (original != null) {
+          toReturn = makeCopy(original);
+        }
+        schedules.put(key, toReturn);
+      }
+      return toReturn;
+    }
+
+    @Override
+    public void persist(Schedule domainObject) {
+      backingStore.persist(domainObject);
+    }
+
+    private Schedule makeCopy(Schedule source) {
+      Schedule destination = new Schedule();
+      copyScheduleFields(source, destination);
+      return destination;
+    }
+  }
+
+  static void copyScheduleFields(Schedule source, Schedule destination) {
+    destination.setKey(source.getKey());
+    destination.setRevision(source.getRevision());
+ destination.setTimeSlots(new ArrayList<TimeSlot>(source.getTimeSlots()));
+  }
+
+  public static Backing createBacking() {
+    return new Backing();
+  }
+
+  public static ScheduleSource getThreadLocalObject() {
+    return SchoolCalendarService.SCHEDULE_SOURCE.get();
+  }
+
+  /**
+   * Create a ScheduleLocator that will act directly on the given list.
+   */
+  public static ScheduleSource of(List<Schedule> schedules) {
+    ScheduleSource backing = createBacking();
+    for (Schedule schedule : schedules) {
+      backing.persist(schedule);
+    }
+    return backing;
+  }
+
+  /**
+ * Create a ScheduleLocator that will read through to the given source and make
+   * copies of any objects that are requested.
+   */
+  public static ScheduleSource of(ScheduleSource backing) {
+    return new CopyOnRead(backing);
+  }
+
+  private static Integer makeKey(String id) {
+    return Integer.valueOf(id);
+  }
+
+  private static int newSerial() {
+    return ++serial;
+  }
+
+ final Map<Integer, Schedule> schedules = new LinkedHashMap<Integer, Schedule>();
+
+  public abstract Schedule create(Class<? extends Schedule> clazz);
+
+  public Schedule find(Integer key) {
+    return find(Schedule.class, key.toString());
+  }
+
+ public abstract Schedule find(Class<? extends Schedule> clazz, String id);
+
+  public abstract Class<Schedule> getDomainType();
+
+  public abstract String getId(Schedule domainObject);
+
+  public abstract Class<String> getIdType();
+
+  public abstract Object getVersion(Schedule domainObject);
+
+  public abstract void persist(Schedule domainObject);
+
+}
=======================================
--- /dev/null
+++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/ScheduleProxy.java Tue Jan 25 08:05:30 2011
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2011 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.shared;
+
+import com.google.gwt.requestfactory.shared.EntityProxy;
+import com.google.gwt.requestfactory.shared.ProxyFor;
+import com.google.gwt.sample.dynatablerf.domain.Schedule;
+import com.google.gwt.sample.dynatablerf.server.ScheduleLocator;
+
+import java.util.List;
+
+/**
+ * Schedule DTO.
+ */
+@ProxyFor(value = Schedule.class, locator = ScheduleLocator.class)
+public interface ScheduleProxy extends EntityProxy {
+  List<TimeSlotProxy> getTimeSlots();
+  void setTimeSlots(List<TimeSlotProxy> slots);
+}
=======================================
--- /dev/null
+++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/TimeSlotProxy.java Tue Jan 25 08:05:30 2011
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2011 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.shared;
+
+import com.google.gwt.requestfactory.shared.ProxyFor;
+import com.google.gwt.requestfactory.shared.ValueProxy;
+import com.google.gwt.sample.dynatablerf.domain.TimeSlot;
+
+/**
+ * TimeSlot DTO.
+ */
+@ProxyFor(TimeSlot.class)
+public interface TimeSlotProxy extends ValueProxy {
+  int getDayOfWeek();
+
+  int getEndMinutes();
+
+  int getStartMinutes();
+
+  void setDayOfWeek(int zeroBasedDayOfWeek);
+
+  void setEndMinutes(int endMinutes);
+
+  void setStartMinutes(int startMinutes);
+}
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/PersonEditorWorkflow.java Fri Oct 22 07:37:38 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/PersonEditorWorkflow.java Tue Jan 25 08:05:30 2011
@@ -28,7 +28,10 @@
 import com.google.gwt.requestfactory.shared.RequestContext;
 import com.google.gwt.requestfactory.shared.Violation;
 import com.google.gwt.sample.dynatablerf.client.events.EditPersonEvent;
+import com.google.gwt.sample.dynatablerf.client.widgets.MentorSelector;
 import com.google.gwt.sample.dynatablerf.client.widgets.PersonEditor;
+import com.google.gwt.sample.dynatablerf.client.widgets.ScheduleEditor;
+import com.google.gwt.sample.dynatablerf.client.widgets.TimeSlotListWidget;
 import com.google.gwt.sample.dynatablerf.shared.DynaTableRequestFactory;
import com.google.gwt.sample.dynatablerf.shared.DynaTableRequestFactory.PersonRequest;
 import com.google.gwt.sample.dynatablerf.shared.PersonProxy;
@@ -88,7 +91,10 @@
     this.requestFactory = requestFactory;
     this.manager = manager;
     this.person = person;
-    personEditor = new PersonEditor(requestFactory);
+ TimeSlotListWidget timeSlotEditor = new TimeSlotListWidget(requestFactory);
+    ScheduleEditor scheduleEditor = new ScheduleEditor(timeSlotEditor);
+    MentorSelector mentorEditor = new MentorSelector(requestFactory);
+    personEditor = new PersonEditor(mentorEditor, scheduleEditor);
     Binder.BINDER.createAndBindUi(this);
     contents.addDomHandler(new KeyUpHandler() {
       public void onKeyUp(KeyUpEvent event) {
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/PersonEditor.java Tue Oct 5 17:59:14 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/PersonEditor.java Tue Jan 25 08:05:30 2011
@@ -20,7 +20,6 @@
 import com.google.gwt.core.client.Scheduler.ScheduledCommand;
 import com.google.gwt.editor.client.Editor;
 import com.google.gwt.editor.ui.client.ValueBoxEditorDecorator;
-import com.google.gwt.sample.dynatablerf.shared.DynaTableRequestFactory;
 import com.google.gwt.sample.dynatablerf.shared.PersonProxy;
 import com.google.gwt.uibinder.client.UiBinder;
 import com.google.gwt.uibinder.client.UiField;
@@ -52,9 +51,13 @@

   @UiField
   Focusable nameBox;
-
-  public PersonEditor(DynaTableRequestFactory factory) {
-    mentor = new MentorSelector(factory);
+
+  @UiField(provided = true)
+  ScheduleEditor classSchedule;
+
+ public PersonEditor(MentorSelector mentorEditor, ScheduleEditor scheduleEditor) {
+    classSchedule = scheduleEditor;
+    this.mentor = mentorEditor;
     initWidget(GWT.<Binder> create(Binder.class).createAndBindUi(this));
   }

=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/PersonEditor.ui.xml Tue Oct 5 17:59:14 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/PersonEditor.ui.xml Tue Jan 25 08:05:30 2011
@@ -35,5 +35,8 @@
<dt:AddressEditor ui:field="address" stylePrimaryName="{style.editField}" />
     Mentor:
<dt:MentorSelector ui:field="mentor" stylePrimaryName="{style.editField}" />
+    <br />
+    Schedule:
+ <dt:ScheduleEditor ui:field="classSchedule" stylePrimaryName="{style.editField}" />
   </g:HTMLPanel>
 </ui:UiBinder>
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/SummaryWidget.java Fri Jan 7 10:19:06 2011 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/SummaryWidget.java Tue Jan 25 08:05:30 2011
@@ -34,6 +34,8 @@
 import com.google.gwt.sample.dynatablerf.shared.DynaTableRequestFactory;
import com.google.gwt.sample.dynatablerf.shared.DynaTableRequestFactory.PersonRequest;
 import com.google.gwt.sample.dynatablerf.shared.PersonProxy;
+import com.google.gwt.sample.dynatablerf.shared.ScheduleProxy;
+import com.google.gwt.sample.dynatablerf.shared.TimeSlotProxy;
 import com.google.gwt.uibinder.client.UiBinder;
 import com.google.gwt.uibinder.client.UiField;
 import com.google.gwt.uibinder.client.UiHandler;
@@ -98,7 +100,7 @@

     @Override
     public String getValue(PersonProxy object) {
-      return object.getSchedule();
+      return object.getScheduleDescription();
     }
   }

@@ -177,8 +179,11 @@
   void onCreate(ClickEvent event) {
     PersonRequest context = requestFactory.personRequest();
     AddressProxy address = context.create(AddressProxy.class);
+    ScheduleProxy schedule = context.create(ScheduleProxy.class);
+    schedule.setTimeSlots(new ArrayList<TimeSlotProxy>());
     PersonProxy person = context.edit(context.create(PersonProxy.class));
     person.setAddress(address);
+    person.setClassSchedule(schedule);
     context.persist().using(person);
     eventBus.fireEvent(new EditPersonEvent(person, context));
   }
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Person.java Thu Nov 18 13:15:58 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Person.java Tue Jan 25 08:05:30 2011
@@ -127,8 +127,8 @@
   public String getNote() {
     return note;
   }
-
-  public String getSchedule() {
+
+  public String getScheduleDescription() {
     return getScheduleWithFilter(daysFilters);
   }

@@ -148,11 +148,6 @@
     return new Person(this);
   }

-  /**
-   * When this was written the RequestFactory required a persist method per
- * type. That requirement should be relaxed very soon (and may well have been
-   * already if we forget to update this comment).
-   */
   public void persist() {
     SchoolCalendarService.persist(this);
   }
@@ -160,6 +155,10 @@
   public void setAddress(Address address) {
     this.address = address;
   }
+
+  public void setClassSchedule(Schedule schedule) {
+    this.classSchedule = schedule;
+  }

   public void setDaysFilter(List<Boolean> daysFilter) {
     assert daysFilter.size() == this.daysFilters.size();
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Schedule.java Thu Oct 14 08:15:26 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Schedule.java Tue Jan 25 08:05:30 2011
@@ -16,14 +16,23 @@
 package com.google.gwt.sample.dynatablerf.domain;

 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;

 /**
- * Hold the relevant data for a Schedule.
+ * Holds the relevant data for a Schedule entity.
+ * This entity does not follow the usual pattern of providing getId(), getVersion()
+ * and findSchedule() methods for RequestFactory's use.
+ * {@link com.google.gwt.sample.dynatablerf.server.ScheduleLocator} handles
+ * those responsibilities instead.
  */
 public class Schedule {

   private List<TimeSlot> timeSlots = new ArrayList<TimeSlot>();
+
+  private Integer key;
+
+  private Integer revision;

   public Schedule() {
   }
@@ -34,7 +43,9 @@

   public String getDescription(List<Boolean> daysFilter) {
     String s = null;
-    for (TimeSlot timeSlot : timeSlots) {
+    ArrayList<TimeSlot> sortedSlots = new ArrayList<TimeSlot>(timeSlots);
+    Collections.sort(sortedSlots);
+    for (TimeSlot timeSlot : sortedSlots) {
       if (daysFilter.get(timeSlot.getDayOfWeek())) {
         if (s == null) {
           s = timeSlot.getDescription();
@@ -50,6 +61,30 @@
       return "";
     }
   }
+
+  public Integer getKey() {
+    return key;
+  }
+
+  public Integer getRevision() {
+    return revision;
+  }
+
+  public List<TimeSlot> getTimeSlots() {
+    return timeSlots;
+  }
+
+  public void setKey(Integer key) {
+    this.key = key;
+  }
+
+  public void setRevision(Integer revision) {
+    this.revision = revision;
+  }
+
+  public void setTimeSlots(List<TimeSlot> timeSlots) {
+    this.timeSlots = new ArrayList<TimeSlot>(timeSlots);
+  }

   @Override
   public String toString() {
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/TimeSlot.java Fri Jul 23 15:42:40 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/TimeSlot.java Tue Jan 25 08:05:30 2011
@@ -48,7 +48,11 @@
         return -1;
       } else if (startMinutes > o.startMinutes) {
         return 1;
-      }
+      } else if (endMinutes < o.endMinutes) {
+        return -1;
+      } else if (endMinutes > o.endMinutes) {
+        return 1;
+      }
     }

     return 0;
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/PersonFuzzer.java Fri Sep 24 12:10:50 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/PersonFuzzer.java Tue Jan 25 08:05:30 2011
@@ -17,10 +17,8 @@

 import com.google.gwt.sample.dynatablerf.domain.Person;
 import com.google.gwt.sample.dynatablerf.domain.Schedule;
-import com.google.gwt.sample.dynatablerf.domain.TimeSlot;

 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Random;

@@ -29,6 +27,8 @@
  */
 class PersonFuzzer {

+  public static final int MAX_PEOPLE = 100;
+
   private static final String[] FIRST_NAMES = new String[] {
       "Inman", "Sally", "Omar", "Teddy", "Jimmy", "Cathy", "Barney", "Fred",
       "Eddie", "Carlos"};
@@ -42,16 +42,16 @@
"Basketball", "Computer Science", "Statistics", "Materials Engineering",
       "English Literature", "Geology"};

-  private static final int CLASS_LENGTH_MINS = 50;
-
-  private static final int MAX_PEOPLE = 100;
-
-  private static final int MAX_SCHED_ENTRIES = 5;
-
-  private static final int MIN_SCHED_ENTRIES = 1;
-
   private static final int STUDENTS_PER_PROF = 5;

+ public static List<Schedule> collectSchedules(List<Person> randomPeople) {
+    List<Schedule> toReturn = new ArrayList<Schedule>();
+    for (Person person : randomPeople) {
+      toReturn.add(person.getClassSchedule());
+    }
+    return toReturn;
+  }
+
   public static Person generatePerson() {
     Random rnd = new Random();
     Person toReturn = generateRandomPerson(rnd);
@@ -71,9 +71,7 @@
   }

   private static Person generateRandomPerson(Random rnd) {
-    // 1 out of every so many people is a prof.
-    //
-    if (rnd.nextInt(STUDENTS_PER_PROF) == 1) {
+    if (isChosenAsProfessor(rnd)) {
       return generateRandomProfessor(rnd);
     } else {
       return generateRandomStudent(rnd);
@@ -90,34 +88,10 @@
     String subject = pickRandomString(rnd, SUBJECTS);
     prof.setDescription("Professor of " + subject);

-    generateRandomSchedule(rnd, prof.getClassSchedule());
+    prof.setClassSchedule(ScheduleFuzzer.generateRandomSchedule(rnd));

     return prof;
   }
-
-  private static void generateRandomSchedule(Random rnd, Schedule sched) {
-    int range = MAX_SCHED_ENTRIES - MIN_SCHED_ENTRIES + 1;
-    int howMany = MIN_SCHED_ENTRIES + rnd.nextInt(range);
-
-    TimeSlot[] timeSlots = new TimeSlot[howMany];
-
-    for (int i = 0; i < howMany; ++i) {
-      int startHrs = 8 + rnd.nextInt(9); // 8 am - 5 pm
-      int startMins = 15 * rnd.nextInt(4); // on the hour or some quarter
-      int dayOfWeek = 1 + rnd.nextInt(5); // Mon - Fri
-
-      int absStartMins = 60 * startHrs + startMins; // convert to minutes
-      int absStopMins = absStartMins + CLASS_LENGTH_MINS;
-
-      timeSlots[i] = new TimeSlot(dayOfWeek, absStartMins, absStopMins);
-    }
-
-    Arrays.sort(timeSlots);
-
-    for (int i = 0; i < howMany; ++i) {
-      sched.addTimeSlot(timeSlots[i]);
-    }
-  }

   private static Person generateRandomStudent(Random rnd) {
     Person student = new Person();
@@ -129,13 +103,18 @@
     String subject = pickRandomString(rnd, SUBJECTS);
     student.setDescription("Majoring in " + subject);

-    generateRandomSchedule(rnd, student.getClassSchedule());
+    student.setClassSchedule(ScheduleFuzzer.generateRandomSchedule(rnd));

     return student;
   }
+
+  private static boolean isChosenAsProfessor(Random rnd) {
+    return rnd.nextInt(STUDENTS_PER_PROF + 1) == 0;
+  }

   private static String pickRandomString(Random rnd, String[] a) {
     int i = rnd.nextInt(a.length);
     return a[i];
   }
-}
+
+}
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/PersonSource.java Thu Nov 18 13:15:58 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/PersonSource.java Tue Jan 25 08:05:30 2011
@@ -19,6 +19,7 @@
import static com.google.gwt.sample.dynatablerf.shared.DynaTableRequestFactory.SchoolCalendarRequest.NO_DAYS;

 import com.google.gwt.sample.dynatablerf.domain.Person;
+import com.google.gwt.sample.dynatablerf.domain.Schedule;

 import java.util.ArrayList;
 import java.util.Collections;
@@ -36,6 +37,11 @@
 public abstract class PersonSource {
   static class Backing extends PersonSource {
     private Long serial = 0L;
+    private final ScheduleSource scheduleStore;
+
+    public Backing(ScheduleSource scheduleStore) {
+      this.scheduleStore = scheduleStore;
+    }

     @Override
     public int countPeople() {
@@ -94,6 +100,9 @@
         person.setId(Long.toString(++serial));
       }
       person.setVersion(person.getVersion() + 1);
+      if (person.getClassSchedule() != null) {
+        scheduleStore.persist(person.getClassSchedule());
+      }
       Person existing = people.get(person.getId());
       if (existing != null) {
         existing.copyFrom(person);
@@ -105,9 +114,11 @@

   static class CopyOnRead extends PersonSource {
     private final PersonSource backingStore;
-
-    public CopyOnRead(PersonSource backingStore) {
+    private final ScheduleSource scheduleStore;
+
+ public CopyOnRead(PersonSource backingStore, ScheduleSource scheduleStore) {
       this.backingStore = backingStore;
+      this.scheduleStore = scheduleStore;
     }

     @Override
@@ -122,6 +133,10 @@
         toReturn = backingStore.findPerson(id);
         if (toReturn != null) {
           toReturn = toReturn.makeCopy();
+
+          Integer scheduleKey = toReturn.getClassSchedule().getKey();
+          Schedule scheduleCopy = scheduleStore.find(scheduleKey);
+          toReturn.setClassSchedule(scheduleCopy);
         }
         people.put(id, toReturn);
       }
@@ -150,8 +165,8 @@
   /**
    * Create a PersonSource that will act directly on the given list.
    */
-  public static PersonSource of(List<Person> people) {
-    PersonSource backing = new Backing();
+ public static PersonSource of(List<Person> people, ScheduleSource schedules) {
+    PersonSource backing = new Backing(schedules);
     for (Person person : people) {
       backing.persist(person);
     }
@@ -162,8 +177,8 @@
* Create a PersonSource that will read through to the given source and make
    * copies of any objects that are requested.
    */
-  public static PersonSource of(PersonSource backing) {
-    return new CopyOnRead(backing);
+ public static PersonSource of(PersonSource backing, ScheduleSource scheduleBacking) {
+    return new CopyOnRead(backing, scheduleBacking);
   }

   final Map<String, Person> people = new LinkedHashMap<String, Person>();
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/SchoolCalendarService.java Thu Nov 18 13:15:58 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/SchoolCalendarService.java Tue Jan 25 08:05:30 2011
@@ -36,6 +36,7 @@
 public class SchoolCalendarService implements Filter {

private static final ThreadLocal<PersonSource> PERSON_SOURCE = new ThreadLocal<PersonSource>(); + static final ThreadLocal<ScheduleSource> SCHEDULE_SOURCE = new ThreadLocal<ScheduleSource>();

   public static Person createPerson() {
     checkPersonSource();
@@ -74,6 +75,7 @@
   }

   private PersonSource backingStore;
+  private ScheduleSource scheduleStore;

   public void destroy() {
   }
@@ -81,10 +83,13 @@
   public void doFilter(ServletRequest req, ServletResponse resp,
       FilterChain chain) throws IOException, ServletException {
     try {
-      PERSON_SOURCE.set(PersonSource.of(backingStore));
+      ScheduleSource scheduleBacking = ScheduleSource.of(scheduleStore);
+      SCHEDULE_SOURCE.set(scheduleBacking);
+      PERSON_SOURCE.set(PersonSource.of(backingStore, scheduleBacking));
       chain.doFilter(req, resp);
     } finally {
       PERSON_SOURCE.set(null);
+      SCHEDULE_SOURCE.set(null);
     }
   }

@@ -92,7 +97,9 @@
     backingStore = (PersonSource) config.getServletContext().getAttribute(
         SchoolCalendarService.class.getName());
     if (backingStore == null) {
-      backingStore = PersonSource.of(PersonFuzzer.generateRandomPeople());
+      List<Person> randomPeople = PersonFuzzer.generateRandomPeople();
+ scheduleStore = ScheduleSource.of(PersonFuzzer.collectSchedules(randomPeople));
+      backingStore = PersonSource.of(randomPeople, scheduleStore);
       config.getServletContext().setAttribute(
           SchoolCalendarService.class.getName(), backingStore);
     }
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/DynaTableRequestFactory.java Thu Nov 18 13:15:58 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/DynaTableRequestFactory.java Tue Jan 25 08:05:30 2011
@@ -22,6 +22,8 @@
 import com.google.gwt.requestfactory.shared.RequestFactory;
 import com.google.gwt.requestfactory.shared.Service;
 import com.google.gwt.sample.dynatablerf.domain.Person;
+import com.google.gwt.sample.dynatablerf.server.ScheduleService;
+import com.google.gwt.sample.dynatablerf.server.ScheduleServiceLocator;
 import com.google.gwt.sample.dynatablerf.server.SchoolCalendarService;

 import java.util.Arrays;
@@ -56,10 +58,21 @@

     Request<PersonProxy> getRandomPerson();
   }
+
+  /**
+   * Source of request objects for Schedule entities.
+   */
+ @Service(value = ScheduleService.class, locator = ScheduleServiceLocator.class)
+  interface ScheduleRequest extends RequestContext {
+ Request<TimeSlotProxy> createTimeSlot(int zeroBasedDayOfWeek, int startMinutes,
+        int endMinutes);
+  }

   LoggingRequest loggingRequest();

   PersonRequest personRequest();
+
+  ScheduleRequest scheduleRequest();

   SchoolCalendarRequest schoolCalendarRequest();
 }
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/PersonProxy.java Fri Oct 8 13:01:19 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/PersonProxy.java Tue Jan 25 08:05:30 2011
@@ -28,6 +28,8 @@

   AddressProxy getAddress();

+  ScheduleProxy getClassSchedule();
+
   String getDescription();

   PersonProxy getMentor();
@@ -36,10 +38,12 @@

   String getNote();

-  String getSchedule();
-
+  String getScheduleDescription();
+
   void setAddress(AddressProxy address);

+  void setClassSchedule(ScheduleProxy schedule);
+
   void setDescription(String description);

   void setMentor(PersonProxy mentor);
@@ -47,6 +51,6 @@
   void setName(String name);

   void setNote(String note);
-
+
   EntityProxyId<PersonProxy> stableId();
 }

--
http://groups.google.com/group/Google-Web-Toolkit-Contributors

Reply via email to