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

com.peterabeles.autocode.ConvertFile32From64 Maven / Gradle / Ivy

/*
 * Auto64to32F is released to Public Domain or MIT License. Either maybe used.
 */

package com.peterabeles.autocode;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * Converts a file written for 64bit numbers into 32bit numbers by replacing keywords.
 *
 * @author Peter Abeles
 */
public class ConvertFile32From64 {

    InputStream in;
    PrintStream out;

    // file specified custom list of ignore tokens
    List customIgnore = new ArrayList<>();
    List replacements = new ArrayList<>();
    List replaceStartsWith = new ArrayList<>();
    List replacementsAfter = new ArrayList<>();

    boolean skipFilterOnLine;
    /**
     * If true then the files will be marked as being auto generated code. These files are often skipped my code
     * validation tools.
     */
    public boolean markAsAutoGenerated = false;
    private Language language = Language.JAVA;

    /**
     * Constructor
     * @param addDefaultReplacements If true all of the defaults replacement patterns are applied.
     */
    public ConvertFile32From64( Language language, boolean addDefaultReplacements ) {
        if( addDefaultReplacements ) {
            if( language == Language.JAVA ) {
//            replacePattern("/\\*\\*/double", "FIXED_DOUBLE");
                replacePattern("double", "float");
                replacePattern("Double", "Float");
                replacePattern("_F64", "_F32");

                replaceStartsWith("Math.", "(float)Math.");
                replaceStartsWith("-Math.", "(float)-Math.");

                replacePatternAfter("FIXED_DOUBLE", "/\\*\\*/double");
            } else if( language == Language.KOTLIN ) {
                replacePattern("Double", "Float");
                replacePattern("_F64", "_F32");
                replacePatternAfter("FIXED_DOUBLE", "/\\*\\*/Double");
            }
        }
    }
    public ConvertFile32From64( boolean addDefaultReplacements ) {
        this(Language.JAVA,addDefaultReplacements);
    }

    public static String fileNameNoExtension( File f , Language language ) {
        int langLength = language.suffix().length()+1; // +1 for '.', e.g. ".java"
        String fileName=f.getName();
        return fileName.substring(0, fileName.length() - langLength);
    }

    /**
     * Applies the specified keyword replacements to the input file and saves the results to the output file
     * @param inputFile File that is to be transformed. Unmodified.
     * @param outputFile Where results of the transformation are written to.  Modified
     * @throws IOException If something goes wrong this is thrown.
     */
    public void process(File inputFile, File outputFile ) throws IOException {
        scanForCustomization(inputFile);

        try {
            in = new FileInputStream(inputFile);
            out = new PrintStream(outputFile);

            int n;
            StringBuilder s = new StringBuilder(1024);
            boolean prevChar = false;

            State state = State.INITIALIZING;
            int totalTokens = 0;
            boolean insideBlockComments = false;
            boolean insideLineComment = false;

            int lineCharacterCount = 0;
            skipFilterOnLine = false;

            while ((n = in.read()) != -1) {
                if( n == '\n' || n == '\r' ) {
                    lineCharacterCount = 0;
                    if (insideLineComment ) {
                        insideLineComment = false;
                    }
                } else {
                    lineCharacterCount++;
                }

                if (Character.isWhitespace((char) n)) {
                    if (prevChar) {
                        boolean skip = false;
                        String token = s.toString();
                        if (insideBlockComments) {
                            if (token.startsWith("*/"))
                                insideBlockComments = false;
                        }
                        if (!(insideBlockComments || insideLineComment)) {
                            if (token.startsWith("/*"))
                                insideBlockComments = true;
                            else if (token.startsWith("//"))
                                insideLineComment = true;
                        }

                        if( insideLineComment && lineCharacterCount == token.length()+1 ) {
                            if( token.startsWith("//NOFILTER")) {
                                skipFilterOnLine = true;
                                skip = true;
                            }
                        }
                        if( !skip ) {
                            switch (state) {
                                case INITIALIZING:
                                    if (totalTokens == 0 && token.startsWith("/*")) {
                                        state = State.INSIDE_COPYRIGHT;
                                    } else if (!(insideBlockComments || insideLineComment) &&
                                            (token.compareTo("class") == 0 || token.compareTo("interface") == 0)) {
                                        state = State.BEFORE_CLASS_NAME;
                                    }
                                    handleToken(token);
                                    break;

                                case INSIDE_COPYRIGHT:
                                    if (token.compareTo("*/") == 0) {
                                        state = State.INITIALIZING;
                                    }
                                    out.print(token);
                                    break;

                                case BEFORE_CLASS_NAME: // for the class name to be the same as the output file
                                    state = State.MAIN;
                                    // In Java there could be Generics touching the class name E.g. class Foo
                                    // make sure we keep that extra info
                                    String inputName = fileNameNoExtension(inputFile,language);
                                    String outputName = fileNameNoExtension(outputFile,language);
                                    out.print(outputName + token.substring(inputName.length()));
                                    break;

                                case MAIN:
                                    handleToken(token);
                                    break;
                            }
                        }
                        s.delete(0, s.length());
                        prevChar = false;
                        totalTokens++;
                    }
                    out.write(n);
                    if( n == '\n' || n == '\r' ) {
                        skipFilterOnLine = false;
                    }
                } else {
                    prevChar = true;
                    s.append((char) n);
                }
            }

            if (prevChar) {
                handleToken(s.toString());
            }
        } finally {
            out.close();
            in.close();
            // Crashes when run in travis-ci with no error message.  Maybe file descriptors
            // are running out because the GC isn't running?
            System.gc();
        }

        // See if it needs to do some additional modifications
        // TODO speed up by never saving to disk the first time
        if( language == Language.JAVA && markAsAutoGenerated ) {
            in = new AugmentJavaFiles().augment(new FileInputStream(outputFile),fileNameNoExtension(inputFile,language));
            OutputStream out = new FileOutputStream(outputFile);
            copy(in, out);
            out.close();
        }
    }

    public void scanForCustomization( File inputFile ) throws IOException {
        customIgnore.clear();

        BufferedReader in = new BufferedReader(new FileReader(inputFile));

        String line;
        while( (line = in.readLine()) != null ) {
            if( !line.startsWith("//CUSTOM"))
                continue;

            String[] words = line.substring(9).split(" ");
            if( words[0].equals("ignore")) {
                customIgnore.add(words[1]);
            }
        }

        in.close();
    }

    /**
     * Adds a text replacement rule.  These will be run in the first pass
     *
     * @param pattern REGEX pattern that is searched for inside the word
     * @param replacement The string that the matching portion will be replaced with
     */
    public void replacePattern(String pattern, String replacement) {
        replacements.add(new Replacement(pattern, replacement));
    }

    /**
     * If a world starts with the pattern (just a string not a REGEX) then the matching text will be replaced
     * by the replacement string.
     *
     * @param pattern Regular text string
     * @param replacement The string that the matching portion will be replaced with
     */
    public void replaceStartsWith(String pattern, String replacement) {
        replaceStartsWith.add(new Replacement(pattern, replacement));
    }

    /**
     * Adds a text replacement rule.  These will be run in the final pass
     *
     * @param pattern REGEX pattern that is searched for inside the word
     * @param replacement The string that the matching portion will be replaced with
     */
    public void replacePatternAfter(String pattern, String replacement) {
        replacementsAfter.add(new Replacement(pattern, replacement));
    }

    private void handleToken(String s) {
        boolean ignore = false;

        for (int i = 0; i < customIgnore.size(); i++) {
            if( s.contains(customIgnore.get(i))) {
                ignore = true;
                break;
            }
        }

        if( !ignore && !skipFilterOnLine && !s.contains("/**/") ) {
            for (int i = 0; i < replacements.size(); i++) {
                Replacement r = replacements.get(i);
                s = s.replaceAll(r.pattern, r.replacement);
            }

            for (int i = 0; i < replaceStartsWith.size(); i++) {
                Replacement r = replaceStartsWith.get(i);
                s = replaceStartString(s, r.pattern, r.replacement);
            }

            s = handleFloats(s);

            for (int i = 0; i < replacementsAfter.size(); i++) {
                Replacement r = replacementsAfter.get(i);
                s = s.replaceAll(r.pattern, r.replacement);
            }
        }

        out.print(s);
        out.flush();
    }

    /**
     * Looks for a floating point constant number and tacks on a 'f' to the end
     * to make it into a float and not a double.
     */
    private String handleFloats(String input) {
        String regex = "\\d+\\.+\\d+([eE][-+]?\\d+)?";

        return input.replaceAll(regex, "$0f");
    }

    private String replaceStartString(String input, String from, String to) {

        if (input.startsWith(from)) {
            return to + input.substring(from.length());
        } else {
            return input;
        }
    }

    void copy(InputStream source, OutputStream target) throws IOException {
        byte[] buf = new byte[8192];
        int length;
        while ((length = source.read(buf)) > 0) {
            target.write(buf, 0, length);
        }
    }

    public Language getLanguage() {
        return language;
    }

    public void setLanguage(Language language) {
        this.language = language;
    }

    private static class Replacement {
        public String pattern;
        public String replacement;

        public Replacement(String pattern, String replacement) {
            this.pattern = pattern;
            this.replacement = replacement;
        }
    }

    private enum State {
        INITIALIZING,
        INSIDE_COPYRIGHT,
        BEFORE_CLASS_NAME,
        MAIN
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy