001package ball.io;
002/*-
003 * ##########################################################################
004 * Utilities
005 * %%
006 * Copyright (C) 2008 - 2022 Allen D. Ball
007 * %%
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *      http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 * ##########################################################################
020 */
021import java.nio.charset.Charset;
022import java.util.Collections;
023import java.util.LinkedHashMap;
024import java.util.Map;
025
026import static java.nio.charset.StandardCharsets.UTF_16BE;
027import static java.nio.charset.StandardCharsets.UTF_16LE;
028import static java.nio.charset.StandardCharsets.UTF_8;
029import static java.util.stream.Collectors.joining;
030
031/**
032 * Byte order mark to {@link Charset} {@link Map} implementation.
033 *
034 * {@include #INSTANCE}
035 *
036 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball}
037 */
038public class BOMCharsetMap extends LinkedHashMap<byte[],Charset> {
039    private static final long serialVersionUID = -4968264081542792700L;
040
041    /**
042     * Unmodifiable instance of {@link BOMCharsetMap}.
043     */
044    public static final Map<byte[],Charset> INSTANCE =
045        Collections.unmodifiableMap(new BOMCharsetMap());
046
047    /**
048     * Sole constructor.
049     */
050    protected BOMCharsetMap() {
051        super();
052
053        put(bytes(0x00, 0x00, 0xFE, 0xFF), Charset.forName("UTF-32BE"));
054        put(bytes(0xFF, 0xFE, 0x00, 0x00), Charset.forName("UTF-32LE"));
055
056        put(bytes(0xEF, 0xBB, 0xBF), UTF_8);
057
058        put(bytes(0xFE, 0xFF), UTF_16BE);
059        put(bytes(0xFF, 0xFE), UTF_16LE);
060    }
061
062    private static byte[] bytes(int... elements) {
063        byte[] bytes = new byte[elements.length];
064
065        for (int i = 0; i < bytes.length; i += 1) {
066            bytes[i] = (byte) (elements[i] & 0xFF);
067        }
068
069        return bytes;
070    }
071
072    @Override
073    public String toString() {
074        String string =
075            entrySet().stream()
076            .map(t -> toString(t.getKey()) + "=" + toString(t.getValue()))
077            .collect(joining(", ", "{", "}"));
078
079        return string;
080    }
081
082    private String toString(byte[] bytes) {
083        StringBuilder buffer = new StringBuilder();
084
085        buffer.append("[");
086
087        for (int i = 0; i < bytes.length; i += 1) {
088            if (i > 0) {
089                buffer.append(", ");
090            }
091
092            buffer.append(String.format("0x%02X", bytes[i]));
093        }
094
095        buffer.append("]");
096
097        return buffer.toString();
098    }
099
100    private String toString(Charset charset) {
101        return (charset != null) ? charset.name() : null;
102    }
103}