Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instead of a string as a key to lookup Property and Model values can we enhance it using a Property Identifier type object? #2

Closed
carldea opened this issue Jun 17, 2024 · 0 comments · Fixed by #3
Assignees
Labels
enhancement New feature or request

Comments

@carldea
Copy link
Owner

carldea commented Jun 17, 2024

Question: Can properties get looked up other than a property name of type String?

Answer: No (not yet), the APIs are currently only using a property name of type String.

Let’s talk about the idea of a Property Identifier type object. This will offer another way to lookup property & model values by using a unique id or property name.

Current State

The current MVVM capability of Cognitive only looks up property and model values based on a key of type String of a property name (used to reference a field in a UI form).

Enhancement

An enhancement updates the APIs to allow a Property Identifier type object that represents a unique id, a property name, and an underlying user data type object. The user data type object can be any object that contains a unique value. e.g. Person class may have a getter method returning a unique id.

With this newer PropertyIdentifier it allows you to continue using it for property and model value lookup and also validation.

/**
 * A property id and name to uniquely represent a field.
 * @param <T> type of unique identifier. e.g. UUID, Integer, String
 * @param <U> The domain object entity type. e.g. Concept record, or String.
 */
public interface PropertyIdentifier<T, U>{
    T getPropertyId();
    U getUserData();
    String getPropertyName();
}

Add (create) a property using a PropertyIdentifier with validation

PropertyIdentifier<UUID, UUID> spId = new SimplePropertyIdentifier<>("firstName", uId -> uId, UUID.randomUUID());

IdValidationViewModel personVm =  new IdValidationViewModel()
   .addProperty(spId, "fred")
   .addValidator(spId, "First Name", (ReadOnlyStringProperty prop, ViewModel vm) -> {
      // Check if first character is capitalized
      String firstChar = prop.get().substring(0,1);
      if (firstChar.toUpperCase().equals(firstChar))
         return VALID;

      return new ValidationMessage(spId.idToString(), MessageType.ERROR, "${%s} first character must be capitalized. Entered as %s ".formatted(spId.idToString(), prop.get()));

   });

String firstName = personVm.getPropertyValue(spId); // fred

Above you'll notice personVm.getPropertyValue(spId) is looked up by a PropertyIdentifier instance. As a convenience the APIs should allow the caller to also lookup properties by using the following (as a key type):

  • Property name - To be consistent with the current APIs still able to lookup by property name.
  • Unique identifier (String) - In certain scenarios the unique id is passed as a string such as a UUID.toString().
  • Unique identifier (Generic type def) - In certain scenarios the actual unique id object is used such as a UUID instance.

Additional example of convenient ways to lookup or set property values on the view model.

// spId.getPropertyId().toString() = 12628b52-2ba5-418c-b6ad-02f87edb97ce
UUID uniqueId = spId.getPropertyId();

// Get property value using any of the three other key types.
String firstName = personVm.getPropertyValue(uniqueId);
String firstName = personVm.getPropertyValue("firstName");
String firstName = personVm.getPropertyValue("12628b52-2ba5-418c-b6ad-02f87edb97ce");

// Validate view model
personVm.validate();  // error message: First Name first character must be capitalized. Entered as fred 

// Set the property value to Fred. All equivalent to personVm.setPropertyValue(spId, "Fred");
personVm.setPropertyValue(uniqueId, "Fred");
personVm.setPropertyValue("firstName", "Fred");
personVm.setPropertyValue("12628b52-2ba5-418c-b6ad-02f87edb97ce", "Fred");

When we validate personVm.validate() the error message will be returned as follows:
First Name first character must be capitalized. Entered as fred

After validation above you'll notice the calls to setPropertyValue() to fix the validation error.

Now that things are valid a save() will move property values into the model values. Below are examples of using the different key types to get model values.

personVm.save(); // override validation

String firstName = personVm.getValue(firstNameFieldPropId);  // PropertyIdentifier
String firstName = personVm.getValue(propId);         // property id (unique id)
String firstName = personVm.getValue("firstName");   // property name
String firstName = personVm.getValue("12628b52-2ba5-418c-b6ad-02f87edb97ce"); // property id as a String

Java Classes & Interfaces to create.

The following is a high level list of items to create.

  • Create an interface PropertIdentifier
  • Create a SimplePropertyIdentifier
  • Add propertys to IdSimpleViewModel - addProperty(PropertyIdentifier propId, T value)
  • Add validators to IdValidationViewModel - addValidator(PropertyIdentifier propId, String friendlyName, Validator validator)
  • Create tests for IdSimpleViewModel
  • Create tests for IdValidationViewModel
  • Refactor Validation code to be a delegate (ValidationManager)

A high-level UML class hierarchy
property-identifier-concept-oriented

Some additional notes:

PropertyIdentifier

The following is an interface to allow for custom PropertyIdentifiers:

package org.carlfx.cognitive.viewmodel;

/**
 * A property id and name to uniquely represent a field. See setUserData() methods to allow the field to reference any object.
 * @param <T> type of unique identifier. e.g. UUID, Integer, String
 * @param <U> The domain object entity type. e.g. Concept record, or String.
 */
public interface PropertyIdentifier<T, U>{
    /**
     * Returns the unique id of this object.
     * @return Returns the unique id of this object.
     */
    T getPropertyId();

    /**
     * Returns the user data set into this object.
     * @return Returns the user data set into this object.
     */
    U getUserData();
    String getPropertyName();
    /**
     * Compares other PropertyIdentifier instances for equality.
     * @param other Other property identifier to compare.
     * @return Returns true if equals otherwise false.
     */
    default boolean equals(PropertyIdentifier other) {
        return getPropertyId().equals(other.getPropertyId());
    }

    /**
     * A unique integer hash code based on the property id.
     * @return A unique integer hash code based on the property id.
     */
    default int hashcode() {
        return getPropertyId().hashCode();
    }

    default String idToString() {
        return getPropertyId().toString();
    }
}

Here's a possible custom property identifier (concrete implementation):

public record ConceptPropertyIdentifier(/* Db db, */ConceptRecord concept) implements PropertyIdentifier<UUID, ConceptRecord> {
    @Override
    public UUID getPropertyId() {
        return concept.uuid();
    }

    @Override
    public ConceptRecord getUserData() {
        return concept;
    }


    /**
     * Additional methods to provide full names, short names, etc.
     * @return The full name of the concept
     */
    public String fullName() {
        // able to retrieve concepts full name from database.
        // return db.query(id: concept.uuid() ).fqn()
        return concept.fullName();
    }
    /**
     * A default readable property name for field often used for the UIs label.
     * @return property name to lookup field
     */
    @Override
    public String getPropertyName() {
        // able to retrieve concepts full name from database.
        // return db.query(id: concept.uuid() ).fqn()
        return concept.shortName();
    }

    /**
     * A default readable property name for field often used for the UIs label.
     * @return property name to lookup field
     */
    public String shortName() {
        // able to retrieve concepts full name from database.
        // return db.query(id: concept.uuid() ).fqn()
        return concept.shortName();
    }

}

Adding a new concept oriented property values and validation

// create a field (label) for Case significants
ConceptRecord caseSigConceptRecord = new ConceptRecord(UUID.randomUUID(), "Case Significance", "Case");

// Two concept values (insensitive & initial capital)
ConceptRecord caseInsenstiveConcept = new ConceptRecord(UUID.randomUUID(), "Case Insensitive", "Insensitive");
ConceptRecord caseCapInitialConcept = new ConceptRecord(UUID.randomUUID(), "Capitalize initial character", "Cap 1st Character");

// Create an IdValidationViewModel
IdValidationViewModel personVm =  new IdValidationViewModel()
   .addProperty(new ConceptPropertyIdentifier(caseSigConceptRecord), caseInsenstiveConcept) // Custom PropertyIdentifier Concept oriented value. propertyId (case sig) -> Property Value (Case insensitive).
   .addValidator(caseSigConceptRecord.uuid(), "Case significance", (ReadOnlyObjectProperty prop, ViewModel vm) -> {
         ConceptRecord conceptRecord = (ConceptRecord) prop.get();
         if (!conceptRecord.uuid().equals(caseCapInitialConcept.uuid())) {
            return new ValidationMessage(caseSigConceptRecord.uuid().toString(), MessageType.ERROR, "Case Significance must be %s. Entered as %s ".formatted(caseCapInitialConcept.shortName(), prop.get()));
         }
         return VALID;
   });

personVm.validate();

Output of error:

{ msgType=ERROR, errorCode=-1, msg="Case Significance must be Cap 1st Character. Entered as ConceptRecord[uuid=4b28f6fb-45a1-4bee-b1fa-ff95eec356b3, fullName=Case Insensitive, shortName=Insensitive] "}

The error says the Case significance must be the concept that is a (Capitalize first character concept) value and not the concept (Case insensitive).

To fix the validation error simply set property value to the concept record representing capitalize initial character concept (variable: caseCapInitialConcept).

personVm.setPropertyValue(caseSigConceptRecord.uuid(), caseCapInitialConcept);
personVm.save();

ConceptRecord value = personVm.getValue(caseSigConceptRecord.uuid());
System.out.println("Case significance: %s".formatted(value.getFullName()));

Output is the following:

Case significance: Capitalize initial character
@carldea carldea self-assigned this Jun 17, 2024
@carldea carldea added the enhancement New feature or request label Jun 17, 2024
carldea pushed a commit that referenced this issue Jun 17, 2024
…n. This allows fields to reference a data object instead of just a property name as a string.
carldea pushed a commit that referenced this issue Jun 17, 2024
…n. This allows fields to reference a data object instead of just a property name as a string. Updated tests.
carldea pushed a commit that referenced this issue Jun 18, 2024
…n. This allows fields to reference a data object instead of just a property name as a string. Updated tests. Updated string interpolator for property ids
carldea pushed a commit that referenced this issue Jun 23, 2024
…n. This allows fields to reference a data object instead of just a property name as a string. Updated tests. Updated string interpolator for property ids. Update Javadoc documentation for required warnings
carldea pushed a commit that referenced this issue Jun 24, 2024
…n. This allows fields to reference a data object instead of just a property name as a string. Updated tests. Updated string interpolator for property ids. Update Javadoc documentation for required warnings
@carldea carldea linked a pull request Jun 24, 2024 that will close this issue
carldea pushed a commit that referenced this issue Jun 24, 2024
…n. This allows fields to reference a data object instead of just a property name as a string. Add license to files.
@carldea carldea changed the title Instead of a string as a key to lookup Property and Model values can we enhance using a Property Identifier type object. Instead of a string as a key to lookup Property and Model values can we enhance using a Property Identifier type object? Jun 26, 2024
@carldea carldea changed the title Instead of a string as a key to lookup Property and Model values can we enhance using a Property Identifier type object? Instead of a string as a key to lookup Property and Model values can we enhance it using a Property Identifier type object? Jun 26, 2024
carldea pushed a commit that referenced this issue Jun 28, 2024
…n. This allows fields to reference a data object instead of just a property name as a string. Add license to files. Updated docs.
@carldea carldea pinned this issue Jun 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant