JCodeModel.parseType(String) silently ignores type params in specific scenarios (#1517)
Signed-off-by: Jorge Bescos Gascon <jorge.bescos.gascon@oracle.com>
diff --git a/jaxb-ri/codemodel/codemodel/src/main/java/com/sun/codemodel/JCodeModel.java b/jaxb-ri/codemodel/codemodel/src/main/java/com/sun/codemodel/JCodeModel.java
index cf61b7d..882258d 100644
--- a/jaxb-ri/codemodel/codemodel/src/main/java/com/sun/codemodel/JCodeModel.java
+++ b/jaxb-ri/codemodel/codemodel/src/main/java/com/sun/codemodel/JCodeModel.java
@@ -18,8 +18,10 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
import com.sun.codemodel.writer.FileCodeWriter;
import com.sun.codemodel.writer.ProgressCodeWriter;
@@ -459,7 +461,95 @@
}
// existing class
- return new TypeNameParser(name).parseTypeName();
+// return new TypeNameParser(name).parseTypeName();
+ return new TreeParser().parseTypeName(name);
+ }
+
+ private class TreeParser {
+
+ private Node buildTree(String str) {
+ StringBuilder content = new StringBuilder();
+ Node root = new Node(null);
+ root.value = str;
+ Node current = root;
+ for (int i = 0; i < str.length(); i++) {
+ char c = str.charAt(i);
+ if (c == '<') {
+ Node child = new Node(current);
+ current.value = content.toString();
+ current.childs.add(child);
+ current = child;
+ content = new StringBuilder();
+ } else if (c == '>') {
+ if (current.value == null) {
+ current.value = content.toString();
+ }
+ current = current.parent;
+ content = new StringBuilder();
+ } else if (c == ',') {
+ if (current.value == null) {
+ current.value = content.toString();
+ }
+ Node brother = new Node(current.parent);
+ brother.parent.childs.add(brother);
+ current = brother;
+ content = new StringBuilder();
+ } else {
+ content.append(c);
+ }
+ }
+ return root;
+ }
+
+ private void postOrderCreateJClass(Node node) throws ClassNotFoundException {
+ if (node != null) {
+ for (Node child : node.childs) {
+ postOrderCreateJClass(child);
+ }
+ node.jClass = new TypeNameParser(node.value).parseTypeName();
+ if (!node.childs.isEmpty()) {
+ List<JClass> args = node.childs.stream().map(n -> n.jClass).collect(Collectors.toList());
+ JClass[] argsA = args.toArray(new JClass[args.size()]);
+ JClass clazz = node.jClass.narrow(argsA);
+ node.jClass = clazz;
+ }
+ }
+ }
+
+ private JClass parseTypeName(String str) throws ClassNotFoundException {
+ Node root = buildTree(str);
+ postOrderCreateJClass(root);
+ return root.jClass;
+ }
+ }
+
+ private static class Node {
+ private String value;
+ private JClass jClass;
+ private final Node parent;
+ private final List<Node> childs = new LinkedList<>();
+
+ public Node(Node parent) {
+ this.parent = parent;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder(value.toString());
+ boolean hasChilds = !childs.isEmpty();
+ if (hasChilds) {
+ builder.append("<");
+ }
+ for (Node child : childs) {
+ builder.append(child.toString()).append(",");
+ }
+ if (hasChilds) {
+ // Remove last comma
+ builder.deleteCharAt(builder.length() - 1);
+ builder.append(">");
+ }
+ return builder.toString();
+ }
}
private final class TypeNameParser {
diff --git a/jaxb-ri/codemodel/codemodel/src/test/java/com/sun/codemodel/Issue1505Test.java b/jaxb-ri/codemodel/codemodel/src/test/java/com/sun/codemodel/Issue1505Test.java
new file mode 100644
index 0000000..563cfed
--- /dev/null
+++ b/jaxb-ri/codemodel/codemodel/src/test/java/com/sun/codemodel/Issue1505Test.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package com.sun.codemodel;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class Issue1505Test {
+
+ private void checks(String test) {
+ checks(test, test);
+ }
+
+ private void checks(String expected, String test) {
+ JCodeModel model = new JCodeModel();
+ try {
+ JType type = model.parseType(test);
+ assertEquals(expected, type.fullName());
+ } catch (ClassNotFoundException e) {
+ fail(e.getMessage());
+ }
+ }
+
+ @Test
+ public void test1() {
+ checks("Map<K,Pair<X,Y>>");
+ }
+
+ @Test
+ public void test2() {
+ checks("Map<Pair<X,Y>,V>");
+ }
+
+ @Test
+ public void test3() {
+ checks("M<K,V<X>>");
+ }
+
+ @Test
+ public void test4() {
+ checks("M<K<X>,V>");
+ }
+
+ @Test
+ public void test5() {
+ checks("A<B,C>");
+ }
+
+ @Test
+ public void test6() {
+ checks("M<K>");
+ }
+
+ @Test
+ public void test7() {
+ checks("M<K<Q>>");
+ }
+
+ @Test
+ public void test8() {
+ checks("M<K,V>");
+ }
+
+ @Test
+ public void test9() {
+ checks("M<K,V<Q>>");
+ }
+
+ @Test
+ @Ignore("Not supported")
+ public void test10() {
+ checks("java.util.Map<K extends ?,V extends ?>");
+ }
+
+ @Test
+ public void test11() {
+ checks("Map<Key,Value<Que>>");
+ }
+
+ @Test
+ public void test12() {
+ checks("M<K<T>,V>");
+ }
+
+ @Test
+ public void test13() {
+ checks("M<T,Q,R>");
+ }
+
+ @Test
+ public void test14() {
+ checks("M<A,B<C,D<E>,F<G>>>");
+ }
+
+ @Test
+ public void test15() {
+ checks("M<A,B[]<C[],D[]<E>,F<G[]>>>");
+ }
+
+ @Test
+ public void test16() {
+ checks("M<? extends A,? extends B>");
+ }
+
+ @Test
+ @Ignore("Not supported")
+ public void test17() {
+ checks("M<A extends Object,B extends Object>");
+ }
+
+ @Test
+ public void test18() {
+ checks("java.lang.Object");
+ }
+
+ @Test
+ public void test19() {
+ checks("java.util.ArrayList<String>");
+ }
+}