/*
* Copyright 2015-2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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 org.hawkular.inventory.api.model;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.hawkular.inventory.paths.CanonicalPath;
import org.hawkular.inventory.paths.DataRole;
import org.hawkular.inventory.paths.SegmentType;
import io.swagger.annotations.ApiModel;
/**
* A data entity is an entity wrapping the data. It's sole purpose is to give a path to the piece of structured data.
*
* <p>A data entity has an owner - the data entities are not shared amongst entities. Further, it has a role - a purpose
* for which the owner holds on to the data.
*
* @author Lukas Krejci
* @since 0.3.0
*/
@ApiModel(description = "Data entity contains JSON data and serves a certain \"role\" in the entity it is contained in",
parent = SyncedEntity.class)
public final class DataEntity extends SyncedEntity<DataEntity.Blueprint<?>, DataEntity.Update> {
public static final SegmentType SEGMENT_TYPE = SegmentType.d;
private final StructuredData value;
@SuppressWarnings("unused")
private DataEntity() {
this.value = null;
}
public DataEntity(CanonicalPath owner, DataRole role, StructuredData value, String identityHash, String contentHash,
String syncHash) {
super(null, owner.extend(DataEntity.SEGMENT_TYPE, role.name()).get(), identityHash, contentHash, syncHash);
this.value = value;
}
public DataEntity(CanonicalPath owner, DataRole role, StructuredData value, String identityHash,
String contentHash, String syncHash, Map<String, Object> properties) {
super(owner.extend(DataEntity.SEGMENT_TYPE, role.name()).get(), identityHash, contentHash, syncHash, properties);
this.value = value;
}
public DataEntity(CanonicalPath path, StructuredData value, String identityHash, String contentHash,
String syncHash, Map<String, Object> properties) {
this(path.up(), DataRole.valueOf(path.getSegment().getElementId()), value, identityHash, contentHash, syncHash,
properties);
}
@Override
public String getName() {
return getRole().name();
}
public StructuredData getValue() {
return value;
}
public DataRole getRole() {
return DataRole.valueOf(getId());
}
@Override
public <R, P> R accept(ElementVisitor<R, P> visitor, P parameter) {
return visitor.visitData(this, null);
}
@Override
public Updater<Update, DataEntity> update() {
return new Updater<>(
(u) -> {
StructuredData newValue = valueOrDefault(u.getValue(), getValue());
return new DataEntity(this.getPath().up(), this.getRole(), newValue, getIdentityHash(),
getContentHash(),
getSyncHash(), u.getProperties());
}, this, Update.builder());
}
@ApiModel("DataEntityBlueprint")
public static final class Blueprint<DR extends DataRole> extends Entity.Blueprint {
private static final StructuredData UNDEFINED = StructuredData.get().undefined();
private final StructuredData value;
private final DR role;
public static <R extends DataRole> Builder<R> builder() {
return new Builder<>();
}
public Blueprint(DR role, StructuredData value, Map<String, Object> properties) {
this(role, value, properties, null, null);
}
public Blueprint(DR role, StructuredData value, Map<String, Object> properties,
Map<String, Set<CanonicalPath>> outgoing,
Map<String, Set<CanonicalPath>> incoming) {
super(role.name(), properties, outgoing, incoming);
if (value == null) {
value = StructuredData.get().undefined();
}
this.role = role;
this.value = value;
}
// this is needed for Jackson deserialization
@SuppressWarnings("unused")
private Blueprint() {
value = null;
role = null;
}
public StructuredData getValue() {
//value can be null if constructed using the no-arg constructor through Jackson
return value == null ? UNDEFINED : value;
}
@SuppressWarnings("unchecked")
public DR getRole() {
return (role == null) ? (DR) DataRole.valueOf(getId()) : role;
}
/**
* This effectively calls {@code getRole().name()}.
*
* @return data entities don't really have a name - they derive it from their roles.
*/
@Override public String getName() {
return getRole().name();
}
@Override
public <R, P> R accept(ElementBlueprintVisitor<R, P> visitor, P parameter) {
return visitor.visitData(this, parameter);
}
public static final class Builder<R extends DataRole> extends Entity.Blueprint.Builder<Blueprint, Builder<R>> {
private R role;
private StructuredData value;
public Builder<R> withValue(StructuredData value) {
this.value = value;
return this;
}
public Builder<R> withRole(R role) {
this.role = role;
return this;
}
@SuppressWarnings("unchecked")
@Override public Builder<R> withId(String id) {
return withRole((R) DataRole.valueOf(id));
}
@Override
public Blueprint<R> build() {
if (role == null) {
throw new NullPointerException("Data entity role not specified.");
}
return new Blueprint<>(role, value, properties);
}
}
}
@ApiModel("DataEntityUpdate")
public static final class Update extends Entity.Update {
private final StructuredData value;
public static Builder builder() {
return new Builder();
}
public Update(StructuredData value, Map<String, Object> properties) {
super(null, properties);
this.value = value;
}
// this is needed for Jackson deserialization
@SuppressWarnings("unused")
private Update() {
this(null, null);
}
public StructuredData getValue() {
return value;
}
@Override
public <R, P> R accept(ElementUpdateVisitor<R, P> visitor, P parameter) {
return visitor.visitData(this, parameter);
}
@Override public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Update)) return false;
if (!super.equals(o)) return false;
Update update = (Update) o;
return value != null ? value.equals(update.value) : update.value == null;
}
@Override public int hashCode() {
int result = super.hashCode();
result = 31 * result + (value != null ? value.hashCode() : 0);
return result;
}
public static final class Builder extends Entity.Update.Builder<DataEntity, Update, Builder> {
private StructuredData value;
public Builder withValue(StructuredData value) {
this.value = value;
return this;
}
@Override
public Builder withDifference(DataEntity original, DataEntity updated) {
if (!Objects.equals(original.getValue(), updated.getValue())) {
withValue(updated.getValue());
}
return super.withDifference(original, updated);
}
@Override
public Update build() {
return new Update(value, properties);
}
}
}
}