/*
* Copyright (c) 2013-2017 Cinchapi Inc.
*
* 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 com.cinchapi.concourse.server.plugin.data;
import io.atomix.catalyst.buffer.Buffer;
import java.nio.ByteBuffer;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import com.cinchapi.common.base.AdHocIterator;
import com.cinchapi.common.reflect.Reflection;
import com.cinchapi.concourse.thrift.TObject;
import com.cinchapi.concourse.thrift.Type;
import com.cinchapi.concourse.util.Convert;
import com.google.common.collect.Maps;
/**
* A {@link ResultDataset} that wraps a {@link TObjectDataset} and lazily
* transforms values while continuing to write through to the underlying
* dataset.
*
* @author Jeff Nelson
*/
public class ObjectResultDataset extends ResultDataset<Object> {
/**
* The internal dataset that contains the data.
*/
protected Dataset<Long, String, TObject> thrift;
/**
* Construct a new instance.
*
* @param thrift
*/
public ObjectResultDataset(Dataset<Long, String, TObject> thrift) {
this.thrift = thrift;
}
/**
* Construct a new instance.
*/
public ObjectResultDataset() {
this.thrift = new TObjectResultDataset();
}
@Override
public boolean delete(Long entity, String attribute, Object value) {
return thrift.delete(entity, attribute, Convert.javaToThrift(value));
}
@Override
public Set<Entry<Long, Map<String, Set<Object>>>> entrySet() {
return new AbstractSet<Entry<Long, Map<String, Set<Object>>>>() {
@Override
public Iterator<Entry<Long, Map<String, Set<Object>>>> iterator() {
Iterator<Entry<Long, Map<String, Set<TObject>>>> it = thrift
.entrySet().iterator();
return new AdHocIterator<Entry<Long, Map<String, Set<Object>>>>() {
@Override
protected Entry<Long, Map<String, Set<Object>>> findNext() {
if(it.hasNext()) {
Entry<Long, Map<String, Set<TObject>>> next = it
.next();
long key = next.getKey();
Map<String, Set<Object>> value = get(key);
return new SimpleEntry<>(key, value);
}
else {
return null;
}
}
};
}
@Override
public int size() {
return thrift.entrySet().size();
}
};
}
@Override
public boolean equals(Object obj) {
if(obj instanceof ObjectResultDataset) {
return thrift.equals(((ObjectResultDataset) obj).thrift);
}
else {
return false;
}
}
@Override
public Set<Object> get(Long entity, String attribute) {
return new AbstractSet<Object>() {
@Override
public boolean add(Object e) {
return insert(entity, attribute, e);
}
@Override
public boolean contains(Object o) {
return thrift.get(entity, attribute)
.contains(Convert.javaToThrift(o));
}
@Override
public Iterator<Object> iterator() {
Set<TObject> values = thrift.get(entity, attribute);
Iterator<TObject> it;
if(values != null) {
it = thrift.get(entity, attribute).iterator();
}
else {
it = Collections.<TObject> emptySet().iterator();
}
return new AdHocIterator<Object>() {
@Override
protected Object findNext() {
if(it.hasNext()) {
return Convert.thriftToJava(it.next());
}
else {
return null;
}
}
};
}
@Override
public boolean remove(Object o) {
return delete(entity, attribute, o);
}
@Override
public int size() {
return thrift.get(entity, attribute).size();
}
};
}
@Override
public Map<String, Set<Object>> get(Object entity) {
if(entity instanceof Long) {
return new AbstractMap<String, Set<Object>>() {
@Override
public Set<Entry<String, Set<Object>>> entrySet() {
return new AbstractSet<Entry<String, Set<Object>>>() {
@Override
public Iterator<Entry<String, Set<Object>>> iterator() {
Iterator<Entry<String, Set<TObject>>> it = thrift
.get(entity).entrySet().iterator();
return new AdHocIterator<Entry<String, Set<Object>>>() {
@Override
protected Entry<String, Set<Object>> findNext() {
if(it.hasNext()) {
Entry<String, Set<TObject>> entry = it
.next();
return new SimpleEntry<>(entry.getKey(),
entry.getValue().stream()
.map((value) -> Convert
.thriftToJava(
value))
.collect(Collectors
.toSet()));
}
else {
return null;
}
}
};
}
@Override
public int size() {
return thrift.get(entity).size();
}
};
}
@Override
public Set<Object> get(Object key) {
if(key instanceof String) {
String attribute = (String) key;
return new AbstractSet<Object>() {
@Override
public boolean add(Object e) {
return insert((Long) entity, attribute, e);
}
@Override
public Iterator<Object> iterator() {
Iterator<TObject> it = thrift
.get((Long) entity, attribute)
.iterator();
return new AdHocIterator<Object>() {
@Override
protected Object findNext() {
if(it.hasNext()) {
return Convert
.thriftToJava(it.next());
}
else {
return null;
}
}
};
}
@Override
public boolean remove(Object o) {
return delete((Long) entity, attribute, o);
}
@Override
public int size() {
Set<TObject> values = thrift.get((Long) entity,
attribute);
return values == null ? 0 : values.size();
}
};
}
else {
return null;
}
}
@Override
public Set<Object> put(String key, Set<Object> value) {
Set<Object> stored = thrift.get((Long) entity, key).stream()
.map((v) -> Convert.thriftToJava(v))
.collect(Collectors.toSet());
value.forEach(v -> insert((Long) entity, key, v));
return stored;
}
@Override
public Set<Object> remove(Object key) {
if(key instanceof String) {
String attribute = (String) key;
Set<Object> stored = thrift
.get((Long) entity, attribute).stream()
.map((v) -> Convert.thriftToJava(v))
.collect(Collectors.toSet());
return stored;
}
else {
return null;
}
}
};
}
else {
return null;
}
}
@Override
public int hashCode() {
return thrift.hashCode();
}
@Override
public boolean insert(Long entity, String attribute, Object value) {
return thrift.insert(entity, attribute, Convert.javaToThrift(value));
}
@Override
public Map<String, Map<Object, Set<Long>>> invert() {
return new AbstractMap<String, Map<Object, Set<Long>>>() {
@Override
public Set<Entry<String, Map<Object, Set<Long>>>> entrySet() {
return new AbstractSet<Entry<String, Map<Object, Set<Long>>>>() {
@Override
public Iterator<Entry<String, Map<Object, Set<Long>>>> iterator() {
final Iterator<String> it = thrift.invert().keySet()
.iterator();
return new AdHocIterator<Entry<String, Map<Object, Set<Long>>>>() {
@Override
protected Entry<String, Map<Object, Set<Long>>> findNext() {
if(it.hasNext()) {
String attribute = it.next();
return new SimpleEntry<>(attribute,
invert(attribute));
}
else {
return null;
}
}
};
}
@Override
public int size() {
return thrift.size();
}
};
}
@Override
public Map<Object, Set<Long>> get(Object attribute) {
if(attribute instanceof String) {
return invert((String) attribute);
}
else {
return null;
}
}
@Override
public Map<Object, Set<Long>> put(String attribute,
Map<Object, Set<Long>> inverted) {
Map<Object, Set<Long>> stored = Maps.newLinkedHashMap();
stored.putAll(get(attribute));
inverted.forEach((value, entities) -> invert(attribute)
.put(value, entities));
return stored;
}
@Override
public Map<Object, Set<Long>> remove(Object key) {
if(key instanceof String) {
String attribute = (String) key;
Map<Object, Set<Long>> stored = Maps.newLinkedHashMap();
stored.putAll(get(attribute));
stored.forEach((value, entities) -> entities
.forEach((entity) -> thrift.delete(entity,
attribute, Convert.javaToThrift(value))));
return stored;
}
else {
return null;
}
}
};
}
@Override
public Map<Object, Set<Long>> invert(String attribute) {
return new TrackingMultimap<Object, Long>(Collections.emptyMap()) {
@Override
public boolean containsDataType(DataType type) {
return ((TrackingMultimap<TObject, Long>) thrift
.invertNullSafe(attribute)).containsDataType(type);
}
@Override
public boolean delete(Object value, Long entity) {
return thrift.delete(entity, attribute,
Convert.javaToThrift(value));
}
@Override
public Set<Entry<Object, Set<Long>>> entrySet() {
return new AbstractSet<Entry<Object, Set<Long>>>() {
@Override
public Iterator<Entry<Object, Set<Long>>> iterator() {
final Iterator<Entry<TObject, Set<Long>>> it = thrift
.invertNullSafe(attribute).entrySet()
.iterator();
return new AdHocIterator<Entry<Object, Set<Long>>>() {
@Override
protected Entry<Object, Set<Long>> findNext() {
if(it.hasNext()) {
Entry<TObject, Set<Long>> entry = it.next();
return new SimpleEntry<>(
Convert.thriftToJava(
entry.getKey()),
entry.getValue());
}
else {
return null;
}
}
};
}
@Override
public int size() {
return thrift.invertNullSafe(attribute).size();
}
};
}
@Override
public double spread() {
return ((TrackingMultimap<TObject, Long>) thrift
.invertNullSafe(attribute)).spread();
}
@Override
public boolean equals(Object obj) {
if(this.getClass() == obj.getClass()) {
Object entrySet = Reflection.call(obj, "entrySet");
return entrySet().equals(entrySet);
}
else {
return false;
}
}
@Override
public Set<Long> get(Object value) {
return thrift.invertNullSafe(attribute)
.get(Convert.javaToThrift(value));
}
@Override
public int hashCode() {
return thrift.invertNullSafe(attribute).hashCode();
}
@Override
public boolean hasValue(Long value) {
return ((TrackingMultimap<TObject, Long>) thrift
.invertNullSafe(attribute)).hasValue(value);
}
@Override
public boolean insert(Object value, Long entity) {
return thrift.insert(entity, attribute,
Convert.javaToThrift(value));
}
@Override
public Set<Long> merge(Object value, Set<Long> entities) {
return ((TrackingMultimap<TObject, Long>) thrift
.invertNullSafe(attribute))
.merge(Convert.javaToThrift(value), entities);
}
@Override
public double percentKeyDataType(DataType type) {
return ((TrackingMultimap<TObject, Long>) thrift
.invertNullSafe(attribute)).percentKeyDataType(type);
}
@Override
public double proportion(Object value) {
return ((TrackingMultimap<TObject, Long>) thrift
.invertNullSafe(attribute))
.proportion(Convert.javaToThrift(value));
}
@Override
public Set<Long> put(Object value, Set<Long> entities) {
return thrift.invertNullSafe(attribute)
.put(Convert.javaToThrift(value), entities);
}
@Override
public Set<Long> remove(Object value) {
return thrift.invertNullSafe(attribute)
.remove(Convert.javaToThrift(value));
}
@Override
public String toString() {
return thrift.invertNullSafe(attribute).toString();
}
@Override
public double uniqueness() {
return ((TrackingMultimap<TObject, Long>) thrift
.invertNullSafe(attribute)).uniqueness();
}
@Override
public VariableType variableType() {
return ((TrackingMultimap<TObject, Long>) thrift
.invertNullSafe(attribute)).variableType();
}
@Override
protected Set<Long> createValueSet() {
// NOTE: this won't ever be called because all wrties are routed
// to the underyling #thrift based collection
return null;
}
};
}
@Override
public String toString() {
return thrift.toString();
}
@Override
protected Object deserializeValue(Buffer buffer) {
Type type = Type.values()[buffer.readByte()];
int length = buffer.readInt();
byte[] data = new byte[length];
buffer.read(data);
TObject value = new TObject(ByteBuffer.wrap(data), type);
return Convert.thriftToJava(value);
}
@Override
protected void serializeValue(Object value, Buffer buffer) {
TObject value0 = Convert.javaToThrift(value);
buffer.writeByte(value0.getType().ordinal());
byte[] data = value0.getData();
buffer.writeInt(data.length);
buffer.write(data);
}
}