001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.expression.spel.ast; 018 019import java.lang.reflect.Array; 020 021import org.springframework.asm.MethodVisitor; 022import org.springframework.asm.Type; 023import org.springframework.expression.EvaluationException; 024import org.springframework.expression.TypedValue; 025import org.springframework.expression.spel.CodeFlow; 026import org.springframework.expression.spel.ExpressionState; 027 028/** 029 * Represents a reference to a type, for example "T(String)" or "T(com.somewhere.Foo)" 030 * 031 * @author Andy Clement 032 */ 033public class TypeReference extends SpelNodeImpl { 034 035 private final int dimensions; 036 037 private transient Class<?> type; 038 039 040 public TypeReference(int pos, SpelNodeImpl qualifiedId) { 041 this(pos, qualifiedId, 0); 042 } 043 044 public TypeReference(int pos, SpelNodeImpl qualifiedId, int dims) { 045 super(pos, qualifiedId); 046 this.dimensions = dims; 047 } 048 049 050 @Override 051 public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { 052 // TODO possible optimization here if we cache the discovered type reference, but can we do that? 053 String typeName = (String) this.children[0].getValueInternal(state).getValue(); 054 if (!typeName.contains(".") && Character.isLowerCase(typeName.charAt(0))) { 055 TypeCode tc = TypeCode.valueOf(typeName.toUpperCase()); 056 if (tc != TypeCode.OBJECT) { 057 // It is a primitive type 058 Class<?> clazz = makeArrayIfNecessary(tc.getType()); 059 this.exitTypeDescriptor = "Ljava/lang/Class"; 060 this.type = clazz; 061 return new TypedValue(clazz); 062 } 063 } 064 Class<?> clazz = state.findType(typeName); 065 clazz = makeArrayIfNecessary(clazz); 066 this.exitTypeDescriptor = "Ljava/lang/Class"; 067 this.type = clazz; 068 return new TypedValue(clazz); 069 } 070 071 private Class<?> makeArrayIfNecessary(Class<?> clazz) { 072 if (this.dimensions != 0) { 073 for (int i = 0; i < this.dimensions; i++) { 074 Object array = Array.newInstance(clazz, 0); 075 clazz = array.getClass(); 076 } 077 } 078 return clazz; 079 } 080 081 @Override 082 public String toStringAST() { 083 StringBuilder sb = new StringBuilder("T("); 084 sb.append(getChild(0).toStringAST()); 085 for (int d = 0; d < this.dimensions; d++) { 086 sb.append("[]"); 087 } 088 sb.append(")"); 089 return sb.toString(); 090 } 091 092 @Override 093 public boolean isCompilable() { 094 return (this.exitTypeDescriptor != null); 095 } 096 097 @Override 098 public void generateCode(MethodVisitor mv, CodeFlow cf) { 099 // TODO Future optimization - if followed by a static method call, skip generating code here 100 if (this.type.isPrimitive()) { 101 if (this.type == Boolean.TYPE) { 102 mv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TYPE", "Ljava/lang/Class;"); 103 } 104 else if (this.type == Byte.TYPE) { 105 mv.visitFieldInsn(GETSTATIC, "java/lang/Byte", "TYPE", "Ljava/lang/Class;"); 106 } 107 else if (this.type == Character.TYPE) { 108 mv.visitFieldInsn(GETSTATIC, "java/lang/Character", "TYPE", "Ljava/lang/Class;"); 109 } 110 else if (this.type == Double.TYPE) { 111 mv.visitFieldInsn(GETSTATIC, "java/lang/Double", "TYPE", "Ljava/lang/Class;"); 112 } 113 else if (this.type == Float.TYPE) { 114 mv.visitFieldInsn(GETSTATIC, "java/lang/Float", "TYPE", "Ljava/lang/Class;"); 115 } 116 else if (this.type == Integer.TYPE) { 117 mv.visitFieldInsn(GETSTATIC, "java/lang/Integer", "TYPE", "Ljava/lang/Class;"); 118 } 119 else if (this.type == Long.TYPE) { 120 mv.visitFieldInsn(GETSTATIC, "java/lang/Long", "TYPE", "Ljava/lang/Class;"); 121 } 122 else if (this.type == Short.TYPE) { 123 mv.visitFieldInsn(GETSTATIC, "java/lang/Short", "TYPE", "Ljava/lang/Class;"); 124 } 125 } 126 else { 127 mv.visitLdcInsn(Type.getType(this.type)); 128 } 129 cf.pushDescriptor(this.exitTypeDescriptor); 130 } 131 132}