package com.bagri.client.hazelcast.serialize;
import static javax.xml.xquery.XQItemType.*;
import java.io.IOException;
import java.util.GregorianCalendar;
import javax.xml.datatype.Duration;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;
import javax.xml.xquery.XQDataFactory;
import javax.xml.xquery.XQException;
import javax.xml.xquery.XQItem;
import javax.xml.xquery.XQItemType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Attr;
import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.ProcessingInstruction;
import org.w3c.dom.Text;
import com.bagri.support.util.XMLUtils;
import com.bagri.support.util.XQUtils;
import com.hazelcast.nio.ObjectDataInput;
import com.hazelcast.nio.ObjectDataOutput;
import com.hazelcast.nio.serialization.StreamSerializer;
public class XQItemSerializer implements StreamSerializer<XQItem> {
private static final Logger logger = LoggerFactory.getLogger(XQItemSerializer.class);
private XQDataFactory xqFactory;
protected XQDataFactory getXQDataFactory() {
return xqFactory;
}
public void setXQDataFactory(XQDataFactory xqDataFactory) {
logger.trace("setXQDataFactory; got factory: {}", xqDataFactory);
this.xqFactory = xqDataFactory;
}
@Override
public int getTypeId() {
return DataSerializationFactoryImpl.cli_XQItem;
}
@Override
public void destroy() {
// nothing to destroy...
}
@Override
public XQItem read(ObjectDataInput in) throws IOException {
try {
XQItemType type = in.readObject();
logger.trace("read; got type: {}", type);
if (type == null) {
Object value = in.readObject();
if (value == null) {
logger.info("read; got null value, returning null");
return null;
}
return xqFactory.createItemFromObject(value, type);
}
switch (type.getItemKind()) {
case XQITEMKIND_ATOMIC: {
int bType = type.getBaseType();
switch (bType) {
case XQBASETYPE_ANYURI:
case XQBASETYPE_NAME:
case XQBASETYPE_NCNAME:
case XQBASETYPE_NMTOKEN:
case XQBASETYPE_UNTYPEDATOMIC: {
return xqFactory.createItemFromString(in.readUTF(), type);
}
case XQBASETYPE_BASE64BINARY:
case XQBASETYPE_HEXBINARY:
int len = in.readInt();
byte[] ba = new byte[len];
in.readFully(ba);
return xqFactory.createItemFromObject(ba, type);
case XQBASETYPE_BOOLEAN:
return xqFactory.createItemFromBoolean(in.readBoolean(), type);
case XQBASETYPE_BYTE:
return xqFactory.createItemFromByte(in.readByte(), type);
case XQBASETYPE_SHORT:
case XQBASETYPE_UNSIGNED_BYTE:
return xqFactory.createItemFromShort(in.readShort(), type);
case XQBASETYPE_INT:
case XQBASETYPE_UNSIGNED_SHORT:
return xqFactory.createItemFromInt(in.readInt(), type);
case XQBASETYPE_LONG:
case XQBASETYPE_UNSIGNED_INT:
return xqFactory.createItemFromLong(in.readLong(), type);
case XQBASETYPE_INTEGER:
case XQBASETYPE_NEGATIVE_INTEGER:
case XQBASETYPE_NONNEGATIVE_INTEGER:
case XQBASETYPE_NONPOSITIVE_INTEGER:
case XQBASETYPE_POSITIVE_INTEGER:
case XQBASETYPE_UNSIGNED_LONG:
// BigInteger
return xqFactory.createItemFromObject(in.readObject(), type);
case XQBASETYPE_DECIMAL:
// BigDecimal
return xqFactory.createItemFromObject(in.readObject(), type);
case XQBASETYPE_DOUBLE:
return xqFactory.createItemFromDouble(in.readDouble(), type);
case XQBASETYPE_FLOAT:
return xqFactory.createItemFromFloat(in.readFloat(), type);
case XQBASETYPE_DATE:
case XQBASETYPE_DATETIME:
case XQBASETYPE_TIME:
case XQBASETYPE_GDAY:
case XQBASETYPE_GMONTH:
case XQBASETYPE_GMONTHDAY:
case XQBASETYPE_GYEAR:
case XQBASETYPE_GYEARMONTH: {
// must be XMLGregorianCalendar
GregorianCalendar gc = (GregorianCalendar) in.readObject();
XMLGregorianCalendar xgc = XMLUtils.getXMLCalendar(gc, bType);
return xqFactory.createItemFromObject(xgc, type);
}
case XQBASETYPE_DURATION:
case XQBASETYPE_DAYTIMEDURATION:
case XQBASETYPE_YEARMONTHDURATION: {
// must be string representation of Duration
Duration xd = XMLUtils.getXMLDuration(in.readUTF(), bType);
return xqFactory.createItemFromObject(xd, type);
}
case XQBASETYPE_QNAME: {
QName qname = new QName(in.readUTF());
return xqFactory.createItemFromObject(qname, type);
}
default: {
String value = in.readUTF();
logger.trace("read; got value: {}", value);
return xqFactory.createItemFromAtomicValue(value, type);
}
}
}
// TODO: find some better approach for XML results serialization
// current Xerces DOM parser is too slow!
case XQITEMKIND_DOCUMENT: {
String value = in.readUTF();
if (value != null && value.length() > 0) {
Document doc = XMLUtils.textToDocument(value);
return xqFactory.createItemFromNode(doc, type);
} else {
Document doc = XMLUtils.textToDocument("<e/>");
return xqFactory.createItemFromNode(doc.createDocumentFragment(), type);
}
}
case XQITEMKIND_DOCUMENT_ELEMENT:
case XQITEMKIND_DOCUMENT_SCHEMA_ELEMENT: {
String value = in.readUTF();
XQItem result = xqFactory.createItemFromDocument(value, null, type);
return result;
}
case XQITEMKIND_ELEMENT:
case XQITEMKIND_SCHEMA_ELEMENT: {
Document doc = XMLUtils.textToDocument(in.readUTF());
return xqFactory.createItemFromNode(doc.getDocumentElement(), type);
}
case XQITEMKIND_ATTRIBUTE:
case XQITEMKIND_SCHEMA_ATTRIBUTE: {
String value = in.readUTF();
Document doc = XMLUtils.textToDocument(value);
Attr a = (Attr) doc.getDocumentElement().getAttributes().item(0);
return xqFactory.createItemFromNode(a, type);
}
case XQITEMKIND_COMMENT: {
String value = in.readUTF();
Document doc = XMLUtils.textToDocument(value);
return xqFactory.createItemFromNode(doc.getDocumentElement().getFirstChild(), type);
}
case XQITEMKIND_NODE: {
String value = in.readUTF();
Document doc = XMLUtils.textToDocument(value);
return xqFactory.createItemFromNode(doc, type);
}
case XQITEMKIND_PI: {
String value = in.readUTF();
Document doc = XMLUtils.textToDocument(value);
return xqFactory.createItemFromNode(doc.getDocumentElement().getFirstChild(), type);
}
case XQITEMKIND_TEXT: {
String value = in.readUTF();
Document doc = XMLUtils.textToDocument(value);
return xqFactory.createItemFromNode(doc.getDocumentElement().getFirstChild(), type);
}
}
logger.info("read; no relevant item created, returning null");
return null;
} catch (XQException ex) {
throw new IOException(ex);
}
}
@Override
public void write(ObjectDataOutput out, XQItem item) throws IOException {
try {
XQItemType type = item.getItemType();
out.writeObject(type);
logger.trace("write; got type: {}", type);
if (XQUtils.isBaseTypeSupported(type.getItemKind())) {
int bType = type.getBaseType();
if (XQUtils.isAtomicType(bType)) {
switch (bType) {
case XQBASETYPE_BASE64BINARY:
case XQBASETYPE_HEXBINARY: {
byte[] ba = (byte[]) item.getObject();
out.writeInt(ba.length);
out.write(ba);
return;
}
case XQBASETYPE_BOOLEAN: {
out.writeBoolean(item.getBoolean());
return;
}
case XQBASETYPE_BYTE: {
out.writeByte(item.getByte());
return;
}
case XQBASETYPE_INT:
case XQBASETYPE_UNSIGNED_SHORT: {
out.writeInt(item.getInt());
return;
}
case XQBASETYPE_LONG:
case XQBASETYPE_UNSIGNED_INT: {
out.writeLong(item.getLong());
return;
}
case XQBASETYPE_SHORT:
case XQBASETYPE_UNSIGNED_BYTE: {
out.writeShort(item.getShort());
return;
}
case XQBASETYPE_INTEGER:
case XQBASETYPE_NEGATIVE_INTEGER:
case XQBASETYPE_NONNEGATIVE_INTEGER:
case XQBASETYPE_NONPOSITIVE_INTEGER:
case XQBASETYPE_POSITIVE_INTEGER:
case XQBASETYPE_UNSIGNED_LONG: {
// this must be BigInteger
out.writeObject(item.getObject());
return;
}
case XQBASETYPE_DOUBLE: {
out.writeDouble(item.getDouble());
return;
}
case XQBASETYPE_FLOAT: {
out.writeFloat(item.getFloat());
return;
}
case XQBASETYPE_DECIMAL: {
// this must be BigDecimal
out.writeObject(item.getObject());
return;
}
case XQBASETYPE_DATE:
case XQBASETYPE_DATETIME:
case XQBASETYPE_TIME:
case XQBASETYPE_GDAY:
case XQBASETYPE_GMONTH:
case XQBASETYPE_GMONTHDAY:
case XQBASETYPE_GYEAR:
case XQBASETYPE_GYEARMONTH: {
// must be XMLGregorianCalendar
XMLGregorianCalendar xgc = (XMLGregorianCalendar) item.getObject();
out.writeObject(xgc.toGregorianCalendar());
return;
}
default: {
out.writeUTF(item.getItemAsString(null));
return;
}
}
}
}
// TODO: try to find some other serialization mechanism
// for DOM objects..
switch (type.getItemKind()) {
case XQITEMKIND_DOCUMENT:
case XQITEMKIND_DOCUMENT_ELEMENT:
case XQITEMKIND_DOCUMENT_SCHEMA_ELEMENT:
case XQITEMKIND_ELEMENT:
case XQITEMKIND_SCHEMA_ELEMENT: {
out.writeUTF(item.getItemAsString(null));
return;
}
case XQITEMKIND_ATTRIBUTE:
case XQITEMKIND_SCHEMA_ATTRIBUTE: {
Attr a = (Attr) item.getNode();
String xml = "<e " + a.getName() + "=\"" + a.getNodeValue() + "\"/>";
out.writeUTF(xml);
return;
}
case XQITEMKIND_COMMENT: {
Comment c = (Comment) item.getNode();
String xml = "<e><!--" + c.getNodeValue() + "--></e>";
out.writeUTF(xml);
return;
}
case XQITEMKIND_NODE: {
// !?!
out.writeUTF(item.getItemAsString(null));
return;
}
case XQITEMKIND_PI: {
ProcessingInstruction pi = (ProcessingInstruction) item.getNode();
String xml = "<e><?" + pi.getTarget() + " " + pi.getData() + "?></e>";
out.writeUTF(xml);
return;
}
case XQITEMKIND_TEXT: {
Text t = (Text) item.getNode();
String xml = "<e>" + t.getNodeValue() + "</e>";
out.writeUTF(xml);
return;
}
default: {
logger.info("write; wrong item kind: {}, writing as is", type.getItemKind());
out.writeObject(item.getObject());
}
}
} catch (XQException ex) {
throw new IOException(ex);
}
}
}