001/*
002 * Copyright 2015-2020 the original author or authors
003 *
004 * This software is licensed under the Apache License, Version 2.0,
005 * the GNU Lesser General Public License version 2 or later ("LGPL")
006 * and the WTFPL.
007 * You may choose either license to govern your use of this software only
008 * upon the condition that you accept all of the terms of either
009 * the Apache License 2.0, the LGPL 2.1+ or the WTFPL.
010 */
011package org.minidns.record;
012
013import java.io.DataInputStream;
014import java.io.DataOutputStream;
015import java.io.IOException;
016import java.io.UnsupportedEncodingException;
017import java.util.ArrayList;
018import java.util.Arrays;
019import java.util.Collections;
020import java.util.Iterator;
021import java.util.List;
022
023import org.minidns.record.Record.TYPE;
024
025/**
026 *  A TXT record. Actually a binary blob containing extents, each of which is a one-byte count
027 *  followed by that many bytes of data, which can usually be interpreted as ASCII strings
028 *  but not always.
029 */
030public class TXT extends Data {
031
032    private final byte[] blob;
033
034    public static TXT parse(DataInputStream dis, int length) throws IOException {
035        byte[] blob = new byte[length];
036        dis.readFully(blob);
037        return new TXT(blob);
038    }
039
040    public TXT(byte[] blob) {
041        this.blob = blob;
042    }
043
044    public byte[] getBlob() {
045        return blob.clone();
046    }
047
048    private transient String textCache;
049
050    public String getText() {
051        if (textCache == null) {
052            StringBuilder sb = new StringBuilder();
053            Iterator<String> it = getCharacterStrings().iterator();
054            while (it.hasNext()) {
055                sb.append(it.next());
056                if (it.hasNext()) {
057                    sb.append(" / ");
058                }
059            }
060            textCache = sb.toString();
061        }
062        return textCache;
063    }
064
065    private transient List<String> characterStringsCache;
066
067    public List<String> getCharacterStrings() {
068        if (characterStringsCache == null) {
069            List<byte[]> extents = getExtents();
070            List<String> characterStrings = new ArrayList<>(extents.size());
071            for (byte[] extent : extents) {
072                try {
073                    characterStrings.add(new String(extent, "UTF-8"));
074                } catch (UnsupportedEncodingException e) {
075                    throw new AssertionError(e);
076                }
077            }
078
079            characterStringsCache = Collections.unmodifiableList(characterStrings);
080        }
081        return characterStringsCache;
082    }
083
084    public List<byte[]> getExtents() {
085        ArrayList<byte[]> extents = new ArrayList<byte[]>();
086        int segLength = 0;
087        for (int used = 0; used < blob.length; used += segLength) {
088            segLength = 0x00ff & blob[used];
089            int end = ++used + segLength;
090            byte[] extent = Arrays.copyOfRange(blob, used, end);
091            extents.add(extent);
092        }
093        return extents;
094    }
095
096    @Override
097    public void serialize(DataOutputStream dos) throws IOException {
098        dos.write(blob);
099    }
100
101    @Override
102    public TYPE getType() {
103        return TYPE.TXT;
104    }
105
106    @Override
107    public String toString() {
108        return "\"" + getText() + "\"";
109    }
110
111}