| /* |
| * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved. |
| * Copyright (c) 1998, 2018 Hans Harz, Andrew Rustleund, IBM Corporation. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // James Sutherland - initial impl |
| // 05/14/2010-2.1 Guy Pelletier |
| // - 253083: Add support for dynamic persistence using ORM.xml/eclipselink-orm.xml |
| // Hans Harz, Andrew Rustleund - Bug 324862 - IndexOutOfBoundsException in |
| // DatabaseSessionImpl.initializeDescriptors because @MapKey Annotation is not found. |
| // 04/21/2011-2.3 dclarke: Upgraded to support ASM 3.3.1 |
| // 08/10/2011-2.3 Lloyd Fernandes : Bug 336133 - Validation error during processing on parameterized generic OneToMany Entity relationship from MappedSuperclass |
| // 10/05/2012-2.4.1 Guy Pelletier |
| // - 373092: Exceptions using generics, embedded key and entity inheritance |
| // 19/04/2014-2.6 Lukas Jungmann |
| // - 429992: JavaSE 8/ASM 5.0.1 support (EclipseLink silently ignores Entity classes with lambda expressions) |
| // 11/05/2015-2.6 Dalia Abo Sheasha |
| // - 480787 : Wrap several privileged method calls with a doPrivileged block |
| package org.eclipse.persistence.internal.jpa.metadata.accessors.objects; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.security.AccessController; |
| import java.security.PrivilegedAction; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.persistence.internal.helper.Helper; |
| import org.eclipse.persistence.internal.jpa.metadata.MetadataDescriptor; |
| import org.eclipse.persistence.internal.jpa.metadata.MetadataLogger; |
| import org.eclipse.persistence.internal.libraries.asm.AnnotationVisitor; |
| import org.eclipse.persistence.internal.libraries.asm.EclipseLinkAnnotationVisitor; |
| import org.eclipse.persistence.internal.libraries.asm.Attribute; |
| import org.eclipse.persistence.internal.libraries.asm.ClassReader; |
| import org.eclipse.persistence.internal.libraries.asm.EclipseLinkClassVisitor; |
| import org.eclipse.persistence.internal.libraries.asm.EclipseLinkFieldVisitor; |
| import org.eclipse.persistence.internal.libraries.asm.EclipseLinkClassReader; |
| import org.eclipse.persistence.internal.libraries.asm.FieldVisitor; |
| import org.eclipse.persistence.internal.libraries.asm.EclipseLinkMethodVisitor; |
| import org.eclipse.persistence.internal.libraries.asm.MethodVisitor; |
| import org.eclipse.persistence.internal.libraries.asm.Type; |
| import org.eclipse.persistence.internal.localization.ExceptionLocalization; |
| import org.eclipse.persistence.internal.security.PrivilegedAccessHelper; |
| import org.eclipse.persistence.logging.AbstractSessionLog; |
| import org.eclipse.persistence.logging.SessionLog; |
| import org.eclipse.persistence.logging.SessionLogEntry; |
| |
| /** |
| * INTERNAL: A metadata factory that uses ASM technology and no reflection |
| * whatsoever to process the metadata model. |
| * |
| * @author James Sutherland |
| * @since EclipseLink 1.2 |
| */ |
| public class MetadataAsmFactory extends MetadataFactory { |
| /** Set of primitive type codes. */ |
| public static final String PRIMITIVES = "VJIBZCSFD"; |
| /** Set of desc token characters. */ |
| public static final String TOKENS = "()<>;"; |
| |
| /** |
| * INTERNAL: |
| */ |
| public MetadataAsmFactory(MetadataLogger logger, ClassLoader loader) { |
| super(logger, loader); |
| |
| addMetadataClass("I", new MetadataClass(this, int.class)); |
| addMetadataClass("J", new MetadataClass(this, long.class)); |
| addMetadataClass("S", new MetadataClass(this, short.class)); |
| addMetadataClass("Z", new MetadataClass(this, boolean.class)); |
| addMetadataClass("F", new MetadataClass(this, float.class)); |
| addMetadataClass("D", new MetadataClass(this, double.class)); |
| addMetadataClass("C", new MetadataClass(this, char.class)); |
| addMetadataClass("B", new MetadataClass(this, byte.class)); |
| } |
| |
| /** |
| * Build the class metadata for the class name using ASM to read the class |
| * byte codes. |
| */ |
| protected void buildClassMetadata(MetadataClass metadataClass, String className, boolean isLazy) { |
| ClassMetadataVisitor visitor = new ClassMetadataVisitor(metadataClass, isLazy); |
| InputStream stream = null; |
| try { |
| String resourceString = className.replace('.', '/') + ".class"; |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) { |
| final String f_resourceString = resourceString; |
| stream = AccessController.doPrivileged(new PrivilegedAction<InputStream>() { |
| @Override |
| public InputStream run() { |
| return m_loader.getResourceAsStream(f_resourceString); |
| } |
| }); |
| } else { |
| stream = m_loader.getResourceAsStream(resourceString); |
| } |
| |
| ClassReader reader = new ClassReader(stream); |
| Attribute[] attributes = new Attribute[0]; |
| reader.accept(visitor, attributes, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); |
| } catch (IllegalArgumentException iae) { |
| // class was probably compiled with some newer than officially |
| // supported and tested JDK |
| // in such case log a warning and try to re-read the class |
| // without class version check |
| SessionLog log = getLogger().getSession() != null |
| ? getLogger().getSession().getSessionLog() : AbstractSessionLog.getLog(); |
| if (log.shouldLog(SessionLog.WARNING, SessionLog.METADATA)) { |
| SessionLogEntry entry = new SessionLogEntry(getLogger().getSession(), SessionLog.WARNING, SessionLog.METADATA, iae); |
| entry.setMessage(ExceptionLocalization.buildMessage("unsupported_classfile_version", new Object[] { className })); |
| log.log(entry); |
| } |
| if (stream != null) { |
| try { |
| ClassReader reader = new EclipseLinkClassReader(stream); |
| Attribute[] attributes = new Attribute[0]; |
| reader.accept(visitor, attributes, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); |
| } catch (Exception e) { |
| // our fall-back failed, this is severe |
| if (log.shouldLog(SessionLog.SEVERE, SessionLog.METADATA)) { |
| SessionLogEntry entry = new SessionLogEntry(getLogger().getSession(), SessionLog.SEVERE, SessionLog.METADATA, e); |
| entry.setMessage(ExceptionLocalization.buildMessage("unsupported_classfile_version", new Object[] { className })); |
| log.log(entry); |
| } |
| addMetadataClass(getVirtualMetadataClass(className)); |
| } |
| } else { |
| addMetadataClass(getVirtualMetadataClass(className)); |
| } |
| } catch (Exception exception) { |
| SessionLog log = getLogger().getSession() != null |
| ? getLogger().getSession().getSessionLog() : AbstractSessionLog.getLog(); |
| if (log.shouldLog(SessionLog.FINEST, SessionLog.METADATA)) { |
| log.logThrowable(SessionLog.FINEST, SessionLog.METADATA, exception); |
| } |
| addMetadataClass(getVirtualMetadataClass(className)); |
| } finally { |
| try { |
| if (stream != null) { |
| stream.close(); |
| } |
| } catch (IOException ignore) { |
| // Ignore. |
| } |
| } |
| } |
| |
| /** |
| * Return the class metadata for the class name. |
| */ |
| @Override |
| public MetadataClass getMetadataClass(String className) { |
| return getMetadataClass(className, false); |
| } |
| |
| /** |
| * Return the class metadata for the class name. |
| */ |
| @Override |
| public MetadataClass getMetadataClass(String className, boolean isLazy) { |
| if (className == null) { |
| return null; |
| } |
| |
| MetadataClass metaClass = m_metadataClasses.get(className); |
| if ((metaClass == null) || (!isLazy && metaClass.isLazy())) { |
| if (metaClass != null) { |
| metaClass.setIsLazy(false); |
| } |
| buildClassMetadata(metaClass, className, isLazy); |
| metaClass = m_metadataClasses.get(className); |
| } |
| |
| return metaClass; |
| } |
| |
| /** |
| * INTERNAL: This method resolves generic types based on the ASM class |
| * metadata. Unless every other factory (e.g. APT mirror factory) respects |
| * the generic format as built from ASM this method will not work since it |
| * is very tied to it. |
| */ |
| @Override |
| public void resolveGenericTypes(MetadataClass child, List<String> genericTypes, MetadataClass parent, MetadataDescriptor descriptor) { |
| // If we have a generic parent we need to grab our generic types |
| // that may be used (and therefore need to be resolved) to map |
| // accessors correctly. |
| if (genericTypes != null) { |
| // The generic types provided map to its parents generic types. The |
| // generics also include the superclass, and interfaces. The parent |
| // generics include the type and ":" and class. |
| |
| List<String> parentGenericTypes = parent.getGenericType(); |
| if (parentGenericTypes != null) { |
| List<String> genericParentTemp = new ArrayList<>(genericTypes); |
| genericParentTemp.removeAll(child.getInterfaces()); |
| |
| int size = genericParentTemp.size(); |
| int parentIndex = 0; |
| |
| for (int index = genericTypes.indexOf(parent.getName()) + 1; index < size; index++) { |
| String actualTypeArgument = genericTypes.get(index); |
| // Ignore extra types on the end of the child, such as |
| // interface generics. |
| if (parentIndex >= parentGenericTypes.size()) { |
| break; |
| } |
| String variable = parentGenericTypes.get(parentIndex); |
| |
| // if we get as far as the superclass name in the parent generic type list, |
| // there is nothing more to process. We have processed all the generics in the type definition |
| if (variable.equals(parent.getSuperclassName())){ |
| break; |
| } |
| parentIndex = parentIndex + 3; |
| |
| // We are building bottom up and need to link up any |
| // TypeVariables with the actual class from the originating |
| // entity. |
| if (actualTypeArgument.length() == 1) { |
| index++; |
| actualTypeArgument = genericTypes.get(index); |
| descriptor.addGenericType(variable, descriptor.getGenericType(actualTypeArgument)); |
| } else { |
| descriptor.addGenericType(variable, actualTypeArgument); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Walk the class byte codes and collect the class info. |
| */ |
| public class ClassMetadataVisitor extends EclipseLinkClassVisitor { |
| |
| private boolean isLazy; |
| private boolean processedMemeber; |
| private MetadataClass classMetadata; |
| |
| ClassMetadataVisitor(MetadataClass metadataClass, boolean isLazy) { |
| super(); |
| this.isLazy = isLazy; |
| this.classMetadata = metadataClass; |
| } |
| |
| @Override |
| public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { |
| String className = toClassName(name); |
| if ((this.classMetadata == null) || !this.classMetadata.getName().equals(className)) { |
| this.classMetadata = new MetadataClass(MetadataAsmFactory.this, className, isLazy); |
| addMetadataClass(this.classMetadata); |
| } |
| this.classMetadata.setName(className); |
| this.classMetadata.setSuperclassName(toClassName(superName)); |
| this.classMetadata.setModifiers(access); |
| this.classMetadata.setGenericType(processDescription(signature, true)); |
| |
| for (String interfaceName : interfaces) { |
| this.classMetadata.addInterface(toClassName(interfaceName)); |
| } |
| } |
| |
| @Override |
| public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { |
| this.processedMemeber = true; |
| if (this.classMetadata.isLazy()) { |
| return null; |
| } |
| return new MetadataFieldVisitor(this.classMetadata, access, name, desc, signature, value); |
| } |
| |
| @Override |
| public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { |
| this.processedMemeber = true; |
| if (this.classMetadata.isLazy() || name.indexOf("init>") != -1) { |
| return null; |
| } |
| return new MetadataMethodVisitor(this.classMetadata, access, name, signature, desc, exceptions); |
| } |
| |
| @Override |
| public AnnotationVisitor visitAnnotation(String desc, boolean visible) { |
| boolean isJPA = false; |
| if (desc.startsWith("Lkotlin")) { |
| //ignore kotlin annotations |
| return null; |
| } |
| if (desc.startsWith("Ljava")) { |
| char c = desc.charAt(5); |
| //ignore annotations from 'java' namespace |
| if (c == '/') { |
| return null; |
| } |
| //ignore annotations from other then 'javax/persistence' namespace |
| if (desc.regionMatches(5, "x/", 0, 2)) { |
| if (desc.regionMatches(7, "persistence", 0, "persistence".length())) { |
| isJPA = true; |
| } else { |
| return null; |
| } |
| } |
| } |
| if (desc.startsWith("Ljakarta")) { |
| //ignore annotations from other then 'jakarta/persistence' namespace |
| if (desc.regionMatches(9, "persistence", 0, "persistence".length())) { |
| isJPA = true; |
| } else { |
| return null; |
| } |
| } |
| if (!this.processedMemeber && this.classMetadata.isLazy()) { |
| this.classMetadata.setIsLazy(false); |
| } |
| //this currently forbids us to use meta-annotations defined in EclipseLink packages |
| return new MetadataAnnotationVisitor(this.classMetadata, desc, isJPA || desc.startsWith("Lorg/eclipse/persistence")); |
| } |
| |
| } |
| |
| /** |
| * {@link AnnotationVisitor} used to process class, field , and method |
| * annotations populating a {@link MetadataAnnotation} and its nested state. |
| * |
| * @see MetadataAnnotationArrayVisitor for population of array attributes |
| */ |
| class MetadataAnnotationVisitor extends EclipseLinkAnnotationVisitor { |
| |
| /** |
| * Element the annotation is being applied to. If this is null the |
| * {@link MetadataAnnotation} being constructed is a nested annotation |
| * and is already referenced from its parent. |
| */ |
| private MetadataAnnotatedElement element; |
| |
| /** |
| * {@link MetadataAnnotation} being populated |
| */ |
| private MetadataAnnotation annotation; |
| |
| MetadataAnnotationVisitor(MetadataAnnotatedElement element, String name) { |
| this(element, name, true); |
| } |
| |
| MetadataAnnotationVisitor(MetadataAnnotatedElement element, String name, boolean isRegular) { |
| super(); |
| this.element = element; |
| this.annotation = new MetadataAnnotation(); |
| this.annotation.setName(processDescription(name, false).get(0)); |
| this.annotation.setIsMeta(!isRegular); |
| } |
| |
| public MetadataAnnotationVisitor(MetadataAnnotation annotation) { |
| super(); |
| this.annotation = annotation; |
| } |
| |
| @Override |
| public void visit(String name, Object value) { |
| this.annotation.addAttribute(name, annotationValue(null, value)); |
| } |
| |
| @Override |
| public void visitEnum(String name, String desc, String value) { |
| this.annotation.addAttribute(name, annotationValue(desc, value)); |
| } |
| |
| @Override |
| public AnnotationVisitor visitAnnotation(String name, String desc) { |
| MetadataAnnotation mda = new MetadataAnnotation(); |
| mda.setName(processDescription(desc, false).get(0)); |
| this.annotation.addAttribute(name, mda); |
| return new MetadataAnnotationVisitor(mda); |
| } |
| |
| @Override |
| public AnnotationVisitor visitArray(String name) { |
| return new MetadataAnnotationArrayVisitor(this.annotation, name); |
| } |
| |
| @Override |
| public void visitEnd() { |
| if (this.element != null) { |
| if (this.annotation.isMeta()) { |
| this.element.addMetaAnnotation(this.annotation); |
| } else { |
| this.element.addAnnotation(this.annotation); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Specialized visitor to handle the population of arrays of annotation |
| * values. |
| */ |
| class MetadataAnnotationArrayVisitor extends EclipseLinkAnnotationVisitor { |
| |
| private MetadataAnnotation annotation; |
| |
| private String attributeName; |
| |
| private List<Object> values; |
| |
| public MetadataAnnotationArrayVisitor(MetadataAnnotation annotation, String name) { |
| super(); |
| this.annotation = annotation; |
| this.attributeName = name; |
| this.values = new ArrayList<Object>(); |
| } |
| |
| @Override |
| public void visit(String name, Object value) { |
| this.values.add(annotationValue(null, value)); |
| } |
| |
| @Override |
| public void visitEnum(String name, String desc, String value) { |
| this.values.add(annotationValue(desc, value)); |
| } |
| |
| @Override |
| public AnnotationVisitor visitAnnotation(String name, String desc) { |
| MetadataAnnotation mda = new MetadataAnnotation(); |
| mda.setName(processDescription(desc, false).get(0)); |
| this.values.add(mda); |
| return new MetadataAnnotationVisitor(mda); |
| } |
| |
| @Override |
| public void visitEnd() { |
| this.annotation.addAttribute(this.attributeName, this.values.toArray()); |
| } |
| } |
| |
| /** |
| * Factory for the creation of {@link MetadataField} handling basic type, |
| * generics, and annotations. |
| */ |
| class MetadataFieldVisitor extends EclipseLinkFieldVisitor { |
| |
| private MetadataField field; |
| |
| public MetadataFieldVisitor(MetadataClass classMetadata, int access, String name, String desc, String signature, Object value) { |
| super(); |
| this.field = new MetadataField(classMetadata); |
| this.field.setModifiers(access); |
| this.field.setName(name); |
| this.field.setAttributeName(name); |
| this.field.setGenericType(processDescription(signature, true)); |
| this.field.setType(processDescription(desc, false).get(0)); |
| } |
| |
| @Override |
| public AnnotationVisitor visitAnnotation(String desc, boolean visible) { |
| if (desc.startsWith("Ljavax/persistence") || desc.startsWith("Ljakarta/persistence") |
| || desc.startsWith("Lorg/eclipse/persistence")) { |
| return new MetadataAnnotationVisitor(this.field, desc); |
| } |
| return null; |
| } |
| |
| @Override |
| public void visitEnd() { |
| this.field.getDeclaringClass().addField(this.field); |
| } |
| } |
| |
| /** |
| * Factory for the creation of {@link MetadataMethod} handling basic type, |
| * generics, and annotations. |
| */ |
| // Note: Subclassed EmptyListener to minimize signature requirements for |
| // ignored MethodVisitor API |
| class MetadataMethodVisitor extends EclipseLinkMethodVisitor { |
| |
| private MetadataMethod method; |
| |
| public MetadataMethodVisitor(MetadataClass classMetadata, int access, String name, String desc, String signature, String[] exceptions) { |
| super(); |
| this.method = new MetadataMethod(MetadataAsmFactory.this, classMetadata); |
| |
| this.method.setName(name); |
| this.method.setAttributeName(Helper.getAttributeNameFromMethodName(name)); |
| this.method.setModifiers(access); |
| |
| this.method.setGenericType(processDescription(desc, true)); |
| |
| List<String> argumentNames = processDescription(signature, false); |
| if (argumentNames != null && !argumentNames.isEmpty()) { |
| this.method.setReturnType(argumentNames.get(argumentNames.size() - 1)); |
| argumentNames.remove(argumentNames.size() - 1); |
| this.method.setParameters(argumentNames); |
| } |
| } |
| |
| @Override |
| public AnnotationVisitor visitAnnotation(String desc, boolean visible) { |
| if (desc.startsWith("Ljavax/persistence") || desc.startsWith("Ljakarta/persistence") |
| || desc.startsWith("Lorg/eclipse/persistence")) { |
| return new MetadataAnnotationVisitor(this.method, desc); |
| } |
| return null; |
| } |
| |
| /** |
| * At the end of visiting this method add it to the |
| * {@link MetadataClass} and handle duplicate method names by chaining |
| * them. |
| */ |
| @Override |
| public void visitEnd() { |
| MetadataClass classMetadata = this.method.getMetadataClass(); |
| |
| MetadataMethod existing = classMetadata.getMethods().get(this.method.getName()); |
| if (existing == null) { |
| classMetadata.getMethods().put(this.method.getName(), this.method); |
| } else { |
| // Handle methods with the same name. |
| while (existing.getNext() != null) { |
| existing = existing.getNext(); |
| } |
| existing.setNext(this.method); |
| } |
| } |
| |
| } |
| |
| /** |
| * Get MetadataClass for a class which can not be found |
| * @param className class which has not been found |
| * @return MetadataClass |
| */ |
| private MetadataClass getVirtualMetadataClass(String className) { |
| // Some basic types can't be found, so can just be registered |
| // (i.e. arrays). Also, VIRTUAL classes may also not exist, |
| // therefore, tag the MetadataClass as loadable false. This will be |
| // used to determine if a class will be dynamically created or not. |
| MetadataClass metadataClass = new MetadataClass(this, className, false); |
| // If the class is a JDK class, then maybe there is a class loader |
| // issues, |
| // since it is a JDK class, just use reflection. |
| if ((className.length() > 5) && className.substring(0, 5).equals("java.")) { |
| try { |
| Class<?> reflectClass = Class.forName(className); |
| if (reflectClass.getSuperclass() != null) { |
| metadataClass.setSuperclassName(reflectClass.getSuperclass().getName()); |
| } |
| for (Class reflectInterface : reflectClass.getInterfaces()) { |
| metadataClass.addInterface(reflectInterface.getName()); |
| } |
| } catch (Exception failed) { |
| SessionLog log = getLogger().getSession() != null |
| ? getLogger().getSession().getSessionLog() : AbstractSessionLog.getLog(); |
| if (log.shouldLog(SessionLog.FINE, SessionLog.METADATA)) { |
| log.logThrowable(SessionLog.FINE, SessionLog.METADATA, failed); |
| } |
| metadataClass.setIsAccessible(false); |
| } |
| } else { |
| metadataClass.setIsAccessible(false); |
| } |
| return metadataClass; |
| } |
| |
| /** |
| * Process the byte-code argument description and return the array of Java |
| * class names. i.e. |
| * "(Lorg/foo/Bar;Z)Ljava/lang/Boolean;"={@literal >}[org.foo.Bar,boolean |
| * ,java.lang.Boolean] |
| */ |
| private static List<String> processDescription(String desc, boolean isGeneric) { |
| if (desc == null) { |
| return null; |
| } |
| List<String> arguments = new ArrayList<String>(); |
| int index = 0; |
| int length = desc.length(); |
| boolean isGenericTyped=false; |
| // PERF: Use char array to make char index faster (note this is a heavily optimized method, be very careful on changes) |
| char[] chars = desc.toCharArray(); |
| while (index < length) { |
| char next = chars[index]; |
| if (('(' != next) && (')' != next) && ('<' != next) && ('>' != next) && (';' != next)) { |
| if (next == 'L') { |
| index++; |
| int start = index; |
| next = chars[index]; |
| while (('(' != next) && (')' != next) && ('<' != next) && ('>' != next) && (';' != next)) { |
| index++; |
| next = chars[index]; |
| } |
| arguments.add(toClassName(desc.substring(start, index))); |
| if(isGenericTyped) { |
| isGenericTyped=false; |
| if(next == '<') { |
| int cnt = 1; |
| while((cnt > 0) && (++index<desc.length())) { |
| switch (desc.charAt(index)) { |
| case '<': cnt ++; break; |
| case '>': cnt --; break; |
| } |
| } |
| } |
| } |
| } else if (!isGeneric && (PRIMITIVES.indexOf(next) != -1)) { |
| // Primitives. |
| arguments.add(getPrimitiveName(next)); |
| } else if (next == '[') { |
| // Arrays. |
| int start = index; |
| index++; |
| next = chars[index]; |
| // Nested arrays. |
| while (next == '[') { |
| index++; |
| next = chars[index]; |
| } |
| if (PRIMITIVES.indexOf(next) == -1) { |
| while (next != ';') { |
| index++; |
| next = chars[index]; |
| } |
| arguments.add(toClassName(desc.substring(start, index + 1))); |
| } else { |
| arguments.add(desc.substring(start, index + 1)); |
| } |
| } else { |
| // Is a generic type variable. |
| int start = index; |
| int end = start; |
| |
| char myNext = next; |
| |
| while (':' != myNext && '(' != myNext && ')' != myNext && '<' != myNext && '>' != myNext && ';' != myNext && end < length - 1) { |
| end++; |
| myNext = chars[end]; |
| } |
| |
| if (myNext == ':') { |
| arguments.add(desc.substring(start, end)); |
| isGenericTyped=true; |
| index = end; |
| arguments.add(":"); |
| if(desc.charAt(index+1)==':') { |
| index ++; |
| } |
| } else if (myNext == ';' && next == 'T') { |
| arguments.add(String.valueOf(next)); |
| arguments.add(desc.substring(start + 1, end)); |
| index = end - 1; |
| } else { |
| arguments.add(String.valueOf(next)); |
| } |
| } |
| } |
| index++; |
| } |
| return arguments; |
| |
| } |
| |
| /** |
| * Return the Java type name for the primitive code. |
| */ |
| private static String getPrimitiveName(char primitive) { |
| if (primitive == 'V') { |
| return "void"; |
| } else if (primitive == 'I') { |
| return "int"; |
| } else if (primitive == 'Z') { |
| return "boolean"; |
| } else if (primitive == 'J') { |
| return "long"; |
| } else if (primitive == 'F') { |
| return "float"; |
| } else if (primitive == 'D') { |
| return "double"; |
| } else if (primitive == 'B') { |
| return "byte"; |
| } else if (primitive == 'C') { |
| return "char"; |
| } else if (primitive == 'S') { |
| return "short"; |
| } else { |
| return String.valueOf(primitive); |
| } |
| } |
| |
| private static String toClassName(String classDescription) { |
| if (classDescription == null) { |
| return "void"; |
| } |
| return classDescription.replace('/', '.'); |
| } |
| |
| /** |
| * Convert the annotation value into the value used in the meta model |
| */ |
| private static Object annotationValue(String description, Object value) { |
| if (value instanceof Type) { |
| return ((Type) value).getClassName(); |
| } |
| return value; |
| } |
| } |