001package io.ebean.enhance.common;
002
003import io.ebean.enhance.asm.ClassReader;
004
005/**
006 * Reads class information as an alternative to using a ClassLoader.
007 * <p>
008 * Used because if annotation classes are not in the classpath they are silently
009 * dropped from the class information. We are especially interested to know if
010 * super classes are entities during enhancement.
011 * </p>
012 */
013public class ClassMetaReader {
014
015  private final ClassMetaCache metaCache;
016  private final EnhanceContext enhanceContext;
017
018  public ClassMetaReader(EnhanceContext enhanceContext, ClassMetaCache metaCache) {
019    this.enhanceContext = enhanceContext;
020    this.metaCache = metaCache;
021  }
022
023  public ClassMeta get(boolean readMethodAnnotations, String name, ClassLoader classLoader) throws ClassNotFoundException {
024    return getWithCache(readMethodAnnotations, name, classLoader);
025  }
026
027  private ClassMeta getWithCache(boolean readMethodAnnotations, String name, ClassLoader classLoader) throws ClassNotFoundException {
028    synchronized (metaCache) {
029      ClassMeta meta = metaCache.get(name);
030      if (meta == null) {
031        meta = readFromResource(readMethodAnnotations, name, classLoader);
032        if (meta != null) {
033          if (meta.isCheckSuperClassForEntity()) {
034            ClassMeta superMeta = getWithCache(readMethodAnnotations, meta.superClassName(), classLoader);
035            if (superMeta != null && superMeta.isEntity()) {
036              meta.setSuperMeta(superMeta);
037            }
038          }
039          metaCache.put(name, meta);
040        }
041      }
042      return meta;
043    }
044  }
045
046  private ClassMeta readFromResource(boolean readMethodAnnotations, String className, ClassLoader classLoader) throws ClassNotFoundException {
047    byte[] classBytes = enhanceContext.classBytes(className, classLoader);
048    if (classBytes == null){
049      if (enhanceContext.isLog(3)) {
050        enhanceContext.log(null, "Could not read meta data for class ["+className+"].");
051      }
052      return null;
053    } else {
054      if (enhanceContext.isLog(5)) {
055        enhanceContext.log(className, "read ClassMeta");
056      }
057    }
058    try {
059      ClassReader cr = new ClassReader(classBytes);
060      ClassMetaReaderVisitor ca = new ClassMetaReaderVisitor(readMethodAnnotations, enhanceContext);
061      cr.accept(ca, ClassReader.SKIP_FRAMES + ClassReader.SKIP_DEBUG);
062      return ca.getClassMeta();
063
064    } catch (IllegalArgumentException e) {
065      // try fallback for IDE partial compile
066      ClassMeta classMeta = metaCache.getFallback(className);
067      if (classMeta != null) {
068        return classMeta;
069      }
070      throw new ClassNotFoundException("Error reading " + className + " bytes len:" + classBytes.length + " no fallback in " + metaCache.fallbackKeys());
071    }
072  }
073
074}