/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.extension.properties;
import java.beans.PropertyChangeListener;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.osgi.util.NLS;
import org.teiid.core.designer.properties.PropertyDefinition;
import org.teiid.core.designer.util.CoreArgCheck;
import org.teiid.core.designer.util.CoreStringUtil;
import org.teiid.designer.extension.Messages;
/**
* A <code>ModelExtensionPropertyDefinition</code> is the property definition of all extension properties.
*
* @since 8.0
*/
public interface ModelExtensionPropertyDefinition extends Cloneable, PropertyDefinition {
/**
* The allowed boolean values.
*/
String[] BOOLEAN_ALLOWED_VALUES = new String[] { Boolean.TRUE.toString(), Boolean.FALSE.toString() };
/**
* The delimiter character between the namespace prefix and the simple identifier.
*/
char ID_DELIM = ':';
/**
* For indexed ID, the delimiter character that starts a namespace URI.
*/
char URI_START_DELIM = '{';
/**
* For indexed ID, the delimiter character that ends a namespace URI.
*/
char URI_END_DELIM = '}';
/**
* The default value for the advanced property (default is {@value} ).
*/
boolean ADVANCED_DEFAULT = false;
/**
* The default value for the indexed property (default is {@value} ).
*/
boolean INDEX_DEFAULT = true;
/**
* The default value for the masked property (default is {@value} ).
*/
boolean MASKED_DEFAULT = false;
/**
* The default value for the required property (default is {@value} ).
*/
boolean REQUIRED_DEFAULT = false;
/**
* The default value for the runtime type property (default is {@value} ).
*/
Type TYPE_DEFAULT = Type.STRING;
/**
* @param newAllowedValue the allowed value being added (cannot be <code>null</code>)
* @return <code>true</code> if the allowed value was added
*/
boolean addAllowedValue( String newAllowedValue );
/**
* @param newDescription the translated description being added (cannot be <code>null</code>)
* @return <code>true</code> if the description was added
*/
boolean addDescription( Translation newDescription );
/**
* @param newDisplayName the translated display name being added (cannot be <code>null</code>)
* @return <code>true</code> if the display name was added
*/
boolean addDisplayName( Translation newDisplayName );
/**
* @param listener the listener being registered to receive property definition property change events (never <code>null</code>)
* @return <code>true</code> if the listener was successfully added
*/
boolean addListener( PropertyChangeListener listener );
/**
* The collection returned can be manipulated without affecting this property definition.
*
* @return the allowed values (never <code>null</code> but can be empty)
*/
Set<String> allowedValues();
/**
* @return a deep copy of this property definition (never <code>null</code>)
*/
Object clone();
/**
* The collection returned can be manipulated without affecting this property definition.
*
* @return the descriptions (never <code>null</code> but can be empty)
*/
Set<Translation> getDescriptions();
/**
* The collection returned can be manipulated without affecting this property definition.
*
* @return the display names (never <code>null</code> but can be empty)
*/
Set<Translation> getDisplayNames();
/**
* @return the fixed value (a non-<code>null</code> value means the value is unmodifiable)
*/
String getFixedValue();
/**
* @return the namespace provider (cannot be <code>null</code>)
*/
NamespaceProvider getNamespaceProvider();
/**
* @return the runtime type (can be <code>null</code> or empty)
*/
String getRuntimeType();
/**
* A simple identifier does not include the namespace prefix.
*
* @return the simple identifier (can be <code>null</code> or empty)
*/
String getSimpleId();
/**
* @return the type (can be <code>null</code> or empty)
*/
Type getType();
/**
* @param allowedValue the allowed value being removed (cannot be <code>null</code>)
* @return <code>true</code> if the allowed value was removed
*/
boolean removeAllowedValue( String allowedValue );
/**
* @param description the description being removed (cannot be <code>null</code>)
* @return <code>true</code> if the description was removed
*/
boolean removeDescription( Translation description );
/**
* @param displayName the display name being removed (cannot be <code>null</code>)
* @return <code>true</code> if the display name was removed
*/
boolean removeDisplayName( Translation displayName );
/**
* @param listener the listener being removed (cannot be <code>null</code>)
* @return <code>true</code> if the listener was successfully removed
*/
boolean removeListener( PropertyChangeListener listener );
/**
* @param newAdvanced the new advanced value
*/
void setAdvanced( boolean newAdvanced );
/**
* @param newAllowedValues the new allowed values (can be <code>null</code> but cannot have <code>null</code> values)
*/
void setAllowedValues( Set<String> newAllowedValues );
/**
* @param newDefaultValue the new default value (can be <code>null</code> or empty)
*/
void setDefaultValue( String newDefaultValue );
/**
* @param newDescriptions the new descriptions (can be <code>null</code> or empty)
*/
void setDescriptions( Set<Translation> newDescriptions );
/**
* @param newDisplayNames the new display names (can be <code>null</code> or empty)
*/
void setDisplayNames( Set<Translation> newDisplayNames );
/**
* @param newFixedValue the new fixed value (can be <code>null</code> or empty)
*/
void setFixedValue( String newFixedValue );
/**
* @param newIndex the new index value
*/
void setIndex( boolean newIndex );
/**
* @param newMasked the new masked value
*/
void setMasked( boolean newMasked );
/**
* @param newNamespaceProvider the new namespace provider (cannot be <code>null</code>)
*/
void setNamespaceProvider( NamespaceProvider newNamespaceProvider );
/**
* @param newRequired the new required value
*/
void setRequired( boolean newRequired );
/**
* @param newSimpleId the new simpleId (can be <code>null</code> or empty)
*/
void setSimpleId( String newSimpleId );
/**
* @param runtimeType the Teiid runtime type (can be <code>null</code>)
*/
void setType( Type runtimeType );
/**
* @return <code>true</code> if this property should be indexed for use by the Teiid Instance
*/
boolean shouldBeIndexed();
/**
* The property names that can be changed.
*/
public enum PropertyName {
/**
* Indicates if the property should only be modified by advanced users.
*/
ADVANCED,
/**
* The allowed values property.
*/
ALLOWED_VALUES,
/**
* The default value of the property (used when user has not entered a value).
*/
DEFAULT_VALUE,
/**
* The description property.
*/
DESCRIPTION,
/**
* The property name to display to the user.
*/
DISPLAY_NAME,
/**
* The fixed property value.
*/
FIXED_VALUE,
/**
* Indicates if the property should be indexed for use in the Teiid runtime.
*/
INDEX,
/**
* Indicates if the property should be masked when displayed to the user.
*/
MASKED,
/**
* The namespace prefix where the extension property is defined.
*/
NAMESPACE_PREFIX,
/**
* Indicates if the property is required to have a value.
*/
REQUIRED,
/**
* The property identifier without the namespace prefix.
*/
SIMPLE_ID,
/**
* The Teiid runtime data type.
*/
TYPE
}
/**
* These runtime types <strong>MUST</strong>> match those listed in the model extension XSD.
*/
@SuppressWarnings( "javadoc" )
public enum Type {
BIG_DECIMAL("bigdecimal"), //$NON-NLS-1$
BIG_INTEGER("biginteger"), //$NON-NLS-1$
BLOB("blob"), //$NON-NLS-1$
BOOLEAN("boolean"), //$NON-NLS-1$
BYTE("byte"), //$NON-NLS-1$
CHAR("char"), //$NON-NLS-1$
CLOB("clob"), //$NON-NLS-1$
DATE("date"), //$NON-NLS-1$
DOUBLE("double"), //$NON-NLS-1$
FLOAT("float"), //$NON-NLS-1$
INTEGER("integer"), //$NON-NLS-1$
LONG("long"), //$NON-NLS-1$
OBJECT("object"), //$NON-NLS-1$
SHORT("short"), //$NON-NLS-1$
STRING("string"), //$NON-NLS-1$
TIME("time"), //$NON-NLS-1$
TIMESTAMP("timestamp"), //$NON-NLS-1$
XML("xml"); //$NON-NLS-1$
/**
* @return a collection, without duplicates, of the first letter of each runtime type (never <code>null</code> or empty)
*/
public static char[] getFirstChars() {
if (_firstChars == null) {
final Set<Character> temp = new HashSet<Character>();
String chars = ""; //$NON-NLS-1$
for (Type type : values()) {
char c = type.getRuntimeType().charAt(0);
if (temp.add(c)) {
chars += c;
}
}
_firstChars = chars.toCharArray();
}
return _firstChars;
}
private static char[] _firstChars;
/**
* The Teiid runtime type (never <code>null</code> or empty).
*/
private final String runtimeType;
/**
* @param type the Teiid runtime type (cannot be <code>null</code>)
* @throws IllegalArgumentException if the param is not valid
*/
private Type( String type ) {
assert type != null : "runtime type is null"; //$NON-NLS-1$
this.runtimeType = type;
}
/**
* @return the Teiid runtime type (never <code>null</code> or empty)
*/
public String getRuntimeType() {
return this.runtimeType;
}
/**
* {@inheritDoc}
*
* @see java.lang.Enum#toString()
*/
@Override
public String toString() {
return getRuntimeType();
}
}
/**
* Utilities for extension properties.
*/
class Utils {
/**
* @param runtimeType the Teiid runtime type being converted (never <code>null</code> or empty)
* @return the model extension property definition type (newver <code>null</code>)
* @throws IllegalArgumentException if argument cannot be converted to a valid type
*/
public static Type convertRuntimeType( String runtimeType ) {
CoreArgCheck.isNotEmpty(runtimeType, "runtimeType is empty"); //$NON-NLS-1$
for (Type type : Type.values()) {
if (type.getRuntimeType().equals(runtimeType)) {
return type;
}
}
throw new IllegalArgumentException(NLS.bind(Messages.invalidRuntimeType, runtimeType));
}
/**
* @param namespaceProvider the namespace provider (can be <code>null</code>)
* @param simpleId the property simple identifier (can be <code>null</code> or empty)
* @return the property ID which should be indexed or <code>null</code> if either the namespace provider or simple
* identifier is empty
*/
public static String getIndexedId( NamespaceProvider namespaceProvider,
String simpleId ) {
if ((namespaceProvider == null) || CoreStringUtil.isEmpty(simpleId)) {
return null;
}
return URI_START_DELIM + namespaceProvider.getNamespaceUri() + URI_END_DELIM + simpleId;
}
/**
* @param propId the string being checked (can be <code>null</code> or empty)
* @return the namespace prefix or <code>null</code> if not found
*/
private static String getNamespacePrefix( String propId ) {
if (CoreStringUtil.isEmpty(propId)) {
return null;
}
int index = propId.indexOf(ID_DELIM);
if (index != -1) {
// delimiter is first character or there are no characters after delimiter
if ((index == 0) || (propId.length() == (index + 1))) {
return null;
}
return propId.substring(0, index);
}
return null;
}
/**
* @param namespaceProvider the namespace provider (can be <code>null</code>)
* @param propertySimpleId the simple identifier (can be <code>null</code> or empty)
* @return the property ID or <code>null</code> if either the namespace provider or simple identifier is empty
*/
public static String getPropertyId( NamespaceProvider namespaceProvider,
String propertySimpleId ) {
if ((namespaceProvider == null) || CoreStringUtil.isEmpty(propertySimpleId)) {
return null;
}
return namespaceProvider.getNamespacePrefix() + ModelExtensionPropertyDefinition.ID_DELIM + propertySimpleId;
}
/**
* @param propId the property ID being checked (cannot be <code>null</code> or empty)
* @param namespaceProvider the namespace provider used to determine the result (cannot be <code>null</code>)
* @return <code>true</code> if the identifier is a property definition ID for the specified namespace provider
*/
public static boolean isExtensionPropertyId( String propId,
NamespaceProvider namespaceProvider ) {
CoreArgCheck.isNotEmpty(propId, "propId is empty"); //$NON-NLS-1$
CoreArgCheck.isNotNull(namespaceProvider, "namespaceProvider is null"); //$NON-NLS-1$
String nsPrefix = namespaceProvider.getNamespacePrefix();
if (CoreStringUtil.isEmpty(nsPrefix)) {
return false;
}
String propNsPrefix = getNamespacePrefix(propId);
if (CoreStringUtil.isEmpty(propNsPrefix)) {
return false;
}
return propNsPrefix.equals(nsPrefix);
}
/**
* @param valueTypeMsg a message describing the type of value being validated (cannot be <code>null</code>)
* @param runtimeType the runtime type (can be <code>null</code>)
* @param proposedValue the proposed value (can be <code>null</code> or empty)
* @param required indicates if the property requires a value
* @param allowedValues the allowed values (can be <code>null</code> or empty)
* @return the error message or <code>null</code>
*/
public static String isValidValue( final String valueTypeMsg,
final Type runtimeType,
final String proposedValue,
final boolean required,
final String[] allowedValues ) {
// must have a runtime type
if (runtimeType == null) {
return Messages.missingRuntimeTypeValidationMsg;
}
// must have a value
if (CoreStringUtil.isEmpty(proposedValue)) {
if (required) {
return NLS.bind(Messages.emptyPropertyValue, valueTypeMsg);
}
}
// validate against allowed values first
if ((allowedValues != null) && (allowedValues.length != 0)) {
for (String allowedValue : allowedValues) {
if (allowedValue.equals(proposedValue)) {
// valid
return null;
}
}
// must match an allowed value
return Messages.valueDoesNotMatchAnAllowedValue;
}
// no validation done on these types
if ((Type.STRING == runtimeType) || (Type.BLOB == runtimeType) || (Type.CLOB == runtimeType)
|| (Type.OBJECT == runtimeType) || (Type.XML == runtimeType)) {
return null; // valid
}
if (Type.BOOLEAN == runtimeType) {
if (!proposedValue.equalsIgnoreCase(Boolean.TRUE.toString())
&& !proposedValue.equalsIgnoreCase(Boolean.FALSE.toString())) {
return NLS.bind(Messages.invalidPropertyValueForType, proposedValue, runtimeType);
}
} else if (Type.CHAR == runtimeType) {
if (proposedValue.length() != 1) {
return NLS.bind(Messages.invalidPropertyValueForType, proposedValue, runtimeType);
}
} else if (Type.BYTE == runtimeType) {
try {
Byte.parseByte(proposedValue);
} catch (Exception e) {
return NLS.bind(Messages.invalidPropertyValueForType, proposedValue, runtimeType);
}
} else if (Type.SHORT == runtimeType) {
try {
Short.parseShort(proposedValue);
} catch (Exception e) {
return NLS.bind(Messages.invalidPropertyValueForType, proposedValue, runtimeType);
}
} else if (Type.INTEGER == runtimeType) {
try {
Integer.parseInt(proposedValue);
} catch (Exception e) {
return NLS.bind(Messages.invalidPropertyValueForType, proposedValue, runtimeType);
}
} else if (Type.LONG == runtimeType) {
try {
Long.parseLong(proposedValue);
} catch (Exception e) {
return NLS.bind(Messages.invalidPropertyValueForType, proposedValue, runtimeType);
}
} else if (Type.FLOAT == runtimeType) {
try {
Float.parseFloat(proposedValue);
} catch (Exception e) {
return NLS.bind(Messages.invalidPropertyValueForType, proposedValue, runtimeType);
}
} else if (Type.DOUBLE == runtimeType) {
try {
Double.parseDouble(proposedValue);
} catch (Exception e) {
return NLS.bind(Messages.invalidPropertyValueForType, proposedValue, runtimeType);
}
} else if (Type.BIG_INTEGER == runtimeType) {
try {
new BigInteger(proposedValue);
} catch (Exception e) {
return NLS.bind(Messages.invalidPropertyValueForType, proposedValue, runtimeType);
}
} else if (Type.BIG_DECIMAL == runtimeType) {
try {
new BigDecimal(proposedValue);
} catch (Exception e) {
return NLS.bind(Messages.invalidPropertyValueForType, proposedValue, runtimeType);
}
} else if (Type.DATE == runtimeType) {
try {
Date.valueOf(proposedValue);
} catch (Exception e) {
return NLS.bind(Messages.invalidPropertyValueForType, proposedValue, runtimeType);
}
} else if (Type.TIME == runtimeType) {
try {
Time.valueOf(proposedValue);
} catch (Exception e) {
return NLS.bind(Messages.invalidPropertyValueForType, proposedValue, runtimeType);
}
} else if (Type.TIMESTAMP == runtimeType) {
try {
Timestamp.valueOf(proposedValue);
} catch (Exception e) {
return NLS.bind(Messages.invalidPropertyValueForType, proposedValue, runtimeType);
}
} else {
// unknown property type
return NLS.bind(Messages.unknownPropertyType, runtimeType);
}
// valid
return null;
}
}
}