/* * Copyright (C) 2014 Servoy BV * * 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.servoy.j2db.server.ngclient.property.types; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.json.JSONException; import org.json.JSONObject; import org.json.JSONWriter; import org.mozilla.javascript.Scriptable; import org.sablo.BaseWebObject; import org.sablo.specification.PropertyDescription; import org.sablo.specification.property.IBrowserConverterContext; import org.sablo.specification.property.IConvertedPropertyType; import org.sablo.specification.property.IPushToServerSpecialType; import org.sablo.specification.property.types.DefaultPropertyType; import org.sablo.util.ValueReference; import org.sablo.websocket.utils.DataConversion; import com.servoy.base.persistence.constants.IFormConstants; import com.servoy.base.persistence.constants.IValueListConstants; import com.servoy.j2db.FlattenedSolution; import com.servoy.j2db.component.ComponentFormat; import com.servoy.j2db.dataprocessing.BufferedDataSet; import com.servoy.j2db.dataprocessing.CustomValueList; import com.servoy.j2db.dataprocessing.IDataSet; import com.servoy.j2db.dataprocessing.IValueList; import com.servoy.j2db.dataprocessing.JSDataSet; import com.servoy.j2db.dataprocessing.ValueListFactory; import com.servoy.j2db.persistence.ColumnWrapper; import com.servoy.j2db.persistence.IColumn; import com.servoy.j2db.persistence.IColumnTypes; import com.servoy.j2db.persistence.IDataProvider; import com.servoy.j2db.persistence.StaticContentSpecLoader; import com.servoy.j2db.persistence.ValueList; import com.servoy.j2db.server.ngclient.ColumnBasedValueList; import com.servoy.j2db.server.ngclient.DataAdapterList; import com.servoy.j2db.server.ngclient.FormElement; import com.servoy.j2db.server.ngclient.FormElementContext; import com.servoy.j2db.server.ngclient.INGApplication; import com.servoy.j2db.server.ngclient.INGFormElement; import com.servoy.j2db.server.ngclient.IWebFormUI; import com.servoy.j2db.server.ngclient.WebFormComponent; import com.servoy.j2db.server.ngclient.WebFormUI; import com.servoy.j2db.server.ngclient.property.ValueListConfig; import com.servoy.j2db.server.ngclient.property.types.NGConversions.IFormElementToSabloComponent; import com.servoy.j2db.server.ngclient.property.types.NGConversions.IRhinoToSabloComponent; import com.servoy.j2db.server.ngclient.property.types.NGConversions.ISabloComponentToRhino; import com.servoy.j2db.util.Debug; import com.servoy.j2db.util.FormatParser.ParsedFormat; import com.servoy.j2db.util.UUID; import com.servoy.j2db.util.Utils; /** * Property type that handles valuelist typed properties. * * @author acostescu * @author jcompagner */ @SuppressWarnings("nls") public class ValueListPropertyType extends DefaultPropertyType<ValueListTypeSabloValue> implements IConvertedPropertyType<ValueListTypeSabloValue>, IFormElementToSabloComponent<Object, ValueListTypeSabloValue>, ISupportTemplateValue<Object>, IDataLinkedType<Object, ValueListTypeSabloValue>, IRhinoToSabloComponent<ValueListTypeSabloValue>, ISabloComponentToRhino<ValueListTypeSabloValue>, IPushToServerSpecialType { public static final ValueListPropertyType INSTANCE = new ValueListPropertyType(); public static final String TYPE_NAME = "valuelist"; protected ValueListPropertyType() { } @Override public String getName() { return TYPE_NAME; } @Override public Object parseConfig(JSONObject json) { String dataprovider = ""; String def = null; boolean canOptimize = true; if (json != null) { dataprovider = json.optString("for"); def = json.optString("default"); canOptimize = json.optBoolean("canOptimize", true); } return new ValueListConfig(dataprovider, def, canOptimize); } @Override public boolean valueInTemplate(Object object, PropertyDescription pd, FormElementContext formElementContext) { return false; } @Override public ValueListTypeSabloValue fromJSON(Object newJSONValue, ValueListTypeSabloValue previousSabloValue, PropertyDescription pd, IBrowserConverterContext dataConverterContext, ValueReference<Boolean> returnValueAdjustedIncommingValue) { // handle any valuelist specific websocket incomming traffic if (previousSabloValue != null && newJSONValue instanceof String) { // currently the only thing that can come from client is a filter request... previousSabloValue.filterValuelist((String)newJSONValue); } else Debug.error("Got a client update for valuelist property, but valuelist is null or value can't be interpreted: " + newJSONValue + "."); return previousSabloValue; } @Override public JSONWriter toJSON(JSONWriter writer, String key, ValueListTypeSabloValue sabloValue, PropertyDescription pd, DataConversion clientConversion, IBrowserConverterContext dataConverterContext) throws JSONException { if (sabloValue != null) { boolean checkIfChanged = false; // TODO we should have type info here to send instead of null for real/display values if (dataConverterContext != null) { BaseWebObject webObject = dataConverterContext.getWebObject(); // if not writing properties, then it means the valuelist is already on the client, so // send it only if it is changed if (webObject instanceof WebFormComponent) { WebFormComponent wfc = (WebFormComponent)webObject; int formViewType = wfc.getFormElement().getForm().getView(); if (sabloValue.config.canOptimize() && (formViewType == IFormConstants.VIEW_TYPE_RECORD || formViewType == IFormConstants.VIEW_TYPE_RECORD_LOCKED) && !wfc.isWritingComponentProperties()) { checkIfChanged = true; } } } sabloValue.toJSON(writer, key, clientConversion, checkIfChanged); } return writer; } @Override public ValueListTypeSabloValue toSabloComponentValue(Object formElementValue, PropertyDescription pd, INGFormElement formElement, WebFormComponent component, DataAdapterList dataAdapterList) { ValueList val = null; IValueList valueList = null; ValueListConfig config = (ValueListConfig)pd.getConfig(); String dataproviderID = (pd.getConfig() != null ? (String)formElement.getPropertyValue(config.getFor()) : null); valueList = getIValueList(formElementValue, pd, formElement, component, dataAdapterList, val, valueList, config, dataproviderID); return valueList != null ? new ValueListTypeSabloValue(valueList, dataAdapterList, config, dataproviderID, pd, getComponentFormat(pd, dataAdapterList, formElement, config, dataproviderID)) : null; } /** * @param formElementValue * @param pd * @param formElement * @param component * @param dataAdapterList * @param val * @param valueList * @param config * @param dataproviderID * @return */ protected IValueList getIValueList(Object formElementValue, PropertyDescription pd, INGFormElement formElement, WebFormComponent component, DataAdapterList dataAdapterList, ValueList val, IValueList valueList, ValueListConfig config, String dataproviderID) { int valuelistID = Utils.getAsInteger(formElementValue); INGApplication application = dataAdapterList.getApplication(); if (valuelistID > 0) { val = application.getFlattenedSolution().getValueList(valuelistID); } else { UUID uuid = Utils.getAsUUID(formElementValue, false); if (uuid != null) val = (ValueList)application.getFlattenedSolution().searchPersist(uuid); } if (val != null) { ComponentFormat fieldFormat = getComponentFormat(pd, dataAdapterList, formElement, config, dataproviderID); valueList = getRealValueList(application, val, fieldFormat, dataproviderID); } else { if ("autoVL".equals(config.getDefaultValue())) { String dp = (String)formElement.getPropertyValue(StaticContentSpecLoader.PROPERTY_DATAPROVIDERID.getPropertyName()); IWebFormUI formUI = component.findParent(WebFormUI.class); if (dp != null && formUI.getController().getTable() != null && formUI.getController().getTable().getColumnType(dp) != 0) { valueList = new ColumnBasedValueList(application, formElement.getForm().getServerName(), formElement.getForm().getTableName(), (String)formElement.getPropertyValue(StaticContentSpecLoader.PROPERTY_DATAPROVIDERID.getPropertyName())); } else { // not supported empty valuelist (based on relations) just return an empty valuelist valueList = new CustomValueList(application, null, "", false, IColumnTypes.TEXT, null); } } } return valueList; } protected ComponentFormat getComponentFormat(PropertyDescription pd, DataAdapterList dataAdapterList, INGFormElement formElement, ValueListConfig config, String dataproviderID) { String format = null; INGApplication application = dataAdapterList.getApplication(); if (dataproviderID != null) { Collection<PropertyDescription> properties = formElement.getProperties(FormatPropertyType.INSTANCE); for (PropertyDescription formatPd : properties) { // compare whether format and valuelist property are for same property (dataprovider) or if format is used for valuelist property itself if (formatPd.getConfig() instanceof String[] && ((String[])formatPd.getConfig()).length > 0 && (config.getFor().equals(((String[])formatPd.getConfig())[0]) || pd.getName().equals(((String[])formatPd.getConfig())[0]))) { format = (String)formElement.getPropertyValue(formatPd.getName()); break; } } } return ComponentFormat.getComponentFormat(format, dataproviderID, application.getFlattenedSolution().getDataproviderLookup(application.getFoundSetManager(), dataAdapterList.getForm().getForm()), application); } protected IValueList getRealValueList(INGApplication application, ValueList val, ComponentFormat fieldFormat, String dataproviderID) { return com.servoy.j2db.component.ComponentFactory.getRealValueList(application, val, true, fieldFormat.dpType, fieldFormat.parsedFormat, dataproviderID); } @Override public TargetDataLinks getDataLinks(Object formElementValue, PropertyDescription pd, FlattenedSolution flattenedSolution, FormElement formElement) { if (formElementValue instanceof IValueList) { IDataProvider[] dependedDataProviders = ((IValueList)formElementValue).getDependedDataProviders(); if (dependedDataProviders == null) return TargetDataLinks.NOT_LINKED_TO_DATA; if (dependedDataProviders.length == 0) return TargetDataLinks.LINKED_TO_ALL; boolean recordLinked = false; String[] dataproviders = new String[dependedDataProviders.length]; for (int i = 0; i < dataproviders.length; i++) { dataproviders[i] = dependedDataProviders[i].getDataProviderID(); recordLinked = recordLinked || (dependedDataProviders[i] instanceof IColumn || dependedDataProviders[i] instanceof ColumnWrapper); } return new TargetDataLinks(dataproviders, recordLinked); } return null; } @Override public ValueListTypeSabloValue toSabloComponentValue(Object rhinoValue, ValueListTypeSabloValue previousComponentValue, PropertyDescription pd, BaseWebObject componentOrService) { Object vl = componentOrService.getProperty(pd.getName()); ParsedFormat format = null; int type = -1; if (vl != null) { ValueListTypeSabloValue value = (ValueListTypeSabloValue)vl; INGApplication application = value.dataAdapterList.getApplication(); IValueList list = value.getValueList(); IValueList newVl = null; if (list != null && list instanceof CustomValueList && (rhinoValue instanceof JSDataSet || rhinoValue instanceof IDataSet)) { String name = list.getName(); ValueList valuelist = application.getFlattenedSolution().getValueList(name); if (valuelist != null && valuelist.getValueListType() == IValueListConstants.CUSTOM_VALUES) { format = ((CustomValueList)list).getFormat(); type = ((CustomValueList)list).getValueType(); newVl = ValueListFactory.fillRealValueList(application, valuelist, IValueListConstants.CUSTOM_VALUES, format, type, rhinoValue); } } ValueListConfig config = (ValueListConfig)pd.getConfig(); String dataproviderID = (componentOrService.getProperty(config.getFor()) != null ? ((DataproviderTypeSabloValue)componentOrService.getProperty(config.getFor())).getDataProviderID() : null); return newVl != null ? new ValueListTypeSabloValue(newVl, value.dataAdapterList, config, dataproviderID, pd, new ComponentFormat(format, type, type)) : previousComponentValue; } return null; } @Override public boolean isValueAvailableInRhino(ValueListTypeSabloValue webComponentValue, PropertyDescription pd, BaseWebObject componentOrService) { return webComponentValue != null; } @Override public Object toRhinoValue(ValueListTypeSabloValue webComponentValue, PropertyDescription pd, BaseWebObject componentOrService, Scriptable startScriptable) { if (webComponentValue != null) { try { INGApplication application = webComponentValue.dataAdapterList.getApplication(); if (webComponentValue.valueList != null) { List<Object[]> rows = new ArrayList<Object[]>(); for (int i = 0; i < webComponentValue.valueList.getSize(); i++) { rows.add(new Object[] { webComponentValue.valueList.getElementAt(i), webComponentValue.valueList.getRealElementAt(i) }); } return new JSDataSet(application, new BufferedDataSet(new String[] { "displayValue", "realValue" }, //$NON-NLS-1$ //$NON-NLS-2$ rows)); } } catch (Exception e) { Debug.error(e); } } return null; } @Override public boolean shouldAlwaysAllowIncommingJSON() { return true; // fromJSON can only filter stuff so it shouldn't be a problem (it's not setting anything from client to server) } }