1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package dev.metaschema.core.metapath.item.atomic;
7   
8   import java.nio.ByteBuffer;
9   
10  import dev.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider;
11  import dev.metaschema.core.metapath.function.InvalidValueForCastFunctionException;
12  import dev.metaschema.core.metapath.item.atomic.impl.Base64BinaryItemImpl;
13  import dev.metaschema.core.metapath.item.atomic.impl.IBinaryItem;
14  import dev.metaschema.core.metapath.type.IAtomicOrUnionType;
15  import dev.metaschema.core.metapath.type.InvalidTypeMetapathException;
16  import edu.umd.cs.findbugs.annotations.NonNull;
17  
18  /**
19   * An atomic Metapath item containing a Base64 encoded data value.
20   */
21  public interface IBase64BinaryItem extends IBinaryItem {
22    /**
23     * Get the type information for this item.
24     *
25     * @return the type information
26     */
27    @NonNull
28    static IAtomicOrUnionType<IBase64BinaryItem> type() {
29      return MetaschemaDataTypeProvider.BASE64.getItemType();
30    }
31  
32    @Override
33    default IAtomicOrUnionType<IBase64BinaryItem> getType() {
34      return type();
35    }
36  
37    /**
38     * Base64 encode the provided string.
39     * <p>
40     * The provided string is first encoded as a stream of UTF8 bytes.
41     *
42     * @param text
43     *          the string to encode
44     * @return a base64 item representing the encoded data
45     */
46    static IBase64BinaryItem encode(@NonNull IStringItem text) {
47      return encode(text.asString());
48    }
49  
50    /**
51     * Base64 encode the provided string.
52     * <p>
53     * The provided string is first encoded as a stream of UTF8 bytes.
54     *
55     * @param text
56     *          the string to encode
57     * @return a base64 item representing the encoded data
58     */
59    static IBase64BinaryItem encode(@NonNull String text) {
60      return valueOf(MetaschemaDataTypeProvider.BASE64.encodeToByteBuffer(text));
61    }
62  
63    /**
64     * Base64 encode the provided bytes.
65     *
66     * @param bytes
67     *          the bytes to encode
68     * @return a base64 item representing the encoded data
69     */
70    @NonNull
71    static IBase64BinaryItem encode(@NonNull byte[] bytes) {
72      return valueOf(MetaschemaDataTypeProvider.BASE64.encodeToByteBuffer(bytes));
73    }
74  
75    /**
76     * Base64 encode the bytes from the provided buffer.
77     *
78     * @param buffer
79     *          the bytes to encode
80     * @return a base64 item representing the encoded data
81     */
82    @NonNull
83    static IBase64BinaryItem encode(@NonNull ByteBuffer buffer) {
84      return valueOf(MetaschemaDataTypeProvider.BASE64.encodeToByteBuffer(buffer));
85    }
86  
87    /**
88     * Base64 decode this item as a new hex binary item.
89     *
90     * @return a new hex binary item containing the decoded bytes
91     */
92    default IHexBinaryItem decode() {
93      return IHexBinaryItem.valueOf(MetaschemaDataTypeProvider.BASE64.decode(asByteBuffer()));
94    }
95  
96    /**
97     * Base64 decode this item as a string.
98     *
99     * @return a new string item containing the decoded text
100    */
101   default IStringItem decodeAsString() {
102     return IStringItem.valueOf(MetaschemaDataTypeProvider.BASE64.decodeToString(asBytes()));
103   }
104 
105   /**
106    * Construct a new base64 byte sequence item using the provided base64 encoded
107    * string {@code value}.
108    *
109    * @param value
110    *          a string representing base64 encoded data
111    * @return the new item
112    * @throws InvalidTypeMetapathException
113    *           if the provided string is not a valid Base64 character sequence
114    */
115   @NonNull
116   static IBase64BinaryItem valueOf(@NonNull String value) {
117     try {
118       return valueOf(MetaschemaDataTypeProvider.BASE64.parse(value));
119     } catch (IllegalArgumentException ex) {
120       throw new InvalidTypeMetapathException(
121           null,
122           String.format("The value starting with '%s' is not a valid Base64 character sequence. %s",
123               value.substring(0, Math.min(value.length(), 200)),
124               ex.getLocalizedMessage()),
125           ex);
126     }
127   }
128 
129   /**
130    * Construct a new URI base64 encoded byte sequence using the provided
131    * {@link ByteBuffer} {@code value}.
132    * <p>
133    * The provided buffer will be managed by this instance. Make a copy of the
134    * buffer to ensure that the position, limit, and mark of the original are not
135    * affect by this.
136    *
137    * @param buffer
138    *          a byte buffer
139    * @return the new item
140    */
141   @NonNull
142   static IBase64BinaryItem valueOf(@NonNull ByteBuffer buffer) {
143     return new Base64BinaryItemImpl(buffer);
144   }
145 
146   /**
147    * Cast the provided type to this item type.
148    *
149    * @param item
150    *          the item to cast
151    * @return the original item if it is already this type, otherwise a new item
152    *         cast to this type
153    * @throws InvalidValueForCastFunctionException
154    *           if the provided {@code item} cannot be cast to this type
155    */
156   @NonNull
157   static IBase64BinaryItem cast(@NonNull IAnyAtomicItem item) {
158     try {
159       return item instanceof IBase64BinaryItem
160           ? (IBase64BinaryItem) item
161           : valueOf(item.asString());
162     } catch (IllegalStateException | InvalidTypeMetapathException ex) {
163       // asString can throw IllegalStateException exception
164       throw new InvalidValueForCastFunctionException(ex);
165     }
166   }
167 
168   @Override
169   default IBase64BinaryItem castAsType(IAnyAtomicItem item) {
170     return cast(item);
171   }
172 
173   /**
174    * Compares this value with the argument.
175    *
176    * @param item
177    *          the item to compare with this value
178    * @return a negative integer, zero, or a positive integer if this value is less
179    *         than, equal to, or greater than the {@code item}.
180    */
181   default int compareTo(@NonNull IBase64BinaryItem item) {
182     return asByteBuffer().compareTo(item.asByteBuffer());
183   }
184 }