Adds partial support for related fields to RequestFactory. Right now, getters of such fields always return null.
Review at http://gwt-code-reviews.appspot.com/741801
Review by: rjrjr@google.com
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8541 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpensesMobile.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpensesMobile.java
index b4b78fc..14fed57 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpensesMobile.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpensesMobile.java
@@ -23,12 +23,14 @@
import com.google.gwt.requestfactory.shared.Receiver;
import com.google.gwt.requestfactory.shared.RequestEvent;
import com.google.gwt.requestfactory.shared.UserInformationRecord;
+import com.google.gwt.sample.expenses.gwt.request.EmployeeRecord;
import com.google.gwt.sample.expenses.gwt.request.ExpensesRequestFactory;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.Window.Location;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.valuestore.shared.SyncResult;
+import com.google.gwt.valuestore.shared.Value;
import java.util.Set;
@@ -44,7 +46,7 @@
/**
* TODO(jgw): Put this some place more sensible.
- *
+ *
* @param amount the amount in dollars
*/
public static String formatCurrency(double amount) {
@@ -89,24 +91,36 @@
}
final HandlerManager eventBus = new HandlerManager(null);
- final ExpensesRequestFactory requestFactory = GWT.create(ExpensesRequestFactory.class);
+ final ExpensesRequestFactory requestFactory = GWT.create(
+ ExpensesRequestFactory.class);
requestFactory.init(eventBus);
- final ExpensesMobileShell shell = new ExpensesMobileShell(eventBus,
- requestFactory, employeeId);
- RootPanel.get().add(shell);
-
- // Check for Authentication failures or mismatches
- eventBus.addHandler(RequestEvent.TYPE, new AuthenticationFailureHandler());
+ final long finalEmployeeId = employeeId;
+ requestFactory.employeeRequest().findEmployee(Value.of(employeeId)).fire(
+ new Receiver<EmployeeRecord>() {
+ @Override
+ public void onSuccess(EmployeeRecord employee,
+ Set<SyncResult> syncResults) {
+ final ExpensesMobileShell shell = new ExpensesMobileShell(eventBus,
+ requestFactory, employee);
+ RootPanel.get().add(shell);
- // Add a login widget to the page
- final LoginWidget login = shell.getLoginWidget();
- Receiver<UserInformationRecord> receiver = new Receiver<UserInformationRecord>() {
- public void onSuccess(UserInformationRecord userInformationRecord, Set<SyncResult> syncResults) {
- login.setUserInformation(userInformationRecord);
- }
- };
- requestFactory.userInformationRequest().getCurrentUserInformation(
- Location.getHref()).fire(receiver);
+ // Check for Authentication failures or mismatches
+ eventBus.addHandler(RequestEvent.TYPE,
+ new AuthenticationFailureHandler());
+
+ // Add a login widget to the page
+ final LoginWidget login = shell.getLoginWidget();
+ Receiver<UserInformationRecord> receiver
+ = new Receiver<UserInformationRecord>() {
+ public void onSuccess(UserInformationRecord userInformationRecord,
+ Set<SyncResult> syncResults) {
+ login.setUserInformation(userInformationRecord);
+ }
+ };
+ requestFactory.userInformationRequest().getCurrentUserInformation(
+ Location.getHref()).fire(receiver);
+ }
+ } );
}
}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpensesMobileShell.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpensesMobileShell.java
index b4d0ac6..d540149 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpensesMobileShell.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpensesMobileShell.java
@@ -20,6 +20,7 @@
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.requestfactory.client.LoginWidget;
+import com.google.gwt.sample.expenses.gwt.request.EmployeeRecord;
import com.google.gwt.sample.expenses.gwt.request.ExpenseRecord;
import com.google.gwt.sample.expenses.gwt.request.ExpensesRequestFactory;
import com.google.gwt.sample.expenses.gwt.request.ReportRecord;
@@ -52,16 +53,16 @@
private MobileExpenseEntry expenseEntry;
private MobileReportEntry reportEntry;
- private final long employeeId;
+ private final EmployeeRecord employee;
private final HandlerManager eventBus;
private final ExpensesRequestFactory requestFactory;
private ArrayList<MobilePage> pages = new ArrayList<MobilePage>();
public ExpensesMobileShell(HandlerManager eventBus,
- ExpensesRequestFactory requestFactory, long employeeId) {
+ ExpensesRequestFactory requestFactory, EmployeeRecord employee) {
this.eventBus = eventBus;
this.requestFactory = requestFactory;
- this.employeeId = employeeId;
+ this.employee = employee;
initWidget(BINDER.createAndBindUi(this));
showReportList();
@@ -151,11 +152,11 @@
pushPage(reportEntry);
}
- private void showExpenseList(ReportRecord report) {
+ private void showExpenseList(final ReportRecord report) {
if (expenseList == null) {
expenseList = new MobileExpenseList(new MobileExpenseList.Listener() {
- public void onCreateExpense(Long reportId) {
- showNewExpenseEntry(reportId);
+ public void onCreateExpense(ReportRecord report) {
+ showNewExpenseEntry(report);
}
public void onEditReport(ReportRecord report) {
@@ -172,7 +173,7 @@
pushPage(expenseList);
}
- private void showNewExpenseEntry(Long reportId) {
+ private void showNewExpenseEntry(ReportRecord report) {
if (expenseEntry == null) {
expenseEntry = new MobileExpenseEntry(new MobileExpenseEntry.Listener() {
public void onExpenseUpdated() {
@@ -181,11 +182,11 @@
}, requestFactory);
}
- expenseEntry.create(reportId);
+ expenseEntry.create(report);
pushPage(expenseEntry);
}
- private void showNewReportEntry(Long reporterId) {
+ private void showNewReportEntry(EmployeeRecord reporter) {
if (reportEntry == null) {
reportEntry = new MobileReportEntry(new MobileReportEntry.Listener() {
public void onReportUpdated() {
@@ -194,7 +195,7 @@
}, requestFactory);
}
- reportEntry.create(reporterId);
+ reportEntry.create(reporter);
pushPage(reportEntry);
}
@@ -223,14 +224,14 @@
private void showReportList() {
if (reportList == null) {
reportList = new MobileReportList(new MobileReportList.Listener() {
- public void onCreateReport(Long reporterId) {
- showNewReportEntry(reporterId);
+ public void onCreateReport(EmployeeRecord reporter) {
+ showNewReportEntry(reporter);
}
public void onReportSelected(ReportRecord report) {
showExpenseList(report);
}
- }, requestFactory, employeeId);
+ }, requestFactory, employee);
}
pushPage(reportList);
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/MobileExpenseEntry.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/MobileExpenseEntry.java
index 4c6f3b5..7cd556b 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/MobileExpenseEntry.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/MobileExpenseEntry.java
@@ -21,6 +21,7 @@
import com.google.gwt.requestfactory.shared.RequestObject;
import com.google.gwt.sample.expenses.gwt.request.ExpenseRecord;
import com.google.gwt.sample.expenses.gwt.request.ExpensesRequestFactory;
+import com.google.gwt.sample.expenses.gwt.request.ReportRecord;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Composite;
@@ -72,11 +73,11 @@
return this;
}
- public void create(Long reportId) {
+ public void create(ReportRecord report) {
expense = (ExpenseRecord) requestFactory.create(ExpenseRecord.class);
requestObject = requestFactory.expenseRequest().persist(expense);
ExpenseRecord editableExpense = requestObject.edit(expense);
- editableExpense.setReportId(reportId);
+ editableExpense.setReport(report);
displayExpense();
}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/MobileExpenseList.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/MobileExpenseList.java
index d372a55..fb9a08c 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/MobileExpenseList.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/MobileExpenseList.java
@@ -51,7 +51,7 @@
* TODO: doc.
*/
public interface Listener {
- void onCreateExpense(Long reportId);
+ void onCreateExpense(ReportRecord report);
void onEditReport(ReportRecord report);
@@ -169,7 +169,7 @@
}
public void onAdd() {
- listener.onCreateExpense(report.getId());
+ listener.onCreateExpense(report);
}
public void onCustom() {
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/MobileReportEntry.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/MobileReportEntry.java
index a08461d..5791343 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/MobileReportEntry.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/MobileReportEntry.java
@@ -19,6 +19,7 @@
import com.google.gwt.dom.client.Element;
import com.google.gwt.requestfactory.shared.Receiver;
import com.google.gwt.requestfactory.shared.RequestObject;
+import com.google.gwt.sample.expenses.gwt.request.EmployeeRecord;
import com.google.gwt.sample.expenses.gwt.request.ExpensesRequestFactory;
import com.google.gwt.sample.expenses.gwt.request.ReportRecord;
import com.google.gwt.uibinder.client.UiBinder;
@@ -81,11 +82,11 @@
return this;
}
- public void create(Long reporterId) {
+ public void create(EmployeeRecord reporter) {
report = (ReportRecord) requestFactory.create(ReportRecord.class);
requestObject = requestFactory.reportRequest().persist(report);
ReportRecord editableReport = requestObject.edit(report);
- report.setReporterKey(reporterId);
+ editableReport.setReporter(reporter);
displayReport();
}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/MobileReportList.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/MobileReportList.java
index be83685..5cda91f 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/MobileReportList.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/MobileReportList.java
@@ -17,6 +17,7 @@
import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.requestfactory.shared.Receiver;
+import com.google.gwt.sample.expenses.gwt.request.EmployeeRecord;
import com.google.gwt.sample.expenses.gwt.request.ExpensesRequestFactory;
import com.google.gwt.sample.expenses.gwt.request.ReportRecord;
import com.google.gwt.user.cellview.client.CellList;
@@ -43,7 +44,7 @@
* TODO: doc.
*/
public interface Listener {
- void onCreateReport(Long reporterId);
+ void onCreateReport(EmployeeRecord reporter);
void onReportSelected(ReportRecord report);
}
@@ -53,7 +54,7 @@
*/
private Receiver<List<ReportRecord>> lastReceiver;
- private final Long employeeId;
+ private final EmployeeRecord employee;
private final Listener listener;
private final CellList<ReportRecord> reportList;
private final AsyncListViewAdapter<ReportRecord> reportAdapter;
@@ -61,10 +62,10 @@
private final ExpensesRequestFactory requestFactory;
public MobileReportList(final Listener listener,
- final ExpensesRequestFactory requestFactory, long employeeId) {
+ final ExpensesRequestFactory requestFactory, EmployeeRecord employee) {
this.listener = listener;
this.requestFactory = requestFactory;
- this.employeeId = new Long(employeeId);
+ this.employee = employee;
reportAdapter = new AsyncListViewAdapter<ReportRecord>() {
@Override
protected void onRangeChanged(HasData<ReportRecord> view) {
@@ -116,7 +117,7 @@
}
public void onAdd() {
- listener.onCreateReport(employeeId);
+ listener.onCreateReport(employee);
}
public void onCustom() {
@@ -147,7 +148,7 @@
reportAdapter.updateViewData(0, size, newValues);
}
};
- requestFactory.reportRequest().findReportEntriesBySearch(employeeId, "",
+ requestFactory.reportRequest().findReportEntriesBySearch(employee.getId(), "",
"", ReportRecord.created.getName() + " DESC", 0, 25).forProperties(
getReportColumns()).fire(lastReceiver);
}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/EmployeeRecord.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/EmployeeRecord.java
index 21ac793..ac47217 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/EmployeeRecord.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/EmployeeRecord.java
@@ -32,8 +32,8 @@
Property<String> displayName = new Property<String>("displayName", "Display Name",
String.class);
Property<String> password = new Property<String>("password", "Password", String.class);
- Property<Long> supervisorKey = new Property<Long>("supervisorKey", "Supervisor Key",
- Long.class);
+ Property<EmployeeRecord> supervisor = new Property<EmployeeRecord>("supervisor", "Supervisor",
+ EmployeeRecord.class);
Property<String> department = new Property<String>("department", "Department", String.class);
@@ -43,7 +43,7 @@
String getPassword();
- Long getSupervisorKey();
+ EmployeeRecord getSupervisor();
String getUserName();
@@ -55,7 +55,7 @@
void setPassword(String password);
- void setSupervisorKey(Long supervisorKey);
+ void setSupervisor(EmployeeRecord supervisor);
void setUserName(String userName);
}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ExpenseRecord.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ExpenseRecord.java
index 268c569..8b9a154 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ExpenseRecord.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ExpenseRecord.java
@@ -36,7 +36,7 @@
Property<Date> created = new Property<Date>("created", "Created", Date.class);
Property<String> description = new Property<String>("description", "Description", String.class);
Property<String> reasonDenied = new Property<String>("reasonDenied", "Reason Denied", String.class);
- Property<Long> reportId = new Property<Long>("reportId", "Report Id", Long.class);
+ Property<ReportRecord> report = new Property<ReportRecord>("report", "Report", ReportRecord.class);
Double getAmount();
@@ -50,7 +50,7 @@
String getReasonDenied();
- Long getReportId();
+ ReportRecord getReport();
boolean isChanged();
@@ -66,5 +66,5 @@
void setReasonDenied(String reasonDenied);
- void setReportId(Long reportId);
+ void setReport(ReportRecord report);
}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ReportRecord.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ReportRecord.java
index d98c4b9..7652744 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ReportRecord.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ReportRecord.java
@@ -30,16 +30,16 @@
@DataTransferObject(com.google.gwt.sample.expenses.server.domain.Report.class)
public interface ReportRecord extends Record {
- Property<Long> approvedSupervisorKey = new Property<Long>("approvedSupervisorKey", "Approved Supervisor Key",
- Long.class);
+ Property<EmployeeRecord> approvedSupervisor = new Property<EmployeeRecord>("approvedSupervisor", "Approved Supervisor Key",
+ EmployeeRecord.class);
Property<Date> created = new Property<Date>("created", "Created", Date.class);
Property<String> department = new Property<String>("department", "Department", String.class);
Property<String> notes = new Property<String>("notes", "Notes", String.class);
Property<String> purpose = new Property<String>("purpose", "Purpose", String.class);
- Property<Long> reporterKey = new Property<Long>("reporterKey", "Reporter Key",
- Long.class);
+ Property<EmployeeRecord> reporter = new Property<EmployeeRecord>("reporter", "Reporter",
+ EmployeeRecord.class);
- Long getApprovedSupervisorKey();
+ EmployeeRecord getApprovedSupervisor();
Date getCreated();
@@ -49,11 +49,11 @@
String getPurpose();
- Long getReporterKey();
+ EmployeeRecord getReporter();
boolean isChanged();
- void setApprovedSupervisorKey(Long approvedSupervisorKey);
+ void setApprovedSupervisor(EmployeeRecord approvedSupervisor);
void setCreated(Date created);
@@ -63,5 +63,5 @@
void setPurpose(String purpose);
- void setReporterKey(Long reporterKey);
+ void setReporter(EmployeeRecord reporter);
}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/server/ReportGeneratorMain.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/server/ReportGeneratorMain.java
index 6aa0395..776f790 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/server/ReportGeneratorMain.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/server/ReportGeneratorMain.java
@@ -55,8 +55,8 @@
repWriter = new PrintWriter(dir + "/Report.csv");
expWriter = new PrintWriter(dir + "/Expense.csv");
empWriter.println("userName,displayName,supervisorKey,VERSION,key,department,password");
- repWriter.println("created,notes,VERSION,approvedSupervisorKey,key,reporterKey,purposeLowerCase,purpose,department");
- expWriter.println("category,description,reasonDenied,amount,VERSION,reportId,key,created,approval");
+ repWriter.println("created,notes,VERSION,approvedSupervisor,key,reporter,purposeLowerCase,purpose,department");
+ expWriter.println("category,description,reasonDenied,amount,VERSION,report,key,created,approval");
} catch (IOException e) {
throw new RuntimeException(e);
}
@@ -95,7 +95,7 @@
@Override
public long storeExpense(ExpenseDTO expense) {
long id = allids++;
- // category,description,reasonDenied,amount,VERSION,reportId,key,created,approval"
+ // category,description,reasonDenied,amount,VERSION,report,key,created,approval"
expWriter.println(expense.category + "," + expense.description + ",," + expense.amount + ","
+ VERSION + "," + expense.reportId + "," + id + "," + dateToString(expense.created)
+ ",");
@@ -105,7 +105,7 @@
@Override
public long storeReport(ReportDTO report) {
long id = allids++;
- // created,notes,VERSION,approvedSupervisorKey,key,reporterKey,purposeLowerCase,purpose,department
+ // created,notes,VERSION,approvedSupervisor,key,reporter,purposeLowerCase,purpose,department
repWriter.println(dateToString(report.created) + ",\"" + report.notes + "\"," + VERSION + ","
+ report.approvedSupervisorKey + "," + id + "," + report.reporterKey + ",\""
+ report.purpose.toLowerCase() + "\",\"" + report.purpose + "\"," + report.department);
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/employee/EmployeeDetailsActivity.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/employee/EmployeeDetailsActivity.java
index 6cb007c..1b73e9f 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/employee/EmployeeDetailsActivity.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/employee/EmployeeDetailsActivity.java
@@ -120,6 +120,6 @@
}
};
- requests.employeeRequest().findEmployee(Value.of(id)).fire(callback);
+ requests.employeeRequest().findEmployee(Value.of(id)).with("supervisor").fire(callback);
}
}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/employee/EmployeeDetailsView.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/employee/EmployeeDetailsView.java
index a71f8ed..6bc8acb 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/employee/EmployeeDetailsView.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/employee/EmployeeDetailsView.java
@@ -52,7 +52,7 @@
@UiField
SpanElement password;
@UiField
- SpanElement supervisorKey;
+ SpanElement supervisor;
@UiField
HasClickHandlers edit;
@UiField
@@ -95,7 +95,7 @@
displayName.setInnerText(record.getDisplayName());
userName.setInnerText(record.getUserName());
password.setInnerText(record.getPassword());
- supervisorKey.setInnerText(String.valueOf(record.getSupervisorKey()));
+ supervisor.setInnerText(String.valueOf(record.getSupervisor() == null ? null : record.getSupervisor().getId()));
idSpan.setInnerText(record.getId().toString());
versionSpan.setInnerText(record.getVersion().toString());
}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/employee/EmployeeDetailsView.ui.xml b/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/employee/EmployeeDetailsView.ui.xml
index bc70a28..2f473d9 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/employee/EmployeeDetailsView.ui.xml
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/employee/EmployeeDetailsView.ui.xml
@@ -35,7 +35,7 @@
<table class='{style.fields}'>
<tr><td><div class='{style.label}'>Display Name:</div></td><td><span ui:field='displayName'></span></td></tr>
<tr><td><div class='{style.label}'>User Name:</div></td><td><span ui:field='userName'></span></td></tr>
- <tr><td><div class='{style.label}'>Supervisor Key:</div></td><td><span ui:field='supervisorKey'></span></td></tr>
+ <tr><td><div class='{style.label}'>Supervisor:</div></td><td><span ui:field='supervisor'></span></td></tr>
<tr><td><div class='{style.label}'>Password:</div></td><td><span ui:field='password'></span></td></tr>
</table>
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/employee/EmployeeListView.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/employee/EmployeeListView.java
index f50fe57..e481300 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/employee/EmployeeListView.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/employee/EmployeeListView.java
@@ -15,7 +15,7 @@
*/
package com.google.gwt.sample.expenses.gwt.ui.employee;
-import com.google.gwt.app.client.LongRenderer;
+import com.google.gwt.app.client.ProxyIdRenderer;
import com.google.gwt.app.place.AbstractRecordListView;
import com.google.gwt.app.place.PropertyColumn;
import com.google.gwt.core.client.GWT;
@@ -53,8 +53,8 @@
columns.add(PropertyColumn.<EmployeeRecord> getStringPropertyColumn(EmployeeRecord.userName));
columns.add(PropertyColumn.<EmployeeRecord> getStringPropertyColumn(EmployeeRecord.displayName));
columns.add(PropertyColumn.<EmployeeRecord> getStringPropertyColumn(EmployeeRecord.password));
- columns.add(new PropertyColumn<EmployeeRecord, java.lang.Long>(
- EmployeeRecord.supervisorKey, LongRenderer.instance()));
+ columns.add(new PropertyColumn<EmployeeRecord, EmployeeRecord>(
+ EmployeeRecord.supervisor, ProxyIdRenderer.<EmployeeRecord>instance()));
return columns;
}
}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/report/ReportDetailsActivity.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/report/ReportDetailsActivity.java
index a7c1163..d7c7770 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/report/ReportDetailsActivity.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/report/ReportDetailsActivity.java
@@ -119,6 +119,7 @@
display.showActivityWidget(view);
}
};
- requests.reportRequest().findReport(Value.of(id)).fire(callback);
+ requests.reportRequest().findReport(Value.of(id)).with(
+ "reporter", "approvedSupervisor").fire(callback);
}
}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/report/ReportDetailsView.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/report/ReportDetailsView.java
index 11190e9..7c2fed6 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/report/ReportDetailsView.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/report/ReportDetailsView.java
@@ -99,7 +99,7 @@
created.setInnerText(new DateTimeFormatRenderer().render(record.getCreated()));
idSpan.setInnerText(record.getId().toString());
versionSpan.setInnerText(record.getVersion().toString());
- reporterKey.setInnerText(String.valueOf(record.getReporterKey()));
- approvedSupervisorKey.setInnerText(String.valueOf(record.getApprovedSupervisorKey()));
+ reporterKey.setInnerText(String.valueOf(record.getReporter()));
+ approvedSupervisorKey.setInnerText(String.valueOf(record.getApprovedSupervisor()));
}
}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/report/ReportListView.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/report/ReportListView.java
index 559886c..0c4c0ac 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/report/ReportListView.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/report/ReportListView.java
@@ -15,12 +15,13 @@
*/
package com.google.gwt.sample.expenses.gwt.ui.report;
-import com.google.gwt.app.client.LongRenderer;
+import com.google.gwt.app.client.ProxyIdRenderer;
import com.google.gwt.app.place.AbstractRecordListView;
import com.google.gwt.app.place.PropertyColumn;
import com.google.gwt.core.client.GWT;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.DateTimeFormatRenderer;
+import com.google.gwt.sample.expenses.gwt.request.EmployeeRecord;
import com.google.gwt.sample.expenses.gwt.request.ReportRecord;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
@@ -58,10 +59,10 @@
columns.add(new PropertyColumn<ReportRecord, Date>(ReportRecord.created,
new DateTimeFormatRenderer(DateTimeFormat.getShortDateFormat())));
columns.add(PropertyColumn.<ReportRecord> getStringPropertyColumn(ReportRecord.purpose));
- columns.add(new PropertyColumn<ReportRecord, java.lang.Long>(
- ReportRecord.reporterKey, LongRenderer.instance()));
- columns.add(new PropertyColumn<ReportRecord, java.lang.Long>(
- ReportRecord.approvedSupervisorKey, LongRenderer.instance()));
+ columns.add(new PropertyColumn<ReportRecord, EmployeeRecord>(
+ ReportRecord.reporter, ProxyIdRenderer.<EmployeeRecord>instance()));
+ columns.add(new PropertyColumn<ReportRecord, EmployeeRecord>(
+ ReportRecord.approvedSupervisor, ProxyIdRenderer.<EmployeeRecord>instance()));
return columns;
}
}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/server/DataGenerator.java b/bikeshed/src/com/google/gwt/sample/expenses/server/DataGenerator.java
index fdf218c..ef2fde3 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/server/DataGenerator.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/server/DataGenerator.java
@@ -206,7 +206,7 @@
private JSONObject getExpenseAsJson(Expense expense) throws JSONException {
JSONObject jsonObject = new JSONObject();
jsonObject.put("id", "" + expense.getId());
- jsonObject.put("reportId", expense.getReportId());
+ jsonObject.put("report", expense.getReportId());
jsonObject.put("description", expense.getDescription());
jsonObject.put("created", expense.getCreated().getTime());
jsonObject.put("amount", expense.getAmount());
@@ -239,12 +239,12 @@
jsonObject.put("id", "" + report.getId());
jsonObject.put("created", report.getCreated().getTime());
jsonObject.put("department", report.getDepartment());
- jsonObject.put("reporterKey", report.getReporterKey());
+ jsonObject.put("reporter", report.getReporterKey());
jsonObject.put("purpose", report.getPurpose());
jsonObject.put("notes", report.getNotes());
Long approvedSupervisorKey = report.getApprovedSupervisorKey();
if (approvedSupervisorKey != null) {
- jsonObject.put("approvedSupervisorKey", "" + report.getApprovedSupervisorKey());
+ jsonObject.put("approvedSupervisor", "" + report.getApprovedSupervisorKey());
}
return jsonObject;
}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Employee.java b/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Employee.java
index 28b2cdf..c7802b0 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Employee.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Employee.java
@@ -24,6 +24,7 @@
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Query;
+import javax.persistence.Transient;
import javax.persistence.Version;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@@ -138,6 +139,9 @@
@Column(name = "version")
private Integer version;
+ @Transient
+ private Employee supervisor;
+
public String getDepartment() {
return department;
}
@@ -154,6 +158,10 @@
return this.password;
}
+ public Employee getSupervisor() {
+ return supervisorKey != null ? findEmployee(supervisorKey) : null;
+ }
+
public Long getSupervisorKey() {
return supervisorKey;
}
@@ -201,6 +209,10 @@
this.password = password;
}
+ public void setSupervisor(Employee supervisor) {
+ supervisorKey = supervisor == null ? null : supervisor.getId();
+ }
+
public void setSupervisorKey(Long supervisorKey) {
this.supervisorKey = supervisorKey;
}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Expense.java b/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Expense.java
index d0034a0..4ca3206 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Expense.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Expense.java
@@ -26,6 +26,7 @@
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Query;
+import javax.persistence.Transient;
import javax.persistence.Version;
/**
@@ -59,7 +60,7 @@
em.close();
}
}
-
+
public static Expense findExpense(Long id) {
if (id == null) {
return null;
@@ -71,7 +72,7 @@
em.close();
}
}
-
+
@SuppressWarnings("unchecked")
public static List<Expense> findExpensesByReport(Long reportId) {
EntityManager em = entityManager();
@@ -98,17 +99,21 @@
private String category;
private Date created;
-
+
private String description;
-
+
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
-
+
private String reasonDenied;
-
+
+ @Transient
+ private Report report;
+
// @JoinColumn
+
private Long reportId;
@Version
@@ -122,7 +127,7 @@
public String getApproval() {
return this.approval;
}
-
+
public String getCategory() {
return this.category;
}
@@ -134,15 +139,19 @@
public String getDescription() {
return description;
}
-
+
public Long getId() {
return this.id;
}
-
+
public String getReasonDenied() {
return this.reasonDenied;
}
+ public Report getReporter() {
+ return reportId != null ? Report.findReport(reportId) : null;
+ }
+
public Long getReportId() {
return this.reportId;
}
@@ -169,11 +178,11 @@
em.close();
}
}
-
+
public void setAmount(Double amount) {
this.amount = amount;
}
-
+
public void setApproval(String approval) {
this.approval = approval;
}
@@ -198,8 +207,12 @@
this.reasonDenied = reasonDenied;
}
+ public void setReport(Report report) {
+ reportId = report == null ? null : report.getId();
+ }
+
public void setReportId(Long reportId) {
- this.reportId = reportId;
+ this.reportId = reportId;
}
public void setVersion(Integer version) {
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Report.java b/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Report.java
index 7d7378d..ac574e1 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Report.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Report.java
@@ -36,6 +36,7 @@
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Query;
+import javax.persistence.Transient;
import javax.persistence.Version;
/**
@@ -66,8 +67,8 @@
String startsWith) {
EntityManager em = entityManager();
try {
- Query query = queryReportsBySearch(em, employeeId, department,
- startsWith, null, true);
+ Query query = queryReportsBySearch(em, employeeId, department, startsWith,
+ null, true);
if (query == null) {
return REPORT_COUNT;
}
@@ -108,11 +109,13 @@
}
@SuppressWarnings("unchecked")
- public static List<Report> findReportEntries(int firstResult, int maxResults) {
+ public static List<Report> findReportEntries(int firstResult,
+ int maxResults) {
EntityManager em = entityManager();
try {
- List<Report> reportList = em.createQuery("select o from Report o").setFirstResult(
- firstResult).setMaxResults(maxResults).getResultList();
+ List<Report> reportList = em.createQuery(
+ "select o from Report o").setFirstResult(firstResult).setMaxResults(
+ maxResults).getResultList();
// force it to materialize
reportList.size();
return reportList;
@@ -127,8 +130,8 @@
int maxResults) {
EntityManager em = entityManager();
try {
- Query query = queryReportsBySearch(em, employeeId, department,
- startsWith, orderBy, false);
+ Query query = queryReportsBySearch(em, employeeId, department, startsWith,
+ orderBy, false);
// Try to get the memcache
if (cache == null) {
@@ -176,7 +179,7 @@
query.setHint(JPACursorHelper.CURSOR_HINT, trialCursor);
while (firstResult > pos) {
int min = Math.min(firstResult - pos, 1000);
-
+
// If we need to skip more than 1000 records, ensure the
// breaks occur at multiples of 1000 in order to increase the
// chances of reusing cursors from the memcache
@@ -184,7 +187,7 @@
int mod = (pos + min) % 1000;
min -= mod;
}
-
+
query.setMaxResults(min);
List<Report> results = query.getResultList();
int count = results.size();
@@ -192,7 +195,7 @@
break;
}
pos += count;
-
+
// Save the cursor for later
Cursor cursor = JPACursorHelper.getCursor(results);
if (cache != null) {
@@ -200,24 +203,25 @@
pos);
cache.put(key, cursor.toWebSafeString());
}
-
+
query.setHint(JPACursorHelper.CURSOR_HINT, cursor);
}
}
-
+
query.setMaxResults(maxResults);
List<Report> reportList = query.getResultList();
// force it to materialize
reportList.size();
-
+
Cursor cursor = JPACursorHelper.getCursor(reportList);
if (cache != null) {
int pos = firstResult + reportList.size();
- String key = createKey(employeeId, department, startsWith, orderBy, pos);
+ String key = createKey(employeeId, department, startsWith, orderBy,
+ pos);
cache.put(key, cursor.toWebSafeString());
}
-
+
return reportList;
} finally {
em.close();
@@ -228,7 +232,8 @@
public static List<Report> findReportsByEmployee(Long employeeId) {
EntityManager em = entityManager();
try {
- Query query = em.createQuery("select o from Report o where o.reporterKey =:reporterKey");
+ Query query = em.createQuery(
+ "select o from Report o where o.reporterKey =:reporterKey");
query.setParameter("reporterKey", employeeId);
List<Report> reportList = query.getResultList();
// force it to materialize
@@ -241,22 +246,19 @@
private static String createKey(Long employeeId, String department,
String startsWith, String orderBy, int firstResult) {
- return "" + employeeId + "+" + encode(department) + "+"
- + encode(startsWith) + "+" + encode(orderBy) + "+" + firstResult;
+ return "" + employeeId + "+" + encode(department) + "+" + encode(startsWith)
+ + "+" + encode(orderBy) + "+" + firstResult;
}
/**
* Returns a String based on an input String that provides the following
* guarantees.
- *
- * <ol>
- * <li>The result contains no '+' characters
- * <li>Distinct inputs always produce distinct results
- * </ol>
- *
- * <p>
- * Note that the transformation is not required to be reversible.
- *
+ *
+ * <ol> <li>The result contains no '+' characters <li>Distinct inputs always
+ * produce distinct results </ol>
+ *
+ * <p> Note that the transformation is not required to be reversible.
+ *
* @param s the input String
* @return a String suitable for use as part of a a memcache key
*/
@@ -272,13 +274,13 @@
/**
* Query for reports based on the search parameters. If startsWith is
* specified, the results will not be ordered.
- *
- * @param em the {@link EntityManager} to use
+ *
+ * @param em the {@link EntityManager} to use
* @param employeeId the employee id
* @param department the department to search
* @param startsWith the starting string
- * @param orderBy the order of the results
- * @param isCount true to query on the count only
+ * @param orderBy the order of the results
+ * @param isCount true to query on the count only
* @return the query, or null to return full report count.
*/
private static Query queryReportsBySearch(EntityManager em, Long employeeId,
@@ -335,7 +337,14 @@
return query;
}
+ @Transient
+ private Employee approvedSupervisor;
+
+ @Transient
+ private Employee reporter;
+
// @JoinColumn
+
private Long approvedSupervisorKey;
private Date created;
@@ -358,8 +367,7 @@
private String purposeLowerCase;
/**
- * Store reporter's key instead of reporter. See:
- * http://code.google.com/appengine
+ * Store reporter's key instead of reporter. See: http://code.google.com/appengine
* /docs/java/datastore/relationships.html#Unowned_Relationships
*/
// @JoinColumn
@@ -369,6 +377,11 @@
@Column(name = "version")
private Integer version;
+ public Employee getApprovedSupervisor() {
+ return approvedSupervisorKey != null ? Employee.findEmployee(
+ approvedSupervisorKey) : null;
+ }
+
public Long getApprovedSupervisorKey() {
return approvedSupervisorKey;
}
@@ -393,6 +406,10 @@
return this.purpose;
}
+ public Employee getReporter() {
+ return reporterKey != null ? Employee.findEmployee(reporterKey) : null;
+ }
+
public Long getReporterKey() {
return this.reporterKey;
}
@@ -445,10 +462,18 @@
this.purposeLowerCase = purpose == null ? "" : purpose.toLowerCase();
}
+ public void setReporter(Employee reporter) {
+ reporterKey = reporter == null ? null : reporter.getId();
+ }
+
public void setReporterKey(Long reporter) {
this.reporterKey = reporter;
}
+ public void setSupervisor(Employee supervisor) {
+ approvedSupervisorKey = supervisor == null ? null : supervisor.getId();
+ }
+
public void setVersion(Integer version) {
this.version = version;
}
@@ -466,4 +491,5 @@
sb.append("ApprovedSupervisor: ").append(getApprovedSupervisorKey());
return sb.toString();
}
+
}
diff --git a/user/src/com/google/gwt/app/client/NullParser.java b/user/src/com/google/gwt/app/client/NullParser.java
new file mode 100644
index 0000000..e37ea84
--- /dev/null
+++ b/user/src/com/google/gwt/app/client/NullParser.java
@@ -0,0 +1,51 @@
+/*
+ * 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
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.app.client;
+
+import com.google.gwt.text.shared.Parser;
+import com.google.gwt.valuestore.shared.Record;
+
+import java.text.ParseException;
+
+/**
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * <p>
+ * A no-op renderer, always returns null
+ * @param <T> a Record type.
+ */
+public class NullParser<T extends Record> implements Parser<T> {
+
+ private static NullParser INSTANCE;
+
+ /**
+ * @return the instance of the null parser
+ */
+ public static <T extends Record> Parser<T> instance() {
+ if (INSTANCE == null) {
+ INSTANCE = new NullParser<T>();
+ }
+ return INSTANCE;
+ }
+
+ protected NullParser() {
+ }
+
+ public T parse(CharSequence object) throws ParseException {
+ return null;
+ }
+}
diff --git a/user/src/com/google/gwt/app/client/ProxyBox.java b/user/src/com/google/gwt/app/client/ProxyBox.java
new file mode 100644
index 0000000..30410a9
--- /dev/null
+++ b/user/src/com/google/gwt/app/client/ProxyBox.java
@@ -0,0 +1,43 @@
+/*
+ * 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
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.app.client;
+
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.user.client.ui.ValueBox;
+import com.google.gwt.valuestore.shared.Record;
+
+/**
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * <p>
+ * A ValueBox that uses {@link com.google.gwt.app.client.ProxyParser} and
+ * {@link com.google.gwt.app.client.ProxyRenderer}.
+ *
+ * @param <T> a proxy record
+ */
+public class ProxyBox<T extends Record> extends ValueBox<T> {
+
+ public ProxyBox() {
+ super(Document.get().createTextInputElement(), ProxyRenderer.<T>instance(),
+ ProxyParser.<T>instance());
+ }
+
+ public T getValue() {
+ // Until a sensible ProxyParser can be written
+ return null;
+ }
+}
diff --git a/user/src/com/google/gwt/app/client/ProxyIdRenderer.java b/user/src/com/google/gwt/app/client/ProxyIdRenderer.java
new file mode 100644
index 0000000..239ecf8
--- /dev/null
+++ b/user/src/com/google/gwt/app/client/ProxyIdRenderer.java
@@ -0,0 +1,49 @@
+/*
+ * 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
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.app.client;
+
+import com.google.gwt.text.shared.AbstractRenderer;
+import com.google.gwt.text.shared.Renderer;
+import com.google.gwt.valuestore.shared.Record;
+
+/**
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * <p>
+ * Renderer of Record values
+ * @param <T> a record type
+ */
+public class ProxyIdRenderer<T extends Record> extends AbstractRenderer<T> {
+ private static ProxyIdRenderer INSTANCE;
+
+ /**
+ * @return the instance
+ */
+ public static <T extends Record> Renderer<T> instance() {
+ if (INSTANCE == null) {
+ INSTANCE = new ProxyIdRenderer<T>();
+ }
+ return INSTANCE;
+ }
+
+ protected ProxyIdRenderer() {
+ }
+
+ public String render(T object) {
+ return toString(object == null ? null : object.getId());
+ }
+}
diff --git a/user/src/com/google/gwt/app/client/ProxyParser.java b/user/src/com/google/gwt/app/client/ProxyParser.java
new file mode 100644
index 0000000..221a4f0
--- /dev/null
+++ b/user/src/com/google/gwt/app/client/ProxyParser.java
@@ -0,0 +1,57 @@
+/*
+ * 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
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.app.client;
+
+import com.google.gwt.text.shared.Parser;
+import com.google.gwt.valuestore.shared.Record;
+
+import java.text.ParseException;
+
+/**
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * <p>
+ * A no-op renderer.
+ * @param <T> a Record type.
+ */
+public class ProxyParser<T extends Record> implements Parser<T> {
+
+ private static ProxyParser INSTANCE;
+
+ /**
+ * @return the instance of the no-op renderer
+ */
+ public static <T extends Record> Parser<T> instance() {
+ if (INSTANCE == null) {
+ INSTANCE = new ProxyParser<T>();
+ }
+ return INSTANCE;
+ }
+
+ protected ProxyParser() {
+ }
+
+ public T parse(CharSequence object) throws ParseException {
+ try {
+ // TODO: how can we map an id back into a record synchronously? Use
+ // ValueStore?
+ return null;
+ } catch (NumberFormatException e) {
+ throw new ParseException(e.getMessage(), 0);
+ }
+ }
+}
diff --git a/user/src/com/google/gwt/app/client/ProxyRenderer.java b/user/src/com/google/gwt/app/client/ProxyRenderer.java
new file mode 100644
index 0000000..5ea3d4f
--- /dev/null
+++ b/user/src/com/google/gwt/app/client/ProxyRenderer.java
@@ -0,0 +1,49 @@
+/*
+ * 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
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.app.client;
+
+import com.google.gwt.text.shared.AbstractRenderer;
+import com.google.gwt.text.shared.Renderer;
+import com.google.gwt.valuestore.shared.Record;
+
+/**
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * <p>
+ * Renderer of Record values
+ * @param <T> a record type
+ */
+public class ProxyRenderer<T extends Record> extends AbstractRenderer<T> {
+ private static ProxyRenderer INSTANCE;
+
+ /**
+ * @return the instance
+ */
+ public static <T extends Record> Renderer<T> instance() {
+ if (INSTANCE == null) {
+ INSTANCE = new ProxyRenderer<T>();
+ }
+ return INSTANCE;
+ }
+
+ protected ProxyRenderer() {
+ }
+
+ public String render(T object) {
+ return toString(object == null ? null : object.getId());
+ }
+}
diff --git a/user/src/com/google/gwt/app/client/ReadonlyProxyBox.java b/user/src/com/google/gwt/app/client/ReadonlyProxyBox.java
new file mode 100644
index 0000000..cafe722
--- /dev/null
+++ b/user/src/com/google/gwt/app/client/ReadonlyProxyBox.java
@@ -0,0 +1,51 @@
+/*
+ * 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
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.app.client;
+
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.user.client.ui.ValueBox;
+import com.google.gwt.valuestore.shared.Record;
+
+/**
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * <p>
+ * A ValueBox that uses {@link NullParser} and
+ * {@link ProxyIdRenderer}. It is a display only placeholder class for now;
+ *
+ * @param <T> a proxy record
+ */
+public class ReadonlyProxyBox<T extends Record> extends ValueBox<T> {
+
+ private T currentValue;
+
+ public ReadonlyProxyBox() {
+ super(Document.get().createTextInputElement(), ProxyIdRenderer.<T>instance(),
+ NullParser.<T>instance());
+ setReadOnly(true);
+ }
+
+ public T getValue() {
+ // The display value cannot be modified;
+ return currentValue;
+ }
+
+ public void setValue(T value) {
+ this.currentValue = value;
+ super.setValue(value);
+ }
+}
diff --git a/user/src/com/google/gwt/app/rebind/EditorSupportGenerator.java b/user/src/com/google/gwt/app/rebind/EditorSupportGenerator.java
index 9df2bbe..f8bc60f 100644
--- a/user/src/com/google/gwt/app/rebind/EditorSupportGenerator.java
+++ b/user/src/com/google/gwt/app/rebind/EditorSupportGenerator.java
@@ -36,6 +36,7 @@
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import com.google.gwt.valuestore.shared.Property;
+import com.google.gwt.valuestore.shared.Record;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -90,6 +91,7 @@
JClassType takesValueType;
JClassType hasTextType;
+ JClassType jrecordType;
JClassType stringType;
private String capitalize(String name) {
@@ -180,7 +182,8 @@
HasText.class.getName());
stringType = generatorContext.getTypeOracle().findType(
String.class.getName());
-
+ recordType = generatorContext.getTypeOracle().findType(
+ Record.class.getName());
writeGetPropertiesMethod(sw, recordType);
writeInit(sw, viewType, recordType);
writeIsChangedMethod(sw, recordType, viewType);
@@ -303,6 +306,9 @@
if (returnType.isAssignableTo(stringType)) {
return "";
}
+ if (returnType.isAssignableTo(jrecordType)) {
+ return ".getId()+\"\"";
+ }
return ".toString()";
}
diff --git a/user/src/com/google/gwt/requestfactory/client/impl/AbstractJsonListRequest.java b/user/src/com/google/gwt/requestfactory/client/impl/AbstractJsonListRequest.java
index 461303b..0c60cf5 100644
--- a/user/src/com/google/gwt/requestfactory/client/impl/AbstractJsonListRequest.java
+++ b/user/src/com/google/gwt/requestfactory/client/impl/AbstractJsonListRequest.java
@@ -50,7 +50,9 @@
}
public void handleResponseText(String text) {
- JsArray<RecordJsoImpl> valueJsos = RecordJsoImpl.arrayFromJson(text);
+ RecordJsoImpl.JsonResults results = RecordJsoImpl.fromResults(text);
+
+ JsArray<RecordJsoImpl> valueJsos = results.getListResult();
List<T> valueList = new ArrayList<T>(valueJsos.length());
for (int i = 0; i < valueJsos.length(); i++) {
RecordJsoImpl jso = valueJsos.get(i);
@@ -58,7 +60,9 @@
valueList.add(schema.create(jso));
}
- requestFactory.getValueStore().setRecords(valueJsos);
+ requestFactory.getValueStore().setRecords(valueJsos, requestFactory);
+ processRelated(results.getRelated());
+
receiver.onSuccess(valueList, Collections.<SyncResult> emptySet());
}
}
diff --git a/user/src/com/google/gwt/requestfactory/client/impl/AbstractJsonObjectRequest.java b/user/src/com/google/gwt/requestfactory/client/impl/AbstractJsonObjectRequest.java
index 903458f..41c0864 100644
--- a/user/src/com/google/gwt/requestfactory/client/impl/AbstractJsonObjectRequest.java
+++ b/user/src/com/google/gwt/requestfactory/client/impl/AbstractJsonObjectRequest.java
@@ -22,22 +22,19 @@
import java.util.Collections;
/**
- * <p>
- * <span style="color:red">Experimental API: This class is still under rapid
+ * <p> <span style="color:red">Experimental API: This class is still under rapid
* development, and is very likely to be deleted. Use it at your own risk.
- * </span>
- * </p>
- * Abstract implementation of
- * {@link com.google.gwt.requestfactory.shared.RequestObject
+ * </span> </p> Abstract implementation of {@link com.google.gwt.requestfactory.shared.RequestObject
* RequestFactory.RequestObject} for requests that return single instances of
* {@link Record}.
- *
+ *
* @param <T> the type of entities returned
* @param <R> this request type
*/
public abstract class //
-AbstractJsonObjectRequest<T extends Record, R extends AbstractJsonObjectRequest<T, R>> //
+ AbstractJsonObjectRequest<T extends Record, R extends AbstractJsonObjectRequest<T, R>> //
extends AbstractRequest<T, R> implements RecordRequest<T> {
+
protected final RecordSchema<? extends T> schema;
public AbstractJsonObjectRequest(RecordSchema<? extends T> schema,
@@ -47,11 +44,15 @@
}
public void handleResponseText(String text) {
- RecordJsoImpl jso = RecordJsoImpl.fromJson(text);
+ RecordJsoImpl.JsonResults results = RecordJsoImpl.fromResults(text);
+
+ RecordJsoImpl jso = results.getResult();
jso.setSchema(schema);
- requestFactory.getValueStore().setRecord(jso);
- receiver.onSuccess(schema.create(jso),
- Collections.<SyncResult> emptySet());
+ requestFactory.getValueStore().setRecord(jso, requestFactory);
+ processRelated(results.getRelated());
+
+ receiver.onSuccess(schema.create(jso), Collections.<SyncResult>emptySet());
}
+
}
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 f7c0631..9a9bfaf 100644
--- a/user/src/com/google/gwt/requestfactory/client/impl/AbstractRequest.java
+++ b/user/src/com/google/gwt/requestfactory/client/impl/AbstractRequest.java
@@ -15,6 +15,7 @@
*/
package com.google.gwt.requestfactory.client.impl;
+import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.requestfactory.shared.Receiver;
import com.google.gwt.requestfactory.shared.RequestObject;
import com.google.gwt.valuestore.shared.Property;
@@ -26,8 +27,7 @@
import java.util.Set;
/**
- * <p>
- * <span style="color:red">Experimental API: This class is still under rapid
+ * <p> <span style="color:red">Experimental API: This class is still under rapid
* development, and is very likely to be deleted. Use it at your own risk.
* </span>
* </p>
@@ -44,7 +44,7 @@
protected DeltaValueStoreJsonImpl deltaValueStore;
protected Receiver<T> receiver;
- private final Set<Property<?>> properties = new HashSet<Property<?>>();
+ private final Set<String> propertyRefs = new HashSet<String>();
public AbstractRequest(RequestFactoryJsonImpl requestFactory) {
this.requestFactory = requestFactory;
@@ -76,21 +76,23 @@
requestFactory.fire(this);
}
+ /**
+ * @deprecated use {@link #with(String...)} instead.
+ * @param properties
+ * @return
+ */
public R forProperties(Collection<Property<?>> properties) {
- this.properties.addAll(properties);
- return getThis();
- }
-
- public R forProperty(Property<?> property) {
- this.properties.add(property);
+ for (Property p : properties) {
+ with(p.getName());
+ }
return getThis();
}
/**
* @return the properties
*/
- public Set<Property<?>> getProperties() {
- return Collections.unmodifiableSet(properties);
+ public Set<String> getPropertyRefs() {
+ return Collections.unmodifiableSet(propertyRefs);
}
public boolean isChanged() {
@@ -102,10 +104,29 @@
deltaValueStore = new DeltaValueStoreJsonImpl(valueStore, requestFactory);
}
+ public void setSchemaAndRecord(String schemaToken, RecordJsoImpl jso) {
+ jso.setSchema(requestFactory.getSchema(schemaToken));
+ requestFactory.getValueStore().setRecord(jso, requestFactory);
+ }
+
+ public R with(String... propertyRef) {
+ for (String ref : propertyRef) {
+ propertyRefs.add(ref);
+ }
+ return getThis();
+ }
+
/**
* Subclasses must override to return {@code this}, to allow builder-style
* methods to do the same.
*/
protected abstract R getThis();
+ protected native void processRelated(JavaScriptObject related) /*-{
+ for(var recordKey in related) {
+ var schemaAndId = recordKey.split(/-/, 2);
+ var jso = related[recordKey];
+ this.@com.google.gwt.requestfactory.client.impl.AbstractRequest::setSchemaAndRecord(Ljava/lang/String;Lcom/google/gwt/requestfactory/client/impl/RecordJsoImpl;)(schemaAndId[0], jso);
+ }
+ }-*/;
}
\ No newline at end of file
diff --git a/user/src/com/google/gwt/requestfactory/client/impl/RecordImpl.java b/user/src/com/google/gwt/requestfactory/client/impl/RecordImpl.java
index 8f6deb3..43b4121 100644
--- a/user/src/com/google/gwt/requestfactory/client/impl/RecordImpl.java
+++ b/user/src/com/google/gwt/requestfactory/client/impl/RecordImpl.java
@@ -50,7 +50,7 @@
public <V> V get(Property<V> property) {
return jso.get(property);
}
-
+
public Long getId() {
return jso.getId();
}
@@ -95,4 +95,8 @@
this.deltaValueStore = deltaValueStore;
}
+ protected ValueStoreJsonImpl getValueStore() {
+ return jso.getValueStore();
+ }
+
}
diff --git a/user/src/com/google/gwt/requestfactory/client/impl/RecordJsoImpl.java b/user/src/com/google/gwt/requestfactory/client/impl/RecordJsoImpl.java
index f530947..feec11b 100644
--- a/user/src/com/google/gwt/requestfactory/client/impl/RecordJsoImpl.java
+++ b/user/src/com/google/gwt/requestfactory/client/impl/RecordJsoImpl.java
@@ -28,15 +28,34 @@
import java.util.Date;
/**
- * <p>
- * <span style="color:red">Experimental API: This class is still under rapid
+ * <p> <span style="color:red">Experimental API: This class is still under rapid
* development, and is very likely to be deleted. Use it at your own risk.
- * </span>
- * </p>
- * JSO implementation of {@link Record}, used to back subclasses of
+ * </span> </p> JSO implementation of {@link Record}, used to back subclasses of
* {@link RecordImpl}.
*/
public class RecordJsoImpl extends JavaScriptObject implements Record {
+
+ /**
+ * JSO to hold result and related objects.
+ */
+ public static class JsonResults extends JavaScriptObject {
+
+ protected JsonResults() {
+ }
+
+ public final native JsArray<RecordJsoImpl> getListResult() /*-{
+ return this.result;
+ }-*/;
+
+ public final native JavaScriptObject getRelated() /*-{
+ return this.related;
+ }-*/;
+
+ public final native RecordJsoImpl getResult() /*-{
+ return this.result;
+ }-*/;
+ }
+
public static native JsArray<RecordJsoImpl> arrayFromJson(String json) /*-{
return eval(json);
}-*/;
@@ -64,7 +83,14 @@
return xyz;
}-*/;
+ public static native JsonResults fromResults(String json) /*-{
+ // TODO: clean this
+ eval("xyz=" + json);
+ return xyz;
+ }-*/;
+
/* Made protected, for testing */
+
protected static native RecordJsoImpl create() /*-{
return {};
}-*/;
@@ -88,7 +114,10 @@
}
try {
if (Boolean.class.equals(property.getType())) {
- return (V) Boolean.valueOf(getBoolean(property.getName()));
+ return (V) Boolean.valueOf(getBoolean(property.getName()));
+ }
+ if (Character.class.equals(property.getType())) {
+ return (V) Character.valueOf(String.valueOf(get(property.getName())).charAt(0));
}
if (Byte.class.equals(property.getType())) {
return (V) Byte.valueOf((byte) getInt(property.getName()));
@@ -130,8 +159,9 @@
}
}
} catch (final Exception ex) {
- throw new IllegalStateException("Property " + property.getName() + " has invalid " +
- " value " + get(property.getName()) + " for type " + property.getType());
+ throw new IllegalStateException(
+ "Property " + property.getName() + " has invalid " + " value "
+ + get(property.getName()) + " for type " + property.getType());
}
if (property instanceof EnumProperty) {
@@ -144,13 +174,28 @@
}
}
}
-
- // Sun JDK compile fails without this cast
- return (V) get(property.getName());
+
+ if (String.class == property.getType()) {
+ return (V) get(property.getName());
+ }
+ // at this point, we checked all the known primitive/value types we support
+ // TODO: handle embedded types, List, Set, Map
+
+ // else, it must be a record type
+ String relatedId = (String) get(property.getName());
+ if (relatedId == null) {
+ return null;
+ } else {
+ // TODO: should cache this or modify JSO field in place
+ String schemaAndId[] = relatedId.split("-");
+ return (V) getValueStore().getRecordBySchemaAndId(
+ getRequestFactory().getSchema(schemaAndId[0]),
+ Long.valueOf(schemaAndId[1]));
+ }
}
public final native <T> T get(String propertyName) /*-{
- return this[propertyName];
+ return this[propertyName] || null;
}-*/;
public final Long getId() {
@@ -160,10 +205,20 @@
public final <V> PropertyReference<V> getRef(Property<V> property) {
return new PropertyReference<V>(this, property);
}
+
+ // TODO: HACK! Need to look up token to schema for relatins
+ public final native RequestFactoryJsonImpl getRequestFactory() /*-{
+ return this['__rf'];
+ }-*/;
public final native RecordSchema<?> getSchema() /*-{
return this['__key'];
}-*/;
+
+ // TODO: HACK! Make VS public and stash it in the record for relation lookups
+ public final native ValueStoreJsonImpl getValueStore() /*-{
+ return this['__vs'];
+ }-*/;
public final Integer getVersion() {
return this.get(version);
@@ -178,8 +233,8 @@
public final boolean isEmpty() {
for (Property<?> property : getSchema().allProperties()) {
- if ((property != Record.id) && (property != Record.version)
- && (isDefined(property.getName()))) {
+ if ((property != Record.id) && (property != Record.version) && (isDefined(
+ property.getName()))) {
return false;
}
}
@@ -211,12 +266,19 @@
setString(property.getName(), (String) value);
return;
}
- if (value instanceof Long || value instanceof BigDecimal || value instanceof BigInteger) {
+ if (value instanceof Character) {
setString(property.getName(), String.valueOf(value));
return;
}
- if (value instanceof Integer || value instanceof Short || value instanceof Byte) {
+ if (value instanceof Long || value instanceof BigDecimal
+ || value instanceof BigInteger) {
+ setString(property.getName(), String.valueOf(value));
+ return;
+ }
+
+ if (value instanceof Integer || value instanceof Short
+ || value instanceof Byte) {
setInt(property.getName(), ((Number) value).intValue());
return;
}
@@ -236,17 +298,39 @@
return;
}
- throw new UnsupportedOperationException("Can't yet set properties of type "
- + value.getClass().getName());
+ if (value instanceof Boolean) {
+ setBoolean(property.getName(), ((Boolean) value).booleanValue());
+ }
+
+ if (value instanceof Record) {
+ setString(property.getName(),
+ getRequestFactory().getSchema(value.getClass().getName()).getToken().getName()
+ + "-" + String.valueOf(((Record) value).getId()));
+ }
+
+ throw new UnsupportedOperationException(
+ "Can't yet set properties of type " + value.getClass().getName());
}
+
+ // TODO: HACK! Need to look up token to schema for relatins
+ public final native void setRequestFactory(RequestFactoryJsonImpl requestFactory) /*-{
+ this['__rf'] = requestFactory;
+ }-*/;
+
public final native void setSchema(RecordSchema<?> schema) /*-{
this['__key'] = schema;
}-*/;
+ // TODO: HACK! Make VS public and stash it in the record for relation lookups
+
+ public final native void setValueStore(ValueStoreJsonImpl valueStoreJson) /*-{
+ this['__vs']=valueStoreJson;
+ }-*/;
+
/**
* Return JSON representation using org.json library.
- *
+ *
* @return returned string.
*/
public final native String toJson() /*-{
@@ -271,7 +355,7 @@
/**
* Return JSON representation of just id and version fields, using org.json
* library.
- *
+ *
* @return returned string.
*/
public final native String toJsonIdVersion() /*-{
@@ -287,7 +371,8 @@
return $wnd.JSON.stringify(object);
}-*/;
- private native boolean copyPropertyIfDifferent(String name, RecordJsoImpl from) /*-{
+ private native boolean copyPropertyIfDifferent(String name,
+ RecordJsoImpl from) /*-{
if (this[name] == from[name]) {
return false;
}
@@ -311,6 +396,10 @@
return this[name];
}-*/;
+ private native void setBoolean(String name, boolean value) /*-{
+ this[name] = value;
+ }-*/;
+
private native void setDouble(String name, double value) /*-{
this[name] = value;
}-*/;
diff --git a/user/src/com/google/gwt/requestfactory/client/impl/RecordToTypeMap.java b/user/src/com/google/gwt/requestfactory/client/impl/RecordToTypeMap.java
index 7559688..3b1c109 100644
--- a/user/src/com/google/gwt/requestfactory/client/impl/RecordToTypeMap.java
+++ b/user/src/com/google/gwt/requestfactory/client/impl/RecordToTypeMap.java
@@ -28,4 +28,5 @@
*/
public interface RecordToTypeMap {
RecordSchema<? extends Record> getType(Class<? extends Record> recordClass);
+ RecordSchema<? extends Record> getType(String token);
}
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 8f3a818..898eae5 100644
--- a/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java
+++ b/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java
@@ -120,6 +120,8 @@
e);
}
}
+
+ public abstract RecordSchema getSchema(String token);
/**
* @param handlerManager
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 d523a71..cff570e 100644
--- a/user/src/com/google/gwt/requestfactory/client/impl/ValueStoreJsonImpl.java
+++ b/user/src/com/google/gwt/requestfactory/client/impl/ValueStoreJsonImpl.java
@@ -17,6 +17,7 @@
import com.google.gwt.core.client.JsArray;
import com.google.gwt.event.shared.HandlerManager;
+import com.google.gwt.valuestore.shared.Record;
import com.google.gwt.valuestore.shared.WriteOperation;
import java.util.HashMap;
@@ -29,7 +30,7 @@
* </span>
* </p>
*/
-class ValueStoreJsonImpl {
+public class ValueStoreJsonImpl {
// package protected fields for use by DeltaValueStoreJsonImpl
final HandlerManager eventBus;
@@ -40,14 +41,26 @@
this.eventBus = eventBus;
}
- public void setRecord(RecordJsoImpl newRecord) {
- setRecordInList(newRecord, 0, null);
+ public Record getRecordBySchemaAndId(RecordSchema<?> schema,
+ Long id) {
+ if (id == null) {
+ return null;
+ }
+ // TODO: pass isFuture to this method from decoding ID string
+ RecordKey key = new RecordKey(id, schema, false);
+ return schema.create(records.get(key));
}
- public void setRecords(JsArray<RecordJsoImpl> newRecords) {
+ public void setRecord(RecordJsoImpl newRecord,
+ RequestFactoryJsonImpl requestFactory) {
+ setRecordInList(newRecord, 0, null, requestFactory);
+ }
+
+ public void setRecords(JsArray<RecordJsoImpl> newRecords,
+ RequestFactoryJsonImpl requestFactory) {
for (int i = 0, l = newRecords.length(); i < l; i++) {
RecordJsoImpl newRecord = newRecords.get(i);
- setRecordInList(newRecord, i, newRecords);
+ setRecordInList(newRecord, i, newRecords, requestFactory);
}
}
@@ -55,11 +68,14 @@
* @param newRecord
* @param i
* @param array
+ * @param requestFactory
*/
private void setRecordInList(RecordJsoImpl newRecord, int i,
- JsArray<RecordJsoImpl> array) {
+ JsArray<RecordJsoImpl> array, RequestFactoryJsonImpl requestFactory) {
RecordKey recordKey = new RecordKey(newRecord, RequestFactoryJsonImpl.NOT_FUTURE);
-
+ newRecord.setValueStore(this);
+ newRecord.setRequestFactory(requestFactory);
+
RecordJsoImpl oldRecord = records.get(recordKey);
if (oldRecord == null) {
records.put(recordKey, newRecord);
diff --git a/user/src/com/google/gwt/requestfactory/rebind/RequestFactoryGenerator.java b/user/src/com/google/gwt/requestfactory/rebind/RequestFactoryGenerator.java
index b315688..0cf7f65 100644
--- a/user/src/com/google/gwt/requestfactory/rebind/RequestFactoryGenerator.java
+++ b/user/src/com/google/gwt/requestfactory/rebind/RequestFactoryGenerator.java
@@ -65,23 +65,22 @@
import java.math.BigInteger;
import java.util.Collections;
import java.util.Date;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
+import java.util.Map;
import java.util.Set;
/**
- * <p>
- * <span style="color:red">Experimental API: This class is still under rapid
+ * <p> <span style="color:red">Experimental API: This class is still under rapid
* development, and is very likely to be deleted. Use it at your own risk.
- * </span>
- * </p>
- * Generates implementations of
- * {@link com.google.gwt.requestfactory.shared.RequestFactory RequestFactory}
- * and its nested interfaces.
+ * </span> </p> Generates implementations of {@link com.google.gwt.requestfactory.shared.RequestFactory
+ * RequestFactory} and its nested interfaces.
*/
public class RequestFactoryGenerator extends Generator {
- private final Set<JClassType> generatedRecordTypes = new HashSet<JClassType>();
+ private final Set<JClassType> generatedRecordTypes
+ = new HashSet<JClassType>();
@Override
public String generate(TreeLogger logger, GeneratorContext generatorContext,
@@ -94,15 +93,16 @@
// Ensure that the requested type exists
if (interfaceType == null) {
- logger.log(TreeLogger.ERROR, "Could not find requested typeName: "
- + interfaceName);
+ logger.log(TreeLogger.ERROR,
+ "Could not find requested typeName: " + interfaceName);
throw new UnableToCompleteException();
}
if (interfaceType.isInterface() == null) {
// The incoming type wasn't a plain interface, we don't support
// abstract base classes
- logger.log(TreeLogger.ERROR, interfaceType.getQualifiedSourceName()
- + " is not an interface.", null);
+ logger.log(TreeLogger.ERROR,
+ interfaceType.getQualifiedSourceName() + " is not an interface.",
+ null);
throw new UnableToCompleteException();
}
@@ -114,9 +114,8 @@
// If an implementation already exists, we don't need to do any work
if (out != null) {
- generateOnce(
- typeOracle.findType(RequestFactory.class.getCanonicalName()), logger,
- generatorContext, out, interfaceType, packageName, implName);
+ generateOnce(typeOracle.findType(RequestFactory.class.getCanonicalName()),
+ logger, generatorContext, out, interfaceType, packageName, implName);
}
return packageName + "." + implName;
@@ -147,6 +146,9 @@
String recordImplTypeName = publicRecordType.getName() + "Impl";
PrintWriter pw = generatorContext.tryCreate(logger, packageName,
recordImplTypeName);
+
+ Set<JClassType> transitiveDeps = new LinkedHashSet<JClassType>();
+
if (pw != null) {
logger = logger.branch(TreeLogger.DEBUG, "Generating "
+ publicRecordType.getName());
@@ -157,9 +159,10 @@
String eventTypeName = publicRecordType.getName() + "Changed";
JClassType eventType = typeOracle.findType(packageName, eventTypeName);
if (eventType == null) {
- logger.log(TreeLogger.ERROR, String.format(
- "Cannot find %s implementation %s.%s",
- RecordChangedEvent.class.getName(), packageName, eventTypeName));
+ logger.log(TreeLogger.ERROR,
+ String.format("Cannot find %s implementation %s.%s",
+ RecordChangedEvent.class.getName(), packageName,
+ eventTypeName));
throw new UnableToCompleteException();
}
@@ -176,6 +179,8 @@
f.addImport(Collections.class.getName());
f.addImport(HashSet.class.getName());
f.addImport(Set.class.getName());
+ f.addImport(Map.class.getName());
+ f.addImport(HashMap.class.getName());
f.setSuperclass(RecordImpl.class.getSimpleName());
f.addImplementedInterface(publicRecordType.getName());
@@ -188,8 +193,10 @@
sw.println();
String simpleImplName = publicRecordType.getSimpleSourceName() + "Impl";
- printRequestImplClass(sw, publicRecordType, simpleImplName, true);
- printRequestImplClass(sw, publicRecordType, simpleImplName, false);
+ printRequestImplClass(sw, publicRecordType, simpleImplName, true,
+ typeOracle);
+ printRequestImplClass(sw, publicRecordType, simpleImplName, false,
+ typeOracle);
sw.println();
sw.println(String.format(
@@ -203,6 +210,7 @@
sw.println("super(jso, isFuture);");
sw.outdent();
sw.println("}");
+ JClassType recordBaseType = typeOracle.findType(Record.class.getName());
// getter methods
for (JField field : publicRecordType.getFields()) {
@@ -210,18 +218,26 @@
if (propertyType.getErasedType() == fieldType.getErasedType()) {
JParameterizedType parameterized = fieldType.isParameterized();
if (parameterized == null) {
- logger.log(TreeLogger.ERROR, fieldType
- + " must have its param type set.");
+ logger.log(TreeLogger.ERROR,
+ fieldType + " must have its param type set.");
throw new UnableToCompleteException();
}
JClassType returnType = parameterized.getTypeArgs()[0];
sw.println();
sw.println(String.format("public %s get%s() {",
- returnType.getQualifiedSourceName(), capitalize(field.getName())));
+ returnType.getQualifiedSourceName(),
+ capitalize(field.getName())));
sw.indent();
sw.println(String.format("return get(%s);", field.getName()));
sw.outdent();
sw.println("}");
+ /*
+ * Because a Proxy A may relate to B which relates to C, we need to
+ * ensure transitively.
+ */
+ if (isRecordType(typeOracle, returnType)) {
+ transitiveDeps.add(returnType);
+ }
}
}
@@ -254,6 +270,11 @@
}
generatedRecordTypes.add(publicRecordType);
+ // ensure generatation of transitive dependencies
+ for (JClassType type : transitiveDeps) {
+ ensureRecordType(logger, generatorContext, type.getPackage().getName(),
+ type);
+ }
}
private void generateOnce(JClassType requestFactoryType, TreeLogger logger,
@@ -261,8 +282,9 @@
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);
@@ -270,6 +292,8 @@
f.addImport(RequestFactoryJsonImpl.class.getName());
f.addImport(interfaceType.getQualifiedSourceName());
f.addImport(RecordToTypeMap.class.getName());
+ f.addImport(Record.class.getName());
+ f.addImport(RecordSchema.class.getName());
f.addImplementedInterface(interfaceType.getName());
f.setSuperclass(RequestFactoryJsonImpl.class.getSimpleName());
@@ -288,11 +312,9 @@
}
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",
- method.getName(), interfaceType.getName()));
+ 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();
}
JClassType asInterface = returnType.isInterface();
@@ -317,8 +339,8 @@
}
// write create(Class..)
- JClassType recordToTypeInterface = generatorContext.getTypeOracle().findType(
- RecordToTypeMap.class.getName());
+ JClassType recordToTypeInterface = generatorContext.getTypeOracle().findType(RecordToTypeMap.class.getName());
+ // TODO: note, this seems like a bug. What if you have 2 RequestFactories?
String recordToTypeMapName = recordToTypeInterface.getName() + "Impl";
sw.println("public " + Record.class.getName() + " create(Class token) {");
sw.indent();
@@ -326,20 +348,26 @@
sw.outdent();
sw.println("}");
+ sw.println(
+ "public RecordSchema<? extends Record> getSchema(String schemaToken) {");
+ sw.indent();
+ sw.println("return new " + recordToTypeMapName + "().getType(schemaToken);");
+ sw.outdent();
+ sw.println("}");
+
// write a method for each request builder and generate it
for (JMethod requestSelector : requestSelectors) {
String returnTypeName = requestSelector.getReturnType().getQualifiedSourceName();
- String nestedImplName = capitalize(requestSelector.getName().replace('.',
- '_'))
- + "Impl";
+ String nestedImplName =
+ capitalize(requestSelector.getName().replace('.', '_')) + "Impl";
String nestedImplPackage = generatorContext.getTypeOracle().findType(
returnTypeName).getPackage().getName();
sw.println("public " + returnTypeName + " " + requestSelector.getName()
+ "() {");
sw.indent();
- sw.println("return new " + nestedImplPackage + "." + nestedImplName
- + "(this);");
+ sw.println(
+ "return new " + nestedImplPackage + "." + nestedImplName + "(this);");
sw.outdent();
sw.println("}");
sw.println();
@@ -369,8 +397,9 @@
private void generateRecordToTypeMap(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);
@@ -396,9 +425,30 @@
sw.println("}");
}
- sw.println("throw new IllegalArgumentException(\"Unknown token \" + token + ");
+ sw.println(
+ "throw new IllegalArgumentException(\"Unknown token \" + token + ");
sw.indent();
- sw.println("\", does not match any of the TOKEN vairables of a Record\");");
+ sw.println("\", does not match any of the TOKEN variables of a Record\");");
+ sw.outdent();
+ sw.outdent();
+ sw.println("}");
+
+ // TODO: unoptimal bloat, and there doesn't need to be two of these methods
+ sw.println("public RecordSchema<? extends Record> getType(String token) {");
+ sw.indent();
+ for (JClassType publicRecordType : generatedRecordTypes) {
+ String qualifiedSourceName = publicRecordType.getQualifiedSourceName();
+ sw.println("if (token.equals(\"" + qualifiedSourceName + "\")) {");
+ sw.indent();
+ sw.println("return " + qualifiedSourceName + "Impl.SCHEMA;");
+ sw.outdent();
+ sw.println("}");
+ }
+
+ sw.println(
+ "throw new IllegalArgumentException(\"Unknown token \" + token + ");
+ sw.indent();
+ sw.println("\", does not match any of the TOKEN variables of a Record\");");
sw.outdent();
sw.outdent();
sw.println("}");
@@ -413,8 +463,9 @@
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);
@@ -428,8 +479,8 @@
sw.println("private final " + mainType.getName() + "Impl factory;");
sw.println();
// constructor for the class.
- sw.println("public " + implName + "(" + mainType.getName()
- + "Impl factory) {");
+ sw.println(
+ "public " + implName + "(" + mainType.getName() + "Impl factory) {");
sw.indent();
sw.println("this.factory = factory;");
sw.outdent();
@@ -485,21 +536,23 @@
} else if (isVoidRequest(typeOracle, requestType)) {
requestClassName = AbstractVoidRequest.class.getName();
} else {
- logger.log(TreeLogger.ERROR, "Return type " + requestType
- + " is not yet supported");
+ logger.log(TreeLogger.ERROR,
+ "Return type " + requestType + " is not yet supported");
throw new UnableToCompleteException();
}
sw.println(getMethodDeclaration(method) + " {");
sw.indent();
- sw.println("return new " + requestClassName + "(factory" + enumArgument
- + ") {");
+ sw.println(
+ "return new " + requestClassName + "(factory" + enumArgument + ") {");
sw.indent();
String requestDataName = RequestData.class.getSimpleName();
sw.println("public " + requestDataName + " getRequestData() {");
sw.indent();
- sw.println("return new " + requestDataName + "(\"" + operationName
- + "\", " + getParametersAsString(method, typeOracle) + ");");
+ sw.println(
+ "return new " + requestDataName + "(\"" + operationName + "\", "
+ + getParametersAsString(method, typeOracle) + ","
+ + "getPropertyRefs());");
sw.outdent();
sw.println("}");
sw.outdent();
@@ -514,9 +567,8 @@
}
/**
- * This method is very similar to {@link
- * com.google.gwt.core.ext.typeinfo.JMethod.getReadableDeclaration()}. The
- * only change is that each parameter is final.
+ * This method is very similar to {@link com.google.gwt.core.ext.typeinfo.JMethod.getReadableDeclaration()}.
+ * The only change is that each parameter is final.
*/
private String getMethodDeclaration(JMethod method) {
StringBuilder sb = new StringBuilder("public ");
@@ -555,7 +607,8 @@
// TODO No. This defeats the entire purpose of PropertyReference. It's
// supposed
// to be dereferenced server side, not client side.
- if ("com.google.gwt.valuestore.shared.PropertyReference".equals(parameter.getType().getQualifiedBinaryName())) {
+ if ("com.google.gwt.valuestore.shared.PropertyReference".equals(
+ parameter.getType().getQualifiedBinaryName())) {
sb.append(".get()");
}
JClassType classType = parameter.getType().isClassOrInterface();
@@ -577,7 +630,8 @@
return requestType.isParameterized().getTypeArgs()[0].isAssignableTo(typeOracle.findType(BigInteger.class.getName()));
}
- private boolean isBooleanRequest(TypeOracle typeOracle, JClassType requestType) {
+ private boolean isBooleanRequest(TypeOracle typeOracle,
+ JClassType requestType) {
return requestType.isParameterized().getTypeArgs()[0].isAssignableTo(typeOracle.findType(Boolean.class.getName()));
}
@@ -594,7 +648,8 @@
return requestType.isParameterized().getTypeArgs()[0].isAssignableTo(typeOracle.findType(Date.class.getName()));
}
- private boolean isDoubleRequest(TypeOracle typeOracle, JClassType requestType) {
+ private boolean isDoubleRequest(TypeOracle typeOracle,
+ JClassType requestType) {
return requestType.isParameterized().getTypeArgs()[0].isAssignableTo(typeOracle.findType(Double.class.getName()));
}
@@ -602,11 +657,13 @@
return requestType.isParameterized().getTypeArgs()[0].isAssignableTo(typeOracle.findType(Enum.class.getName()));
}
- private boolean isFloatRequest(TypeOracle typeOracle, JClassType requestType) {
+ private boolean isFloatRequest(TypeOracle typeOracle,
+ JClassType requestType) {
return requestType.isParameterized().getTypeArgs()[0].isAssignableTo(typeOracle.findType(Float.class.getName()));
}
- private boolean isIntegerRequest(TypeOracle typeOracle, JClassType requestType) {
+ private boolean isIntegerRequest(TypeOracle typeOracle,
+ JClassType requestType) {
return requestType.isParameterized().getTypeArgs()[0].isAssignableTo(typeOracle.findType(Integer.class.getName()));
}
@@ -619,11 +676,17 @@
return requestType.isAssignableTo(typeOracle.findType(RecordListRequest.class.getName()));
}
- private boolean isRecordRequest(TypeOracle typeOracle, JClassType requestType) {
+ private boolean isRecordRequest(TypeOracle typeOracle,
+ JClassType requestType) {
return requestType.isAssignableTo(typeOracle.findType(RecordRequest.class.getName()));
}
- private boolean isShortRequest(TypeOracle typeOracle, JClassType requestType) {
+ private boolean isRecordType(TypeOracle typeOracle, JClassType requestType) {
+ return requestType.isAssignableTo(typeOracle.findType(Record.class.getName()));
+ }
+
+ private boolean isShortRequest(TypeOracle typeOracle,
+ JClassType requestType) {
return requestType.isParameterized().getTypeArgs()[0].isAssignableTo(typeOracle.findType(Short.class.getName()));
}
@@ -633,11 +696,9 @@
/**
* Prints a ListRequestImpl or ObjectRequestImpl class.
- *
- * @param list
*/
private void printRequestImplClass(SourceWriter sw, JClassType returnType,
- String returnImplTypeName, boolean list) {
+ String returnImplTypeName, boolean list, TypeOracle typeOracle) {
String name = list ? "ListRequestImpl" : "ObjectRequestImpl";
Class<?> superClass = list ? AbstractJsonListRequest.class
@@ -648,6 +709,7 @@
+ "> {");
sw.println();
sw.indent();
+
sw.println(String.format("%s(%s factory) {", name,
RequestFactoryJsonImpl.class.getSimpleName()));
sw.indent();
@@ -661,6 +723,7 @@
sw.println("return this;");
sw.outdent();
sw.println("}");
+
sw.outdent();
sw.println("}");
sw.println();
@@ -675,12 +738,13 @@
* @return
* @throws UnableToCompleteException
*/
+
private JClassType printSchema(TypeOracle typeOracle,
JClassType publicRecordType, String recordImplTypeName,
JClassType eventType, SourceWriter sw) {
- sw.println(String.format(
- "public static class MySchema extends RecordSchema<%s> {",
- recordImplTypeName));
+ sw.println(
+ String.format("public static class MySchema extends RecordSchema<%s> {",
+ recordImplTypeName));
sw.indent();
sw.println("private final Set<Property<?>> allProperties;");
@@ -737,8 +801,8 @@
sw.println();
sw.println("public Class getToken() {");
sw.indent();
- sw.println("return " + publicRecordType.getQualifiedSourceName()
- + ".class;" + " // special field");
+ sw.println("return " + publicRecordType.getQualifiedSourceName() + ".class;"
+ + " // special field");
sw.outdent();
sw.println("}");
diff --git a/user/src/com/google/gwt/requestfactory/server/DefaultSecurityProvider.java b/user/src/com/google/gwt/requestfactory/server/DefaultSecurityProvider.java
index a063474..72b1ed2 100644
--- a/user/src/com/google/gwt/requestfactory/server/DefaultSecurityProvider.java
+++ b/user/src/com/google/gwt/requestfactory/server/DefaultSecurityProvider.java
@@ -43,6 +43,10 @@
}
}
+ public String encodeClassType(Class<?> type) {
+ return type.getName();
+ }
+
public String mapOperation(String operationName) throws SecurityException {
return operationName;
}
diff --git a/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java b/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java
index d017f36..ed09eca 100644
--- a/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java
+++ b/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java
@@ -18,6 +18,7 @@
import com.google.gwt.requestfactory.shared.DataTransferObject;
import com.google.gwt.requestfactory.shared.RequestData;
import com.google.gwt.requestfactory.shared.RequestFactory;
+import com.google.gwt.requestfactory.shared.Service;
import com.google.gwt.valuestore.shared.Property;
import com.google.gwt.valuestore.shared.Record;
import com.google.gwt.valuestore.shared.WriteOperation;
@@ -56,12 +57,15 @@
private static final Logger log = Logger.getLogger(JsonRequestProcessor.class.getName());
// TODO should we consume String, InputStream, or JSONObject?
+
/**
* A class representing the pair of a domain entity and its corresponding
* record class on the client side.
*/
public static class EntityRecordPair {
+
public final Class<?> entity;
+
public final Class<? extends Record> record;
EntityRecordPair(Class<?> entity, Class<? extends Record> record) {
@@ -80,13 +84,17 @@
return Collections.unmodifiableSet(blackList);
}
+ private RequestProperty propertyRefs;
+
+ private Map<String, JSONObject> relatedObjects
+ = new HashMap<String, JSONObject>();
+
private OperationRegistry operationRegistry;
public Collection<Property<?>> allProperties(Class<? extends Record> clazz) {
Set<Property<?>> rtn = new HashSet<Property<?>>();
for (Field f : clazz.getFields()) {
- if (Modifier.isStatic(f.getModifiers())
- && Property.class.isAssignableFrom(f.getType())) {
+ if (Modifier.isStatic(f.getModifiers()) && Property.class.isAssignableFrom(f.getType())) {
try {
rtn.add((Property<?>) f.get(null));
} catch (IllegalAccessException e) {
@@ -99,7 +107,12 @@
public String decodeAndInvokeRequest(String encodedRequest) throws Exception {
try {
- return processJsonRequest(encodedRequest).toString();
+ Logger.getLogger(this.getClass().getName()).finest("Incoming request "
+ + encodedRequest);
+ String response = processJsonRequest(encodedRequest).toString();
+ Logger.getLogger(this.getClass().getName()).finest("Outgoing response "
+ + response);
+ return response;
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException(e);
} catch (IllegalAccessException e) {
@@ -119,7 +132,6 @@
/**
* Encodes parameter value.
- *
*/
public Object decodeParameterValue(Class<?> parameterType,
String parameterValue) {
@@ -174,21 +186,53 @@
}
}
}
- throw new IllegalArgumentException("Can't decode enum " + parameterType
- + " no matching ordinal " + ordinal);
+ throw new IllegalArgumentException(
+ "Can't decode enum " + parameterType + " no matching ordinal "
+ + ordinal);
} catch (NoSuchMethodException e) {
- throw new IllegalArgumentException("Can't decode enum " + parameterType);
+ throw new IllegalArgumentException(
+ "Can't decode enum " + parameterType);
} catch (InvocationTargetException e) {
- throw new IllegalArgumentException("Can't decode enum " + parameterType);
+ throw new IllegalArgumentException(
+ "Can't decode enum " + parameterType);
} catch (IllegalAccessException e) {
- throw new IllegalArgumentException("Can't decode enum " + parameterType);
+ throw new IllegalArgumentException(
+ "Can't decode enum " + parameterType);
}
}
if (Date.class == parameterType) {
return new Date(Long.parseLong(parameterValue));
}
- throw new IllegalArgumentException("Unknown parameter type: "
- + parameterType);
+ if (Record.class.isAssignableFrom(parameterType)) {
+ Service service = parameterType.getAnnotation(Service.class);
+ if (service != null) {
+ Class<?> sClass = service.value();
+ String schemaAndId[] = parameterValue.toString().split("-", 2);
+ // ignore schema for now and use Property type
+ String findMeth = null;
+ try {
+ findMeth = getMethodNameFromPropertyName(sClass.getSimpleName(),
+ "find");
+ Method meth = sClass.getMethod(findMeth, Long.class);
+ return meth.invoke(null, Long.valueOf(schemaAndId[1]));
+ } catch (NoSuchMethodException e) {
+ e.printStackTrace();
+ throw new IllegalArgumentException(
+ sClass + " no method named " + findMeth);
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+
+ throw new IllegalArgumentException(
+ sClass + " can't invoke method named " + findMeth);
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ throw new IllegalArgumentException(
+ sClass + " can't access method named " + findMeth);
+ }
+ }
+ }
+ throw new IllegalArgumentException(
+ "Unknown parameter type: " + parameterType);
}
public Object encodePropertyValue(Object value) {
@@ -220,18 +264,33 @@
* is sent into the response.
*/
public Object encodePropertyValueFromDataStore(Object entityElement,
- String propertyName) throws SecurityException, NoSuchMethodException,
- IllegalAccessException, InvocationTargetException {
+ Class<?> propertyType, String propertyName,
+ RequestProperty propertyContext)
+ throws SecurityException, NoSuchMethodException, IllegalAccessException,
+ InvocationTargetException, JSONException {
String methodName = getMethodNameFromPropertyName(propertyName, "get");
Method method = entityElement.getClass().getMethod(methodName);
Object returnValue = method.invoke(entityElement);
+ if (returnValue != null && Record.class.isAssignableFrom(propertyType)) {
+ Method idMethod = entityElement.getClass().getMethod("getId");
+ Long id = (Long) idMethod.invoke(entityElement);
+
+ String keyRef =
+ operationRegistry.getSecurityProvider().encodeClassType(propertyType)
+ + "-" + id;
+ addRelatedObject(keyRef, returnValue,
+ (Class<? extends Record>) propertyType,
+ propertyContext.getProperty(propertyName));
+ // replace value with id reference
+ return keyRef;
+ }
return encodePropertyValue(returnValue);
}
/**
* Generate an ID for a new record. The default behavior is to return null and
* let the data store generate the ID automatically.
- *
+ *
* @param key the key of the record field
* @return the ID of the new record, or null to auto generate
*/
@@ -260,43 +319,46 @@
return entity.getConstructor().newInstance();
}
// TODO: check "version" validity.
- return entity.getMethod("find" + entity.getSimpleName(), idType).invoke(
- null, decodeParameterValue(idType, idValue.toString()));
+ return entity.getMethod("find" + entity.getSimpleName(),
+ idType).invoke(null, decodeParameterValue(idType, idValue.toString()));
}
/**
* Converts the returnValue of a 'get' method to a JSONArray.
- *
- * @param resultList object returned by a 'get' method, must be of type
- * List<?>
+ *
+ * @param resultList object returned by a 'get' method, must be of type
+ * List<?>
* @param entityKeyClass the class type of the contained value
* @return the JSONArray
*/
public JSONArray getJsonArray(List<?> resultList,
- Class<? extends Record> entityKeyClass) throws IllegalArgumentException,
- SecurityException, IllegalAccessException, JSONException,
- NoSuchMethodException, InvocationTargetException {
+ Class<? extends Record> entityKeyClass)
+ throws IllegalArgumentException, SecurityException,
+ IllegalAccessException, JSONException, NoSuchMethodException,
+ InvocationTargetException {
JSONArray jsonArray = new JSONArray();
if (resultList.size() == 0) {
return jsonArray;
}
for (Object entityElement : resultList) {
- jsonArray.put(getJsonObject(entityElement, entityKeyClass));
+ jsonArray.put(getJsonObject(entityElement, entityKeyClass, propertyRefs));
}
return jsonArray;
}
public JSONObject getJsonObject(Object entityElement,
- Class<? extends Record> entityKeyClass) throws JSONException,
- NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+ Class<? extends Record> entityKeyClass, RequestProperty propertyContext)
+ throws JSONException, NoSuchMethodException, IllegalAccessException,
+ InvocationTargetException {
JSONObject jsonObject = new JSONObject();
for (Property<?> p : allProperties(entityKeyClass)) {
- if (requestedProperty(p)) {
+ if (requestedProperty(p, propertyContext)) {
String propertyName = p.getName();
- jsonObject.put(propertyName, encodePropertyValueFromDataStore(
- entityElement, propertyName));
+ jsonObject.put(propertyName,
+ encodePropertyValueFromDataStore(entityElement, p.getType(),
+ propertyName, propertyContext));
}
}
return jsonObject;
@@ -305,11 +367,12 @@
/**
* Returns methodName corresponding to the propertyName that can be invoked on
* an entity.
- *
+ *
* Example: "userName" returns prefix + "UserName". "version" returns prefix +
* "Version"
*/
- public String getMethodNameFromPropertyName(String propertyName, String prefix) {
+ public String getMethodNameFromPropertyName(String propertyName,
+ String prefix) {
if (propertyName == null) {
throw new NullPointerException("propertyName must not be null");
}
@@ -362,13 +425,14 @@
* Returns the property fields (name => type) for a record.
*/
public Map<String, Class<?>> getPropertiesFromRecord(
- Class<? extends Record> record) throws SecurityException,
- IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+ Class<? extends Record> record)
+ throws SecurityException, IllegalAccessException,
+ InvocationTargetException, NoSuchMethodException {
Map<String, Class<?>> properties = new HashMap<String, Class<?>>();
for (Field f : record.getFields()) {
if (Property.class.isAssignableFrom(f.getType())) {
- Class<?> propertyType = (Class<?>) f.getType().getMethod("getType").invoke(
- f.get(null));
+ Class<?> propertyType = (Class<?>) f.getType().getMethod(
+ "getType").invoke(f.get(null));
properties.put(f.getName(), propertyType);
}
}
@@ -379,32 +443,33 @@
* Returns the property value, in the specified type, from the request object.
* The value is put in the DataStore.
*/
- public Object getPropertyValueFromRequest(JSONObject recordObject,
- String key, Class<?> propertyType) throws JSONException {
+ public Object getPropertyValueFromRequest(JSONObject recordObject, String key,
+ Class<?> propertyType) throws JSONException {
return decodeParameterValue(propertyType, recordObject.get(key).toString());
}
@SuppressWarnings("unchecked")
public Class<Record> getRecordFromClassToken(String recordToken) {
try {
- Class<?> clazz = Class.forName(recordToken, false,
+ Class<?> clazz = Class.forName(recordToken, false,
getClass().getClassLoader());
if (Record.class.isAssignableFrom(clazz)) {
return (Class<Record>) clazz;
}
- throw new SecurityException("Attempt to access non-record class "
- + recordToken);
+ throw new SecurityException(
+ "Attempt to access non-record class " + recordToken);
} catch (ClassNotFoundException e) {
- throw new IllegalArgumentException("Non-existent record class "
- + recordToken);
+ throw new IllegalArgumentException(
+ "Non-existent record class " + recordToken);
}
}
public JSONObject getReturnRecord(WriteOperation writeOperation,
Object entityInstance, JSONObject recordObject,
- Set<ConstraintViolation<Object>> violations) throws SecurityException,
- JSONException, IllegalAccessException, InvocationTargetException,
- NoSuchMethodException {
+ Set<ConstraintViolation<Object>> violations,
+ Class<? extends Record> record)
+ throws SecurityException, JSONException, IllegalAccessException,
+ InvocationTargetException, NoSuchMethodException {
// id/futureId, the identifying field is sent back from the incoming record.
JSONObject returnObject = new JSONObject();
final boolean hasViolations = violations != null && !violations.isEmpty();
@@ -415,10 +480,12 @@
case CREATE:
returnObject.put("futureId", recordObject.getString("id"));
if (!hasViolations) {
- returnObject.put("id", encodePropertyValueFromDataStore(
- entityInstance, "id"));
- returnObject.put("version", encodePropertyValueFromDataStore(
- entityInstance, "version"));
+ returnObject.put("id",
+ encodePropertyValueFromDataStore(entityInstance, Long.class,
+ "id", propertyRefs));
+ returnObject.put("version",
+ encodePropertyValueFromDataStore(entityInstance, Integer.class,
+ "version", propertyRefs));
}
break;
case DELETE:
@@ -427,8 +494,9 @@
case UPDATE:
returnObject.put("id", recordObject.getString("id"));
if (!hasViolations) {
- returnObject.put("version", encodePropertyValueFromDataStore(
- entityInstance, "version"));
+ returnObject.put("version",
+ encodePropertyValueFromDataStore(entityInstance, Integer.class,
+ "version", propertyRefs));
}
break;
}
@@ -464,7 +532,7 @@
Set<ConstraintViolation<Object>> violations) throws JSONException {
JSONObject violationsAsJson = new JSONObject();
for (ConstraintViolation<Object> violation : violations) {
- violationsAsJson.put(violation.getPropertyPath().toString(),
+ violationsAsJson.put(violation.getPropertyPath().toString(),
violation.getMessage());
}
return violationsAsJson;
@@ -481,7 +549,13 @@
RequestDefinition operation;
JSONObject topLevelJsonObject = new JSONObject(jsonRequestString);
- String operationName = topLevelJsonObject.getString(RequestData.OPERATION_TOKEN);
+ String operationName = topLevelJsonObject.getString(
+ RequestData.OPERATION_TOKEN);
+
+ String propertyRefsString =
+ topLevelJsonObject.has(RequestData.PROPERTY_REF_TOKEN) ?
+ topLevelJsonObject.getString(RequestData.PROPERTY_REF_TOKEN) : "";
+ propertyRefs = RequestProperty.parse(propertyRefsString);
if (operationName.equals(RequestFactory.SYNC)) {
return sync(topLevelJsonObject.getString(RequestData.CONTENT_TOKEN));
} else {
@@ -490,28 +564,35 @@
Method domainMethod = domainClass.getMethod(
operation.getDomainMethodName(), operation.getParameterTypes());
if (!Modifier.isStatic(domainMethod.getModifiers())) {
- throw new IllegalArgumentException("the " + domainMethod.getName()
- + " is not static");
+ throw new IllegalArgumentException(
+ "the " + domainMethod.getName() + " is not static");
}
Object args[] = getObjectsFromParameterMap(
- getParameterMap(topLevelJsonObject), domainMethod.getParameterTypes());
+ getParameterMap(topLevelJsonObject),
+ domainMethod.getParameterTypes());
Object result = invokeStaticDomainMethod(domainMethod, args);
if ((result instanceof List<?>) != operation.isReturnTypeList()) {
- throw new IllegalArgumentException(String.format(
- "Type mismatch, expected %s%s, but %s returns %s",
- operation.isReturnTypeList() ? "list of " : "",
- operation.getReturnType(), domainMethod,
- domainMethod.getReturnType()));
+ throw new IllegalArgumentException(
+ String.format("Type mismatch, expected %s%s, but %s returns %s",
+ operation.isReturnTypeList() ? "list of " : "",
+ operation.getReturnType(), domainMethod,
+ domainMethod.getReturnType()));
}
if (result instanceof List<?>) {
- return toJsonArray(operation, result);
- } else if (result instanceof Number
- && !(result instanceof BigDecimal || result instanceof BigInteger)) {
+ JSONObject envelop = new JSONObject();
+ envelop.put("result", toJsonArray(operation, result));
+ envelop.put("related", encodeRelatedObjectsToJson());
+ return envelop;
+ } else if (result instanceof Number && !(result instanceof BigDecimal
+ || result instanceof BigInteger)) {
return result;
} else {
+ JSONObject envelop = new JSONObject();
JSONObject jsonObject = toJsonObject(operation, result);
- return jsonObject;
+ envelop.put("result", jsonObject);
+ envelop.put("related", encodeRelatedObjectsToJson());
+ return envelop;
}
}
}
@@ -519,12 +600,18 @@
/**
* returns true if the property has been requested. TODO: use the properties
* that should be coming with the request.
- *
- * @param p the field of entity ref
+ *
+ * @param p the field of entity ref
+ * @param propertyContext the root of the current dotted property reference
* @return has the property value been requested
*/
- public boolean requestedProperty(Property<?> p) {
- return !BLACK_LIST.contains(p.getName());
+ public boolean requestedProperty(Property<?> p,
+ RequestProperty propertyContext) {
+ if (Record.class.isAssignableFrom(p.getType())) {
+ return propertyContext.hasProperty(p.getName());
+ } else {
+ return !BLACK_LIST.contains(p.getName());
+ }
}
public void setOperationRegistry(OperationRegistry operationRegistry) {
@@ -546,8 +633,9 @@
int length = reportArray.length();
if (length == 0) {
- throw new IllegalArgumentException("No json array for "
- + writeOperation.name() + " should have been sent");
+ throw new IllegalArgumentException(
+ "No json array for " + writeOperation.name()
+ + " should have been sent");
}
for (int i = 0; i < length; i++) {
JSONObject recordWithSchema = reportArray.getJSONObject(i);
@@ -585,18 +673,11 @@
/**
* Persist a recordObject of token "recordToken" and return useful information
- * as a JSONObject to return back.
- * <p>
- * Example: recordToken = "Employee", entity = Employee.class, record =
- * EmployeeRecord.class
- *<p>
- * Steps:
- * <ol>
- * <li>assert that each property is present in "EmployeeRecord"
- * <li>invoke "findEmployee (id)" OR new Employee()
- * <li>set various fields on the attached entity and persist OR remove()
- * <li>return data
- * </ol>
+ * as a JSONObject to return back. <p> Example: recordToken = "Employee",
+ * entity = Employee.class, record = EmployeeRecord.class <p> Steps: <ol>
+ * <li>assert that each property is present in "EmployeeRecord" <li>invoke
+ * "findEmployee (id)" OR new Employee() <li>set various fields on the
+ * attached entity and persist OR remove() <li>return data </ol>
*/
public JSONObject updateRecordInDataStore(String recordToken,
JSONObject recordObject, WriteOperation writeOperation) {
@@ -605,7 +686,8 @@
Class<? extends Record> record = getRecordFromClassToken(recordToken);
Class<?> entity = getEntityFromRecordAnnotation(record);
- Map<String, Class<?>> propertiesInRecord = getPropertiesFromRecord(record);
+ Map<String, Class<?>> propertiesInRecord = getPropertiesFromRecord(
+ record);
validateKeys(recordObject, propertiesInRecord.keySet());
updatePropertyTypes(propertiesInRecord, entity);
@@ -664,7 +746,7 @@
// return data back.
return getReturnRecord(writeOperation, entityInstance, recordObject,
- violations);
+ violations, record);
} catch (Exception ex) {
log.severe(String.format("Caught exception [%s] %s",
ex.getClass().getName(), ex.getLocalizedMessage()));
@@ -678,12 +760,31 @@
while (keys.hasNext()) {
String key = (String) keys.next();
if (!declaredProperties.contains(key)) {
- throw new IllegalArgumentException("key " + key
- + " is not permitted to be set");
+ throw new IllegalArgumentException(
+ "key " + key + " is not permitted to be set");
}
}
}
+ private void addRelatedObject(String keyRef, Object returnValue,
+ Class<? extends Record> propertyType, RequestProperty propertyContext)
+ throws JSONException, IllegalAccessException, NoSuchMethodException,
+ InvocationTargetException {
+ Class<? extends Record> clazz =
+ (Class<? extends Record>) returnValue.getClass();
+
+ relatedObjects.put(keyRef, getJsonObject(returnValue, propertyType,
+ propertyContext));
+ }
+
+ private JSONObject encodeRelatedObjectsToJson() throws JSONException {
+ JSONObject array = new JSONObject();
+ for (Map.Entry<String, JSONObject> entry : relatedObjects.entrySet()) {
+ array.put(entry.getKey(), entry.getValue());
+ }
+ return array;
+ }
+
@SuppressWarnings("unchecked")
private Object toJsonArray(RequestDefinition operation, Object result)
throws IllegalAccessException, JSONException, NoSuchMethodException,
@@ -698,7 +799,7 @@
throws JSONException, NoSuchMethodException, IllegalAccessException,
InvocationTargetException {
JSONObject jsonObject = getJsonObject(result,
- (Class<? extends Record>) operation.getReturnType());
+ (Class<? extends Record>) operation.getReturnType(), propertyRefs);
return jsonObject;
}
}
diff --git a/user/src/com/google/gwt/requestfactory/server/OperationRegistry.java b/user/src/com/google/gwt/requestfactory/server/OperationRegistry.java
index 5cb63a6..06c7675 100644
--- a/user/src/com/google/gwt/requestfactory/server/OperationRegistry.java
+++ b/user/src/com/google/gwt/requestfactory/server/OperationRegistry.java
@@ -26,4 +26,6 @@
public interface OperationRegistry {
RequestDefinition getOperation(String operationName);
+
+ RequestSecurityProvider getSecurityProvider();
}
diff --git a/user/src/com/google/gwt/requestfactory/server/ReflectionBasedOperationRegistry.java b/user/src/com/google/gwt/requestfactory/server/ReflectionBasedOperationRegistry.java
index a0deea1..14efd2b 100644
--- a/user/src/com/google/gwt/requestfactory/server/ReflectionBasedOperationRegistry.java
+++ b/user/src/com/google/gwt/requestfactory/server/ReflectionBasedOperationRegistry.java
@@ -151,6 +151,7 @@
}
/**
+
* Turns an operation in the form of package.requestClass::method into a
* RequestDefinition via reflection.
*/
@@ -182,6 +183,10 @@
}
}
+ public RequestSecurityProvider getSecurityProvider() {
+ return securityProvider;
+ }
+
private Method findMethod(Class<?> clazz, String methodName) {
for (Method method : clazz.getDeclaredMethods()) {
if ((method.getModifiers() & Modifier.PUBLIC) != 0) {
diff --git a/user/src/com/google/gwt/requestfactory/server/RequestFactoryServlet.java b/user/src/com/google/gwt/requestfactory/server/RequestFactoryServlet.java
index a52862d..72dc579 100644
--- a/user/src/com/google/gwt/requestfactory/server/RequestFactoryServlet.java
+++ b/user/src/com/google/gwt/requestfactory/server/RequestFactoryServlet.java
@@ -20,6 +20,7 @@
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.logging.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
@@ -88,6 +89,8 @@
}
// TODO: clean exception handling code below.
} catch (Exception e) {
+ Logger.getLogger(getClass().getName()).severe(e.getMessage());
+ e.printStackTrace();
throw new RuntimeException(e);
}
}
diff --git a/user/src/com/google/gwt/requestfactory/server/RequestProperty.java b/user/src/com/google/gwt/requestfactory/server/RequestProperty.java
new file mode 100644
index 0000000..7287a0b
--- /dev/null
+++ b/user/src/com/google/gwt/requestfactory/server/RequestProperty.java
@@ -0,0 +1,137 @@
+/*
+ * 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
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.requestfactory.server;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * <p> <span style="color:red">Experimental API: This class is still under rapid development, and is
+ * very likely to be deleted. Use it at your own risk. </span> </p> Represents one piece in a
+ * property reference sequence.
+ */
+public class RequestProperty implements Iterable<RequestProperty> {
+
+ /**
+ * Merge two property chains.
+ */
+ public static RequestProperty coalesce(RequestProperty... properties) {
+ assert properties.length > 0;
+ RequestProperty root = new RequestProperty("");
+ for (RequestProperty prop : properties) {
+ if ("".equals(prop.getPropertyName())) {
+ for (RequestProperty p : prop) {
+ root.mergeProperty(p);
+ }
+ } else {
+ root.mergeProperty(prop);
+ }
+ }
+ return root;
+ }
+
+ public static RequestProperty parse(String selectors) {
+ String parts[] = selectors.split("\\s*,\\s*");
+ RequestProperty props[] = new RequestProperty[parts.length];
+ for (int i = 0; i < parts.length; i++) {
+ RequestProperty newProp = new RequestProperty("");
+ newProp.parseInternal(parts[i]);
+ props[i] = newProp;
+ }
+ return props.length == 1 ? props[0] : coalesce(props);
+ }
+
+ private String propertyName;
+ private Map<String, RequestProperty> subProperties;
+
+ private RequestProperty(String propertyName) {
+ this.propertyName = propertyName;
+ }
+
+ public RequestProperty add(RequestProperty propertyRef) {
+ if (subProperties == null) {
+ subProperties = new HashMap<String, RequestProperty>();
+ }
+ subProperties.put(propertyRef.getPropertyName(), propertyRef);
+ return this;
+ }
+
+ public RequestProperty getProperty(String propName) {
+ return subProperties == null ? null : subProperties.get(propName);
+ }
+
+ public String getPropertyName() {
+ return propertyName;
+ }
+
+ public boolean hasProperty(String name) {
+ return subProperties == null ? false : subProperties.containsKey(name);
+ }
+
+ public Iterator<RequestProperty> iterator() {
+ return subProperties == null ?
+ (Iterator<RequestProperty>) Collections.EMPTY_MAP.values().iterator() :
+ subProperties.values().iterator();
+ }
+
+ public RequestProperty mergeProperty(RequestProperty property) {
+ RequestProperty foundProp = getProperty(property.getPropertyName());
+ if (foundProp == null && !"".equals(property.getPropertyName())) {
+ add(property);
+ } else {
+ for (RequestProperty p : property) {
+ if (foundProp == null) {
+ add(p);
+ } else {
+ foundProp.mergeProperty(p);
+ }
+ }
+ }
+ return foundProp;
+ }
+
+ private RequestProperty getOrCreate(String part) {
+ RequestProperty prop = getProperty(part);
+ if (prop == null) {
+ prop = new RequestProperty(part);
+ add(prop);
+ }
+ return prop;
+ }
+
+ private RequestProperty parseInternal(String sequence) {
+ int dotIndex = sequence.indexOf('.');
+ String part = dotIndex > -1 ? sequence.substring(0, dotIndex) : sequence;
+ RequestProperty prop = getOrCreate(part);
+ add(prop);
+
+ if (dotIndex > -1) {
+ if (dotIndex < sequence.length() - 1) {
+ String next = sequence.substring(dotIndex + 1);
+ if ("".equals(next)) {
+ throw new IllegalArgumentException("Empty property name '..' not allowed in " + sequence);
+ }
+ if (next.length() > 0) {
+ return prop.parseInternal(next);
+ }
+ }
+ throw new IllegalArgumentException("Trailing '.' in with() call " + sequence);
+ }
+ return prop;
+ }
+}
diff --git a/user/src/com/google/gwt/requestfactory/server/RequestSecurityProvider.java b/user/src/com/google/gwt/requestfactory/server/RequestSecurityProvider.java
index 232eb85..b16dd02 100644
--- a/user/src/com/google/gwt/requestfactory/server/RequestSecurityProvider.java
+++ b/user/src/com/google/gwt/requestfactory/server/RequestSecurityProvider.java
@@ -32,6 +32,11 @@
void checkClass(Class<?> clazz) throws SecurityException;
/**
+ * Encode a Class type into a String token.
+ */
+ String encodeClassType(Class<?> type);
+
+ /**
* Optionally decodes a previously encoded operation. Throws exception if
* argument is not a legal operation.
*/
diff --git a/user/src/com/google/gwt/requestfactory/server/UserInformation.java b/user/src/com/google/gwt/requestfactory/server/UserInformation.java
index 15e871d..a90243f 100644
--- a/user/src/com/google/gwt/requestfactory/server/UserInformation.java
+++ b/user/src/com/google/gwt/requestfactory/server/UserInformation.java
@@ -74,7 +74,7 @@
public static UserInformation getCurrentUserInformation(String redirectUrl) {
UserInformation userInfo = null;
- if (!userInformationImplClass.isEmpty()) {
+ if (!"".equals(userInformationImplClass)) {
try {
userInfo = (UserInformation) Class.forName(
userInformationImplClass).getConstructor(
diff --git a/user/src/com/google/gwt/requestfactory/shared/RecordListRequest.java b/user/src/com/google/gwt/requestfactory/shared/RecordListRequest.java
index 7d741de..ee417a5 100644
--- a/user/src/com/google/gwt/requestfactory/shared/RecordListRequest.java
+++ b/user/src/com/google/gwt/requestfactory/shared/RecordListRequest.java
@@ -22,20 +22,20 @@
import java.util.List;
/**
- * <p>
- * <span style="color:red">Experimental API: This class is still under rapid
+ * <p> <span style="color:red">Experimental API: This class is still under rapid
* development, and is very likely to be deleted. Use it at your own risk.
- * </span>
- * </p>
- * Implemented by RequestObjects for service methods that return lists of
- * records.
- *
+ * </span> </p> Implemented by RequestObjects for service methods that return
+ * lists of records.
+ *
* @param <R> The type held by the returned list
*/
-public interface RecordListRequest<R extends Record> extends
- RequestObject<List<R>> {
+public interface RecordListRequest<R extends Record>
+ extends RequestObject<List<R>> {
+ RecordListRequest<R> with(String... propertyRefs);
+
+ /**
+ * @deprecated use {@link #with(String...)} instead.
+ */
RecordListRequest<R> forProperties(Collection<Property<?>> properties);
-
- RecordListRequest<R> forProperty(Property<?> property);
}
diff --git a/user/src/com/google/gwt/requestfactory/shared/RecordRequest.java b/user/src/com/google/gwt/requestfactory/shared/RecordRequest.java
index a359fb5..db93689 100644
--- a/user/src/com/google/gwt/requestfactory/shared/RecordRequest.java
+++ b/user/src/com/google/gwt/requestfactory/shared/RecordRequest.java
@@ -34,7 +34,11 @@
public interface RecordRequest<R extends Record> extends
RequestObject<R> {
+ RecordRequest<R> with(String... propertyRefs);
+
+ /**
+ * @deprecated use {@link #with(String...)} instead.
+ */
RecordRequest<R> forProperties(Collection<Property<?>> properties);
- RecordRequest<R> forProperty(Property<?> property);
}
diff --git a/user/src/com/google/gwt/requestfactory/shared/RequestData.java b/user/src/com/google/gwt/requestfactory/shared/RequestData.java
index 59e77ec..e6085c7 100644
--- a/user/src/com/google/gwt/requestfactory/shared/RequestData.java
+++ b/user/src/com/google/gwt/requestfactory/shared/RequestData.java
@@ -16,7 +16,9 @@
package com.google.gwt.requestfactory.shared;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
+import java.util.Set;
/**
* <p>
@@ -34,14 +36,19 @@
public static final String CONTENT_TOKEN = "contentData";
public static final String OPERATION_TOKEN = "operation";
public static final String PARAM_TOKEN = "param";
+ public static final String PROPERTY_REF_TOKEN = "propertyRefs";
// TODO: non-final is a hack for now.
private String operation;
private final Object[] parameters;
- public RequestData(String operation, Object[] parameters) {
+ private Set<String> propertyRefs;
+
+ public RequestData(String operation, Object[] parameters,
+ Set<String> propertyRefs) {
this.operation = operation;
this.parameters = parameters;
+ this.propertyRefs = propertyRefs;
}
/**
@@ -64,6 +71,18 @@
if (contentData != null) {
requestMap.put(CONTENT_TOKEN, contentData);
}
+
+ if (propertyRefs != null && !propertyRefs.isEmpty()) {
+ StringBuffer props = new StringBuffer();
+ Iterator<String> propIt = propertyRefs.iterator();
+ while (propIt.hasNext()) {
+ props.append(propIt.next());
+ if (propIt.hasNext()) {
+ props.append(",");
+ }
+ }
+ requestMap.put(PROPERTY_REF_TOKEN, props.toString());
+ }
return requestMap;
}
}
diff --git a/user/src/com/google/gwt/requestfactory/shared/RequestFactory.java b/user/src/com/google/gwt/requestfactory/shared/RequestFactory.java
index c032975..dd80628 100644
--- a/user/src/com/google/gwt/requestfactory/shared/RequestFactory.java
+++ b/user/src/com/google/gwt/requestfactory/shared/RequestFactory.java
@@ -27,8 +27,7 @@
* Marker interface for the RequestFactory code generator.
*/
public interface RequestFactory {
- String JSON_CONTENT_TYPE_UTF8 =
- "application/json; charset=utf-8";
+ static final String JSON_CONTENT_TYPE_UTF8 = "application/json; charset=utf-8";
String SYNC = "SYNC";
@@ -36,7 +35,7 @@
String URL = "gwtRequest";
Record create(Class token);
-
+
void init(HandlerManager eventBus);
// The following methods match the format for the generated sub-interfaces
diff --git a/user/test/com/google/gwt/requestfactory/client/impl/DeltaValueStoreJsonImplTest.java b/user/test/com/google/gwt/requestfactory/client/impl/DeltaValueStoreJsonImplTest.java
index d7f828f..faa4821 100644
--- a/user/test/com/google/gwt/requestfactory/client/impl/DeltaValueStoreJsonImplTest.java
+++ b/user/test/com/google/gwt/requestfactory/client/impl/DeltaValueStoreJsonImplTest.java
@@ -51,6 +51,14 @@
}
throw new IllegalArgumentException("Unknown token " + recordClass);
}
+
+ public RecordSchema<? extends Record> getType(
+ String recordClass) {
+ if (recordClass.equals(SimpleFooRecord.class.getName())) {
+ return SimpleFooRecordImpl.SCHEMA;
+ }
+ throw new IllegalArgumentException("Unknown token " + recordClass);
+ }
};
ValueStoreJsonImpl valueStore = null;
RequestFactoryJsonImpl requestFactory = null;
@@ -71,6 +79,11 @@
return create(token, typeMap);
}
+
+ public RecordSchema getSchema(String token) {
+ return typeMap.getType(token);
+ }
+
@Override
public void init(HandlerManager eventBus) {
// ignore.
@@ -91,7 +104,7 @@
jso.set(SimpleFooRecord.intId, 4);
jso.set(SimpleFooRecord.created, new Date());
jso.setSchema(SimpleFooRecordImpl.SCHEMA);
- valueStore.setRecord(jso);
+ valueStore.setRecord(jso, requestFactory);
}
public void testCreate() {
@@ -188,7 +201,7 @@
RecordImpl mockRecord = new RecordImpl(RecordJsoImpl.create(futureId, 1,
SimpleFooRecordImpl.SCHEMA), RequestFactoryJsonImpl.NOT_FUTURE);
- valueStore.setRecord(mockRecord.asJso()); // marked as non-future..
+ valueStore.setRecord(mockRecord.asJso(), requestFactory); // marked as non-future..
DeltaValueStoreJsonImpl deltaValueStore = new DeltaValueStoreJsonImpl(
valueStore, requestFactory);
diff --git a/user/test/com/google/gwt/requestfactory/client/impl/SimpleFooRecordImpl.java b/user/test/com/google/gwt/requestfactory/client/impl/SimpleFooRecordImpl.java
index 1017275..6c77dc1 100644
--- a/user/test/com/google/gwt/requestfactory/client/impl/SimpleFooRecordImpl.java
+++ b/user/test/com/google/gwt/requestfactory/client/impl/SimpleFooRecordImpl.java
@@ -18,6 +18,7 @@
import com.google.gwt.valuestore.shared.Property;
import com.google.gwt.valuestore.shared.Record;
import com.google.gwt.valuestore.shared.RecordChangedEvent;
+import com.google.gwt.valuestore.shared.SimpleBarRecord;
import com.google.gwt.valuestore.shared.SimpleFooRecord;
import com.google.gwt.valuestore.shared.WriteOperation;
@@ -80,10 +81,14 @@
super(jso, isFuture);
}
+ public SimpleBarRecord getBarField() {
+ return (SimpleBarRecord) getValueStore().getRecordBySchemaAndId(SCHEMA, (Long) (Object) get(barField));
+ }
+
public Boolean getBoolField() {
return get(boolField);
}
-
+
public java.util.Date getCreated() {
return get(created);
}
diff --git a/user/test/com/google/gwt/requestfactory/server/JsonRequestProcessorTest.java b/user/test/com/google/gwt/requestfactory/server/JsonRequestProcessorTest.java
index 82b527f..682f0e7 100644
--- a/user/test/com/google/gwt/requestfactory/server/JsonRequestProcessorTest.java
+++ b/user/test/com/google/gwt/requestfactory/server/JsonRequestProcessorTest.java
@@ -95,12 +95,13 @@
com.google.gwt.valuestore.server.SimpleFoo.reset();
try {
// fetch object
- JSONObject foo = (JSONObject) requestProcessor.processJsonRequest("{ \""
+ JSONObject results = (JSONObject) requestProcessor.processJsonRequest("{ \""
+ RequestData.OPERATION_TOKEN + "\": \""
+ com.google.gwt.valuestore.shared.SimpleFooRequest.class.getName()
+ ReflectionBasedOperationRegistry.SCOPE_SEPARATOR
+ "findSimpleFooById\", " + "\"" + RequestData.PARAM_TOKEN
+ "0\": \"999\" }");
+ JSONObject foo = results.getJSONObject("result");
assertEquals(foo.getInt("id"), 999);
assertEquals(foo.getInt("intId"), 42);
assertEquals(foo.getString("userName"), "GWT");
diff --git a/user/test/com/google/gwt/requestfactory/server/RequestPropertyTest.java b/user/test/com/google/gwt/requestfactory/server/RequestPropertyTest.java
new file mode 100644
index 0000000..e08ca68
--- /dev/null
+++ b/user/test/com/google/gwt/requestfactory/server/RequestPropertyTest.java
@@ -0,0 +1,41 @@
+/*
+ * 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
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.requestfactory.server;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for {@link com.google.gwt.requestfactory.server.RequestProperty} .
+ */
+public class RequestPropertyTest extends TestCase {
+
+ public void testParseMultipleSelector() {
+ RequestProperty prop = RequestProperty.parse("supervisor.name, supervisor.age");
+ RequestProperty sup = prop.getProperty("supervisor");
+ assertNotNull(sup);
+ RequestProperty name = sup.getProperty("name");
+ assertNotNull(name);
+ RequestProperty age = sup.getProperty("age");
+ assertNotNull(name);
+ }
+ public void testParseSingleSelector() {
+ RequestProperty prop = RequestProperty.parse("supervisor.name");
+ RequestProperty sup = prop.getProperty("supervisor");
+ assertNotNull(sup);
+ RequestProperty name = sup.getProperty("name");
+ assertNotNull(name);
+ }
+}
\ No newline at end of file
diff --git a/user/test/com/google/gwt/valuestore/client/RequestFactoryTest.java b/user/test/com/google/gwt/valuestore/client/RequestFactoryTest.java
index b6b2001..132646d 100644
--- a/user/test/com/google/gwt/valuestore/client/RequestFactoryTest.java
+++ b/user/test/com/google/gwt/valuestore/client/RequestFactoryTest.java
@@ -48,6 +48,27 @@
assertEquals(8L, (long) response.getLongField());
assertEquals(com.google.gwt.valuestore.shared.SimpleEnum.FOO,
response.getEnumField());
+ assertEquals(null, response.getBarField());
+ finishTest();
+ }
+ });
+ }
+
+ public void testFetchEntityWithRelation() {
+ SimpleRequestFactory req = GWT.create(SimpleRequestFactory.class);
+ HandlerManager hm = new HandlerManager(null);
+ req.init(hm);
+ delayTestFinish(5000);
+ req.simpleFooRequest().findSimpleFooById(999L).with("barField").fire(
+ new Receiver<SimpleFooRecord>() {
+ public void onSuccess(SimpleFooRecord response,
+ Set<SyncResult> syncResult) {
+ assertEquals(42, (int) response.getIntId());
+ assertEquals("GWT", response.getUserName());
+ assertEquals(8L, (long) response.getLongField());
+ assertEquals(com.google.gwt.valuestore.shared.SimpleEnum.FOO,
+ response.getEnumField());
+ assertNotNull(response.getBarField());
finishTest();
}
});
diff --git a/user/test/com/google/gwt/valuestore/server/SimpleBar.java b/user/test/com/google/gwt/valuestore/server/SimpleBar.java
new file mode 100644
index 0000000..8d2e908
--- /dev/null
+++ b/user/test/com/google/gwt/valuestore/server/SimpleBar.java
@@ -0,0 +1,93 @@
+/*
+ * 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
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.valuestore.server;
+
+import com.google.gwt.requestfactory.shared.Id;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Domain object for SimpleFooRequest.
+ */
+public class SimpleBar {
+
+ static SimpleBar singleton = new SimpleBar();
+
+ public static Long countSimpleBar() {
+ return 1L;
+ }
+
+ public static List<SimpleBar> findAll() {
+ return Collections.singletonList(singleton);
+ }
+
+ public static SimpleBar findSimpleBar(Long id) {
+ return findSimpleBarById(id);
+ }
+
+ public static SimpleBar findSimpleBarById(Long id) {
+ singleton.setId(id);
+ return singleton;
+ }
+
+ public static SimpleBar getSingleton() {
+ return singleton;
+ }
+
+ public static void reset() {
+ singleton = new SimpleBar();
+ }
+
+ Integer version = 1;
+
+ @Id
+ private Long id = -1L;
+
+ private String userName;
+
+ public SimpleBar() {
+ version = 1;
+ userName = "FOO";
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public Integer getVersion() {
+ return version;
+ }
+
+ public void persist() {
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+
+ public void setVersion(Integer version) {
+ this.version = version;
+ }
+}
diff --git a/user/test/com/google/gwt/valuestore/server/SimpleFoo.java b/user/test/com/google/gwt/valuestore/server/SimpleFoo.java
index 6de9282..6c7ac35 100644
--- a/user/test/com/google/gwt/valuestore/server/SimpleFoo.java
+++ b/user/test/com/google/gwt/valuestore/server/SimpleFoo.java
@@ -36,6 +36,7 @@
public static List<SimpleFoo> findAll() {
return Collections.singletonList(singleton);
}
+
public static SimpleFoo findSimpleFoo(Long id) {
return findSimpleFooById(id);
}
@@ -51,11 +52,13 @@
public static void reset() {
singleton = new SimpleFoo();
- };
+ }
+
@SuppressWarnings("unused")
private static Integer privateMethod() {
return 0;
}
+
Integer version = 1;
private Boolean boolField;
@@ -70,6 +73,8 @@
private String userName;
+ private SimpleBar barField;
+
public SimpleFoo() {
intId = 42;
version = 1;
@@ -77,9 +82,14 @@
longField = 8L;
enumField = SimpleEnum.FOO;
created = new Date();
+ barField = SimpleBar.getSingleton();
boolField = true;
}
+ public SimpleBar getBarField() {
+ return barField;
+ }
+
public Boolean getBoolField() {
return boolField;
}
@@ -115,6 +125,10 @@
public void persist() {
}
+ public void setBarField(SimpleBar barField) {
+ this.barField = barField;
+ }
+
public void setBoolField(Boolean bool) {
boolField = bool;
}
diff --git a/user/test/com/google/gwt/valuestore/shared/SimpleBarRecord.java b/user/test/com/google/gwt/valuestore/shared/SimpleBarRecord.java
new file mode 100644
index 0000000..86e85b7
--- /dev/null
+++ b/user/test/com/google/gwt/valuestore/shared/SimpleBarRecord.java
@@ -0,0 +1,33 @@
+/*
+ * 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
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.valuestore.shared;
+
+import com.google.gwt.requestfactory.shared.DataTransferObject;
+import com.google.gwt.valuestore.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.
+ */
+@DataTransferObject(SimpleBar.class)
+public interface SimpleBarRecord extends Record {
+
+ Property<String> userName = new Property<String>("userName", "User Name",
+ String.class);
+
+ String getUserName();
+
+}
\ No newline at end of file
diff --git a/user/test/com/google/gwt/valuestore/shared/SimpleBarRecordChanged.java b/user/test/com/google/gwt/valuestore/shared/SimpleBarRecordChanged.java
new file mode 100644
index 0000000..55b68c0
--- /dev/null
+++ b/user/test/com/google/gwt/valuestore/shared/SimpleBarRecordChanged.java
@@ -0,0 +1,50 @@
+/*
+ * 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
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.valuestore.shared;
+
+import com.google.gwt.event.shared.EventHandler;
+
+/**
+ * Test implementation of {@link com.google.gwt.valuestore.shared.RecordChangedEvent} for
+ * {@link com.google.gwt.valuestore.shared.SimpleFooRecord}.
+ */
+public class SimpleBarRecordChanged extends
+ RecordChangedEvent<SimpleBarRecord, SimpleBarRecordChanged.Handler> {
+
+/**
+ * Test Handler for SimpleFooChanged event.
+ */
+ public interface Handler extends EventHandler {
+ void onSimpleBarRecordChanged(SimpleBarRecordChanged record);
+ }
+
+ public static final Type<Handler> TYPE = new Type<Handler>();
+
+ public SimpleBarRecordChanged(SimpleBarRecord record,
+ WriteOperation writeOperation) {
+ super(record, writeOperation);
+ }
+
+ @Override
+ public Type<Handler> getAssociatedType() {
+ return TYPE;
+ }
+
+ @Override
+ protected void dispatch(Handler handler) {
+ handler.onSimpleBarRecordChanged(this);
+ }
+}
diff --git a/user/test/com/google/gwt/valuestore/shared/SimpleFooRecord.java b/user/test/com/google/gwt/valuestore/shared/SimpleFooRecord.java
index 02b09ff..e0f8c44 100644
--- a/user/test/com/google/gwt/valuestore/shared/SimpleFooRecord.java
+++ b/user/test/com/google/gwt/valuestore/shared/SimpleFooRecord.java
@@ -39,18 +39,23 @@
new EnumProperty<com.google.gwt.valuestore.shared.SimpleEnum>("enumField",
com.google.gwt.valuestore.shared.SimpleEnum.class, SimpleEnum.values());
+ Property<SimpleBarRecord> barField = new Property<SimpleBarRecord>("barField",
+ SimpleBarRecord.class);
+
+ SimpleBarRecord getBarField();
+
Boolean getBoolField();
Date getCreated();
+ com.google.gwt.valuestore.shared.SimpleEnum getEnumField();
+
Integer getIntId();
+ Long getLongField();
+
String getPassword();
String getUserName();
- Long getLongField();
-
- com.google.gwt.valuestore.shared.SimpleEnum getEnumField();
-
-}
\ No newline at end of file
+}