// Copyright 2017 JanusGraph Authors
//
// 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.janusgraph.diskstorage.util;
import com.google.common.base.Preconditions;
import org.janusgraph.diskstorage.Entry;
import org.janusgraph.diskstorage.EntryMetaData;
import org.janusgraph.diskstorage.MetaAnnotatable;
import org.janusgraph.diskstorage.StaticBuffer;
import org.janusgraph.graphdb.relations.RelationCache;
import java.nio.ByteBuffer;
import java.util.Map;
/**
* @author Matthias Broecheler (me@matthiasb.com)
*/
public class StaticArrayEntry extends BaseStaticArrayEntry implements Entry, MetaAnnotatable {
public StaticArrayEntry(byte[] array, int offset, int limit, int valuePosition) {
super(array, offset, limit, valuePosition);
}
public StaticArrayEntry(byte[] array, int limit, int valuePosition) {
super(array, limit, valuePosition);
}
public StaticArrayEntry(byte[] array, int valuePosition) {
super(array, valuePosition);
}
public StaticArrayEntry(StaticBuffer buffer, int valuePosition) {
super(buffer, valuePosition);
}
StaticArrayEntry(Entry entry) {
super(entry,entry.getValuePosition());
}
//########## META DATA ############
private Map<EntryMetaData,Object> metadata = EntryMetaData.EMPTY_METADATA;
@Override
public synchronized Object setMetaData(EntryMetaData key, Object value) {
if (metadata==EntryMetaData.EMPTY_METADATA) metadata = new EntryMetaData.Map();
return metadata.put(key,value);
}
@Override
public boolean hasMetaData() {
return !metadata.isEmpty();
}
@Override
public Map<EntryMetaData,Object> getMetaData() {
return metadata;
}
/**
* ############# IDENTICAL CODE ###############
*/
private volatile transient RelationCache cache;
@Override
public RelationCache getCache() {
return cache;
}
@Override
public void setCache(RelationCache cache) {
Preconditions.checkNotNull(cache);
this.cache = cache;
}
//########### CONSTRUCTORS AND UTILITIES ###########
public static Entry of(StaticBuffer buffer) {
return new StaticArrayEntry(buffer,buffer.length());
}
public static final<E> Entry ofBytes(E element, StaticArrayEntry.GetColVal<E,byte[]> getter) {
return of(element, getter, ByteArrayHandler.INSTANCE);
}
public static final<E> Entry ofByteBuffer(E element, StaticArrayEntry.GetColVal<E,ByteBuffer> getter) {
return of(element, getter, ByteBufferHandler.INSTANCE);
}
public static final<E> Entry ofStaticBuffer(E element, StaticArrayEntry.GetColVal<E,StaticBuffer> getter) {
return of(element, getter, StaticBufferHandler.INSTANCE);
}
public static final<E> Entry of(StaticBuffer column, StaticBuffer value) {
return of(column, value, StaticBufferHandler.INSTANCE);
}
private static final<E,D> Entry of(E element, StaticArrayEntry.GetColVal<E,D> getter, StaticArrayEntry.DataHandler<D> datahandler) {
StaticArrayEntry entry = of(getter.getColumn(element),getter.getValue(element),datahandler);
//Add meta data if exists
if (getter.getMetaSchema(element).length>0) {
for (EntryMetaData meta : getter.getMetaSchema(element)) {
entry.setMetaData(meta,getter.getMetaData(element,meta));
}
}
return entry;
}
private static final<E,D> StaticArrayEntry of(D column, D value, StaticArrayEntry.DataHandler<D> datahandler) {
int valuePos = datahandler.getSize(column);
byte[] data = new byte[valuePos+datahandler.getSize(value)];
datahandler.copy(column,data,0);
datahandler.copy(value,data,valuePos);
return new StaticArrayEntry(data,valuePos);
}
public static interface GetColVal<E,D> {
public D getColumn(E element);
public D getValue(E element);
public EntryMetaData[] getMetaSchema(E element);
public Object getMetaData(E element, EntryMetaData meta);
}
public static final EntryMetaData[] EMPTY_SCHEMA = new EntryMetaData[0];
public static GetColVal<Entry,StaticBuffer> ENTRY_GETTER = new GetColVal<Entry, StaticBuffer>() {
@Override
public StaticBuffer getColumn(Entry entry) {
return entry.getColumn();
}
@Override
public StaticBuffer getValue(Entry entry) {
return entry.getValue();
}
@Override
public EntryMetaData[] getMetaSchema(Entry element) {
if (!element.hasMetaData()) return EMPTY_SCHEMA;
Map<EntryMetaData,Object> metas = element.getMetaData();
return metas.keySet().toArray(new EntryMetaData[metas.size()]);
}
@Override
public Object getMetaData(Entry element, EntryMetaData meta) {
return element.getMetaData().get(meta);
}
};
public static interface DataHandler<D> {
public int getSize(D data);
public void copy(D data, byte[] dest, int destOffset);
}
static enum ByteArrayHandler implements DataHandler<byte[]> {
INSTANCE;
@Override
public int getSize(byte[] data) {
return data.length;
}
@Override
public void copy(byte[] data, byte[] dest, int destOffset) {
System.arraycopy(data,0,dest,destOffset,data.length);
}
}
static enum ByteBufferHandler implements DataHandler<ByteBuffer> {
INSTANCE;
@Override
public int getSize(ByteBuffer data) {
return data.remaining();
}
@Override
public void copy(ByteBuffer data, byte[] dest, int destOffset) {
if (data.hasArray()) {
System.arraycopy(data.array(),data.arrayOffset()+data.position(),dest,destOffset,data.remaining());
} else {
data.mark();
data.get(dest,destOffset,data.remaining());
data.reset();
}
}
}
static enum StaticBufferHandler implements DataHandler<StaticBuffer> {
INSTANCE;
@Override
public int getSize(StaticBuffer data) {
return data.length();
}
@Override
public void copy(StaticBuffer data, byte[] dest, int destOffset) {
if (data instanceof StaticArrayBuffer) {
StaticArrayBuffer buffer = (StaticArrayBuffer) data;
buffer.copyTo(dest,destOffset);
} else throw new IllegalArgumentException("Expected StaticArrayBuffer but got: " + data.getClass());
}
}
}
class BaseStaticArrayEntry extends StaticArrayBuffer implements Entry {
private final int valuePosition;
public BaseStaticArrayEntry(byte[] array, int offset, int limit, int valuePosition) {
super(array,offset,limit);
Preconditions.checkArgument(valuePosition>0);
Preconditions.checkArgument(valuePosition<=length());
this.valuePosition=valuePosition;
}
public BaseStaticArrayEntry(byte[] array, int limit, int valuePosition) {
this(array, 0, limit, valuePosition);
}
public BaseStaticArrayEntry(byte[] array, int valuePosition) {
this(array, 0, array.length, valuePosition);
}
public BaseStaticArrayEntry(StaticBuffer buffer, int valuePosition) {
super(buffer);
Preconditions.checkArgument(valuePosition>0);
Preconditions.checkArgument(valuePosition<=length());
this.valuePosition=valuePosition;
}
@Override
public int getValuePosition() {
return valuePosition;
}
@Override
public boolean hasValue() {
return valuePosition<length();
}
@Override
public StaticBuffer getColumn() {
return getColumnAs(StaticBuffer.STATIC_FACTORY);
}
@Override
public <T> T getColumnAs(Factory<T> factory) {
return super.as(factory,0,valuePosition);
}
@Override
public StaticBuffer getValue() {
return getValueAs(StaticBuffer.STATIC_FACTORY);
}
@Override
public <T> T getValueAs(Factory<T> factory) {
return super.as(factory,valuePosition,super.length()-valuePosition);
}
//Override from StaticArrayBuffer to restrict to column
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null) return false;
if (!(o instanceof StaticBuffer)) return false;
Entry b = (Entry)o;
if (getValuePosition()!=b.getValuePosition()) return false;
return compareTo(getValuePosition(),b,getValuePosition())==0;
}
@Override
public int hashCode() {
return hashCode(getValuePosition());
}
@Override
public int compareTo(StaticBuffer other) {
int otherLen = (other instanceof Entry)?((Entry) other).getValuePosition():other.length();
return compareTo(getValuePosition(),other, otherLen);
}
@Override
public String toString() {
String s = super.toString();
int pos = getValuePosition()*4;
return s.substring(0,pos-1) + "->" + (getValuePosition()<length()?s.substring(pos):"");
}
//########## CACHE ############
@Override
public RelationCache getCache() {
return null;
}
@Override
public void setCache(RelationCache cache) {
throw new UnsupportedOperationException();
}
//########## META DATA ############
@Override
public boolean hasMetaData() {
return false;
}
@Override
public Map<EntryMetaData,Object> getMetaData() {
return EntryMetaData.EMPTY_METADATA;
}
}