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

org.apache.cayenne.gen.ImportUtils Maven / Gradle / Ivy

/*****************************************************************
 *   Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you 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 org.apache.cayenne.gen;

import org.apache.cayenne.dba.TypesMapping;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.EmbeddableAttribute;
import org.apache.cayenne.map.ObjAttribute;
import org.apache.cayenne.util.Util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Methods for mangling strings.
 * 
 */
public class ImportUtils {

	public static final String[] importOrdering = { "java.", "javax.", "org.", "com." };

	static final String[] primitives = { "long", "double", "byte", "boolean", "float", "short", "int", "char" };

	static final String[] primitiveClasses = new String[] { Long.class.getName(), Double.class.getName(),
			Byte.class.getName(), Boolean.class.getName(), Float.class.getName(), Short.class.getName(),
			Integer.class.getName(), Character.class.getName() };

	static Map classesForPrimitives = Util.toMap(primitives, primitiveClasses);
	static Map primitivesForClasses = Util.toMap(primitiveClasses, primitives);

	protected Map importTypesMap = new HashMap<>();

	// Types forced to be FQN
	protected Map reservedImportTypesMap = new HashMap<>();

	protected String packageName;

	protected boolean canRegisterType(String typeName) {
		// Not sure why this would ever happen, but it did
		if (null == typeName) {
            return false;
        }

		StringUtils stringUtils = StringUtils.getInstance();
		String typeClassName = stringUtils.stripPackageName(typeName);
		String typePackageName = stringUtils.stripClass(typeName);

		if (typePackageName.length() == 0) {
            return false; // disallow non-packaged types (primitives, probably)
        }
		if ("java.lang".equals(typePackageName)) {
            return false;
        }

		// Can only have one type -- rest must use fqn
		if (reservedImportTypesMap.containsKey(typeClassName)) {
            return false;
        }
        return !importTypesMap.containsKey(typeClassName);
    }

	/**
	 * Reserve a fully-qualified data type class name so it cannot be used by
	 * another class. No import statements will be generated for reserved types.
	 * Typically, this is the fully-qualified class name of the class being
	 * generated.
	 * 
	 * @param typeName
	 *            FQ data type class name.
	 */
	public void addReservedType(String typeName) {
		if (!canRegisterType(typeName)) {
            return;
        }

		StringUtils stringUtils = StringUtils.getInstance();
		String typeClassName = stringUtils.stripPackageName(typeName);

		reservedImportTypesMap.put(typeClassName, typeName);
	}

	/**
	 * Register a fully-qualified data type class name. For example,
	 * org.apache.cayenne.GenericPersistentObject.
	 * 
	 * @param typeName
	 *            FQ data type class name.
	 */
	public void addType(String typeName) {
		if (!canRegisterType(typeName)) {
            return;
        }

		StringUtils stringUtils = StringUtils.getInstance();
		String typePackageName = stringUtils.stripClass(typeName);
		if (typePackageName.equals(packageName)) {
            return;
        }

		importTypesMap.put(stringUtils.stripPackageName(typeName), typeName);
	}

	/**
	 * Add the package name to use for this importUtil invocation.
	 */
	public void setPackage(String packageName) {
		this.packageName = packageName;
	}

	/**
	 * Performs processing similar to formatJavaType(String), with
	 * special handling of primitive types and their Java class counterparts.
	 * This method allows users to make a decision whether to use primitives or
	 * not, regardless of how type is mapped.
	 */
	public String formatJavaType(String typeName, boolean usePrimitives) {
		if (usePrimitives) {
			String primitive = primitivesForClasses.get(typeName);
			return (primitive != null) ? primitive : formatJavaType(typeName);
		} else {
			String primitiveClass = classesForPrimitives.get(typeName);
			return (primitiveClass != null) ? formatJavaType(primitiveClass) : formatJavaType(typeName);
		}
	}

	/**
	 * Removes registered package and non-reserved registered type name prefixes
	 * from java types
	 */
	public String formatJavaType(String typeName) {
		if (typeName != null) {
			StringUtils stringUtils = StringUtils.getInstance();
			String typeClassName = stringUtils.stripPackageName(typeName);

			if (!reservedImportTypesMap.containsKey(typeClassName)
                && importTypesMap.containsKey(typeClassName)
                    && typeName.equals(importTypesMap.get(typeClassName))) {
			    return typeClassName;
			}

			String typePackageName = stringUtils.stripClass(typeName);
			if ("java.lang".equals(typePackageName)) {
                return typeClassName;
            }
			if ((null != packageName) && (packageName.equals(typePackageName))) {
                return typeClassName;
            }
		}

		return typeName;
	}

	/**
	 * @since 3.0
	 */
	public String formatJavaTypeAsNonBooleanPrimitive(String type) {
		String value = ImportUtils.classesForPrimitives.get(type);
		return formatJavaType(value != null ? value : type);
	}

	/**
	 * @since 3.0
	 */
	public boolean isNonBooleanPrimitive(String type) {
		return ImportUtils.classesForPrimitives.containsKey(type) && !isBoolean(type);
	}

	/**
	 * @since 4.2
	 */
	public boolean isNumericPrimitive(String type) {
		return isNonBooleanPrimitive(type) && !"char".equals(type);
	}

	/**
	 * @since 3.0
	 */
	public boolean isBoolean(String type) {
		return "boolean".equals(type);
	}

	/**
	 * @since 4.1
	 * @param type name
	 * @return is given type primitive
	 */
	public boolean isPrimitive(String type) {
		return classesForPrimitives.containsKey(type);
	}

	/**
	 *
	 * This method decide can primitive type be used for given attribute.
	 * It can be used in following cases:
	 * 		- attribute is PK and primitive
	 * 		- attribute not PK and is mandatory
	 *
	 * @param attribute to check
	 * @return can primitive Java type be used
	 *
	 * @since 4.1
	 */
	public boolean canUsePrimitive(ObjAttribute attribute) {
        return !attribute.isLazy() && attribute.isMandatory() && isPrimitive(attribute.getType());
    }

	public boolean canUsePrimitive(EmbeddableAttribute attribute) {
		return isPrimitive(attribute.getType());
	}

	/**
	 * Generate package and list of import statements based on the registered
	 * types.
	 */
	public String generate() {
		StringBuilder outputBuffer = new StringBuilder();

		if (null != packageName) {
			outputBuffer.append("package ");
			outputBuffer.append(packageName);

			// Using UNIX line endings intentionally - generated Java files should look
			// the same regardless of platform to prevent developer teams working on
			// multiple OS's to override each other's work
			outputBuffer.append(";\n\n");
		}

		List typesList = new ArrayList<>(importTypesMap.values());
		typesList.sort((s1, s2) -> {
            for (String ordering : importOrdering) {
                if ((s1.startsWith(ordering)) && (!s2.startsWith(ordering))) {
                    return -1;
                }
                if ((!s1.startsWith(ordering)) && (s2.startsWith(ordering))) {
                    return 1;
                }
            }

            return s1.compareTo(s2);
        });

		String lastStringPrefix = null;
		boolean firstIteration = true;
		for (String typeName : typesList) {

			if (firstIteration) {
				firstIteration = false;
			} else {
				outputBuffer.append('\n');
			}
			// Output another newline if we're in a different root package.
			// Find root package
			String thisStringPrefix = typeName;
			int dotIndex = typeName.indexOf('.');
			if (-1 != dotIndex) {
				thisStringPrefix = typeName.substring(0, dotIndex);
			}
			// if this isn't the first import,
			if (null != lastStringPrefix) {
				// and it's different from the last import
				if (!thisStringPrefix.equals(lastStringPrefix)) {
					// output a newline; force UNIX style per comment above
					outputBuffer.append("\n");
				}
			}
			lastStringPrefix = thisStringPrefix;

			outputBuffer.append("import ");
			outputBuffer.append(typeName);
			outputBuffer.append(';');
		}

		return outputBuffer.toString();
	}

	/**
	 * @param attribute db attribute
	 * @return name of the java type
	 *
	 * @since 4.1
	 */
	public String dbAttributeToJavaType(DbAttribute attribute) {
		String javaTypeName = TypesMapping.getJavaBySqlType(attribute);
		return formatJavaType(javaTypeName);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy