com.thoughtworks.qdox.builder.impl.EvaluatingVisitor Maven / Gradle / Ivy
package com.thoughtworks.qdox.builder.impl;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import com.thoughtworks.qdox.model.JavaAnnotation;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaField;
import com.thoughtworks.qdox.model.JavaType;
import com.thoughtworks.qdox.model.expression.Add;
import com.thoughtworks.qdox.model.expression.And;
import com.thoughtworks.qdox.model.expression.AnnotationValue;
import com.thoughtworks.qdox.model.expression.AnnotationValueList;
import com.thoughtworks.qdox.model.expression.Assignment;
import com.thoughtworks.qdox.model.expression.Cast;
import com.thoughtworks.qdox.model.expression.Constant;
import com.thoughtworks.qdox.model.expression.Divide;
import com.thoughtworks.qdox.model.expression.Equals;
import com.thoughtworks.qdox.model.expression.ExclusiveOr;
import com.thoughtworks.qdox.model.expression.ExpressionVisitor;
import com.thoughtworks.qdox.model.expression.FieldRef;
import com.thoughtworks.qdox.model.expression.GreaterEquals;
import com.thoughtworks.qdox.model.expression.GreaterThan;
import com.thoughtworks.qdox.model.expression.LessEquals;
import com.thoughtworks.qdox.model.expression.LessThan;
import com.thoughtworks.qdox.model.expression.LogicalAnd;
import com.thoughtworks.qdox.model.expression.LogicalNot;
import com.thoughtworks.qdox.model.expression.LogicalOr;
import com.thoughtworks.qdox.model.expression.MethodInvocation;
import com.thoughtworks.qdox.model.expression.MinusSign;
import com.thoughtworks.qdox.model.expression.Multiply;
import com.thoughtworks.qdox.model.expression.Not;
import com.thoughtworks.qdox.model.expression.NotEquals;
import com.thoughtworks.qdox.model.expression.Or;
import com.thoughtworks.qdox.model.expression.ParenExpression;
import com.thoughtworks.qdox.model.expression.PlusSign;
import com.thoughtworks.qdox.model.expression.PostDecrement;
import com.thoughtworks.qdox.model.expression.PostIncrement;
import com.thoughtworks.qdox.model.expression.PreDecrement;
import com.thoughtworks.qdox.model.expression.PreIncrement;
import com.thoughtworks.qdox.model.expression.Query;
import com.thoughtworks.qdox.model.expression.Remainder;
import com.thoughtworks.qdox.model.expression.ShiftLeft;
import com.thoughtworks.qdox.model.expression.ShiftRight;
import com.thoughtworks.qdox.model.expression.Subtract;
import com.thoughtworks.qdox.model.expression.TypeRef;
import com.thoughtworks.qdox.model.expression.UnsignedShiftRight;
/**
* Visitor that evaluates annotation expressions.
*
* Users of this class must override {@link EvaluatingVisitor#getFieldReferenceValue(JavaField)} to return values for
* referenced fields.
*
* @author Jochen Kuhnle
*/
public class EvaluatingVisitor
implements ExpressionVisitor
{
public Object getValue( JavaAnnotation annotation, String property )
{
Object result = null;
AnnotationValue value = annotation.getProperty( property );
if ( value != null )
{
result = value.accept( this );
}
return result;
}
public List> getListValue( JavaAnnotation annotation, String property )
{
Object value = getValue( annotation, property );
List> list = null;
if ( value != null )
{
if ( value instanceof List )
{
list = (List>) value;
}
else
{
list = Collections.singletonList( value );
}
}
return list;
}
/**
* Return the result type of a binary operator
*
* Performs binary numeric promotion as specified in the Java Language Specification,
*
* @param left the left hand side instance
* @param right the right hand side instance
* @return the expected result Class
* @see section 5.6.1
*/
protected static Class> resultType( final Object left, final Object right )
{
Class> type = void.class;
if ( left instanceof String || right instanceof String )
{
type = String.class;
}
else if ( left instanceof Number && right instanceof Number )
{
if ( left instanceof Double || right instanceof Double )
{
type = Double.class;
}
else if ( left instanceof Float || right instanceof Float )
{
type = Float.class;
}
else if ( left instanceof Long || right instanceof Long )
{
type = Long.class;
}
else
{
type = Integer.class;
}
}
return type;
}
/**
* Return the numeric result type of a binary operator
*
* Performs binary numeric promotion as specified in the Java Language Specification,
*
* @param left the left hand side instance
* @param right the right hand side instance
* @return the expected result Class
* @see section 5.6.1
*/
protected static Class> numericResultType( final Object left, final Object right )
{
Class> type = void.class;
if ( left instanceof Number && right instanceof Number )
{
if ( left instanceof Long || right instanceof Long )
{
type = Long.class;
}
else if ( left instanceof Integer || right instanceof Integer )
{
type = Integer.class;
}
}
return type;
}
/**
* Return the result type of an unary operator
*
* Performs unary numeric promotion as specified in the Java Language Specification,
*
* @param value the instance
* @return the expected result Class
* @see section 5.6.2
*/
protected static Class> unaryNumericResultType( final Object value )
{
Class> type = void.class;
if ( value instanceof Byte || value instanceof Short || value instanceof Character || value instanceof Integer )
{
type = Integer.class;
}
else if ( value instanceof Long )
{
type = Long.class;
}
return type;
}
protected static Class> unaryResultType( final Object value )
{
Class> type = unaryNumericResultType( value );
if ( type == void.class )
{
if ( value instanceof Float )
{
type = Float.class;
}
else if ( value instanceof Double )
{
type = Double.class;
}
}
return type;
}
/** {@inheritDoc} */
public Object visit( JavaAnnotation annotation ) throws UnsupportedOperationException
{
throw new UnsupportedOperationException( "Illegal annotation value '" + annotation + "'." );
}
/** {@inheritDoc} */
public Object visit( Add op )
{
Object left = op.getLeft().accept( this );
Object right = op.getRight().accept( this );
Class> type = resultType( left, right );
Object result;
if ( type == String.class )
{
result = left.toString() + right.toString();
}
else if ( type == Double.class )
{
result = Double.valueOf( ( (Number) left ).doubleValue() + ( (Number) right ).doubleValue() );
}
else if ( type == Float.class )
{
result = Float.valueOf( ( (Number) left ).floatValue() + ( (Number) right ).floatValue() );
}
else if ( type == Long.class )
{
result = Long.valueOf( ( (Number) left ).longValue() + ( (Number) right ).longValue() );
}
else if ( type == Integer.class )
{
result = Integer.valueOf( ( (Number) left ).intValue() + ( (Number) right ).intValue() );
}
else
{
throw new IllegalArgumentException( "Cannot evaluate '" + op + "'." );
}
return result;
}
/** {@inheritDoc} */
public Object visit( Constant constant )
{
return constant.getValue();
}
/** {@inheritDoc} */
public Object visit( Divide op )
{
Object left = op.getLeft().accept( this );
Object right = op.getRight().accept( this );
Class> type = resultType( left, right );
Object result;
if ( type == Double.class )
{
result = Double.valueOf( ( (Number) left ).doubleValue() / ( (Number) right ).doubleValue() );
}
else if ( type == Float.class )
{
result = Float.valueOf( ( (Number) left ).floatValue() / ( (Number) right ).floatValue() );
}
else if ( type == Long.class )
{
result = Long.valueOf( ( (Number) left ).longValue() / ( (Number) right ).longValue() );
}
else if ( type == Integer.class )
{
result = Integer.valueOf( ( (Number) left ).intValue() / ( (Number) right ).intValue() );
}
else
{
throw new IllegalArgumentException( "Cannot evaluate '" + op + "'." );
}
return result;
}
/** {@inheritDoc} */
public Object visit( FieldRef fieldRef )
{
JavaField javaField = fieldRef.getField();
if ( javaField == null )
{
throw new IllegalArgumentException( "Cannot resolve field reference '" + fieldRef + "'." );
}
if ( !(javaField.isFinal() && javaField.isStatic() ) )
{
throw new IllegalArgumentException( "Field reference '" + fieldRef + "' must be static and final." );
}
return getFieldReferenceValue( javaField );
}
protected Object getFieldReferenceValue( JavaField javaField ) {
throw new UnsupportedOperationException("getFieldReferenceValue(JavaField) has not been implemented.");
}
/** {@inheritDoc} */
public Object visit( GreaterThan op )
{
Object left = op.getLeft().accept( this );
Object right = op.getRight().accept( this );
Class> type = resultType( left, right );
boolean result;
if ( type == Double.class )
{
result = ( (Number) left ).doubleValue() > ( (Number) right ).doubleValue();
}
else if ( type == Float.class )
{
result = ( (Number) left ).floatValue() > ( (Number) right ).floatValue();
}
else if ( type == Long.class )
{
result = ( (Number) left ).longValue() > ( (Number) right ).longValue();
}
else if ( type == Integer.class )
{
result = ( (Number) left ).intValue() > ( (Number) right ).intValue();
}
else
{
throw new IllegalArgumentException( "Cannot evaluate '" + op + "'." );
}
return result ? Boolean.TRUE : Boolean.FALSE;
}
/** {@inheritDoc} */
public Object visit( LessThan op )
{
Object left = op.getLeft().accept( this );
Object right = op.getRight().accept( this );
Class> type = resultType( left, right );
boolean result;
if ( type == Double.class )
{
result = ( (Number) left ).doubleValue() < ( (Number) right ).doubleValue();
}
else if ( type == Float.class )
{
result = ( (Number) left ).floatValue() < ( (Number) right ).floatValue();
}
else if ( type == Long.class )
{
result = ( (Number) left ).longValue() < ( (Number) right ).longValue();
}
else if ( type == Integer.class )
{
result = ( (Number) left ).intValue() < ( (Number) right ).intValue();
}
else
{
throw new IllegalArgumentException( "Cannot evaluate '" + op + "'." );
}
return result ? Boolean.TRUE : Boolean.FALSE;
}
/** {@inheritDoc} */
public Object visit( Multiply op )
{
Object left = op.getLeft().accept( this );
Object right = op.getRight().accept( this );
Class> type = resultType( left, right );
Object result;
if ( type == Double.class )
{
result = Double.valueOf( ( (Number) left ).doubleValue() * ( (Number) right ).doubleValue() );
}
else if ( type == Float.class )
{
result = Float.valueOf( ( (Number) left ).floatValue() * ( (Number) right ).floatValue() );
}
else if ( type == Long.class )
{
result = Long.valueOf( ( (Number) left ).longValue() * ( (Number) right ).longValue() );
}
else if ( type == Integer.class )
{
result = Integer.valueOf( ( (Number) left ).intValue() * ( (Number) right ).intValue() );
}
else
{
throw new IllegalArgumentException( "Cannot evaluate '" + op + "'." );
}
return result;
}
/** {@inheritDoc} */
public Object visit( ParenExpression parenExpression )
{
return parenExpression.getValue().accept( this );
}
/** {@inheritDoc} */
public Object visit( Subtract op )
{
Object left = op.getLeft().accept( this );
Object right = op.getRight().accept( this );
Class> type = resultType( left, right );
Object result;
if ( type == Double.class )
{
result = Double.valueOf( ( (Number) left ).doubleValue() - ( (Number) right ).doubleValue() );
}
else if ( type == Float.class )
{
result = Float.valueOf( ( (Number) left ).floatValue() - ( (Number) right ).floatValue() );
}
else if ( type == Long.class )
{
result = Long.valueOf( ( (Number) left ).longValue() - ( (Number) right ).longValue() );
}
else if ( type == Integer.class )
{
result = Integer.valueOf( ( (Number) left ).intValue() - ( (Number) right ).intValue() );
}
else
{
throw new IllegalArgumentException( "Cannot evaluate '" + op + "'." );
}
return result;
}
/** {@inheritDoc} */
public JavaType visit( TypeRef typeRef )
{
return typeRef.getType();
}
/** {@inheritDoc} */
public List> visit( AnnotationValueList valueList )
{
List