All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jsimpledb.kv.util.XMLSerializer Maven / Gradle / Ivy

The newest version!

/*
 * Copyright (C) 2015 Archie L. Cobbs. All rights reserved.
 */

package org.jsimpledb.kv.util;

import com.google.common.base.Preconditions;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.Writer;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;

import org.dellroad.stuff.xml.IndentXMLStreamWriter;
import org.jsimpledb.kv.KVPair;
import org.jsimpledb.kv.KVStore;
import org.jsimpledb.util.AbstractXMLStreaming;
import org.jsimpledb.util.ByteUtil;
import org.jsimpledb.util.CloseableIterator;

/**
 * Utility methods for serializing and deserializing the contents of a {@link org.jsimpledb.kv.KVStore} to/from XML.
 *
 * 

* The XML has a simple format; empty values may be omitted: *

 *  <xml version="1.0" encoding="UTF-8"?>
 *
 *  <entries>
 *      <entry>
 *          <key>013f7b</key>
 *          <value>5502</value>
 *      </entry>
 *      <entry>
 *          <key>ee7698</key>
 *      </entry>
 *      ...
 *  </entries>
 *  
*/ public class XMLSerializer extends AbstractXMLStreaming { public static final QName ENTRIES_TAG = new QName("entries"); public static final QName ENTRY_TAG = new QName("entry"); public static final QName KEY_TAG = new QName("key"); public static final QName VALUE_TAG = new QName("value"); private final KVStore kv; /** * Constructor. * * @param kv key/value store on which to operate * @throws IllegalArgumentException if {@code kv} is null */ public XMLSerializer(KVStore kv) { Preconditions.checkArgument(kv != null, "null kv"); this.kv = kv; } /** * Import key/value pairs into the {@link KVStore} associated with this instance from the given XML input. * *

* The {@code input} is not closed by this method. * * @param input XML input * @return the number of key/value pairs read * @throws XMLStreamException if an error occurs * @throws IllegalArgumentException if {@code input} is null */ public int read(InputStream input) throws XMLStreamException { Preconditions.checkArgument(input != null, "null input"); return this.read(XMLInputFactory.newFactory().createXMLStreamReader(input)); } /** * Import key/value pairs into the {@link KVStore} associated with this instance from the given XML input. * This method expects to see an opening {@code } as the next event (not counting whitespace, comments, etc.), * which is then consumed up through the closing {@code } event. The {@code reader} is not closed by this method. * Therefore, this tag could be part of a larger XML document. * * @param reader XML reader * @return the number of key/value pairs read * @throws XMLStreamException if an error occurs * @throws IllegalArgumentException if {@code reader} is null */ public int read(XMLStreamReader reader) throws XMLStreamException { Preconditions.checkArgument(reader != null, "null reader"); this.expect(reader, false, ENTRIES_TAG); int count; for (count = 0; this.expect(reader, true, ENTRY_TAG); count++) { this.expect(reader, false, KEY_TAG); byte[] key; try { key = ByteUtil.parse(reader.getElementText()); } catch (IllegalArgumentException e) { throw new XMLStreamException("invalid hexadecimal key", reader.getLocation(), e); } if (!this.expect(reader, true, VALUE_TAG)) { this.kv.put(key, ByteUtil.EMPTY); continue; // closing tag alread read } byte[] value; try { value = ByteUtil.parse(reader.getElementText()); } catch (IllegalArgumentException e) { throw new XMLStreamException("invalid hexadecimal value", reader.getLocation(), e); } this.kv.put(key, value); this.expectClose(reader); // read closing tag } return count; } /** * Export all key/value pairs from the {@link KVStore} associated with this instance to the given output. * *

* The {@code output} is not closed by this method. * * @param output XML output; will not be closed by this method * @param indent true to indent output, false for all on one line * @return the number of key/value pairs written * @throws XMLStreamException if an error occurs * @throws IllegalArgumentException if {@code output} is null */ public int write(OutputStream output, boolean indent) throws XMLStreamException { Preconditions.checkArgument(output != null, "null output"); XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(output, "UTF-8"); if (indent) xmlWriter = new IndentXMLStreamWriter(xmlWriter); xmlWriter.writeStartDocument("UTF-8", "1.0"); return this.write(xmlWriter, null, null); } /** * Export all key/value pairs from the {@link KVStore} associated with this instance to the given writer. * *

* The {@code writer} is not closed by this method. * * @param writer XML output; will not be closed by this method * @param indent true to indent output, false for all on one line * @return the number of key/value pairs written * @throws XMLStreamException if an error occurs * @throws IllegalArgumentException if {@code writer} is null */ public int write(Writer writer, boolean indent) throws XMLStreamException { Preconditions.checkArgument(writer != null, "null writer"); XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(writer); if (indent) xmlWriter = new IndentXMLStreamWriter(xmlWriter); xmlWriter.writeStartDocument("1.0"); return this.write(xmlWriter, null, null); } /** * Export a range of key/value pairs from the {@link KVStore} associated with this instance to the given XML output. * *

* This method writes a start element as its first action, allowing the output to be embedded into a larger XML document. * Callers not embedding the output may with to precede invocation of this method with a call to * {@link XMLStreamWriter#writeStartDocument writer.writeStartDocument()}. * *

* The {@code writer} is not closed by this method. * * @param writer XML writer; will not be closed by this method * @param minKey minimum key (inclusive), or null for none * @param maxKey maximum key (exclusive), or null for none * @return the number of key/value pairs written * @throws XMLStreamException if an error occurs * @throws IllegalArgumentException if {@code writer} is null */ public int write(XMLStreamWriter writer, byte[] minKey, byte[] maxKey) throws XMLStreamException { Preconditions.checkArgument(writer != null, "null writer"); writer.setDefaultNamespace(ENTRIES_TAG.getNamespaceURI()); writer.writeStartElement(ENTRIES_TAG.getNamespaceURI(), ENTRIES_TAG.getLocalPart()); int count = 0; try (CloseableIterator i = this.kv.getRange(minKey, maxKey)) { while (i.hasNext()) { writer.writeStartElement(ENTRY_TAG.getNamespaceURI(), ENTRY_TAG.getLocalPart()); final KVPair pair = i.next(); this.writeElement(writer, KEY_TAG, pair.getKey()); final byte[] value = pair.getValue(); if (value.length > 0) this.writeElement(writer, VALUE_TAG, value); writer.writeEndElement(); count++; } } writer.writeEndElement(); writer.flush(); return count; } // Internal methods private void writeElement(XMLStreamWriter writer, QName element, byte[] value) throws XMLStreamException { this.writeElement(writer, element, ByteUtil.toString(value)); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy