/* * Copyright 2001-2008 Geert Bevin <gbevin[remove] at uwyn dot com> * Licensed under the Apache License, Version 2.0 (the "License") * $Id: ConstrainedProperty.java 3918 2008-04-14 17:35:35Z gbevin $ */ package com.uwyn.rife.site; import java.util.*; import com.uwyn.rife.cmf.MimeType; import com.uwyn.rife.cmf.transform.ContentTransformer; import com.uwyn.rife.database.queries.CreateTable; import com.uwyn.rife.tools.ClassUtils; import com.uwyn.rife.tools.Convert; import java.text.Format; /** * A <code>ConstrainedProperty</code> object makes it possible to easily * define all constraints for a named property of a bean. * <p>The property name refers to the actual name of the bean property. * However, this sometimes doesn't correspond to its conceptual usage. It can * be handy to receive constraint violation reports with another conceptual * name: the subject name. Notice that this corresponds to the subject that is * used in a {@link ValidationError}. If no subject name is specified, the * property name will be used instead. * <p>It's possible to add constraints to a ConstrainedProperty instance * through regular setters, but chainable setters are also available to make * it possible to easily define a series of constraints, for example: * <pre>ConstrainedProperty constrained = new ConstrainedProperty("password") * .maxLength(8) * .notNull(true);</pre> * <p> * <p>Constrained properties are typically added to a {@link Constrained} bean * in its constructor. These are the static constraints that will be set for * each and every instance of the bean. You'll however most of the time use * the {@link MetaData} class that provides the {@link * MetaData#activateMetaData activateMetaData} method which initializes * the constraints on a need-to-have basis. This dramatically reduces memory * usage since otherwise all constraints will be initialized for every bean * instance, even though you don't use them, for example: * <pre>public class Credentials extends MetaData *{ * private String mLogin = null; * private String mPassword = null; * private String mLanguage = null; * * public Credentials() * { * } * * public activateMetaData() * { * addConstraint(new ConstrainedProperty("login").maxLength(6).notNull(true)); * addConstraint(new ConstrainedProperty("password").maxLength(8).notNull(true)); * addConstraint(new ConstrainedProperty("language").notNull(true)); * } * * public void setLogin(String login) { mLogin = login; } * public String getLogin() { return mLogin; } * public void setPassword(String password) { mPassword = password; } * public String getPassword() { return mPassword; } * public void setLanguage(String language) { mLanguage = language; } * public String getLanguage() { return mLanguage; } *}</pre> * <p> * <p>It's however also possible to add constraints to a single bean instance * whenever they can't be determined beforehand. These are then dynamic * constraints than can be populated at runtime, for example: * <pre>Credentials credentials = new Credentials(); *credentials.addConstraint(new ConstrainedProperty("language").inList(new String[] {"nl", "fr", "en"})); *</pre> * * @author Geert Bevin (gbevin[remove] at uwyn dot com) * @see Constrained * @see ConstrainedBean * @version $Revision: 3918 $ * @since 1.0 */ public class ConstrainedProperty<T extends ConstrainedProperty> implements Cloneable { // standard constraint identifiers public final static String NOT_NULL = "NOT_NULL"; public final static String NOT_EMPTY = "NOT_EMPTY"; public final static String NOT_EQUAL = "NOT_EQUAL"; public final static String UNIQUE = "UNIQUE"; public final static String IDENTIFIER = "IDENTIFIER"; public final static String EDITABLE = "EDITABLE"; public final static String PERSISTENT = "PERSISTENT"; public final static String SAVED = "SAVED"; public final static String DISPLAYED_RAW = "DISPLAYED_RAW"; public final static String MIN_LENGTH = "MIN_LENGTH"; public final static String MAX_LENGTH = "MAX_LENGTH"; public final static String SCALE = "SCALE"; public final static String REGEXP = "REGEXP"; public final static String EMAIL = "EMAIL"; public final static String URL = "URL"; public final static String MIN_DATE = "MIN_DATE"; public final static String MAX_DATE = "MAX_DATE"; public final static String IN_LIST = "IN_LIST"; public final static String RANGE_BEGIN = "RANGE_BEGIN"; public final static String RANGE_END = "RANGE_END"; public final static String DEFAULT_VALUE = "DEFAULT_VALUE"; public final static String SAME_AS = "SAME_AS"; public final static String MANY_TO_ONE = "MANY_TO_ONE"; public final static String MANY_TO_ONE_ASSOCIATION = "MANY_TO_ONE_ASSOCIATION"; public final static String MANY_TO_MANY = "MANY_TO_MANY"; public final static String MANY_TO_MANY_ASSOCIATION = "MANY_TO_MANY_ASSOCIATION"; public final static String FORMAT = "FORMAT"; public final static String FILE = "FILE"; public final static String SPARSE = "SPARSE"; // standard CMF constraint identifiers public final static String LISTED = "LISTED"; public final static String POSITION = "POSITION"; public final static String MIMETYPE = "MIMETYPE"; public final static String AUTO_RETRIEVED = "AUTO_RETRIEVED"; public final static String FRAGMENT = "FRAGMENT"; public final static String NAME = "NAME"; public final static String REPOSITORY = "REPOSITORY"; public final static String ORDINAL = "ORDINAL"; public final static String ORDINAL_RESTRICTION = "ORDINAL_RESTRICTION"; public final static String CONTENT_ATTRIBUTES = "CONTENT_ATTRIBUTES"; public final static String TRANSFORMER = "TRANSFORMER"; public final static String CACHED_LOADED_DATA = "CACHED_LOADED_DATA"; // required member variables private String mPropertyName = null; private String mSubjectName = null; // constraints protected Map<String, Object> mConstraints = new HashMap<String, Object>(); // listeners protected List<ConstrainedPropertyListener> mListeners; /** * Adds a new listener. * <p> * Listeners will be notified when events occur that are specified in the * {@code ConstrainedPropertyListener} interface. * * @param listener the listener instance that will be added * @since 1.6 */ public void addListener(ConstrainedPropertyListener listener) { if (null == listener) { return; } if (null == mListeners) { mListeners = new ArrayList<ConstrainedPropertyListener>(); } synchronized (mListeners) { if (!mListeners.contains(listener)) { mListeners.add(listener); } } } /** * Removes a listener. * <p> * Once the listener has been removed, it will not receive any events anymore. * * @param listener the listener instance that will be removed * * @return {@code true} when the listener could be found and has been removed; or * <p>{@code false} when the listener wasn't registered before * @since 1.6 */ public boolean removeListener(ConstrainedPropertyListener listener) { if (null == mListeners) { return false; } synchronized (mListeners) { return mListeners.remove(listener); } } private void fireConstraintSet(String name, Object constraintData) { if (null == mListeners) { return; } synchronized (mListeners) { for (ConstrainedPropertyListener listener : mListeners) { listener.constraintSet(this, name, constraintData); } } } /** * Creates a new <code>ConstrainedProperty</code> for the specified * property name. * * @param propertyName the name of the property that has to be * constrained * @since 1.0 */ public ConstrainedProperty(String propertyName) { if (null == propertyName) throw new IllegalArgumentException("propertyName can't be null."); if (0 == propertyName.length()) throw new IllegalArgumentException("propertyName can't be empty."); mPropertyName = propertyName; } /** * Sets the subject name. * * @param name the subject name * @return this <code>ConstrainedProperty</code> * @since 1.0 */ public T subjectName(String name) { setSubjectName(name); return (T)this; } /** * Sets the subject name. * * @param name the subject name * @since 1.0 */ public void setSubjectName(String name) { mSubjectName = name; } /** * Retrieves the subject name. * * @return the subject name; or * <p>the property name if no subject was specified. * @since 1.0 */ public String getSubjectName() { if (null == mSubjectName) { return mPropertyName; } return mSubjectName; } /** * Retrieves the property name. * * @return the property name * @since 1.0 */ public String getPropertyName() { return mPropertyName; } /** * Set whether the property value can be <code>null</code>. * <p>Note that this has different meanings in different contexts: * <ul> * <li>for values in java this is only applicable to object references * as primitive values are never <code>null</code>, * <li>for values that are stored in a database, it's applicable to * every column. * </ul> * * @param notNull <code>true</code> when the value can't be * <code>null</code>; or <code>false</code> when the value can be * <code>null</code>. * @return this <code>ConstrainedProperty</code> * @see #isNotNull() * @since 1.0 */ public T notNull(boolean notNull) { setNotNull(notNull); return (T)this; } /** * Set whether the property value can be <code>null</code>. * * @see #notNull(boolean) * @since 1.0 */ public void setNotNull(boolean notNull) { setConstraint(NOT_NULL, notNull); } /** * Retrieves whether the property value can be <code>null</code>. * * @return <code>true</code> when the value can't be <code>null</code>; * or * <p><code>false</code> when the value can be <code>null</code>. * @see #notNull(boolean) * @since 1.0 */ public boolean isNotNull() { return Convert.toBoolean(mConstraints.get(NOT_NULL), false); } /** * Set whether the property value can be empty. * <p>Note that this has different meanings for different datatypes * <ul> * <li>for textual types this is an empty string, ie. "", * <li>for numeric types this is 0 (zero). * </ul> * * @param notEmpty <code>true</code> when the value can't be empty; or * <code>false</code> when the value can be empty. * @return this <code>ConstrainedProperty</code> * @see #isNotEmpty() * @since 1.0 */ public T notEmpty(boolean notEmpty) { setNotEmpty(notEmpty); return (T)this; } /** * Set whether the property value can be empty. * * @see #notEmpty(boolean) * @since 1.0 */ public void setNotEmpty(boolean notEmpty) { setConstraint(NOT_EMPTY, notEmpty); } /** * Retrieves whether the property value can be empty. * * @return <code>true</code> when the value can't be empty; or * <p><code>false</code> when the value can be empty. * @see #notEmpty(boolean) * @since 1.0 */ public boolean isNotEmpty() { return Convert.toBoolean(mConstraints.get(NOT_EMPTY), false); } /** * Set that the property value can't be equal to a specified * <code>boolean</code> reference value. * * @param reference the reference value it will be checked against * @return this <code>ConstrainedProperty</code> * @see #isNotEqual() * @since 1.0 */ public T notEqual(boolean reference) { setNotEqual(reference); return (T)this; } /** * Set that the property value can't be equal to a specified * <code>byte</code> reference value. * * @see #notEqual(boolean) * @since 1.0 */ public T notEqual(byte reference) { setNotEqual(reference); return (T)this; } /** * Set that the property value can't be equal to a specified * <code>char</code> reference value. * * @see #notEqual(boolean) * @since 1.0 */ public T notEqual(char reference) { setNotEqual(reference); return (T)this; } /** * Set that the property value can't be equal to a specified * <code>short</code> reference value. * * @see #notEqual(boolean) * @since 1.0 */ public T notEqual(short reference) { setNotEqual(reference); return (T)this; } /** * Set that the property value can't be equal to a specified * <code>int</code> reference value. * * @see #notEqual(boolean) * @since 1.0 */ public T notEqual(int reference) { setNotEqual(reference); return (T)this; } /** * Set that the property value can't be equal to a specified * <code>long</code> reference value. * * @see #notEqual(boolean) * @since 1.0 */ public T notEqual(long reference) { setNotEqual(reference); return (T)this; } /** * Set that the property value can't be equal to a specified * <code>float</code> reference value. * * @see #notEqual(boolean) * @since 1.0 */ public T notEqual(float reference) { setNotEqual(reference); return (T)this; } /** * Set that the property value can't be equal to a specified * <code>double</code> reference value. * * @see #notEqual(boolean) * @since 1.0 */ public T notEqual(double reference) { setNotEqual(reference); return (T)this; } /** * Set that the property value can't be equal to a specified * <code>Object</code> reference value. * * @see #notEqual(boolean) * @since 1.0 */ public T notEqual(Object reference) { setNotEqual(reference); return (T)this; } /** * Set that the property value can't be equal to a specified * <code>boolean</code> reference value. * * @see #notEqual(boolean) * @since 1.0 */ public void setNotEqual(boolean reference) { setNotEqual(Boolean.valueOf(reference)); } /** * Set that the property value can't be equal to a specified * <code>byte</code> reference value. * * @see #notEqual(boolean) * @since 1.0 */ public void setNotEqual(byte reference) { setNotEqual(new Byte(reference)); } /** * Set that the property value can't be equal to a specified * <code>char</code> reference value. * * @see #notEqual(boolean) * @since 1.0 */ public void setNotEqual(char reference) { setNotEqual(new Character(reference)); } /** * Set that the property value can't be equal to a specified * <code>short</code> reference value. * * @see #notEqual(boolean) * @since 1.0 */ public void setNotEqual(short reference) { setNotEqual(new Short(reference)); } /** * Set that the property value can't be equal to a specified * <code>int</code> reference value. * * @see #notEqual(boolean) * @since 1.0 */ public void setNotEqual(int reference) { setNotEqual(new Integer(reference)); } /** * Set that the property value can't be equal to a specified * <code>long</code> reference value. * * @see #notEqual(boolean) * @since 1.0 */ public void setNotEqual(long reference) { setNotEqual(new Long(reference)); } /** * Set that the property value can't be equal to a specified * <code>float</code> reference value. * * @see #notEqual(boolean) * @since 1.0 */ public void setNotEqual(float reference) { setNotEqual(new Float(reference)); } /** * Set that the property value can't be equal to a specified * <code>double</code> reference value. * * @see #notEqual(boolean) * @since 1.0 */ public void setNotEqual(double reference) { setNotEqual(new Double(reference)); } /** * Set that the property value can't be equal to a specified * <code>Object</code> reference value. * * @see #notEqual(boolean) * @since 1.0 */ public void setNotEqual(Object reference) { if (null == reference) { mConstraints.remove(NOT_EQUAL); } else { setConstraint(NOT_EQUAL, reference); } } /** * Retrieves whether the property can't be equal to a specific * reference value. * * @return <code>true</code> when the value can't be equal; or * <p><code>false</code> when the value can be equal. * @see #notEqual(boolean) * @since 1.0 */ public boolean isNotEqual() { return mConstraints.containsKey(NOT_EQUAL); } /** * Retrieves the reference object to which the property value can't be * equal. * * @return the requested reference object instance; or * <p><code>null</code> when the property has no notEqual constraint. * @see #notEqual(boolean) * @since 1.0 */ public Object getNotEqual() { return mConstraints.get(NOT_EQUAL); } /** * Set whether the property value has to be unique. * <p>Note that this is only applicable to contexts where a collection * of the data is stored an that uniqueness can apply against the * other entries. In a singular context, uniqueness is always * guaranteed. * * @param unique <code>true</code> when the value has to be unique; or * <code>false</code> when it doesn't have to be. * @return this <code>ConstrainedProperty</code> * @see #isUnique() * @since 1.0 */ public T unique(boolean unique) { setUnique(unique); return (T)this; } /** * Set whether the property value has to be unique. * * @see #unique(boolean) * @since 1.0 */ public void setUnique(boolean unique) { setConstraint(UNIQUE, unique); } /** * Retrieves whether the property value has to be unique. * * @return <code>true</code> when the value has to be unique; or * <p><code>false</code> it doesn't have to be. * @see #unique(boolean) * @since 1.0 */ public boolean isUnique() { return Convert.toBoolean(mConstraints.get(UNIQUE), false); } /** * Set whether the property value is an identifier. * <p>Note that this is only applicable to contexts where a collection * of the data is stored an that identification can apply against the * other entries. In a singular context, identification is * meaningless. * * @param identifier <code>true</code> when the value is an * identifier; or <code>false</code> when it isn't. * @return this <code>ConstrainedProperty</code> * @see #isIdentifier() * @since 1.0 */ public T identifier(boolean identifier) { setIdentifier(identifier); return (T)this; } /** * Set whether the property value is an identifier. * * @see #identifier(boolean) * @since 1.0 */ public void setIdentifier(boolean identifier) { setConstraint(IDENTIFIER, identifier); } /** * Retrieves whether the property is an identifier. * * @return <code>true</code> when the property is an identifier; or * <p><code>false</code> it isn't. * @see #identifier(boolean) * @since 1.0 */ public boolean isIdentifier() { return Convert.toBoolean(mConstraints.get(IDENTIFIER), false); } public T editable(boolean editable) { setEditable(editable); return (T)this; } public void setEditable(boolean editable) { setConstraint(EDITABLE, editable); } public boolean isEditable() { return Convert.toBoolean(mConstraints.get(EDITABLE), true); } public T persistent(boolean persistent) { setPersistent(persistent); return (T)this; } public void setPersistent(boolean persistent) { if (hasMimeType() && persistent) { throw new IllegalArgumentException("Can't make a property persistent that has a content mime type assigned to it."); } setConstraint(PERSISTENT, persistent); } public boolean isPersistent() { return Convert.toBoolean(mConstraints.get(PERSISTENT), true); } public T saved(boolean saved) { setSaved(saved); return (T)this; } public void setSaved(boolean saved) { setConstraint(SAVED, saved); } public boolean isSaved() { return Convert.toBoolean(mConstraints.get(SAVED), true); } public T displayedRaw(boolean displayedRaw) { setDisplayedRaw(displayedRaw); return (T)this; } public void setDisplayedRaw(boolean displayedRaw) { if (hasMimeType() && !displayedRaw) { throw new IllegalArgumentException("Can't make a property not ùbeing displayed raw that has a content mime type assigned to it."); } setConstraint(DISPLAYED_RAW, displayedRaw); } public boolean isDisplayedRaw() { return Convert.toBoolean(mConstraints.get(DISPLAYED_RAW), false); } public boolean hasLimitedLength() { return mConstraints.containsKey(MIN_LENGTH) || mConstraints.containsKey(MAX_LENGTH); } public boolean hasMixLength() { return mConstraints.containsKey(MAX_LENGTH); } public boolean hasMaxLength() { return mConstraints.containsKey(MAX_LENGTH); } public T minLength(int minLength) { setMinLength(minLength); return (T)this; } public void setMinLength(int minLength) { if (minLength <= 0) { mConstraints.remove(MIN_LENGTH); } else { setConstraint(MIN_LENGTH, minLength); } } public int getMinLength() { return Convert.toInt(mConstraints.get(MIN_LENGTH), -1); } public T maxLength(int maxLength) { setMaxLength(maxLength); return (T)this; } public void setMaxLength(int maxLength) { if (maxLength < 0) { mConstraints.remove(MAX_LENGTH); } else { setConstraint(MAX_LENGTH, maxLength); } } public int getMaxLength() { return Convert.toInt(mConstraints.get(MAX_LENGTH), -1); } public boolean hasPrecision() { return mConstraints.containsKey(MAX_LENGTH); } public T precision(int precision) { setPrecision(precision); return (T)this; } public void setPrecision(int precision) { setMaxLength(precision); } public int getPrecision() { return getMaxLength(); } public boolean hasScale() { return mConstraints.containsKey(SCALE); } public T scale(int scale) { setScale(scale); return (T)this; } public void setScale(int scale) { if (scale < 0) { mConstraints.remove(SCALE); } else { setConstraint(SCALE, scale); } } public int getScale() { return Convert.toInt(mConstraints.get(SCALE), -1); } public T regexp(String regexp) { setRegexp(regexp); return (T)this; } public void setRegexp(String regexp) { if (null == regexp) { mConstraints.remove(REGEXP); } else { setConstraint(REGEXP, regexp); } } public String getRegexp() { return (String)mConstraints.get(REGEXP); } public boolean matchesRegexp() { return mConstraints.containsKey(REGEXP); } public T email(boolean email) { setEmail(email); return (T)this; } public void setEmail(boolean email) { setConstraint(EMAIL, email); } public boolean isEmail() { return Convert.toBoolean(mConstraints.get(EMAIL), false); } public T url(boolean url) { setUrl(url); return (T)this; } public void setUrl(boolean url) { setConstraint(URL, url); } public boolean isUrl() { return Convert.toBoolean(mConstraints.get(URL), false); } public T minDate(Date minDate) { setMinDate(minDate); return (T)this; } public void setMinDate(Date minDate) { if (null == minDate) { mConstraints.remove(MIN_DATE); } else { setConstraint(MIN_DATE, minDate); } } public Date getMinDate() { return (Date)mConstraints.get(MIN_DATE); } public T maxDate(Date maxDate) { setMaxDate(maxDate); return (T)this; } public void setMaxDate(Date maxDate) { if (null == maxDate) { mConstraints.remove(MAX_DATE); } else { setConstraint(MAX_DATE, maxDate); } } public Date getMaxDate() { return (Date)mConstraints.get(MAX_DATE); } public boolean isLimitedDate() { return mConstraints.containsKey(MIN_DATE) || mConstraints.containsKey(MAX_DATE); } public T inList(String... inList) { setInList(inList); return (T)this; } public void setInList(String... inList) { if (null == inList) { mConstraints.remove(IN_LIST); } else { setConstraint(IN_LIST, inList); } } public T inList(int... inList) { setInList(inList); return (T)this; } public void setInList(int... inList) { String[] list = null; if (inList != null) { list = new String[inList.length]; for (int i = 0; i < inList.length; i++) { list[i] = String.valueOf(inList[i]); } } setInList(list); } public T inList(byte... inList) { setInList(inList); return (T)this; } public void setInList(byte... inList) { String[] list = null; if (inList != null) { list = new String[inList.length]; for (int i = 0; i < inList.length; i++) { list[i] = String.valueOf(inList[i]); } } setInList(list); } public T inList(char... inList) { setInList(inList); return (T)this; } public void setInList(char... inList) { String[] list = null; if (inList != null) { list = new String[inList.length]; for (int i = 0; i < inList.length; i++) { list[i] = String.valueOf(inList[i]); } } setInList(list); } public T inList(short... inList) { setInList(inList); return (T)this; } public void setInList(short... inList) { String[] list = null; if (inList != null) { list = new String[inList.length]; for (int i = 0; i < inList.length; i++) { list[i] = String.valueOf(inList[i]); } } setInList(list); } public T inList(long... inList) { setInList(inList); return (T)this; } public void setInList(long... inList) { String[] list = null; if (inList != null) { list = new String[inList.length]; for (int i = 0; i < inList.length; i++) { list[i] = String.valueOf(inList[i]); } } setInList(list); } public T inList(float... inList) { setInList(inList); return (T)this; } public void setInList(float... inList) { String[] list = null; if (inList != null) { list = new String[inList.length]; for (int i = 0; i < inList.length; i++) { list[i] = String.valueOf(inList[i]); } } setInList(list); } public T inList(double... inList) { setInList(inList); return (T)this; } public void setInList(double... inList) { String[] list = null; if (inList != null) { list = new String[inList.length]; for (int i = 0; i < inList.length; i++) { list[i] = String.valueOf(inList[i]); } } setInList(list); } public T inList(Collection inList) { setInList(inList); return (T)this; } public void setInList(Collection inList) { String[] list = null; if (inList != null) { list = new String[inList.size()]; int i = 0; for (Object entry : inList) { list[i++] = String.valueOf(entry); } } setInList(list); } public String[] getInList() { return (String[])mConstraints.get(IN_LIST); } public boolean isInList() { return mConstraints.containsKey(IN_LIST) && ((String[])mConstraints.get(IN_LIST)).length > 0; } public T rangeBegin(byte value) { setRangeBegin(value); return (T)this; } public T rangeBegin(char value) { setRangeBegin(value); return (T)this; } public T rangeBegin(short value) { setRangeBegin(value); return (T)this; } public T rangeBegin(int value) { setRangeBegin(value); return (T)this; } public T rangeBegin(long value) { setRangeBegin(value); return (T)this; } public T rangeBegin(float value) { setRangeBegin(value); return (T)this; } public T rangeBegin(double value) { setRangeBegin(value); return (T)this; } public T rangeBegin(Comparable value) { setRangeBegin(value); return (T)this; } public void setRangeBegin(byte value) { setRangeBegin(new Byte(value)); } public void setRangeBegin(char value) { setRangeBegin(new Character(value)); } public void setRangeBegin(short value) { setRangeBegin(new Short(value)); } public void setRangeBegin(int value) { setRangeBegin(new Integer(value)); } public void setRangeBegin(long value) { setRangeBegin(new Long(value)); } public void setRangeBegin(float value) { setRangeBegin(new Float(value)); } public void setRangeBegin(double value) { setRangeBegin(new Double(value)); } public void setRangeBegin(Comparable rangeBegin) { if (null == rangeBegin) { mConstraints.remove(RANGE_BEGIN); } else { setConstraint(RANGE_BEGIN, rangeBegin); } } public Comparable getRangeBegin() { return (Comparable)mConstraints.get(RANGE_BEGIN); } public T rangeEnd(char value) { setRangeEnd(value); return (T)this; } public T rangeEnd(byte value) { setRangeEnd(value); return (T)this; } public T rangeEnd(double value) { setRangeEnd(value); return (T)this; } public T rangeEnd(float value) { setRangeEnd(value); return (T)this; } public T rangeEnd(int value) { setRangeEnd(value); return (T)this; } public T rangeEnd(long value) { setRangeEnd(value); return (T)this; } public T rangeEnd(short value) { setRangeEnd(value); return (T)this; } public T rangeEnd(Comparable value) { setRangeEnd(value); return (T)this; } public void setRangeEnd(byte value) { setRangeEnd(new Byte(value)); } public void setRangeEnd(char value) { setRangeEnd(new Character(value)); } public void setRangeEnd(short value) { setRangeEnd(new Short(value)); } public void setRangeEnd(int value) { setRangeEnd(new Integer(value)); } public void setRangeEnd(long value) { setRangeEnd(new Long(value)); } public void setRangeEnd(float value) { setRangeEnd(new Float(value)); } public void setRangeEnd(double value) { setRangeEnd(new Double(value)); } public void setRangeEnd(Comparable rangeEnd) { if (null == rangeEnd) { mConstraints.remove(RANGE_END); } else { setConstraint(RANGE_END, rangeEnd); } } public Comparable getRangeEnd() { return (Comparable)mConstraints.get(RANGE_END); } public boolean isRange() { return mConstraints.containsKey(RANGE_BEGIN) || mConstraints.containsKey(RANGE_END); } public T defaultValue(boolean value) { return defaultValue(Boolean.valueOf(value)); } public T defaultValue(char value) { return defaultValue(new Character(value)); } public T defaultValue(byte value) { return defaultValue(new Byte(value)); } public T defaultValue(short value) { return defaultValue(new Short(value)); } public T defaultValue(int value) { return defaultValue(new Integer(value)); } public T defaultValue(long value) { return defaultValue(new Long(value)); } public T defaultValue(float value) { return defaultValue(new Float(value)); } public T defaultValue(double value) { return defaultValue(new Double(value)); } public T defaultValue(Object value) { setDefaultValue(value); return (T)this; } public void setDefaultValue(Object value) { if (null == value) { mConstraints.remove(DEFAULT_VALUE); } else { setConstraint(DEFAULT_VALUE, value); } } public Object getDefaultValue() { return mConstraints.get(DEFAULT_VALUE); } public boolean hasDefaultValue() { return mConstraints.containsKey(DEFAULT_VALUE); } public T sameAs(String reference) { setSameAs(reference); return (T)this; } public void setSameAs(String reference) { if (null == reference) { mConstraints.remove(SAME_AS); } else { setConstraint(SAME_AS, reference); } } public String getSameAs() { return (String)mConstraints.get(SAME_AS); } public boolean isSameAs() { return mConstraints.containsKey(SAME_AS); } public void setManyToOne() { setConstraint(MANY_TO_ONE, new ManyToOne()); } public void setManyToOne(Class klass) { setConstraint(MANY_TO_ONE, new ManyToOne(klass)); } public void setManyToOne(Class klass, String columnReference) { setConstraint(MANY_TO_ONE, new ManyToOne(klass, columnReference, null, null)); } public void setManyToOne(String table, String columnReference) { setConstraint(MANY_TO_ONE, new ManyToOne(table, columnReference, null, null)); } public void setManyToOne(Class klass, String columnReference, CreateTable.ViolationAction onUpdate, CreateTable.ViolationAction onDelete) { setConstraint(MANY_TO_ONE, new ManyToOne(klass, columnReference, onUpdate, onDelete)); } public void setManyToOne(String table, String columnReference, CreateTable.ViolationAction onUpdate, CreateTable.ViolationAction onDelete) { setConstraint(MANY_TO_ONE, new ManyToOne(table, columnReference, onUpdate, onDelete)); } public ManyToOne getManyToOne() { return (ManyToOne)mConstraints.get(MANY_TO_ONE); } public T manyToOne() { setManyToOne(); return (T)this; } public T manyToOne(Class klass) { setManyToOne(klass); return (T)this; } public T manyToOne(Class klass, String columnReference) { setManyToOne(klass, columnReference); return (T)this; } public T manyToOne(String table, String columnReference) { setManyToOne(table, columnReference); return (T)this; } public T manyToOne(Class klass, String columnReference, CreateTable.ViolationAction onUpdate, CreateTable.ViolationAction onDelete) { setManyToOne(klass, columnReference, onUpdate, onDelete); return (T)this; } public T manyToOne(String table, String columnReference, CreateTable.ViolationAction onUpdate, CreateTable.ViolationAction onDelete) { setManyToOne(table, columnReference, onUpdate, onDelete); return (T)this; } public boolean hasManyToOne() { return mConstraints.containsKey(MANY_TO_ONE); } public void setManyToOneAssociation() { setConstraint(MANY_TO_ONE_ASSOCIATION, new ManyToOneAssociation()); } public void setManyToOneAssociation(String property) { setConstraint(MANY_TO_ONE_ASSOCIATION, new ManyToOneAssociation(property)); } public void setManyToOneAssociation(Class klass, String property) { setConstraint(MANY_TO_ONE_ASSOCIATION, new ManyToOneAssociation(klass, property)); } public ManyToOneAssociation getManyToOneAssociation() { return (ManyToOneAssociation)mConstraints.get(MANY_TO_ONE_ASSOCIATION); } public T manyToOneAssociation() { setManyToOneAssociation(); return (T)this; } public T manyToOneAssociation(String property) { setManyToOneAssociation(property); return (T)this; } public T manyToOneAssociation(Class klass, String property) { setManyToOneAssociation(klass, property); return (T)this; } public boolean hasManyToOneAssociation() { return mConstraints.containsKey(MANY_TO_ONE_ASSOCIATION); } public void setManyToMany() { setConstraint(MANY_TO_MANY, new ManyToMany()); } public void setManyToMany(Class klass) { setConstraint(MANY_TO_MANY, new ManyToMany(klass)); } public void setManyToMany(CreateTable.ViolationAction onUpdate, CreateTable.ViolationAction onDelete) { setConstraint(MANY_TO_MANY, new ManyToMany(onUpdate, onDelete)); } public void setManyToMany(Class klass, CreateTable.ViolationAction onUpdate, CreateTable.ViolationAction onDelete) { setConstraint(MANY_TO_MANY, new ManyToMany(klass, onUpdate, onDelete)); } public ManyToMany getManyToMany() { return (ManyToMany)mConstraints.get(MANY_TO_MANY); } public T manyToMany() { setManyToMany(); return (T)this; } public T manyToMany(Class klass) { setManyToMany(klass); return (T)this; } public T manyToMany(CreateTable.ViolationAction onUpdate, CreateTable.ViolationAction onDelete) { setManyToMany(onUpdate, onDelete); return (T)this; } public T manyToMany(Class klass, CreateTable.ViolationAction onUpdate, CreateTable.ViolationAction onDelete) { setManyToMany(klass, onUpdate, onDelete); return (T)this; } public boolean hasManyToMany() { return mConstraints.containsKey(MANY_TO_MANY); } public void setManyToManyAssociation() { setConstraint(MANY_TO_MANY_ASSOCIATION, new ManyToManyAssociation()); } public void setManyToManyAssociation(String property) { setConstraint(MANY_TO_MANY_ASSOCIATION, new ManyToManyAssociation(property)); } public void setManyToManyAssociation(Class klass, String property) { setConstraint(MANY_TO_MANY_ASSOCIATION, new ManyToManyAssociation(klass, property)); } public ManyToManyAssociation getManyToManyAssociation() { return (ManyToManyAssociation)mConstraints.get(MANY_TO_MANY_ASSOCIATION); } public T manyToManyAssociation() { setManyToManyAssociation(); return (T)this; } public T manyToManyAssociation(String property) { setManyToManyAssociation(property); return (T)this; } public T manyToManyAssociation(Class klass, String property) { setManyToManyAssociation(klass, property); return (T)this; } public boolean hasManyToManyAssociation() { return mConstraints.containsKey(MANY_TO_MANY_ASSOCIATION); } public T format(Format format) { setFormat(format); return (T)this; } public void setFormat(Format format) { if (null == format) { mConstraints.remove(FORMAT); } else { setConstraint(FORMAT, format); } } public Format getFormat() { return (Format)mConstraints.get(FORMAT); } public boolean isFormatted() { return mConstraints.containsKey(FORMAT); } public T file(boolean file) { setFile(file); return (T)this; } public void setFile(boolean file) { setConstraint(FILE, file); } public boolean isFile() { return Convert.toBoolean(mConstraints.get(FILE), false); } public T sparse(boolean sparse) { setSparse(sparse); return (T)this; } public void setSparse(boolean sparse) { setConstraint(SPARSE, sparse); } public boolean isSparse() { return Convert.toBoolean(mConstraints.get(SPARSE), false); } /** * Sets whether the property should be included in data lists. * <p>This is not actually used by the CMF itself, but is very useful when * integrating with automatic user interface generation libraries. * * @param listed <code>true</code> if the property should be listed; or * <p><code>false</code> if it shouldn't * @return the current <code>ConstrainedProperty</code> instance * @see #setListed(boolean) * @see #isListed() * @since 1.0 */ public T listed(boolean listed) { setListed(listed); return (T)this; } /** * Sets whether the property should be included in data lists. * * @param listed <code>true</code> if the property should be listed; or * <p><code>false</code> if it shouldn't * @see #listed(boolean) * @see #isListed() * @since 1.0 */ public void setListed(boolean listed) { setConstraint(LISTED, listed); } /** * Retrieves whether the property should be included in data lists. * * @return <code>true</code> if the property should be listed; or * <p><code>false</code> if it shouldn't * @see #listed(boolean) * @see #setListed(boolean) * @since 1.0 */ public boolean isListed() { return Convert.toBoolean(mConstraints.get(LISTED), false); } /** * Sets the position in which the property should be displayed. * <p>This is not actually used by the CMF itself, but is very useful when * integrating with automatic user interface generation libraries. * * @param position an integer value with the position; or * <p><code>-1</code> if the property shouldn't be positioned * @return the current <code>ConstrainedProperty</code> instance * @see #setPosition(int) * @see #hasPosition() * @see #getPosition() * @since 1.0 */ public T position(int position) { setPosition(position); return (T)this; } /** * Sets the position in which the property should be displayed. * * @param position an integer value with the position; or * <p><code>-1</code> if the property shouldn't be positioned * @see #position(int) * @see #hasPosition() * @see #getPosition() * @since 1.0 */ public void setPosition(int position) { if (position < 0) { mConstraints.remove(POSITION); } else { setConstraint(POSITION, position); } } /** * Indicates whether the position of the property is set. * * @return <code>true</code> if the property has a position; or * <p><code>false</code> if it hasn't * @see #position(int) * @see #setPosition(int) * @see #getPosition() * @since 1.0 */ public boolean hasPosition() { return mConstraints.containsKey(POSITION); } /** * Retrieves the position in which the property should be displayed. * * @return an integer value with the position; or * <p><code>-1</code> if the property shouldn't be positioned * @see #position(int) * @see #setPosition(int) * @see #hasPosition() * @since 1.0 */ public int getPosition() { return Convert.toInt(mConstraints.get(POSITION), -1); } /** * Sets the mime type of the property. * <p>Setting this constraint will make the {@link * com.uwyn.rife.cmf.dam.ContentQueryManager ContentQueryManager} * automatically store the data in this property in the content management * back-end. This column will not be stored in a regular database table. * All this is handled transparently and automatically. * * @param mimeType the <code>MimeType</code> of the property * @return the current <code>ConstrainedProperty</code> instance * @see #setMimeType(MimeType) * @see #hasMimeType() * @see #getMimeType() * @since 1.0 */ public T mimeType(MimeType mimeType) { setMimeType(mimeType); return (T)this; } /** * Sets the mime type of the property. * * @param mimeType the <code>MimeType</code> of the property * @see #mimeType(MimeType) * @see #hasMimeType() * @see #getMimeType() * @since 1.0 */ public void setMimeType(MimeType mimeType) { if (null == mimeType) { mConstraints.remove(MIMETYPE); } else { setConstraint(MIMETYPE, mimeType); persistent(false); displayedRaw(true); } } /** * Indicates whether the property has a mime type. * * @return <code>true</code> if the property has a mime type; or * <p><code>false</code> if it hasn't * @see #mimeType(MimeType) * @see #setMimeType(MimeType) * @see #getMimeType() * @since 1.0 */ public boolean hasMimeType() { return mConstraints.containsKey(MIMETYPE); } /** * Retrieves the mime type of the property. * * @return the mime type of the property; or * <p><code>null</code> if the property has no mime type * @see #mimeType(MimeType) * @see #setMimeType(MimeType) * @see #hasMimeType() * @since 1.0 */ public MimeType getMimeType() { return (MimeType)mConstraints.get(MIMETYPE); } /** * Sets whether the content data of this property should be retrieved * automatically from the back-end. * <p>This is only useful when the property also has a mime type * constraint. * <p>It's not recommended to enable this constraint for large data since * everything will be stored in memory, only use this for text snippets or * something relatively small. * * @param autoRetrieved <code>true</code> if the data should be * automatically retrieved; or * <p><code>false</code> otherwise * @return the current <code>ConstrainedProperty</code> instance * @see #mimeType(MimeType) * @see #setAutoRetrieved(boolean) * @see #isAutoRetrieved() * @since 1.0 */ public T autoRetrieved(boolean autoRetrieved) { setAutoRetrieved(autoRetrieved); return (T)this; } /** * Sets whether the content data of this property should be retrieved * automatically from the back-end. * * @param autoRetrieved <code>true</code> if the data should be * automatically retrieved; or * <p><code>false</code> otherwise * @see #autoRetrieved(boolean) * @see #isAutoRetrieved() * @since 1.0 */ public void setAutoRetrieved(boolean autoRetrieved) { setConstraint(AUTO_RETRIEVED, autoRetrieved); } /** * Indicates whether the content data of this property is automatically * retrieved from the back-end. * * @return <code>true</code> if the data should be automatically * retrieved; or * <p><code>false</code> otherwise * @see #autoRetrieved(boolean) * @see #setAutoRetrieved(boolean) * @since 1.0 */ public boolean isAutoRetrieved() { return Convert.toBoolean(mConstraints.get(AUTO_RETRIEVED), false); } /** * Sets whether the content data of this property is a fragment. * <p>This is only useful when the property also has a mime type * constraint. A fragment means that it's not a complete document or a * file, but rather a small part that is intended to be used within a * larger document. For example a HTML snippet. This information is for * example important when validating the data. * * @param fragment <code>true</code> if the content is a fragment; or * <p><code>false</code> otherwise * @return the current <code>ConstrainedProperty</code> instance * @see #mimeType(MimeType) * @see #setFragment(boolean) * @see #isFragment() * @since 1.0 */ public T fragment(boolean fragment) { setFragment(fragment); return (T)this; } /** * Sets whether the content data of this property is a fragment. * * @param fragment <code>true</code> if the content is a fragment; or * <p><code>false</code> otherwise * @see #fragment(boolean) * @see #isFragment() * @since 1.0 */ public void setFragment(boolean fragment) { setConstraint(FRAGMENT, fragment); } /** * Indicates whether the content data of this property is a fragment. * * @return <code>true</code> if the content is a fragment; or * <p><code>false</code> otherwise * @see #fragment(boolean) * @see #setFragment(boolean) * @since 1.0 */ public boolean isFragment() { return Convert.toBoolean(mConstraints.get(FRAGMENT), false); } /** * Sets the name of the content data of this property. * <p>This is only useful when the property also has a mime type * constraint. * * @param name the name * @return the current <code>ConstrainedProperty</code> instance * @see #mimeType(MimeType) * @see #setName(String) * @see #getName() * @see #hasName() * @since 1.0 */ public T name(String name) { setName(name); return (T)this; } /** * Sets the name of the content data of this property. * * @param name the name * @see #name(String) * @see #getName() * @see #hasName() * @since 1.0 */ public void setName(String name) { if (null == name) { mConstraints.remove(NAME); } else { setConstraint(NAME, name); } } /** * Retrieves the name of this property. * * @return <code>null</code> if the content data has no name; or * <p>the name of the content * @see #name(String) * @see #setName(String) * @see #hasName() * @since 1.0 */ public String getName() { return (String)mConstraints.get(NAME); } /** * Indicates whether this property has a name. * * @return <code>true</code> if the property has a name; or * <p><code>false</code> otherwise * @see #name(String) * @see #setName(String) * @see #getName() * @since 1.0 */ public boolean hasName() { return mConstraints.containsKey(NAME); } /** * Sets the repository where the content data of this property will be * stored. * <p>This is only useful when the property also has a mime type * constraint. * * @param repository the repository * @return the current <code>CmrProperty</code> instance * @see #mimeType(MimeType) * @see #setRepository(String) * @see #getRepository() * @see #hasRepository() * @since 1.0 */ public T repository(String repository) { setRepository(repository); return (T)this; } /** * Sets the repository where the content data of this property will be * stored. * * @param repository the repository * @see #repository(String) * @see #getRepository() * @see #hasRepository() * @since 1.0 */ public void setRepository(String repository) { if (null == repository) { mConstraints.remove(REPOSITORY); } else { setConstraint(REPOSITORY, repository); } } /** * Retrieves the repository where the content data of this property will * be stored. * * @return <code>null</code> if no repository has been specified; or * <p>the name of the repository * @see #repository(String) * @see #setRepository(String) * @see #hasRepository() * @since 1.0 */ public String getRepository() { return (String)mConstraints.get(REPOSITORY); } /** * Indicates whether this property will be stored in another repository * than the default repository. * * @return <code>true</code> if the property will be stored in another * repository; or * <p><code>false</code> otherwise * @see #repository(String) * @see #setRepository(String) * @see #getRepository() * @since 1.0 */ public boolean hasRepository() { return mConstraints.containsKey(REPOSITORY); } /** * Sets whether this property has to be used as an ordinal. * <p>The value of this property will be handled in the back-end by an * {@link com.uwyn.rife.cmf.dam.OrdinalManager OrdinalManager}. It will * also enable the {@link * com.uwyn.rife.cmf.dam.ContentQueryManager#move(Constrained, String, com.uwyn.rife.cmf.dam.OrdinalManager.Direction) * move}, {@link * com.uwyn.rife.cmf.dam.ContentQueryManager#up(Constrained, String) up} * and {@link * com.uwyn.rife.cmf.dam.ContentQueryManager#down(Constrained, String) * down} methods in the {@link com.uwyn.rife.cmf.dam.ContentQueryManager * ContentQueryManager} to easily reorder data rows in the back-end. * * @param ordinal <code>true</code> if this property is an ordinal; or * <p><code>false</code> otherwise * @return the current <code>ConstrainedProperty</code> instance * @see #ordinal(boolean, String) * @see #setOrdinal(boolean) * @see #setOrdinal(boolean, String) * @see #isOrdinal() * @since 1.0 */ public T ordinal(boolean ordinal) { setOrdinal(ordinal); return (T)this; } /** * Sets whether this property has to be used as an ordinal with a * restricting column. * * @param ordinal <code>true</code> if this property is an ordinal; or * <p><code>false</code> otherwise * @param restriction the name of the restricting column * @return the current <code>ConstrainedProperty</code> instance * @see #ordinal(boolean) * @see #setOrdinal(boolean) * @see #setOrdinal(boolean, String) * @see #isOrdinal() * @see #hasOrdinalRestriction() * @see #getOrdinalRestriction() * @since 1.0 */ public T ordinal(boolean ordinal, String restriction) { setOrdinal(ordinal, restriction); return (T)this; } /** * Sets whether this property has to be used as an ordinal. * * @param ordinal <code>true</code> if this property is an ordinal; or * <p><code>false</code> otherwise * @see #ordinal(boolean) * @see #ordinal(boolean, String) * @see #setOrdinal(boolean, String) * @see #isOrdinal() * @since 1.0 */ public void setOrdinal(boolean ordinal) { setConstraint(ORDINAL, ordinal); mConstraints.remove(ORDINAL_RESTRICTION); } /** * Sets whether this property has to be used as an ordinal with a * restricting column. * * @param ordinal <code>true</code> if this property is an ordinal; or * <p><code>false</code> otherwise * @param restriction the name of the restricting column * @see #ordinal(boolean) * @see #ordinal(boolean, String) * @see #setOrdinal(boolean) * @see #isOrdinal() * @see #hasOrdinalRestriction() * @see #getOrdinalRestriction() * @since 1.0 */ public void setOrdinal(boolean ordinal, String restriction) { setConstraint(ORDINAL, ordinal); if (ordinal) { if (null == restriction) { mConstraints.remove(ORDINAL_RESTRICTION); } else { setConstraint(ORDINAL_RESTRICTION, restriction); } } else { mConstraints.remove(ORDINAL_RESTRICTION); } } /** * Indicates whether this property has to be used as an ordinal. * * @return <code>true</code> if this property is an ordinal; or * <p><code>false</code> otherwise * @see #ordinal(boolean) * @see #ordinal(boolean, String) * @see #setOrdinal(boolean) * @see #setOrdinal(boolean, String) * @since 1.0 */ public boolean isOrdinal() { return Convert.toBoolean(mConstraints.get(ORDINAL), false); } /** * Indicates whether this property has an ordinal restricting column. * * @return <code>true</code> if this property has an ordinal restricting * column; or * <p><code>false</code> otherwise * @see #ordinal(boolean, String) * @see #setOrdinal(boolean, String) * @see #getOrdinalRestriction() * @since 1.0 */ public boolean hasOrdinalRestriction() { return mConstraints.containsKey(ORDINAL_RESTRICTION); } /** * Retrieves the ordinal restriction of this property. * * @return the name of the ordinal restricting column; or * <p><code>null</code> if no ordinal restricting column has been defined * @see #ordinal(boolean, String) * @see #setOrdinal(boolean, String) * @see #hasOrdinalRestriction() * @since 1.0 */ public String getOrdinalRestriction() { return (String)mConstraints.get(ORDINAL_RESTRICTION); } /** * Sets a named content attribute for this property that will be converted * internally to a <code>String</code> value. * * @param name the name of the attribute * @param value the value of the attribute * @return the current <code>Content</code> instance * @see #contentAttribute(String, String) * @see #getContentAttributes() * @since 1.0 */ public T contentAttribute(String name, boolean value) { return contentAttribute(name, String.valueOf(value)); } /** * Sets a named content attribute for this property that will be converted * internally to a <code>String</code> value. * * @param name the name of the attribute * @param value the value of the attribute * @return the current <code>Content</code> instance * @see #contentAttribute(String, String) * @see #getContentAttributes() * @since 1.0 */ public T contentAttribute(String name, char value) { return contentAttribute(name, String.valueOf(value)); } /** * Sets a named content attribute for this property that will be converted * internally to a <code>String</code> value. * * @param name the name of the attribute * @param value the value of the attribute * @return the current <code>Content</code> instance * @see #contentAttribute(String, String) * @see #getContentAttributes() * @since 1.0 */ public T contentAttribute(String name, byte value) { return contentAttribute(name, String.valueOf(value)); } /** * Sets a named content attribute for this property that will be converted * internally to a <code>String</code> value. * * @param name the name of the attribute * @param value the value of the attribute * @return the current <code>Content</code> instance * @see #contentAttribute(String, String) * @see #getContentAttributes() * @since 1.0 */ public T contentAttribute(String name, short value) { return contentAttribute(name, String.valueOf(value)); } /** * Sets a named content attribute for this property that will be converted * internally to a <code>String</code> value. * * @param name the name of the attribute * @param value the value of the attribute * @return the current <code>Content</code> instance * @see #contentAttribute(String, String) * @see #getContentAttributes() * @since 1.0 */ public T contentAttribute(String name, int value) { return contentAttribute(name, String.valueOf(value)); } /** * Sets a named content attribute for this property that will be converted * internally to a <code>String</code> value. * * @param name the name of the attribute * @param value the value of the attribute * @return the current <code>Content</code> instance * @see #contentAttribute(String, String) * @see #getContentAttributes() * @since 1.0 */ public T contentAttribute(String name, long value) { return contentAttribute(name, String.valueOf(value)); } /** * Sets a named content attribute for this property that will be converted * internally to a <code>String</code> value. * * @param name the name of the attribute * @param value the value of the attribute * @return the current <code>Content</code> instance * @see #contentAttribute(String, String) * @see #getContentAttributes() * @since 1.0 */ public T contentAttribute(String name, float value) { return contentAttribute(name, String.valueOf(value)); } /** * Sets a named content attribute for this property that will be converted * internally to a <code>String</code> value. * * @param name the name of the attribute * @param value the value of the attribute * @return the current <code>Content</code> instance * @see #contentAttribute(String, String) * @see #getContentAttributes() * @since 1.0 */ public T contentAttribute(String name, double value) { return contentAttribute(name, String.valueOf(value)); } /** * Sets a named content attribute for this property. * <p>This is only useful when the property also has a mime type * constraint. * <p>A content attribute provides additional meta data about how you want * to store the content data after loading, this can for example be image * dimensions. * * @param name the name of the attribute * @param value the value of the attribute * @return the current <code>Content</code> instance * @see #mimeType(MimeType) * @see #getContentAttributes() * @since 1.0 */ @SuppressWarnings("unchecked") public T contentAttribute(String name, String value) { HashMap<String, String> content_attributes = (HashMap<String, String>)mConstraints.get(CONTENT_ATTRIBUTES); if (null == content_attributes) { content_attributes = new HashMap<String, String>(); setConstraint(CONTENT_ATTRIBUTES, content_attributes); } content_attributes.put(name, value); return (T)this; } /** * Retrieves the map of named content attributes for this property. * * @return the map of named content attributes; or * <p><code>null</code> if no attributes are present * @see #contentAttribute(String, String) * @since 1.0 */ @SuppressWarnings("unchecked") public Map<String, String> getContentAttributes() { return (HashMap<String, String>)mConstraints.get(CONTENT_ATTRIBUTES); } /** * Sets a content transformer for this property. * <p>This is only useful when the property also has a mime type * constraint. * * @param transformer the content transformer * @return the current <code>Content</code> instance * @see #mimeType(MimeType) * @see #setTransformer(ContentTransformer) * @see #hasTransformer() * @see #getTransformer() * @since 1.0 */ public T transformer(ContentTransformer transformer) { setTransformer(transformer); return (T)this; } /** * Sets a content transformer for this property. * * @param transformer the content transformer * @see #mimeType(MimeType) * @see #transformer(ContentTransformer) * @see #hasTransformer() * @see #getTransformer() * @since 1.0 */ public void setTransformer(ContentTransformer transformer) { if (null == transformer) { mConstraints.remove(TRANSFORMER); } else { setConstraint(TRANSFORMER, transformer); } } /** * Indicates whether this property has a content transformer. * * @return <code>true</code> if this property has a content transformer; * or * <p><code>false</code> otherwise * @see #transformer(ContentTransformer) * @see #setTransformer(ContentTransformer) * @see #getTransformer() * @since 1.0 */ public boolean hasTransformer() { return mConstraints.containsKey(TRANSFORMER); } /** * Retrieves the content transformer of this property. * * @return the requested content transformer; or * <p><code>null</code> if no content transformer has been defined * @see #transformer(ContentTransformer) * @see #setTransformer(ContentTransformer) * @see #hasTransformer() * @since 1.0 */ public ContentTransformer getTransformer() { return (ContentTransformer)mConstraints.get(TRANSFORMER); } /** * Sets the cached loaded data. * <p>This is used internally and should never be used explicitly by a * developer, see {@link * com.uwyn.rife.cmf.Content#cachedLoadedData(Object) * Content.cachedLoadedData(Object)} for more information. * * @param data the loaded data * @see #getCachedLoadedData() * @since 1.0 */ public void setCachedLoadedData(Object data) { if (null == data) { synchronized (mConstraints) { mConstraints.remove(CACHED_LOADED_DATA); } } else { setConstraint(CACHED_LOADED_DATA, data); } } /** * Retrieves the cached loaded content data. * * @return the cached loaded content data; or * <p><code>null</code> if no loaded content data has been cached * @see #setCachedLoadedData(Object) * @since 1.0 */ public Object getCachedLoadedData() { return mConstraints.get(CACHED_LOADED_DATA); } /** * Sets the data of a particular constraint in a generic * fashion. * <p>Note that it's not recommended to use this to set any of * the standard constraints since none of the additional logic * and checks are executed. * * @see #constraint * @see #getConstraint * @see #getConstraints * @since 1.4 */ public void setConstraint(String name, Object constraintData) { synchronized (mConstraints) { mConstraints.put(name, constraintData); } fireConstraintSet(name, constraintData); } /** * Sets the data of a particular constraint in a generic * fashion. * <p> Note that it's not recommended to use this to set any of * the standard constraints since none of the additional logic * and checks are executed. * * @return the current <code>Content</code> instance * @see #setConstraint * @see #getConstraint * @see #getConstraints * @since 1.4 */ public T constraint(String name, Object constraintData) { setConstraint(name, constraintData); return (T)this; } /** * Retrieves the value of a particular constraint in a * generic fashion * * @return the data of a particular constraint; or * <p><code>null</code> if nothing has been registered for * that constraint * @see #setConstraint * @see #constraint * @see #getConstraints * @since 1.4 */ public Object getConstraint(String name) { return mConstraints.get(name); } /** * Retrieves the map of all the constraints. * * @return the map with all the registered constraints * @see #setConstraint * @see #constraint * @see #getConstraint * @since 1.4 */ public Map<String, Object> getConstraints() { return mConstraints; } public ConstrainedProperty clone() { ConstrainedProperty new_instance = null; try { new_instance = (ConstrainedProperty)super.clone(); new_instance.mConstraints = new HashMap<String, Object>(mConstraints); if (mListeners != null) { new_instance.mListeners = new ArrayList<ConstrainedPropertyListener>(mListeners); } } catch (CloneNotSupportedException e) { new_instance = null; } return new_instance; } public class ManyToOne implements Cloneable { private String mColumn = null; private String mTable = null; private String mDerivedTable = null; private Class mClass = null; private CreateTable.ViolationAction mOnUpdate = null; private CreateTable.ViolationAction mOnDelete = null; public ManyToOne() { this((Class)null, null, null, null); } public ManyToOne(Class klass) { this(klass, null, null, null); } public ManyToOne(String table, String column, CreateTable.ViolationAction onUpdate, CreateTable.ViolationAction onDelete) { mColumn = column; mTable = table; mOnUpdate = onUpdate; mOnDelete = onDelete; } public ManyToOne(Class klass, String column, CreateTable.ViolationAction onUpdate, CreateTable.ViolationAction onDelete) { this((String)null, column, onUpdate, onDelete); mClass = klass; } public String getDerivedTable() { if (null == mDerivedTable) { if (mTable != null) { mDerivedTable = mTable; } if (mClass != null) { mDerivedTable = ClassUtils.shortenClassName(mClass); } } return mDerivedTable; } public void setColumn(String column) { mColumn = column; } public String getColumn() { return mColumn; } public void setTable(String table) { mDerivedTable = null; mTable = table; } public String getTable() { return mTable; } public void setAssociatedClass(Class klass) { mDerivedTable = null; mClass = klass; } public Class getAssociatedClass() { return mClass; } public void setOnUpdate(CreateTable.ViolationAction onUpdate) { mOnUpdate = onUpdate; } public CreateTable.ViolationAction getOnUpdate() { return mOnUpdate; } public void setOnDelete(CreateTable.ViolationAction onDelete) { mOnDelete = onDelete; } public CreateTable.ViolationAction getOnDelete() { return mOnDelete; } public ManyToOne clone() { ManyToOne new_instance = null; try { new_instance = (ManyToOne)super.clone(); } catch (CloneNotSupportedException e) { new_instance = null; } return new_instance; } } public class ManyToOneAssociation implements Cloneable { private Class mClass = null; private String mProperty = null; public ManyToOneAssociation() { } public ManyToOneAssociation(String property) { mProperty = property; } public ManyToOneAssociation(Class klass, String property) { this(property); mClass = klass; } public void setMainClass(Class klass) { mClass = klass; } public Class getMainClass() { return mClass; } public void setMainProperty(String property) { mProperty = property; } public String getMainProperty() { return mProperty; } public ManyToOneAssociation clone() { ManyToOneAssociation new_instance = null; try { new_instance = (ManyToOneAssociation)super.clone(); } catch (CloneNotSupportedException e) { new_instance = null; } return new_instance; } } public class ManyToMany implements Cloneable { private Class mClass = null; private CreateTable.ViolationAction mOnUpdate = null; private CreateTable.ViolationAction mOnDelete = null; public ManyToMany() { this(null, null, null); } public ManyToMany(Class klass) { this(klass, null, null); } public ManyToMany(CreateTable.ViolationAction onUpdate, CreateTable.ViolationAction onDelete) { this(null, onUpdate, onDelete); mOnUpdate = onUpdate; mOnDelete = onDelete; } public ManyToMany(Class klass, CreateTable.ViolationAction onUpdate, CreateTable.ViolationAction onDelete) { mOnUpdate = onUpdate; mOnDelete = onDelete; mClass = klass; } public void setAssociatedClass(Class klass) { mClass = klass; } public Class getAssociatedClass() { return mClass; } public void setOnUpdate(CreateTable.ViolationAction onUpdate) { mOnUpdate = onUpdate; } public CreateTable.ViolationAction getOnUpdate() { return mOnUpdate; } public void setOnDelete(CreateTable.ViolationAction onDelete) { mOnDelete = onDelete; } public CreateTable.ViolationAction getOnDelete() { return mOnDelete; } public ManyToMany clone() { ManyToMany new_instance = null; try { new_instance = (ManyToMany)super.clone(); } catch (CloneNotSupportedException e) { new_instance = null; } return new_instance; } } public class ManyToManyAssociation implements Cloneable { private Class mClass = null; private String mProperty = null; public ManyToManyAssociation() { } public ManyToManyAssociation(String property) { mProperty = property; } public ManyToManyAssociation(Class klass, String property) { this(property); mClass = klass; } public void setAssociatedClass(Class klass) { mClass = klass; } public Class getAssociatedClass() { return mClass; } public void setAssociatedProperty(String property) { mProperty = property; } public String getAssociatedProperty() { return mProperty; } public ManyToManyAssociation clone() { ManyToManyAssociation new_instance = null; try { new_instance = (ManyToManyAssociation)super.clone(); } catch (CloneNotSupportedException e) { new_instance = null; } return new_instance; } } }