How to register and use a characteristic value provider with parameters

Introduction

Use the SDK to create new plugin, contribute value provider implementation and add value provider to a characteristic.

Create Plugin using SDK

Create new plug-in project similar to "com.heiler.ppm.customizing.sample.valueProvider" and add required dependencies.
Sample plugin is present under sdk→examples→customizing.

Add Extension in MANIFEST.MF

Open Extensions

add extension for "com.heiler.ppm.charateristic.core.characteristicsValueProvider"

  • Add valueProvider

    • Identifier : intgerValueProvider

    • Label : %valueProvider.text (Localized text)

    • dataType : INTEGER

    • providerFactory : SampleIntegerCharacteristicValueProviderFactory

images/download/attachments/487803643/image2022-5-9_14-52-27.png

Contribute the characteristic value provider

Create class with name: "SampleIntegerCharacteristicValueProviderFactory" in package: "com.heiler.ppm.customizing.sample.valueProvider.integer", implements interface: "CharacteristicValueProviderFactory".

CharacteristicValueProviderFactory: Creates new instance of CharacteristicValueProvider.

SampleIntegerCharacteristicValueProviderFactory
import com.heiler.ppm.characteristic.core.valueProvider.CharacteristicValueProvider;
import com.heiler.ppm.characteristic.core.valueProvider.CharacteristicValueProviderFactory;
import com.heiler.ppm.std.core.entity.list.EntityItemList;
 
public class SampleIntegerCharacteristicValueProviderFactory implements CharacteristicValueProviderFactory
{
 
@Override
public CharacteristicValueProvider createValueProvider( EntityItemList itemList )
{
return new SampleIntegerCharacteristicValueProvider( itemList );
}
 
}

Create class with name "SampleIntegerCharacteristicValueProvider" in package "com.heiler.ppm.customizing.sample.valueProvider.integer", implements interface: "CharacteristicValueProvider".

CharacteriticValueProvider: Provides characteristic values and check for a valid value.

Provide the implementation of interface Value inside the SampleIntegerCharacteristicValueProvider itself.

SampleIntegerCharacteristicValueProvider
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
 
import com.heiler.ppm.characteristic.core.record.CharacteristicRecord;
import com.heiler.ppm.characteristic.core.valueProvider.CharacteristicValueProvider;
import com.heiler.ppm.lookup.core.LookupValue;
import com.heiler.ppm.std.core.entity.list.EntityItemList;
 
@SuppressWarnings( "unused" )
public class SampleIntegerCharacteristicValueProvider implements CharacteristicValueProvider
{
 
public SampleIntegerCharacteristicValueProvider( EntityItemList itemList )
{
//
}
 
@Override
public Collection< Value > getValues( LookupValue language, CharacteristicRecord charRecord )
{
List< Value > values = createValues();
return values;
}
 
@Override
public boolean containsValue( LookupValue language, CharacteristicRecord charRecord,
Object value /*actual charateristic record value*/ )
{
// make sure values are of datatype Long for Integer type characteristics
Set< Long > values = new HashSet();
values.add( 1024L );
values.add( 2048L );
values.add( 4096L );
return values.contains( value );
}
 
@SuppressWarnings( "nls" )
private List< Value > createValues()
{
List< Value > values = new ArrayList();
// make sure values are of datatype Long for Integer type characteristics
Value v1 = new ValueImpl( 1024L, "1 MB" );
Value v2 = new ValueImpl( 2048L, "2 MB" );
Value v3 = new ValueImpl( 4096L, "4 MB" );
values.add( v1 );
values.add( v2 );
values.add( v3 );
return values;
}
 
public static class ValueImpl implements Value
{
private String label;
private Object value;
 
public ValueImpl( Object value, String label )
{
this.value = value;
this.label = label;
 
}
 
@Override
public Object getValue()
{
return this.value;
}
 
@Override
public String getLabel( Locale locale )
{
return this.label;
}
}
}

Deploy the plugin and restart Product 360 server

export the plugin as deployable plug-ins and fragments.

images/download/attachments/487803643/image2021-7-13_14-37-26.png

Deploy exported jar inside server → plugins folder

We are good to start the server

Attaching value provider to a characteristic

Characteristic value provider can be attached In characteristic modeling detail view.

images/download/attachments/487803643/image2022-6-8_12-2-0.png

Characteristic UI showing the provided values

Provided values will be available to select in the dropdown.

images/download/attachments/487803643/image2021-7-9_9-30-34.png

DepedentLookup Characteristic

Introduction

Create lookups lookup values

  • Country

  • State

Setup of Lookup Value Reference

referencing lookup

referenced lookup

business case

Country

State

A country can have multiple states

Navigate to lookup context view

Add the referenced values for desired lookup value.

There are two ways you can add referenced values for a lookup value:

  • Include values table : Only selected values can be referenced.

  • Exclude values table : All the values can be referenced except selected values.

images/download/attachments/487803643/image2022-5-9_15-59-1.png

Refer Importing lookups and lookup value references documentation for importing lookup references.

Contribute the country to state dependent characteristics to value provider

class name: "ParentReferencingChildLookupValueProviderFactory" package: "com.heiler.ppm.customizing.sample.valueProvider.parentchild" interface: "CharacteristicValueProviderFactory".

ParentReferencingChildLookupValueProviderFactory
package com.heiler.ppm.customizing.sample.valueProvider.parentchild;
 
import com.heiler.ppm.characteristic.core.valueProvider.CharacteristicValueProvider;
import com.heiler.ppm.characteristic.core.valueProvider.CharacteristicValueProviderFactory;
import com.heiler.ppm.std.core.entity.list.EntityItemList;
 
public class ParentReferencingChildLookupValueProviderFactory implements CharacteristicValueProviderFactory
{
 
@Override
public CharacteristicValueProvider createValueProvider( EntityItemList itemList )
{
return new ParentReferencingChildLookupValueProvider( itemList );
}
 
}

class name: "ParentReferencingChildLookupValueProvider" package: "com.heiler.ppm.customizing.sample.valueProvider.parentchild" interface: "CharacteristicValueProvider".

ParentReferencingChildLookupValueProvider
package com.heiler.ppm.customizing.sample.valueProvider.parentchild;
 
import java.util.Collection;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.stream.Collectors;
 
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.core.runtime.CoreException;
 
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.heiler.ppm.characteristic.core.record.CharacteristicRecord;
import com.heiler.ppm.characteristic.core.valueProvider.CharacteristicValueProvider;
import com.heiler.ppm.commons.RuntimeCoreException;
import com.heiler.ppm.lookup.core.Lookup;
import com.heiler.ppm.lookup.core.LookupValue;
import com.heiler.ppm.security.core.SecurityComponent;
import com.heiler.ppm.std.core.entity.list.EntityItemList;
 
public class ParentReferencingChildLookupValueProvider implements CharacteristicValueProvider
{
 
private Locale locale;
private static final String PARENT_CHARACTERISTIC = "parentCharacteristic"; //$NON-NLS-1$
private static final Log LOG = LogFactory.getLog( ParentReferencingChildLookupValueProvider.class );
 
public ParentReferencingChildLookupValueProvider( @SuppressWarnings( "unused" ) EntityItemList itemList )
{
this.locale = SecurityComponent.getImpersonator()
.getLoginToken()
.getLocale();
}
 
@Override
public Collection getValues( LookupValue language, CharacteristicRecord characteristicRecord )
{
Set< LookupValue > dependentLookupValues = fetchDependentLookupValues( language, characteristicRecord );
return dependentLookupValues.stream()
.map( lookupValue -> new ValueImpl( lookupValue, lookupValue.getName( this.locale ) ) )
.collect( Collectors.toList() );
}
 
@Override
public boolean containsValue( LookupValue language, CharacteristicRecord characteristicRecord, Object value )
{
Set< LookupValue > dependentLookupValues = fetchDependentLookupValues( language, characteristicRecord );
return dependentLookupValues.contains( value );
}
 
private Set< LookupValue > fetchDependentLookupValues( LookupValue language,
CharacteristicRecord characteristicRecord )
{
Lookup lookup = characteristicRecord.getCharacteristic()
.getLookup();
CharacteristicRecord parent = getParentCharacteristicRecord( characteristicRecord );
Set< LookupValue > values = new HashSet();
if ( parent != null )
{
parent.getValues( language )
.forEach( lookupValue -> {
addLookupValueReferences( lookup, values, lookupValue );
} );
}
//In case parent is null then by default all the values present in the characteristic lookup should be returned
else if ( lookup != null )
{
Collection< LookupValue > lookUpValues = lookup.getValues();
values.addAll( lookUpValues );
}
return values.stream()
.filter( v -> v.isActive() )
.collect( Collectors.toSet() );
}
 
private void addLookupValueReferences( Lookup lookup, Set< LookupValue > values, Object lookupValue )
{
LookupValue parentLookupValue = ( LookupValue ) lookupValue;
try
{
Collection< LookupValue > lookupValueReference = parentLookupValue.getReferences( lookup );
values.addAll( lookupValueReference );
}
catch ( CoreException e )
{
throw new RuntimeCoreException( e );
}
}
 
private CharacteristicRecord getParentCharacteristicRecord( CharacteristicRecord characteristicRecord )
{
CharacteristicRecord parent;
if ( characteristicRecord.getCharacteristic()
.getValueProviderParameter() != null )
{
parent = getRequiredCharacteristicRecord( characteristicRecord, characteristicRecord.getCharacteristic()
.getValueProviderParameter() );
}
else
{
parent = characteristicRecord.getParent();
}
return parent;
}
 
private CharacteristicRecord getRequiredCharacteristicRecord( CharacteristicRecord characteristicRecord,
String parameter )
{
CharacteristicRecord parent = characteristicRecord.getParent();
JsonObject charParameter = parseStringtoJson( parameter );
String parentRefParam = ( charParameter != null
&& charParameter.get( PARENT_CHARACTERISTIC ) != null ) ? charParameter.get( PARENT_CHARACTERISTIC )
.getAsString()
: null;
return getParent( parent, parentRefParam );
}
 
private CharacteristicRecord getParent( CharacteristicRecord parent, String parentRefParam )
{
if ( parent == null )
{
return parent;
}
if ( parentRefParam != null && parent.getCharacteristic()
.getIdentifier()
.equals( parentRefParam ) )
{
return parent;
}
return getParent( parent.getParent(), parentRefParam );
}
 
private JsonObject parseStringtoJson( String input )
{
JsonObject jsonObject = null;
JsonParser parser = new JsonParser();
try
{
JsonElement element = parser.parse( input );
if ( element.isJsonObject() )
{
jsonObject = element.getAsJsonObject();
}
}
catch ( Exception e )
{
LOG.error( e.getMessage() );
}
return jsonObject;
}
 
public static class ValueImpl implements Value
{
private String label;
private Object value;
 
public ValueImpl( Object value, String label )
{
this.value = value;
this.label = label;
 
}
 
@Override
public Object getValue()
{
return this.value;
}
 
@Override
public String getLabel( Locale locale )
{
return this.label;
}
}
}

class name: "ParentReferencingChildLookupValueProviderParameter" package: "com.heiler.ppm.customizing.sample.valueProvider.parentchild" interface: "ValueProviderParameter".

ParentReferencingChildLookupValueProviderParameter
package com.heiler.ppm.customizing.sample.valueProvider.parentchild;
 
import java.util.ArrayList;
import java.util.Collection;
import java.util.Locale;
 
import com.heiler.ppm.characteristic.core.model.Characteristic;
import com.heiler.ppm.characteristic.core.record.CharacteristicRecord;
import com.heiler.ppm.characteristic.core.valueProvider.ValueProviderParameter;
import com.heiler.ppm.lookup.core.LookupValue;
 
/**
* @author rkumhar
*/
public class ParentReferencingChildLookupValueProviderParameter implements ValueProviderParameter
{
 
@Override
public Collection getParameters( LookupValue language, CharacteristicRecord characteristicRecord )
{
Collection< ParameterValue > paramList = new ArrayList< ParameterValue >();
 
Collection< Characteristic > parentList = new ArrayList<>();
Characteristic characteristic = characteristicRecord.getCharacteristic();
checkForParent( characteristic.getParent(), parentList );
 
parentList.forEach( parentCharacteristic -> paramList.add( new ParameterValueImpl( parentCharacteristic.getIdentifier(),
parentCharacteristic.getName( Locale.ENGLISH ) ) ) );
 
return paramList;
}
 
Collection< CharacteristicRecord > checkForParent( CharacteristicRecord characteristicRecord,
Collection< CharacteristicRecord > parentCharRecordSetList )
{
if ( characteristicRecord != null )
{
parentCharRecordSetList.add( characteristicRecord );
 
if ( characteristicRecord.getParent() != null )
{
checkForParent( characteristicRecord.getParent(), parentCharRecordSetList );
}
}
 
return parentCharRecordSetList;
 
}
 
Collection< Characteristic > checkForParent( Characteristic parentCharacteristic,
Collection< Characteristic > parentCharList )
{
if ( parentCharacteristic != null )
{
parentCharList.add( parentCharacteristic );
 
if ( parentCharacteristic.getParent() != null )
{
checkForParent( parentCharacteristic.getParent(), parentCharList );
}
}
 
return parentCharList;
 
}
 
public static class ParameterValueImpl implements ParameterValue
{
private String label;
private Object value;
 
public ParameterValueImpl( Object value, String label )
{
this.value = value;
this.label = label;
 
}
 
@Override
public Object getValue()
{
return this.value;
}
 
@Override
public String getLabel( Locale locale )
{
return this.label;
}
}
}

Characteristic modeling:

Create characteristics like the following:

Characteristic

Name

Identifier

Data type

Lookup

Parent characteristic

Country -> state

Country_to_State

Lookup value

Country

Child characteristic

Parent referencing child lookup

parent_referencing_child_lookup

Lookup value

State

Assigning value provider to characteristics in modelling UI

  • Value provider dropdown lists all the contributed value providers.

  • Parameter dropdown list appropriate values for corresponding value provider

images/download/attachments/487803643/image2022-5-9_16-12-42.png

Characteristic UI showing the provided values

Characteristic showing filtered values of state lookup.

images/download/attachments/487803643/image2022-5-9_16-21-46.png