/*
* Copyright (c) 2005-2016 Vincent Vandenschrick. All rights reserved.
*
* This file is part of the Jspresso framework.
*
* Jspresso is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Jspresso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Jspresso. If not, see <http://www.gnu.org/licenses/>.
*/
package org.jspresso.framework.model.descriptor.basic;
import java.util.Locale;
import java.util.regex.Pattern;
import org.jspresso.framework.model.descriptor.IStringPropertyDescriptor;
import org.jspresso.framework.util.bean.integrity.IntegrityException;
import org.jspresso.framework.util.i18n.ITranslationProvider;
/**
* Describes a string based property.
*
* @author Vincent Vandenschrick
*/
public class BasicStringPropertyDescriptor extends BasicScalarPropertyDescriptor implements IStringPropertyDescriptor {
private Integer maxLength;
private String regexpPattern;
private String regexpPatternSample;
private Boolean upperCase;
private Boolean translatable;
private Boolean truncate;
/**
* {@inheritDoc}
*/
@Override
public BasicStringPropertyDescriptor clone() {
BasicStringPropertyDescriptor clonedDescriptor = (BasicStringPropertyDescriptor) super.clone();
return clonedDescriptor;
}
/**
* {@inheritDoc}
*/
@Override
public BasicStringPropertyDescriptor createQueryDescriptor() {
BasicStringPropertyDescriptor queryDescriptor = (BasicStringPropertyDescriptor) super.createQueryDescriptor();
// Don't set it to null because in that case, the default max length will apply.
queryDescriptor.setMaxLength(-1);
queryDescriptor.setRegexpPattern(null);
queryDescriptor.setTranslatable(false);
return queryDescriptor;
}
/**
* {@inheritDoc}
*/
@Override
public Integer getMaxLength() {
if (maxLength != null) {
return maxLength;
}
if (isComputed()) {
return null;
}
return getDefaultMaxLength();
}
/**
* {@inheritDoc}
*/
@Override
public Class<?> getModelType() {
return String.class;
}
/**
* Gets the regexpPattern.
*
* @return the regexpPattern.
*/
@Override
public String getRegexpPattern() {
return regexpPattern;
}
/**
* Gets the regexpPatternSample.
*
* @return the regexpPatternSample.
*/
@Override
public String getRegexpPatternSample() {
return regexpPatternSample;
}
/**
* {@inheritDoc}
*/
@Override
public Object interceptSetter(Object component, Object newValue) {
String actualNewValue = getValueAsString(newValue);
if (actualNewValue != null) {
if (isUpperCase()) {
actualNewValue = actualNewValue.toUpperCase();
}
if (isTruncate() && !isLengthValid(component, newValue)) {
actualNewValue = actualNewValue.substring(0, getMaxLength());
}
}
return super.interceptSetter(component, actualNewValue);
}
/**
* {@inheritDoc}
*/
@Override
public boolean isUpperCase() {
if (upperCase != null) {
return upperCase;
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isTranslatable() {
if (translatable != null) {
return translatable;
}
return false;
}
@Override
public void preprocessSetter(final Object component, final Object newValue) {
super.preprocessSetter(component, newValue);
final String propertyValueAsString = getValueAsString(newValue);
final Integer maxL = getMaxLength();
if (!isLengthValid(component, newValue)) {
IntegrityException ie = new IntegrityException(
"[" + getName() + "] value (" + propertyValueAsString + ") is too long on [" + component + "].") {
private static final long serialVersionUID = 7459823123892198831L;
@Override
public String getI18nMessage(ITranslationProvider translationProvider, Locale locale) {
StringBuilder boundsSpec = new StringBuilder("l");
boundsSpec.append(" <= ").append(maxL);
return translationProvider.getTranslation("integrity.property.toolong",
new Object[]{getI18nName(translationProvider, locale), boundsSpec, component}, locale);
}
};
}
if (propertyValueAsString != null && getRegexpPattern() != null && !Pattern.matches(getRegexpPattern(),
propertyValueAsString)) {
IntegrityException ie = new IntegrityException(
"[" + getName() + "] value (" + propertyValueAsString + ") does not match pattern ["
+ getRegexpPatternSample() + "] on [" + component + "].") {
private static final long serialVersionUID = 7459823123892198831L;
@Override
public String getI18nMessage(ITranslationProvider translationProvider, Locale locale) {
return translationProvider.getTranslation("integrity.property.pattern",
new Object[]{getI18nName(translationProvider, locale), getRegexpPatternSample(), component}, locale);
}
};
throw ie;
}
}
private boolean isLengthValid(final Object component, Object newValue) {
String propertyValueAsString = getValueAsString(newValue);
Integer maxL = getMaxLength();
if (propertyValueAsString != null && maxL != null && maxL > 0 && propertyValueAsString.length() > maxL) {
return false;
}
return true;
}
/**
* Configures the maximum string length this property allows. Default value is
* {@code null} which means unlimited.
*
* @param maxLength
* the maxLength to set.
*/
public void setMaxLength(Integer maxLength) {
this.maxLength = maxLength;
}
/**
* Configures the regular expression pattern this string property allows.
* Default is {@code null} which means constraint free.
*
* @param regexpPattern
* the regexpPattern to set.
*/
public void setRegexpPattern(String regexpPattern) {
this.regexpPattern = regexpPattern;
}
/**
* Allows for providing a conforming sample for the regular expression
* pattern. This human-readable example is used when the end-user has to be
* notified that the incoming property value does not match the pattern
* constraint.
*
* @param regexpPatternSample
* the regexpPatternSample to set.
*/
public void setRegexpPatternSample(String regexpPatternSample) {
this.regexpPatternSample = regexpPatternSample;
}
/**
* This is a shortcut to implement the common use-case of handling upper-case
* only properties. all incoming values will be transformed to uppercase as if
* a property processor was registered to perform the transformation.
*
* @param upperCase
* the upperCase to set.
*/
public void setUpperCase(boolean upperCase) {
this.upperCase = upperCase;
}
/**
* Configures the string property to be translatable. Jspresso will then implement a translation store for the
* property so that it can be displayed in the connected user language. This is particularly useful for
* non-european character sets like chinese, indian, and so on. In that case,
* only the "raw" untranslated property value is stored in the object itself. the various translations
* are stored in an extra store. translated properties support searching, ordering,
* ... exactly like non-translatable properties.
*
* @param translatable
* the translatable
*/
public void setTranslatable(boolean translatable) {
this.translatable = translatable;
}
/**
* Performs the necessary transformations to build a string out of a property
* value.
*
* @param value
* the raw property value.
* @return the resulting string.
*/
protected String getValueAsString(Object value) {
return (String) value;
}
/**
* Gets default max length.
*
* @return the default max length
*/
protected Integer getDefaultMaxLength() {
return 255;
}
/**
* {@inheritDoc}
*/
public boolean isTruncate() {
if (truncate != null) {
return truncate;
}
return false;
}
/**
* Configures whether the underlying string property should be truncated
* automatically if too long. Default value is {@code false}.
*
* @param truncate
* set to true if the underlying string property should be truncated
* automatically if too long.
*/
public void setTruncate(Boolean truncate) {
this.truncate = truncate;
}
}