package com.eas.client.metadata;
import com.eas.script.AlreadyPublishedException;
import com.eas.script.HasPublished;
import com.eas.script.NoPublisherException;
import com.eas.script.ScriptFunction;
import com.eas.script.Scripts;
import com.eas.util.IdGenerator;
import java.beans.PropertyChangeSupport;
import java.util.Date;
import jdk.nashorn.api.scripting.JSObject;
/**
* This class is table field representation. It holds information about field
* name, description, typeInfo, size and information about primary and foreign
* keys. If <code>isPk()</code> returns true, than this field is the primary key
* in corresponding table. If <code>getFk()</code> returns reference to a
* <code>PrimaryKeySpec</code>, than it is a foreign key in corresponding table,
* and it references to returning <code>PrimaryKeySpec</code>.
*
* @author mg
*/
public class Field implements HasPublished {
public static final String NAME_PROPERTY = "name";
public static final String DESCRIPTION_PROPERTY = "description";
public static final String ORIGINAL_NAME_PROPERTY = "originalName";
public static final String TABLE_NAME_PROPERTY = "tableName";
public static final String TYPE_PROPERTY = "type";
public static final String READONLY_PROPERTY = "readonly";
public static final String NULLABLE_PROPERTY = "nullable";
public static final String PK_PROPERTY = "pk";
public static final String FK_PROPERTY = "fk";
protected String name = "";
// In queries, such as select t1.f1 as f11, t2.f1 as f21 to preserve output fields' names unique,
// but be able to generate right update sql clauses for multiple tables.
protected String originalName = "";
protected String tableName;
protected String description;
protected String type;// Null value will be used as "unknown" type
protected boolean readonly;
protected boolean nullable = true;
protected boolean pk;
protected ForeignKeySpec fk;
protected PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
protected JSObject published;
/**
* The default constructor.
*/
public Field() {
super();
}
/**
* Constructor with name.
*
* @param aName Name of the created field.
*/
public Field(String aName) {
this();
name = aName;
}
/**
* Constructor with name and description.
*
* @param aName Name of the created field.
* @param aDescription Description of the created field.
*/
public Field(String aName, String aDescription) {
this(aName);
description = aDescription;
}
/**
* Constructor with name, description and typeInfo.
*
* @param aName Name of the created field.
* @param aDescription Description of the created field.
* @param aType Type name of the created field.
*/
public Field(String aName, String aDescription, String aType) {
this(aName, aDescription);
type = aType;
}
/**
* Copy constructor of <code>Field</code> class.
*
* @param aSourceField Source of created field.
*/
public Field(Field aSourceField) {
super();
assignFrom(aSourceField);
}
private static final String ORIGINAL_NAME_JS_DOC = ""
+ "/**\n"
+ " * The original name of the field.\n"
+ " * In queries, such as select t1.f1 as f11, t2.f1 as f21 to preserve output fields' names unique,\n"
+ " * but be able to generate right update sql clauses for multiple tables.\n"
+ " */";
@ScriptFunction(jsDoc = ORIGINAL_NAME_JS_DOC)
public String getOriginalName() {
return originalName;
}
@ScriptFunction
public void setOriginalName(String aValue) {
String oldValue = originalName;
originalName = aValue;
changeSupport.firePropertyChange(ORIGINAL_NAME_PROPERTY, oldValue, originalName);
}
private static final String TABLE_NAME_JS_DOC = ""
+ "/**\n"
+ " * This field table's name.\n"
+ " */";
/**
* Returns the field's table name.
*
* @return The field's table name.
*/
@ScriptFunction(jsDoc = TABLE_NAME_JS_DOC)
public String getTableName() {
return tableName;
}
/**
* Sets table name.
*
* @param aValue The table name to be set.
*/
@ScriptFunction
public void setTableName(String aValue) {
String oldValue = tableName;
tableName = aValue;
changeSupport.firePropertyChange(TABLE_NAME_PROPERTY, oldValue, aValue);
}
public PropertyChangeSupport getChangeSupport() {
return changeSupport;
}
private static final String FK_JS_DOC = ""
+ "/**\n"
+ " * Indicates that this field is a foreign key to another table or it is a self-reference key.\n"
+ " */";
/**
* Returns if this field is foreign key to another table or it is a
* self-reference key.
*
* @return If this field is foreign key to another table or it is
* self-reference key.
*/
@ScriptFunction(jsDoc = FK_JS_DOC)
public boolean isFk() {
return fk != null;
}
private static final String PK_JS_DOC = ""
+ "/**\n"
+ " * Determines that this field is a primary key.\n"
+ " */";
/**
* Returns if this field is primary key.
*
* @return If this field is primary key.
*/
@ScriptFunction(jsDoc = PK_JS_DOC)
public boolean isPk() {
return pk;
}
/**
* Sets indicating primary key state of this field.
*
* @param aValue Flag, indicating primary key state of this field.
*/
@ScriptFunction
public void setPk(boolean aValue) {
boolean oldValue = pk;
pk = aValue;
changeSupport.firePropertyChange(PK_PROPERTY, oldValue, aValue);
}
/**
* Returns foreign key specification of this field if it references to some
* table.
*
* @return Foreign key specification of this field if it references to some
* table.
*/
public ForeignKeySpec getFk() {
return fk;
}
/**
* Sets foreign key specification to this field, making it the reference to
* some table.
*
* @param aValue Foreign key specification to be set to this field.
*/
public void setFk(ForeignKeySpec aValue) {
ForeignKeySpec oldValue = fk;
fk = aValue;
changeSupport.firePropertyChange(FK_PROPERTY, oldValue, aValue);
}
private static final String READOLNY_JS_DOC = ""
+ "/**\n"
+ " * Determines if this field is readonly.\n"
+ " */";
/**
* Returns if this field is readonly.
*
* @return If true this field is readonly.
*/
@ScriptFunction(jsDoc = READOLNY_JS_DOC)
public boolean isReadonly() {
return readonly;
}
/**
* Sets readonly flag to this field.
*
* @param aValue Flag to be set to this field.
*/
@ScriptFunction
public void setReadonly(boolean aValue) {
boolean oldValue = readonly;
readonly = aValue;
changeSupport.firePropertyChange(READONLY_PROPERTY, oldValue, aValue);
}
/**
* Tests the equality of this field to another object.
*
* @param obj Object to be tested as equal or n ot equal.
* @return The equality of this field to another object.
*/
public boolean isEqual(Object obj) {
if (obj != null && obj instanceof Field) {
Field other = (Field) obj;
String rfDescription = other.getDescription();
String rfName = other.getName();
String rfTableName = other.getTableName();
String rfOriginalName = other.getOriginalName();
String rfType = other.getType();
return nullable == other.isNullable()
&& pk == other.isPk()
&& readonly == other.isReadonly()
&& (fk == null ? other.getFk() == null : fk.equals(other.getFk()))
&& (description == null ? rfDescription == null : description.equals(rfDescription))
&& (name == null ? rfName == null : name.equals(rfName))
&& (originalName == null ? rfOriginalName == null : originalName.equals(rfOriginalName))
&& (tableName == null ? rfTableName == null : tableName.equals(rfTableName))
&& (type == null ? rfType == null : type.equals(rfType));
}
return false;
}
private static final String NAME_JS_DOC = ""
+ "/**\n"
+ " * The name of the field.\n"
+ " */";
/**
* Returns the name of the field.
*
* @return The name of the field.
*/
@ScriptFunction(jsDoc = NAME_JS_DOC)
public String getName() {
return name;
}
/**
* Set the name to this field.
*
* @param aValue A name to be set.
*/
@ScriptFunction
public void setName(String aValue) {
String oldValue = name;
name = aValue;
changeSupport.firePropertyChange(NAME_PROPERTY, oldValue, aValue);
}
private static final String DESCRIPTION_JS_DOC = ""
+ "/**\n"
+ " * The description of the field.\n"
+ " */";
/**
* Returns description of the field.
*
* @return Description of the field.
*/
@ScriptFunction(jsDoc = DESCRIPTION_JS_DOC)
public String getDescription() {
return description;
}
/**
* Set the description to this field.
*
* @param aValue A description to be set.
*/
@ScriptFunction
public void setDescription(String aValue) {
String oldValue = description;
description = aValue;
changeSupport.firePropertyChange(DESCRIPTION_PROPERTY, oldValue, aValue);
}
private static final String TYPE_INFO_JS_DOC = ""
+ "/**\n"
+ " * The field's type information.\n"
+ " */";
/**
* Returns the field's type information
*
* @return The field's type information
*/
@ScriptFunction(jsDoc = TYPE_INFO_JS_DOC)
public String getType() {
return type;
}
/**
* Sets the field's type description
*
* @param aValue The filed's type description
*/
@ScriptFunction
public void setType(String aValue) {
String oldValue = type;
type = aValue;
changeSupport.firePropertyChange(TYPE_PROPERTY, oldValue, type);
}
public Object generateValue() {
Object value;
if (type != null) {
switch (type) {
case Scripts.NUMBER_TYPE_NAME:
value = IdGenerator.genId();
break;
case Scripts.STRING_TYPE_NAME:
value = IdGenerator.genStringId();
break;
case Scripts.DATE_TYPE_NAME:
value = new Date();
break;
case Scripts.BOOLEAN_TYPE_NAME:
value = false;
break;
default:
value = null;
break;
}
} else {
value = null;
}
return value;
}
private static final String NULLABLE_JS_DOC = ""
+ "/**\n"
+ " * Determines if field is nullable.\n"
+ " */";
/**
* Returns whether this field is nullable.
*
* @return Whether this field is nullable.
*/
@ScriptFunction(jsDoc = NULLABLE_JS_DOC)
public boolean isNullable() {
return nullable;
}
/**
* Sets the field's nullable state.
*
* @param aValue Field's nullable flag.
*/
@ScriptFunction
public void setNullable(boolean aValue) {
boolean oldValue = nullable;
nullable = aValue;
changeSupport.firePropertyChange(NULLABLE_PROPERTY, oldValue, aValue);
}
/**
* Copies this feld's information to another instance.
*
* @return Another instance of <code>Field</code> class, initialized with
* this field information.
*/
public Field copy() {
return new Field(this);
}
/**
* Assignes <code>aSourceField</code> information to this <code>Field</code>
* instance.
*
* @param aSourceField <code>Field</code> instance used as a source for
* assigning.
*/
public void assignFrom(Field aSourceField) {
if (aSourceField != null) {
setName(aSourceField.getName());
setOriginalName(aSourceField.getOriginalName());
setTableName(aSourceField.getTableName());
setType(aSourceField.getType());
setDescription(aSourceField.getDescription());
setNullable(aSourceField.isNullable());
setReadonly(aSourceField.isReadonly());
setPk(aSourceField.isPk());
setFk(aSourceField.getFk() != null ? (ForeignKeySpec) aSourceField.getFk().copy() : null);
}
}
protected static boolean equalsOrNulls(Object o1, Object o2) {
return (o1 == null && o2 == null) || (o1 != null && o1.equals(o2)) || (o2 != null && o2.equals(o1));
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (tableName != null && !tableName.isEmpty()) {
sb.append(tableName).append(".");
}
if (originalName != null && !originalName.isEmpty()) {
sb.append(originalName);
} else {
sb.append(name);
}
if (description != null && !description.isEmpty()) {
sb.append(" (").append(description).append(")");
}
if (pk) {
sb.append(", primary key");
}
if (fk != null && fk.getReferee() != null) {
PrimaryKeySpec rf = fk.getReferee();
sb.append(", foreign key to ");
if (rf.schema != null && !rf.schema.isEmpty()) {
sb.append(rf.schema).append(".");
}
if (rf.table != null && !rf.table.isEmpty()) {
sb.append(rf.table).append(".");
}
sb.append(rf.field);
}
sb.append(", ").append(type);
if (nullable) {
sb.append(", nullable");
}
if (readonly) {
sb.append(", readonly");
}
return sb.toString();
}
@Override
public JSObject getPublished() {
if (published == null) {
JSObject publisher = Scripts.getSpace().getPublisher(this.getClass().getName());
if (publisher == null || !publisher.isFunction()) {
throw new NoPublisherException();
}
published = (JSObject) publisher.call(null, new Object[]{this});
}
return published;
}
@Override
public void setPublished(JSObject aValue) {
if (published != null) {
throw new AlreadyPublishedException();
}
published = aValue;
}
}