
com.apicatalog.cborld.encoder.Encoder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of iridium-cbor-ld-jre8 Show documentation
Show all versions of iridium-cbor-ld-jre8 Show documentation
A CBOR-based serialization for Linked Data
The newest version!
package com.apicatalog.cborld.encoder;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.net.URI;
import java.util.Collection;
import com.apicatalog.cborld.CborLd;
import com.apicatalog.cborld.config.DefaultConfig;
import com.apicatalog.cborld.config.DictionaryAlgorithm;
import com.apicatalog.cborld.context.ContextError;
import com.apicatalog.cborld.dictionary.Dictionary;
import com.apicatalog.cborld.encoder.EncoderError.Code;
import com.apicatalog.cborld.encoder.value.ValueEncoder;
import com.apicatalog.cborld.loader.StaticContextLoader;
import com.apicatalog.cborld.mapper.Mapping;
import com.apicatalog.cborld.mapper.MappingProvider;
import com.apicatalog.cborld.mapper.TypeMap;
import com.apicatalog.cursor.ArrayCursor;
import com.apicatalog.cursor.ArrayItemCursor;
import com.apicatalog.cursor.MapCursor;
import com.apicatalog.cursor.MapEntryCursor;
import com.apicatalog.cursor.ValueCursor;
import com.apicatalog.jsonld.JsonLdError;
import com.apicatalog.jsonld.JsonLdOptions;
import com.apicatalog.jsonld.http.DefaultHttpClient;
import com.apicatalog.jsonld.http.media.MediaType;
import com.apicatalog.jsonld.loader.DocumentLoader;
import com.apicatalog.jsonld.loader.HttpLoader;
import co.nstant.in.cbor.CborBuilder;
import co.nstant.in.cbor.CborEncoder;
import co.nstant.in.cbor.CborException;
import co.nstant.in.cbor.builder.ArrayBuilder;
import co.nstant.in.cbor.builder.MapBuilder;
import co.nstant.in.cbor.model.DataItem;
import co.nstant.in.cbor.model.DoublePrecisionFloat;
import co.nstant.in.cbor.model.NegativeInteger;
import co.nstant.in.cbor.model.SimpleValue;
import co.nstant.in.cbor.model.UnicodeString;
import co.nstant.in.cbor.model.UnsignedInteger;
public class Encoder implements EncoderConfig {
protected final MapCursor document;
protected MappingProvider provider;
protected Dictionary index;
// options
protected Collection valueEncoders;
protected boolean compactArrays;
protected DocumentLoader loader;
protected boolean bundledContexts;
protected URI base;
protected Encoder(MapCursor document) {
this.document = document;
// default options
config(DefaultConfig.INSTANCE);
this.bundledContexts = DefaultConfig.STATIC_CONTEXTS;
this.base = null;
this.loader = null;
}
public static final Encoder create(MapCursor document) throws EncoderError {
if (document == null) {
throw new IllegalArgumentException("The 'document' parameter must not be null.");
}
return new Encoder(document);
}
/**
* If set to true, the encoder replaces arrays with
* just one element with that element during encoding saving one byte.
* Enabled by default.
*
* @param enable true
to enable arrays compaction
* @return {@link Encoder} instance
*
*/
public Encoder compactArray(boolean enable) {
compactArrays = enable;
return this;
}
/**
* Override any existing configuration by the given configuration set.
*
* @param config a configuration set
* @return {@link Encoder} instance
*/
public Encoder config(EncoderConfig config) {
this.compactArrays = config.isCompactArrays();
this.valueEncoders = config.valueEncoders();
this.provider = config.provider();
return this;
}
/**
* Set {@link DocumentLoader} used to fetch referenced JSON-LD contexts.
* If not set then default document loader provided by {@link JsonLdOptions} is used.
*
* @param loader a document loader to set
* @return {@link Encoder} instance
*/
public Encoder loader(DocumentLoader loader) {
this.loader = loader;
return this;
}
/**
* Use well-known contexts that are bundled with the library instead of fetching it online.
* true
by default. Disabling might cause slower processing.
*
* @param enable true
to use static bundled contexts
* @return {@link Encoder} instance
*/
public Encoder useBundledContexts(boolean enable) {
this.bundledContexts = enable;
return this;
}
/**
* If set, then is used as the input document's base IRI.
*
* @param base a document base
* @return {@link Encoder} instance
*/
public Encoder base(URI base) {
this.base = base;
return this;
}
/**
* Encode JSON-LD document as CBOR-LD document.
*
* @return a byte array representing the encoded CBOR-LD document.
*
* @throws EncoderError
* @throws ContextError
*/
public byte[] encode() throws EncoderError, ContextError {
if (loader == null) {
loader = new HttpLoader(DefaultHttpClient.defaultInstance());
((HttpLoader)loader).setFallbackContentType(MediaType.JSON);
}
if (bundledContexts) {
loader = new StaticContextLoader(loader);
}
try {
final Collection contexts = EncoderContext.get(document);
if (contexts.isEmpty()) { // is not JSON-LD document
throw new EncoderError(Code.InvalidDocument, "Not a valid JSON-LD document in a compacted form.");
}
return compress(document, contexts);
// non compressable context
} catch (IllegalArgumentException e) {
/* ignored, expected in a case of non compress-able documents */
}
throw new EncoderError(Code.InvalidDocument, "Non compress-able document.");
}
/**
* Compress the given JSON-LD document into CBOR-LD byte array.
*
* @see Compressed
* CBOR-LD Buffer Algorithm
*
* @param document the document to compress
* @param contextUrls a set of URLs of @context
referenced by the
* document
* @return the compressed document as byte array
*
* @throws IOException
* @throws ContextError
* @throws EncoderError
*/
final byte[] compress(final MapCursor document, Collection contextUrls) throws ContextError, EncoderError {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
final Mapping mapping = provider.getEncoderMapping(document, base, loader, this);
index = mapping.dictionary();
final CborBuilder builder = (CborBuilder) encode(document, new CborBuilder().addMap(), mapping.typeMap()).end();
// 2.CBOR Tag - 0xD9, CBOR-LD - 0x05, Compressed - CBOR-LD compression algorithm
// version 1 - 0x01
baos.write(CborLd.CBOR_LD_BYTE_PREFIX);
baos.write(CborLd.COMPRESSED_V1);
new CborEncoder(baos).encode(builder.build());
return baos.toByteArray();
} catch (CborException e) {
throw new EncoderError(Code.InvalidDocument, e);
} catch (IOException | JsonLdError e) {
throw new EncoderError(Code.Internal, e);
}
}
final MapBuilder> encode(final MapCursor object, final MapBuilder> builder, TypeMap typeMapping) throws EncoderError, JsonLdError {
if (object.isEmpty()) {
return builder;
}
MapBuilder> flow = builder;
for (final MapEntryCursor entry : object) {
final String property = entry.mapKey();
final BigInteger encodedProperty = index.getCode(property);
if (entry.isArray()) {
final DataItem key = encodedProperty != null
? new UnsignedInteger(encodedProperty.add(BigInteger.ONE))
: new UnicodeString(property);
if (compactArrays && object.asArray().size() == 1) {
object.asArray().item(0);
if (object.isMap()) {
final TypeMap propertyTypeMapping = typeMapping.getMapping(property);
flow = (MapBuilder>) encode(object.asMap(), flow.putMap(key), propertyTypeMapping).end();
} else if (object.isArray()) {
flow = (MapBuilder>) encode(
object.asArray(),
flow.putArray(key),
property,
typeMapping
).end();
} else {
final DataItem value = encode(object, property, typeMapping);
flow = flow.put(key, value);
}
object.parent();
} else {
flow = (MapBuilder>) encode(object.asArray(), flow.putArray(key),
property,
typeMapping
).end();
}
continue;
}
final DataItem key = encodedProperty != null
? new UnsignedInteger(encodedProperty)
: new UnicodeString(property);
if (entry.isMap()) {
final TypeMap propertyTypeMapping = typeMapping.getMapping(property);
flow = (MapBuilder>) encode(entry.asMap(), flow.putMap(key),
propertyTypeMapping
).end();
continue;
}
final DataItem value = encode(entry, property, typeMapping);
flow = flow.put(key, value);
}
object.parent();
return flow;
}
final DataItem encode(final ValueCursor value, final String term, TypeMap typeMapping) throws EncoderError {
if (value.isBoolean()) {
return value.booleanValue() ? SimpleValue.TRUE : SimpleValue.FALSE;
}
if (value.isString()) {
if (typeMapping != null) {
final Collection types = typeMapping.getType(term);
for (final ValueEncoder valueEncoder : valueEncoders) {
final DataItem dataItem = valueEncoder.encode(index, value, term, types);
if (dataItem != null) {
return dataItem;
}
}
}
return new UnicodeString(value.stringValue());
}
if (value.isInteger()) {
BigInteger integer = value.integerValue();
switch (integer.signum()) {
case -1:
return new NegativeInteger(integer);
case 0:
return new UnsignedInteger(BigInteger.ZERO);
case 1:
return new UnsignedInteger(integer);
}
}
if (value.isDecimal()) {
return new DoublePrecisionFloat(value.decimalValue().doubleValue());
}
if (value.isNull()) {
return SimpleValue.NULL;
}
throw new IllegalStateException();
}
final ArrayBuilder> encode(final ArrayCursor object, final ArrayBuilder> builder, String property, TypeMap typeMapping) throws EncoderError, JsonLdError {
if (object.isEmpty()) {
return builder;
}
ArrayBuilder> flow = builder;
for (final ArrayItemCursor item : object) {
if (item.isMap()) {
flow = (ArrayBuilder>) encode(item.asMap(), flow.startMap(), typeMapping).end();
continue;
}
if (item.isArray()) {
flow = (ArrayBuilder>) encode(item.asArray(), flow.startArray(), property, typeMapping).end();
continue;
}
final DataItem value = encode(item, property, typeMapping);
flow = flow.add(value);
}
object.parent();
return flow;
}
@Override
public boolean isCompactArrays() {
return compactArrays;
}
@Override
public DictionaryAlgorithm dictonaryAlgorithm() {
return DictionaryAlgorithm.ProcessingOrderAppliedContexts;
}
@Override
public Collection valueEncoders() {
return valueEncoders;
}
@Override
public MappingProvider provider() {
return provider;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy