All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.kapua.kura.simulator.topic.Topic Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2017 Red Hat Inc and others
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Red Hat Inc - initial API and implementation
 *******************************************************************************/
package org.eclipse.kapua.kura.simulator.topic;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import com.google.common.base.Function;

public final class Topic {

    private static final Segment CONTROL = new Segment() {

        @Override
        public String render(final Function replaceMapper) {
            return "$EDC";
        }

        @Override
        public String toString() {
            return render(null);
        }
    };

    private static final Segment WILDCARD = new Segment() {

        @Override
        public String render(final Function replaceMapper) {
            return "#";
        }

        @Override
        public String toString() {
            return render(null);
        }
    };

    public interface Segment {

        public String render(Function replaceMapper);

        public default String render() {
            return render(key -> null);
        }

        public static Segment control() {
            return CONTROL;
        }

        public static Segment wildcard() {
            return WILDCARD;
        }

        public static Segment plain(final String segment) {
            Objects.requireNonNull(segment);
            if (segment.isEmpty() || segment.contains("/")) {
                throw new IllegalArgumentException(String.format("Illegal argument: '%s'", segment));
            }

            return raw(segment);
        }

        public static List plain(final String... segment) {
            Objects.requireNonNull(segment);
            return Arrays.stream(segment).map(Segment::plain).collect(Collectors.toList());
        }

        public static Segment raw(final String raw) {
            Objects.requireNonNull(raw);

            return new Segment() {

                @Override
                public String render(final Function replaceMapper) {
                    return raw;
                }

                @Override
                public String toString() {
                    return render(null);
                }
            };
        }

        public static Segment replace(final String key) {
            return new ReplaceSegment(key);
        }

        public static Segment account() {
            return replace("account-name");
        }

        public static Segment clientId() {
            return replace("client-id");
        }

        public static Segment applicationId() {
            return replace("application-id");
        }
    }

    private static class ReplaceSegment implements Segment {

        private final String key;

        public ReplaceSegment(final String key) {
            this.key = key;
        }

        @Override
        public String render(final Function replaceMapper) {
            final String value = replaceMapper.apply(this.key);
            if (value == null || value.isEmpty()) {
                throw new IllegalStateException(
                        String.format("Unable to replace segment '%s', no value found", this.key));
            }
            return value;
        }

        @Override
        public String toString() {
            return "<" + this.key + ">";
        }

    }

    private final List segments;
    private final Map context = new HashMap<>();

    private Topic(final List segments) {
        this.segments = segments;
    }

    private Topic(final Segment... segments) {
        this.segments = Arrays.asList(segments);
    }

    public List getSegments() {
        return Collections.unmodifiableList(this.segments);
    }

    public String renderSegment(final Segment segment, final Map context) {
        return segment.render(key -> {
            if (context.containsKey(key)) {
                return context.get(key);
            }
            return Topic.this.context.get(key);
        });
    }

    public String render(final Map context) {
        return renderInternal(this.segments, context);
    }

    /**
     * Renders all tokens from {@code fromIndex} (inclusive) to {@code toIndex}
     * (exclusive)
     *
     * @param fromIndex
     *            first item to render
     * @param toIndex
     *            first item not to render
     * @param context
     *            the additional context to use
     * @return the rendered string
     */
    public String render(final int fromIndex, final int toIndex, final Map context) {
        return renderInternal(this.segments.subList(fromIndex, toIndex), context);
    }

    public String render() {
        return renderInternal(this.segments, Collections.emptyMap());
    }

    /**
     * Renders all tokens from {@code fromIndex} (inclusive) to {@code toIndex}
     * (exclusive)
     *
     * @param fromIndex
     *            first item to render
     * @param toIndex
     *            first item not to render
     * @return the rendered string
     */
    public String render(final int fromIndex, final int toIndex) {
        return renderInternal(this.segments.subList(fromIndex, toIndex), Collections.emptyMap());
    }

    private String renderInternal(final List segments, final Map context) {
        return segments.stream().map(seg -> renderSegment(seg, context)).collect(Collectors.joining("/"));
    }

    public static Topic from(final List segments) {
        Objects.requireNonNull(segments);

        return new Topic(segments);
    }

    public static Topic fromString(final String topic) {
        return from(Arrays.stream(topic.split("\\/")).map(Segment::plain).collect(Collectors.toList()));
    }

    public static Topic reply(final String requesterClientId, final String requestId) {
        return new Topic(Segment.control(), Segment.account(), Segment.plain(requesterClientId), Segment.replace("application-id"), Segment.plain("REPLY"),
                Segment.plain(requestId));
    }

    public static Topic notify(final String requesterClientId, final String... resource) {
        final List s = new LinkedList<>();
        s.add(Segment.control());
        s.add(Segment.account());
        s.add(Segment.plain(requesterClientId));
        s.add(Segment.replace("application-id"));
        s.add(Segment.plain("NOTIFY"));
        s.addAll(Segment.plain(resource));
        return new Topic(s);
    }

    /**
     * Get the topic for an application
     * 

* Note: This is a topic for a control application, for sending data use a data application topic created by {@link #data(String)}. *

* * @param application * the application ID * @return a new topic */ public static Topic application(final String application) { return new Topic(Segment.control(), Segment.account(), Segment.clientId(), Segment.plain(application)); } public static Topic device(final String localTopic) { return new Topic(Segment.control(), Segment.account(), Segment.clientId(), Segment.raw(localTopic)); } public static Topic data(final String dataTopic) { if (dataTopic.isEmpty()) { throw new IllegalArgumentException("Data topic must not be empty"); } if (dataTopic.contains("#") || dataTopic.contains("+")) { throw new IllegalArgumentException("Data topic must not contain wildcards"); } if (dataTopic.startsWith("/")) { throw new IllegalArgumentException("Data topic must not start with /"); } return new Topic(Segment.account(), Segment.clientId(), Segment.applicationId(), Segment.raw(dataTopic)); } public Topic append(final Segment segment) { final List segs = new ArrayList<>(this.segments.size() + 1); segs.addAll(this.segments); segs.add(segment); return new Topic(segs); } /** * Attach information to the local topic context * * @param key * the key of the value * @param value * the value to attach * @return the current instance */ public Topic attach(final String key, final String value) { this.context.put(key, value); return this; } public Topic attachAll(final Map topicContext) { this.context.putAll(topicContext); return this; } public Topic localize(final Topic otherTopic) { return localize(otherTopic, Collections.emptyMap()); } public Topic localize(final Topic otherTopic, final Map topicContext) { final LinkedList newTopic = new LinkedList<>(getSegments()); for (final Segment seg : otherTopic.getSegments()) { final String segValue1 = renderSegment(newTopic.removeFirst(), topicContext); final String segValue2 = otherTopic.renderSegment(seg, topicContext); if (!segValue1.equals(segValue2)) { return null; } } return Topic.from(newTopic); } @Override public String toString() { return this.segments.stream().map(Object::toString).collect(Collectors.joining("/")); } public Map getContext() { return Collections.unmodifiableMap(this.context); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy