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

org.nuiton.eugene.java.extension.ImportsManager Maven / Gradle / Ivy

There is a newer version: 3.0
Show newest version
/*
 * #%L
 * EUGene :: EUGene
 * %%
 * Copyright (C) 2004 - 2010 CodeLutin
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as 
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 * 
 * This program 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 Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public 
 * License along with this program.  If not, see
 * .
 * #L%
 */

package org.nuiton.eugene.java.extension;

import org.apache.commons.lang3.StringUtils;
import org.nuiton.eugene.GeneratorUtil;
import org.nuiton.eugene.java.JavaGeneratorUtil;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Class used in generators that allows to manage easily imports. A first-pass
 * allow to register imports, and in a second-pass, returns the type to use in
 * generated code.
 *
 * @author athimel (Code Lutin)
 * @author Tony Chemit - [email protected]
 * @since 2.0.0
 */
public class ImportsManager {

    /**
     * Internal states of the imports manager.
     *
     * @see #state
     */
    private enum State {

        FILLING, READING
    }

    private static Set primitiveTypes;

    static {
        primitiveTypes = new HashSet<>();

        primitiveTypes.add("byte");
        primitiveTypes.add("Byte");
        primitiveTypes.add("short");
        primitiveTypes.add("Short");
        primitiveTypes.add("int");
        primitiveTypes.add("Integer");
        primitiveTypes.add("long");
        primitiveTypes.add("Long");
        primitiveTypes.add("float");
        primitiveTypes.add("Float");
        primitiveTypes.add("double");
        primitiveTypes.add("Double");

        primitiveTypes.add("char");
        primitiveTypes.add("Char");
        primitiveTypes.add("String");

        primitiveTypes.add("boolean");
        primitiveTypes.add("Boolean");

        primitiveTypes.add("void");

    }

    private Map imports = new HashMap<>();

    private State state = State.FILLING;

    /**
     * From the given class, add it to the imports list.
     *
     * @param clazz the class to import
     * @return true if import add was successful
     * @see ImportsManager#addImport(String)
     */
    public boolean addImport(Class clazz) {
        return addImport(clazz.getName());
    }

    /**
     * From the given fqn (fully qualified name), add it to the imports list.
     * If there is a conflict adding this import, will return false.
     * If reading of the imports has started, this method will return false,
     * unless type does not need to be imported.
     *
     * @param fqn the fully qualified name to import
     * @return true if import add was successful
     */
    public boolean addImport(String fqn) {

        // don't include null fqn
        if (fqn == null) {
            return false;
        }

        int lastDotIndex = getLastDotIndex(fqn);

        // if no package don't include it
        if (lastDotIndex == -1) {
            return true;
        }

        // Exclude java.lang classes

        if (fqn.trim().isEmpty() ||
            fqn.startsWith("java.lang.") && lastDotIndex == 9) {

            // reacts as if it was imported
            return true;
        }
        // Exclude primitive types
        if (primitiveTypes.contains(fqn)) {

            // was not imported
            return false;
        }

        if (fqn.endsWith("[]")) {

            // fqn contains an array definition
            String simpleFQN = fqn.substring(0, fqn.length() - 2);
            return addImport(simpleFQN);
        }

        if (JavaGeneratorUtil.containsGenerics(fqn)) {

            // Generics case :
            String[] parts = JavaGeneratorUtil.splitGeneric(fqn);
            boolean doImport = false;
            if (addImport(parts[0])) {
                doImport = true;
            }
            for (int i = 1, partsLength = parts.length; i < partsLength; i++) {
                String part = parts[i];
                addImport(part);
            }
            return doImport;
        }
//        // Reject generics
//        if (fqn.contains("<") || fqn.contains(">")) {
//            return false;
//        }
        String name = fqn.substring(lastDotIndex + 1);
        String inPlaceFqn = imports.get(name);
        if (inPlaceFqn == null) {
            // Someone has started to read imports, impossible to add some more
            if (state == State.READING) {
                return false;
            } else {
                imports.put(name, fqn);
                return true;
            }
        }
        // if fqn is not the same, return false. Otherwise, no need to override.
        return inPlaceFqn.equals(fqn);
    }

    /**
     * Accorging to the already added types, returns the type to write in file.
     * If there is a conflict, returns the fully qualified name, otherwise
     * returns the simple name
     *
     * @param clazz the clazz to add
     * @return the fqn or simple name according to in-place imports
     * @since 2.3.2
     */
    public String getType(Class clazz) {
        String type = getType(clazz.getName());
        return type;
    }

    /**
     * Accorging to the already added types, returns the type to write in file.
     * If there is a conflict, returns the fully qualified name, otherwise
     * returns the simple name
     *
     * @param fqn the fully qualified name to add
     * @return the fqn or simple name according to in-place imports
     */
    public String getType(String fqn) {
        boolean importResult = addImport(fqn);
        if (JavaGeneratorUtil.containsGenerics(fqn)) {

            String[] parts = JavaGeneratorUtil.splitGeneric(fqn);
            for (int i = 0; i < parts.length; i++) {
                String part = parts[i];
                parts[i] = getType(part);
            }
            return JavaGeneratorUtil.joinGeneric(parts);
        }

        if (!importResult) {

            // There is a conflict, do not use simple name
            return fqn;
        }
        // No conflict, use simple name
        int packageEndIndex = getLastDotIndex(fqn);
        if (packageEndIndex == -1) {
            return fqn;
        } else {
            return fqn.substring(packageEndIndex + 1);
        }
    }

    public String getReturnType(String returnType) {
        if (StringUtils.isBlank(returnType)) {
            return null;
        }
        if (JavaGeneratorUtil.containsGenerics(returnType)) {

            // the return type is in two parts : <...> ...
            String[] parts =
                    GeneratorUtil.splitGenericDefinition(returnType);
            if (parts.length == 1) {

                // no generic definition
                return getType(returnType);
            }
            String genericDef = parts[0];
            String strictReturnType = parts[1];
            if (StringUtils.isNotBlank(genericDef)) {
                genericDef += " ";
            }
            return genericDef + getType(strictReturnType);
        } else {
            return getType(returnType);
        }
    }

    /**
     * List the imports. This method will remove the useless imports according
     * to the given packageName (no need to import a class in the same package)
     *
     * @param packageName the current package name (to avoid useless imports)
     * @return the imports alphabeticaly sorted
     */
    public List getImports(String packageName) {
        state = State.READING;
        List result = new ArrayList<>();
        int packageLength = packageName.length();
        String packagePrefix = packageName + ".";
        for (String fqn : imports.values()) {
            int lastDotIndex = getLastDotIndex(fqn);

            // only keep sub package of given package
            if (!(lastDotIndex == packageLength &&
                  fqn.startsWith(packagePrefix))) {
                result.add(fqn);
            }
        }
        Collections.sort(result);
        return result;
    }

    /**
     * Method to reset imports list. If imports has been listed, it becomes back
     * possible to add imports.
     */
    public void clearImports() {
        imports.clear();
        state = State.FILLING;
    }

    /**
     * Obtains the last dot index in the given fqn.
     *
     * @param fqn the fqn to test
     * @return the last index of a dot in given fqn
     * @since 2.3.2
     */
    public int getLastDotIndex(String fqn) {
        return fqn.lastIndexOf(".");
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy