Skip to content

Commit

Permalink
Merge pull request #749 from spelletier/add_uuid_support
Browse files Browse the repository at this point in the history
Add uuid support
  • Loading branch information
avendasora authored Jun 25, 2016
2 parents c25de0b + 12732ce commit 347468d
Show file tree
Hide file tree
Showing 10 changed files with 257 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@
import er.extensions.foundation.ERXArrayUtilities;
import er.extensions.foundation.ERXDictionaryUtilities;
import er.extensions.foundation.ERXKeyValueCodingUtilities;
import er.extensions.foundation.ERXStringUtilities;
import er.extensions.foundation.UUIDUtilities;
import er.extensions.jdbc.ERXSQLHelper;
import er.extensions.validation.ERXValidationException;
import er.extensions.validation.ERXValidationFactory;
Expand Down Expand Up @@ -1308,6 +1310,13 @@ public static String _stringForPrimaryKey(Object pk) {
if(pk instanceof String || pk instanceof Number) {
return pk.toString();
}
if (pk instanceof NSData) {
byte[] pkBytes = ((NSData)pk)._bytesNoCopy();
if (pkBytes.length == 16) {
return UUIDUtilities.encodeAsPrettyString(pkBytes);
}
return ERXStringUtilities.byteArrayToHexString(pkBytes);
}
return NSPropertyListSerialization.stringFromPropertyList(pk);
}

Expand Down Expand Up @@ -1341,6 +1350,9 @@ public static NSDictionary<String, Object> primaryKeyDictionaryForString(EOEditi
if(attribute.adaptorValueType() == EOAttribute.AdaptorDateType && !(value instanceof NSTimestamp)) {
value = new NSTimestampFormatter("%Y-%m-%d %H:%M:%S %Z").parseObject((String)value);
}
if(attribute.adaptorValueType() == EOAttribute.AdaptorBytesType && attribute.width() == 16 && !(value instanceof NSData)) {
value = UUIDUtilities.decodeStringAsNSData((String)value);
}
value = attribute.validateValue(value);
pk.setObjectForKey(value, attribute.name());
if(pks.count() == 1) {
Expand All @@ -1357,6 +1369,9 @@ public static NSDictionary<String, Object> primaryKeyDictionaryForString(EOEditi
}
EOAttribute attribute = pks.objectAtIndex(0);
Object value = rawValue;
if(attribute.adaptorValueType() == EOAttribute.AdaptorBytesType && attribute.width() == 16 && !(value instanceof NSData)) {
value = UUIDUtilities.decodeStringAsNSData((String)value);
}
value = attribute.validateValue(value);
pk.setObjectForKey(value, attribute.name());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import er.extensions.foundation.ERXDictionaryUtilities;
import er.extensions.foundation.ERXProperties;
import er.extensions.foundation.ERXUtilities;
import er.extensions.foundation.UUIDUtilities;
import er.extensions.localization.ERXLocalizer;
import er.extensions.validation.ERXValidationException;
import er.extensions.validation.ERXValidationFactory;
Expand Down Expand Up @@ -89,6 +90,8 @@ public class ERXGenericRecord extends EOGenericRecord implements ERXGuardedObjec
/** holds all subclass related Logger's */
private static final NSMutableDictionary<Class, Logger> classLogs = new NSMutableDictionary<Class, Logger>();

private static final Object uuidPrototypeName = "uuid";

public static boolean shouldTrimSpaces() {
return ERXProperties.booleanForKeyWithDefault("er.extensions.ERXGenericRecord.shouldTrimSpaces", false);
}
Expand Down Expand Up @@ -749,13 +752,36 @@ public NSDictionary<String, Object> rawPrimaryKeyDictionary(boolean inTransactio
_primaryKeyDictionary = compositePrimaryKey;
}
else {
_primaryKeyDictionary = ERXEOControlUtilities.newPrimaryKeyDictionaryForObject(this);
_primaryKeyDictionary = createUuidPrimaryKey(primaryKeyAttributes);
if (_primaryKeyDictionary == null) {
_primaryKeyDictionary = ERXEOControlUtilities.newPrimaryKeyDictionaryForObject(this);
}
}
}
}
else { // inTransaction
EOEntity entity = entity();
NSArray<EOAttribute> primaryKeyAttributes = entity.primaryKeyAttributes();
_primaryKeyDictionary = createUuidPrimaryKey(primaryKeyAttributes);
}
}
return _primaryKeyDictionary;
}

/**
* Create a primary key if the entity primary key is an attribute with uuid prototype.
* @param primaryKeyAttributes
* @return the primary key dictionary or null if the primary key is not a uuid.
*/
private NSDictionary<String, Object> createUuidPrimaryKey(NSArray<EOAttribute> primaryKeyAttributes) {
if (primaryKeyAttributes.count() == 1) {
EOAttribute primaryKeyAttribute = primaryKeyAttributes.objectAtIndex(0);
if (primaryKeyAttribute.prototypeName().equals(uuidPrototypeName)) {
return new NSDictionary<String, Object>(UUIDUtilities.generateAsNSData(), primaryKeyAttribute.name());
}
}
return null;
}

/**
* Sets the value for the primary key attribute with the given name. This should only be called
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package er.extensions.foundation;

import java.security.SecureRandom;

import com.webobjects.foundation.NSData;

/**
* Helper functions for UUID
*
*/
public class UUIDUtilities {
private static final String validHexCharacters = "ABCDEF0123456789abcdef";
static final SecureRandom randomGenerator = new SecureRandom();

public final static int typeByteOffset = 6;
public final static int variationByteOffset = 8;


/**
* Generate a type 4 UUID from a secure random source like the java UUID class.
* @return a new UUID in a NSData
*/
public static NSData generateAsNSData() {
return new NSData(generateAsByteArray());
}

/**
* Generate a type 4 UUID from a secure random source like the java UUID class.
* @return a new UUID in a byte[]
*/
private static byte[] generateAsByteArray() {
byte[] value = new byte[16];
randomGenerator.nextBytes(value);

value[typeByteOffset] &= 0xF; // Clear the type part of the byte
value[typeByteOffset] |= 0x40; // Set the type part of the byte to random
value[variationByteOffset] &= 0x3F; // Clear the variant part of the byte
value[variationByteOffset] |= 0x80; // set to IETF variant

return value;
}

/**
* Decode a string representing a UUID in hex.
* All non hex char are filtered before the decoding, any formats are accepted.
* @param uuid the string representing a UUID
* @return the decoded UUID in a NSData
*/
static public NSData decodeStringAsNSData(String uuid) {
return new NSData(decodeStringAsByteArray(uuid));
}

/**
* Decode a string representing a UUID in hex.
* All non hex char are filtered before the decoding, any formats are accepted.
* @param uuid the string representing a UUID
* @return the decoded UUID in a byte[]
*/
static public byte[] decodeStringAsByteArray(String uuid) {
if (uuid == null) {
throw new IllegalArgumentException("Null is not a valid UUID value.");
}
String cleannedUuid = ERXStringUtilities.removeExceptCharacters(uuid, validHexCharacters);
if (cleannedUuid.length() != 32) {
throw new IllegalArgumentException("\""+uuid+"\" is not a valid hex string representing a byte[16].");
}

try {
return ERXStringUtilities.hexStringToByteArray(cleannedUuid);
}
catch (IllegalArgumentException e) {
throw new IllegalArgumentException("\""+uuid+"\" is not a valid hex string representing a byte[16].", e);
}
}

/**
* Encode a UUID in a String using the usual format 12345678-1234-1234-1234-123456789ABC
* @param uuid a NSData containing a UUID
* @return the encoded UUID
*/
static public String encodeAsPrettyString(NSData uuid) {
return encodeAsPrettyString(uuid.bytes());
}

/**
* Encode a UUID in a String using the usual format 12345678-1234-1234-1234-123456789ABC
* @param uuid a byte[] containing a UUID
* @return the encoded UUID
*/
static public String encodeAsPrettyString(byte[] uuid) {
String rawString = encodeAsString(uuid);

StringBuilder formattedString = new StringBuilder();
formattedString.append(rawString.substring(0, 8));
formattedString.append("-");
formattedString.append(rawString.substring(8, 12));
formattedString.append("-");
formattedString.append(rawString.substring(12, 16));
formattedString.append("-");
formattedString.append(rawString.substring(16, 20));
formattedString.append("-");
formattedString.append(rawString.substring(20, 32));
return formattedString.toString();
}

/**
* Encode a UUID in a String in hex
* @param uuid a NSData containing a UUID
* @return the encoded UUID
*/
static public String encodeAsString(NSData uuid) {
return encodeAsString(uuid.bytes());
}

/**
* Encode a UUID in a String in hex
* @param uuid a byte[] containing a UUID
* @return the encoded UUID
*/
static public String encodeAsString(byte[] uuid) {
if (uuid == null) {
throw new IllegalArgumentException("Null is not a valid UUID value.");
}

String rawString = ERXStringUtilities.byteArrayToHexString(uuid);
if (uuid.length != 16) {
throw new IllegalArgumentException("\""+rawString+"\" is not a byte[16].");
}
return rawString;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2194,6 +2194,9 @@ public String externalTypeForJDBCType(JDBCAdaptor adaptor, int jdbcType) {
if (jdbcType == Types.BOOLEAN) {
externalType = "boolean";
}
else if (jdbcType == Types.BINARY) {
externalType = "byte";
}
else {
externalType = super.externalTypeForJDBCType(adaptor, jdbcType);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -907,7 +907,19 @@ public ERXMigrationColumn newIpAddressColumn(String name, boolean allowsNull) th
public ERXMigrationColumn newIpAddressColumn(String name, boolean allowsNull, String defaultValue) throws SQLException {
return newColumn(name, CustomTypes.INET, 39, 0, 0, allowsNull, null, defaultValue);
}


/**
* Returns a new UUID column. See newColumn(..) for the full docs.
*
* @param name the name of the column
* @param allowsNull if true, the column will allow null values
* @return the new ERXMigrationColumn
* @throws SQLException if the column cannot be created
*/
public ERXMigrationColumn newUuidColumn(String name, boolean allowsNull) throws SQLException {
return newColumn(name, Types.BINARY, 16, 0, 0, allowsNull, null);
}

/**
* Callback method for ERXMigrationColumn to notify the table that
* it has been deleted.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,13 @@
valueClassName = NSString;
valueType = c;
width = 10000000;
},
{
columnName = uuid;
externalType = BYTE;
name = uuid;
valueClassName = NSData;
width = 16;
}
);
attributesUsedForLocking = (boolean, id);
Expand Down Expand Up @@ -431,6 +438,7 @@
osType,
shortString,
type,
uuid,
varchar10,
varchar100,
varchar1000,
Expand All @@ -443,7 +451,7 @@
};
isAbstractEntity = Y;
name = EOJDBCFrontBasePrototypes;
primaryKeyAttributes = (eofKey, id);
primaryKeyAttributes = (eofKey, id, uuid);
userInfo = {
exampleConnectionDictionary = {URL = "jdbc:FrontBase://127.0.0.1/DatabaseName"; };
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,13 @@
name = varcharLarge;
valueClassName = NSString;
valueType = c;
},
{
columnName = uuid;
externalType = BINARY;
name = uuid;
valueClassName = NSData;
width = 16;
}
);
attributesUsedForLocking = (
Expand Down Expand Up @@ -456,5 +463,5 @@
};
isAbstractEntity = Y;
name = EOJDBCMySQLPrototypes;
primaryKeyAttributes = (id);
primaryKeyAttributes = (id, uuid);
}
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,13 @@
externalType = CLOB;
name = varcharLarge;
valueClassName = NSString;
},
{
columnName = uuid;
externalType = RAW;
name = uuid;
valueClassName = NSData;
width = 16;
}
);
attributesUsedForLocking = (
Expand Down Expand Up @@ -441,5 +448,5 @@
};
isAbstractEntity = Y;
name = EOJDBCOraclePrototypes;
primaryKeyAttributes = (eofKey, id);
primaryKeyAttributes = (eofKey, id, uuid);
}
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,13 @@
valueClassName = NSString;
valueType = S;
width = 10000000;
},
{
columnName = uuid;
externalType = bytea;
name = uuid;
valueClassName = NSData;
width = 16;
}
);
attributesUsedForLocking = (boolean, id, jodaDateTime);
Expand Down Expand Up @@ -441,5 +448,5 @@
};
isAbstractEntity = Y;
name = EOJDBCPostgresqlPrototypes;
primaryKeyAttributes = (eofKey, id);
primaryKeyAttributes = (eofKey, id, uuid);
}
Loading

0 comments on commit 347468d

Please sign in to comment.