blob: 057132761d1c4e12e1e677b652adc6a97fb7440d [file] [log] [blame]
/**
*
* 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.utils.vaadin;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
import org.eclipse.osbp.blob.component.BlobComponent;
import org.eclipse.osbp.ui.api.customfields.IBlobService;
import org.eclipse.osbp.ui.api.metadata.IDSLMetadataService;
import org.eclipse.osbp.ui.api.themes.IThemeResourceService;
import org.eclipse.osbp.ui.api.themes.IThemeResourceService.ThemeResourceType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.vaadin.data.util.converter.Converter.ConversionException;
import com.vaadin.server.Resource;
class DoubleComparator implements Comparator<Double> {
@Override
public int compare(Double arg0, Double arg1) {
return arg0.compareTo(arg1);
}
}
class IntegerComparator implements Comparator<Integer> {
@Override
public int compare(Integer arg0, Integer arg1) {
return arg0.compareTo(arg1);
}
}
class BigDecimalComparator implements Comparator<BigDecimal> {
@Override
public int compare(BigDecimal arg0, BigDecimal arg1) {
return arg0.compareTo(arg1);
}
}
class DateComparator implements Comparator<Date> {
@Override
public int compare(Date o1, Date o2) {
return o1.compareTo(o2);
}
}
public class PropertyLookup {
private final IThemeResourceService themeResourceService;
private final IDSLMetadataService dslMetadataService;
private static final Logger LOGGER = LoggerFactory.getLogger(PropertyLookup.class);
// datatypes
public static final int NOT_SET = -1;
public static final int DATE = 0;
public static final int NUMBER = 1;
public static final int BIGDECIMAL = 2;
public static final int STRING = 3;
public static final int INTEGER = 4;
public static final int LONG = 5;
// infotype
public static final int STYLE = 0;
public static final int RESOURCE = 1;
String format = null;
String defaultStyle = null;
boolean styleDiscreteValues = false;
boolean resourceDiscreteValues = false;
boolean tooltipDiscreteValues = false;
boolean hideLabel = false;
boolean collapseColumn = false;
String tooltipPattern = null;
private Map<Date, Resource> dateResourceMap = new TreeMap<>(new DateComparator());
private Map<Date, String> dateStyleMap = new TreeMap<>(new DateComparator());
private Map<Date, String> dateTooltipMap = new TreeMap<>(new DateComparator());
private Map<Double, Resource> doubleResourceMap = new TreeMap<>(new DoubleComparator());
private Map<Double, String> doubleStyleMap = new TreeMap<>(new DoubleComparator());
private Map<Double, String> doubleTooltipMap = new TreeMap<>(new DoubleComparator());
private Map<Integer, Resource> intResourceMap = new TreeMap<>(new IntegerComparator());
private Map<Integer, String> intStyleMap = new TreeMap<>(new IntegerComparator());
private Map<Integer, String> intTooltipMap = new TreeMap<>(new IntegerComparator());
private Map<String, Resource> stringResourceMap = new HashMap<>();
private Map<String, String> stringStyleMap = new HashMap<>();
private Map<String, String> stringTooltipMap = new HashMap<>();
private Locale locale;
private int defaultType = NOT_SET;
private boolean isImage = false;
private boolean isBlob = false;
private boolean isEnum = false;
private StringToFormattedDateConverter dateConv = new StringToFormattedDateConverter();
private StringToFormattedDoubleConverter numberConv = new StringToFormattedDoubleConverter();
private StringToFormattedBigDecimalConverter bigDecimalConv = new StringToFormattedBigDecimalConverter();
private StringToFormattedIntegerConverter intConv = new StringToFormattedIntegerConverter();
private StringToFormattedLongConverter longConv = new StringToFormattedLongConverter();
private String imagePath = null;
private String imageParameterPattern = null;
private String imageParameterFormat = null;
private String resizeString = null;
private Resource columnIcon = null;
private int resolutionId = 0;
private IBlobService blobService;
@SuppressWarnings("rawtypes")
private Class<?> enumClass;
public PropertyLookup(IThemeResourceService themeResourceService, IDSLMetadataService dslMetadataService,
IBlobService blobService, Locale locale) {
this.themeResourceService = themeResourceService;
this.dslMetadataService = dslMetadataService;
this.blobService = blobService;
this.locale = locale;
}
public PropertyLookup setLocale(Locale locale) {
this.locale = locale;
return this;
}
public Locale getLocale() {
return locale;
}
public String getFormat() {
return format;
}
public PropertyLookup setFormat(String format) {
this.format = format;
dateConv.setLookup(this);
numberConv.setLookup(this);
bigDecimalConv.setLookup(this);
return this;
}
public String getTooltipPattern() {
return tooltipPattern;
}
public PropertyLookup setTooltipPattern(String tooltipPattern) {
this.tooltipPattern = tooltipPattern;
return this;
}
public String getTooltip(Object value) {
String tooltip = null;
String item = "";
switch (defaultType) {
case DATE:
item = dateConv.convertToPresentation((Date) value, String.class, locale);
break;
case NUMBER:
try {
item = numberConv.convertToPresentation(getDoubleValue(value), String.class, locale);
} catch (ConversionException e) {
LOGGER.error("{}", e);
}
break;
case INTEGER:
try {
item = intConv.convertToPresentation((Integer) value, String.class, locale);
} catch (ConversionException e) {
LOGGER.error("{}", e);
}
break;
case LONG:
try {
item = longConv.convertToPresentation((Long) value, String.class, locale);
} catch (ConversionException e) {
LOGGER.error("{}", e);
}
break;
case BIGDECIMAL:
try {
item = bigDecimalConv.convertToPresentation(getBigDecimalValue(value), String.class, locale);
} catch (ConversionException e) {
LOGGER.error("{}", e);
}
break;
case STRING:
item = (String) value;
LOGGER.debug("getTooltip >" + item + "<");
break;
default:
break;
}
if (tooltipPattern != null) {
LOGGER.debug("getTooltip pattern >" + tooltipPattern + "< result:" + tooltip);
tooltip = String.format(tooltipPattern, item);
} else if (value != null) {
tooltip = item;
}
return tooltip;
}
public Resource getColumnIcon() {
return columnIcon;
}
public PropertyLookup setColumnIcon(String resourceName) {
columnIcon = themeResourceService.getThemeResource(resourceName, ThemeResourceType.ICON);
return this;
}
public boolean isBlob() {
return isBlob;
}
public PropertyLookup setBlob(boolean isBlob, int resolutionId) {
this.isBlob = isBlob;
this.resolutionId = resolutionId;
return this;
}
public boolean isEnum() {
return isEnum;
}
@SuppressWarnings("rawtypes")
public PropertyLookup setEnum(boolean isEnum, Class<?> enumClass) {
this.isEnum = isEnum;
this.enumClass = enumClass;
return this;
}
public String getResizeString() {
return resizeString;
}
public PropertyLookup setResizeString(String resizeString) {
this.resizeString = resizeString;
return this;
}
public StringToFormattedDateConverter getDateConverter() {
return dateConv;
}
public StringToFormattedDoubleConverter getNumberConverter() {
return numberConv;
}
public StringToFormattedIntegerConverter getIntegerConverter() {
return intConv;
}
public StringToFormattedBigDecimalConverter getBigDecimalConverter() {
return bigDecimalConv;
}
/**
* @param until
* @param resourceName
*/
public PropertyLookup addResourceInterval(Date until, String resourceName) {
Resource resource = themeResourceService.getThemeResource(resourceName, ThemeResourceType.ICON);
dateResourceMap.put(until, resource);
isImage = true;
defaultType = DATE;
return this;
}
public PropertyLookup addStyleInterval(Date until, String styleName) {
dateStyleMap.put(until, styleName);
defaultType = DATE;
return this;
}
public PropertyLookup addTooltipInterval(Date until, String tooltip) {
dateTooltipMap.put(until, tooltip);
defaultType = DATE;
return this;
}
public PropertyLookup addResourceLookup(Date value, String resourceName) {
resourceDiscreteValues = true;
addResourceInterval(value, resourceName);
return this;
}
public PropertyLookup addStyleLookup(Date value, String styleName) {
styleDiscreteValues = true;
addStyleInterval(value, styleName);
return this;
}
public PropertyLookup addTooltipLookup(Date value, String tooltip) {
tooltipDiscreteValues = true;
addResourceInterval(value, tooltip);
return this;
}
public PropertyLookup addResourceLookup(String value, String resourceName) {
Resource resource = themeResourceService.getThemeResource(resourceName, ThemeResourceType.ICON);
stringResourceMap.put(value, resource);
isImage = true;
defaultType = STRING;
return this;
}
public PropertyLookup addStyleLookup(String value, String styleName) {
stringStyleMap.put(value, styleName);
defaultType = STRING;
return this;
}
public PropertyLookup addTooltipLookup(String value, String tooltip) {
stringTooltipMap.put(value, tooltip);
defaultType = STRING;
return this;
}
public PropertyLookup addResourceLookup(int value, String resourceName) {
resourceDiscreteValues = true;
addResourceInterval(value, resourceName);
return this;
}
public PropertyLookup addStyleLookup(int value, String styleName) {
styleDiscreteValues = true;
addStyleInterval(value, styleName);
return this;
}
public PropertyLookup addTooltipLookup(int value, String tooltip) {
tooltipDiscreteValues = true;
addTooltipInterval(value, tooltip);
return this;
}
public PropertyLookup addResourceInterval(int until, String resourceName) {
Resource resource = themeResourceService.getThemeResource(resourceName, ThemeResourceType.ICON);
intResourceMap.put(until, resource);
isImage = true;
defaultType = INTEGER;
return this;
}
public PropertyLookup addStyleInterval(int until, String styleName) {
intStyleMap.put(until, styleName);
defaultType = INTEGER;
return this;
}
public PropertyLookup addTooltipInterval(int until, String tooltip) {
intTooltipMap.put(until, tooltip);
defaultType = INTEGER;
return this;
}
public PropertyLookup addResourceLookup(double value, String resourceName) {
resourceDiscreteValues = true;
addResourceInterval(value, resourceName);
return this;
}
public PropertyLookup addStyleLookup(double value, String styleName) {
styleDiscreteValues = true;
addStyleInterval(value, styleName);
return this;
}
public PropertyLookup addTooltipLookup(double value, String tooltip) {
tooltipDiscreteValues = true;
addTooltipInterval(value, tooltip);
return this;
}
public PropertyLookup addResourceInterval(double until, String resourceName) {
Resource resource = themeResourceService.getThemeResource(resourceName, ThemeResourceType.ICON);
doubleResourceMap.put(until, resource);
isImage = true;
defaultType = NUMBER;
return this;
}
public PropertyLookup addStyleInterval(double until, String styleName) {
doubleStyleMap.put(until, styleName);
defaultType = NUMBER;
return this;
}
public PropertyLookup addTooltipInterval(double until, String tooltip) {
doubleTooltipMap.put(until, tooltip);
defaultType = NUMBER;
return this;
}
public boolean isHideLabel() {
return hideLabel;
}
public PropertyLookup setHideLabel(boolean hideLabel) {
this.hideLabel = hideLabel;
return this;
}
public boolean isCollapseColumn() {
return collapseColumn;
}
public PropertyLookup setCollapseColumn(boolean collapseColumn) {
this.collapseColumn = collapseColumn;
return this;
}
protected double getDoubleValue(Object value) {
if (value instanceof Double) {
return (Double) value;
} else if (value instanceof Float) {
return (Float) value;
} else if (value instanceof Long) {
return (Long) value;
}
return 0;
}
protected BigDecimal getBigDecimalValue(Object value) {
if (value instanceof BigDecimal) {
return (BigDecimal) value;
}
return BigDecimal.ZERO;
}
public int getType() {
return defaultType;
}
public PropertyLookup setType(int type) {
this.defaultType = type;
return this;
}
@SuppressWarnings("rawtypes")
public Object getValueObject(Object value) {
if (value == null) {
return null;
}
switch (defaultType) {
case DATE:
for (Date key : dateResourceMap.keySet()) {
if ((resourceDiscreteValues && ((Date) value).equals(key))
|| (!resourceDiscreteValues && ((Date) value).before(key))) {
return new CellSetImage(value, dateConv.convertToPresentation((Date) value, String.class, locale),
dateResourceMap.get(key), hideLabel, resizeString);
}
}
// if it should be an image and no match was found, return an empty
// image to avoid complaints
if (isImage) {
return new CellSetImage(value, dateConv.convertToPresentation((Date) value, String.class, locale), null,
hideLabel, resizeString);
}
break;
case NUMBER:
double doubleValue = getDoubleValue(value);
for (Double key : doubleResourceMap.keySet()) {
if ((resourceDiscreteValues && doubleValue == key) || (!resourceDiscreteValues && doubleValue <= key)) {
return new CellSetImage(doubleValue,
numberConv.convertToPresentation(doubleValue, String.class, locale),
doubleResourceMap.get(key), hideLabel, resizeString);
}
}
if (imagePath != null) {
String path = imagePath;
if (imageParameterPattern != null) {
path = path.replace(imageParameterPattern, String.format(imageParameterFormat, doubleValue));
}
Resource res = themeResourceService.getThemeResource(path, ThemeResourceType.ICON);
return new CellSetImage(doubleValue,
numberConv.convertToPresentation(doubleValue, String.class, locale), res, hideLabel,
resizeString);
}
// if it should be an image and no match was found, return an empty
// image to avoid complaints
if (isImage) {
return new CellSetImage(doubleValue,
numberConv.convertToPresentation(doubleValue, String.class, locale), null, hideLabel,
resizeString);
}
break;
case INTEGER:
Integer intValue = (Integer) value;
for (Integer key : intResourceMap.keySet()) {
if ((resourceDiscreteValues && intValue == key) || (!resourceDiscreteValues && intValue <= key)) {
return new CellSetImage(intValue, intConv.convertToPresentation(intValue, String.class, locale),
intResourceMap.get(key), hideLabel, resizeString);
}
}
if (imagePath != null) {
String path = imagePath;
if (imageParameterPattern != null) {
path = path.replace(imageParameterPattern, String.format(imageParameterFormat, intValue));
}
Resource res = themeResourceService.getThemeResource(path, ThemeResourceType.ICON);
return new CellSetImage(intValue, intConv.convertToPresentation(intValue, String.class, locale), res,
hideLabel, resizeString);
}
// if it should be an image and no match was found, return an empty
// image to avoid complaints
if (isImage) {
return new CellSetImage(intValue, intConv.convertToPresentation(intValue, String.class, locale), null,
hideLabel, resizeString);
}
if (isEnum) {
return dslMetadataService.translate(locale.toLanguageTag(),
((Enum) enumClass.getEnumConstants()[intValue]).name().toLowerCase(locale));
}
break;
case LONG:
Long longValue = (Long) value;
for (Integer key : intResourceMap.keySet()) {
if ((resourceDiscreteValues && longValue == (long) key)
|| (!resourceDiscreteValues && longValue <= (long) key)) {
return new CellSetImage(longValue, longConv.convertToPresentation(longValue, String.class, locale),
intResourceMap.get(key), hideLabel, resizeString);
}
}
if (imagePath != null) {
String path = imagePath;
if (imageParameterPattern != null) {
path = path.replace(imageParameterPattern,
String.format(imageParameterFormat, longValue.intValue()));
}
Resource res = themeResourceService.getThemeResource(path, ThemeResourceType.ICON);
return new CellSetImage(longValue, longConv.convertToPresentation(longValue, String.class, locale), res,
hideLabel, resizeString);
}
// if it should be an image and no match was found, return an empty
// image to avoid complaints
if (isImage) {
return new CellSetImage(longValue, longConv.convertToPresentation(longValue, String.class, locale),
null, hideLabel, resizeString);
}
break;
case BIGDECIMAL:
BigDecimal bigValue = getBigDecimalValue(value);
for (Double key : doubleResourceMap.keySet()) {
BigDecimal bdKey = new BigDecimal(key);
if ((resourceDiscreteValues && bigValue.equals(bdKey))
|| (!resourceDiscreteValues && bigValue.compareTo(bdKey) <= 0)) {
return new CellSetImage(bigValue,
bigDecimalConv.convertToPresentation(bigValue, String.class, locale),
doubleResourceMap.get(key), hideLabel, resizeString);
}
}
if (imagePath != null) {
String path = imagePath;
if (imageParameterPattern != null) {
path = path.replace(imageParameterPattern, String.format(imageParameterFormat, bigValue));
}
Resource res = themeResourceService.getThemeResource(path, ThemeResourceType.ICON);
return new CellSetImage(bigValue, bigDecimalConv.convertToPresentation(bigValue, String.class, locale),
res, hideLabel, resizeString);
}
// if it should be an image and no match was found, return an empty
// image to avoid complaints
if (isImage) {
return new CellSetImage(bigValue, bigDecimalConv.convertToPresentation(bigValue, String.class, locale),
null, hideLabel, resizeString);
}
break;
case STRING:
for (String key : stringResourceMap.keySet()) {
if (key.equals(value)) {
return new CellSetImage(value, (String) value, stringResourceMap.get(key), hideLabel, resizeString);
}
}
if (imagePath != null) {
String path = imagePath;
if (imageParameterPattern != null) {
path = path.replace(imageParameterPattern, String.format(imageParameterFormat, (String) value));
}
Resource res = themeResourceService.getThemeResource(path, ThemeResourceType.ICON);
return new CellSetImage(value, (String) value, res, hideLabel, resizeString);
}
if (isBlob) {
return new BlobComponent(blobService, (String) value, resolutionId);
}
// if it should be an image and no match was found, return an empty
// image to avoid complaints
if (isImage) {
return new CellSetImage(value, (String) value, null, hideLabel, resizeString);
}
break;
default:
break;
}
return value;
}
public String getValueTooltip(Object value) {
switch (defaultType) {
case DATE:
for (Date key : dateTooltipMap.keySet()) {
if ((tooltipDiscreteValues && ((Date) value).equals(key))
|| (!tooltipDiscreteValues && ((Date) value).before(key))) {
return dslMetadataService.translate(locale.toLanguageTag(), dateTooltipMap.get(key));
}
}
break;
case NUMBER:
double doubleValue = getDoubleValue(value);
for (Double key : doubleTooltipMap.keySet()) {
if ((tooltipDiscreteValues && doubleValue == key) || (!tooltipDiscreteValues && doubleValue <= key)) {
return dslMetadataService.translate(locale.toLanguageTag(), doubleTooltipMap.get(key));
}
}
break;
case INTEGER:
Integer intValue = (Integer) value;
for (Integer key : intTooltipMap.keySet()) {
if ((tooltipDiscreteValues && intValue == key) || (!tooltipDiscreteValues && intValue <= key)) {
return dslMetadataService.translate(locale.toLanguageTag(), intTooltipMap.get(key));
}
}
break;
case LONG:
Long longValue = (Long) value;
for (Integer key : intTooltipMap.keySet()) {
if ((tooltipDiscreteValues && longValue == (long) key)
|| (!tooltipDiscreteValues && longValue <= (long) key)) {
return dslMetadataService.translate(locale.toLanguageTag(), intTooltipMap.get(key));
}
}
break;
case BIGDECIMAL:
BigDecimal bigValue = BigDecimal.ZERO;
bigValue = getBigDecimalValue(value);
for (Double key : doubleTooltipMap.keySet()) {
BigDecimal bdKey = new BigDecimal(key);
if ((tooltipDiscreteValues && bigValue.equals(bdKey))
|| (!tooltipDiscreteValues && bigValue.compareTo(bdKey) <= 0)) {
return dslMetadataService.translate(locale.toLanguageTag(), doubleTooltipMap.get(key));
}
}
break;
case STRING:
for (String key : stringTooltipMap.keySet()) {
if (key.equals(value)) {
return dslMetadataService.translate(locale.toLanguageTag(), stringTooltipMap.get(key));
}
}
break;
default:
break;
}
return getTooltip(value);
}
public String getDefaultStyle() {
return defaultStyle;
}
public PropertyLookup setDefaultStyle(Class<?> type, boolean isHeader) {
if (isHeader) {
defaultStyle = "rowheader";
} else if (type.equals(double.class) || type.equals(Double.class) || type.equals(float.class)
|| type.equals(Float.class) || type.equals(int.class) || type.equals(Integer.class)
|| type.equals(long.class) || type.equals(Long.class) || type.equals(BigDecimal.class)) {
defaultStyle = "v-align-right";
} else if (type.equals(String.class)) {
defaultStyle = "v-align-left";
} else if (type.equals(Date.class) || type.equals(Timestamp.class)) {
defaultStyle = "v-align-left";
} else if (type.equals(CellSetImage.class)) {
defaultStyle = "v-align-center";
}
return this;
}
public String getValueStyle(Object value, int rowNumber) {
switch (defaultType) {
case DATE:
if (dateStyleMap.size() == 0) {
return defaultStyle;
}
for (Date key : dateStyleMap.keySet()) {
if ((styleDiscreteValues && ((Date) value).equals(key))
|| (!styleDiscreteValues && ((Date) value).before(key))) {
return dateStyleMap.get(key);
}
}
break;
case NUMBER:
if (doubleStyleMap.size() == 0) {
return defaultStyle;
}
double doubleValue = getDoubleValue(value);
for (Double key : doubleStyleMap.keySet()) {
if ((styleDiscreteValues && doubleValue == key) || (!styleDiscreteValues && doubleValue <= key)) {
return doubleStyleMap.get(key);
}
}
break;
case INTEGER:
if (intStyleMap.size() == 0) {
return defaultStyle;
}
Integer intValue = (Integer) value;
for (Integer key : intStyleMap.keySet()) {
if ((styleDiscreteValues && intValue == key) || (!styleDiscreteValues && intValue <= key)) {
return intStyleMap.get(key);
}
}
break;
case LONG:
if (intStyleMap.size() == 0) {
return defaultStyle;
}
Long longValue = (Long) value;
for (Integer key : intStyleMap.keySet()) {
if ((styleDiscreteValues && longValue == (long) key)
|| (!styleDiscreteValues && longValue <= (long) key)) {
return intStyleMap.get(key);
}
}
break;
case BIGDECIMAL:
if (doubleStyleMap.size() == 0) {
return defaultStyle;
}
BigDecimal bigDecimalValue = BigDecimal.ZERO;
bigDecimalValue = getBigDecimalValue(value);
for (Double key : doubleStyleMap.keySet()) {
BigDecimal bdKey = new BigDecimal(key);
if ((styleDiscreteValues && bigDecimalValue.equals(bdKey))
|| (!styleDiscreteValues && bigDecimalValue.compareTo(bdKey) <= 0)) {
return doubleStyleMap.get(key);
}
}
break;
case STRING:
if (stringStyleMap.size() == 0) {
return defaultStyle;
}
for (String key : stringStyleMap.keySet()) {
if (key.equals(value)) {
return stringStyleMap.get(key);
}
}
break;
default:
break;
}
return defaultStyle;
}
public Class<?> getPropertyType(Class<?> defaultType) {
if (defaultType.equals(double.class) || defaultType.equals(Double.class) || defaultType.equals(float.class)
|| defaultType.equals(Float.class)) {
this.defaultType = NUMBER;
} else if (defaultType.equals(Integer.class) || defaultType.equals(int.class)) {
this.defaultType = INTEGER;
} else if (defaultType.equals(long.class) || defaultType.equals(Long.class)) {
this.defaultType = LONG;
} else if (defaultType.equals(BigDecimal.class)) {
this.defaultType = BIGDECIMAL;
} else if (defaultType.equals(String.class)) {
this.defaultType = STRING;
} else if (defaultType.equals(Date.class) || defaultType.equals(Timestamp.class)) {
this.defaultType = DATE;
}
if (isImage) {
return CellSetImage.class;
}
if (isBlob) {
return BlobComponent.class;
}
if (isEnum) {
return String.class;
}
return defaultType;
}
public PropertyLookup setImageService(String path, boolean hasParameter) {
isImage = true;
imagePath = path;
if (hasParameter) {
String[] token = path.split("%");
// there must only be one parameter for the moment
if (token.length == 3) {
imageParameterPattern = "%" + token[1] + "%";
imageParameterFormat = "%" + token[1];
}
}
return this;
}
}