/*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package org.glassfish.pfl.dynamic.codegen.impl;
import java.util.List ;
import org.glassfish.pfl.dynamic.codegen.spi.Type ;
import org.glassfish.pfl.dynamic.codegen.spi.Variable ;
import org.glassfish.pfl.dynamic.codegen.spi.ImportList ;
A Visitor that generates Java source for an expression.
All expression are converted into a simple Java String.
This visitor operates only on ExpressionInternal nodes. It ignores all
Statements as well as the top level generators.
This visitor compile complex expressions by applying another
instance of this visitor to the subexpressions recursively
using TreeWalker. Note that the mark facility of the
TreeWalkerContext is needed here to handle arguments lists
for various types of calls.
/** A Visitor that generates Java source for an expression.
* All expression are converted into a simple Java String.
* This visitor operates only on ExpressionInternal nodes. It ignores all
* Statements as well as the top level generators.
* <P>
* This visitor compile complex expressions by applying another
* instance of this visitor to the subexpressions recursively
* using TreeWalker. Note that the mark facility of the
* TreeWalkerContext is needed here to handle arguments lists
* for various types of calls.
*/
public class SourceExpressionVisitor extends TreeWalker {
private ImportList imports ;
private StringBuilder sb ;
private String typeName( Type type ) {
if (imports.contains( type ))
return type.className() ;
else
return type.name() ;
}
public String value() {
return sb.toString() ;
}
public SourceExpressionVisitor( TreeWalkerContext context, ImportList imports ) {
super( context ) ;
context.push( this ) ;
this.imports = imports ;
this.sb = new StringBuilder() ;
}
// Node
@Override
public boolean preNode( Node arg ) {
// Make sure that any nodes not otherwise specified
// are not traversed.
return false ;
}
@Override
public boolean preVariable( Variable arg ) {
sb.append( ((VariableInternal)arg).ident() ) ;
return false ;
}
@Override
public void postVariable( Variable arg ) {
// NO-OP
}
// ConstantExpression
@Override
public boolean preConstantExpression( ExpressionFactory.ConstantExpression arg ) {
// Need to handle the different cases here.
String javaRep ;
Object value = arg.value() ;
Type type = arg.type() ;
if (type == Type._null())
javaRep = "null" ;
else if (type == Type._Class())
javaRep = typeName( Type.class.cast(value) ) + ".class" ;
else if (type == Type._String())
javaRep = "\"" + value + "\"" ;
else if (type == Type._char())
javaRep = "\'" + value + "\'" ;
else
javaRep = value.toString() ;
sb.append( javaRep ) ;
return false ;
}
// VoidExpression
@Override
public boolean preVoidExpression( ExpressionFactory.VoidExpression arg ) {
sb.append( "void" ) ;
return false ;
}
// ThisExpression
@Override
public boolean preThisExpression( ExpressionFactory.ThisExpression arg ) {
sb.append( "this" ) ;
return false ;
}
// UnaryOperatorExpression
@Override
public boolean preUnaryOperatorExpression( ExpressionFactory.UnaryOperatorExpression arg ) {
sb.append( arg.operator().javaRepresentation() ) ;
sb.append( '(' ) ;
return true ;
}
@Override
public void postUnaryOperatorExpression( ExpressionFactory.UnaryOperatorExpression arg ) {
sb.append( ')' ) ;
}
// BinaryOperatorExpression
@Override
public boolean preBinaryOperatorExpression( ExpressionFactory.BinaryOperatorExpression arg ) {
sb.append( '(' ) ;
return true ;
}
@Override
public void binaryOperatorExpressionBeforeRight(
ExpressionFactory.BinaryOperatorExpression arg ) {
sb.append( arg.operator().javaRepresentation() ) ;
}
@Override
public void postBinaryOperatorExpression( ExpressionFactory.BinaryOperatorExpression arg ) {
sb.append( ')' ) ;
}
// CastExpression
@Override
public boolean preCastExpression( ExpressionFactory.CastExpression arg ) {
sb.append( "((" ) ;
sb.append( typeName( arg.type() ) ) ;
sb.append( ')' ) ;
return true ;
}
@Override
public void postCastExpression( ExpressionFactory.CastExpression arg ) {
sb.append( ')' ) ;
}
// InstofExpression
@Override
public boolean preInstofExpression( ExpressionFactory.InstofExpression arg ) {
sb.append( '(' ) ;
return true ;
}
@Override
public void postInstofExpression( ExpressionFactory.InstofExpression arg ) {
sb.append( " instanceof " ) ;
sb.append( typeName( arg.type() ) ) ;
sb.append( ')' ) ;
}
// NonStaticCallExpression
@Override
public boolean preNonStaticCallExpression( ExpressionFactory.NonStaticCallExpression arg ) {
// Mark the start of the context stack for this call expression
context.mark() ;
// Push an expression visitor to handle the target of this method call
new SourceExpressionVisitor( context, imports ) ;
return true ;
}
@Override
public void nonStaticCallExpressionBeforeArg( ExpressionFactory.NonStaticCallExpression arg ) {
// Push an expression visitor to handle the next argument
new SourceExpressionVisitor( context, imports ) ;
}
@Override
public void postNonStaticCallExpression( ExpressionFactory.NonStaticCallExpression arg ) {
List<Visitor> marks = context.popMark() ;
// The first element of marks contains visitor for the target.
// Each subsequent element contains a visitor for an argument.
// Turn all of this into target.op( arg1, ..., arg n ) and append to sb.
int ctr = 0 ;
for (Visitor visitor : marks ) {
SourceExpressionVisitor sev = SourceExpressionVisitor.class.cast( visitor ) ;
if (ctr==0) {
sb.append( sev.value() ) ;
sb.append( '.' ) ;
sb.append( arg.ident() ) ;
sb.append( '(' ) ;
} else {
if (ctr>1)
sb.append( ", " ) ;
sb.append( sev.value() ) ;
}
ctr++ ;
}
sb.append( ")" ) ;
}
// StaticCallExpression
@Override
public boolean preStaticCallExpression( ExpressionFactory.StaticCallExpression arg ) {
// Mark the start of the context stack for this call expression
context.mark() ;
return true ;
}
@Override
public void staticCallExpressionBeforeArg( ExpressionFactory.StaticCallExpression arg ) {
// Push an expression visitor to handle the next argument
new SourceExpressionVisitor( context, imports ) ;
}
@Override
public void postStaticCallExpression( ExpressionFactory.StaticCallExpression arg ) {
List<Visitor> marks = context.popMark() ;
sb.append( typeName( arg.target() ) ) ;
sb.append( '.' ) ;
sb.append( arg.ident() ) ;
sb.append( '(' ) ;
// Each element of marks contains a visitor for an argument.
// Turn all of this into target.op( arg1, ..., arg n ) and append to sb.
int ctr = 0 ;
for (Visitor visitor : marks ) {
SourceExpressionVisitor sev = SourceExpressionVisitor.class.cast( visitor ) ;
if (ctr>0) {
sb.append( ", " ) ;
}
sb.append( sev.value() ) ;
ctr++ ;
}
sb.append( ")" ) ;
}
// NewObjExpression
@Override
public boolean preNewObjExpression( ExpressionFactory.NewObjExpression arg ) {
// Mark the start of the context stack for this call expression
context.mark() ;
return true ;
}
@Override
public void newObjExpressionBeforeArg( ExpressionFactory.NewObjExpression arg ) {
// Push an expression visitor to handle the next argument
new SourceExpressionVisitor( context, imports ) ;
}
@Override
public void postNewObjExpression( ExpressionFactory.NewObjExpression arg ) {
List<Visitor> marks = context.popMark() ;
sb.append( "new " ) ;
sb.append( typeName( arg.type() ) ) ;
sb.append( '(' ) ;
// The element of marks contains visitor for the target.
// Each subsequent element contains a visitor for an argument.
// Turn all of this into "new type( arg1, ..., arg n )" and append to sb.
int ctr = 0 ;
for (Visitor visitor : marks ) {
SourceExpressionVisitor sev = SourceExpressionVisitor.class.cast( visitor ) ;
if (ctr>0) {
sb.append( ", " ) ;
}
sb.append( sev.value() ) ;
ctr++ ;
}
sb.append( ")" ) ;
}
@Override
public boolean preNewArrExpression( ExpressionFactory.NewArrExpression arg ) {
context.mark() ;
new SourceExpressionVisitor( context, imports ) ;
return true ;
}
@Override
public void newArrExpressionBeforeExpression( ExpressionFactory.NewArrExpression arg ) {
new SourceExpressionVisitor( context, imports ) ;
}
// NewArrExpression
// This generates either
// new type[expr] if arg.
// or
// new type[] { expr1, ... , exprn }
//
// In the first case, arg.exprs() == null, otherwise we generate the second
// case.
@Override
public void postNewArrExpression( ExpressionFactory.NewArrExpression arg ) {
List<Visitor> marks = context.popMark() ;
int ctr = 0 ;
for (Visitor visitor : marks ) {
SourceExpressionVisitor sev = SourceExpressionVisitor.class.cast( visitor ) ;
if (ctr==0) {
sb.append( "new " ) ;
sb.append( typeName( arg.ctype() ) ) ;
sb.append( '[' ) ;
if (arg.exprs().size() == 0) {
// marks contains only the array size
sb.append( sev.value() ) ;
sb.append( ']' ) ;
} else {
// marks contains the size followed by the initial elements of array.
// Ignore the size in this case.
sb.append( "] {" ) ;
}
} else {
if (ctr>1)
sb.append( ", " ) ;
sb.append( sev.value() ) ;
}
ctr++ ;
}
sb.append( "}" ) ;
}
// SuperCallExpression
@Override
public boolean preSuperCallExpression( ExpressionFactory.SuperCallExpression arg ) {
// Mark the start of the context stack for this call expression
context.mark() ;
return true ;
}
@Override
public void superCallExpressionBeforeArg( ExpressionFactory.SuperCallExpression arg ) {
// Push an expression visitor to handle the next argument
new SourceExpressionVisitor( context, imports ) ;
}
@Override
public void postSuperCallExpression( ExpressionFactory.SuperCallExpression arg ) {
List<Visitor> marks = context.popMark() ;
sb.append( "super." ) ;
sb.append( arg.ident() ) ;
sb.append( '(' ) ;
// Each element of marks contains a visitor for an argument.
// Turn this into "super.ident( arg1, ..., arg n )" and append to sb.
int ctr = 0 ;
for (Visitor visitor : marks ) {
SourceExpressionVisitor sev = SourceExpressionVisitor.class.cast( visitor ) ;
if (ctr>0) {
sb.append( ", " ) ;
}
sb.append( sev.value() ) ;
ctr++ ;
}
sb.append( ")" ) ;
}
// SuperObjExpression
@Override
public boolean preSuperObjExpression( ExpressionFactory.SuperObjExpression arg ) {
// Mark the start of the context stack for this call expression
context.mark() ;
return true ;
}
@Override
public void superObjExpressionBeforeArg( ExpressionFactory.SuperObjExpression arg ) {
// Push an expression visitor to handle the next argument
new SourceExpressionVisitor( context, imports ) ;
}
@Override
public void postSuperObjExpression( ExpressionFactory.SuperObjExpression arg ) {
List<Visitor> marks = context.popMark() ;
sb.append( "super(" ) ;
// Each element of marks contains a visitor for an argument.
// Turn this into "super( arg1, ..., arg n )" and append to sb.
int ctr = 0 ;
for (Visitor visitor : marks ) {
SourceExpressionVisitor sev = SourceExpressionVisitor.class.cast( visitor ) ;
if (ctr>0) {
sb.append( ", " ) ;
}
sb.append( sev.value() ) ;
ctr++ ;
}
sb.append( ")" ) ;
}
// ThisObjExpression
@Override
public boolean preThisObjExpression( ExpressionFactory.ThisObjExpression arg ) {
// Mark the start of the context stack for this call expression
context.mark() ;
return true ;
}
@Override
public void thisObjExpressionBeforeArg( ExpressionFactory.ThisObjExpression arg ) {
// Push an expression visitor to handle the next argument
new SourceExpressionVisitor( context, imports ) ;
}
@Override
public void postThisObjExpression( ExpressionFactory.ThisObjExpression arg ) {
List<Visitor> marks = context.popMark() ;
sb.append( "this(" ) ;
// Each element of marks contains a visitor for an argument.
// Turn this into "super( arg1, ..., arg n )" and append to sb.
int ctr = 0 ;
for (Visitor visitor : marks ) {
SourceExpressionVisitor sev = SourceExpressionVisitor.class.cast( visitor ) ;
if (ctr>0) {
sb.append( ", " ) ;
}
sb.append( sev.value() ) ;
ctr++ ;
}
sb.append( ")" ) ;
}
// NonStaticFieldAccessExpression
@Override
public boolean preNonStaticFieldAccessExpression(
ExpressionFactory.NonStaticFieldAccessExpression arg ) {
// Just let the visitor write to sb for arg.expr()
return true ;
}
@Override
public void postNonStaticFieldAccessExpression(
ExpressionFactory.NonStaticFieldAccessExpression arg ) {
sb.append( '.' ) ;
sb.append( arg.fieldName() ) ;
}
// StaticFieldAccessExpression
@Override
public boolean preStaticFieldAccessExpression(
ExpressionFactory.StaticFieldAccessExpression arg ) {
// Just let the visitor write to sb for arg.expr()
return true ;
}
@Override
public void postStaticFieldAccessExpression(
ExpressionFactory.StaticFieldAccessExpression arg ) {
sb.append( typeName( arg.target() ) ) ;
sb.append( '.' ) ;
sb.append( arg.fieldName() ) ;
}
// ArrayIndexExpression
@Override
public boolean preArrayIndexExpression( ExpressionFactory.ArrayIndexExpression arg ) {
// Push an expression visitor to handle the index
new SourceExpressionVisitor( context, imports ) ;
return true ;
}
@Override
public void arrayIndexExpressionBeforeExpr( ExpressionFactory.ArrayIndexExpression arg ) {
// Push an expression visitor to handle the array expression
new SourceExpressionVisitor( context, imports ) ;
}
@Override
public void postArrayIndexExpression( ExpressionFactory.ArrayIndexExpression arg ) {
SourceExpressionVisitor expr =
SourceExpressionVisitor.class.cast( context.pop() ) ;
SourceExpressionVisitor index =
SourceExpressionVisitor.class.cast( context.pop() ) ;
sb.append( expr.value() ) ;
sb.append( '[' ) ;
sb.append( index.value() ) ;
sb.append( ']' ) ;
}
// ArrayLengthExpression
@Override
public boolean preArrayLengthExpression(
ExpressionFactory.ArrayLengthExpression arg ) {
new SourceExpressionVisitor( context, imports ) ;
return true ;
}
@Override
public void postArrayLengthExpression( ExpressionFactory.ArrayLengthExpression arg ) {
SourceExpressionVisitor expr =
SourceExpressionVisitor.class.cast( context.pop() ) ;
sb.append( expr.value() ) ;
sb.append( ".length" ) ;
}
}