| /** |
| * |
| * Copyright (c) 2011, 2016 - Loetz GmbH&Co.KG (69115 Heidelberg, Germany) |
| * |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Christophe Loetz (Loetz GmbH&Co.KG) - initial implementation |
| * |
| */ |
| package org.eclipse.osbp.ecview.extension.presentation.converter; |
| |
| import java.lang.reflect.InvocationTargetException; |
| import java.text.DecimalFormat; |
| import java.text.ParseException; |
| import java.util.Locale; |
| |
| import org.apache.commons.beanutils.PropertyUtils; |
| import org.eclipse.osbp.ecview.core.common.context.IViewContext; |
| import org.eclipse.osbp.runtime.common.types.ITypeProviderService; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import com.vaadin.data.util.converter.Converter; |
| |
| import org.eclipse.osbp.ecview.extension.api.IUomService; |
| import org.eclipse.osbp.ecview.extension.model.converter.CxQuantityToStringConverter; |
| |
| /** |
| * This converter converters values from a "quantity object" (model) to a String |
| * (presentation) and back to a quantity object. |
| * <p> |
| * Therefore it uses following logic:<br> |
| * The <code>model value</code> is an object containing the value as a numeric |
| * value and the uom (UnitOfMeasure). Value and uom may be nested properties. |
| * <h3>Access path</h3> |
| * The CxQuantityToStringConverter contains two "access path". An "access path" |
| * is a property path with dot-notation like "uom.isoCode". To access the values |
| * inside the given object, reflection needs to be used. |
| * <p> |
| * Example: |
| * |
| * <pre> |
| * class Quantity { |
| * double value; |
| * Uom uom; |
| * } |
| * |
| * class Uom { |
| * String iso3Code; |
| * String name; |
| * ... |
| * } |
| * </pre> |
| * |
| * The Quantity bean is the model value. To access the values following |
| * accessPath in the CxQuantityToStringConverter needs to be used:<br> |
| * -> for value: <code>value</code><br> |
| * -> for currency: <code>uom.iso3Code</code><br> |
| * By reflection the values can become accessed. |
| * |
| * <h3>Convert To Presentation</h3> For the convert to presentation |
| * implementation, a number format pattern is used. The uom-String-value is |
| * added to the number format pattern as a constant. <br> |
| * <br> |
| * The result may look like: <code>1.200,23 kg</code> |
| * |
| * <h3>Convert To Model</h3> For the convert to model implementation, the given |
| * presentation-String needs to become parsed. And a new Object of type |
| * "model value" needs to be created. The type of the instance, is also |
| * contained in the CxQuantityToStringConverter object. See |
| * {@link CxQuantityToStringConverter#getTypeQualifiedName()}. To get a class |
| * based on the FQN use the {@link ITypeProviderService}.<br> |
| * The UOM needs to be converted to a valid UOM-object. Therefore use the |
| * {@link IUomService}. |
| * |
| * <h3>Unsupported UOMs</h3> To ensure, that only valid UOMs are being entered |
| * in the UI, an additional Validator needs to be implemented. It validates, |
| * that the entered UOM is valid. To query about valid UOMs, the UOM-Service is |
| * available. See {@link IUomService}. |
| */ |
| @SuppressWarnings("serial") |
| public class QuantityToStringConverter implements Converter<String, Object> { |
| |
| private static final Logger LOGGER = LoggerFactory |
| .getLogger(QuantityToStringConverter.class); |
| |
| private static final String NUMBER_FORMAT_PATTERN = "#,###,###,##0.00 {$uom}"; |
| |
| private final CxQuantityToStringConverter cxConverter; |
| private final IUomService uomService; |
| private final Class<?> quantityType; |
| |
| public QuantityToStringConverter(IViewContext context, |
| CxQuantityToStringConverter cxConverter) { |
| super(); |
| this.cxConverter = cxConverter; |
| |
| ITypeProviderService typeService = context |
| .getService(ITypeProviderService.class.getName()); |
| quantityType = typeService.forName(null, |
| cxConverter.getTypeQualifiedName()); |
| uomService = context.getService(IUomService.class.getName()); |
| } |
| |
| @Override |
| public Object convertToModel(String valueString, |
| Class<? extends Object> targetType, Locale locale) |
| throws com.vaadin.data.util.converter.Converter.ConversionException { |
| |
| try { |
| Object quantity = quantityType.newInstance(); |
| Number amount = parseAmount(valueString); |
| Object uom = parseUom(valueString); |
| |
| applyAmount(quantity, amount); |
| applyUom(quantity, uom); |
| |
| return quantity; |
| } catch (InstantiationException | IllegalAccessException |
| | InvocationTargetException | ParseException |
| | NoSuchMethodException e) { |
| LOGGER.error("{}", e); |
| return null; |
| } |
| } |
| |
| private void applyUom(Object quantity, Object uom) |
| throws IllegalAccessException, InvocationTargetException, |
| NoSuchMethodException { |
| PropertyUtils.setNestedProperty(quantity, |
| cxConverter.getUomPropertyPath(), uom); |
| } |
| |
| private void applyAmount(Object quantity, Number amount) |
| throws IllegalAccessException, InvocationTargetException, |
| NoSuchMethodException { |
| PropertyUtils.setNestedProperty(quantity, |
| cxConverter.getAmountPropertyPath(), amount); |
| } |
| |
| /** |
| * Parses the amount part from the given value String.<br> |
| * "1200.98 kg" -> "1200.98" |
| * |
| * @param valueString |
| * @return |
| * @throws ParseException |
| */ |
| private Number parseAmount(String valueString) throws ParseException { |
| String[] tokens = valueString.trim().split(" "); |
| String amountString = tokens.length == 2 ? tokens[0] : ""; |
| return new DecimalFormat().parse(amountString); |
| } |
| |
| /** |
| * Parses the uom part from the given value String and creates a UOM Object.<br> |
| * "1200.98 kg" -> "kg" -> Object{kg} |
| * |
| * @param valueString |
| * @return |
| */ |
| private Object parseUom(String valueString) { |
| String[] tokens = valueString.trim().split(" "); |
| String uomCode = tokens.length == 2 ? tokens[1] : ""; |
| return uomService.getUOMForCode(uomCode); |
| } |
| |
| @Override |
| public String convertToPresentation(Object value, |
| Class<? extends String> targetType, Locale locale) |
| throws com.vaadin.data.util.converter.Converter.ConversionException { |
| |
| try { |
| String pattern = NUMBER_FORMAT_PATTERN; |
| pattern = pattern.replace("{$uom}", getUomCode(value)); |
| DecimalFormat format = new DecimalFormat(pattern); |
| return format.format(getAmount(value)); |
| } catch (IllegalAccessException | InvocationTargetException |
| | NoSuchMethodException e) { |
| LOGGER.error("{}", e); |
| return "Error"; |
| } |
| } |
| |
| /** |
| * Returns the amount value based on the AmountPropertyPath. |
| * |
| * @param value |
| * @return |
| * @throws IllegalAccessException |
| * @throws InvocationTargetException |
| * @throws NoSuchMethodException |
| */ |
| private Object getAmount(Object value) throws IllegalAccessException, |
| InvocationTargetException, NoSuchMethodException { |
| return PropertyUtils.getNestedProperty(value, |
| cxConverter.getAmountPropertyPath()); |
| } |
| |
| /** |
| * Returns the amount value based on the UomPropertyPath. |
| * |
| * @param value |
| * @return |
| * @throws IllegalAccessException |
| * @throws InvocationTargetException |
| * @throws NoSuchMethodException |
| */ |
| private String getUomCode(Object value) throws IllegalAccessException, |
| InvocationTargetException, NoSuchMethodException { |
| return (String) PropertyUtils.getNestedProperty(value, |
| cxConverter.getUomPropertyPath()); |
| } |
| |
| @Override |
| public Class<Object> getModelType() { |
| return Object.class; |
| } |
| |
| @Override |
| public Class<String> getPresentationType() { |
| return String.class; |
| } |
| |
| } |