use of org.sablo.specification.WebObjectSpecification.PushToServerEnum in project servoy-client by Servoy.
the class FoundsetLinkedTypeSabloValue method browserUpdatesReceived.
public void browserUpdatesReceived(Object newJSONValue, PropertyDescription wrappedPropertyDescription, PropertyDescription pd, IBrowserConverterContext dataConverterContext, ValueReference<Boolean> returnValueAdjustedIncommingValue) {
PushToServerEnum pushToServer = BrowserConverterContext.getPushToServerValue(dataConverterContext);
if (!wrappedValueInitialized) {
Debug.error("Trying to update state for an uninitialized foundset linked property: " + wrappedPropertyDescription + " | " + webObjectContext);
return;
}
if (!(newJSONValue instanceof JSONArray)) {
// is a data push
if (wrappedSabloValue instanceof DataproviderTypeSabloValue) {
((DataproviderTypeSabloValue) wrappedSabloValue).browserUpdateReceived(newJSONValue, dataConverterContext);
}
return;
}
if ((wrappedPropertyDescription instanceof IPushToServerSpecialType && ((IPushToServerSpecialType) wrappedPropertyDescription).shouldAlwaysAllowIncommingJSON()) || PushToServerEnum.allow.compareTo(pushToServer) <= 0) {
try {
JSONArray updates = (JSONArray) newJSONValue;
for (int i = 0; i < updates.length(); i++) {
JSONObject update = (JSONObject) updates.get(i);
if (update.has(PROPERTY_CHANGE)) {
if (viewPortChangeMonitor != null) {
Debug.error("Trying to update single state value for a foundset linked record dependent property: " + wrappedPropertyDescription + " | " + webObjectContext);
return;
}
Object object = update.get(PROPERTY_CHANGE);
YT newWrappedValue = (YT) JSONUtils.fromJSONUnwrapped(wrappedSabloValue, object, wrappedPropertyDescription, dataConverterContext, returnValueAdjustedIncommingValue);
if (newWrappedValue != wrappedSabloValue) {
// TODO should we make current method return a completely new instance instead and leave component code do the rest?
if (wrappedSabloValue instanceof IDataLinkedPropertyValue)
((IDataLinkedPropertyValue) wrappedSabloValue).detach();
setWrappedSabloValue(newWrappedValue);
if (wrappedSabloValue instanceof IDataLinkedPropertyValue)
((IDataLinkedPropertyValue) wrappedSabloValue).attachToBaseObject(changeMonitor, wrappedComponentContext);
}
} else if (update.has(ViewportDataChangeMonitor.VIEWPORT_CHANGED)) {
if (viewPortChangeMonitor == null) {
Debug.error("Trying to update some record value for a foundset linked non-record dependent property: " + wrappedPropertyDescription + " | " + webObjectContext);
return;
}
// property is linked to a foundset and the value of a property that depends on the record changed client side;
// in this case update DataAdapterList with the correct record and then set the value on the wrapped property
FoundsetTypeSabloValue foundsetPropertyValue = getFoundsetValue();
if (foundsetPropertyValue != null && foundsetPropertyValue.getFoundset() != null) {
JSONObject change = update.getJSONObject(ViewportDataChangeMonitor.VIEWPORT_CHANGED);
String rowIDValue = change.getString(FoundsetTypeSabloValue.ROW_ID_COL_KEY);
Object value = change.get(FoundsetTypeSabloValue.VALUE_KEY);
updatePropertyValueForRecord(foundsetPropertyValue, rowIDValue, value, wrappedPropertyDescription, dataConverterContext);
} else {
Debug.error("Component updates received for record linked property, but component is not linked to a foundset: " + update.get(ViewportDataChangeMonitor.VIEWPORT_CHANGED));
}
}
// as svyApply/data push for DPs does not go through here but through NGFormServiceHandler.executeMethod(String, JSONObject), the
// "setApplyingDPValueFromClient" will be called from there not here...
}
} catch (Exception ex) {
Debug.error(ex);
}
} else {
Debug.error("Foundset linked property (" + pd + ") that doesn't define a suitable pushToServer value (allow/shallow/deep) tried to update proxied value(s) serverside. Denying and sending back server value!");
if (viewPortChangeMonitor != null)
viewPortChangeMonitor.shouldSendWholeViewport();
else
changeMonitor.valueChanged();
}
}
use of org.sablo.specification.WebObjectSpecification.PushToServerEnum in project servoy-client by Servoy.
the class FoundsetTypeSabloValue method changesToJSON.
public JSONWriter changesToJSON(JSONWriter destinationJSON, DataConversion conversionMarkers, IBrowserConverterContext dataConverterContext) throws JSONException {
if (changeMonitor.shouldSendAll())
return toJSON(destinationJSON, conversionMarkers, dataConverterContext);
else {
// so that the client knows it must use the custom client side JS for what JSON it gets
if (conversionMarkers != null)
conversionMarkers.convert(FoundsetPropertyType.TYPE_NAME);
rowDataProvider.initializeIfNeeded(dataConverterContext);
boolean somethingChanged = false;
// change monitor already takes care not to report duplicates here (like whole viewport + viewport bounds)
if (changeMonitor.shouldSendFoundsetSize()) {
destinationJSON.object();
destinationJSON.key(UPDATE_PREFIX + SERVER_SIZE).value(getFoundset() != null ? getFoundset().getSize() : 0);
somethingChanged = true;
}
if (changeMonitor.shouldSendFoundsetID()) {
if (!somethingChanged)
destinationJSON.object();
destinationJSON.key(UPDATE_PREFIX + FOUNDSET_ID).value(getFoundset() != null ? getFoundset().getID() : 0);
somethingChanged = true;
}
if (changeMonitor.shouldSendFoundsetDefinitionChange()) {
if (!somethingChanged)
destinationJSON.object();
destinationJSON.key(UPDATE_PREFIX + FOUNDSET_DEFINITION).value(true);
somethingChanged = true;
}
if (changeMonitor.shouldSendPushToServer()) {
PushToServerEnum pushToServer = BrowserConverterContext.getPushToServerValue(dataConverterContext);
if (pushToServer == PushToServerEnum.shallow || pushToServer == PushToServerEnum.deep) {
if (!somethingChanged)
destinationJSON.object();
destinationJSON.key(UPDATE_PREFIX + PUSH_TO_SERVER).value(pushToServer == PushToServerEnum.shallow ? false : true);
somethingChanged = true;
}
}
if (changeMonitor.shouldSendFoundsetSort()) {
if (!somethingChanged)
destinationJSON.object();
destinationJSON.key(UPDATE_PREFIX + SORT).value(getSortStringAsNames());
somethingChanged = true;
}
if (changeMonitor.shouldSendHadMoreRows()) {
if (!somethingChanged)
destinationJSON.object();
destinationJSON.key(UPDATE_PREFIX + HAS_MORE_ROWS).value(getFoundset() != null ? getFoundset().hadMoreRows() : false);
somethingChanged = true;
}
if (changeMonitor.shouldSendMultiSelect()) {
if (!somethingChanged)
destinationJSON.object();
destinationJSON.key(UPDATE_PREFIX + MULTI_SELECT).value(getFoundset() != null ? getFoundset().isMultiSelect() : false);
somethingChanged = true;
}
if (changeMonitor.shouldSendSelectedIndexes()) {
if (!somethingChanged)
destinationJSON.object();
destinationJSON.key(UPDATE_PREFIX + SELECTED_ROW_INDEXES);
addSelectedIndexes(destinationJSON);
if (changeMonitor.shouldSendUserSetSelection()) {
destinationJSON.key(UPDATE_PREFIX + USER_SET_SELECTION).value(true);
}
somethingChanged = true;
}
somethingChanged = addHandledClientRequestIdsIfNeeded(destinationJSON, somethingChanged);
if (changeMonitor.shouldSendColumnFormats()) {
if (!somethingChanged)
destinationJSON.object();
writeColumnFormatsIfNeededAndAvailable(destinationJSON, dataConverterContext, true);
somethingChanged = true;
}
if (changeMonitor.shouldSendWholeViewPort()) {
changeMonitor.clearChanges();
if (!somethingChanged)
destinationJSON.object();
destinationJSON.key(UPDATE_PREFIX + VIEW_PORT);
addViewPort(destinationJSON);
somethingChanged = true;
} else {
boolean viewPortUpdateAdded = false;
if (changeMonitor.shouldSendViewPortBounds()) {
if (!somethingChanged)
destinationJSON.object();
destinationJSON.key(UPDATE_PREFIX + VIEW_PORT).object();
viewPortUpdateAdded = true;
addViewPortBounds(destinationJSON);
somethingChanged = true;
}
if (changeMonitor.hasViewportChanges()) {
ArrayOperation[] viewPortChanges = changeMonitor.getViewPortChanges();
changeMonitor.clearChanges();
if (!somethingChanged)
destinationJSON.object();
if (!viewPortUpdateAdded) {
destinationJSON.key(UPDATE_PREFIX + VIEW_PORT).object();
viewPortUpdateAdded = true;
}
DataConversion clientConversionInfo = new DataConversion();
clientConversionInfo.pushNode(UPDATE_PREFIX + ROWS);
destinationJSON.key(UPDATE_PREFIX + ROWS).array();
for (int i = 0; i < viewPortChanges.length; i++) {
clientConversionInfo.pushNode(String.valueOf(i));
FoundsetPropertyType.writeViewportOperationToJSON(viewPortChanges[i], rowDataProvider, foundset, viewPort.getStartIndex(), destinationJSON, null, clientConversionInfo, null);
clientConversionInfo.popNode();
}
clientConversionInfo.popNode();
destinationJSON.endArray();
// conversion info for websocket traffic (for example Date objects will turn into long)
JSONUtils.writeClientConversions(destinationJSON, clientConversionInfo);
somethingChanged = true;
} else
// changes have to be cleared anyway
changeMonitor.clearChanges();
if (viewPortUpdateAdded)
destinationJSON.endObject();
}
if (somethingChanged)
destinationJSON.endObject();
else {
// no change yet we are still asked to send changes (so not full value); send a dummy NO_OP
destinationJSON.object().key(NO_OP).value(true).endObject();
}
return destinationJSON;
}
}
use of org.sablo.specification.WebObjectSpecification.PushToServerEnum in project servoy-client by Servoy.
the class FoundsetTypeSabloValue method browserUpdatesReceived.
public void browserUpdatesReceived(Object jsonValue, PropertyDescription pd, IBrowserConverterContext dataConverterContext) {
PushToServerEnum pushToServer = BrowserConverterContext.getPushToServerValue(dataConverterContext);
if (getFoundset() == null)
return;
try {
if (jsonValue instanceof JSONArray) {
JSONArray arr = (JSONArray) jsonValue;
for (int i = 0; i < arr.length(); i++) {
JSONObject update = (JSONObject) arr.get(i);
// {newViewPort: {startIndex : startIndex, size : size}}
if (update.has("newViewPort")) {
JSONObject newViewport = update.getJSONObject("newViewPort");
int requestID = update.getInt(ID_KEY);
viewPort.clearSendingInitialPreferredViewport();
viewPort.setBounds(newViewport.getInt(START_INDEX), newViewport.getInt(SIZE));
changeMonitor.requestIdHandled(requestID, true);
}
if (update.has(PREFERRED_VIEWPORT_SIZE)) {
viewPort.setPreferredViewportSize(update.getInt(PREFERRED_VIEWPORT_SIZE));
if (update.has(FoundsetPropertyTypeConfig.SEND_SELECTION_VIEWPORT_INITIALLY))
viewPort.setSendSelectionViewportInitially(update.getBoolean(FoundsetPropertyTypeConfig.SEND_SELECTION_VIEWPORT_INITIALLY));
if (update.has(INITIAL_SELECTION_VIEWPORT_CENTERED))
viewPort.setInitialSelectionViewportCentered(update.getBoolean(INITIAL_SELECTION_VIEWPORT_CENTERED));
} else // {loadExtraRecords: negativeOrPositiveCount}
if (update.has("loadExtraRecords")) {
int requestID = update.getInt(ID_KEY);
viewPort.clearSendingInitialPreferredViewport();
viewPort.loadExtraRecords(update.getInt("loadExtraRecords"));
changeMonitor.requestIdHandled(requestID, true);
} else // {loadLessRecords: negativeOrPositiveCount}
if (update.has("loadLessRecords")) {
int requestID = update.getInt(ID_KEY);
viewPort.clearSendingInitialPreferredViewport();
viewPort.loadLessRecords(update.getInt("loadLessRecords"));
changeMonitor.requestIdHandled(requestID, true);
} else if (update.has("sort")) {
int requestID = update.getInt(ID_KEY);
JSONArray columns = update.getJSONArray("sort");
StringBuilder sort = new StringBuilder();
Map<String, String> dp = dataproviders.size() > 0 ? dataproviders : recordDataLinkedPropertyIDToColumnDP;
String dataProviderID = null;
boolean sortAscending = true;
for (int j = 0; j < columns.length(); j++) {
JSONObject sortColumn = columns.getJSONObject(j);
String name = sortColumn.getString("name");
if (dp.containsKey(name)) {
sort.append(dp.get(name));
sort.append(" " + sortColumn.getString("direction"));
if (dataProviderID == null) {
dataProviderID = dp.get(name);
sortAscending = "asc".equalsIgnoreCase(sortColumn.getString("direction"));
}
if (j < columns.length() - 1)
sort.append(",");
}
}
IWebFormUI formUI = getFormUI();
IWebFormController fc = (formUI != null ? formUI.getController() : null);
if (fc != null && fc.getForm().getOnSortCmdMethodID() > 0 && dataProviderID != null) {
// our api only supports one dataproviderid sort at a time
JSEvent event = new JSEvent();
event.setFormName(fc.getName());
fc.executeFunction(String.valueOf(fc.getForm().getOnSortCmdMethodID()), Utils.arrayMerge((new Object[] { dataProviderID, Boolean.valueOf(sortAscending), event }), // $NON-NLS-1$
Utils.parseJSExpressions(fc.getForm().getFlattenedMethodArguments("onSortCmdMethodID"))), true, null, false, // $NON-NLS-1$
"onSortCmdMethodID");
} else {
try {
String currentSort = foundset.getSort();
String newSort = sort.toString();
foundset.setSort(newSort);
if (// really a new sort
!Utils.equalObjects(currentSort, newSort) || // not sorted, send back to client
!Utils.equalObjects(foundset.getSort(), newSort)) {
changeMonitor.foundsetSortChanged();
}
} catch (ServoyException e) {
Debug.error("Cannot sort foundset by " + sort.toString(), e);
}
}
changeMonitor.requestIdHandled(requestID, true);
} else // {newClientSelection: newSelectedIndexesArray}
if (update.has("newClientSelection")) {
JSONArray jsonSelectedIndexes = update.getJSONArray("newClientSelection");
int[] newSelectedIndexes = new int[jsonSelectedIndexes.length()];
for (int j = newSelectedIndexes.length - 1; j >= 0; j--) {
newSelectedIndexes[j] = jsonSelectedIndexes.getInt(j);
}
// this !Arrays.equals check in conjunction with pause()/resume() is needed to avoid an effect on the client that server always sends back changed selection in which case
// if the user quickly changes selection multiple times and the connection is slow, selection will jump all over
// the place until it stabilizes correctly
getListSelectionListener().pause();
try {
if (newSelectedIndexes.length == 1) {
foundset.setSelectedIndex(newSelectedIndexes[0]);
} else {
foundset.setSelectedIndexes(newSelectedIndexes);
}
} finally {
getListSelectionListener().resume();
// if server denies the new selection as invalid and doesn't change selection, send it to the client so that it doesn't keep invalid selection
if (!Arrays.equals(foundset.getSelectedIndexes(), newSelectedIndexes)) {
changeMonitor.selectionChanged(false);
}
}
} else // {newClientSelectionRequest: newSelectedIndexesArray}
if (update.has("newClientSelectionRequest")) {
int requestID = update.getInt(ID_KEY);
JSONArray jsonSelectedIndexes = update.getJSONArray("newClientSelectionRequest");
int[] newSelectedIndexes = new int[jsonSelectedIndexes.length()];
for (int j = newSelectedIndexes.length - 1; j >= 0; j--) {
newSelectedIndexes[j] = jsonSelectedIndexes.getInt(j);
}
int[] oldSelection = foundset.getSelectedIndexes();
// this !Arrays.equals check in conjunction with pause()/resume() is needed to avoid an effect on the client that server always sends back changed selection in which case
// if the user quickly changes selection multiple times and the connection is slow, selection will jump all over
// the place until it stabilizes correctly
getListSelectionListener().pause();
try {
if (newSelectedIndexes.length == 1) {
foundset.setSelectedIndex(newSelectedIndexes[0]);
} else {
foundset.setSelectedIndexes(newSelectedIndexes);
}
} finally {
getListSelectionListener().resume();
if (!Arrays.equals(oldSelection, foundset.getSelectedIndexes())) {
// if the selection is changed, send it back to the client so that its model is also updated
changeMonitor.selectionChanged(false);
changeMonitor.requestIdHandled(requestID, true);
} else {
if (!Arrays.equals(oldSelection, newSelectedIndexes)) {
// it was supposed to change but the server did not allow it
changeMonitor.requestIdHandled(requestID, false);
} else
changeMonitor.requestIdHandled(requestID, true);
}
}
} else if (update.has(ViewportDataChangeMonitor.VIEWPORT_CHANGED)) {
if (PushToServerEnum.allow.compareTo(pushToServer) <= 0) {
// {dataChanged: { ROW_ID_COL_KEY: rowIDValue, dataproviderName: value }}
JSONObject dataChangeJSON = (JSONObject) update.get(ViewportDataChangeMonitor.VIEWPORT_CHANGED);
String rowIDValue = dataChangeJSON.getString(ROW_ID_COL_KEY);
String dpKey = dataChangeJSON.getString(DATAPROVIDER_KEY);
String dataProviderName;
if (dataproviders.containsKey(dpKey)) {
dataProviderName = dataproviders.get(dpKey);
} else {
dataProviderName = recordDataLinkedPropertyIDToColumnDP.get(dpKey);
}
Object value = dataChangeJSON.get(VALUE_KEY);
if (foundset != null) {
Pair<String, Integer> splitHashAndIndex = splitPKHashAndIndex(rowIDValue);
int recordIndex = foundset.getRecordIndex(splitHashAndIndex.getLeft(), splitHashAndIndex.getRight().intValue());
if (recordIndex != -1) {
IRecordInternal record = foundset.getRecord(recordIndex);
// convert Dates where it's needed
// this should be enough for when only foundset dataproviders are used
PropertyDescription dataProviderPropDesc = getDataProviderPropertyDescription(dataProviderName);
ValueReference<Boolean> returnValueAdjustedIncommingValueForRow = new ValueReference<Boolean>(Boolean.FALSE);
value = JSONUtils.fromJSONUnwrapped(null, value, dataProviderPropDesc, dataConverterContext, returnValueAdjustedIncommingValueForRow);
try {
if (record.startEditing()) {
try {
record.setValue(dataProviderName, value);
} catch (IllegalArgumentException e) {
// TODO handle the validaton errors.
IWebFormUI formUI = getFormUI();
formUI.getController().getApplication().reportError("Validation for " + dataProviderName + " for value: " + value + " failed.", e);
}
}
// else cannot start editing; finally block will deal with it (send old value back to client as new one can't be pushed)
} finally {
// if server denies the new value as invalid and doesn't change it, send it to the client so that it doesn't keep invalid value; the same if for example a double was rounded to an int
if (!Utils.equalObjects(record.getValue(dataProviderName), value) || returnValueAdjustedIncommingValueForRow.value.booleanValue()) {
changeMonitor.recordsUpdated(recordIndex, recordIndex, foundset.getSize(), viewPort, Arrays.asList(new String[] { dataProviderName }));
}
}
} else {
Debug.error("Cannot set foundset record (" + rowIDValue + ") dataprovider '" + dataProviderName + "' to value '" + value + ". Record not found.");
}
}
} else {
log.error("Property (" + pd + ") that doesn't define a suitable pushToServer value (allow/shallow/deep) tried to modify foundset dataprovider value serverside. Denying and sending back full viewport!");
changeMonitor.viewPortCompletelyChanged();
}
}
}
}
} catch (JSONException e) {
Debug.error("Error when getting browser updates for property (" + this.toString() + ") for " + jsonValue, e);
}
}
use of org.sablo.specification.WebObjectSpecification.PushToServerEnum in project servoy-client by Servoy.
the class FoundsetTypeSabloValue method toJSON.
public JSONWriter toJSON(JSONWriter destinationJSON, DataConversion conversionMarkers, IBrowserConverterContext dataConverterContext) throws JSONException {
// so that the client knows it must use the custom client side JS for what JSON it gets
if (conversionMarkers != null)
conversionMarkers.convert(FoundsetPropertyType.TYPE_NAME);
rowDataProvider.initializeIfNeeded(dataConverterContext);
destinationJSON.object();
PushToServerEnum pushToServer = BrowserConverterContext.getPushToServerValue(dataConverterContext);
if (pushToServer == PushToServerEnum.shallow || pushToServer == PushToServerEnum.deep) {
destinationJSON.key(PUSH_TO_SERVER).value(pushToServer == PushToServerEnum.shallow ? false : true);
}
destinationJSON.key(SERVER_SIZE).value(getFoundset() != null ? getFoundset().getSize() : 0);
if (getFoundset() != null)
destinationJSON.key(FOUNDSET_ID).value(getFoundset().getID());
destinationJSON.key(SORT).value(getSortStringAsNames());
destinationJSON.key(SELECTED_ROW_INDEXES);
addSelectedIndexes(destinationJSON);
destinationJSON.key(MULTI_SELECT).value(getFoundset() != null ? getFoundset().isMultiSelect() : false);
destinationJSON.key(HAS_MORE_ROWS).value(getFoundset() != null ? getFoundset().hadMoreRows() : false);
writeColumnFormatsIfNeededAndAvailable(destinationJSON, dataConverterContext, false);
addHandledClientRequestIdsIfNeeded(destinationJSON, true);
// viewPort
destinationJSON.key(VIEW_PORT);
changeMonitor.clearChanges();
addViewPort(destinationJSON);
// end viewPort
destinationJSON.endObject();
return destinationJSON;
}
use of org.sablo.specification.WebObjectSpecification.PushToServerEnum in project servoy-client by Servoy.
the class FoundsetLinkedTypeSabloValue method fullToJSON.
public JSONWriter fullToJSON(JSONWriter writer, String key, DataConversion clientConversion, PropertyDescription wrappedPropertyDescription, IBrowserConverterContext dataConverterContext) throws JSONException {
if (!wrappedValueInitialized) {
Debug.warn("Trying to get full value from an uninitialized foundset linked property: " + wrappedPropertyDescription);
return writer;
}
clientConversion.convert(FoundsetLinkedPropertyType.CONVERSION_NAME);
JSONUtils.addKeyIfPresent(writer, key);
writer.object();
writer.key(FoundsetLinkedPropertyType.FOR_FOUNDSET_PROPERTY_NAME).value(forFoundsetPropertyName);
if (idForFoundset != null)
writer.key(ID_FOR_FOUNDSET).value(idForFoundset == null ? JSONObject.NULL : idForFoundset);
idForFoundsetChanged = false;
PushToServerEnum pushToServer = BrowserConverterContext.getPushToServerValue(dataConverterContext);
if (pushToServer == PushToServerEnum.shallow || pushToServer == PushToServerEnum.deep) {
writer.key(PUSH_TO_SERVER).value(pushToServer == PushToServerEnum.shallow ? false : true);
}
if (viewPortChangeMonitor == null) {
// single value; not record dependent
DataConversion dataConversions = new DataConversion();
dataConversions.pushNode(FoundsetLinkedPropertyType.SINGLE_VALUE);
FullValueToJSONConverter.INSTANCE.toJSONValue(writer, FoundsetLinkedPropertyType.SINGLE_VALUE, wrappedSabloValue, wrappedPropertyDescription, dataConversions, dataConverterContext);
JSONUtils.writeClientConversions(writer, dataConversions);
} else {
viewPortChangeMonitor.getRowDataProvider().initializeIfNeeded(dataConverterContext);
viewPortChangeMonitor.clearChanges();
// record dependent; viewport value
writeWholeViewportToJSON(writer);
viewPortChangeMonitor.doneWritingChanges();
}
writer.endObject();
return writer;
}
Aggregations