FullyQualifiedKotlinType.java
/*
* Copyright 2006-2026 the original author or authors.
*
* 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 org.mybatis.generator.api.dom.kotlin;
import static org.mybatis.generator.internal.util.StringUtility.stringHasValue;
import static org.mybatis.generator.internal.util.messages.Messages.getString;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.mybatis.generator.exception.TypeParsingException;
public class FullyQualifiedKotlinType {
private static final Set<String> AUTOMATIC_KOTLIN_PACKAGES = new HashSet<>();
static {
AUTOMATIC_KOTLIN_PACKAGES.add("kotlin"); //$NON-NLS-1$
AUTOMATIC_KOTLIN_PACKAGES.add("kotlin.collections"); //$NON-NLS-1$
}
private String packageName = ""; //$NON-NLS-1$
private final List<FullyQualifiedKotlinType> typeArguments = new ArrayList<>();
private String shortNameWithoutTypeArguments = ""; //$NON-NLS-1$
private boolean isExplicitlyImported;
public FullyQualifiedKotlinType(String fullTypeSpecification) {
parse(Objects.requireNonNull(fullTypeSpecification).trim());
}
public String getPackageName() {
return packageName;
}
public String getShortNameWithoutTypeArguments() {
return shortNameWithoutTypeArguments;
}
public String getShortNameWithTypeArguments() {
if (typeArguments.isEmpty()) {
return shortNameWithoutTypeArguments;
}
return typeArguments.stream().map(FullyQualifiedKotlinType::getShortNameWithTypeArguments)
.collect(Collectors.joining(", ", shortNameWithoutTypeArguments //$NON-NLS-1$
+ "<", ">")); //$NON-NLS-1$ //$NON-NLS-2$
}
public List<FullyQualifiedKotlinType> getTypeArguments() {
return typeArguments;
}
public void addTypeArgument(FullyQualifiedKotlinType typeArgument) {
typeArguments.add(typeArgument);
}
/**
* Returns a list of Strings that are the fully qualified names of this type,
* and any generic type argument associated with this type.
*
* @return the import list
*/
public Set<String> getImportList() {
Stream<String> thisImport;
if (isExplicitlyImported) {
thisImport = Stream.of(packageName + "." + shortNameWithoutTypeArguments); //$NON-NLS-1$
} else {
thisImport = Stream.empty();
}
Stream<String> ss = typeArguments.stream()
.map(FullyQualifiedKotlinType::getImportList)
.flatMap(Set::stream);
return Stream.of(thisImport, ss)
.flatMap(Function.identity())
.collect(Collectors.toSet());
}
private void parse(String fullTypeSpecification) {
int index = fullTypeSpecification.indexOf('<');
if (index == -1) {
simpleParse(fullTypeSpecification);
} else {
simpleParse(fullTypeSpecification.substring(0, index));
int endIndex = fullTypeSpecification.lastIndexOf('>');
if (endIndex == -1) {
throw new TypeParsingException(getString("RuntimeError.22", fullTypeSpecification)); //$NON-NLS-1$
}
genericParse(fullTypeSpecification.substring(index, endIndex + 1));
}
}
private void simpleParse(String typeSpecification) {
String baseQualifiedName = typeSpecification.trim();
if (baseQualifiedName.contains(".")) { //$NON-NLS-1$
packageName = getPackage(baseQualifiedName);
shortNameWithoutTypeArguments = baseQualifiedName.substring(packageName.length() + 1);
isExplicitlyImported = !AUTOMATIC_KOTLIN_PACKAGES.contains(packageName);
} else {
shortNameWithoutTypeArguments = baseQualifiedName;
isExplicitlyImported = false;
packageName = ""; //$NON-NLS-1$
}
}
private void genericParse(String genericSpecification) {
int lastIndex = genericSpecification.lastIndexOf('>');
if (lastIndex == -1) {
// shouldn't happen - should be caught already, but just in case...
throw new TypeParsingException(getString("RuntimeError.22", genericSpecification)); //$NON-NLS-1$
}
String argumentString = genericSpecification.substring(1, lastIndex);
// need to find "," outside a <> bounds
StringTokenizer st = new StringTokenizer(argumentString, ",<>", true); //$NON-NLS-1$
int openCount = 0;
StringBuilder sb = new StringBuilder();
while (st.hasMoreTokens()) {
String token = st.nextToken();
if ("<".equals(token)) { //$NON-NLS-1$
sb.append(token);
openCount++;
} else if (">".equals(token)) { //$NON-NLS-1$
sb.append(token);
openCount--;
} else if (",".equals(token)) { //$NON-NLS-1$
if (openCount == 0) {
typeArguments.add(new FullyQualifiedKotlinType(sb.toString()));
sb.setLength(0);
} else {
sb.append(token);
}
} else {
sb.append(token);
}
}
if (openCount != 0) {
throw new TypeParsingException(getString("RuntimeError.22", genericSpecification)); //$NON-NLS-1$
}
String finalType = sb.toString();
if (stringHasValue(finalType)) {
typeArguments.add(new FullyQualifiedKotlinType(finalType));
}
}
/**
* Returns the package name of a fully qualified type.
*
* <p>This method calculates the package as the part of the fully qualified name up
* to, but not including, the last element. Therefore, it does not support fully
* qualified inner classes. Not totally foolproof, but correct in most
* instances.
*
* @param baseQualifiedName the base qualified name
* @return the package
*/
private static String getPackage(String baseQualifiedName) {
int index = baseQualifiedName.lastIndexOf('.');
return baseQualifiedName.substring(0, index);
}
}