/* * Copyright 2015, The Querydsl Team (http://www.querydsl.com/team) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.querydsl.collections; import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import com.google.common.primitives.Primitives; import com.querydsl.codegen.Serializer; import com.querydsl.core.QueryException; import com.querydsl.core.support.SerializerBase; import com.querydsl.core.types.*; /** * {@code CollQuerySerializer} is a {@link Serializer} implementation for the Java language * * @author tiwe */ public final class CollQuerySerializer extends SerializerBase<CollQuerySerializer> { private static final Set<Class<?>> WRAPPER_TYPES = ImmutableSet.copyOf(Primitives.allWrapperTypes()); private static final Map<Operator, String> OPERATOR_SYMBOLS = Maps.newIdentityHashMap(); private static final Map<Class<?>, String> CAST_SUFFIXES = Maps.newHashMap(); static { OPERATOR_SYMBOLS.put(Ops.EQ, " == "); OPERATOR_SYMBOLS.put(Ops.NE, " != "); OPERATOR_SYMBOLS.put(Ops.GT, " > "); OPERATOR_SYMBOLS.put(Ops.LT, " < "); OPERATOR_SYMBOLS.put(Ops.GOE, " >= "); OPERATOR_SYMBOLS.put(Ops.LOE, " <= "); OPERATOR_SYMBOLS.put(Ops.ADD, " + "); OPERATOR_SYMBOLS.put(Ops.SUB, " - "); OPERATOR_SYMBOLS.put(Ops.MULT, " * "); OPERATOR_SYMBOLS.put(Ops.DIV, " / "); CAST_SUFFIXES.put(Boolean.class, ".booleanValue()"); CAST_SUFFIXES.put(Byte.class, ".byteValue()"); CAST_SUFFIXES.put(Character.class, ".charValue()"); CAST_SUFFIXES.put(Double.class, ".doubleValue()"); CAST_SUFFIXES.put(Float.class, ".floatValue()"); CAST_SUFFIXES.put(Integer.class, ".intValue()"); CAST_SUFFIXES.put(Long.class, ".longValue()"); CAST_SUFFIXES.put(Short.class, ".shortValue()"); CAST_SUFFIXES.put(String.class, ".toString()"); } public CollQuerySerializer(CollQueryTemplates templates) { super(templates); } @Override public Void visit(Path<?> path, Void context) { final PathType pathType = path.getMetadata().getPathType(); if (pathType == PathType.PROPERTY) { final Path<?> parent = path.getMetadata().getParent(); final String property = path.getMetadata().getName(); final Class<?> parentType = parent.getType(); try { // getter Method m = getAccessor(parentType, property); if (m != null && Modifier.isPublic(m.getModifiers())) { handle(parent); append(".").append(m.getName()).append("()"); } else { // field Field f = getField(parentType, property); if (f != null && Modifier.isPublic(f.getModifiers())) { handle(parent); append(".").append(property); } else { // field access by reflection append(CollQueryFunctions.class.getName() + ".<"); append((path.getType()).getName()).append(">get("); handle(parent); append(", \"" + property + "\")"); } } } catch (Exception e) { throw new QueryException(e); } } else if (pathType == PathType.DELEGATE) { append("("); append("(").append(path.getType().getName()).append(")"); path.getMetadata().getParent().accept(this, context); append(")"); } else { List<Object> args = new ArrayList<Object>(2); if (path.getMetadata().getParent() != null) { args.add(path.getMetadata().getParent()); } args.add(path.getMetadata().getElement()); final Template template = getTemplate(pathType); for (Template.Element element : template.getElements()) { Object rv = element.convert(args); if (rv instanceof Expression) { ((Expression<?>) rv).accept(this, context); } else if (element.isString()) { append(rv.toString()); } else { visitConstant(rv); } } } return null; } private Method getAccessor(Class<?> owner, String property) { try { BeanInfo beanInfo = Introspector.getBeanInfo(owner); PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors(); for (PropertyDescriptor pd : descriptors) { if (pd.getName().equals(property)) { return pd.getReadMethod(); } } return null; } catch (IntrospectionException e) { return null; } } private Field getField(Class<?> owner, String field) { try { return owner.getField(field); } catch (NoSuchFieldException e) { return null; } } @Override public Void visit(SubQueryExpression<?> expr, Void context) { throw new IllegalArgumentException("Not supported"); } private void visitCast(Operator operator, Expression<?> source, Class<?> targetType) { if (Number.class.isAssignableFrom(source.getType()) && !Constant.class.isInstance(source)) { append("new ").append(source.getType().getSimpleName()).append("("); handle(source); append(")"); } else { handle(source); } if (CAST_SUFFIXES.containsKey(targetType)) { append(CAST_SUFFIXES.get(targetType)); } else { throw new IllegalArgumentException("Unsupported cast type " + targetType.getName()); } } @Override protected void visitOperation(Class<?> type, Operator operator, List<? extends Expression<?>> args) { if (Ops.aggOps.contains(operator)) { throw new UnsupportedOperationException("Aggregation operators are only supported as single expressions"); } if (args.size() == 2 && OPERATOR_SYMBOLS.containsKey(operator) && isPrimitive(args.get(0).getType()) && isPrimitive(args.get(1).getType())) { handle(args.get(0)); append(OPERATOR_SYMBOLS.get(operator)); handle(args.get(1)); if (args.get(1) instanceof Constant<?>) { append(CAST_SUFFIXES.get(args.get(1).getType())); } return; } if (operator == Ops.STRING_CAST) { visitCast(operator, args.get(0), String.class); } else if (operator == Ops.NUMCAST) { @SuppressWarnings("unchecked") //this is the second argument's type Constant<Class<?>> rightArg = (Constant<Class<?>>) args.get(1); visitCast(operator, args.get(0), rightArg.getConstant()); } else { super.visitOperation(type, operator, args); } } private static boolean isPrimitive(Class<?> type) { return type.isPrimitive() || WRAPPER_TYPES.contains(type); } @Override public Void visit(FactoryExpression<?> expr, Void context) { visitConstant(expr); append(".newInstance("); handle(", ", expr.getArgs()); append(")"); return null; } }