com.jaxio.celerio.model.Attribute Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of celerio-engine Show documentation
Show all versions of celerio-engine Show documentation
Celerio Core Generation Engine
/*
* Copyright 2015 JAXIO http://www.jaxio.com
*
* 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.jaxio.celerio.model;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.jaxio.celerio.Config;
import com.jaxio.celerio.aspects.ForbiddenWhenBuilding;
import com.jaxio.celerio.aspects.ForbiddenWhenBuildingAspect;
import com.jaxio.celerio.configuration.database.JdbcType;
import com.jaxio.celerio.configuration.entity.ColumnConfig;
import com.jaxio.celerio.configuration.entity.EnumConfig;
import com.jaxio.celerio.configuration.entity.IndexedField;
import com.jaxio.celerio.convention.CommentStyle;
import com.jaxio.celerio.factory.RelationCollisionUtil;
import com.jaxio.celerio.model.support.AttributeSetup;
import com.jaxio.celerio.model.support.EnumNamer;
import com.jaxio.celerio.model.support.SuffixPrefixPredicates.*;
import com.jaxio.celerio.model.support.jpa.JpaAttribute;
import com.jaxio.celerio.support.AbstractNamer;
import com.jaxio.celerio.util.Labels;
import com.jaxio.celerio.util.MappedType;
import com.jaxio.celerio.util.Named;
import com.jaxio.celerio.util.StringUtil;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.apache.commons.lang.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import java.util.*;
import static com.google.common.base.Predicates.and;
import static com.google.common.collect.Iterables.find;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Maps.newHashMap;
import static com.jaxio.celerio.configuration.Module.CHAR_PADDING;
import static com.jaxio.celerio.configuration.database.JdbcType.CHAR;
import static com.jaxio.celerio.configuration.database.support.SqlUtil.escapeSql;
import static com.jaxio.celerio.model.support.AttributePredicates.*;
import static com.jaxio.celerio.model.support.SuffixPrefixPredicates.*;
import static com.jaxio.celerio.model.support.SuffixPrefixPredicates.IS_LABEL;
import static com.jaxio.celerio.util.FallBackUtil.fallBack;
import static com.jaxio.celerio.util.MiscUtil.toReadableLabel;
import static com.jaxio.celerio.util.StringUtil.orderToString;
import static org.apache.commons.lang.BooleanUtils.toBoolean;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
* JPA Attribute meta information.
*/
@Component
@Scope(SCOPE_PROTOTYPE)
@Getter
public class Attribute extends AbstractNamer implements Named, Map {
@Autowired
private Config config;
@Autowired
private RelationCollisionUtil collisionUtil;
private Entity entity;
private ColumnConfig columnConfig;
private MappedType mappedType;
private JpaAttribute jpa = new JpaAttribute(this); // TODO: make it an SPI
public void setEntity(Entity entity) {
this.entity = entity;
}
public void setColumnConfig(ColumnConfig columnConfig) {
Assert.isNull(this.columnConfig, "you can set the columnConfig only once");
this.columnConfig = columnConfig;
}
// -----------------------------------------------------
// Namer override
// -----------------------------------------------------
@Autowired
ForbiddenWhenBuildingAspect fwba;
private String cachedVar;
/**
* The variable/property name.
* @return the variable (aka property) name.
*/
@ForbiddenWhenBuilding
@Override
public String getVar() {
fwba.checkNotForbidden();
if (cachedVar != null) {
return cachedVar;
} else {
cachedVar = StringUtil.escape(columnConfig.getFieldName());
return cachedVar;
}
}
// -----------------------------------------------------
// Enum Namers (lazy as attribute is not always enum)
// -----------------------------------------------------
private EnumType enumType;
public EnumType getEnumType() {
if (enumType == null) {
enumType = new EnumType(this.getEnumConfig());
}
return enumType;
}
// -----------------------------------------------------
// Entity shortcuts
// -----------------------------------------------------
/**
* @return the entity name + "."+ the variable name.
*/
public String getFullName() {
return getEntity().getName() + "." + getVar();
}
public String getFullVar() {
return getEntity().getModel().getVar() + "." + getVar();
}
public String getFullModelVar() {
return getEntity().getModel().getVar() + "." + getVar();
}
public String getFullColumnName() {
return getEntity().getTableName() + "." + columnConfig.getColumnName();
}
// -----------------------------------------------------
// Validation
// -----------------------------------------------------
public String getValidate() {
// since we generate inside a validator class,
// no need to prefix it with 'validate'... it would
// be to verbose in xhtml/jsf pages
return getVar();
}
// -----------------------------------------------------
// Internationalization
// -----------------------------------------------------
Labels labels;
public String getLabelName() {
return getEntity().getModel().getVar() + "_" + getVar();
}
public Labels getLabels() {
if (labels == null) {
labels = new Labels(getColumnConfig().getLabels());
labels.setFallBack(fallBack(getColumnConfig().getLabel(), toReadableLabel(getColumnConfig().getFieldName())));
}
return labels;
}
// -----------------------------------------------------
// PK
// -----------------------------------------------------
@Setter
private boolean simplePk;
private boolean inCpk;
/**
* Null means, we do not know... happens for example when the driver does not support the IS_AUTOINCREMENT feature.
*/
@ForbiddenWhenBuilding
public Boolean getAutoIncrement() {
return getColumnConfig().getAutoIncrement();
}
@ForbiddenWhenBuilding
public boolean isSimplePk() {
return simplePk;
}
public void setInCpk(boolean inCpk) {
this.inCpk = inCpk;
// since this attribute is not going to be mapped, we can remove it from var clash
collisionUtil.removeVar(getEntity().getName(), getColumnConfig().getFieldName());
}
@ForbiddenWhenBuilding
public boolean isInCpk() {
return inCpk;
}
// -----------------------------------------------------
// FK
// -----------------------------------------------------
private boolean simpleFk;
private boolean inCompositeFk;
public void setSimpleFk(boolean simpleFk) {
this.simpleFk = simpleFk;
if (!isInPk()) {
// since this attribute is not going to be mapped, we can remove it from var clash
collisionUtil.removeVar(getEntity().getName(), getColumnConfig().getFieldName());
}
}
@ForbiddenWhenBuilding
public boolean isSimpleFk() {
return simpleFk;
}
public void setInCompositeFk(boolean inCompositeFk) {
this.inCompositeFk = inCompositeFk;
if (!isInPk()) {
// since this attribute is not going to be mapped, we can remove it from var clash
collisionUtil.removeVar(getEntity().getName(), getColumnConfig().getFieldName());
}
}
@ForbiddenWhenBuilding
public boolean isInCompositeFk() {
return inCompositeFk;
}
public boolean isSimple() {
return !(isInPk() || isInFk() || isVersion());
}
private String setterAccessibility;
public String getSetterAccessibility() {
if (setterAccessibility == null) {
if (!isInPk() && isInFk() && hasXToOneRelation()) {
// developer are confused if it is public and are tempted to use it.
// The only reason we generate a setter is for Hibernate search by example feature.
// we make it private to avoid confusions..
setterAccessibility = "private";
} else {
setterAccessibility = "public";
}
}
return setterAccessibility;
}
public boolean isSetterAccessibilityPublic() {
return getSetterAccessibility().equals("public");
}
// -----------------------------------------------------
// BK
// -----------------------------------------------------
@Setter
@Getter
private boolean inBk;
// -----------------------------------------------------
// Column shortcuts
// -----------------------------------------------------
@Override
public String getName() {
return columnConfig.getFieldName();
}
public String getColumnName() {
return columnConfig.getColumnName();
}
private String columnNameEscaped;
public String getColumnNameEscaped() {
if (columnNameEscaped == null) {
columnNameEscaped = escapeSql(getColumnName());
}
return columnNameEscaped;
}
public String getColumnFullName() {
return getTableName() + "." + getColumnName();
}
public String getTableName() {
return columnConfig.getTableName();
}
public JdbcType getJdbcType() {
return getColumnConfig().getType();
}
public boolean isJavaBaseClass() {
return getMappedType().isJavaBaseClass() && !isEnum();
}
public boolean isEnum() {
return getColumnConfig().hasEnum();
}
public boolean isSortable() {
return !isBinary() && !isTransient();
}
public EnumConfig getEnumConfig() {
return getColumnConfig().getEnumConfig();
}
public EnumNamer getEnumModel() {
return getEnumType().getModel();
}
public String getEnumClass() {
return getEnumModel().getType();
}
public String getEnumItemsType() {
return getEnumType().getItems().getType();
}
public String getEnumItemsVar() {
return getEnumType().getItems().getVar();
}
public int getSize() {
return getColumnConfig().getSize();
}
public boolean isFixedSize() {
if (getJdbcType() == CHAR) {
return true;
}
if (getColumnConfig().getSize() != null && getColumnConfig().getSize().equals(getColumnConfig().getMin())) {
return true;
}
return false;
}
public String getComment() {
return getColumnConfig().getComment();
}
public String getJavadoc() {
if (getColumnConfig().hasComment()) {
return CommentStyle.JAVADOC.decorate(getColumnConfig().getComment(), " ");
} else if (getLabels().hasBaseLabel()) {
return CommentStyle.JAVADOC.decorate(labels.getLabel(), " ");
}
return "";
}
public boolean hasComment() {
return getColumnConfig().hasComment();
}
@ForbiddenWhenBuilding
public boolean isUnique() {
return getColumnConfig().getUnique();
}
public boolean isRequired() {
return !isNullable() || isUnique() || isCharPadding();
}
public boolean isCharPadding() {
return isFixedSize() && !isEnum() && getConfig().getCelerio().getConfiguration().has(CHAR_PADDING);
}
public boolean isNullable() {
return getColumnConfig().getNullable();
}
public boolean isNotNullable() {
return !isNullable();
}
public boolean hasDefaultValue() {
return getJavaDefaultValue() != null;
}
public boolean hasPertinentDefaultValue() {
return !isInPk() && !isInFk() && !isVersion() && hasDefaultValue();
}
public String getJavaDefaultValue() {
if (getColumnConfig().getDefaultValue() == null || isBlob()) {
return null;
} else if (isEnum()) {
EnumConfig enumConfig = getColumnConfig().getEnumConfig();
if (enumConfig.isCustomType() || enumConfig.isOrdinal()) {
return getEnumClass() + "." + enumConfig.getEnumNameByValue(getColumnConfig().getDefaultValue());
} else {
return getEnumClass() + "." + getColumnConfig().getDefaultValue();
}
} else if (isString()) {
return "\"" + getColumnConfig().getDefaultValue() + "\"";
} else if (isBoolean()) {
if ("1".equals(getColumnConfig().getDefaultValue())) {
return "true";
} else {
return toBoolean(getColumnConfig().getDefaultValue()) ? "true" : "false";
}
} else if (isDate()) {
List isNow = newArrayList("now()", "sysdate", "current_time");
if (isNow.contains(getColumnConfig().getDefaultValue().toLowerCase())) {
if (isLocalDateOrTime() || isZonedDateTime()) {
return getMappedType().getJavaType() + ".now()"; // TODO: nice import
} else if (getMappedType() == MappedType.M_UTILDATE) {
return "new " + getMappedType().getJavaType() + "()";
} else if (getMappedType() == MappedType.M_TIMESTAMP) {
return "new Timestamp(new Date().getTime())";
}
}
return null;
} else if (isNumeric()) {
String defaultValue = getColumnConfig().getDefaultValue();
if (NumberUtils.isNumber(defaultValue)) {
if (isBigDecimal()) {
return "new BigDecimal(\"" + defaultValue + "\")"; // use the right scale
} else if (isBigInteger()) {
return "new BigInteger(\"" + defaultValue + "\")";
} else if (isLong()) {
return defaultValue + "l";
} else if (isDouble()) {
return defaultValue + "d"; // so it is considered as a double
} else if (isFloat()) {
return defaultValue + "f"; // as by default it would be considered as a double
} else {
return defaultValue;
}
} else {
return null;
}
} else {
return getColumnConfig().getDefaultValue();
}
}
private String displayOrderAsString;
/**
* Used to sort columns in abstract list holder. Must be a String.
*/
public String getDisplayOrderAsString() {
if (displayOrderAsString == null) {
displayOrderAsString = orderToString(getColumnConfig().getDisplayOrder());
}
return displayOrderAsString;
}
private String formFieldOrderAsString;
/**
* Used to sort columns in abstract list holder. Must be a String.
*/
public String getFormFieldOrderAsString() {
if (formFieldOrderAsString == null) {
formFieldOrderAsString = orderToString(getColumnConfig().getFormFieldOrder());
}
return formFieldOrderAsString;
}
private String searchFieldOrderAsString;
/**
* Used to sort columns in abstract list holder. Must be a String.
*/
public String getSearchFieldOrderAsString() {
if (searchFieldOrderAsString == null) {
searchFieldOrderAsString = orderToString(getColumnConfig().getSearchFieldOrder());
}
return searchFieldOrderAsString;
}
private String searchResultOrderAsString;
/**
* Used to sort columns in abstract list holder. Must be a String.
*/
public String getSearchResultOrderAsString() {
if (searchResultOrderAsString == null) {
searchResultOrderAsString = orderToString(getColumnConfig().getSearchResultOrder());
}
return searchResultOrderAsString;
}
// -----------------------------------------------------
// Column defaulting to MappedType
// -----------------------------------------------------
@Override
public String getType() {
if (isEnum()) {
return getEnumModel().getType();
}
return getMappedType().getJavaType();
}
@Override
public String getFullType() {
if (isEnum()) {
return getEnumFullType();
}
return getMappedType().getFullJavaType();
}
private String getEnumFullType() {
return getEnumModel().getFullType();
}
/**
* Since import is always complicated, I use full type when needed for now in XxxValidator...
*/
public String getFullTypeIfImportNeeded() {
return isJavaBaseClass() ? getType() : getFullType();
}
// -----------------------------------------------------
// Convention over configuration
// -----------------------------------------------------
public boolean isLocaleKey() {
// configuration
if (null != getColumnConfig().getMessageKey()) {
return getColumnConfig().getMessageKey();
}
// convention
return IS_LOCALE_SUFFIX.apply(this);
}
public boolean columnNameHasLanguageSuffix() {
// convention
return IS_LANGUAGE_SUFFIX.apply(this);
}
public boolean isLabel() {
// convention
return isSimple() && IS_LABEL.apply(this);
}
public String getColumnNameWithoutLanguage() {
if (columnNameHasLanguageSuffix()) {
return StringUtils.substringBeforeLast(getColumnName(), "_").toLowerCase();
} else {
throw new IllegalStateException("Can be invoked only if columnNameHasLanguageSuffix returns true, please write safer code");
}
}
public String getColumnNameLanguage() {
if (columnNameHasLanguageSuffix()) {
return StringUtils.substringAfterLast(getColumnName(), "_").toLowerCase();
} else {
throw new IllegalStateException("Can be invoked only if columnNameHasLanguageSuffix returns true, please write safer code");
}
}
public boolean isVersion() {
boolean isVersion = false;
if (null != getColumnConfig().getVersion()) {
// configuration
isVersion = getColumnConfig().getVersion();
if (isVersion && !getMappedType().isEligibleForVersion()) {
throw new IllegalStateException("The column " + getFullColumnName()
+ " type cannot be used with @Version. Please review the entityConfig of entityName=" + getEntity().getName());
}
} else {
// convention
isVersion = IS_VERSION_SUFFIX.apply(this);
}
return isVersion && getMappedType().isEligibleForVersion();
}
private Boolean isInFileDefinition;
public boolean isInFileDefinition() {
if (isInFileDefinition == null) {
isInFileDefinition = HAS_FILE_ATTRIBUTES.apply(this) && (isFile() || isFileSize() || isFilename() || isContentType());
}
return isInFileDefinition;
}
public boolean isFile() {
return (isBlob() || isFileBinary()) && !isInPk();
}
private Boolean isFile;
public boolean isFileBinary() {
if (isFile == null) {
isFile = IS_FILE_BINARY.apply(this);
}
return isFile;
}
private Boolean isFileSize;
public boolean isFileSize() {
if (isFileSize == null) {
isFileSize = IS_FILE_SIZE.apply(this);
}
return isFileSize;
}
private Boolean isFilename;
public boolean isFilename() {
if (isFilename == null) {
isFilename = IS_FILE_NAME.apply(this);
}
return isFilename;
}
private Boolean isContentType;
public boolean isContentType() {
if (isContentType == null) {
isContentType = IS_FILE_CONTENT_TYPE.apply(this);
}
return isContentType;
}
public boolean isPatternSearchable() {
return isString() && isSimple() && !isEnum() && !isTransient();
}
public boolean isCpkPatternSearchable() {
return isString() && !isEnum() && !isTransient();
}
public boolean isTransient() {
return getColumnConfig().isTransient();
}
public Attribute getFileSize() {
return findFileAttributeOrNull(IS_FILE_SIZE);
}
public Attribute getFileContentType() {
return findFileAttributeOrNull(IS_FILE_CONTENT_TYPE);
}
public Attribute getFilename() {
return findFileAttributeOrNull(IS_FILE_NAME);
}
public Attribute getFile() {
Attribute result = isBlob() ? this : findFileAttributeOrNull(IS_FILE_BINARY);
if (result == null) {
// Note: in some case, when we have multiple binary, there could be only one filename (ex: an image and the thumbnail)
// Here, the suffix does not match, let's return the first binary file we find. This way we
// do not return null and avoid issue in front end templates.
result = find(getEntity().getAttributes().getList(), BLOB);
}
return result;
}
private Attribute findFileAttributeOrNull(Predicate predicate) {
try {
return find(getEntity().getAttributes().getList(), and(predicate, new AttributeShareSameSuffix(this)));
} catch (NoSuchElementException nse) {
return null; // can be interpreted as 'false' in velocity if statement
}
}
private Boolean isEmail;
public boolean isEmail() {
if (isEmail == null) {
isEmail = IS_EMAIL.apply(this);
}
return isEmail;
}
private Boolean isPassword;
public boolean isPassword() {
if (isPassword == null) {
isPassword = IS_PASSWORD.apply(this);
}
return isPassword;
}
public boolean isVisible() {
if (getColumnConfig().getVisible() != null) {
return getColumnConfig().getVisible();
} else if (isSimplePk() && jpa.isManuallyAssigned()) {
return true;
} else if (isSimplePk() && !isInFk()) {
return false;
} else if (isSimpleFk()) {
return true;
} else if (isInFk()) {
return false;
} else if (isVersion() || getEntity().getAuditEntityAttributes().contains(this)) {
return false;
} else {
return true;
}
}
public boolean isFormField() {
// configuration
if (getEntity().getEntityConfig().useFormFieldConfig()) {
if (getColumnConfig().getFormField() != null) {
return getColumnConfig().getFormField();
} else {
return false;
}
}
// convention
if (isSimplePk() || isVersion()) {
return true;
} else if (isFile()) {
return true;
} else if (isInFileDefinition()) {
return false;
} else {
return isVisible();
}
}
public boolean isSearchResultFieldConvention() {
return isVisible() && !isHtml() && !isBinary() && !isFileSize() && !isContentType() && !(hasXToOneRelation() && !getXToOneRelation().hasInverse());
}
public boolean isSearchResultField() {
if (getColumnConfig().getSearchResult() != null) {
// configuration
return getColumnConfig().getSearchResult();
} else {
// convention
return isSearchResultFieldConvention();
}
}
public boolean isSearchField() {
if (getColumnConfig().getSearchField() != null) {
// configuration
return getColumnConfig().getSearchField();
} else {
// convention
return isVisible() && !isInFileDefinition() && !isBinary() && !isHtml() && !isTransient();
}
}
/**
* Can we apply a search by range with this attribute?
*/
public boolean isRangeable() {
return (!isInPk() && !isInFk() && !isVersion()) && (isDate() || isNumeric()) && !isEnum();
}
/**
* Can we apply a search with a PropertySelector on this attribute?
*/
public boolean isMultiSelectable() {
return ((!isInPk() && !isInFk() && !isVersion()) || (isSimplePk() && jpa.isManuallyAssigned())) //
&& (isBoolean() || isEnum() || isString() || isNumeric());
}
// -----------------------------------------------------
// Mapped Type & shortcuts
// -----------------------------------------------------
public MappedType getMappedType() {
if (mappedType == null) {
mappedType = new AttributeSetup(this).getMappedType();
}
return mappedType;
}
@Override
final public String getPackageName() {
if (isEnum()) {
return getEnumModel().getPackageName();
} else {
return getMappedType().getPackageName();
}
}
/**
* Whether this attribute is numeric and is neither an enum nor a version.
*
* @return
*/
final public boolean hasDigits() {
return isNumeric() && !isEnum() && !isVersion();
}
final public boolean isNumeric() {
return getMappedType().isNumeric();
}
final public boolean isLong() {
return getMappedType().isLong();
}
final public boolean isInteger() {
return getMappedType().isInteger();
}
final public boolean isBigInteger() {
return getMappedType().isBigInteger();
}
final public boolean isDouble() {
return getMappedType().isDouble();
}
final public boolean isFloat() {
return getMappedType().isFloat();
}
final public boolean isBigDecimal() {
return getMappedType().isBigDecimal();
}
final public boolean isString() {
return getMappedType().isString();
}
final public boolean isChar() {
return getMappedType().isChar();
}
final public boolean isBoolean() {
return getMappedType().isBoolean();
}
final public boolean isDate() {
return getMappedType().isDate();
}
final public boolean isJavaUtilDate() {
return getMappedType().isJavaUtilDate();
}
final public boolean isJavaUtilOnlyDate() {
return getMappedType().isJavaUtilDate() && getJdbcType() == JdbcType.DATE;
}
final public boolean isJavaUtilDateAndTime() {
return getMappedType().isJavaUtilDate() && getJdbcType() == JdbcType.TIMESTAMP;
}
final public boolean isJavaUtilOnlyTime() {
return getMappedType().isJavaUtilDate() && getJdbcType() == JdbcType.TIME;
}
final public boolean isLocalDateOrTime() {
return isLocalDate() || isLocalDateTime();
}
final public boolean isLocalDate() {
return getMappedType().isLocalDate();
}
final public boolean isLocalDateTime() {
return getMappedType().isLocalDateTime();
}
final public boolean isZonedDateTime() { return getMappedType().isZonedDateTime(); }
final public boolean isInstant() { return getMappedType().isInstant(); }
final public boolean isLob() {
return getMappedType().isLob();
}
final public boolean isBlob() {
return getMappedType().isBlob();
}
final public boolean isClob() {
return getMappedType().isClob();
}
final public boolean isComparable() {
return getMappedType().isComparable();
}
// -----------------------------------------------
// Derived
// -----------------------------------------------
/**
* Whether this attribute is an integer, a long or a big integer.
*/
final public boolean isIntegralNumber() {
return isInteger() || isLong() || isBigInteger();
}
/**
* Is the corresponding column a LOB, BLOB or CLOB.
*
* @return
*/
final public boolean isBinary() {
return isLob() || isBlob() || isClob();
}
/**
* Whether this attribute has a non null {@link IndexedField} configuration element.
*
* @see ColumnConfig#getIndexedField
*/
public boolean isIndexed() {
return getColumnConfig().getIndexedField() != null;
}
public boolean isInPk() {
return isSimplePk() || isInCpk();
}
public boolean isInFk() {
return isSimpleFk() || isInCompositeFk();
}
public boolean isHidden() {
return !isVisible();
}
public boolean isLazyLoaded() {
return getColumnConfig().hasLazy() ? getColumnConfig().getLazy() : isLob();
}
public boolean isLocalizable() {
return isLocaleKey() || isDate() || isBoolean() || isEnum();
}
public boolean hasIntSetter() {
return !isInteger() && isNumeric() && !isVersion() && !isEnum() && isSetterAccessibilityPublic();
}
public boolean isHtml() {
// configuration
if (getColumnConfig().hasHtml()) {
return getColumnConfig().getHtml();
} else if (getColumnConfig().hasSafeHtml()) {
return true;
} else {
// convention
return CONTAINS_HTML.apply(this);
}
}
public boolean isSafeHtml() {
if (getColumnConfig().getHtml() == Boolean.FALSE) {
return false;
} else if (getColumnConfig().hasSafeHtml()) {
return true;
}
return false;
}
public boolean isUrl() {
return isString() && IS_URL_SUFFIX.apply(this);
}
public boolean isTextArea() {
return isString() && getSize() >= 255;
}
@Override
public String toString() {
return getName() + " " + ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
}
public boolean isAuditEntityAttribute() {
return getEntity().getAuditEntityAttributes().contains(this);
}
// -----------------------------------------------
// equals & hashCode
// -----------------------------------------------
@Override
final public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof Attribute)) {
return false;
}
Attribute other = (Attribute) o;
return getColumnFullName().equals(other.getColumnFullName());
}
@Override
final public int hashCode() {
return getColumnFullName().hashCode();
}
// -----------------------------------------------------
// Account Entity Related
// -----------------------------------------------------
public String getDefaultValueForCurrentAccountId() {
if (getEntity().isAccount()) {
if (getMappedType().isString()) {
return "\"-1\"";
}
if (getMappedType().isLong()) {
return "-1l";
}
// default
return "-1";
}
return "null"; // won't work...
}
// -----------------------------------------------------
// Very useful from the View
// -----------------------------------------------------
/**
* When there are dozen of attributes, it is convenient to have next to the attribute declaration a short comment such as "// not null"
*/
public String getOneLineComment() {
if (isSimplePk()) {
return " // pk";
}
if (isNullable() && isUnique()) {
return " // unique (but null allowed)";
}
if (isNotNullable() && isUnique()) {
return " // unique (not null)";
}
if (isNotNullable()) {
return " // not null";
}
return "";
}
public boolean hasXToOneRelation() {
return getXToOneRelation() != null;
}
/**
* Should replace hasXToOneRelation.
*/
public boolean hasForwardXToOneRelation() {
return getXToOneRelation() != null && !getXToOneRelation().isInverse();
}
private Relation xToOneRelation;
private boolean xToOneRelationSet;
@ForbiddenWhenBuilding
public Relation getXToOneRelation() {
if (!xToOneRelationSet) {
for (Relation r : getEntity().getXToOne().getList()) {
if (r.isIntermediate()) {
continue;
} else if (r.getFromAttribute() == this) {
xToOneRelation = r;
xToOneRelationSet = true;
break;
}
}
}
return xToOneRelation;
}
public Entity getEntityIPointTo() {
Relation relation = getXToOneRelation();
if (relation == null) {
throw new IllegalStateException("you should have at least a XToOne relation");
}
return relation.getToEntity();
}
public String getVarPath() {
return isInCpk() ? getEntity().getPrimaryKey().getVar() + "." + getVar() : getVar();
}
// derived
public static Predicate IS_FILE_SIZE = and(NUMERIC, and(IS_FILE_SIZE_SUFFIX, HAS_FILE_ATTRIBUTES));
public static Predicate IS_FILE_NAME = and(STRING, and(IS_FILE_NAME_SUFFIX, HAS_FILE_ATTRIBUTES));
public static Predicate IS_FILE_BINARY = and(BLOB, and(IS_BINARY_SUFFIX, HAS_FILE_ATTRIBUTES));
public static Predicate IS_FILE_CONTENT_TYPE = and(STRING, and(IS_CONTENT_TYPE_SUFFIX, HAS_FILE_ATTRIBUTES));
public static Predicate IS_EMAIL = and(STRING, IS_EMAIL_SUFFIX);
public static Predicate IS_PASSWORD = and(STRING, IS_PASSWORD_SUFFIX);
// ------------------------------------
// SPI are put in a Map so we can access
// from velocity templates as if we had getter.
// ------------------------------------
private Map spis = newHashMap();
@Override
public void clear() {
spis.clear();
}
@Override
public boolean containsKey(Object arg0) {
return spis.containsKey(arg0);
}
@Override
public boolean containsValue(Object arg0) {
return spis.containsValue(arg0);
}
@Override
public Set> entrySet() {
return spis.entrySet();
}
@Override
public Object get(Object arg0) {
Object o = spis.get(arg0);
Preconditions.checkNotNull(o, "No SPI having its var=" + arg0 + " was found. Tip: in your template for predicate method, use always ref.isSomething() instead of xxx.something");
return o;
}
@Override
public boolean isEmpty() {
return spis.isEmpty();
}
@Override
public Set keySet() {
return spis.keySet();
}
@Override
public Object put(String arg0, Object arg1) {
return spis.put(arg0, arg1);
}
@Override
public void putAll(Map extends String, ? extends Object> arg0) {
spis.putAll(arg0);
}
@Override
public Object remove(Object arg0) {
return spis.remove(arg0);
}
@Override
public int size() {
return spis.size();
}
@Override
public Collection