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

Allow ProfileTypeBuilder to pass its three filters list to both implementations of ProfileType #4325

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
import org.openhab.core.thing.dto.ChannelTypeDTO;
import org.openhab.core.thing.profiles.ProfileType;
import org.openhab.core.thing.profiles.ProfileTypeRegistry;
import org.openhab.core.thing.profiles.TriggerProfileType;
import org.openhab.core.thing.type.ChannelKind;
import org.openhab.core.thing.type.ChannelType;
import org.openhab.core.thing.type.ChannelTypeRegistry;
Expand Down Expand Up @@ -174,10 +173,8 @@ public Response getLinkableItemTypes(

Set<String> result = new HashSet<>();
for (ProfileType profileType : profileTypeRegistry.getProfileTypes()) {
if (profileType instanceof TriggerProfileType type) {
if (type.getSupportedChannelTypeUIDs().contains(ctUID)) {
result.addAll(profileType.getSupportedItemTypes());
}
if (profileType.getSupportedChannelTypeUIDs().contains(ctUID)) {
result.addAll(profileType.getSupportedItemTypes());
}
}
if (result.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,10 @@ public Response link(@PathParam("itemName") @Parameter(description = "itemName")
}
if (!(profileType.getSupportedItemTypes().isEmpty()
|| profileType.getSupportedItemTypes().contains(itemType))
|| !(((TriggerProfileType) profileType).getSupportedChannelTypeUIDs().isEmpty()
|| ((TriggerProfileType) profileType).getSupportedChannelTypeUIDs()
.contains(channel.getChannelTypeUID()))) {
|| !(profileType.getSupportedChannelTypeUIDs().isEmpty()
|| profileType.getSupportedChannelTypeUIDs().contains(channel.getChannelTypeUID()))
|| !(profileType.getSupportedItemTypesOfChannel().isEmpty()
|| profileType.getSupportedItemTypesOfChannel().contains(itemType))) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That looks really horrible. It would probably be better to

   Collection<String> supportedItemTypes = profileType.getSupportedItemTypes();
   ...
   if ((!supportedItemType.isEmpty() && !supportedItemTypes.contains(itemType)) || ...) {

Why do you check profileType.getSupportedItemTypesOfChannel? A profile can change the item-type:

  • The timestamp update profile works with all channel-types (e.g. a humidity channel with supported item-type Number:Dimensionless) but can only be linked to DateTime items. So a check would fail (in fact, currently the profile does not provide information about accepted channel types, so this will not cause an issue, but in principle it could).
  • A profile could map numeric values to strings, so the channel would support Number, but the profile String. In this case you could never link them.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that it looks horrible, but it was jut to "fit in" with the already existing code.

As to why I do the check, well it's because I did a global search for the interface methods and made sure both methods are called in all the places where one was called following an instanceof call.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, the style was not good before either and if you want to keep it: ok. But the getSupportedItemTypesOfChannel is excluding valid combinations.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm definitely in favor of changing the code style here, but as always when I'm submitting a pull request, I try to limit my changes to the code logic, adapting to the local style I encounter.
I'll try to come up with a more sensible way of writing this up.

// item or channel type not matching
return Response.status(Status.BAD_REQUEST).build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,9 @@
import org.openhab.core.items.ItemUtil;
import org.openhab.core.thing.profiles.ProfileType;
import org.openhab.core.thing.profiles.ProfileTypeRegistry;
import org.openhab.core.thing.profiles.StateProfileType;
import org.openhab.core.thing.profiles.TriggerProfileType;
import org.openhab.core.thing.profiles.dto.ProfileTypeDTO;
import org.openhab.core.thing.profiles.dto.ProfileTypeDTOMapper;
import org.openhab.core.thing.type.ChannelKind;
import org.openhab.core.thing.type.ChannelType;
import org.openhab.core.thing.type.ChannelTypeRegistry;
import org.openhab.core.thing.type.ChannelTypeUID;
Expand Down Expand Up @@ -128,13 +127,7 @@ private Predicate<ProfileType> matchesChannelUID(@Nullable String channelTypeUID
// requested to filter against an unknown channel type -> do not return a ProfileType
return t -> false;
}
switch (channelType.getKind()) {
case STATE:
return t -> stateProfileMatchesProfileType(t, channelType);
case TRIGGER:
return t -> triggerProfileMatchesProfileType(t, channelType);
}
return t -> false;
return t -> profileTypeMatchesChannelType(t, channelType);
J-N-K marked this conversation as resolved.
Show resolved Hide resolved
}

private Predicate<ProfileType> matchesItemType(@Nullable String itemType) {
Expand All @@ -151,31 +144,26 @@ private boolean profileTypeMatchesItemType(ProfileType pt, String itemType) {
|| supportedItemTypesOnProfileType.contains(itemType);
}

private boolean triggerProfileMatchesProfileType(ProfileType profileType, ChannelType channelType) {
if (profileType instanceof TriggerProfileType triggerProfileType) {
if (triggerProfileType.getSupportedChannelTypeUIDs().isEmpty()) {
return true;
}
private boolean profileTypeMatchesChannelType(ProfileType profileType, ChannelType channelType) {
@Nullable
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need @Nullable annotation for local variables.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My IDE (VSCode) disagrees and outputs a warning saying that the value cannot be null. That's what I've been doing in my plugin bundles, but I can remove it if you prefer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO it's wrong. The default locations for @NonNullByDefault are PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT, so it does not apply to local variables.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough, I have removed them

ChannelKind supportedChannelKind = profileType.getSupportedChannelKind();
if (supportedChannelKind != null && supportedChannelKind != channelType.getKind())
return false;

if (triggerProfileType.getSupportedChannelTypeUIDs().contains(channelType.getUID())) {
return true;
}
Collection<ChannelTypeUID> supportedChannelTypeUIDsOnProfileType = profileType.getSupportedChannelTypeUIDs();
if (!supportedChannelTypeUIDsOnProfileType.isEmpty()
&& !supportedChannelTypeUIDsOnProfileType.contains(channelType.getUID())) {
return false;
}
return false;
}

private boolean stateProfileMatchesProfileType(ProfileType profileType, ChannelType channelType) {
if (profileType instanceof StateProfileType stateProfileType) {
if (stateProfileType.getSupportedItemTypesOfChannel().isEmpty()) {
return true;
}

Collection<String> supportedItemTypesOfChannelOnProfileType = stateProfileType
.getSupportedItemTypesOfChannel();
Collection<String> supportedItemTypesOfChannelOnProfileType = profileType.getSupportedItemTypesOfChannel();
if (supportedItemTypesOfChannelOnProfileType.isEmpty()) {
return true;
} else {
@Nullable
String itemType = channelType.getItemType();
return itemType != null
&& supportedItemTypesOfChannelOnProfileType.contains(ItemUtil.getMainItemType(itemType));
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,23 @@
import org.openhab.core.config.core.ParameterOption;
import org.openhab.core.items.Item;
import org.openhab.core.items.ItemRegistry;
import org.openhab.core.items.ItemUtil;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingRegistry;
import org.openhab.core.thing.link.ItemChannelLink;
import org.openhab.core.thing.link.ItemChannelLinkRegistry;
import org.openhab.core.thing.profiles.ProfileType;
import org.openhab.core.thing.profiles.ProfileTypeRegistry;
import org.openhab.core.thing.profiles.StateProfileType;
import org.openhab.core.thing.profiles.TriggerProfileType;
import org.openhab.core.thing.type.ChannelKind;
import org.openhab.core.thing.type.ChannelType;
import org.openhab.core.thing.type.ChannelTypeRegistry;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Provider for framework config parameters on {@link ItemChannelLink}s.
Expand All @@ -51,20 +56,25 @@
@NonNullByDefault
public class ItemChannelLinkConfigDescriptionProvider implements ConfigDescriptionProvider {

private final Logger logger = LoggerFactory.getLogger(ItemChannelLinkConfigDescriptionProvider.class);

private static final String SCHEME = "link";
public static final String PARAM_PROFILE = "profile";

private final ProfileTypeRegistry profileTypeRegistry;
private final ChannelTypeRegistry channelTypeRegistry;
private final ItemChannelLinkRegistry itemChannelLinkRegistry;
private final ItemRegistry itemRegistry;
private final ThingRegistry thingRegistry;

@Activate
public ItemChannelLinkConfigDescriptionProvider(final @Reference ProfileTypeRegistry profileTypeRegistry, //
final @Reference ChannelTypeRegistry channelTypeRegistry, //
final @Reference ItemChannelLinkRegistry itemChannelLinkRegistry, //
final @Reference ItemRegistry itemRegistry, //
final @Reference ThingRegistry thingRegistry) {
this.profileTypeRegistry = profileTypeRegistry;
this.channelTypeRegistry = channelTypeRegistry;
this.itemChannelLinkRegistry = itemChannelLinkRegistry;
this.itemRegistry = itemRegistry;
this.thingRegistry = thingRegistry;
Expand Down Expand Up @@ -106,15 +116,7 @@ private List<ParameterOption> getOptions(ItemChannelLink link, Item item, Channe
@Nullable Locale locale) {
Collection<ProfileType> profileTypes = profileTypeRegistry.getProfileTypes(locale);
return profileTypes.stream().filter(profileType -> {
switch (channel.getKind()) {
case STATE:
return profileType instanceof StateProfileType && isSupportedItemType(profileType, item);
case TRIGGER:
return profileType instanceof TriggerProfileType tpt && isSupportedItemType(profileType, item)
&& isSupportedChannelType(tpt, channel);
default:
throw new IllegalArgumentException("Unknown channel kind: " + channel.getKind());
}
return isSupportedItemType(profileType, item) && isSupportedChannelType(profileType, channel, locale);
}).map(profileType -> new ParameterOption(profileType.getUID().toString(), profileType.getLabel())).toList();
}

Expand All @@ -123,8 +125,35 @@ private boolean isSupportedItemType(ProfileType profileType, Item item) {
|| profileType.getSupportedItemTypes().contains(item.getType());
}

private boolean isSupportedChannelType(TriggerProfileType profileType, Channel channel) {
return profileType.getSupportedChannelTypeUIDs().isEmpty()
|| profileType.getSupportedChannelTypeUIDs().contains(channel.getChannelTypeUID());
private boolean isSupportedChannelType(ProfileType profileType, Channel channel, @Nullable Locale locale) {
@Nullable
ChannelKind supportedChannelKind = profileType.getSupportedChannelKind();
if (supportedChannelKind != null && supportedChannelKind != channel.getKind())
return false;
J-N-K marked this conversation as resolved.
Show resolved Hide resolved

ChannelTypeUID channelTypeUID = channel.getChannelTypeUID();

Collection<ChannelTypeUID> supportedChannelTypeUIDsOnProfileType = profileType.getSupportedChannelTypeUIDs();
if (!supportedChannelTypeUIDsOnProfileType.isEmpty()
&& !supportedChannelTypeUIDsOnProfileType.contains(channelTypeUID)) {
return false;
J-N-K marked this conversation as resolved.
Show resolved Hide resolved
}

Collection<String> supportedItemTypesOfChannelOnProfileType = profileType.getSupportedItemTypesOfChannel();
if (supportedItemTypesOfChannelOnProfileType.isEmpty()) {
return true;
} else {
ChannelType channelType = channelTypeRegistry.getChannelType(channelTypeUID, locale);
if (channelType == null) {
logger.error("Requested to filter against an unknown channel type: {} is not known to the registry",
channelTypeUID.toString());
return false;
}

@Nullable
String itemType = channelType.getItemType();
return itemType != null
&& supportedItemTypesOfChannelOnProfileType.contains(ItemUtil.getMainItemType(itemType));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@
import java.util.Collections;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.thing.profiles.ProfileTypeUID;
import org.openhab.core.thing.profiles.StateProfileType;
import org.openhab.core.thing.type.ChannelKind;
import org.openhab.core.thing.type.ChannelTypeUID;

/**
* Default implementation of a {@link StateProfileType}.
Expand All @@ -31,25 +34,38 @@ public class StateProfileTypeImpl implements StateProfileType {
private final String label;
private final Collection<String> supportedItemTypes;
private final Collection<String> supportedItemTypesOfChannel;
private final Collection<ChannelTypeUID> supportedChannelTypeUIDs;

public StateProfileTypeImpl(ProfileTypeUID profileTypeUID, String label, Collection<String> supportedItemTypes,
Collection<String> supportedItemTypesOfChannel) {
Collection<String> supportedItemTypesOfChannel, Collection<ChannelTypeUID> supportedChannelTypeUIDs) {
this.profileTypeUID = profileTypeUID;
this.label = label;
this.supportedItemTypes = Collections.unmodifiableCollection(supportedItemTypes);
this.supportedItemTypesOfChannel = Collections.unmodifiableCollection(supportedItemTypesOfChannel);
this.supportedChannelTypeUIDs = Collections.unmodifiableCollection(supportedChannelTypeUIDs);
}

@Override
public ProfileTypeUID getUID() {
return profileTypeUID;
}

@Override
public Collection<ChannelTypeUID> getSupportedChannelTypeUIDs() {
return supportedChannelTypeUIDs;
}

@Override
public Collection<String> getSupportedItemTypes() {
return supportedItemTypes;
}

@Override
@Nullable
public ChannelKind getSupportedChannelKind() {
return ChannelKind.STATE;
}

J-N-K marked this conversation as resolved.
Show resolved Hide resolved
@Override
public String getLabel() {
return label;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
import java.util.Collections;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.thing.profiles.ProfileTypeUID;
import org.openhab.core.thing.profiles.TriggerProfileType;
import org.openhab.core.thing.type.ChannelKind;
import org.openhab.core.thing.type.ChannelTypeUID;

/**
Expand All @@ -31,13 +33,15 @@ public class TriggerProfileTypeImpl implements TriggerProfileType {
private final ProfileTypeUID profileTypeUID;
private final String label;
private final Collection<String> supportedItemTypes;
private final Collection<String> supportedItemTypesOfChannel;
private final Collection<ChannelTypeUID> supportedChannelTypeUIDs;

public TriggerProfileTypeImpl(ProfileTypeUID profileTypeUID, String label, Collection<String> supportedItemTypes,
Collection<ChannelTypeUID> supportedChannelTypeUIDs) {
Collection<String> supportedItemTypesOfChannel, Collection<ChannelTypeUID> supportedChannelTypeUIDs) {
this.profileTypeUID = profileTypeUID;
this.label = label;
this.supportedItemTypes = Collections.unmodifiableCollection(supportedItemTypes);
this.supportedItemTypesOfChannel = Collections.unmodifiableCollection(supportedItemTypesOfChannel);
this.supportedChannelTypeUIDs = Collections.unmodifiableCollection(supportedChannelTypeUIDs);
}

Expand All @@ -60,4 +64,15 @@ public String getLabel() {
public ProfileTypeUID getUID() {
return profileTypeUID;
}

@Override
public Collection<String> getSupportedItemTypesOfChannel() {
return supportedItemTypesOfChannel;
}

@Override
@Nullable
public ChannelKind getSupportedChannelKind() {
return ChannelKind.TRIGGER;
}
J-N-K marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
import java.util.Collection;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.common.registry.Identifiable;
import org.openhab.core.thing.type.ChannelKind;
import org.openhab.core.thing.type.ChannelTypeUID;

/**
* Describes a profile type.
Expand All @@ -26,11 +29,34 @@
public interface ProfileType extends Identifiable<ProfileTypeUID> {

/**
* Get a collection of ItemType names that this profile type supports
*
* @return a collection of item types (may be empty if all are supported)
*/
Collection<String> getSupportedItemTypes();

/**
* Get a collection of ItemType names that a Channel needs to support in order to able to use this ProfileType
*
* @return a collection of supported ItemType names (an empty list means ALL types are supported)
*/
Collection<String> getSupportedItemTypesOfChannel();

/**
* Get a collection of ChannelType UIDs that this profile type supports
*
J-N-K marked this conversation as resolved.
Show resolved Hide resolved
* @return a collection of ChannelTypeUIDs (may be empty if all are supported).
*/
Collection<ChannelTypeUID> getSupportedChannelTypeUIDs();

/**
* Get the ChannelKind this profile type supports.
*
* @return The supported ChannelKind for this profile type. If null then all channel kinds are supported
*/
@Nullable
ChannelKind getSupportedChannelKind();

J-N-K marked this conversation as resolved.
Show resolved Hide resolved
/**
* Get a human readable description.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public static ProfileTypeBuilder<StateProfileType> newState(ProfileTypeUID profi
return new ProfileTypeBuilder<>(profileTypeUID, label,
(leProfileTypeUID, leLabel, leSupportedItemTypes, leSupportedItemTypesOfChannel,
leSupportedChannelTypeUIDs) -> new StateProfileTypeImpl(leProfileTypeUID, leLabel,
leSupportedItemTypes, leSupportedItemTypesOfChannel));
leSupportedItemTypes, leSupportedItemTypesOfChannel, leSupportedChannelTypeUIDs));
}

/**
Expand All @@ -78,7 +78,7 @@ public static ProfileTypeBuilder<TriggerProfileType> newTrigger(ProfileTypeUID p
return new ProfileTypeBuilder<>(profileTypeUID, label,
(leProfileTypeUID, leLabel, leSupportedItemTypes, leSupportedItemTypesOfChannel,
leSupportedChannelTypeUIDs) -> new TriggerProfileTypeImpl(leProfileTypeUID, leLabel,
leSupportedItemTypes, leSupportedChannelTypeUIDs));
leSupportedItemTypes, leSupportedItemTypesOfChannel, leSupportedChannelTypeUIDs));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
*/
package org.openhab.core.thing.profiles;

import java.util.Collection;

import org.eclipse.jdt.annotation.NonNullByDefault;

/**
Expand All @@ -24,11 +22,4 @@
*/
@NonNullByDefault
public interface StateProfileType extends ProfileType {

/**
* Get a collection of ItemType names that a Channel needs to support in order to able to use this ProfileType
*
* @return a collection of supported ItemType names (an empty list means ALL types are supported)
*/
Collection<String> getSupportedItemTypesOfChannel();
}
Loading