net.spfbl.core.Huffman.java Source code

Java tutorial

Introduction

Here is the source code for net.spfbl.core.Huffman.java

Source

/*
 * This file is part of SPFBL.
 * 
 * SPFBL is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * SPFBL is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with SPFBL.  If not, see <http://www.gnu.org/licenses/>.
 */
package net.spfbl.core;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.InputStream;
import java.io.Serializable;
import java.util.PriorityQueue;
import java.util.Set;
import org.apache.commons.lang3.SerializationUtils;

/**
 * Algoritmo de Hullman para compresso de texto.
 *
 * @author Leandro Carlos Rodrigues <leandro@spfbl.net>
 */
public class Huffman implements Comparable<Huffman>, Serializable {

    private static final long serialVersionUID = 1L;

    private final char character;
    private final int frequency;
    private final Huffman left, right;

    private Huffman(char character, int frequency, Huffman left, Huffman right) {
        this.character = character;
        this.frequency = frequency;
        this.left = left;
        this.right = right;
    }

    private boolean isLeaf() {
        assert ((left == null) && (right == null)) || ((left != null) && (right != null));
        return (left == null) && (right == null);
    }

    @Override
    public int compareTo(Huffman other) {
        return this.frequency - other.frequency;
    }

    public static void main(String[] args) throws Exception {
        File file = new File("C:\\Users\\Leandro\\Desktop\\amostra.txt");
        BufferedReader reader = new BufferedReader(new FileReader(file));
        int[] frequency = new int[256];
        int count = 0;
        String line;
        while ((line = reader.readLine()) != null) {
            line = " " + line;
            if (count % 3 == 0) {
                line += '\0';
            }
            char[] input = line.toCharArray();
            for (int i = 0; i < input.length; i++) {
                frequency[input[i]]++;
            }
            count++;
        }
        reader.close();
        Huffman huffman = buildTree(frequency);
        FileOutputStream outputStream = new FileOutputStream("C:\\Users\\Leandro\\Desktop\\huffman.obj");
        SerializationUtils.serialize(huffman, outputStream);
        outputStream.close();
    }

    public static Huffman load() {
        try {
            InputStream inputStream = Huffman.class.getResourceAsStream("huffman.obj");
            try {
                return SerializationUtils.deserialize(inputStream);
            } finally {
                inputStream.close();
            }
        } catch (Exception ex) {
            Server.logError(ex);
            System.exit(1);
            return null;
        }
    }

    //    public String encode(String text) throws ProcessException {
    //        text += '\0';
    //        char[] input = text.toCharArray();
    //        String[] st = new String[256];
    //        buildCode(st, this, "");
    //        StringBuilder builder = new StringBuilder();
    //        for (int i = 0; i < input.length; i++) {
    //            String code = st[input[i]];
    //            if (code == null) {
    //                throw new ProcessException("ERROR: COMPRESSION");
    //            } else {
    //                for (int j = 0; j < code.length(); j++) {
    //                    if (code.charAt(j) == '0') {
    //                        builder.append('0');
    //                    } else if (code.charAt(j) == '1') {
    //                        builder.append('1');
    //                    } else {
    //                        throw new ProcessException("ERROR: COMPRESSION");
    //                    }
    //                }
    //            }
    //        }
    //        // Completar o byte final.
    //        while (builder.length() % 8 > 0) {
    //            builder.append('0');
    //        }
    //        return builder.toString();
    //    }

    public byte[] encodeByteArray(String text, int deslocamento) throws ProcessException {
        text += '\0';
        char[] input = text.toCharArray();
        String[] st = new String[256];
        buildCode(st, this, "");
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < input.length; i++) {
            String code = st[input[i]];
            if (code == null) {
                throw new ProcessException("ERROR: COMPRESSION");
            } else {
                for (int j = 0; j < code.length(); j++) {
                    if (code.charAt(j) == '0') {
                        builder.append('0');
                    } else if (code.charAt(j) == '1') {
                        builder.append('1');
                    } else {
                        throw new ProcessException("ERROR: COMPRESSION");
                    }
                }
            }
        }
        // Completar o byte final.
        while (builder.length() % 8 > 0) {
            builder.append('0');
        }
        int n = builder.length() / 8;
        byte[] array = new byte[n + deslocamento];
        String code = builder.toString();
        for (int i = 0; i < n; i++) {
            String octet = code.substring(i * 8, i * 8 + 8);
            array[i + deslocamento] = (byte) (Short.parseShort(octet, 2) & 0xFF);
        }
        return array;
    }

    /**
     * Constri uma rvore de Huffman a partir da 
     * otimizao de um conjunto de textos.
     * @param textSet o conjunto de textos para otmizar a compresso.
     * @return a rvore de Huffman com a otimizao dos textos.
     */
    public static Huffman buildTree(Set<String> textSet) {
        int[] frequency = new int[256];
        for (String text : textSet) {
            text += '\0';
            char[] input = text.toCharArray();
            for (int i = 0; i < input.length; i++) {
                frequency[input[i]]++;
            }
        }
        return buildTree(frequency);
    }

    private static Huffman buildTree(int[] frequency) {
        PriorityQueue<Huffman> queue = new PriorityQueue<Huffman>();
        for (char i = 0; i < 256; i++) {
            if (frequency[i] > 0) {
                queue.add(new Huffman(i, frequency[i], null, null));
            }
        }
        if (queue.size() == 1) {
            if (frequency['\0'] == 0) {
                queue.add(new Huffman('\0', 0, null, null));
            } else {
                queue.add(new Huffman('\1', 0, null, null));
            }
        }
        while (queue.size() > 1) {
            Huffman left = queue.poll();
            Huffman right = queue.poll();
            Huffman parent = new Huffman('\0', left.frequency + right.frequency, left, right);
            queue.add(parent);
        }
        return queue.poll();
    }

    private static void buildCode(String[] st, Huffman node, String text) {
        if (!node.isLeaf()) {
            buildCode(st, node.left, text + '0');
            buildCode(st, node.right, text + '1');
        } else {
            st[node.character] = text;
        }
    }

    public String decode(String code) {
        StringBuilder builder = new StringBuilder();
        char[] array = code.toCharArray();
        int k = 0;
        while (k < array.length) {
            Huffman node = this;
            while (!node.isLeaf()) {
                if (array[k++] == '1') {
                    node = node.right;
                } else {
                    node = node.left;
                }
            }
            if (node.character == '\0') {
                // Fim do texto.
                break;
            } else {
                builder.append(node.character);
            }
        }
        return builder.toString();
    }

    public String decode(byte[] byteArray, int deslocamento) {
        StringBuilder builder = new StringBuilder();
        for (int i = deslocamento; i < byteArray.length; i++) {
            byte octet = byteArray[i];
            int codeInt = octet & 0xFF;
            String code = Integer.toBinaryString(codeInt);
            while (code.length() < 8) {
                code = '0' + code;
            }
            builder.append(code);
        }
        return decode(builder.toString());
    }
}