Advanced Customization > Business Logic Customization > Customizing UDI Super Set > Attribute Populators for New UDI Super Sets
Attribute Populators for New UDI Super Sets
This chapter describes how to extend the out-of-the-box UDI Super Set attribute populators and add custom attribute population for UDI Super Sets and UDI Super Set Details.
The com.ptc.qualitymanagement.udi.superset package contains two out-of-the-box registered attribute populators. These populators are used to create UDI Super Sets when a template is specified. The package also provides a method that can be overridden to enable a custom attribute population. The attributes to be overwritten can be derived from the subject part and can include the creation of additional UDI Super Set Details.
The following sections describe how to create custom logic for populating attributes on UDI Super Sets when they are created. This involves two main steps:
Creating a custom class that extends an OOTB UDI Super Set attribute populator
Creating an xconf entry to register your custom class
Prerequisites
Knowledge of Java classes and basic concepts such as class extension
Knowledge of the xconf mechanism to register service classes
The description of the various solution elements is given in the table below.
Element
Type
Description
com.ptc.qualitymanagement.udi.superset.StandardUdiSuperSetAttributePopulator
Java Class
OOTB populator registered to populate attributes from a template when creating a simple non-revisable UDI Super Set (UdiSuperSet).
com.ptc.qualitymanagement.udi.superset.StandardUdiSuperSet2AttributePopulator
Java Class
OOTB populator registered to populate attributes from a template when creating a revisable UDI Super Set (UdiSuperSet2).
populateFromAttributes(UDISubmission submission, Map attributes)
API
Populates attributes to a UDISubmission based on a map containing attribute names and attribute values.
createUDISubmissionDetail(UDISubmission submission, String typeName, Map attributes)
API
Creates a new UdiSuperSetDetail related to a UDISubmission based on a UdiSuperSetDetail type and a map containing attribute names and attribute values.
createUDIPackagingDetail(UDISubmission submission, UDIPackaging parent, Map attributes)
API
Creates a new UDIPackaging related to a UDISubmission based on the parent UDIPackaging and a map containing attribute names and attribute values.
populateFromSouce(UDISubmission submission, Map attributes)
API
The method to be overridden in custom classes. It extends the OOTB attribute populators and is used to enable customizations.
getPart()
API
Returns the WTPart which is the Subject of the UdiSuperSet2. Used in the overridden populateFromSource method to refer the WTPart for attribute population.
getAttributeValue(Typed typedObject, String attributeName)
API
Returns the value of a hard or soft attribute defined on a typed object.
Creating Custom Class
To customize attribute population when creating a simple UdiSuperSet or a Revisable UdiSuperSet2, you need to create a new Java class (your populator) that extends the StandardUdiSuperSetAttributePopulator or the StandardUdiSuperSet2AttributePopulator and overrides the populateFromSource method. The example of a new custom class with the minimum requirements of the overridden populateFromSource method is given below.
public class CustomStandardUdiSuperSet2AttributePopulator extends StandardUdiSuperSet2AttributePopulator {
/**
* @param submission
* @param attributes
* @return details
* @throws WTException
* @throws WTPropertyVetoException
*/
@Override
public List <UDISubmissionDetailsIfc> populateFromSouce(UDISubmission submission,Map attributes) throws WTPropertyVetoException, WTException {
/*
* Overriding this method provides the means to add custom attribute
* population to any hard or soft attribute on the UdiSuperSet or
* UdiSuperSet2 passed into this method, and also adds a means to create and populate
* UdiSuperSetDetails for any hard or soft Type defined for UdiSuperSetDetails
*
* This method returns a List of UDISubmissionDetailsIfc and
* as such the details List should be created in this method and
* returned so that the UdiSuperSetDetails can be persisted.
* Created UdiSuperSetDetails should be added to the details list.
*/
List <UDISubmissionDetailsIfc> details = new ArrayList<>();
return details;
}
}
You can add logic to populate attributes to a UDISubmission from the Part object by using the getPart() API. An example of overriding the populateFromSource method for custom population of attributes from a Part is given below.
@Override
public List<UDISubmissionDetailsIfc> populateFromSouce(UDISubmission submission, Map attributes) throws WTPropertyVetoException, WTException {
/*
* The getPart() API retrieves the WTPart that was set in the StandardUDICreatorService.
* Attributes from the Part can be used to populate attributes defined on the UDI.
* In this example the attributes brandName, modelNumber, lotControlled, and serialControlled
* are placed into a map for population. The populateFromAttributes API is used
* to load the attributes to the UDISubmission Type.
*/
List<UDISubmissionDetailsIfc> details = new ArrayList<>();
WTPart thePart = getPart();
Map<String, Object> sourceAttributes = new HashMap<>();
sourceAttributes.put("brandName", thePart.getName());
StandardVersionedDisplayIdentity verId = (StandardVersionedDisplayIdentity) ((RevisionControlled) thePart).getDisplayIdentity();
LocalizableMessage identifier = verId.getIterationDisplayIdentity().getDisplayIdentifier();
String modelNumber = thePart.getNumber() + ", " + identifier.getLocalizedMessage(SessionHelper.getLocale());
sourceAttributes.put("modelNumber", modelNumber);
TraceCode defaultTraceCode = thePart.getDefaultTraceCode();
if (TraceCode.LOT_NUMBER.equals(defaultTraceCode) || TraceCode.LOT_SERIAL_NUMBER.equals(defaultTraceCode)) {
sourceAttributes.put("lotControlled", true);
}
if (TraceCode.SERIAL_NUMBER.equals(defaultTraceCode) || TraceCode.LOT_SERIAL_NUMBER.equals(defaultTraceCode)) {
sourceAttributes.put("serialControlled", true);
}
populateFromAttributes(submission, sourceAttributes);

return details;
}
You can use the getAttributeValue API to retrieve a hard or soft attribute value that can be populated to attributes defined on the UDISubmission. An example is given below.
@Override
public List<UDISubmissionDetailsIfc> populateFromSouce(UDISubmission submission, Map attributes)
throws WTPropertyVetoException, WTException {
/*
* The getAttributeValue(Typed typedObject, String attributeName) API retrieves the value
* of a hard or soft attribute from a Typed object. In this example there is a Soft
* attribute named reusableAtt1 which is retrieved from the Part and set on the UDI
* brandName attribute. The contractNumber hard attribute is retreived from the Part
* and set on the UDI catalogNumber.
*/
List<UDISubmissionDetailsIfc> details = new ArrayList<>();
WTPart thePart = getPart();
Object partBrandName = getAttributeValue(thePart, "reusableAtt1");
Object partContractNumber = getAttributeValue(thePart, "contractNumber");
Map<String, Object> sourceAttributes = new HashMap<>();

sourceAttributes.put("brandName", partBrandName);
sourceAttributes.put("catalogNumber", partContractNumber);
populateFromAttributes(submission, sourceAttributes);

return details;
}
You can use the createUDISubmissionDetail API to create and populate a UDISubmissionDetail for any UDISubmissionDetail type other than UDIPackaging. An example is given below.
@Override
public List<UDISubmissionDetailsIfc> populateFromSource(UDISubmission submission, Map attributes)
throws WTPropertyVetoException, WTException {
/*
* The createUDISubmissionDetail(UDISubmission submission, String typeName,
* Map<String, Object> attributes) API provides a method to create and
* populate a UDISubmissionDetail object for any UDISubmissionDetail Type
* other than UDIPackaging.
* In this example a UDISubmissionDetail object is created for the
* com.ptc.qualitymanagement.udi.superset.FDAListingNumber Type, populating the
* udiss_fdaListingNumber attribute, and a UDISubmissionDetail object is created
* for the com.ptc.qualitymanagement.udi.superset.FDAPremarketAuthorizationNumber
* Type, populating the udiss_fdaPremarketAuthorizationNumber attribute and
* udiss_fdaBadSupplementNumber attribute
*/
List<UDISubmissionDetailsIfc> details = new ArrayList<>();
Map<String, Object> detailsAttributes = new HashMap<>();
UDISubmissionDetailsIfc detail = null;
detailsAttributes.put("udiss_fdaListingNumber", "12345");
detail = createUDISubmissionDetail(submission,
"com.ptc.qualitymanagement.udi.superset.FDAListingNumber",
detailsAttributes);
if (detail != null) {
details.add(detail);
}

detailsAttributes = new HashMap<>();
detailsAttributes.put("udiss_fdaPremarketAuthorizationNumber", "98765");
detailsAttributes.put("udiss_fdaBadSupplementNumber", "54321");
detail = createUDISubmissionDetail(submission,
"com.ptc.qualitymanagement.udi.superset.FDAPremarketAuthorizationNumber",
detailsAttributes);
if (detail != null) {
details.add(detail);
}

return details;
}
You can use the createUDIPackagingDetail API to create the UDIPackaging structure for a UDISubmission. The UDISUbmission, parent UDIPackaging object, and an attributes map are passed to this API. The parent object is always null for the top UDIPackaging object, and the appropriate parent object is passed to the subsequent calls to createUDIPackagingDetail to establish the references. An example of a packaging structure created for Gloves that are contained by a Box, which is contained by a Carton is given below.
@Override
public List<UDISubmissionDetailsIfc> populateFromSouce(UDISubmission submission, Map attributes)
throws WTPropertyVetoException, WTException {
/*
* The createUDIPackagingDetail(UDISubmission submission, UDIPackaging parent,
* Map<String, Object> attributes) API provides a method to create and
* populate UDIPackaging objects.
* In this example a UDIPackaging object is created for the
* Top packaging object, gloves. with a null Parent reference.
* A packaging object is then created passing the gloves packaging object
* as the parent, which creates a Box packaging object that contains a
* number of Gloves. Then a packaging object is created passing the Box packaging object
* as the parent, which creates a Carton packaging object that contains a
* number of Boxes
*/
List<UDISubmissionDetailsIfc> details = new ArrayList<>();
Map<String, Object> detailsAttributes = new HashMap<>();
detailsAttributes = new HashMap<>();
detailsAttributes.put("packageDescription", "Gloves");
detailsAttributes.put("packageDeviceIdentifer", "98765432101256");
detailsAttributes.put("packageQuantity", "1");
Date date = new Date();
detailsAttributes.put("discontinuedDate", new Timestamp(date.getTime()));
UDISubmissionDetailsIfc parent = createUDIPackagingDetail(submission, null, detailsAttributes);
if (parent != null) {
details.add(parent);
}

detailsAttributes = new HashMap<>();
detailsAttributes.put("packageDescription", "Box");
detailsAttributes.put("packageDeviceIdentifer", "96385274102589");
detailsAttributes.put("packageQuantity", "120");
UDISubmissionDetailsIfc glovesBox = createUDIPackagingDetail(submission, (UDIPackaging) parent, detailsAttributes);
if (glovesBox != null) {
details.add(glovesBox);
}

detailsAttributes = new HashMap<>();
detailsAttributes.put("packageDescription", "Carton");
detailsAttributes.put("packageDeviceIdentifer", "35715965485214");
detailsAttributes.put("packageQuantity", "50");
UDISubmissionDetailsIfc glovesCarton = createUDIPackagingDetail(submission,
(UDIPackaging) glovesBox, detailsAttributes);
if (glovesCarton != null) {
details.add(glovesCarton);
}

return details;
}
Creating XConf Entry
To register custom populators relative to specific UdiSuperSet or UdiSuperSet2 types and subtypes, create entries in a custom service.properties.xconf file.
An example of an xconf entry to register against UdiSuperSet:
<Service name="com.ptc.qualitymanagement.udi.UDIAttributePopulatorService">
<Option
requestor="null"
serviceClass="com.ptc.qualitymanagement.udi.superset.CustomUdiSuperSetAttributePopulator"
selector="com.ptc.qualitymanagement.udi.superset.UdiSuperSet"
cardinality="duplicate"
/>
</Service>
An example of an xconf entry to register against UdiSuperSet2:
<Service name="com.ptc.qualitymanagement.udi.UDIAttributePopulatorService">
<Option
requestor="null"
serviceClass="com.ptc.qualitymanagement.udi.superset.CustomUdiSuperSet2AttributePopulator"
selector="com.ptc.qualitymanagement.udi.superset.UdiSuperSet2"
cardinality="duplicate"
/>
</Service>
An example of an xconf entry to register against UdiSuperSet subtype:
<Service name="com.ptc.qualitymanagement.udi.UDIAttributePopulatorService">
<Option
requestor="null"
serviceClass="com.ptc.qualitymanagement.udi.superset.CustomUdiSuperSetSubType1AttributePopulator"
selector="com.ptc.qualitymanagement.udi.superset.UdiSuperSet|com.acme.UdiSuperSetSubtype1"
cardinality="duplicate"
/>
</Service>
An example of an xconf entry to register against UdiSuperSet2 subtype:
<Service name="com.ptc.qualitymanagement.udi.UDIAttributePopulatorService">
<Option
requestor="null"
serviceClass="com.ptc.qualitymanagement.udi.superset.CustomUdiSuperSet2SubType1AttributePopulator"
selector="com.ptc.qualitymanagement.udi.superset.UdiSuperSet2|com.acme.UdiSuperSet2Subtype1"
cardinality="duplicate"
/>
</Service>
* 
If a service class does not exist for a subtype, the parent types of the subtype are checked until a Service Class is found. If a service class is not found, a trace log entry reports on the class and type with no service class defined. Turn on TRACE logging for com.ptc.qualitymanagement.qms.util.helper.QMSServiceClassHelper to display the log entry. If no service class is found then an attribute populator will not be invoked.
If any attribute name configured for population in the overridden populateFromSource method does not exist for the type being processed, the attribute is skipped and an error with the attribute and type information is logged.
Was this helpful?