Package com.avaje.ebeaninternal.server.querydefn

Source Code of com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties

package com.avaje.ebeaninternal.server.querydefn;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import com.avaje.ebean.ExpressionFactory;
import com.avaje.ebean.FetchConfig;
import com.avaje.ebean.OrderBy;
import com.avaje.ebean.Query;
import com.avaje.ebean.event.BeanQueryRequest;
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionFactory;
import com.avaje.ebeaninternal.api.SpiExpressionList;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.expression.FilterExprPath;
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
import com.avaje.ebeaninternal.server.query.SplitName;
import com.avaje.ebeaninternal.util.FilterExpressionList;

/**
* Represents the Properties of an Object Relational query.
*/
public class OrmQueryProperties implements Serializable {

  private static final long serialVersionUID = -8785582703966455658L;

  private String parentPath;
  private String path;

  private String properties;

  private String trimmedProperties;

  /**
   * NB: -1 means no +query, 0 means use the default batch size.
   */
  private int queryFetchBatch = -1;
  private boolean queryFetchAll;

  /**
   * NB: -1 means no +lazy, 0 means use the default batch size.
   */
  private int lazyFetchBatch = -1;

  private FetchConfig fetchConfig;

  private boolean cache;

  private boolean readOnly;

  /**
   * Note this SHOULD be a LinkedHashSet to preserve order of the properties. This is to make using
   * SqlSelect easier with predictable property/column ordering.
   */
  private Set<String> included;

  /**
   * Included bean joins.
   */
  private Set<String> includedBeanJoin;

  /**
   * Add these properties to the select so that the foreign key columns are included in the query.
   */
  private Set<String> secondaryQueryJoins;

  private List<OrmQueryProperties> secondaryChildren;

  /**
   * OrderBy properties that where on the main query but moved here as they relate to this (query
   * join).
   */
  @SuppressWarnings("rawtypes")
  private OrderBy orderBy;

  /**
   * A filter that can be applied to the fetch of this path in the object graph.
   */
  @SuppressWarnings("rawtypes")
  private SpiExpressionList filterMany;

  /**
   * Construct with a given path (null == root path).
   */
  public OrmQueryProperties(String path) {
    this.path = path;
    this.parentPath = SplitName.parent(path);
  }

  public OrmQueryProperties() {
    this(null);
  }

  /**
   * Used by query language parser.
   */
  public OrmQueryProperties(String path, String properties) {
    this(path);
    setProperties(properties);
  }

  /**
   * Move a OrderBy.Property from the main query to this query join.
   */
  @SuppressWarnings("rawtypes")
  public void addSecJoinOrderProperty(OrderBy.Property orderProp) {
    if (orderBy == null) {
      orderBy = new OrderBy();
    }
    orderBy.add(orderProp);
  }

  /**
   * Set the Fetch configuration options for this path.
   */
  public void setFetchConfig(FetchConfig fetchConfig) {
    if (fetchConfig != null) {
      this.fetchConfig = fetchConfig;
      lazyFetchBatch = fetchConfig.getLazyBatchSize();
      queryFetchBatch = fetchConfig.getQueryBatchSize();
      queryFetchAll = fetchConfig.isQueryAll();
    }
  }

  public FetchConfig getFetchConfig() {
    return fetchConfig;
  }

  /**
   * Set the comma delimited properties to fetch for this path.
   * <p>
   * This can include the +query and +lazy type hints.
   * </p>
   */
  public void setProperties(String properties) {
    this.properties = properties;
    this.trimmedProperties = properties;
    parseProperties();

    if (!isAllProperties()) {
      Set<String> parsed = parseIncluded(trimmedProperties);
      if (parsed.contains("*")) {
        this.included = null;
      } else {
        this.included = parsed;
      }
    } else {
      this.included = null;
    }
  }

  private boolean isAllProperties() {
    return (trimmedProperties == null) || (trimmedProperties.length() == 0) || "*".equals(trimmedProperties);
  }

  /**
   * Return the expressions used to filter on this path. This should be a many path to use this
   * method.
   */
  @SuppressWarnings({ "rawtypes", "unchecked" })
  public <T> SpiExpressionList<T> filterMany(Query<T> rootQuery) {
    if (filterMany == null) {
      FilterExprPath exprPath = new FilterExprPath(path);
      SpiExpressionFactory queryEf = (SpiExpressionFactory) rootQuery.getExpressionFactory();
      ExpressionFactory filterEf = queryEf.createExpressionFactory();// exprPath);
      filterMany = new FilterExpressionList(exprPath, filterEf, rootQuery);
      // by default we need to make this a 'query join' now
      queryFetchAll = true;
      queryFetchBatch = 100;
      lazyFetchBatch = 100;
    }
    return filterMany;
  }

  /**
   * Return the filterMany expression list (can be null).
   */
  public SpiExpressionList<?> getFilterManyTrimPath(int trimPath) {
    if (filterMany == null) {
      return null;
    }
    return filterMany.trimPath(trimPath);
  }

  /**
   * Return the filterMany expression list (can be null).
   */
  public SpiExpressionList<?> getFilterMany() {
    return filterMany;
  }

  /**
   * Set the filterMany expression list.
   */
  public void setFilterMany(SpiExpressionList<?> filterMany) {
    this.filterMany = filterMany;
  }

  /**
   * Set the properties from deployment default FetchTypes.
   */
  public void setDefaultProperties(String properties, Set<String> included) {
    this.properties = properties;
    this.trimmedProperties = properties;
    this.included = included;
  }

  /**
   * Set the properties from a matching autofetch tuned properties.
   */
  public void setTunedProperties(OrmQueryProperties tunedProperties) {
    this.properties = tunedProperties.properties;
    this.trimmedProperties = tunedProperties.trimmedProperties;
    this.included = tunedProperties.included;
  }

  /**
   * Define the select and joins for this query.
   */
  @SuppressWarnings("unchecked")
  public void configureBeanQuery(SpiQuery<?> query) {

    if (trimmedProperties != null && trimmedProperties.length() > 0) {
      query.select(trimmedProperties);
    }

    if (filterMany != null) {
      SpiExpressionList<?> trimPath = filterMany.trimPath(path.length() + 1);
      List<SpiExpression> underlyingList = trimPath.getUnderlyingList();
      for (SpiExpression spiExpression : underlyingList) {
        query.where().add(spiExpression);
      }
    }

    if (secondaryChildren != null) {
      int trimPath = path.length() + 1;
      for (int i = 0; i < secondaryChildren.size(); i++) {
        OrmQueryProperties p = secondaryChildren.get(i);
        String path = p.getPath();
        path = path.substring(trimPath);
        query.fetch(path, p.getProperties(), p.getFetchConfig());
        query.setFilterMany(path, p.getFilterManyTrimPath(trimPath));
      }
    }

    if (orderBy != null) {
      query.setOrder(orderBy.copyWithTrim(path));
    }
  }

  /**
   * Creates a copy of the OrmQueryProperties.
   */
  public OrmQueryProperties copy() {
    OrmQueryProperties copy = new OrmQueryProperties();
    copy.parentPath = parentPath;
    copy.path = path;
    copy.properties = properties;
    copy.cache = cache;
    copy.readOnly = readOnly;
    copy.queryFetchAll = queryFetchAll;
    copy.queryFetchBatch = queryFetchBatch;
    copy.lazyFetchBatch = lazyFetchBatch;
    copy.filterMany = filterMany;
    if (included != null) {
      copy.included = new HashSet<String>(included);
    }
    if (includedBeanJoin != null) {
      copy.includedBeanJoin = new HashSet<String>(includedBeanJoin);
    }
    return copy;
  }

  public boolean hasSelectClause() {
    if ("*".equals(trimmedProperties)) {
      // explicitly selected all properties
      return true;
    }
    // explicitly selected some properties
    return included != null;
  }

  public String toString() {
    String s = "";
    if (path != null) {
      s += path + " ";
    }
    if (properties != null) {
      s += "(" + properties + ") ";
    } else if (included != null) {
      s += "(" + included + ") ";
    }
    return s;
  }

  public boolean isChild(OrmQueryProperties possibleChild) {
    return possibleChild.getPath().startsWith(path + ".");
  }

  /**
   * For secondary queries add a child element.
   */
  public void add(OrmQueryProperties child) {
    if (secondaryChildren == null) {
      secondaryChildren = new ArrayList<OrmQueryProperties>();
    }
    secondaryChildren.add(child);
  }

  public int autofetchPlanHash() {

    int hc = (path != null ? path.hashCode() : 1);
    if (properties != null) {
      hc = hc * 31 + properties.hashCode();
    } else {
      hc = hc * 31 + (included != null ? included.hashCode() : 1);
    }

    return hc;
  }

  /**
   * Calculate the query plan hash.
   */
  @SuppressWarnings("unchecked")
  public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {

    builder.add(path);
    if (properties != null) {
      builder.add(properties);
    } else {
      builder.add(included);
    }
    builder.add(filterMany != null);
    if (filterMany != null) {
      filterMany.queryPlanHash(request, builder);
    }
  }

  public String getProperties() {
    return properties;
  }

  /**
   * Return true if this has properties.
   */
  public boolean hasProperties() {
    return properties != null || included != null;
  }

  /**
   * Return true if this property is included as a bean join.
   * <p>
   * If a property is included as a bean join then it should not be included as a reference/proxy to
   * avoid duplication.
   * </p>
   */
  public boolean isIncludedBeanJoin(String propertyName) {
    if (includedBeanJoin == null) {
      return false;
    } else {
      return includedBeanJoin.contains(propertyName);
    }
  }

  /**
   * Add a bean join property.
   */
  public void includeBeanJoin(String propertyName) {
    if (includedBeanJoin == null) {
      includedBeanJoin = new HashSet<String>();
    }
    includedBeanJoin.add(propertyName);
  }

  public boolean allProperties() {
    return included == null;
  }

  /**
   * This excludes the bean joined properties.
   * <p>
   * This is because bean joins will have there own node in the SqlTree.
   * </p>
   */
  public Set<String> getSelectProperties() {

    if (secondaryQueryJoins == null) {
      return included;
    }

    LinkedHashSet<String> temp = new LinkedHashSet<String>(secondaryQueryJoins.size() + included.size());
    temp.addAll(included);
    temp.addAll(secondaryQueryJoins);
    return temp;
  }

  public void addSecondaryQueryJoin(String property) {
    if (secondaryQueryJoins == null) {
      secondaryQueryJoins = new HashSet<String>(4);
    }
    secondaryQueryJoins.add(property);
  }

  /**
   * Return all the properties including the bean joins. This is the set that will be used by
   * EntityBeanIntercept to determine if a property needs to be lazy loaded.
   */
  public Set<String> getAllIncludedProperties() {

    if (included == null) {
      return null;
    }

    if (includedBeanJoin == null && secondaryQueryJoins == null) {
      return new LinkedHashSet<String>(included);
    }

    LinkedHashSet<String> s = new LinkedHashSet<String>(2 * (included.size() + 5));
    if (included != null) {
      s.addAll(included);
    }
    if (includedBeanJoin != null) {
      s.addAll(includedBeanJoin);
    }
    if (secondaryQueryJoins != null) {
      s.addAll(secondaryQueryJoins);
    }
    return s;
  }

  public boolean isIncluded(String propName) {

    if (includedBeanJoin != null && includedBeanJoin.contains(propName)) {
      return false;
    }
    if (included == null) {
      // all properties included
      return true;
    }
    return included.contains(propName);
  }

  /**
   * Used to convert this join to a query join.
   *
   * @param queryJoinBatch
   *          where -1 means not a query join and 0 means use the default batch size.
   */
  public OrmQueryProperties setQueryFetchBatch(int queryFetchBatch) {
    this.queryFetchBatch = queryFetchBatch;
    return this;
  }

  public OrmQueryProperties setQueryFetchAll(boolean queryFetchAll) {
    this.queryFetchAll = queryFetchAll;
    return this;
  }

  public OrmQueryProperties setQueryFetch(int batch, boolean queryFetchAll) {
    this.queryFetchBatch = batch;
    this.queryFetchAll = queryFetchAll;
    return this;
  }

  /**
   * Set the lazy loading batch size.
   */
  public OrmQueryProperties setLazyFetchBatch(int lazyFetchBatch) {
    this.lazyFetchBatch = lazyFetchBatch;
    return this;
  }

  public boolean isFetchJoin() {
    return !isQueryFetch() && !isLazyFetch();
  }

  public boolean isQueryFetch() {
    return queryFetchBatch > -1;
  }

  public int getQueryFetchBatch() {
    return queryFetchBatch;
  }

  public boolean isQueryFetchAll() {
    return queryFetchAll;
  }

  public boolean isLazyFetch() {
    return lazyFetchBatch > -1;
  }

  public int getLazyFetchBatch() {
    return lazyFetchBatch;
  }

  public boolean isReadOnly() {
    return readOnly;
  }

  public boolean isCache() {
    return cache;
  }

  public String getParentPath() {
    return parentPath;
  }

  public String getPath() {
    return path;
  }

  private void parseProperties() {
    if (trimmedProperties == null) {
      return;
    }
    int pos = trimmedProperties.indexOf("+readonly");
    if (pos > -1) {
      trimmedProperties = StringHelper.replaceString(trimmedProperties, "+readonly", "");
      this.readOnly = true;
    }
    pos = trimmedProperties.indexOf("+cache");
    if (pos > -1) {
      trimmedProperties = StringHelper.replaceString(trimmedProperties, "+cache", "");
      this.cache = true;
    }
    pos = trimmedProperties.indexOf("+query");
    if (pos > -1) {
      queryFetchBatch = parseBatchHint(pos, "+query");
    }
    pos = trimmedProperties.indexOf("+lazy");
    if (pos > -1) {
      lazyFetchBatch = parseBatchHint(pos, "+lazy");
    }

    trimmedProperties = trimmedProperties.trim();
    while (trimmedProperties.startsWith(",")) {
      trimmedProperties = trimmedProperties.substring(1).trim();
    }
  }

  private int parseBatchHint(int pos, String option) {

    int startPos = pos + option.length();

    int endPos = findEndPos(startPos, trimmedProperties);
    if (endPos == -1) {
      trimmedProperties = StringHelper.replaceString(trimmedProperties, option, "");
      return 0;

    } else {

      String batchParam = trimmedProperties.substring(startPos + 1, endPos);

      if (endPos + 1 >= trimmedProperties.length()) {
        trimmedProperties = trimmedProperties.substring(0, pos);
      } else {
        trimmedProperties = trimmedProperties.substring(0, pos) + trimmedProperties.substring(endPos + 1);
      }
      return Integer.parseInt(batchParam);
    }
  }

  private int findEndPos(int pos, String props) {

    if (pos < props.length()) {
      if (props.charAt(pos) == '(') {
        int endPara = props.indexOf(')', pos + 1);
        if (endPara == -1) {
          String m = "Error could not find ')' in " + props + " after position " + pos;
          throw new RuntimeException(m);
        }
        return endPara;
      }
    }
    return -1;
  }

  /**
   * Parse the include separating by comma or semicolon.
   */
  private static Set<String> parseIncluded(String rawList) {

    String[] res = rawList.split(",");

    LinkedHashSet<String> set = new LinkedHashSet<String>(res.length + 3);

    String temp = null;
    for (int i = 0; i < res.length; i++) {
      temp = res[i].trim();
      if (temp.length() > 0) {
        set.add(temp);
      }
    }

    if (set.isEmpty()) {
      return null;
    }

    return Collections.unmodifiableSet(set);
  }
}
TOP

Related Classes of com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.