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
017  private final EnhanceContext enhanceContext;
018
019  public ClassMetaReader(EnhanceContext enhanceContext, ClassMetaCache metaCache) {
020    this.enhanceContext = enhanceContext;
021    this.metaCache = metaCache;
022  }
023
024  public ClassMeta get(boolean readMethodAnnotations, String name, ClassLoader classLoader) throws ClassNotFoundException {
025    return getWithCache(readMethodAnnotations, name, classLoader);
026  }
027
028  private ClassMeta getWithCache(boolean readMethodAnnotations, String name, ClassLoader classLoader) throws ClassNotFoundException {
029
030    synchronized (metaCache) {
031      ClassMeta meta = metaCache.get(name);
032      if (meta == null) {
033        meta = readFromResource(readMethodAnnotations, name, classLoader);
034        if (meta != null) {
035          if (meta.isCheckSuperClassForEntity()) {
036            ClassMeta superMeta = getWithCache(readMethodAnnotations, meta.getSuperClassName(), classLoader);
037            if (superMeta != null && superMeta.isEntity()) {
038              meta.setSuperMeta(superMeta);
039            }
040          }
041          metaCache.put(name, meta);
042        }
043      }
044      return meta;
045    }
046  }
047
048  private ClassMeta readFromResource(boolean readMethodAnnotations, String className, ClassLoader classLoader)
049      throws ClassNotFoundException {
050
051    byte[] classBytes = enhanceContext.getClassBytes(className, classLoader);
052    if (classBytes == null){
053      if (enhanceContext.isLog(3)) {
054        enhanceContext.log(null, "Could not read meta data for class ["+className+"].");
055      }
056      return null;
057    } else {
058      if (enhanceContext.isLog(5)) {
059        enhanceContext.log(className, "read ClassMeta");
060      }
061    }
062    try {
063      ClassReader cr = new ClassReader(classBytes);
064      ClassMetaReaderVisitor ca = new ClassMetaReaderVisitor(readMethodAnnotations, enhanceContext);
065      cr.accept(ca, ClassReader.SKIP_FRAMES + ClassReader.SKIP_DEBUG);
066      return ca.getClassMeta();
067
068    } catch (IllegalArgumentException e) {
069      // try fallback for IDE partial compile
070      ClassMeta classMeta = metaCache.getFallback(className);
071      if (classMeta != null) {
072        return classMeta;
073      }
074      throw new ClassNotFoundException("Error reading " + className + " bytes len:" + classBytes.length + " no fallback in " + metaCache.fallbackKeys());
075    }
076  }
077
078}