001package io.ebean.enhance.querybean;
002
003import java.util.HashSet;
004import java.util.Set;
005
006/**
007 * Detects if a class is a query bean.
008 * <p>
009 * Used by enhancement to detect when GETFIELD access on query beans should be replaced by
010 * appropriate method calls.
011 * </p>
012 */
013public class DetectQueryBean {
014
015  private final Set<String> entityPackages = new HashSet<>();
016
017  public DetectQueryBean() {
018  }
019
020  public void addAll(Set<String> rawEntityPackages) {
021    for (String rawEntityPackage : rawEntityPackages) {
022      entityPackages.add(convert(rawEntityPackage));
023    }
024  }
025
026  /**
027   * Convert package to slash notation taking into account trailing wildcard.
028   */
029  private static String convert(String pkg) {
030
031    pkg = pkg.trim();
032    if (pkg.endsWith("*")) {
033      pkg = pkg.substring(0, pkg.length() - 1);
034    }
035    if (pkg.endsWith(".query")) {
036      // always work with entity bean packages so trim
037      pkg = pkg.substring(0, pkg.length() - 6);
038    }
039    pkg = pkg.replace('.', '/');
040    return pkg.endsWith("/") ? pkg : pkg + "/";
041  }
042
043
044  @Override
045  public String toString() {
046    return entityPackages.toString();
047  }
048
049  /**
050   * Return true if there are no known packages.
051   */
052  public boolean isEmpty() {
053    return entityPackages.isEmpty();
054  }
055
056  /**
057   * Return true if this class is a query bean using naming conventions for query beans.
058   */
059  public boolean isQueryBean(String owner) {
060
061    int subPackagePos = owner.lastIndexOf("/query/");
062    if (subPackagePos > -1) {
063      String suffix = owner.substring(subPackagePos);
064      if (isQueryBeanSuffix(suffix)) {
065        String domainPackage = owner.substring(0, subPackagePos + 1);
066        return isQueryBeanPackage(domainPackage);
067      }
068    }
069    return false;
070  }
071
072  /**
073   * Check that the class is in an expected package (sub package of a package containing entity beans).
074   */
075  private boolean isQueryBeanPackage(String domainPackage) {
076    for (String aPackage : entityPackages) {
077      if (domainPackage.startsWith(aPackage)) {
078        return true;
079      }
080    }
081    return false;
082  }
083
084  /**
085   * Check that the class follows query bean naming convention.
086   */
087  private boolean isQueryBeanSuffix(String suffix) {
088    return (suffix.startsWith("/query/Q") || suffix.startsWith("/query/assoc/Q"));
089  }
090}