pl.baczkowicz.spy.formatting.FormattingUtils.java Source code

Java tutorial

Introduction

Here is the source code for pl.baczkowicz.spy.formatting.FormattingUtils.java

Source

/***********************************************************************************
 * 
 * Copyright (c) 2014 Kamil Baczkowicz
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 *
 * The Eclipse Public License is available at
 *    http://www.eclipse.org/legal/epl-v10.html
 *    
 * The Eclipse Distribution License is available at
 *   http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * Contributors:
 * 
 *    Kamil Baczkowicz - initial API and implementation and/or initial documentation
 *    
 */
package pl.baczkowicz.spy.formatting;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import pl.baczkowicz.spy.common.generated.ConversionFormatterDetails;
import pl.baczkowicz.spy.common.generated.ConversionMethod;
import pl.baczkowicz.spy.common.generated.FormatterDetails;
import pl.baczkowicz.spy.common.generated.FormatterFunction;
import pl.baczkowicz.spy.common.generated.SubstringConversionFormatterDetails;
import pl.baczkowicz.spy.common.generated.SubstringExtractFormatterDetails;
import pl.baczkowicz.spy.common.generated.SubstringFormatterDetails;
import pl.baczkowicz.spy.common.generated.SubstringReplaceFormatterDetails;
import pl.baczkowicz.spy.exceptions.ConversionException;
import pl.baczkowicz.spy.utils.ConversionUtils;

/**
 * Formatting-related utils.
 */
public class FormattingUtils {
    public static final String DEFAULT_PREFIX = "default";

    public static final String SCRIPT_PREFIX = "script";

    /** Diagnostic logger. */
    private final static Logger logger = LoggerFactory.getLogger(FormattingUtils.class);

    /**
     * Formats the given number into one where thousands are separated by a space.
     * 
     * @param number The number to format
     * @return Formatted string (e.g. "1 315 124" for 1315124)
     */
    public static String formatNumber(final long number) {
        long divided = number;
        final StringBuffer sb = new StringBuffer();

        while (divided > 1000) {
            long rest = divided % 1000;
            sb.insert(0, " " + String.format("%03d", rest));

            divided = divided / 1000;
        }

        long rest = divided % 1000;
        sb.insert(0, rest);

        return sb.toString();
    }

    /**
     * Formats the given text using the provided formatter.
     *  
     * @param customFormatter The formatter details
     * @param text The text to be formatted
     * 
     * @return The formatted text
     */
    public static String formatText(final FormatterDetails customFormatter, final String text,
            final byte[] rawText) {
        logger.trace("Formatting '" + text + "' with " + customFormatter.getName());
        String formattedText = text;

        for (final FormatterFunction function : customFormatter.getFunction()) {
            if (function.getSubstringReplace() != null) {
                formattedText = doSubstringReplacement(function.getSubstringReplace(), formattedText);
            } else if (function.getSubstringExtract() != null) {
                formattedText = doSubstringExtract(function.getSubstringExtract(), formattedText);
            } else if (function.getSubstringConversion() != null) {
                formattedText = doSubstringConversion(function.getSubstringConversion(), formattedText);
            } else if (function.getConversion() != null) {
                // Here we want the raw text, otherwise the encoding might be incorrect
                if (customFormatter.getFunction().size() == 1 && rawText != null) {
                    formattedText = convertText(function.getConversion().getFormat(), rawText);
                } else {
                    formattedText = convertText(function.getConversion().getFormat(), formattedText);
                }
            } else if (function.getCharacterReplace() != null) {
                formattedText = replaceCharacters(function.getCharacterReplace().getFormat(), formattedText,
                        function.getCharacterReplace().getCharacterRangeFrom(),
                        function.getCharacterReplace().getCharacterRangeTo(),
                        function.getCharacterReplace().getWrapCharacter());
            }

            logger.trace("After function transformation = '" + formattedText + "'");
        }

        return formattedText;
    }

    /**
     * Replaces characters using the given conversion method.
     * 
     * @param conversionMethod The conversion method to be used
     * @param input The input text
     * @param fromCharacter From index
     * @param toCharacter To index
     * @param wrap Characters to put around the converted text
     * 
     * @return The converted text
     */
    public static String replaceCharacters(final ConversionMethod conversionMethod, final String input,
            final int fromCharacter, final int toCharacter, final String wrap) {
        String convertedText = input;

        for (int i = fromCharacter; i <= toCharacter; i++) {
            final String characterToReplace = new String(Character.toChars(i));

            if (wrap != null) {
                convertedText = convertedText.replace(characterToReplace,
                        wrap + convertText(conversionMethod, characterToReplace) + wrap);
            } else {
                convertedText = convertedText.replace(characterToReplace,
                        convertText(conversionMethod, characterToReplace));
            }
        }

        return convertedText;
    }

    /**
     * Extracts a substring from the given text.
     * 
     * @param details Details about what to extract
     * @param text The text from which to extract
     * 
     * @return The extracted value
     * 
     * @throws ConversionException Thrown when cannot process the parameters correctly
     */
    private static String extractValueForConversion(final SubstringFormatterDetails details, final String text)
            throws ConversionException {
        final int startTagIndex = text.indexOf(details.getStartTag());

        if (startTagIndex != -1) {
            final int endTagIndex = text.indexOf(details.getEndTag(), startTagIndex);

            if (endTagIndex != -1) {
                return text.substring(startTagIndex + details.getStartTag().length(), endTagIndex);
            }
        }

        throw new ConversionException("Cannot find tags");
    }

    /**
     * Replaces the given input text (optionally with the configured tags) with the given output text.
     *  
     * @param details The formatter details
     * @param text The text to modify
     * @param input The input text to replace
     * @param output The text to replace the input with
     * 
     * @return The converted text
     */
    private static String replaceTextAndTags(final SubstringFormatterDetails details, final String text,
            final String input, final String output) {
        String convertedText = text;

        if (details.isKeepTags()) {
            convertedText = convertedText.replace(input, output);
        } else {
            convertedText = convertedText.replace(details.getStartTag() + input + details.getEndTag(), output);
        }

        return convertedText;
    }

    /**
     * Performs a string conversion for the given text based on the supplied formatter details.
     * 
     * @param details The formatter details
     * @param text The text to format
     * 
     * @return The formatted text
     */
    private static String doSubstringConversion(final SubstringConversionFormatterDetails details,
            final String text) {
        String convertedText = text;

        try {
            final String input = extractValueForConversion(details, convertedText);

            // The actual conversion value
            final String output = convertText(details.getFormat(), input);

            convertedText = replaceTextAndTags(details, convertedText, input, output);
        } catch (ConversionException e) {
            // Ignore, just use the input text as output
        }

        return convertedText;
    }

    /**
     * Performs a string replacement for the given text based on the supplied formatter details.
     * 
     * @param details The formatter details
     * @param text The text to format
     * 
     * @return The formatted text
     */
    private static String doSubstringReplacement(final SubstringReplaceFormatterDetails details,
            final String text) {
        String convertedText = text;

        try {
            final String input = extractValueForConversion(details, convertedText);

            // The actual replacement value
            final String output = details.getReplaceWith();

            convertedText = replaceTextAndTags(details, convertedText, input, output);
        } catch (ConversionException e) {
            // Ignore, just use the input text as output
        }

        return convertedText;
    }

    /**
     * Performs a string extraction for the given text based on the supplied formatter details.
     * 
     * @param details The formatter details
     * @param text The text to format
     * 
     * @return The formatted text
     */
    private static String doSubstringExtract(final SubstringExtractFormatterDetails details, final String text) {
        String convertedText = text;

        try {
            final String input = extractValueForConversion(details, convertedText);

            if (details.isKeepTags()) {
                convertedText = details.getStartTag() + input + details.getEndTag();
            } else {
                convertedText = input;
            }
        } catch (ConversionException e) {
            // Ignore, just use the input text as output
        }

        return convertedText;
    }

    /**
     * Converts the given text using the supplied method.
     * 
     * @param method The method to use for conversion
     * @param text The text to be converted
     * 
     * @return The converted text
     */
    public static String convertText(final ConversionMethod method, final String text) {
        switch (method) {
        case PLAIN: {
            return text;
        }
        case HEX_ENCODE: {
            return ConversionUtils.stringToHex(text);
        }
        case HEX_DECODE: {
            return ConversionUtils.hexToStringNoException(text);
        }
        case BASE_64_ENCODE: {
            return ConversionUtils.stringToBase64(text);
        }
        case BASE_64_DECODE: {
            return ConversionUtils.base64ToString(text);
        }
        default:
            return text;
        }
    }

    /**
     * Converts the given text using the supplied method.
     * 
     * @param method The method to use for conversion
     * @param text The text to be converted
     * 
     * @return The converted text
     */
    public static String convertText(final ConversionMethod method, final byte[] text) {
        switch (method) {
        case HEX_ENCODE: {
            return new String(Hex.encodeHex(text));
        }
        case HEX_DECODE: {
            return ConversionUtils.hexToStringNoException(ConversionUtils.arrayToString(text));
        }
        case BASE_64_ENCODE: {
            return Base64.encodeBase64String(text);
        }
        case BASE_64_DECODE: {
            return new String(Base64.decodeBase64(text));
        }
        default:
            return ConversionUtils.arrayToString(text);
        }
    }

    /**
     * Formats the given text using the supplied format.
     * 
     * @param format The format to use for conversion
     * @param text The text to be formatted
     * 
     * @return The formatted text
     */
    public static String checkAndFormatText(final FormatterDetails format, final String text) {
        if (format != null) {
            return FormattingUtils.formatText(format, text, null);
        }
        return text;
    }

    /**
     * Formats the given text using the supplied format.
     * 
     * @param format The format to use for conversion
     * @param text The text to be formatted
     * 
     * @return The formatted text
     */
    public static String checkAndFormatText(final FormatterDetails format, final byte[] text) {
        if (format != null) {
            return FormattingUtils.formatText(format, ConversionUtils.arrayToString(text), text);
        }
        return ConversionUtils.arrayToString(text);
    }

    /**
     * Creates a basic formatter function from the supplied conversion method.
     *  
     * @param conversionMethod The conversion method to be used
     * 
     * @return Created formatter function
     */
    private static FormatterFunction createBasicFormatterFunction(final ConversionMethod conversionMethod) {
        final FormatterFunction function = new FormatterFunction();

        final ConversionFormatterDetails conversionFormatterDetails = new ConversionFormatterDetails();
        conversionFormatterDetails.setFormat(conversionMethod);

        function.setConversion(conversionFormatterDetails);

        return function;
    }

    /**
     * Creates formatter details for the given parameters.
     * 
     * @param id The ID of the formatter
     * @param name The name of the formatter
     * @param conversionMethod The conversion method
     * 
     * @return FormatterDetails object
     */
    public static FormatterDetails createBasicFormatter(final String id, final String name,
            final String description, final ConversionMethod conversionMethod) {
        final FormatterDetails formatter = new FormatterDetails();

        formatter.setID(id);
        formatter.setName(name);
        formatter.setDescription(description);
        formatter.getFunction().add(createBasicFormatterFunction(conversionMethod));

        return formatter;
    }

    public static boolean isScriptBased(final FormatterDetails formatter) {
        return formatter.getID().startsWith(SCRIPT_PREFIX)
                || formatter.getID().startsWith(DEFAULT_PREFIX + SCRIPT_PREFIX);
    }

    public static List<FormatterDetails> createBaseFormatters() {
        final List<FormatterDetails> baseFormatters = new ArrayList<>();

        baseFormatters.add(FormattingUtils.createBasicFormatter(DEFAULT_PREFIX, "Plain",
                "No formatting - as received.", ConversionMethod.PLAIN));
        baseFormatters.add(FormattingUtils.createBasicFormatter(DEFAULT_PREFIX + "-hexDecoder", "HEX decoder",
                "Decodes from a HEX string.", ConversionMethod.HEX_DECODE));
        baseFormatters.add(FormattingUtils.createBasicFormatter(DEFAULT_PREFIX + "-hexEncoder", "HEX encoder",
                "Encodes the given value as a HEX string.", ConversionMethod.HEX_ENCODE));
        baseFormatters.add(FormattingUtils.createBasicFormatter(DEFAULT_PREFIX + "-base64Decoder", "Base64 decoder",
                "Decodes a Base64 string.", ConversionMethod.BASE_64_DECODE));
        baseFormatters.add(FormattingUtils.createBasicFormatter(DEFAULT_PREFIX + "-base64Encoder", "Base64 encoder",
                "Encodes the given value to Base64.", ConversionMethod.BASE_64_ENCODE));

        return baseFormatters;
    }

    public static boolean isDefault(final FormatterDetails formatter) {
        return formatter.getID().startsWith(DEFAULT_PREFIX);
    }

    public static String prettyXml(final Document document, final int indent) throws TransformerException {
        final TransformerFactory transformerFactory = TransformerFactory.newInstance();

        final Transformer transformer = transformerFactory.newTransformer();
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", Integer.toString(indent));

        final StringWriter writer = new StringWriter();
        final Result result = new StreamResult(writer);
        final Source source = new DOMSource(document);
        transformer.transform(source, result);

        return writer.getBuffer().toString();
    }

    public static String prettyXml(final String xml, final int indent)
            throws SAXException, IOException, TransformerException, ParserConfigurationException {
        final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        final DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
        final Document document = documentBuilder.parse(new InputSource(new StringReader(xml)));

        return prettyXml(document, indent);
    }
}