Skip to content

Commit 0c8eafb

Browse files
Adjusted behavior of the base64 item and adapter to store the encoded text, instead of decoded text. This should make the use of this item more effecient. Added the 'hex-binary' data type to represent raw binary data.
1 parent f7932f3 commit 0c8eafb

File tree

17 files changed

+607
-172
lines changed

17 files changed

+607
-172
lines changed

core/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@
9999
<groupId>nl.talsmasoftware</groupId>
100100
<artifactId>lazy4j</artifactId>
101101
</dependency>
102+
<dependency>
103+
<groupId>commons-codec</groupId>
104+
<artifactId>commons-codec</artifactId>
105+
</dependency>
102106
<dependency>
103107
<groupId>org.apache.commons</groupId>
104108
<artifactId>commons-text</artifactId>

core/src/main/java/gov/nist/secauto/metaschema/core/datatype/AbstractDataTypeAdapter.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@
3939
* @param <TYPE>
4040
* the raw Java type this adapter supports
4141
* @param <ITEM_TYPE>
42-
* the metapath item type corresponding to the raw Java type supported
43-
* by the adapter
42+
* the metapath item type supported by the adapter
4443
*/
4544
public abstract class AbstractDataTypeAdapter<TYPE, ITEM_TYPE extends IAnyAtomicItem>
4645
implements IDataTypeAdapter<TYPE> {
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
/*
2+
* SPDX-FileCopyrightText: none
3+
* SPDX-License-Identifier: CC0-1.0
4+
*/
5+
6+
package gov.nist.secauto.metaschema.core.datatype.adapter;
7+
8+
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes;
9+
10+
import gov.nist.secauto.metaschema.core.datatype.AbstractDataTypeAdapter;
11+
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem;
12+
import gov.nist.secauto.metaschema.core.metapath.type.AbstractAtomicOrUnionType;
13+
import gov.nist.secauto.metaschema.core.util.ObjectUtils;
14+
15+
import org.apache.commons.codec.BinaryDecoder;
16+
import org.apache.commons.codec.BinaryEncoder;
17+
import org.apache.commons.codec.DecoderException;
18+
import org.apache.commons.codec.EncoderException;
19+
20+
import java.nio.ByteBuffer;
21+
import java.nio.charset.StandardCharsets;
22+
import java.util.Arrays;
23+
24+
import edu.umd.cs.findbugs.annotations.NonNull;
25+
26+
/**
27+
* Maintains a byte buffer representation of a byte stream.
28+
* <p>
29+
* The maintained byte stream is kept in a decoded form.
30+
*
31+
* @param <ITEM_TYPE>
32+
* the metapath item type supported by the adapter
33+
*/
34+
public abstract class AbstractBinaryAdapter<ITEM_TYPE extends IAnyAtomicItem>
35+
extends AbstractDataTypeAdapter<ByteBuffer, ITEM_TYPE> {
36+
37+
/**
38+
* Construct a new Java type adapter for a provided class.
39+
*
40+
* @param itemClass
41+
* the Java type of the Matepath item this adapter supports
42+
* @param castExecutor
43+
* the method to call to cast an item to an item based on this type
44+
*/
45+
protected AbstractBinaryAdapter(
46+
@NonNull Class<ITEM_TYPE> itemClass,
47+
@NonNull AbstractAtomicOrUnionType.ICastExecutor<ITEM_TYPE> castExecutor) {
48+
super(ByteBuffer.class, itemClass, castExecutor);
49+
}
50+
51+
@NonNull
52+
protected abstract BinaryDecoder getDecoder();
53+
54+
@NonNull
55+
protected abstract BinaryEncoder getEncoder();
56+
57+
@NonNull
58+
private static byte[] stringToBytes(@NonNull String text) {
59+
return text.getBytes(StandardCharsets.UTF_8);
60+
}
61+
62+
@NonNull
63+
private static String bytesToString(@NonNull byte[] bytes) {
64+
return new String(bytes, StandardCharsets.UTF_8);
65+
}
66+
67+
private static String elide(@NonNull String text, int length) {
68+
return text.length() <= length ? text : text.substring(0, length) + "…";
69+
}
70+
71+
@NonNull
72+
public byte[] encode(@NonNull byte[] decodedBytes) {
73+
try {
74+
return ObjectUtils.notNull(getEncoder().encode(decodedBytes));
75+
} catch (EncoderException ex) {
76+
throw new IllegalArgumentException(
77+
String.format("unable to encode text '%s'", elide(bytesToString(decodedBytes), 64)),
78+
ex);
79+
}
80+
}
81+
82+
@NonNull
83+
public ByteBuffer encodeToByteBuffer(@NonNull ByteBuffer decodedBuffer) {
84+
byte[] decodedBytes = bufferToBytes(decodedBuffer, false);
85+
return encodeToByteBuffer(decodedBytes);
86+
}
87+
88+
@NonNull
89+
public ByteBuffer encodeToByteBuffer(String decodedText) {
90+
byte[] decodedBytes = stringToBytes(decodedText);
91+
return encodeToByteBuffer(decodedBytes);
92+
}
93+
94+
@NonNull
95+
public ByteBuffer encodeToByteBuffer(byte[] decodedBytes) {
96+
byte[] encodedBytes = encode(decodedBytes);
97+
return ObjectUtils.notNull(ByteBuffer.wrap(encodedBytes));
98+
}
99+
100+
@NonNull
101+
public byte[] decode(@NonNull byte[] enodedBytes) {
102+
try {
103+
return ObjectUtils.notNull(getDecoder().decode(enodedBytes));
104+
} catch (DecoderException ex) {
105+
throw new IllegalArgumentException(
106+
String.format("unable to decode text '%s'", elide(bytesToString(enodedBytes), 64)),
107+
ex);
108+
}
109+
}
110+
111+
@NonNull
112+
public ByteBuffer decode(@NonNull ByteBuffer encodedBuffer) {
113+
byte[] encodedBytes = bufferToBytes(encodedBuffer, false);
114+
byte[] decodedBytes = decode(encodedBytes);
115+
return ObjectUtils.notNull(ByteBuffer.wrap(decodedBytes));
116+
}
117+
118+
@NonNull
119+
public String decodeToString(@NonNull byte[] encodedBytes) {
120+
byte[] decodedBytes = decode(encodedBytes);
121+
return bytesToString(decodedBytes);
122+
}
123+
124+
/**
125+
* Decodes the provided string.
126+
*
127+
* @return a buffer containing the decoded bytes
128+
*/
129+
@Override
130+
public ByteBuffer parse(String encodedString) {
131+
byte[] encodedBytes = stringToBytes(encodedString);
132+
// byte[] decodedBytes = decode(encodedBytes);
133+
// return ObjectUtils.notNull(ByteBuffer.wrap(decodedBytes));
134+
return ObjectUtils.notNull(ByteBuffer.wrap(encodedBytes));
135+
}
136+
137+
/**
138+
* Get the wrapped value as a base64 encoded string.
139+
* <p>
140+
* Encodes the wrapped value to produce a string.
141+
*
142+
* @return the base64 encoded value
143+
*/
144+
@Override
145+
public String asString(Object encodedBuffer) {
146+
byte[] encodedBytes = bufferToBytes((ByteBuffer) encodedBuffer, false);
147+
return new String(encodedBytes, StandardCharsets.UTF_8);
148+
// return bytesToString(encodedBytes);
149+
150+
// byte[] decodedBytes = bufferToBytes((ByteBuffer) decodedBuffer, false);
151+
// byte[] encodedBytes = encode(decodedBytes);
152+
// return bytesToString(encodedBytes);
153+
}
154+
155+
@Override
156+
public JsonFormatTypes getJsonRawType() {
157+
return JsonFormatTypes.STRING;
158+
}
159+
160+
@Override
161+
public ByteBuffer copy(Object obj) {
162+
ByteBuffer buffer = (ByteBuffer) obj;
163+
ByteBuffer clone = buffer.isDirect()
164+
? ByteBuffer.allocateDirect(buffer.capacity())
165+
: ByteBuffer.allocate(buffer.capacity());
166+
ByteBuffer readOnlyCopy = buffer.asReadOnlyBuffer();
167+
readOnlyCopy.flip();
168+
clone.put(readOnlyCopy);
169+
return clone;
170+
}
171+
172+
@NonNull
173+
public static byte[] bufferToBytes(@NonNull ByteBuffer buffer, boolean copy) {
174+
byte[] array;
175+
if (buffer.hasArray()) {
176+
array = buffer.array();
177+
if (copy) {
178+
array = Arrays.copyOf(array, array.length);
179+
}
180+
} else {
181+
// Handle direct buffers
182+
array = new byte[buffer.remaining()];
183+
buffer.mark();
184+
try {
185+
buffer.get(array);
186+
} finally {
187+
buffer.reset();
188+
}
189+
}
190+
return ObjectUtils.notNull(array);
191+
}
192+
}

core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/Base64Adapter.java

Lines changed: 20 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -5,152 +5,63 @@
55

66
package gov.nist.secauto.metaschema.core.datatype.adapter;
77

8-
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes;
9-
10-
import gov.nist.secauto.metaschema.core.datatype.AbstractDataTypeAdapter;
118
import gov.nist.secauto.metaschema.core.metapath.MetapathConstants;
129
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBase64BinaryItem;
1310
import gov.nist.secauto.metaschema.core.qname.EQNameFactory;
1411
import gov.nist.secauto.metaschema.core.qname.IEnhancedQName;
1512
import gov.nist.secauto.metaschema.core.util.ObjectUtils;
1613

14+
import org.apache.commons.codec.BinaryDecoder;
15+
import org.apache.commons.codec.BinaryEncoder;
16+
import org.apache.commons.codec.binary.Base64;
17+
1718
import java.nio.ByteBuffer;
18-
import java.nio.charset.StandardCharsets;
19-
import java.util.Arrays;
20-
import java.util.Base64;
2119
import java.util.List;
2220

2321
import edu.umd.cs.findbugs.annotations.NonNull;
2422

2523
/**
26-
* Maintains a byte buffer backed representation of a byte stream parsed from a base64 encoded
27-
* string.
24+
* Maintains a byte buffer backed representation of a byte stream parsed from a
25+
* BASE64 encoded string.
2826
* <p>
29-
* Provides support for the Metaschema
30-
* <a href= "https://pages.nist.gov/metaschema/specification/datatypes/#base64">base64</a> data
31-
* type.
27+
* Provides support for the Metaschema <a href=
28+
* "https://pages.nist.gov/metaschema/specification/datatypes/#BASE64">BASE64</a>
29+
* data type.
3230
*/
3331
public class Base64Adapter
34-
extends AbstractDataTypeAdapter<ByteBuffer, IBase64BinaryItem> {
32+
extends AbstractBinaryAdapter<IBase64BinaryItem> {
3533
@NonNull
3634
private static final List<IEnhancedQName> NAMES = ObjectUtils.notNull(
3735
List.of(
3836
EQNameFactory.instance().newQName(MetapathConstants.NS_METAPATH, "base64"),
3937
// for backwards compatibility with original type name
4038
EQNameFactory.instance().newQName(MetapathConstants.NS_METAPATH, "base64Binary")));
4139

42-
Base64Adapter() {
43-
super(ByteBuffer.class, IBase64BinaryItem.class, IBase64BinaryItem::cast);
44-
}
45-
46-
@Override
47-
public List<IEnhancedQName> getNames() {
48-
return NAMES;
49-
}
40+
@NonNull
41+
private static final Base64 BASE64 = ObjectUtils.notNull(Base64.builder().get());
5042

51-
@Override
52-
public JsonFormatTypes getJsonRawType() {
53-
return JsonFormatTypes.STRING;
43+
Base64Adapter() {
44+
super(IBase64BinaryItem.class, IBase64BinaryItem::cast);
5445
}
5546

5647
@Override
57-
public ByteBuffer parse(String value) {
58-
Base64.Decoder decoder = Base64.getDecoder();
59-
byte[] result = decoder.decode(value);
60-
return ObjectUtils.notNull(ByteBuffer.wrap(result));
48+
protected BinaryEncoder getEncoder() {
49+
return BASE64;
6150
}
6251

6352
@Override
64-
public ByteBuffer copy(Object obj) {
65-
ByteBuffer buffer = (ByteBuffer) obj;
66-
ByteBuffer clone = buffer.isDirect()
67-
? ByteBuffer.allocateDirect(buffer.capacity())
68-
: ByteBuffer.allocate(buffer.capacity());
69-
ByteBuffer readOnlyCopy = buffer.asReadOnlyBuffer();
70-
readOnlyCopy.flip();
71-
clone.put(readOnlyCopy);
72-
return clone;
53+
protected BinaryDecoder getDecoder() {
54+
return BASE64;
7355
}
7456

75-
/**
76-
* Get the wrapped value as a base64 encoded string.
77-
*
78-
* @return the base64 encoded value
79-
*/
8057
@Override
81-
public String asString(Object value) {
82-
byte[] array = bufferToBytes((ByteBuffer) value, false);
83-
return ObjectUtils.notNull(Base64.getEncoder().encodeToString(array));
58+
public List<IEnhancedQName> getNames() {
59+
return NAMES;
8460
}
8561

8662
@Override
8763
public IBase64BinaryItem newItem(Object value) {
8864
ByteBuffer item = toValue(value);
8965
return IBase64BinaryItem.valueOf(item);
9066
}
91-
92-
@NonNull
93-
public static ByteBuffer encode(@NonNull ByteBuffer decodedBuffer) {
94-
return Base64.getEncoder().encode(decodedBuffer);
95-
}
96-
97-
@NonNull
98-
public static ByteBuffer encodeToByteBuffer(@NonNull String decodedString) {
99-
return encodeToByteBuffer(decodedString.getBytes());
100-
}
101-
102-
@NonNull
103-
public static ByteBuffer encodeToByteBuffer(@NonNull byte[] bytes) {
104-
byte[] encodedBytes = Base64.getEncoder().encode(bytes);
105-
return ByteBuffer.wrap(encodedBytes);
106-
}
107-
108-
@NonNull
109-
public static ByteBuffer decode(@NonNull ByteBuffer encodedBuffer) {
110-
return Base64.getDecoder().decode(encodedBuffer);
111-
}
112-
113-
@NonNull
114-
public static String decodeToString(@NonNull ByteBuffer encodedBuffer) {
115-
ByteBuffer decodedBuffer = decode(encodedBuffer);
116-
byte[] decodedBytes = bufferToBytes(decodedBuffer, true);
117-
return new String(decodedBytes, StandardCharsets.UTF_8);
118-
}
119-
120-
@NonNull
121-
public static String decodeToString(@NonNull String encodedString) {
122-
byte[] decodedBytes = Base64.getDecoder().decode(encodedString);
123-
return new String(decodedBytes, StandardCharsets.UTF_8);
124-
}
125-
126-
@NonNull
127-
public ByteBuffer stringToByteBuffer(@NonNull String text) {
128-
return ByteBuffer.wrap(text.getBytes(StandardCharsets.UTF_8));
129-
}
130-
131-
@NonNull
132-
public String byteBufferToString(@NonNull ByteBuffer buffer) {
133-
byte[] bytes = bufferToBytes(buffer, false);
134-
return new String(bytes, StandardCharsets.UTF_8);
135-
}
136-
137-
private static byte[] bufferToBytes(@NonNull ByteBuffer buffer, boolean copy) {
138-
byte[] array;
139-
if (buffer.hasArray()) {
140-
array = buffer.array();
141-
if (copy) {
142-
array = Arrays.copyOf(array, array.length);
143-
}
144-
} else {
145-
// Handle direct buffers
146-
array = new byte[buffer.remaining()];
147-
buffer.mark();
148-
try {
149-
buffer.get(array);
150-
} finally {
151-
buffer.reset();
152-
}
153-
}
154-
return array;
155-
}
15667
}

0 commit comments

Comments
 (0)