All Downloads are FREE. Search and download functionalities are using the official Maven repository.

uk.co.real_logic.sbe.generation.java.JavaUtil Maven / Gradle / Ivy

Go to download

FIX/SBE - OSI layer 6 presentation for encoding and decoding application messages in binary format for low-latency applications.

The newest version!
/*
 * Copyright 2013-2024 Real Logic Limited.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package uk.co.real_logic.sbe.generation.java;

import org.agrona.Strings;
import uk.co.real_logic.sbe.PrimitiveType;
import uk.co.real_logic.sbe.SbeTool;
import uk.co.real_logic.sbe.ValidationUtil;
import uk.co.real_logic.sbe.generation.Generators;
import uk.co.real_logic.sbe.ir.Token;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;

/**
 * Utilities for mapping between {@link uk.co.real_logic.sbe.ir.Ir} and the Java language.
 */
public class JavaUtil
{
    /**
     * Separator symbols for {@link Object#toString()} implementations on codecs.
     */
    enum Separator
    {
        BEGIN_GROUP('['),
        END_GROUP(']'),
        BEGIN_COMPOSITE('('),
        END_COMPOSITE(')'),
        BEGIN_SET('{'),
        END_SET('}'),
        BEGIN_ARRAY('['),
        END_ARRAY(']'),
        FIELD('|'),
        KEY_VALUE('='),
        ENTRY(',');

        private final char symbol;

        Separator(final char symbol)
        {
            this.symbol = symbol;
        }

        void appendToGeneratedBuilder(final StringBuilder builder, final String indent)
        {
            builder.append(indent).append("builder.append('").append(symbol).append("');").append('\n');
        }

        /**
         * {@inheritDoc}
         */
        public String toString()
        {
            return String.valueOf(symbol);
        }
    }

    private static final Map TYPE_NAME_BY_PRIMITIVE_TYPE_MAP =
        new EnumMap<>(PrimitiveType.class);

    static
    {
        TYPE_NAME_BY_PRIMITIVE_TYPE_MAP.put(PrimitiveType.CHAR, "byte");
        TYPE_NAME_BY_PRIMITIVE_TYPE_MAP.put(PrimitiveType.INT8, "byte");
        TYPE_NAME_BY_PRIMITIVE_TYPE_MAP.put(PrimitiveType.INT16, "short");
        TYPE_NAME_BY_PRIMITIVE_TYPE_MAP.put(PrimitiveType.INT32, "int");
        TYPE_NAME_BY_PRIMITIVE_TYPE_MAP.put(PrimitiveType.INT64, "long");
        TYPE_NAME_BY_PRIMITIVE_TYPE_MAP.put(PrimitiveType.UINT8, "short");
        TYPE_NAME_BY_PRIMITIVE_TYPE_MAP.put(PrimitiveType.UINT16, "int");
        TYPE_NAME_BY_PRIMITIVE_TYPE_MAP.put(PrimitiveType.UINT32, "long");
        TYPE_NAME_BY_PRIMITIVE_TYPE_MAP.put(PrimitiveType.UINT64, "long");
        TYPE_NAME_BY_PRIMITIVE_TYPE_MAP.put(PrimitiveType.FLOAT, "float");
        TYPE_NAME_BY_PRIMITIVE_TYPE_MAP.put(PrimitiveType.DOUBLE, "double");
    }

    /**
     * Indexes known charset aliases to the name of the instance in {@link StandardCharsets}.
     */
    static final HashMap STD_CHARSETS = new HashMap<>();

    static
    {
        try
        {
            for (final Field field : StandardCharsets.class.getDeclaredFields())
            {
                if (Charset.class.isAssignableFrom(field.getType()) && Modifier.isStatic(field.getModifiers()) &&
                    Modifier.isPublic(field.getModifiers()))
                {
                    final Charset charset = (Charset)field.get(null);
                    final String name = field.getName();
                    String oldName = STD_CHARSETS.put(charset.name(), name);

                    if (null != oldName)
                    {
                        throw new IllegalStateException("Duplicate charset alias: old=" + oldName + ", new=" + name);
                    }

                    for (final String alias : charset.aliases())
                    {
                        oldName = STD_CHARSETS.put(alias, name);
                        if (null != oldName)
                        {
                            throw new IllegalStateException(
                                "Duplicate charset alias: old=" + oldName + ", new=" + alias);
                        }
                    }
                }
            }
        }
        catch (final IllegalAccessException ex)
        {
            throw new RuntimeException(ex);
        }
    }

    /**
     * Map the name of a {@link uk.co.real_logic.sbe.PrimitiveType} to a Java primitive type name.
     *
     * @param primitiveType to map.
     * @return the name of the Java primitive that most closely maps.
     */
    public static String javaTypeName(final PrimitiveType primitiveType)
    {
        return TYPE_NAME_BY_PRIMITIVE_TYPE_MAP.get(primitiveType);
    }

    /**
     * Format a property name for generated code.
     * 

* If the formatted property name is a keyword then {@link SbeTool#KEYWORD_APPEND_TOKEN} is appended if set. * * @param value to be formatted. * @return the string formatted as a property name. * @throws IllegalStateException if a keyword and {@link SbeTool#KEYWORD_APPEND_TOKEN} is not set. */ public static String formatPropertyName(final String value) { return formatForJavaKeyword(Generators.toLowerFirstChar(value)); } /** * Format a name for generated code. *

* If the formatted name is a keyword then {@link SbeTool#KEYWORD_APPEND_TOKEN} is appended if set. * * @param value to be formatted. * @return the string formatted as a valid name. * @throws IllegalStateException if a keyword and {@link SbeTool#KEYWORD_APPEND_TOKEN} is not set. */ public static String formatForJavaKeyword(final String value) { if (ValidationUtil.isJavaKeyword(value)) { final String keywordAppendToken = System.getProperty(SbeTool.KEYWORD_APPEND_TOKEN); if (null == keywordAppendToken) { throw new IllegalStateException( "Invalid property name='" + value + "' please correct the schema or consider setting system property: " + SbeTool.KEYWORD_APPEND_TOKEN); } return value + keywordAppendToken; } return value; } /** * Format a Getter name for generated code. * * @param propertyName to be formatted. * @return the property name formatted as a getter name. */ public static String formatGetterName(final String propertyName) { return "get" + Generators.toUpperFirstChar(propertyName); } /** * Format a class name for the generated code. * * @param className to be formatted. * @return the formatted class name. */ public static String formatClassName(final String className) { return Generators.toUpperFirstChar(className); } /** * Shortcut to append a line of generated code * * @param builder string builder to which to append the line * @param indent current text indentation * @param line line to be appended */ public static void append(final StringBuilder builder, final String indent, final String line) { builder.append(indent).append(line).append('\n'); } /** * Code to fetch an instance of {@link java.nio.charset.Charset} corresponding to the given encoding. * * @param encoding as a string name (eg. UTF-8). * @return the code to fetch the associated Charset. */ public static String charset(final String encoding) { final String charsetName = STD_CHARSETS.get(encoding); if (null != charsetName) { return "java.nio.charset.StandardCharsets." + charsetName; } else { final String canonicalName = Charset.isSupported(encoding) ? Charset.forName(encoding).name() : encoding; return "java.nio.charset.Charset.forName(\"" + canonicalName + "\")"; } } /** * Code to fetch the name of the {@link Charset} given the encoding. * * @param encoding as a string name (eg. UTF-8). * @return the code to fetch the associated Charset name. */ public static String charsetName(final String encoding) { final String charsetName = STD_CHARSETS.get(encoding); if (null != charsetName) { return "java.nio.charset.StandardCharsets." + charsetName + ".name()"; } else { return "\"" + (Charset.isSupported(encoding) ? Charset.forName(encoding).name() : encoding) + "\""; } } /** * Checks if the given encoding represents an ASCII charset. * * @param encoding as a string name (e.g. ASCII). * @return {@code true} if the encoding denotes an ASCII charset. */ public static boolean isAsciiEncoding(final String encoding) { return "US_ASCII".equals(STD_CHARSETS.get(encoding)); } /** * Checks if the given encoding represents a UTF-8 charset. * * @param encoding as a string name (e.g. unicode-1-1-utf-8). * @return {@code true} if the encoding denotes a UTF-8 charset. */ public static boolean isUtf8Encoding(final String encoding) { return "UTF_8".equals(STD_CHARSETS.get(encoding)); } /** * Generate a literal value to be used in code generation. * * @param type of the lateral value. * @param value of the lateral. * @return a String representation of the Java literal. */ public static String generateLiteral(final PrimitiveType type, final String value) { String literal = ""; final String castType = javaTypeName(type); switch (type) { case CHAR: case UINT8: case INT8: case INT16: literal = "(" + castType + ")" + value; break; case UINT16: case INT32: literal = value; break; case UINT32: literal = value + "L"; break; case FLOAT: literal = value.endsWith("NaN") ? "Float.NaN" : value + "f"; break; case INT64: literal = value + "L"; break; case UINT64: literal = "0x" + Long.toHexString(Long.parseLong(value)) + "L"; break; case DOUBLE: literal = value.endsWith("NaN") ? "Double.NaN" : value + "d"; break; } return literal; } /** * Generate the Javadoc comment header for a type. * * @param sb to append to. * @param indent level for the comment. * @param typeToken for the type. */ public static void generateTypeJavadoc(final StringBuilder sb, final String indent, final Token typeToken) { final String description = typeToken.description(); if (Strings.isEmpty(description)) { return; } sb.append('\n') .append(indent).append("/**\n") .append(indent).append(" * "); escapeJavadoc(sb, description); sb.append('\n') .append(indent).append(" */\n"); } /** * Generate the Javadoc comment header for a bitset choice option decode method. * * @param out to append to. * @param indent level for the comment. * @param optionToken for the type. * @throws IOException on failing to write to output. */ public static void generateOptionDecodeJavadoc(final Appendable out, final String indent, final Token optionToken) throws IOException { final String description = optionToken.description(); if (Strings.isEmpty(description)) { return; } out.append(indent).append("/**\n") .append(indent).append(" * "); escapeJavadoc(out, description); out.append('\n') .append(indent).append(" *\n") .append(indent).append(" * @return true if ").append(optionToken.name()).append(" set or false if not.\n") .append(indent).append(" */\n"); } /** * Generate the Javadoc comment header for a bitset choice option encode method. * * @param out to append to. * @param indent level for the comment. * @param optionToken for the type. * @throws IOException on failing to write to output. */ public static void generateOptionEncodeJavadoc(final Appendable out, final String indent, final Token optionToken) throws IOException { final String description = optionToken.description(); if (Strings.isEmpty(description)) { return; } out.append(indent).append("/**\n") .append(indent).append(" * "); escapeJavadoc(out, description); final String name = optionToken.name(); out.append('\n') .append(indent).append(" *\n") .append(indent).append(" * @param value true if ").append(name).append(" is set or false if not.\n") .append(indent).append(" * @return this for a fluent API.\n") .append(indent).append(" */\n"); } /** * Generate the Javadoc comment header for flyweight property. * * @param sb to append to. * @param indent level for the comment. * @param propertyToken for the property name. * @param typeName for the property type. */ public static void generateFlyweightPropertyJavadoc( final StringBuilder sb, final String indent, final Token propertyToken, final String typeName) { final String description = propertyToken.description(); if (Strings.isEmpty(description)) { return; } sb.append('\n') .append(indent).append("/**\n") .append(indent).append(" * "); escapeJavadoc(sb, description); sb.append('\n') .append(indent).append(" *\n") .append(indent).append(" * @return ").append(typeName).append(" : "); escapeJavadoc(sb, description); sb.append("\n") .append(indent).append(" */"); } /** * Generate the Javadoc comment header for group encode property. * * @param sb to append to. * @param indent level for the comment. * @param propertyToken for the property name. * @param typeName for the property type. */ public static void generateGroupEncodePropertyJavadoc( final StringBuilder sb, final String indent, final Token propertyToken, final String typeName) { final String description = propertyToken.description(); if (Strings.isEmpty(description)) { return; } sb.append('\n') .append(indent).append("/**\n") .append(indent).append(" * "); escapeJavadoc(sb, description); sb.append("\n") .append(indent).append(" *\n") .append(indent).append(" * @param count of times the group will be encoded.\n") .append(indent).append(" * @return ").append(typeName).append(" : encoder for the group.\n") .append(indent).append(" */"); } static String encoderName(final String className) { return formatClassName(className) + "Encoder"; } static String decoderName(final String className) { return formatClassName(className) + "Decoder"; } private static void escapeJavadoc(final Appendable out, final String doc) throws IOException { for (int i = 0, length = doc.length(); i < length; i++) { final char c = doc.charAt(i); switch (c) { case '<': out.append("<"); break; case '>': out.append(">"); break; default: out.append(c); break; } } } private static void escapeJavadoc(final StringBuilder sb, final String doc) { for (int i = 0, length = doc.length(); i < length; i++) { final char c = doc.charAt(i); switch (c) { case '<': sb.append("<"); break; case '>': sb.append(">"); break; case '&': sb.append("&"); break; default: sb.append(c); break; } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy