/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.translator.infinispan.hotrod;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.TreeMap;
import org.infinispan.protostream.ImmutableSerializationContext;
import org.infinispan.protostream.RawProtoStreamReader;
import org.infinispan.protostream.RawProtoStreamWriter;
import org.infinispan.protostream.RawProtobufMarshaller;
import org.infinispan.protostream.impl.ByteArrayOutputStreamEx;
import org.infinispan.protostream.impl.RawProtoStreamWriterImpl;
import org.teiid.infinispan.api.InfinispanDocument;
import org.teiid.infinispan.api.ProtobufDataManager;
import org.teiid.infinispan.api.TableWireFormat;
import org.teiid.translator.TranslatorException;
import org.teiid.translator.document.Document;
import org.teiid.translator.infinispan.hotrod.DocumentFilter.Action;
public class TeiidTableMarsheller implements RawProtobufMarshaller<InfinispanDocument> {
private String documentName;
private TreeMap<Integer, TableWireFormat> wireMap = new TreeMap<>();
private DocumentFilter docFilter;
public TeiidTableMarsheller(String docName, TreeMap<Integer, TableWireFormat> wireMap) {
this(docName, wireMap, null);
}
public TeiidTableMarsheller(String docName, TreeMap<Integer, TableWireFormat> wireMap, DocumentFilter filter) {
this.documentName= docName;
this.wireMap = wireMap;
this.docFilter = filter;
}
@Override
public String getTypeName() {
return this.documentName;
}
// Read from ISPN Types >> Teiid Types
@Override
public InfinispanDocument readFrom(ImmutableSerializationContext ctx, RawProtoStreamReader in) throws IOException {
InfinispanDocument row = new InfinispanDocument(this.documentName, this.wireMap, null);
readDocument(in, row, this.wireMap, this.docFilter);
return row;
}
// Write from Teiid Types >> ISPN Types
@Override
public void writeTo(ImmutableSerializationContext ctx, RawProtoStreamWriter out, InfinispanDocument document)
throws IOException {
TreeMap<Integer, TableWireFormat> wireMap = document.getWireMap();
for (Entry<Integer, TableWireFormat> entry : wireMap.entrySet()) {
TableWireFormat twf = entry.getValue();
if (twf == null) {
throw new IOException("Error in wireformat");
}
int tag = twf.getWriteTag();
if (twf.isNested()) {
List<? extends Document> children = document.getChildDocuments(twf.getAttributeName());
if (children != null) {
for (Document d : children) {
ByteArrayOutputStreamEx baos = new ByteArrayOutputStreamEx();
RawProtoStreamWriter rpsw = RawProtoStreamWriterImpl.newInstance(baos);
writeTo(ctx, rpsw, (InfinispanDocument)d);
rpsw.flush();
baos.flush();
// here readtag because this is inner object, even other one uses write tag but calculated
// based on the write operation used.
out.writeBytes(tag, baos.getByteBuffer());
}
}
continue;
}
Object value = document.getProperties().get(twf.getAttributeName());
if (value == null) {
continue;
}
ArrayList<Object> values = null;
boolean array = twf.isArrayType();
if (array) {
values = (ArrayList<Object>)value;
}
switch (twf.getProtobufType()) {
case DOUBLE:
if (array) {
for (Object o:values) {
out.writeDouble(tag, ProtobufDataManager.convertToInfinispan(Double.class, o));
}
} else {
out.writeDouble(tag, ProtobufDataManager.convertToInfinispan(Double.class, value));
}
break;
case FLOAT:
if (array) {
for (Object o:values) {
out.writeFloat(tag, ProtobufDataManager.convertToInfinispan(Float.class, o));
}
} else {
out.writeFloat(tag, ProtobufDataManager.convertToInfinispan(Float.class, value));
}
break;
case BOOL:
if (array) {
for (Object o:values) {
out.writeBool(tag, ProtobufDataManager.convertToInfinispan(Boolean.class, o));
}
} else {
out.writeBool(tag, ProtobufDataManager.convertToInfinispan(Boolean.class, value));
}
break;
case STRING:
if (array) {
for (Object o:values) {
out.writeString(tag, ProtobufDataManager.convertToInfinispan(String.class, o));
}
} else {
out.writeString(tag, ProtobufDataManager.convertToInfinispan(String.class, value));
}
break;
case BYTES:
if (array) {
for (Object o:values) {
out.writeBytes(tag, ProtobufDataManager.convertToInfinispan(byte[].class, o));
}
} else {
out.writeBytes(tag, ProtobufDataManager.convertToInfinispan(byte[].class, value));
}
break;
case INT32:
if (array) {
for (Object o:values) {
out.writeInt32(tag, ProtobufDataManager.convertToInfinispan(Integer.class, o));
}
} else {
out.writeInt32(tag, ProtobufDataManager.convertToInfinispan(Integer.class, value));
}
break;
case SFIXED32:
if (array) {
for (Object o:values) {
out.writeSFixed32(tag, ProtobufDataManager.convertToInfinispan(Integer.class, o));
}
} else {
out.writeSFixed32(tag, ProtobufDataManager.convertToInfinispan(Integer.class, value));
}
break;
case FIXED32:
if (array) {
for (Object o:values) {
out.writeFixed32(tag, ProtobufDataManager.convertToInfinispan(Integer.class, o));
}
} else {
out.writeFixed32(tag, ProtobufDataManager.convertToInfinispan(Integer.class, value));
}
break;
case UINT32:
if (array) {
for (Object o:values) {
out.writeUInt32(tag, ProtobufDataManager.convertToInfinispan(Integer.class, o));
}
} else {
out.writeUInt32(tag, ProtobufDataManager.convertToInfinispan(Integer.class, value));
}
break;
case SINT32:
if (array) {
for (Object o:values) {
out.writeSInt32(tag, ProtobufDataManager.convertToInfinispan(Integer.class, o));
}
} else {
out.writeSInt32(tag, ProtobufDataManager.convertToInfinispan(Integer.class, value));
}
break;
case INT64:
if (array) {
for (Object o:values) {
out.writeInt64(tag, ProtobufDataManager.convertToInfinispan(Long.class, o));
}
} else {
out.writeInt64(tag, ProtobufDataManager.convertToInfinispan(Long.class, value));
}
break;
case UINT64:
if (array) {
for (Object o:values) {
out.writeUInt64(tag, ProtobufDataManager.convertToInfinispan(Long.class, o));
}
} else {
out.writeUInt64(tag, ProtobufDataManager.convertToInfinispan(Long.class, value));
}
break;
case FIXED64:
if (array) {
for (Object o:values) {
out.writeFixed64(tag, ProtobufDataManager.convertToInfinispan(Long.class, o));
}
} else {
out.writeFixed64(tag, ProtobufDataManager.convertToInfinispan(Long.class, value));
}
break;
case SFIXED64:
if (array) {
for (Object o:values) {
out.writeSFixed64(tag, ProtobufDataManager.convertToInfinispan(Long.class, o));
}
} else {
out.writeSFixed64(tag, ProtobufDataManager.convertToInfinispan(Long.class, value));
}
break;
case SINT64:
if (array) {
for (Object o:values) {
out.writeSInt64(tag, ProtobufDataManager.convertToInfinispan(Long.class, o));
}
} else {
out.writeSInt64(tag, ProtobufDataManager.convertToInfinispan(Long.class, value));
}
break;
default:
throw new IOException("Unexpected field type : " + twf.getProtobufType());
}
}
}
static void readDocument(RawProtoStreamReader in, InfinispanDocument document,
TreeMap<Integer, TableWireFormat> columnMap, DocumentFilter filter) throws IOException {
while (true) {
int tag = in.readTag();
if (tag == 0) {
break;
}
TableWireFormat twf = columnMap.get(tag);
if (twf == null) {
throw new IOException("Error in wireformat");
}
if (twf.isNested()) {
InfinispanDocument child = new InfinispanDocument(twf.getAttributeName(), twf.getNestedWireMap(), document);
int length = in.readRawVarint32();
int oldLimit = in.pushLimit(length);
readDocument(in, child, twf.getNestedWireMap(), filter);
try {
if (filter == null) {
document.addChildDocument(twf.getAttributeName(), child);
document.incrementUpdateCount(twf.getAttributeName(), true);
} else {
boolean matched = filter.matches(document.getProperties(), child.getProperties());
if (matched) {
if (filter.action() == Action.ADD) { // SELECT
document.addChildDocument(twf.getAttributeName(), child);
} else if (filter.action() == Action.REMOVE) { // DELETE
// no op, ie removed.
} else {
// UPDATE
document.addChildDocument(twf.getAttributeName(), child);
}
} else {
if (filter.action() == Action.ALWAYSADD || filter.action() == Action.REMOVE) {
document.addChildDocument(twf.getAttributeName(), child);
}
}
// keep track all the adds and removed based on this filter
child.setMatched(matched);
document.incrementUpdateCount(twf.getAttributeName(), matched);
}
} catch (TranslatorException e) {
throw new IOException(e.getCause());
}
in.checkLastTagWas(0);
in.popLimit(oldLimit);
continue;
}
switch (twf.getProtobufType()) {
case DOUBLE:
document.addProperty(tag, ProtobufDataManager.convertToRuntime(twf.expectedType(), in.readDouble()));
break;
case FLOAT:
document.addProperty(tag, ProtobufDataManager.convertToRuntime(twf.expectedType(), in.readFloat()));
break;
case BOOL:
document.addProperty(tag, ProtobufDataManager.convertToRuntime(twf.expectedType(),in.readBool()));
break;
case STRING:
document.addProperty(tag, ProtobufDataManager.convertToRuntime(twf.expectedType(), in.readString()));
break;
case BYTES:
document.addProperty(tag, ProtobufDataManager.convertToRuntime(twf.expectedType(), in.readByteArray()));
break;
case INT32:
document.addProperty(tag, ProtobufDataManager.convertToRuntime(twf.expectedType(), in.readInt32()));
break;
case SFIXED32:
document.addProperty(tag, ProtobufDataManager.convertToRuntime(twf.expectedType(), in.readSFixed32()));
break;
case FIXED32:
document.addProperty(tag, ProtobufDataManager.convertToRuntime(twf.expectedType(), in.readFixed32()));
break;
case UINT32:
document.addProperty(tag, ProtobufDataManager.convertToRuntime(twf.expectedType(), in.readUInt32()));
break;
case SINT32:
document.addProperty(tag, ProtobufDataManager.convertToRuntime(twf.expectedType(), in.readSInt32()));
break;
case INT64:
document.addProperty(tag, ProtobufDataManager.convertToRuntime(twf.expectedType(), in.readInt64()));
break;
case UINT64:
document.addProperty(tag, ProtobufDataManager.convertToRuntime(twf.expectedType(), in.readUInt64()));
break;
case FIXED64:
document.addProperty(tag, ProtobufDataManager.convertToRuntime(twf.expectedType(), in.readFixed64()));
break;
case SFIXED64:
document.addProperty(tag, ProtobufDataManager.convertToRuntime(twf.expectedType(), in.readSFixed64()));
break;
case SINT64:
document.addProperty(tag, ProtobufDataManager.convertToRuntime(twf.expectedType(), in.readSInt64()));
break;
default:
throw new IOException("Unexpected field type : " + twf.getProtobufType());
}
}
}
@Override
public Class getJavaClass() {
return InfinispanDocument.class;
}
}