class ProtocolFactory {

    @SuppressWarnings({"squid:S1166"})
    public static TopicPath newTopicPath(final String path) {
        checkNotNull(path, "path");
        final LinkedList<String> parts = new LinkedList<>(Arrays.asList(path.split(TopicPath.PATH_DELIMITER)));

        try {
            final String namespace = parts.pop(); // parts[0]
            final String id = parts.pop(); // parts[1]
            final TopicPath.Group group =
                    TopicPath.Group.forName(parts.pop()) // parts[2]
                            .orElseThrow(() -> UnknownTopicPathException.newBuilder(path).build());

            final TopicPath.Channel channel;
            switch (group) {
                case POLICIES:
                    channel = TopicPath.Channel.NONE;
                    break;
                case THINGS:
                    channel = TopicPath.Channel.forName(parts.pop()) // parts[3]
                            .orElseThrow(() -> UnknownTopicPathException.newBuilder(path).build());
                    break;
                default:
                    throw UnknownTopicPathException.newBuilder(path).build();
            }

            final TopicPath.Criterion criterion =
                    TopicPath.Criterion.forName(parts.pop())
                            .orElseThrow(() -> UnknownTopicPathException.newBuilder(path).build());

            switch (criterion) {
                case COMMANDS:
                case EVENTS:
                    // commands and events Path always contain an ID:
                    final TopicPath.Action action =
                            TopicPath.Action.forName(parts.pop())
                                    .orElseThrow(() -> UnknownTopicPathException.newBuilder(path).build());
                    return ImmutableTopicPath.of(namespace, id, group, channel, criterion, action);
                case SEARCH:
                    final TopicPath.SearchAction searchAction =
                            TopicPath.SearchAction.forName(parts.pop())
                                    .orElseThrow(() -> UnknownTopicPathException.newBuilder(path).build());
                    return ImmutableTopicPath.of(namespace, id, group, channel, criterion, searchAction);
                case ERRORS:
                    // errors Path does neither contain an "action":
                    return ImmutableTopicPath.of(namespace, id, group, channel, criterion);
                case MESSAGES:
                case ACKS:
                    // messages should always contain a non-empty subject:
                    // ACK Paths contain a custom acknowledgement label or an empty subject for aggregated ACKs:
                    final String subject = String.join(TopicPath.PATH_DELIMITER, parts);
                    if (subject.isEmpty()) {
                        return ImmutableTopicPath.of(namespace, id, group, channel, criterion);
                    } else {
                        return ImmutableTopicPath.of(namespace, id, group, channel, criterion, subject);
                    }
                default:
                    throw UnknownTopicPathException.newBuilder(path).build();
            }
        } catch (final NoSuchElementException e) {
            throw UnknownTopicPathException.newBuilder(path).build();
        }
    }

    private  ProtocolFactory();

}