Drop dependency on javax.xml.bind
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / codec / BinaryStringCodec.java
index 04ba7211ea544d5d1403edb9c0509ce63ff6f0a5..172d55d7f6534534a1e012b36435f2421c1482e1 100644 (file)
@@ -7,29 +7,74 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.codec;
 
-import com.google.common.base.Optional;
-import com.google.common.io.BaseEncoding;
-import javax.xml.bind.DatatypeConverter;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.RangeSet;
+import java.util.Base64;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
 import org.opendaylight.yangtools.yang.data.api.codec.BinaryCodec;
+import org.opendaylight.yangtools.yang.data.api.codec.YangInvalidValueException;
 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
+
+/**
+ * Do not use this class outside of yangtools, its presence does not fall into the API stability contract.
+ */
+@Beta
+public abstract class BinaryStringCodec extends TypeDefinitionAwareCodec<byte[], BinaryTypeDefinition>
+        implements BinaryCodec<String> {
+    private static final class Restricted extends BinaryStringCodec {
+        private final LengthConstraint lengthConstraint;
 
-final class BinaryStringCodec extends TypeDefinitionAwareCodec<byte[], BinaryTypeDefinition> implements BinaryCodec<String> {
+        Restricted(final BinaryTypeDefinition typeDef, final LengthConstraint lengthConstraint) {
+            super(typeDef);
+            this.lengthConstraint = requireNonNull(lengthConstraint);
+        }
 
-    private BinaryStringCodec(final Optional<BinaryTypeDefinition> typeDef) {
-        super(typeDef, byte[].class);
+        @Override
+        void validate(final byte[] value) {
+            final RangeSet<Integer> ranges = lengthConstraint.getAllowedRanges();
+            if (!ranges.contains(value.length)) {
+                throw new YangInvalidValueException(ErrorType.PROTOCOL, lengthConstraint,
+                        "Value length " + value.length + " is not in required ranges " + ranges);
+            }
+        }
     }
 
-    static TypeDefinitionAwareCodec<?,BinaryTypeDefinition> from(final BinaryTypeDefinition type) {
-        return new BinaryStringCodec(Optional.fromNullable(type));
+    private static final class Unrestricted extends BinaryStringCodec {
+        Unrestricted(final BinaryTypeDefinition typeDef) {
+            super(typeDef);
+        }
+
+        @Override
+        void validate(final byte[] value) {
+            // No-op
+        }
+    }
+
+    BinaryStringCodec(final BinaryTypeDefinition typeDef) {
+        super(requireNonNull(typeDef), byte[].class);
+    }
+
+    public static BinaryStringCodec from(final BinaryTypeDefinition type) {
+        final java.util.Optional<LengthConstraint> optConstraint = type.getLengthConstraint();
+        return optConstraint.isPresent() ? new Restricted(type, optConstraint.get()) : new Unrestricted(type);
     }
 
     @Override
-    public String serialize(final byte[] data) {
-        return data == null ? "" : BaseEncoding.base64().encode(data);
+    public final byte[] deserializeImpl(final String product) {
+        // https://tools.ietf.org/html/rfc4648#section-4 plus lenient to allow for MIME blocks
+        final byte[] ret = Base64.getMimeDecoder().decode(product);
+        validate(ret);
+        return ret;
     }
 
     @Override
-    public byte[] deserialize(final String stringRepresentation) {
-        return stringRepresentation == null ? null : DatatypeConverter.parseBase64Binary(stringRepresentation);
+    protected final String serializeImpl(final byte[] data) {
+        // We do not split data on 76 characters on output
+        return Base64.getEncoder().encodeToString(data);
     }
+
+    abstract void validate(byte[] value);
 }