Package es.twentymobile.mule.modules.jpa.dsl

Source Code of es.twentymobile.mule.modules.jpa.dsl.CustomJPQLSerializer

/**
*
*/
package es.twentymobile.mule.modules.jpa.dsl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import javax.annotation.Nullable;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.PersistenceUnitUtil;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.Metamodel;
import javax.persistence.metamodel.SingularAttribute;

import com.mysema.query.JoinExpression;
import com.mysema.query.JoinType;
import com.mysema.query.QueryMetadata;
import com.mysema.query.jpa.JPQLQueryMixin;
import com.mysema.query.jpa.JPQLTemplates;
import com.mysema.query.support.SerializerBase;
import com.mysema.query.types.Constant;
import com.mysema.query.types.ConstantImpl;
import com.mysema.query.types.EntityPath;
import com.mysema.query.types.Expression;
import com.mysema.query.types.ExpressionUtils;
import com.mysema.query.types.FactoryExpression;
import com.mysema.query.types.Operation;
import com.mysema.query.types.Operator;
import com.mysema.query.types.Ops;
import com.mysema.query.types.OrderSpecifier;
import com.mysema.query.types.ParamExpression;
import com.mysema.query.types.Path;
import com.mysema.query.types.PathImpl;
import com.mysema.query.types.PathType;
import com.mysema.query.types.Predicate;
import com.mysema.query.types.SubQueryExpression;
import com.mysema.util.MathUtils;

/**
* @author alvarezjm
*
*/
public class CustomJPQLSerializer extends SerializerBase<CustomJPQLSerializer> {

  private static final Set<Operator<?>> NUMERIC = new HashSet<Operator<?>>(Arrays.<Operator<?>> asList(Ops.ADD, Ops.SUB, Ops.MULT, Ops.DIV, Ops.LT, Ops.LOE,
      Ops.GT, Ops.GOE, Ops.BETWEEN));

  private static final String COMMA = ", ";

  private static final String DELETE = "delete from ";

  private static final String FROM = "from ";

  private static final String GROUP_BY = "\ngroup by ";

  private static final String HAVING = "\nhaving ";

  private static final String ORDER_BY = "\norder by ";

  private static final String SELECT = "select ";

  private static final String SELECT_COUNT = "select count(";

  private static final String SELECT_COUNT_DISTINCT = "select count(distinct ";

  private static final String SELECT_DISTINCT = "select distinct ";

  private static final String SET = "\nset ";

  private static final String UPDATE = "update ";

  private static final String WHERE = "\nwhere ";

  private static final String WITH = " with ";

  private static final String ON = " on ";

  private static final Map<JoinType, String> joinTypes = new HashMap<JoinType, String>();

  private final JPQLTemplates templates;

  private final EntityManager entityManager;

  private boolean inProjection = false;

  static {
    joinTypes.put(JoinType.DEFAULT, COMMA);
    joinTypes.put(JoinType.FULLJOIN, "\n  full join ");
    joinTypes.put(JoinType.INNERJOIN, "\n  inner join ");
    joinTypes.put(JoinType.JOIN, "\n  join ");
    joinTypes.put(JoinType.LEFTJOIN, "\n  left join ");
    joinTypes.put(JoinType.RIGHTJOIN, "\n  right join ");
  }

  private boolean wrapElements = false;

  public CustomJPQLSerializer(JPQLTemplates templates) {
    this(templates, null);
  }

  public CustomJPQLSerializer(JPQLTemplates templates, EntityManager em) {
    super(templates);
    this.templates = templates;
    this.entityManager = em;
  }

  private void handleJoinTarget(JoinExpression je) {
    // type specifier
    if (je.getTarget() instanceof EntityPath<?>) {
      EntityPath<?> pe = (EntityPath<?>) je.getTarget();
      if (pe.getMetadata().getParent() == null) {
        Entity entityAnnotation = pe.getAnnotatedElement().getAnnotation(Entity.class);
        if (entityAnnotation != null && entityAnnotation.name().length() > 0) {
          append(entityAnnotation.name());
        } else if (pe.getType().getPackage() != null) {
//          String pn = pe.getType().getPackage().getName();
          String typeName = pe.getType().getName();
          append(typeName);
        } else {
          append(pe.getType().getName());
        }
        append(" ");
      }
    }
    handle(je.getTarget());
  }

  public void serialize(QueryMetadata metadata, boolean forCountRow, @Nullable String projection) {
    List<? extends Expression<?>> select = metadata.getProjection();
    List<JoinExpression> joins = metadata.getJoins();
    Predicate where = metadata.getWhere();
    List<? extends Expression<?>> groupBy = metadata.getGroupBy();
    Predicate having = metadata.getHaving();
    List<OrderSpecifier<?>> orderBy = metadata.getOrderBy();

    // select
    boolean inProjectionOrig = inProjection;
    inProjection = true;
    if (projection != null) {
      append(SELECT).append(projection).append("\n");

    } else if (forCountRow) {
      if (!metadata.isDistinct()) {
        append(SELECT_COUNT);
      } else {
        append(SELECT_COUNT_DISTINCT);
      }
      if (!select.isEmpty()) {
        if (select.get(0) instanceof FactoryExpression) {
          handle(joins.get(0).getTarget());
        } else {
          // TODO : make sure this works
          handle(COMMA, select);
        }
      } else {
        handle(joins.get(0).getTarget());
      }
      append(")\n");

    } else {
      if (!metadata.isDistinct()) {
        append(SELECT);
      } else {
        append(SELECT_DISTINCT);
      }
      if (!select.isEmpty()) {
        handle(COMMA, select);
      } else {
        handle(metadata.getJoins().get(0).getTarget());
      }
      append("\n");

    }
    inProjection = inProjectionOrig;

    // from
    append(FROM);
    serializeSources(forCountRow, joins);

    // where
    if (where != null) {
      append(WHERE).handle(where);
    }

    // group by
    if (!groupBy.isEmpty()) {
      append(GROUP_BY).handle(COMMA, groupBy);
    }

    // having
    if (having != null) {
      append(HAVING).handle(having);
    }

    // order by
    if (!orderBy.isEmpty() && !forCountRow) {
      append(ORDER_BY);
      boolean first = true;
      for (OrderSpecifier<?> os : orderBy) {
        if (!first) {
          append(COMMA);
        }
        handle(os.getTarget());
        append(" " + os.getOrder().toString().toLowerCase(Locale.ENGLISH));
        first = false;
      }
    }
  }

  public void serializeForDelete(QueryMetadata md) {
    append(DELETE);
    handleJoinTarget(md.getJoins().get(0));
    if (md.getWhere() != null) {
      append(WHERE).handle(md.getWhere());
    }
  }

  public void serializeForUpdate(QueryMetadata md) {
    append(UPDATE);
    handleJoinTarget(md.getJoins().get(0));
    append(SET);
    handle(COMMA, md.getProjection());
    if (md.getWhere() != null) {
      append(WHERE).handle(md.getWhere());
    }
  }

  private void serializeSources(boolean forCountRow, List<JoinExpression> joins) {
    for (int i = 0; i < joins.size(); i++) {
      JoinExpression je = joins.get(i);
      if (i > 0) {
        append(joinTypes.get(je.getType()));
      }
      if (je.hasFlag(JPQLQueryMixin.FETCH) && !forCountRow) {
        handle(JPQLQueryMixin.FETCH);
      }
      handleJoinTarget(je);
      // XXX Hibernate specific flag
      if (je.hasFlag(JPQLQueryMixin.FETCH_ALL_PROPERTIES) && !forCountRow) {
        handle(JPQLQueryMixin.FETCH_ALL_PROPERTIES);
      }

      if (je.getCondition() != null) {
        append(templates.isWithForOn() ? WITH : ON);
        handle(je.getCondition());
      }
    }
  }

  @Override
  public Void visit(Constant<?> expr, Void context) {
    boolean wrap = templates.wrapConstant(expr);
    if (wrap) {
      append("(");
    }
    append("?");
    if (!getConstantToLabel().containsKey(expr.getConstant())) {
      String constLabel = String.valueOf(getConstantToLabel().size() + 1);
      getConstantToLabel().put(expr.getConstant(), constLabel);
      append(constLabel);
    } else {
      append(getConstantToLabel().get(expr.getConstant()));
    }
    if (wrap) {
      append(")");
    }
    return null;
  }

  @Override
  public Void visit(ParamExpression<?> param, Void context) {
    append("?");
    if (!getConstantToLabel().containsKey(param)) {
      String paramLabel = String.valueOf(getConstantToLabel().size() + 1);
      getConstantToLabel().put(param, paramLabel);
      append(paramLabel);
    } else {
      append(getConstantToLabel().get(param));
    }
    return null;
  }

  @Override
  public Void visit(SubQueryExpression<?> query, Void context) {
    append("(");
    serialize(query.getMetadata(), false, null);
    append(")");
    return null;
  }

  @Override
  public Void visit(Path<?> expr, Void context) {
    // only wrap a PathCollection, if it the pathType is PROPERTY
    boolean wrap = wrapElements && (Collection.class.isAssignableFrom(expr.getType()) || Map.class.isAssignableFrom(expr.getType()))
        && expr.getMetadata().getPathType().equals(PathType.PROPERTY);
    if (wrap) {
      append("elements(");
    }
    super.visit(expr, context);
    if (wrap) {
      append(")");
    }
    return null;
  }

  @Override
  public Void visit(FactoryExpression<?> expr, Void context) {
    if (!inProjection) {
      append("(");
      super.visit(expr, context);
      append(")");
    } else {
      super.visit(expr, context);
    }
    return null;
  }

  @Override
  @SuppressWarnings({ "unchecked", "rawtypes" })
  protected void visitOperation(Class<?> type, Operator<?> operator, List<? extends Expression<?>> args) {
    boolean old = wrapElements;
    wrapElements = templates.wrapElements(operator);

    if (operator.equals(Ops.EQ) && args.get(1) instanceof Operation && ((Operation) args.get(1)).getOperator() == Ops.QuantOps.ANY) {
      args = Arrays.<Expression<?>> asList(args.get(0), ((Operation) args.get(1)).getArg(0));
      visitOperation(type, Ops.IN, args);

    } else if (operator.equals(Ops.IN)) {
      if (args.get(1) instanceof Path) {
        visitAnyInPath(type, args);
      } else if (args.get(0) instanceof Path && args.get(1) instanceof Constant) {
        visitPathInCollection(type, operator, args);
      } else {
        super.visitOperation(type, operator, args);
      }

    } else if (operator.equals(Ops.INSTANCE_OF)) {
      visitInstanceOf(type, operator, args);

    } else if (operator.equals(Ops.NUMCAST)) {
      visitNumCast(args);

    } else if (operator.equals(Ops.EXISTS) && args.get(0) instanceof SubQueryExpression) {
      SubQueryExpression subQuery = (SubQueryExpression) args.get(0);
      append("exists (");
      serialize(subQuery.getMetadata(), false, templates.getExistsProjection());
      append(")");

    } else if (operator.equals(Ops.MATCHES) || operator.equals(Ops.MATCHES_IC)) {
      super.visitOperation(type, Ops.LIKE, Arrays.asList(args.get(0), ExpressionUtils.regexToLike((Expression<String>) args.get(1))));

    } else if (NUMERIC.contains(operator)) {
      super.visitOperation(type, operator, normalizeNumericArgs(args));

    } else {
      super.visitOperation(type, operator, args);
    }
    //
    wrapElements = old;
  }

  private void visitNumCast(List<? extends Expression<?>> args) {
    Class<?> targetType = (Class<?>) ((Constant<?>) args.get(1)).getConstant();
    String typeName = targetType.getSimpleName().toLowerCase(Locale.ENGLISH);
    visitOperation(targetType, JPQLTemplates.CAST, Arrays.<Expression<?>> asList(args.get(0), ConstantImpl.create(typeName)));
  }

  private void visitInstanceOf(Class<?> type, Operator<?> operator, List<? extends Expression<?>> args) {
    if (templates.isTypeAsString()) {
      List<Expression<?>> newArgs = new ArrayList<Expression<?>>(args);
      Class<?> cl = ((Class<?>) ((Constant<?>) newArgs.get(1)).getConstant());
      // use discriminator value instead of fqnm
      if (cl.getAnnotation(DiscriminatorValue.class) != null) {
        newArgs.set(1, ConstantImpl.create(cl.getAnnotation(DiscriminatorValue.class).value()));
      } else {
        newArgs.set(1, ConstantImpl.create(cl.getName()));
      }
      super.visitOperation(type, operator, newArgs);
    } else {
      super.visitOperation(type, operator, args);
    }
  }

  @SuppressWarnings({ "rawtypes", "unchecked" })
  private void visitPathInCollection(Class<?> type, Operator<?> operator, List<? extends Expression<?>> args) {
    // NOTE turns entityPath in collection into entityPath.id in (collection
    // of ids)
    if (entityManager != null && args.get(0).getType().isAnnotationPresent(Entity.class)) {
      Path<?> lhs = (Path<?>) args.get(0);
      Constant<?> rhs = (Constant<?>) args.get(1);
      Metamodel metamodel = entityManager.getMetamodel();
      PersistenceUnitUtil util = entityManager.getEntityManagerFactory().getPersistenceUnitUtil();
      EntityType<?> entityType = metamodel.entity(args.get(0).getType());
      if (entityType.hasSingleIdAttribute()) {
        SingularAttribute<?, ?> id = getIdProperty(entityType);
        // turn lhs into id path
        lhs = new PathImpl(id.getJavaType(), lhs, id.getName());
        // turn rhs into id collection
        Set ids = new HashSet();
        for (Object entity : (Collection<?>) rhs.getConstant()) {
          ids.add(util.getIdentifier(entity));
        }
        rhs = new ConstantImpl(ids);
        args = Arrays.asList(lhs, rhs);
      }
    }
    super.visitOperation(type, operator, args);
  }

  @SuppressWarnings({ "rawtypes", "unchecked" })
  private SingularAttribute<?, ?> getIdProperty(EntityType entity) {
    Set<SingularAttribute> singularAttributes = entity.getSingularAttributes();
    for (SingularAttribute singularAttribute : singularAttributes) {
      if (singularAttribute.isId()) {
        return singularAttribute;
      }
    }
    return null;
  }

  @SuppressWarnings({ "rawtypes", "unchecked" })
  private void visitAnyInPath(Class<?> type, List<? extends Expression<?>> args) {
    if (!templates.isEnumInPathSupported() && args.get(0) instanceof Constant && Enum.class.isAssignableFrom(args.get(0).getType())) {
      Enumerated enumerated = ((Path) args.get(1)).getAnnotatedElement().getAnnotation(Enumerated.class);
      Enum constant = (Enum) ((Constant) args.get(0)).getConstant();
      if (enumerated == null || enumerated.value() == EnumType.ORDINAL) {
        args = Arrays.asList(new ConstantImpl<Integer>(constant.ordinal()), args.get(1));
      } else {
        args = Arrays.asList(new ConstantImpl<String>(constant.name()), args.get(1));
      }
    }
    super.visitOperation(type, JPQLTemplates.MEMBER_OF, args);
  }

  @SuppressWarnings({ "unchecked", "rawtypes" })
  private List<? extends Expression<?>> normalizeNumericArgs(List<? extends Expression<?>> args) {
    boolean hasConstants = false;
    Class<? extends Number> numType = null;
    for (Expression<?> arg : args) {
      if (Number.class.isAssignableFrom(arg.getType())) {
        if (arg instanceof Constant) {
          hasConstants = true;
        } else {
          numType = (Class<? extends Number>) arg.getType();
        }
      }
    }
    if (hasConstants && numType != null) {
      List<Expression<?>> newArgs = new ArrayList<Expression<?>>(args.size());
      for (Expression<?> arg : args) {
        if (arg instanceof Constant && Number.class.isAssignableFrom(arg.getType()) && !arg.getType().equals(numType)) {
          Number number = (Number) ((Constant) arg).getConstant();
          newArgs.add(new ConstantImpl(MathUtils.cast(number, (Class) numType)));
        } else {
          newArgs.add(arg);
        }
      }
      return newArgs;
    } else {
      return args;
    }
  }

}
TOP

Related Classes of es.twentymobile.mule.modules.jpa.dsl.CustomJPQLSerializer

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.