com.adobe.granite.crx2oak.pipeline.PipeData Maven / Gradle / Ivy
/*************************************************************************
* ADOBE CONFIDENTIAL
* ___________________
*
* Copyright 2016 Adobe
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Adobe and its suppliers, if any. The intellectual
* and technical concepts contained herein are proprietary to Adobe
* and its suppliers and are protected by all applicable intellectual
* property laws, including trade secret and copyright laws.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe.
**************************************************************************/
package com.adobe.granite.crx2oak.pipeline;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static java.util.Objects.requireNonNull;
/**
* Represents an abstract generic data passed through pipeline for each pipe.
* The implementation assumes that you have any complex composition of objects
* identifiable via keys like for maps. In this case it is called topic.
*
* Therefore pipe data consist of multiple topics. Each topic accepts one type/class
* of information in which the concrete value might be passed.
*
* It is dependent from concrete pipeline use what topics are passed there.
*/
public final class PipeData {
/**
* Empty pipe data without any topics defined.
*/
public static final PipeData EMPTY = new PipeData(Collections.emptyMap());
private Map map;
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final PipeData pipeData = (PipeData) o;
return Objects.equals(map, pipeData.map);
}
@Override
public int hashCode() {
return Objects.hash(map);
}
/**
* Use topics that exist in the provided pipe data to build a new pipe data using returned builder.
* @param pipeData the pipe data that will be used as initial state of pipe data builder
* @return the new instance of pipe data builder
*/
public static PipeDataBuilder use(final PipeData pipeData) {
return new PipeDataBuilder(pipeData.map);
}
private PipeData(final Map map) {
this.map = requireNonNull(map);
}
/**
* Create a new builder instance of empty pipe data that allows you to add more data.
*
* @return a not null instance of builder
*/
public static PipeDataBuilder empty() {
return new PipeDataBuilder(Collections.emptyMap());
}
/**
* Put the topic with the specified value into currently built pipe data.
*
* @param topic the topic that will be placed
* @param value the value object of the topic
* @param the type of the value that is inferred from topic
* @return the instance of the pipe data builder
*/
public static PipeDataBuilder put(final Topic topic, @Nonnull final T value) {
return empty().put(topic, value);
}
/**
* Conditionally put the topic into currently built pipe data if the value is not null.
*
* @param topic the topic that will be optionally put
* @param value the nullable value that will be put into built pipe data if it is not null
* @param the the of the value derived from topic
* @return the instance of this builder
*/
public static PipeDataBuilder putOptionally(final Topic topic, @Nullable final T value) {
return empty().putOptionally(topic, value);
}
/**
* Joins the specified topic from the input pipe data that represents list values with
* new joiners values and stores them together as defined topic into the builder state.
* If this topic already existed it will be overridden.
* If some other topic were defined before this operation they will remain as it were before.
*
* @param input the input to apply
* @param topic the topic to apply
* @param newjoiners the new values that will be added to topic and joined for the current topic content
* @return the instance of this builder
* @param the type of elements that are stored in list
* @param the type of the topic which needs to be a list of parameter V.
*/
@SuppressWarnings("unchecked")
public static > PipeDataBuilder join(final PipeData input,
final Topic topic,
final V... newjoiners) {
return empty().join(input, topic, newjoiners);
}
/**
* Joins the specified topic from the input pipe data that represents list values with
* new joiners values and stores them together as defined topic into the builder state.
* If this topic already existed it will be overridden.
* If some other topic were defined before this operation they will remain as it were before.
*
* @param input the input to apply
* @param topic the topic to apply
* @param newjoiners the new values that will be added to topic and joined for the current topic content
* @return the instance of this builder
* @param the type of elements that are stored in list
* @param the type of the topic which needs to be a list of parameter V.
*/
@SuppressWarnings("unchecked")
public static > PipeDataBuilder join(final PipeData input,
final Topic topic,
final List newjoiners) {
return empty().join(input, topic, newjoiners);
}
/**
* Joins the specified topic from 2 input pipe data objects that in both topic represents list values
* stores them together as defined topic into the builder state.
* If this topic already existed it will be overridden.
* If some other topic were defined before this operation they will remain as it were before.
*
* @param input the input to apply
* @param topic the topic to apply
* @param inputToJoin the input from which new values of the specified topic that will be added to the same
* topic and joined for the current topic state
* @return the instance of this builder
* @param the type of elements that are stored in list
* @param the type of the topic which needs to be a list of parameter V.
*/
@SuppressWarnings("unchecked")
public static > PipeDataBuilder join(final PipeData input,
final Topic topic,
final PipeData inputToJoin) {
return empty().join(input, topic, inputToJoin);
}
/**
* Get the specified optional topic from pipe data.
*
* @param topic the topic that holds identifier and type of value of that topic
* @param the type of topic for which the value of T will be returned
* @return the optional value of type defined by the topic
*/
@SuppressWarnings("unchecked")
public Optional get(final Topic topic) {
return Optional.fromNullable((T) map.get(topic.getName()));
}
/**
* Require the specified topic to be present in pipe data. If it is the value is returned but
* if not, the {@linkplain IllegalStateException} is immediately thrown.
*
* @param topic the topic that holds identifier and type of value of that topic
* @param the type of topic for which the value of T will be returned
* @return the value of type defined by the topic
* @throws IllegalStateException if the topic is not available
*/
public T require(final Topic topic) {
final Optional optionalValue = get(topic);
if (optionalValue.isPresent()) {
return optionalValue.get();
} else {
throw new IllegalStateException(String.format("The value of %s is required", topic.getName()));
}
}
@Override
public String toString() {
return "PipeData" + map;
}
/**
* The topic of data passed though pipe.
*
* An equivalent of key from a map but additionally defining the accepted type
* of the value of such key - value entry a map can store.
*
* @param the class/type of value that this topic accepts for storage/retrieval
*/
public static final class Topic {
private final String name;
/**
* Create a topic with the specified name that needs to be unique across all entries
* in the pipe data.
*
* @param name any not null name
*/
public Topic(final String name) {
this.name = requireNonNull(name);
}
/**
* Get the name of the topic.
*
* @return the not null string
*/
public String getName() {
return name;
}
@Override
public String toString() {
return "Topic{" + "name='" + name + '\'' + '}';
}
}
public static final class PipeDataBuilder {
private Map map;
private PipeDataBuilder(final Map pipeData) {
map = new HashMap<>(pipeData);
}
/**
* Put the topic with the specified value into currently built pipe data.
*
* @param topic the topic that will be placed
* @param value the value object of the topic
* @param the type of the value that is inferred from topic
* @return the instance of this builder
*/
public PipeDataBuilder put(final Topic topic, @Nonnull final T value) {
map.put(topic.getName(), requireNonNull(value));
return this;
}
/**
* Drops the specified topic from currently built pipe data.
*
* @param topic the topic to drop
* @return the instance of this builder
*/
public PipeDataBuilder drop(final Topic> topic) {
map.remove(topic.getName());
return this;
}
/**
* Returns the current state of builder as immutable pipe data new instance.
*
* @return the always not null snapshot of builder state
*/
public PipeData toPipe() {
return new PipeData(ImmutableMap.copyOf(map));
}
/**
* Applies all topics from the into the builder state. If some topics already existed
* they will be overridden. If some topic were defined before this operation
* and they aren't redefined by the input they will remain as it were before.
*
* @param input the input to apply
* @return the instance of this builder
*/
public PipeDataBuilder apply(final PipeData input) {
map.putAll(input.map);
return this;
}
/**
* Conditionally put the topic into currently built pipe data if the value is not null.
*
* @param topic the topic that will be optionally put
* @param value the nullable value that will be put into built pipe data if it is not null
* @param the the of the value derived from topic
* @return the instance of this builder
*/
public PipeDataBuilder putOptionally(final Topic topic, @Nullable final T value) {
if (value != null) put(topic, value);
return this;
}
/**
* Joins the specified topic from the input pipe data that represents list values with
* new joiners values and stores them together as defined topic into the builder state.
* If this topic already existed it will be overridden.
* If some other topic were defined before this operation they will remain as it were before.
*
* @param input the input to apply
* @param topic the topic to apply
* @param newjoiners the new values that will be added to topic and joined for the current topic content
* @return the instance of this builder
* @param the type of elements that are stored in list
* @param the type of the topic which needs to be a list of parameter V.
*/
@SuppressWarnings("unchecked")
public > PipeDataBuilder join(final PipeData input,
final Topic topic,
final V... newjoiners) {
join(input, topic, Arrays.asList(newjoiners));
return this;
}
/**
* Joins the specified topic from 2 input pipe data objects that in both topic represents list values
* stores them together as defined topic into the builder state.
* If this topic already existed it will be overridden.
* If some other topic were defined before this operation they will remain as it were before.
*
* @param input the input to apply
* @param topic the topic to apply
* @param inputToJoin the input from which new values of the specified topic that will be added to the same
* topic and joined for the current topic state
* @return the instance of this builder
* @param the type of elements that are stored in list
* @param the type of the topic which needs to be a list of parameter V.
*/
@SuppressWarnings("unchecked")
public > PipeDataBuilder join(final PipeData input,
final Topic topic,
final PipeData inputToJoin) {
final List existingValues = getTopicValueAsList(inputToJoin, (Topic>) topic);
join(input, topic, existingValues);
return this;
}
/**
* Joins the specified topic from the input pipe data that represents list values with
* new joiners values and stores them together as defined topic into the builder state.
* If this topic already existed it will be overridden.
* If some other topic were defined before this operation they will remain as it were before.
*
* @param input the input to apply
* @param topic the topic to apply
* @param newjoiners the new values that will be added to topic and joined for the current topic content
* @return the instance of this builder
* @param the type of elements that are stored in list
* @param the type of the topic which needs to be a list of parameter V.
*/
@SuppressWarnings("unchecked")
public > PipeDataBuilder join(final PipeData input,
final Topic topic,
final List newjoiners) {
//noinspection unchecked
final List existingValues = getTopicValueAsList(input, (Topic>) topic);
final List newList = new ArrayList<>(existingValues);
newList.addAll(newjoiners);
//noinspection unchecked
put((Topic>)topic, newList);
return this;
}
private > List getTopicValueAsList(final PipeData input, final Topic> topic) {
return input.get(topic).or(Collections.emptyList());
}
}
}