uk.gov.gchq.gaffer.data.elementdefinition.view.View Maven / Gradle / Ivy
/*
* Copyright 2016-2020 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package uk.gov.gchq.gaffer.data.elementdefinition.view;
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import uk.gov.gchq.gaffer.commonutil.CommonConstants;
import uk.gov.gchq.gaffer.commonutil.ToStringBuilder;
import uk.gov.gchq.gaffer.data.elementdefinition.ElementDefinitions;
import uk.gov.gchq.gaffer.data.elementdefinition.exception.SchemaException;
import uk.gov.gchq.koryphe.serialisation.json.JsonSimpleClassName;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
/**
* The {@code View} defines the {@link uk.gov.gchq.gaffer.data.element.Element}s to be returned for an operation.
* A view should contain {@link uk.gov.gchq.gaffer.data.element.Edge} and {@link uk.gov.gchq.gaffer.data.element.Entity} types required and
* for each group it can optionally contain an {@link uk.gov.gchq.gaffer.data.element.function.ElementFilter} and a
* {@link uk.gov.gchq.gaffer.data.element.function.ElementTransformer}.
* The {@link java.util.function.Predicate}s within the ElementFilter describe the how the elements should be filtered.
* The {@link java.util.function.Function}s within ElementTransformer allow transient properties to be created
* from other properties and identifiers.
* It also contains any transient properties that are created in transform functions.
*
* @see Builder
* @see uk.gov.gchq.gaffer.data.elementdefinition.view.ViewElementDefinition
* @see uk.gov.gchq.gaffer.data.element.function.ElementFilter
* @see uk.gov.gchq.gaffer.data.element.function.ElementTransformer
*/
@JsonDeserialize(builder = View.Builder.class)
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = As.EXISTING_PROPERTY, property = "class", defaultImpl = View.class)
@JsonPropertyOrder(value = {"class", "edges", "entities", "allEdges", "allEntities", "globalElements", "globalEntities", "globalEdges"}, alphabetic = true)
@JsonSimpleClassName(includeSubtypes = true)
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
public class View extends ElementDefinitions implements Cloneable {
private List globalElements;
private List globalEntities;
private List globalEdges;
private Map config = new HashMap<>();
private boolean allEntities = false;
private boolean allEdges = false;
public View() {
super();
}
public static View fromJson(final InputStream inputStream) throws SchemaException {
return new View.Builder().json(inputStream).build();
}
public static View fromJson(final Path filePath) throws SchemaException {
return new View.Builder().json(filePath).build();
}
public static View fromJson(final byte[] jsonBytes) throws SchemaException {
return new View.Builder().json(jsonBytes).build();
}
public byte[] toCompactJson() throws SchemaException {
return toJson(false);
}
@Override
public String toString() {
try {
return new ToStringBuilder(this)
.append(new String(toJson(true), CommonConstants.UTF_8))
.build();
} catch (final UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
@Override
public ViewElementDefinition getElement(final String group) {
return (ViewElementDefinition) super.getElement(group);
}
public Set getElementGroupBy(final String group) {
ViewElementDefinition viewElementDef = (ViewElementDefinition) super.getElement(group);
if (null == viewElementDef) {
return null;
}
return viewElementDef.getGroupBy();
}
public boolean isAllEntities() {
return allEntities;
}
public void setAllEntities(final boolean allEntities) {
this.allEntities = allEntities;
}
public boolean isAllEdges() {
return allEdges;
}
public void setAllEdges(final boolean allEdges) {
this.allEdges = allEdges;
}
public List getGlobalElements() {
return globalElements;
}
public List getGlobalEntities() {
return globalEntities;
}
public List getGlobalEdges() {
return globalEdges;
}
public boolean hasPreAggregationFilters() {
return hasFilters(ViewElementDefinition::hasPreAggregationFilters);
}
public boolean hasPostAggregationFilters() {
return hasFilters(ViewElementDefinition::hasPostAggregationFilters);
}
public boolean hasPostTransformFilters() {
return hasFilters(ViewElementDefinition::hasPostTransformFilters);
}
public boolean hasEntityFilters() {
return hasEntityFilters(ViewElementDefinition::hasPostAggregationFilters)
|| hasEntityFilters(ViewElementDefinition::hasPostTransformFilters)
|| hasEntityFilters(ViewElementDefinition::hasPreAggregationFilters);
}
public boolean hasEdgeFilters() {
return hasEdgeFilters(ViewElementDefinition::hasPostAggregationFilters)
|| hasEdgeFilters(ViewElementDefinition::hasPostTransformFilters)
|| hasEdgeFilters(ViewElementDefinition::hasPreAggregationFilters);
}
@SuppressWarnings("CloneDoesntCallSuperClone")
@SuppressFBWarnings(value = "CN_IDIOM_NO_SUPER_CALL", justification = "Only inherits from Object")
@Override
public View clone() {
return fromJson(toJson(false));
}
@Override
protected void lock() {
super.lock();
if (null != globalElements) {
globalElements = Collections.unmodifiableList(globalElements);
}
if (null != globalEntities) {
globalEntities = Collections.unmodifiableList(globalEntities);
}
if (null != globalEdges) {
globalEdges = Collections.unmodifiableList(globalEdges);
}
}
/**
* Copies all the global element definitions into the individual element definitions.
* The global element definitions will then be set to null
*/
public void expandGlobalDefinitions() {
if (null != globalEntities && !globalEntities.isEmpty()) {
setEntities(expandGlobalDefinitions(getEntities(), getEntityGroups(), globalEntities, false));
globalEntities = null;
}
if (null != globalEdges && !globalEdges.isEmpty()) {
setEdges(expandGlobalDefinitions(getEdges(), getEdgeGroups(), globalEdges, false));
globalEdges = null;
}
if (null != globalElements && !globalElements.isEmpty()) {
setEntities(expandGlobalDefinitions(getEntities(), getEntityGroups(), globalElements, true));
setEdges(expandGlobalDefinitions(getEdges(), getEdgeGroups(), globalElements, true));
globalElements = null;
}
}
@JsonInclude(Include.NON_EMPTY)
public Map getConfig() {
return this.config;
}
public void addConfig(final String key, final String value) {
if (!this.config.containsKey(key)) {
this.config.put(key, value);
}
}
public String getConfig(final String key) {
return this.config.get(key);
}
private Map expandGlobalDefinitions(
final Map elements,
final Set groups,
final List globalElements,
final boolean skipMissingGroups) {
final Map newElements = new LinkedHashMap<>();
for (final GlobalViewElementDefinition globalElement : globalElements) {
final Set globalGroups;
if (null != globalElement.groups) {
globalGroups = new HashSet<>(globalElement.groups);
final boolean hasMissingGroups = globalGroups.retainAll(groups);
if (hasMissingGroups && !skipMissingGroups) {
final Set missingGroups = new HashSet<>(globalElement.groups);
missingGroups.removeAll(groups);
throw new IllegalArgumentException("A global element definition is invalid, these groups do not exist: " + missingGroups);
}
} else {
globalGroups = groups;
}
for (final String group : globalGroups) {
final ViewElementDefinition.Builder builder = new ViewElementDefinition.Builder();
if (newElements.containsKey(group)) {
builder.merge(newElements.get(group));
}
builder.merge(globalElement.clone());
newElements.put(group, builder.build());
}
}
if (null != elements) {
for (final Map.Entry entry : elements.entrySet()) {
final String group = entry.getKey();
if (newElements.containsKey(group)) {
newElements.put(group, new ViewElementDefinition.Builder()
.merge(newElements.get(group))
.merge(entry.getValue())
.build());
} else {
newElements.put(group, entry.getValue());
}
}
}
return Collections.unmodifiableMap(newElements);
}
private boolean hasFilters(final Function hasFilters) {
return hasEdgeFilters(hasFilters) || hasEntityFilters(hasFilters);
}
private boolean hasEntityFilters(final Function hasEntityFilters) {
for (final ViewElementDefinition value : getEntities().values()) {
if (null != value && hasEntityFilters.apply(value)) {
return true;
}
}
return false;
}
private boolean hasEdgeFilters(final Function hasEdgeFilters) {
for (final ViewElementDefinition value : getEdges().values()) {
if (null != value && hasEdgeFilters.apply(value)) {
return true;
}
}
return false;
}
public boolean canMerge(final View addingView, final View srcView) {
if (addingView instanceof View && !(srcView instanceof View)) {
return false;
}
return true;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (null == obj || getClass() != obj.getClass()) {
return false;
}
final View view = (View) obj;
return new EqualsBuilder()
.appendSuper(super.equals(view))
.append(globalElements, view.getGlobalElements())
.append(globalEntities, view.getGlobalEntities())
.append(globalEdges, view.getGlobalEdges())
.append(allEntities, view.isAllEntities())
.append(allEdges, view.isAllEdges())
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.appendSuper(super.hashCode())
.append(globalElements)
.append(globalEntities)
.append(globalEdges)
.append(allEntities)
.append(allEdges)
.toHashCode();
}
@JsonInclude(Include.NON_NULL)
@JsonGetter("class")
public String getClassName() {
return View.class.equals(getClass()) ? null : getClass().getName();
}
@JsonSetter("class")
void setClassName(final String className) {
// ignore the className as it will be picked up by the JsonTypeInfo annotation.
}
public abstract static class BaseBuilder> extends ElementDefinitions.BaseBuilder {
public BaseBuilder() {
super(new View());
}
protected BaseBuilder(final View view) {
super(view);
}
public CHILD_CLASS entity(final String group) {
return entity(group, new ViewElementDefinition());
}
@Override
public CHILD_CLASS entity(final String group, final ViewElementDefinition entityDef) {
return super.entity(group, null != entityDef ? entityDef : new ViewElementDefinition());
}
@JsonIgnore
public CHILD_CLASS entities(final Collection groups) {
if (null != groups) {
for (final String group : groups) {
entity(group);
}
}
return self();
}
public CHILD_CLASS allEntities(final boolean allEntites) {
getThisView().allEntities = allEntites;
return self();
}
public CHILD_CLASS edge(final String group) {
return edge(group, new ViewElementDefinition());
}
@Override
public CHILD_CLASS edge(final String group, final ViewElementDefinition edgeDef) {
return super.edge(group, null != edgeDef ? edgeDef : new ViewElementDefinition());
}
public CHILD_CLASS edges(final Collection groups) {
if (null != groups) {
for (final String group : groups) {
edge(group);
}
}
return self();
}
public CHILD_CLASS allEdges(final boolean allEdges) {
getThisView().allEdges = allEdges;
return self();
}
public CHILD_CLASS config(final String key, final String value) {
getThisView().config.put(key, value);
return self();
}
public CHILD_CLASS config(final Map config) {
if (null != config) {
getThisView().config.putAll(config);
}
return self();
}
public CHILD_CLASS globalElements(final GlobalViewElementDefinition... globalElements) {
if (null != globalElements && globalElements.length > 0) {
if (null == getThisView().globalElements) {
getThisView().globalElements = new ArrayList<>();
}
Collections.addAll(getThisView().globalElements, globalElements);
}
return self();
}
public CHILD_CLASS globalEntities(final GlobalViewElementDefinition... globalEntities) {
if (null != globalEntities && globalEntities.length > 0) {
if (null == getThisView().globalEntities) {
getThisView().globalEntities = new ArrayList<>();
}
Collections.addAll(getThisView().globalEntities, globalEntities);
}
return self();
}
public CHILD_CLASS globalEdges(final GlobalViewElementDefinition... globalEdges) {
if (null != globalEdges && globalEdges.length > 0) {
if (null == getThisView().globalEdges) {
getThisView().globalEdges = new ArrayList<>();
}
Collections.addAll(getThisView().globalEdges, globalEdges);
}
return self();
}
@JsonIgnore
public CHILD_CLASS json(final InputStream... inputStreams) throws SchemaException {
return json(View.class, inputStreams);
}
@JsonIgnore
public CHILD_CLASS json(final Path... filePaths) throws SchemaException {
return json(View.class, filePaths);
}
@JsonIgnore
public CHILD_CLASS json(final byte[]... jsonBytes) throws SchemaException {
return json(View.class, jsonBytes);
}
@Override
@JsonIgnore
public CHILD_CLASS merge(final View view) {
if (null != view) {
if (!(getThisView().canMerge(view, getThisView()) && view.canMerge(view, getThisView()))) {
throw new IllegalArgumentException("A " + view.getClass().getSimpleName() +
" cannot be merged into a " + getThisView().getClass().getSimpleName());
}
if (getThisView().getEntities().isEmpty()) {
getThisView().getEntities().putAll(view.getEntities());
} else {
for (final Map.Entry entry : view.getEntities().entrySet()) {
if (!getThisView().getEntities().containsKey(entry.getKey())) {
entity(entry.getKey(), entry.getValue());
} else {
final ViewElementDefinition mergedElementDef = new ViewElementDefinition.Builder()
.merge(getThisView().getEntities().get(entry.getKey()))
.merge(entry.getValue())
.build();
getThisView().getEntities().put(entry.getKey(), mergedElementDef);
}
}
}
if (getThisView().getEdges().isEmpty()) {
getThisView().getEdges().putAll(view.getEdges());
} else {
for (final Map.Entry entry : view.getEdges().entrySet()) {
if (!getThisView().getEdges().containsKey(entry.getKey())) {
edge(entry.getKey(), entry.getValue());
} else {
final ViewElementDefinition mergedElementDef = new ViewElementDefinition.Builder()
.merge(getThisView().getEdges().get(entry.getKey()))
.merge(entry.getValue())
.build();
getThisView().getEdges().put(entry.getKey(), mergedElementDef);
}
}
}
if (null != view.globalElements) {
if (null == getThisView().globalElements) {
getThisView().globalElements = new ArrayList<>();
}
getThisView().globalElements.addAll(view.globalElements);
}
if (null != view.globalEntities) {
if (null == getThisView().globalEntities) {
getThisView().globalEntities = new ArrayList<>();
}
getThisView().globalEntities.addAll(view.globalEntities);
}
if (null != view.globalEdges) {
if (null == getThisView().globalEdges) {
getThisView().globalEdges = new ArrayList<>();
}
getThisView().globalEdges.addAll(view.globalEdges);
}
if (null != view.config) {
getThisView().config.putAll(view.config);
}
getThisView().setAllEntities(view.allEntities);
getThisView().setAllEdges(view.allEdges);
}
return self();
}
public CHILD_CLASS expandGlobalDefinitions() {
getThisView().expandGlobalDefinitions();
return self();
}
@Override
public View build() {
return super.build();
}
private View getThisView() {
return getElementDefs();
}
}
@JsonPOJOBuilder(buildMethodName = "build", withPrefix = "")
public static final class Builder extends BaseBuilder {
public Builder() {
}
public Builder(final View view) {
this();
merge(view);
}
@Override
protected Builder self() {
return this;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy