de.tsl2.nano.bean.def.AttributeDefinition Maven / Gradle / Ivy
/*
* File: $HeadURL$
* Id : $Id$
*
* created by: Thomas Schneider
* created on: Oct 13, 2012
*
* Copyright: (c) Thomas Schneider 2012, all rights reserved
*/
package de.tsl2.nano.bean.def;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.Format;
import java.text.ParseException;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedList;
import java.util.Map;
import java.util.UUID;
import org.apache.commons.logging.Log;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Default;
import org.simpleframework.xml.DefaultType;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.core.Commit;
import org.simpleframework.xml.core.Persist;
import de.tsl2.nano.action.IActivable;
import de.tsl2.nano.action.IConstraint;
import de.tsl2.nano.action.IStatus;
import de.tsl2.nano.bean.BeanContainer;
import de.tsl2.nano.bean.BeanUtil;
import de.tsl2.nano.bean.IAttributeDef;
import de.tsl2.nano.bean.IConnector;
import de.tsl2.nano.bean.IRuleCover;
import de.tsl2.nano.bean.IValueAccess;
import de.tsl2.nano.bean.ValueHolder;
import de.tsl2.nano.core.ENV;
import de.tsl2.nano.core.ManagedException;
import de.tsl2.nano.core.cls.BeanAttribute;
import de.tsl2.nano.core.cls.BeanClass;
import de.tsl2.nano.core.cls.IAttribute;
import de.tsl2.nano.core.cls.PrivateAccessor;
import de.tsl2.nano.core.cls.UnboundAccessor;
import de.tsl2.nano.core.log.LogFactory;
import de.tsl2.nano.core.messaging.EventController;
import de.tsl2.nano.core.messaging.IListener;
import de.tsl2.nano.core.secure.ISecure;
import de.tsl2.nano.core.util.ByteUtil;
import de.tsl2.nano.core.util.DelegationHandler;
import de.tsl2.nano.core.util.FormatUtil;
import de.tsl2.nano.core.util.MapUtil;
import de.tsl2.nano.core.util.NumberUtil;
import de.tsl2.nano.core.util.StringUtil;
import de.tsl2.nano.core.util.Util;
import de.tsl2.nano.format.RegExpFormat;
/**
*
* @author Thomas Schneider
* @version $Revision$
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
@Default(value = DefaultType.FIELD, required = false)
public class AttributeDefinition implements IAttributeDefinition {
/** serialVersionUID */
private static final long serialVersionUID = 1403875731423120506L;
@Element(name = "declaring")
protected IAttribute attribute;
protected EventController eventController;
@Element(type = Constraint.class, required = false)
protected IConstraint constraint;
@Attribute(required = false)
private boolean id;
@Attribute(required = false)
private boolean unique;
@Element(required = false)
private Class extends Date> temporalType;
@Element(required = false)
protected String description;
protected transient IStatus status;
@Element(type = Presentable.class, required = false)
private IPresentable presentable;
@Element(type = ValueColumn.class, required = false)
private IPresentableColumn columnDefinition;
@Attribute(required = false)
private boolean doValidation = true;
/** see {@link #composition()} */
@Attribute(required = false)
private boolean composition;
/** see {@link #cascading()} */
@Attribute(required = false)
private boolean cascading;
/** see {@link #generatedValue()} */
@Attribute(required = false)
private boolean generatedValue;
/** optional encryption */
@Element(required = false)
private ISecure secure;
/**
* optional plugins.
*/
@ElementList(inline = true, entry = "plugin", required = false)
protected Collection plugins;
private static final Log LOG = LogFactory.getLog(AttributeDefinition.class);
/** internal definition to create a temporarily new beanvalue instance. */
static final Method UNDEFINEDMETHOD = UNDEFINEDMETHOD();
/**
* constructor to be serializable
*
* @throws SecurityException
* @throws NoSuchMethodException
*/
protected AttributeDefinition() {
super();
status = Status.STATUS_OK;
}
/**
* constructor
*
* @param attribute
*/
public AttributeDefinition(IAttribute attribute) {
super();
this.attribute = attribute;
if (attribute.getAccessMethod() != null) {
defineDefaults();
}
}
public AttributeDefinition(String name, IConstraint constraint) {
this(new VAttribute(name));
this.constraint = constraint;
}
protected AttributeDefinition(Method readAccessMethod) {
super();
attribute = new BeanAttribute(readAccessMethod);
defineDefaults();
}
/**
* default readaccessmethod if default constructor was invoked
*/
private static final Method UNDEFINEDMETHOD() {
try {
//get yourself
return AttributeDefinition.class.getDeclaredMethod("UNDEFINEDMETHOD", new Class[0]);
} catch (Exception e) {
ManagedException.forward(e);
return null;
}
}
/**
* sets default properties through the {@link BeanContainer}, if available. These defaults may be read from
* jpa-annotations.
*/
protected void defineDefaults() {
defineFromAnnotations();
if (BeanContainer.isInitialized() && BeanContainer.instance().isPersistable(getDeclaringClass())) {
IAttributeDef def = BeanContainer.instance().getAttributeDef(getDeclaringClass(), getName());
if (def != null) {
LOG.debug("setting defaults from annotations for attribute: " + getName());
setId(def.id());
setUnique(def.unique());
setBasicDef(def.length(), def.nullable(), null, null, null);
getConstraint().setNumberDef(def.scale(), def.precision());
temporalType = def.temporalType();
composition = def.composition();
cascading = def.cascading();
generatedValue = def.generatedValue();
}
}
if (status == null)
initDeserialization();
}
private void defineFromAnnotations() {
Method m = getAccessMethod();
if (m != null) {
if (constraint == null && m.isAnnotationPresent(de.tsl2.nano.bean.annotation.Constraint.class)) {
de.tsl2.nano.bean.annotation.Constraint c =
m.getAnnotation(de.tsl2.nano.bean.annotation.Constraint.class);
constraint = new Constraint(c.type(), c.allowed());
constraint.setBasicDef(c.length(), c.nullable(),
new RegExpFormat(c.pattern(), 255), (T) Util.nonEmpty(c.defaultValue()));
}
if (presentable == null && m.isAnnotationPresent(de.tsl2.nano.bean.annotation.Presentable.class)) {
de.tsl2.nano.bean.annotation.Presentable p =
m.getAnnotation(de.tsl2.nano.bean.annotation.Presentable.class);
presentable = new Presentable(this);
presentable.setPresentation(p.label(), p.type(), p.style(), p.enabled() ? IActivable.ACTIVE : IActivable.INACTIVE, p.visible()
, (Serializable)MapUtil.asMap(p.layout()), (Serializable)MapUtil.asMap(p.layoutConstraints()), p.description());
presentable.setIcon(p.icon());
}
if (columnDefinition == null && m.isAnnotationPresent(de.tsl2.nano.bean.annotation.Column.class)) {
de.tsl2.nano.bean.annotation.Column c =
m.getAnnotation(de.tsl2.nano.bean.annotation.Column.class);
columnDefinition = new ValueColumn(this);
ValueColumn vc = (ValueColumn) columnDefinition;
vc.name = c.name();
vc.format = new RegExpFormat(c.pattern(), 255);
vc.columnIndex = c.index();
vc.width = c.width();
vc.sortIndex = c.sortIndex();
vc.isSortUpDirection = c.sortUp();
// vc.minsearch = c.min();
// vc.maxsearch = c.max();
}
}
}
@Persist
private void initSerialization() {
//disconnect from beandefinition to be serializable
if (plugins != null) {
for (IConnector p : plugins) {
LOG.info("disconnecting plugin " + p + " from " + this);
p.disconnect(this);
}
}
}
@Commit
private void initDeserialization() {
status = IStatus.STATUS_OK;
if (getColumnDefinition() != null && getColumnDefinition() instanceof ValueColumn) {
((ValueColumn) getColumnDefinition()).attributeDefinition = this;
}
//injectAttributeOnChangeListeners() will be called from BeanDefinition
injectIntoPlugins(this);
}
/**
* injectPlugins
*/
protected void injectIntoPlugins(IAttributeDefinition attr) {
//connect optional plugins
if (plugins != null) {
for (IConnector p : plugins) {
LOG.info("connecting plugin " + p + " to " + attr);
p.connect(attr);
}
}
}
/**
* injectRuleCover
*
* @param attr new attribute definition to connect to rule cover instance
*/
protected void injectIntoRuleCover(IValueDefinition attr) {
//connect optional rule-covers (use accessor instead of BeanDefinition to avoid stackoverflow
injectIntoRuleCover(new PrivateAccessor(attr), attr.getInstance());
}
/**
* inject the given attribute as context - walks recursive to the member tree of acc
*
* @param acc direct/indirect member of attr in member tree
* @param instance to be set as context object in all RuleCover Proxies.
*/
protected static void injectIntoRuleCover(UnboundAccessor acc, Object instance) {
//connect optional rule-covers (use accessor instead of BeanDefinition to avoid stackoverflow
Map members = acc.members();
InvocationHandler handler;
Object item;
for (Object k : members.keySet()) {
item = members.get(k);
if (item != null) {
//first inject the child tree - be careful, don't produce a stackoverflow
if (item != instance && Util.isFrameworkClass(item.getClass()) && !item.getClass().isAnonymousClass()
&& !(item instanceof IAttribute) && !(item instanceof BeanDefinition))
injectIntoRuleCover(new PrivateAccessor(item), instance);
//now the own direct members
if (Proxy.isProxyClass(item.getClass())) {
handler = Proxy.getInvocationHandler(item);
//create proxy for each bean instance
if (handler instanceof DelegationHandler && handler instanceof IRuleCover) {
// compare instances: if attr is a delegation-handler we must ignore its delegate!
if (item == instance) {
LOG.warn/*throw new IllegalStateException*/("the given instance " + instance
+ " seems to be a rulecover itself!");
continue;
}
handler = (InvocationHandler) ((DelegationHandler) handler).clone();
item = DelegationHandler.createProxy((DelegationHandler) handler);
acc.set((String) k, item);
new UnboundAccessor(handler).call("setContext", null, new Class[] { Serializable.class },
instance);
}
}
}
}
}
/**
* uses the information of {@link #attributeID} to inject the real {@link AttributeDefinition} into registered
* change listeners
*
* @throws CloneNotSupportedException
*/
void injectAttributeOnChangeListeners(BeanDefinition beandef) {
//provide dependency listeners their attribute-definition
if (hasListeners()) {
Collection listener = changeHandler().getListeners(null);
boolean isBean = beandef instanceof Bean;
for (IListener l : listener) {
if (l instanceof AbstractDependencyListener) {
AbstractDependencyListener, ?> dl = (AbstractDependencyListener, ?>) l;
if (isBean) {//create a specific listener instance for the given bean!
dl = (AbstractDependencyListener, ?>) dl.clone();
Class eventType = changeHandler().getEventType(l);
changeHandler().removeListener(l);
changeHandler().addListener(dl, eventType);
}
if (dl.attributeID != null) {
String name = StringUtil.substring(dl.attributeID, ".", null);
dl.setAttribute((AttributeDefinition) beandef.getAttribute(name));
}
}
}
}
}
@Override
public final IConstraint getConstraint() {
if (constraint == null) {
constraint = new Constraint(BeanClass.getDefiningClass(attribute.getType()));
}
return constraint;
}
/**
* setBasicDef
*
* @param length {@link #length()}
* @param nullable {@link #nullable()}
* @param format {@link #getPattern()}
* @param defaultValue {@link #getDefault()}
* @param description {@link #getDescription()}
*/
@Override
public IAttributeDefinition setBasicDef(int length,
boolean nullable,
Format format,
T defaultValue,
String description) {
try {
getConstraint().setBasicDef(length, nullable, format, defaultValue);
} catch (Exception e) {
if (doValidation) {
ManagedException.forward(e);
}
}
this.description = description;
return this;
}
/**
* {@inheritDoc}
*/
@Override
public T getValue(Object beanInstance) {
try {
//using the default may result in problems on checking the value (e.g. in value-expression or isValue())
T result = /*beanInstance == null && isVirtualAccess() ? getDefault() : */attribute.getValue(beanInstance);
if (result != null && secure != null)
result = (T) secure.decrypt((String) result);
return result;
} catch (Exception ex) {
LOG.error("error evaluating value for attribute '" + getName() + "'", ex);
status = new Status(ex);
return null;
}
}
/**
* {@inheritDoc}
*/
@Override
public IAttributeDefinition setId(boolean isId) {
this.id = isId;
return this;
}
/**
* {@inheritDoc}
*/
@Override
public IAttributeDefinition setUnique(boolean isUnique) {
this.unique = isUnique;
return this;
}
/**
* {@inheritDoc}
*/
@Override
public int length() {
return getConstraint().getLength();
}
/**
* {@inheritDoc}
*/
@Override
public int scale() {
return getConstraint().getScale();
}
/**
* {@inheritDoc}
*/
@Override
public int precision() {
return getConstraint().getPrecision();
}
/**
* {@inheritDoc}
*/
@Override
public boolean nullable() {
return getConstraint().isNullable();
}
/**
* {@inheritDoc}
*/
@Override
public boolean id() {
return id;
}
@Override
public boolean unique() {
return unique;
}
/**
* {@inheritDoc}
*/
@Override
public Class extends Date> temporalType() {
return temporalType;
}
/**
* {@inheritDoc}
*/
@Override
public Format getFormat() {
if (getConstraint().getFormat() == null) {
Class type = getType();
if (Collection.class.isAssignableFrom(type)) {
getConstraint().setFormat(new CollectionExpressionTypeFormat(getGenericType(0)));
} else if (Map.class.isAssignableFrom(type)) {
getConstraint().setFormat(new MapExpressionFormat(getGenericType(1)));
} else if (type.isEnum()) {
getConstraint().setFormat(FormatUtil.getDefaultFormat(type, true));
} else if (BeanUtil.isStandardType(type) && !ByteUtil.isByteStream(type)) {
getConstraint().setFormat(ENV.get(BeanPresentationHelper.class).getDefaultRegExpFormat(this));
} else {
getConstraint().setFormat(new ValueExpressionTypeFormat(type));
}
}
return getConstraint().getFormat();
}
@Override
public ValueExpression getValueExpression() {
Format f = getFormat();
return f instanceof ValueExpressionFormat ? ((ValueExpressionFormat) f).getValueExpression() : null;
}
/**
* {@inheritDoc}
*/
@Override
public Class getType() {
return attribute.getType();
}
@Override
public Method getAccessMethod() {
return attribute.getAccessMethod();
}
protected Class getGenericType(int pos) {
return (Class) (getAccessMethod() != null ? BeanAttribute.getGenericType(getAccessMethod(), pos)
: Object.class);
}
/**
* isMultiValue
*
* @return true, if the value type is a collection
*/
@Override
public boolean isMultiValue() {
Class> type = getType();
return Collection.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type);
}
/**
* see {@link BeanClass#getActions()} and {@link BeanDefinition#isSelectable()}
*
* @param beanValue attribute to evaluate
* @return true, if bean type is selectable
*/
public boolean isSelectable() {
Class type = getType();
return isMultiValue()
|| (!BeanUtil.isStandardType(type) && BeanDefinition.getBeanDefinition(type).isSelectable());
}
/**
* {@inheritDoc}
*/
@Override
public IStatus isValid(T value) {
return getConstraint().checkStatus(getId(), value);
}
/**
* getParsedValue. throws a runtime exception on parsing error.
*
* @param source text to parse
* @return parsed value
*/
public T getParsedValue(String source) {
T value = null;
if (getFormat() != null) {
try {
if (Util.isEmpty(source) && nullable())
return null;
//the parser will decide, how to handle empty/null values
value = (T) getFormat().parseObject(source);
} catch (ParseException e) {
ManagedException.forward(e);
}
} else if (String.class.isAssignableFrom(getType())) {
value = (T) source;
} else {
throw new ManagedException("no format/parser available for field " + getName());
}
return value;
}
/**
* hasStatusError
*
* @return true, if has status.error != null
*/
public boolean hasStatusError() {
return status != null && status.error() != null;
}
/**
* {@inheritDoc}
*/
public void check() {
if (hasStatusError()) {
throw new RuntimeException(status.error());
}
}
/**
* {@inheritDoc}
*/
public T getDefault() {
IConstraint c = getConstraint();
if (c.getDefault() == null && getAccessMethod() != null && !c.isNullable()
&& !BeanPresentationHelper.isGeneratedValue(this)) {
Object genType = getAccessMethod().getGenericReturnType();
if (genType instanceof Class) {
Class gtype = (Class) genType;
if (BeanUtil.isStandardType(gtype)) {
if (BeanClass.hasDefaultConstructor(gtype)) {
getConstraint().setDefault(BeanClass.createInstance(gtype));
}
}
}
if (c.getDefault() == null) {
if (NumberUtil.isNumber(getType())) {
getConstraint().setDefault((T) NumberUtil.getDefaultInstance((Class) getType()));
} else if (ENV.isTestMode()) {
/*
* to create new entities without user input, these fields are filled on test mode
*/
if (CharSequence.class.isAssignableFrom(getType())) {
c.setDefault((T) ("Y" + UUID.randomUUID().toString().substring(0, c.getLength() - 1)));
} else if (BeanContainer.instance().isPersistable(getType())) {
Collection beans = BeanContainer.instance().getBeans(getType(), 0, 1);
if (isMultiValue()) {
c.setDefault((T) beans);
} else if (beans.size() > 0) {
c.setDefault(beans.iterator().next());
}
}
}
}
}
return getConstraint().getDefault();
}
/**
* {@inheritDoc}
*/
@Override
public String getDescription() {
if (description == null && getName() != null) {
if (getPresentation() != null) {
description = getPresentation().getDescription();
} else {
description = StringUtil.toFirstUpper(getName());
}
}
return description;
}
public void setDescription(String description) {
this.description = description;
}
/**
* {@inheritDoc}
*/
@Override
public IAttributeDefinition setNumberDef(int scale, int precision) {
getConstraint().setNumberDef(scale, precision);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public IAttributeDefinition setRange(Comparable min, Comparable max) {
getConstraint().setRange(min, max);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public IAttributeDefinition setRange(Collection allowedValues) {
getConstraint().setRange(allowedValues);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public IAttributeDefinition setFormat(Format format) {
getConstraint().setFormat(format);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public IAttributeDefinition setLength(int length) {
getConstraint().setLength(length);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public IAttributeDefinition setScale(int scale) {
getConstraint().setScale(scale);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public IAttributeDefinition setPrecision(int precision) {
getConstraint().setPrecision(precision);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public IAttributeDefinition setNullable(boolean nullable) {
getConstraint().setNullable(nullable);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public IStatus getStatus() {
return status;
}
public void setStatus(IStatus status) {
this.status = status;
}
/** see IVirtualDefinition#isRelation() */
@Override
public boolean isRelation() {
return BeanContainer.isConnected()
&& BeanContainer.instance().isPersistable(getType());
}
/**
* if the bean instance is of type {@link ValueHolder}, the attribute is virtual - no special bean-attribute is
* available, the attribute name is always {@link ValueHolder#getValue()} .
*
* @return true, if the declaring class is of type {@link IValueAccess}.
*/
@Override
public boolean isVirtual() {
return attribute.isVirtual();
}
protected boolean isVirtualAccess() {
return isVirtual() && getAccessMethod() != null;
}
/**
* getPresentation
*
* @return
*/
@Override
public IPresentable getPresentation() {
//TODO: create presentation helper through MultipleInheritanceProxy
if (presentable == null) {
presentable = ENV.get(BeanPresentationHelper.class).createPresentable(this);
}
return presentable;
}
/**
* setPresentation
*
* @param label
* @param type
* @param style
* @param enabler
* @param visible
* @param layout
* @param layoutConstraints
* @param description
* @return
*/
public IPresentable setPresentation(final String label,
final int type,
final int style,
final IActivable enabler,
final boolean visible,
final Map layout,
final Map layoutConstraints,
final String description) {
presentable = ENV.get(BeanPresentationHelper.class).createPresentable();
presentable.setPresentation(label, type, style, enabler, visible, (Serializable) layout,
(Serializable) layoutConstraints, description);
return presentable;
}
/**
* setPresentation
*
* @param presentable
*/
public void setPresentation(IPresentable presentable) {
this.presentable = presentable;
}
/**
* perhaps that definition was build yet. use BeanCollector.getColumnDefinition() to get the column - on first time
* they will be created.
*
* @return Returns the columnDefinition.
*/
@Override
public IPresentableColumn getColumnDefinition() {
return columnDefinition;
}
/**
* @param columnDefinition The columnDefinition to set.
*/
public void setColumnDefinition(IPresentableColumn columnDefinition) {
this.columnDefinition = columnDefinition;
}
/**
* setColumnDefinition
*
* @param index
* @param sortIndex
* @param sortUpDirection
* @param width
*/
@Override
public void setColumnDefinition(int index, int sortIndex, boolean sortUpDirection, int width) {
this.columnDefinition = new ValueColumn(this, index, sortIndex, sortUpDirection, width);
this.columnDefinition.setPresentable(getPresentation());
}
/**
* @return Returns the doValidation.
*/
public boolean isDoValidation() {
return doValidation;
}
/**
* @param doValidation The doValidation to set.
*/
public void setDoValidation(boolean doValidation) {
this.doValidation = doValidation;
}
/**
* {@inheritDoc}
*/
@Override
public boolean composition() {
return composition;
}
@Override
public boolean cascading() {
return cascading;
}
@Override
public boolean generatedValue() {
return generatedValue;
}
@Override
public void setAsRelation(String relationChain) {
new PrivateAccessor>(this).set("name", relationChain);
}
/**
* @return Returns the plugins.
*/
@Override
public Collection getPlugins() {
return plugins;
}
/**
* @param plugin The plugin to add.
*/
@Override
public void addPlugin(IConnector plugin) {
if (plugins == null) {
plugins = new LinkedList();
}
LOG.info("connecting plugin " + plugin + " to " + this);
plugin.connect(this);
plugins.add(plugin);
}
/**
* removePlugin
*
* @param plugin to remove
* @return true, if plugin was removed
*/
@Override
public boolean removePlugin(IConnector plugin) {
if (plugins == null) {
LOG.warn("plugin " + plugin + " can't be removed. no plugins available yet!");
return false;
}
LOG.info("disconnecting plugin " + plugin + " from " + this);
plugin.disconnect(this);
return plugins.remove(plugin);
}
///////////////////////////////////////////////////////////////////////////////
// Delegators to IAttribute
///////////////////////////////////////////////////////////////////////////////
/**
* {@inheritDoc}
*/
@Override
public Class getDeclaringClass() {
return attribute.getDeclaringClass();
}
/**
* {@inheritDoc}
*/
@Override
public String getName() {
return attribute.getName();
}
@Override
public void setName(String name) {
if (!isVirtual()) {
throw new IllegalStateException("name cannot be changed on non-virtual (=fixed) attributes!");
}
attribute.setName(name);
}
public String getPath() {
return getDeclaringClass().getName() + "." + getName();
}
/**
* {@inheritDoc}
*/
@Override
public void setValue(Object instance, T value) {
if (secure != null)
value = (T) secure.encrypt((String) value);
attribute.setValue(instance, value);
}
/**
* @return Returns the secure.
*/
public ISecure getSecure() {
return secure;
}
/**
* @param secure The secure to set.
*/
public void setSecure(ISecure secure) {
if (String.class.isAssignableFrom(getType()))
throw new IllegalStateException("encrypted fields must be of type String.class");
this.secure = secure;
}
/**
* {@inheritDoc}
*/
@Override
public String getId() {
return attribute.getId();
}
/**
* {@inheritDoc}
*/
@Override
public boolean hasWriteAccess() {
return attribute.hasWriteAccess();
}
/**
* looks for registered change handlers without creating an {@link EventController} instance.
*
* @return
*/
public boolean hasListeners() {
return eventController != null && eventController.hasListeners();
}
/**
* {@inheritDoc}
*/
@Override
public EventController changeHandler() {
if (eventController == null) {
eventController = new EventController();
}
return eventController;
}
@Override
public boolean equals(Object obj) {
return hashCode() == obj.hashCode();
}
@Override
public int hashCode() {
return attribute != null ? attribute.hashCode() : super.hashCode();
}
/**
* {@inheritDoc}
*/
@Override
public int compareTo(IAttribute o) {
return attribute.compareTo(o);
}
@Override
public String toString() {
return attribute != null ? attribute.toString() : super.toString();
}
public String toDebugString() {
return Util.toString(getClass(), "declaringClass: " + getType(), "temporal-type: " + temporalType, "name: "
+ getName(),
"id: " + id, "unique: " + unique, "cascading: " + cascading, "composition: " + composition, "\nattribute: "
+ attribute,
"\nstatus: "
+ status,
"\nconstraints: "
+ constraint,
"\npresentable: " + presentable);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy