| /* |
| * Copyright (c) 1997, 2022 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 java.io.File; |
| import java.io.IOException; |
| import java.io.PrintStream; |
| import java.lang.reflect.Modifier; |
| import java.util.ArrayList; |
| 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 com.sun.codemodel.writer.FileCodeWriter; |
| import com.sun.codemodel.writer.ProgressCodeWriter; |
| |
| /** |
| * Root of the code DOM. |
| * |
| * <p> |
| * Here's your typical CodeModel application. |
| * |
| * <pre>{@code |
| * JCodeModel cm = new JCodeModel(); |
| * |
| * // generate source code by populating the 'cm' tree. |
| * cm._class(...); |
| * ... |
| * |
| * // write them out |
| * cm.build(new File(".")); |
| * }</pre> |
| * |
| * <p> |
| * Every CodeModel node is always owned by one {@link JCodeModel} object |
| * at any given time (which can be often accesesd by the {@code owner()} method.) |
| * |
| * As such, when you generate Java code, most of the operation works |
| * in a top-down fashion. For example, you create a class from {@link JCodeModel}, |
| * which gives you a {@link JDefinedClass}. Then you invoke a method on it |
| * to generate a new method, which gives you {@link JMethod}, and so on. |
| * |
| * There are a few exceptions to this, most notably building {@link JExpression}s, |
| * but generally you work with CodeModel in a top-down fashion. |
| * |
| * Because of this design, most of the CodeModel classes aren't directly instanciable. |
| * |
| * |
| * <h2>Where to go from here?</h2> |
| * <p> |
| * Most of the time you'd want to populate new type definitions in a {@link JCodeModel}. |
| * See {@link #_class(String, ClassType)}. |
| */ |
| public final class JCodeModel { |
| |
| /** The packages that this JCodeWriter contains. */ |
| private final HashMap<String,JPackage> packages = new HashMap<>(); |
| |
| /** Java module in {@code module-info.java} file. */ |
| private JModule module; |
| |
| /** All JReferencedClasses are pooled here. */ |
| private final HashMap<Class<?>,JReferencedClass> refClasses = new HashMap<>(); |
| |
| private final Map<String, String> classNameReplacer = new HashMap<>(); |
| |
| /** Obtains a reference to the special "null" type. */ |
| public final JNullType NULL = new JNullType(this); |
| // primitive types |
| public final JPrimitiveType VOID = new JPrimitiveType(this,"void", Void.class); |
| public final JPrimitiveType BOOLEAN = new JPrimitiveType(this,"boolean",Boolean.class); |
| public final JPrimitiveType BYTE = new JPrimitiveType(this,"byte", Byte.class); |
| public final JPrimitiveType SHORT = new JPrimitiveType(this,"short", Short.class); |
| public final JPrimitiveType CHAR = new JPrimitiveType(this,"char", Character.class); |
| public final JPrimitiveType INT = new JPrimitiveType(this,"int", Integer.class); |
| public final JPrimitiveType FLOAT = new JPrimitiveType(this,"float", Float.class); |
| public final JPrimitiveType LONG = new JPrimitiveType(this,"long", Long.class); |
| public final JPrimitiveType DOUBLE = new JPrimitiveType(this,"double", Double.class); |
| |
| /** |
| * If the flag is true, we will consider two classes "Foo" and "foo" |
| * as a collision. |
| */ |
| /* package */ static final boolean isCaseSensitiveFileSystem = getFileSystemCaseSensitivity(); |
| |
| private static boolean getFileSystemCaseSensitivity() { |
| try { |
| // let the system property override, in case the user really |
| // wants to override. |
| if( System.getProperty("com.sun.codemodel.FileSystemCaseSensitive")!=null ) |
| return true; |
| } catch( Exception e ) {} |
| |
| // on Unix, it's case sensitive. |
| return (File.separatorChar == '/'); |
| } |
| |
| |
| public JCodeModel() {} |
| |
| /** |
| * Add a package to the list of packages to be generated. |
| * |
| * @param name |
| * Name of the package. Use "" to indicate the root package. |
| * |
| * @return Newly generated package |
| */ |
| public JPackage _package(String name) { |
| JPackage p = packages.get(name); |
| if (p == null) { |
| p = new JPackage(name, this); |
| packages.put(name, p); |
| } |
| return p; |
| } |
| |
| /** |
| * Creates and returns Java module to be generated. |
| * @param name The Name of Java module. |
| * @return New Java module. |
| */ |
| public JModule _moduleInfo(final String name) { |
| return module = new JModule(name); |
| } |
| |
| /** |
| * Returns existing Java module to be generated. |
| * @return Java module or {@code null} if Java module was not created yet. |
| */ |
| public JModule _getModuleInfo() { |
| return module; |
| } |
| |
| /** |
| * Creates Java module instance and adds existing packages with classes to the Java module info. |
| * Used to initialize and build Java module instance with existing packages content. |
| * @param name The Name of Java module. |
| * @param requires Requires directives to add. |
| * @throws IllegalStateException when Java module instance was not initialized. |
| */ |
| public void _prepareModuleInfo(final String name, final String ...requires) { |
| _moduleInfo(name); |
| _updateModuleInfo(requires); |
| } |
| |
| /** |
| * Adds existing packages with classes to the Java module info. |
| * Java module instance must exist before calling this method. |
| * Used to update Java module instance with existing packages content after it was prepared on client side. |
| * @param requires Requires directives to add. |
| * @throws IllegalStateException when Java module instance was not initialized. |
| */ |
| public void _updateModuleInfo(final String ...requires) { |
| if (module == null) { |
| throw new IllegalStateException("Java module instance was not initialized yet."); |
| } |
| module._exports(packages.values(), false); |
| module._requires(requires); |
| } |
| |
| public JPackage rootPackage() { |
| return _package(""); |
| } |
| |
| /** |
| * Returns an iterator that walks the packages defined using this code |
| * writer. |
| */ |
| public Iterator<JPackage> packages() { |
| return packages.values().iterator(); |
| } |
| |
| /** |
| * Creates a new generated class. |
| * |
| * @exception JClassAlreadyExistsException |
| * When the specified class/interface was already created. |
| */ |
| public JDefinedClass _class(String fullyqualifiedName) throws JClassAlreadyExistsException { |
| return _class(fullyqualifiedName,ClassType.CLASS); |
| } |
| |
| /** |
| * Creates a dummy, unknown {@link JClass} that represents a given name. |
| * |
| * <p> |
| * This method is useful when the code generation needs to include the user-specified |
| * class that may or may not exist, and only thing known about it is a class name. |
| */ |
| public JClass directClass(String name) { |
| return new JDirectClass(this,name); |
| } |
| |
| /** |
| * Creates a new generated class. |
| * |
| * @exception JClassAlreadyExistsException |
| * When the specified class/interface was already created. |
| */ |
| public JDefinedClass _class(int mods, String fullyqualifiedName,ClassType t) throws JClassAlreadyExistsException { |
| int idx = fullyqualifiedName.lastIndexOf('.'); |
| if( idx<0 ) return rootPackage()._class(fullyqualifiedName); |
| else |
| return _package(fullyqualifiedName.substring(0,idx)) |
| ._class(mods, fullyqualifiedName.substring(idx+1), t ); |
| } |
| |
| /** |
| * Creates a new generated class. |
| * |
| * @exception JClassAlreadyExistsException |
| * When the specified class/interface was already created. |
| */ |
| public JDefinedClass _class(String fullyqualifiedName,ClassType t) throws JClassAlreadyExistsException { |
| return _class( JMod.PUBLIC, fullyqualifiedName, t ); |
| } |
| |
| /** |
| * Gets a reference to the already created generated class. |
| * |
| * @return null |
| * If the class is not yet created. |
| * @see JPackage#_getClass(String) |
| */ |
| public JDefinedClass _getClass(String fullyQualifiedName) { |
| int idx = fullyQualifiedName.lastIndexOf('.'); |
| if( idx<0 ) return rootPackage()._getClass(fullyQualifiedName); |
| else |
| return _package(fullyQualifiedName.substring(0,idx)) |
| ._getClass( fullyQualifiedName.substring(idx+1) ); |
| } |
| |
| /** |
| * Creates a new anonymous class. |
| * |
| * @deprecated |
| * The naming convention doesn't match the rest of the CodeModel. |
| * Use {@link #anonymousClass(JClass)} instead. |
| */ |
| @Deprecated |
| public JDefinedClass newAnonymousClass(JClass baseType) { |
| return new JAnonymousClass(baseType); |
| } |
| |
| /** |
| * Creates a new anonymous class. |
| */ |
| public JDefinedClass anonymousClass(JClass baseType) { |
| return new JAnonymousClass(baseType); |
| } |
| |
| public JDefinedClass anonymousClass(Class<?> baseType) { |
| return anonymousClass(ref(baseType)); |
| } |
| |
| /** |
| * Generates Java source code. |
| * A convenience method for <code>build(destDir,destDir,System.out)</code>. |
| * |
| * @param destDir |
| * source files are generated into this directory. |
| * @param status |
| * if non-null, progress indication will be sent to this stream. |
| */ |
| public void build( File destDir, PrintStream status ) throws IOException { |
| build(destDir,destDir,status); |
| } |
| |
| /** |
| * Generates Java source code. |
| * A convenience method that calls {@link #build(CodeWriter,CodeWriter)}. |
| * |
| * @param srcDir |
| * Java source files are generated into this directory. |
| * @param resourceDir |
| * Other resource files are generated into this directory. |
| * @param status |
| * if non-null, progress indication will be sent to this stream. |
| */ |
| public void build( File srcDir, File resourceDir, PrintStream status ) throws IOException { |
| CodeWriter src = new FileCodeWriter(srcDir); |
| CodeWriter res = new FileCodeWriter(resourceDir); |
| if(status!=null) { |
| src = new ProgressCodeWriter(src, status ); |
| res = new ProgressCodeWriter(res, status ); |
| } |
| build(src,res); |
| } |
| |
| /** |
| * A convenience method for <code>build(destDir,System.out)</code>. |
| */ |
| public void build( File destDir ) throws IOException { |
| build(destDir,System.out); |
| } |
| |
| /** |
| * A convenience method for <code>build(srcDir,resourceDir,System.out)</code>. |
| */ |
| public void build( File srcDir, File resourceDir ) throws IOException { |
| build(srcDir,resourceDir,System.out); |
| } |
| |
| /** |
| * A convenience method for <code>build(out,out)</code>. |
| */ |
| public void build( CodeWriter out ) throws IOException { |
| build(out,out); |
| } |
| |
| /** |
| * Generates Java source code. |
| */ |
| public void build( CodeWriter source, CodeWriter resource ) throws IOException { |
| JPackage[] pkgs = packages.values().toArray(new JPackage[0]); |
| // avoid concurrent modification exception |
| for( JPackage pkg : pkgs ) { |
| pkg.build(source,resource); |
| } |
| if (module != null) { |
| module.build(source); |
| } |
| source.close(); |
| resource.close(); |
| } |
| |
| /** |
| * Returns the number of files to be generated if |
| * {@link #build} is invoked now. |
| */ |
| public int countArtifacts() { |
| int r = 0; |
| JPackage[] pkgs = packages.values().toArray(new JPackage[0]); |
| // avoid concurrent modification exception |
| for( JPackage pkg : pkgs ) |
| r += pkg.countArtifacts(); |
| return r; |
| } |
| |
| /** |
| * Specify class names or packages to be replaced when the model is dumped into files. |
| * @param c1 the regular expression to which class name or package will be replaced. |
| * @param c2 the string to be substituted for the first match. |
| */ |
| public void addClassNameReplacer(String c1, String c2) { |
| classNameReplacer.put(c1, c2); |
| } |
| |
| /** |
| * Gives an unmodifiable copy of classNameReplacer |
| * @return classNameReplacer |
| */ |
| public Map<String, String> classNameReplacer() { |
| return Collections.unmodifiableMap(classNameReplacer); |
| } |
| |
| /** |
| * Obtains a reference to an existing class from its Class object. |
| * |
| * <p> |
| * The parameter may not be primitive. |
| * |
| * @see #_ref(Class) for the version that handles more cases. |
| */ |
| public JClass ref(Class<?> clazz) { |
| JReferencedClass jrc = refClasses.get(clazz); |
| if (jrc == null) { |
| if (clazz.isPrimitive()) |
| throw new IllegalArgumentException(clazz+" is a primitive"); |
| if (clazz.isArray()) { |
| return new JArrayClass(this, _ref(clazz.getComponentType())); |
| } else { |
| jrc = new JReferencedClass(clazz); |
| refClasses.put(clazz, jrc); |
| } |
| } |
| return jrc; |
| } |
| |
| public JType _ref(Class<?> c) { |
| if(c.isPrimitive()) |
| return JType.parse(this,c.getName()); |
| else |
| return ref(c); |
| } |
| |
| /** |
| * Obtains a reference to an existing class from its fully-qualified |
| * class name. |
| * |
| * <p> |
| * First, this method attempts to load the class of the given name. |
| * If that fails, we assume that the class is derived straight from |
| * {@link Object}, and return a {@link JClass}. |
| */ |
| public JClass ref(String fullyQualifiedClassName) { |
| try { |
| // try the context class loader first |
| return ref(SecureLoader.getContextClassLoader().loadClass(fullyQualifiedClassName)); |
| } catch (ClassNotFoundException e) { |
| // fall through |
| } |
| // then the default mechanism. |
| try { |
| return ref(Class.forName(fullyQualifiedClassName)); |
| } catch (ClassNotFoundException e1) { |
| // fall through |
| } |
| |
| // assume it's not visible to us. |
| return new JDirectClass(this,fullyQualifiedClassName); |
| } |
| |
| /** |
| * Cached for {@link #wildcard()}. |
| */ |
| private JClass wildcard; |
| |
| /** |
| * Gets a {@link JClass} representation for "?", |
| * which is equivalent to "? extends Object". |
| */ |
| public JClass wildcard() { |
| if(wildcard==null) |
| wildcard = ref(Object.class).wildcard(); |
| return wildcard; |
| } |
| |
| /** |
| * Obtains a type object from a type name. |
| * |
| * <p> |
| * This method handles primitive types, arrays, and existing {@link Class}es. |
| * |
| * @exception ClassNotFoundException |
| * If the specified type is not found. |
| */ |
| public JType parseType(String name) throws ClassNotFoundException { |
| // array |
| if(name.endsWith("[]")) |
| return parseType(name.substring(0,name.length()-2)).array(); |
| |
| // try primitive type |
| try { |
| return JType.parse(this,name); |
| } catch (IllegalArgumentException e) { |
| } |
| |
| // existing class |
| // 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()) { |
| JClass[] argsA = node.childs.stream().map(n -> n.jClass).toArray(JClass[]::new); |
| node.jClass = node.jClass.narrow(argsA); |
| } |
| } |
| } |
| |
| 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); |
| 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 { |
| private final String s; |
| private int idx; |
| |
| public TypeNameParser(String s) { |
| this.s = s; |
| } |
| |
| /** |
| * Parses a type name token T (which can be potentially of the form {@code T<T1,T2,...>}, |
| * or {@code ? extends/super T}.) |
| * |
| * @return the index of the character next to T. |
| */ |
| JClass parseTypeName() throws ClassNotFoundException { |
| int start = idx; |
| |
| if(s.charAt(idx)=='?') { |
| // wildcard |
| idx++; |
| ws(); |
| String head = s.substring(idx); |
| if(head.startsWith("extends")) { |
| idx+=7; |
| ws(); |
| return parseTypeName().wildcard(); |
| } else |
| if(head.startsWith("super")) { |
| throw new UnsupportedOperationException("? super T not implemented"); |
| } else { |
| // not supported |
| throw new IllegalArgumentException("only extends/super can follow ?, but found "+s.substring(idx)); |
| } |
| } |
| |
| while(idx<s.length()) { |
| char ch = s.charAt(idx); |
| if(Character.isJavaIdentifierStart(ch) |
| || Character.isJavaIdentifierPart(ch) |
| || ch=='.') |
| idx++; |
| else |
| break; |
| } |
| |
| JClass clazz = ref(s.substring(start,idx)); |
| |
| return parseSuffix(clazz); |
| } |
| |
| /** |
| * Parses additional left-associative suffixes, like type arguments |
| * and array specifiers. |
| */ |
| private JClass parseSuffix(JClass clazz) throws ClassNotFoundException { |
| if(idx==s.length()) |
| return clazz; // hit EOL |
| |
| char ch = s.charAt(idx); |
| |
| if(ch=='<') |
| return parseSuffix(parseArguments(clazz)); |
| |
| if(ch=='[') { |
| if(s.charAt(idx+1)==']') { |
| idx+=2; |
| return parseSuffix(clazz.array()); |
| } |
| throw new IllegalArgumentException("Expected ']' but found "+s.substring(idx+1)); |
| } |
| |
| return clazz; |
| } |
| |
| /** |
| * Skips whitespaces |
| */ |
| private void ws() { |
| while(Character.isWhitespace(s.charAt(idx)) && idx<s.length()) |
| idx++; |
| } |
| |
| /** |
| * Parses {@code '<T1,T2,...,Tn>'} |
| * |
| * @return the index of the character next to '{@literal >}' |
| */ |
| private JClass parseArguments(JClass rawType) throws ClassNotFoundException { |
| if(s.charAt(idx)!='<') |
| throw new IllegalArgumentException(); |
| idx++; |
| |
| List<JClass> args = new ArrayList<>(); |
| |
| while(true) { |
| args.add(parseTypeName()); |
| if(idx==s.length()) |
| throw new IllegalArgumentException("Missing '>' in "+s); |
| char ch = s.charAt(idx); |
| if(ch=='>') |
| return rawType.narrow(args.toArray(new JClass[0])); |
| |
| if(ch!=',') |
| throw new IllegalArgumentException(s); |
| idx++; |
| } |
| |
| } |
| } |
| |
| /** |
| * References to existing classes. |
| * |
| * <p> |
| * JReferencedClass is kept in a pool so that they are shared. |
| * There is one pool for each JCodeModel object. |
| * |
| * <p> |
| * It is impossible to cache JReferencedClass globally only because |
| * there is the _package() method, which obtains the owner JPackage |
| * object, which is scoped to JCodeModel. |
| */ |
| private class JReferencedClass extends JClass implements JDeclaration { |
| private final Class<?> _class; |
| |
| JReferencedClass(Class<?> _clazz) { |
| super(JCodeModel.this); |
| this._class = _clazz; |
| assert !_class.isArray(); |
| } |
| |
| @Override |
| public String name() { |
| return _class.getSimpleName().replace('$','.'); |
| } |
| |
| @Override |
| public String fullName() { |
| return _class.getName().replace('$','.'); |
| } |
| |
| @Override |
| public String binaryName() { |
| return _class.getName(); |
| } |
| |
| @Override |
| public JClass outer() { |
| Class<?> p = _class.getDeclaringClass(); |
| if(p==null) return null; |
| return ref(p); |
| } |
| |
| @Override |
| public JPackage _package() { |
| String name = fullName(); |
| |
| // this type is array |
| if (name.indexOf('[') != -1) |
| return JCodeModel.this._package(""); |
| |
| // other normal case |
| int idx = name.lastIndexOf('.'); |
| if (idx < 0) |
| return JCodeModel.this._package(""); |
| else |
| return JCodeModel.this._package(name.substring(0, idx)); |
| } |
| |
| @Override |
| public JClass _extends() { |
| Class<?> sp = _class.getSuperclass(); |
| if (sp == null) { |
| if(isInterface()) |
| return owner().ref(Object.class); |
| return null; |
| } else |
| return ref(sp); |
| } |
| |
| @Override |
| public Iterator<JClass> _implements() { |
| final Class<?>[] interfaces = _class.getInterfaces(); |
| return new Iterator<>() { |
| private int idx = 0; |
| |
| @Override |
| public boolean hasNext() { |
| return idx < interfaces.length; |
| } |
| |
| @Override |
| public JClass next() { |
| return JCodeModel.this.ref(interfaces[idx++]); |
| } |
| |
| @Override |
| public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| }; |
| } |
| |
| @Override |
| public boolean isInterface() { |
| return _class.isInterface(); |
| } |
| |
| @Override |
| public boolean isAbstract() { |
| return Modifier.isAbstract(_class.getModifiers()); |
| } |
| |
| @Override |
| public JPrimitiveType getPrimitiveType() { |
| Class<?> v = boxToPrimitive.get(_class); |
| if(v!=null) |
| return JType.parse(JCodeModel.this,v.getName()); |
| else |
| return null; |
| } |
| |
| @Override |
| public boolean isArray() { |
| return false; |
| } |
| |
| @Override |
| public void declare(JFormatter f) { |
| } |
| |
| @Override |
| public JTypeVar[] typeParams() { |
| // TODO: does JDK 1.5 reflection provides these information? |
| return super.typeParams(); |
| } |
| |
| @Override |
| protected JClass substituteParams(JTypeVar[] variables, List<JClass> bindings) { |
| // TODO: does JDK 1.5 reflection provides these information? |
| return this; |
| } |
| } |
| |
| /** |
| * Conversion from primitive type {@link Class} (such as {@link Integer#TYPE} |
| * to its boxed type (such as {@code Integer.class}) |
| */ |
| public static final Map<Class<?>,Class<?>> primitiveToBox; |
| /** |
| * The reverse look up for {@link #primitiveToBox} |
| */ |
| public static final Map<Class<?>,Class<?>> boxToPrimitive; |
| |
| static { |
| Map<Class<?>,Class<?>> m1 = new HashMap<>(); |
| Map<Class<?>,Class<?>> m2 = new HashMap<>(); |
| |
| m1.put(Boolean.class,Boolean.TYPE); |
| m1.put(Byte.class,Byte.TYPE); |
| m1.put(Character.class,Character.TYPE); |
| m1.put(Double.class,Double.TYPE); |
| m1.put(Float.class,Float.TYPE); |
| m1.put(Integer.class,Integer.TYPE); |
| m1.put(Long.class,Long.TYPE); |
| m1.put(Short.class,Short.TYPE); |
| m1.put(Void.class,Void.TYPE); |
| |
| for (Map.Entry<Class<?>, Class<?>> e : m1.entrySet()) |
| m2.put(e.getValue(),e.getKey()); |
| |
| boxToPrimitive = Collections.unmodifiableMap(m1); |
| primitiveToBox = Collections.unmodifiableMap(m2); |
| |
| } |
| } |