/* * 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.sql.Types; import java.text.DecimalFormatSymbols; import java.util.HashMap; import java.util.Locale; import java.util.Map; import org.json.JSONArray; 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.IYieldingType; import org.sablo.specification.PropertyDescription; import org.sablo.specification.property.IBrowserConverterContext; import org.sablo.specification.property.IConvertedPropertyType; import org.sablo.specification.property.IPropertyType; import org.sablo.specification.property.types.DefaultPropertyType; import org.sablo.util.ValueReference; import org.sablo.websocket.utils.DataConversion; import org.sablo.websocket.utils.JSONUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.servoy.base.persistence.constants.IValueListConstants; import com.servoy.j2db.IApplication; import com.servoy.j2db.component.ComponentFormat; import com.servoy.j2db.dataprocessing.IValueList; import com.servoy.j2db.persistence.Column; import com.servoy.j2db.persistence.IColumnTypes; import com.servoy.j2db.persistence.IDataProvider; import com.servoy.j2db.persistence.ITable; import com.servoy.j2db.persistence.Relation; import com.servoy.j2db.persistence.Table; import com.servoy.j2db.persistence.ValueList; import com.servoy.j2db.server.ngclient.DataAdapterList; import com.servoy.j2db.server.ngclient.FormElementContext; import com.servoy.j2db.server.ngclient.IContextProvider; import com.servoy.j2db.server.ngclient.INGFormElement; import com.servoy.j2db.server.ngclient.WebFormComponent; import com.servoy.j2db.server.ngclient.property.types.NGConversions.IFormElementDefaultValueToSabloComponent; 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.RoundHalfUpDecimalFormat; import com.servoy.j2db.util.UUID; import com.servoy.j2db.util.Utils; /** * TODO is format really mapped on a special object (like ParsedFormat) * maybe this should go into Servoy * @author jcompagner * */ public class FormatPropertyType extends DefaultPropertyType<Object> implements IConvertedPropertyType<Object>/* <ComponentFormat> */, ISupportTemplateValue<Object>, IFormElementDefaultValueToSabloComponent<Object, Object>, ISabloComponentToRhino<Object> /* <ComponentFormat */, IRhinoToSabloComponent<Object> /* <ComponentFormat */ { private static final Logger log = LoggerFactory.getLogger(FormatPropertyType.class.getCanonicalName()); public static final FormatPropertyType INSTANCE = new FormatPropertyType(); public static final String TYPE_NAME = "format"; private static Object DESIGN_DEFAULT = new Object(); private FormatPropertyType() { } @Override public String getName() { return TYPE_NAME; } @SuppressWarnings("nls") @Override public Object parseConfig(JSONObject json) { if (json != null && json.has("for")) { try { Object object = json.get("for"); if (object instanceof JSONArray) { JSONArray arr = (JSONArray)object; String[] retValue = new String[arr.length()]; for (int i = 0; i < arr.length(); i++) { retValue[i] = arr.getString(i); } return retValue; } else if (object instanceof String) { return new String[] { (String)object }; } return null; } catch (JSONException e) { log.error("JSONException", e); } } return ""; } @Override public Object/* ComponentFormat */ defaultValue(PropertyDescription pd) { return DESIGN_DEFAULT; } @Override public Object/* ComponentFormat */ fromJSON(Object newValue, Object/* ComponentFormat */ previousValue, PropertyDescription pd, IBrowserConverterContext dataConverterContext, ValueReference<Boolean> returnValueAdjustedIncommingValue) { // TODO remove when these types are design-aware and we know exactly how to deal with FormElement values (a refactor is to be done soon) return newValue; // ? // return null; } @Override // @formatter:off public JSONWriter toJSON(JSONWriter writer, String key, Object/* ComponentFormat */formatValue, PropertyDescription pdd/* if this arg is needed in the future please also handle it in foundset property type! that uses null */, DataConversion cc/* if this arg is needed in the future please also handle it in foundset property type! then that will need to handle client side conversions as well */, IBrowserConverterContext dataConverterContext) throws JSONException // @formatter:on { ComponentFormat format; if (formatValue == null || formatValue instanceof String) { JSONUtils.addKeyIfPresent(writer, key); return writer.value(null); } format = (ComponentFormat)formatValue; Map<String, Object> map = new HashMap<>(); String type = Column.getDisplayTypeString(format.uiType); map.put("type", type); boolean isMask = format.parsedFormat.isMask(); boolean isAllUppercase = format.parsedFormat.isAllUpperCase(); boolean isAllLowercase = format.parsedFormat.isAllLowerCase(); String placeHolder = null; if (format.parsedFormat.getPlaceHolderString() != null) placeHolder = format.parsedFormat.getPlaceHolderString(); else if (format.parsedFormat.getPlaceHolderCharacter() != 0) placeHolder = Character.toString(format.parsedFormat.getPlaceHolderCharacter()); String mask = format.parsedFormat.getEditFormat(); if (isMask && type.equals("DATETIME")) { mask = format.parsedFormat.getDateMask(); if (placeHolder == null) placeHolder = format.parsedFormat.getDisplayFormat(); } else if (format.parsedFormat.getDisplayFormat() != null && type.equals("TEXT")) { isMask = true; mask = format.parsedFormat.getDisplayFormat(); } map.put("isMask", Boolean.valueOf(isMask)); map.put("edit", mask); map.put("placeHolder", placeHolder); map.put("allowedCharacters", format.parsedFormat.getAllowedCharacters()); map.put("display", format.parsedFormat.getDisplayFormat()); map.put("isNumberValidator", Boolean.valueOf(format.parsedFormat.isNumberValidator())); if (type.equals("NUMBER") || type.equals("INTEGER") || format.parsedFormat.isNumberValidator()) { BaseWebObject webObject = dataConverterContext.getWebObject(); Locale clientLocale; if (webObject instanceof IContextProvider) { clientLocale = ((IContextProvider)webObject).getDataConverterContext().getApplication().getLocale(); } else { Debug.warn("Cannot get client locale for : " + webObject.toString() + " , using system default"); clientLocale = Locale.getDefault(); } DecimalFormatSymbols dfs = RoundHalfUpDecimalFormat.getDecimalFormatSymbols(clientLocale); // the commented out values are already available client-side in numeral.languageData(); they are taken from there now // map.put("decimalSeparator", String.valueOf(dfs.getDecimalSeparator())); // map.put("groupingSeparator", String.valueOf(dfs.getGroupingSeparator())); // map.put("currencySymbol", dfs.getCurrencySymbol()); map.put("percent", String.valueOf(dfs.getPercent())); } if (format.parsedFormat.getMaxLength() != null) { map.put("maxLength", format.parsedFormat.getMaxLength()); } if (isAllUppercase) map.put("uppercase", Boolean.valueOf(isAllUppercase)); else if (isAllLowercase) map.put("lowercase", Boolean.valueOf(isAllLowercase)); return JSONUtils.toBrowserJSONFullValue(writer, key, map, null, cc, null); } @Override public boolean valueInTemplate(Object object, PropertyDescription pd, FormElementContext formElementContext) { return false; } @Override public Object toSabloComponentValue(Object formElementValue, PropertyDescription pd, INGFormElement formElement, WebFormComponent component, DataAdapterList dataAdapterList) { return getSabloValue(formElementValue, formElement, pd, component); } @Override public Object toSabloComponentDefaultValue(PropertyDescription pd, INGFormElement formElement, WebFormComponent component, DataAdapterList dataAdapterList) { return getSabloValue(null, formElement, pd, component); } private Object getSabloValue(Object formElementValue, INGFormElement formElement, PropertyDescription pd, WebFormComponent component) { IApplication application = component.getDataConverterContext().getApplication(); if (formElementValue == NGConversions.IDesignToFormElement.TYPE_DEFAULT_VALUE_MARKER || formElementValue == DESIGN_DEFAULT) { formElementValue = null; } if (formElementValue instanceof String || formElementValue == null) { String dataproviderId = null; if (pd.getConfig() instanceof String[]) { for (String element : (String[])pd.getConfig()) { PropertyDescription forProperty = formElement.getProperty(element); if (forProperty != null) { IPropertyType< ? > type = forProperty.getType(); if (type instanceof IYieldingType< ? , ? >) { type = ((IYieldingType)type).getPossibleYieldType(); } if (type instanceof DataproviderPropertyType) { dataproviderId = (String)formElement.getPropertyValue(element); break; } else if (type instanceof ValueListPropertyType) { Object id = formElement.getPropertyValue(element); int valuelistID = Utils.getAsInteger(id); ValueList val = null; if (valuelistID > 0) { val = application.getFlattenedSolution().getValueList(valuelistID); } else { UUID uuid = Utils.getAsUUID(id, false); if (uuid != null) val = (ValueList)application.getFlattenedSolution().searchPersist(uuid); } if (val != null) { int dpType = IColumnTypes.TEXT; IDataProvider dataProvider = null; ITable table; try { if (val.getRelationName() != null) { Relation[] relations = application.getFlattenedSolution().getRelationSequence(val.getRelationName()); table = relations[relations.length - 1].getForeignTable(); } else { table = val.getTable(); } if (table != null) { String dp = null; int showDataProviders = val.getShowDataProviders(); if (showDataProviders == 1) { dp = val.getDataProviderID1(); } else if (showDataProviders == 2) { dp = val.getDataProviderID2(); } else if (showDataProviders == 4) { dp = val.getDataProviderID3(); } if (dp != null) { dataProvider = application.getFlattenedSolution().getDataProviderForTable((Table)table, dp); } if (dataProvider != null) { dpType = dataProvider.getDataProviderType(); } return ComponentFormat.getComponentFormat((String)formElementValue, dpType, application); } else if (val.getValueListType() == IValueListConstants.CUSTOM_VALUES) { IValueList realValuelist = com.servoy.j2db.component.ComponentFactory.getRealValueList(application, val, true, Types.OTHER, null, null, true); if (realValuelist.hasRealValues()) { return ComponentFormat.getComponentFormat((String)formElementValue, dpType, application); } } } catch (Exception ex) { Debug.error(ex); } } } } } } ComponentFormat format = ComponentFormat.getComponentFormat( (String)formElementValue, dataproviderId, application.getFlattenedSolution().getDataproviderLookup(application.getFoundSetManager(), component.getDataConverterContext().getForm().getForm()), application, true); return format; } return formElementValue; } @Override public boolean isValueAvailableInRhino(Object webComponentValue, PropertyDescription pd, BaseWebObject componentOrService) { return true; } @Override public Object toRhinoValue(Object webComponentValue, PropertyDescription pd, BaseWebObject componentOrService, Scriptable startScriptable) { if (webComponentValue instanceof ComponentFormat) { return ((ComponentFormat)webComponentValue).parsedFormat.getFormatString(); } return null; } @Override public Object toSabloComponentValue(Object rhinoValue, Object previousComponentValue, PropertyDescription pd, BaseWebObject componentOrService) { return getSabloValue(rhinoValue, ((WebFormComponent)componentOrService).getFormElement(), pd, (WebFormComponent)componentOrService); } }