blob: 59b837e2c10295fd2356c171fcf1b1385846834d [file] [log] [blame]
/* $Id$
*
* 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.
*/
package org.apache.etch.bindings.java.compiler;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.etch.compiler.Backend;
import org.apache.etch.compiler.CmdLineOptions;
import org.apache.etch.compiler.EtchGrammarConstants;
import org.apache.etch.compiler.LogHandler;
import org.apache.etch.compiler.Output;
import org.apache.etch.compiler.ParseException;
import org.apache.etch.compiler.Token;
import org.apache.etch.compiler.Version;
import org.apache.etch.compiler.ast.Builtin;
import org.apache.etch.compiler.ast.Except;
import org.apache.etch.compiler.ast.Item;
import org.apache.etch.compiler.ast.Message;
import org.apache.etch.compiler.ast.MessageDirection;
import org.apache.etch.compiler.ast.Module;
import org.apache.etch.compiler.ast.MsgDirHelper;
import org.apache.etch.compiler.ast.Name;
import org.apache.etch.compiler.ast.Named;
import org.apache.etch.compiler.ast.ParamList;
import org.apache.etch.compiler.ast.Parameter;
import org.apache.etch.compiler.ast.ReservedWordChecker;
import org.apache.etch.compiler.ast.Service;
import org.apache.etch.compiler.ast.Struct;
import org.apache.etch.compiler.ast.Thrown;
import org.apache.etch.compiler.ast.TypeRef;
import org.apache.etch.compiler.opt.ToString;
import org.apache.etch.compiler.opt.ToString.FieldItem;
import org.apache.etch.compiler.opt.ToString.FmtItem;
import org.apache.etch.compiler.opt.ToString.StringItem;
import org.apache.etch.util.Assertion;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.runtime.RuntimeServices;
import org.apache.velocity.runtime.log.LogChute;
/**
* Compiler is a helper class not only for Backend, but also for the templates.
* They call methods here to perform "hard" tasks.
*/
public class Compiler extends Backend
{
private final static String tmplPath1 = "org/apache/etch/bindings/java/compiler/";
private final static String tmplPath2 = "resources/org/apache/etch/bindings/java/compiler/";
private final static String fnSuffix = ".java";
private final static String VERSION = Version.VERSION + " / "
+ CompilerVersion.VERSION;
/**
* Constructs the Compiler. This is a helper class not only for Backend, but
* also for the templates. They call methods here to perform "hard" tasks.
*
* @throws Exception
*/
public Compiler() throws Exception
{
initVelocity();
String[] path = { tmplPath1, tmplPath2 };
vf_vm = getTemplate( path, "vf.vm" );
intf_vm = getTemplate( path, "intf.vm" );
remote_vm = getTemplate( path, "remote.vm" );
stub_vm = getTemplate( path, "stub.vm" );
helper_vm = getTemplate( path, "helper.vm" );
readme_vm = getTemplate( path, "readme.vm" );
main_vm = getTemplate( path, "main.vm" );
base_vm = getTemplate( path, "base.vm" );
impl_vm = getTemplate( path, "impl.vm" );
local_kwd = getPath( path, "javaKeywords.kwd" );
}
private final Template vf_vm;
private final Template intf_vm;
private final Template remote_vm;
private final Template stub_vm;
private final Template helper_vm;
private final Template readme_vm;
private final Template main_vm;
private final Template base_vm;
private final Template impl_vm;
private final String local_kwd;
private LogHandler lh;
/**
* Initializes use of velocity engine and sets up
* resource loaders.
* @throws Exception
*/
private void initVelocity()
throws Exception
{
Velocity.setProperty( Velocity.RUNTIME_LOG_LOGSYSTEM, new MyLogger() );
Velocity.setProperty( Velocity.RESOURCE_LOADER, "file, class" );
Velocity.setProperty( "file.resource.loader.description", "Velocity File Resource Loader" );
Velocity.setProperty( "file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.FileResourceLoader" );
Velocity.setProperty( "file.resource.loader.path", "." );
Velocity.setProperty( "class.resource.loader.description", "Velocity Classpath Resource Loader" );
Velocity.setProperty( "class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader" );
Velocity.init();
}
private static class MyLogger implements LogChute
{
private final LogHandler lh = null;
public void init( RuntimeServices rts ) throws Exception
{
// ignore.
}
public boolean isLevelEnabled( int level )
{
return level >= 2;
}
public void log( int level, String msg )
{
if (level < 2)
return;
if (lh != null)
lh.report( level == 2 ? LogHandler.LEVEL_WARNING : LogHandler.LEVEL_ERROR, null, msg );
else
System.out.printf( "Velocity msg (%d): %s\n", level, msg );
}
public void log( int level, String msg, Throwable e )
{
if (level < 2)
return;
if (lh != null)
lh.report( level == 2 ? LogHandler.LEVEL_WARNING : LogHandler.LEVEL_ERROR, null, msg );
else
System.out.printf( "Velocity msg (%d): %s: %s\n", level, msg, e );
}
}
/**
* @param path
* @param fn
* @return the velocity template
* @throws Exception
*/
private Template getTemplate( String[] path, String fn )
throws Exception
{
ResourceNotFoundException rnfe = null;
for (String p: path)
{
if (p == null)
continue;
// System.out.println( "trying to load template "+(p+fn) );
try
{
if (Velocity.resourceExists( p+fn ))
return Velocity.getTemplate( p+fn );
}
catch ( ResourceNotFoundException e )
{
rnfe = e;
}
catch ( Exception e )
{
System.out.println( "ignoring "+e);
}
}
if (rnfe != null)
throw rnfe;
throw new ResourceNotFoundException("could not find resource: "+fn);
}
@Override
public void generate( Module module, CmdLineOptions options )
throws Exception
{
// java always wants to not flatten packages:
options.noFlattenPackages = true;
lh = options.lh;
boolean ignoreGlobal = options.ignoreGlobalWordsList;
boolean ignoreLocal = options.ignoreLocalWordsList;
String userWords = options.userWordsList != null ? options.userWordsList.getPath() : null;
Set<String> what = options.what;
// Load the reserved words lists if any have been provided.
Map<String, String> words = new HashMap<String, String>();
if (!ignoreGlobal)
mapWords( global_kwd, words );
if (!ignoreLocal)
mapWords( local_kwd, words );
if (userWords != null)
mapWords( userWords, words );
// check for collisions with the reserved word list.
ReservedWordChecker checker = new ReservedWordChecker( words, false, lh );
module.treewalk( checker );
if (!checker.ok())
{
lh.report( LogHandler.LEVEL_ERROR, null, "Encountered errors during java generation." );
return;
}
// ok, we're ready to generate code. make sure the
// output directories exist.
Output dir = options.output;
Output templateDir = options.templateOutput;
String m = module.name().name;
if (m.length() > 0)
{
dir = dir.newPackage( m );
templateDir = templateDir.newPackage( m );
}
// generate code for each service.
for (Service intf : module)
{
generate( intf, what, dir, templateDir );
}
}
private void generate( final Service intf, Set<String> what, Output dir,
Output templateDir ) throws Exception
{
what = populateWhat( what );
if (what.isEmpty())
{
// lh.logMessage( lh.LEVEL_ERROR, null, "User has selected NONE\n" );
return;
}
final MessageDirection msgDir = getMessageDirection( what );
if (what.contains( WHAT_INTF ))
{
// Generate the value factory file.
generateVf( intf, dir );
// Generate the interface, remote, and stub files.
generateIntfRemoteStub( intf, dir, msgDir, MessageDirection.BOTH, false );
generateIntfRemoteStub( intf, dir, msgDir, MessageDirection.SERVER, true );
generateIntfRemoteStub( intf, dir, msgDir, MessageDirection.CLIENT, true );
// Generate helper file.
generateHelper( intf, dir, msgDir );
// Generate base file.
generateBase( intf, dir, msgDir );
// Generate readme file.
generateReadme( intf, dir, msgDir );
}
// Generate main template file.
if (what.contains( WHAT_MAIN ))
generateMain( intf, templateDir, msgDir );
// Generate impl template file.
if (what.contains( WHAT_IMPL ))
generateImpl( intf, templateDir, msgDir );
}
private void generateReadme( final Service intf, Output dir,
final MessageDirection msgDir ) throws Exception
{
doFile( dir, "readme-etch-java-files.txt", lh, new Gen()
{
public void run( PrintWriter pw ) throws Exception
{
generateReadme( pw, intf, msgDir );
}
} );
}
private void generateVf( final Service intf, Output dir )
throws Exception
{
doFile( dir, getVfName( intf ) + fnSuffix, lh, new Gen()
{
public void run( PrintWriter pw ) throws Exception
{
generateVf( pw, intf );
}
} );
}
private void generateHelper( final Service intf, Output dir,
final MessageDirection msgDir ) throws Exception
{
doFile( dir, getHelperName( intf ) + fnSuffix, lh, new Gen()
{
public void run( PrintWriter pw ) throws Exception
{
generateHelper( pw, intf, msgDir );
}
} );
}
private void generateMain( final Service intf, Output dir,
MessageDirection msgDir ) throws Exception
{
if (msgDir == MessageDirection.BOTH
|| msgDir == MessageDirection.CLIENT)
doFile( dir, getMainName( intf, MessageDirection.CLIENT ) + fnSuffix, lh, new Gen()
{
public void run( PrintWriter pw ) throws Exception
{
generateMain( pw, intf, MessageDirection.CLIENT, false );
}
} );
if (msgDir == MessageDirection.BOTH
|| msgDir == MessageDirection.SERVER)
doFile( dir, getMainName( intf, MessageDirection.SERVER ) + fnSuffix, lh, new Gen()
{
public void run( PrintWriter pw ) throws Exception
{
generateMain( pw, intf, MessageDirection.SERVER, false );
}
} );
}
private void generateBase( final Service intf, Output dir,
MessageDirection msgDir ) throws Exception
{
if (msgDir == MessageDirection.BOTH || msgDir == MessageDirection.CLIENT)
doFile( dir, getBaseName( intf, MessageDirection.CLIENT ) + fnSuffix, lh, new Gen()
{
public void run( PrintWriter pw ) throws Exception
{
generateBase (pw, intf, MessageDirection.CLIENT, false );
}
} );
if (msgDir == MessageDirection.BOTH || msgDir == MessageDirection.SERVER)
doFile( dir, getBaseName( intf, MessageDirection.SERVER ) + fnSuffix, lh, new Gen()
{
public void run( PrintWriter pw ) throws Exception
{
generateBase (pw, intf, MessageDirection.SERVER, false );
}
} );
}
private void generateImpl( final Service intf, Output dir,
MessageDirection msgDir ) throws Exception
{
if (msgDir == MessageDirection.BOTH
|| msgDir == MessageDirection.CLIENT)
doFile( dir, getImplName( intf, MessageDirection.CLIENT ) + fnSuffix, lh, new Gen()
{
public void run( PrintWriter pw ) throws Exception
{
generateImpl( pw, intf, MessageDirection.CLIENT, false );
}
} );
if (msgDir == MessageDirection.BOTH
|| msgDir == MessageDirection.SERVER)
doFile( dir, getImplName( intf, MessageDirection.SERVER ) + fnSuffix, lh, new Gen()
{
public void run( PrintWriter pw ) throws Exception
{
generateImpl( pw, intf, MessageDirection.SERVER, false );
}
} );
}
private void generateIntfRemoteStub( final Service intf, Output dir,
final MessageDirection what, final MessageDirection mc,
final boolean hasBaseClass ) throws Exception
{
// Generate interface file
doFile( dir, getIntfName( intf, mc ) + fnSuffix, lh, new Gen()
{
public void run( PrintWriter pw ) throws Exception
{
generateIntf( pw, intf, mc, hasBaseClass );
}
} );
// Generate remote file
if (mc == MessageDirection.BOTH || what == MessageDirection.BOTH
|| mc != what)
doFile( dir, getRemoteName( intf, mc ) + fnSuffix, lh, new Gen()
{
public void run( PrintWriter pw ) throws Exception
{
generateRemote( pw, intf, mc, hasBaseClass );
}
} );
// Generate stub file
if (mc == MessageDirection.BOTH || what == MessageDirection.BOTH
|| mc == what)
doFile( dir, getStubName( intf, mc ) + fnSuffix, lh, new Gen()
{
public void run( PrintWriter pw ) throws Exception
{
generateStub( pw, intf, mc, hasBaseClass );
}
} );
}
/**
* Generate the value factory for the service.
*
* @param pw
* @param intf
* @throws Exception
*/
void generateVf( PrintWriter pw, Service intf ) throws Exception
{
// params keeps track of the total set of parameters
// named (for enums, structs, exceptions, and messages).
Set<String> params = new HashSet<String>();
VelocityContext context = new VelocityContext();
context.put( "now", new Date() );
context.put( "version", VERSION );
context.put( "helper", this );
context.put( "intf", intf );
context.put( "params", params );
vf_vm.merge( context, pw );
}
/**
* Generate the interface for the service.
*
* @param pw
* @param intf
* @param mc
* @param hasBaseClass
* @throws Exception
*/
void generateIntf( PrintWriter pw, Service intf, MessageDirection mc,
boolean hasBaseClass ) throws Exception
{
VelocityContext context = new VelocityContext();
context.put( "now", new Date() );
context.put( "version", VERSION );
context.put( "helper", this );
context.put( "intf", intf );
context.put( "mc", mc );
context.put( "suffix", MsgDirHelper.getSuffix( mc ) );
context.put( "hasBaseClass", hasBaseClass );
intf_vm.merge( context, pw );
}
/**
* Generate the call to message implementation of the interface. This class
* turns calls on its methods into messages which are sent to the remote
* stub. For two-way calls, it then waits for a response message, returning
* the result therein to the caller.
*
* @param pw
* @param intf
* @param mc
* @param hasBaseClass
* @throws Exception
*/
void generateRemote( PrintWriter pw, Service intf, MessageDirection mc,
boolean hasBaseClass ) throws Exception
{
VelocityContext context = new VelocityContext();
context.put( "now", new Date() );
context.put( "version", VERSION );
context.put( "helper", this );
context.put( "intf", intf );
context.put( "mc", mc );
context.put( "suffix", MsgDirHelper.getSuffix( mc ) );
context.put( "hasBaseClass", hasBaseClass );
context.put( "methodList", new ArrayList<String>());
remote_vm.merge( context, pw );
}
/**
* Generate the message to call implementation. This class accepts a message
* and turns it back into a call on the user's implementation. For two-way
* messages, the return value from the user's implementation method is turned
* into the appropriate response message and sent.
*
* @param pw
* @param intf
* @param mc
* @param hasBaseClass
* @throws Exception
*/
void generateStub( PrintWriter pw, Service intf, MessageDirection mc,
boolean hasBaseClass ) throws Exception
{
VelocityContext context = new VelocityContext();
context.put( "now", new Date() );
context.put( "version", VERSION );
context.put( "helper", this );
context.put( "intf", intf );
context.put( "mc", mc );
context.put( "suffix", MsgDirHelper.getSuffix( mc ) );
context.put( "hasBaseClass", hasBaseClass );
stub_vm.merge( context, pw );
}
/**
* Generate the transport plumbing helper.
*
* @param pw
* @param intf
* @param mc
* @throws Exception
*/
void generateHelper( PrintWriter pw, Service intf, MessageDirection mc )
throws Exception
{
VelocityContext context = new VelocityContext();
context.put( "now", new Date() );
context.put( "version", VERSION );
context.put( "helper", this );
context.put( "intf", intf );
context.put( "mc", mc );
helper_vm.merge( context, pw );
}
/**
* Generate the readme.txt.
*
* @param pw
* @param intf
* @param mc
* @throws Exception
*/
void generateReadme( PrintWriter pw, Service intf, MessageDirection mc ) throws Exception
{
VelocityContext context = new VelocityContext();
context.put( "now", new Date() );
context.put( "version", VERSION );
context.put( "helper", this );
context.put( "intf", intf );
context.put( "mc", mc );
readme_vm.merge( context, pw );
}
/**
* Generate the template main program.
*
* @param pw
* @param intf
* @param mc
* @param hasBaseClass
* @throws Exception
*/
void generateMain( PrintWriter pw, Service intf, MessageDirection mc,
boolean hasBaseClass ) throws Exception
{
VelocityContext context = new VelocityContext();
context.put( "now", new Date() );
context.put( "version", VERSION );
context.put( "helper", this );
context.put( "intf", intf );
context.put( "mc", mc );
context.put( "suffix", MsgDirHelper.getSuffix( mc ) );
context.put( "hasBaseClass", hasBaseClass );
main_vm.merge( context, pw );
}
/**
* Generates the base implementation of the interfaces, with each
* method throwing an exception to the tune that it isn't implemented.
* User's impl will extend this base implementation.
* @param pw
* @param intf
* @param mc
* @param hasBaseClass
* @throws Exception
*/
void generateBase( PrintWriter pw, Service intf, MessageDirection mc,
boolean hasBaseClass ) throws Exception
{
VelocityContext context = new VelocityContext();
context.put( "now", new Date() );
context.put( "version", VERSION );
context.put( "helper", this );
context.put( "intf", intf );
context.put( "mc", mc );
context.put( "suffix", MsgDirHelper.getSuffix( mc ) );
context.put( "hasBaseClass", hasBaseClass );
context.put( "methodList", new ArrayList<String>());
base_vm.merge( context, pw );
}
/**
* Generate the template user implemention class which extends the base
* implementation generated above. This class will only have the appropriate
* constructor and reference to the appropriate remote, and a comment inviting
* the user to override methods.
* @param pw
* @param intf
* @param mc
* @param hasBaseClass
* @throws Exception
*/
void generateImpl( PrintWriter pw, Service intf, MessageDirection mc,
boolean hasBaseClass ) throws Exception
{
VelocityContext context = new VelocityContext();
context.put( "now", new Date() );
context.put( "version", VERSION );
context.put( "helper", this );
context.put( "intf", intf );
context.put( "mc", mc );
context.put( "suffix", MsgDirHelper.getSuffix( mc ) );
context.put( "hasBaseClass", hasBaseClass );
impl_vm.merge( context, pw );
}
private String getVfName( Service intf )
{
return "ValueFactory" + getIntfName( intf, MessageDirection.BOTH );
}
private String getIntfName( Service intf, MessageDirection mc )
{
String suffix = MsgDirHelper.getSuffix( mc );
return intf.name() + suffix;
}
private String getMainName( Service intf, MessageDirection mc )
{
if (mc == MessageDirection.SERVER)
return "Main" + intf.name() + "Listener";
return "Main" + getIntfName( intf, mc );
}
private String getImplName( Service intf, MessageDirection mc )
{
return "Impl" + getIntfName( intf, mc );
}
private String getRemoteName( Service intf, MessageDirection mc )
{
return "Remote" + getIntfName( intf, mc );
}
private String getStubName( Service intf, MessageDirection mc )
{
return "Stub" + getIntfName( intf, mc );
}
private String getHelperName( Service intf )
{
return intf.name() + "Helper";
}
private String getBaseName( Service intf, MessageDirection mc )
{
return "Base" + getIntfName( intf, mc );
}
@Override
public String asyncReceiverPoolName( Message msg )
{
return msg.getAsyncReceiver().toString().toLowerCase();
}
@Override
public String getTypeValue( TypeRef type, Token value )
{
// System.out.println( "getTypeValue called with: "+type+": "+value );
Token t = type.type();
switch (t.kind)
{
case EtchGrammarConstants.LONG:
return value.image + "l";
case EtchGrammarConstants.FLOAT:
return value.image + "f";
case EtchGrammarConstants.DOUBLE:
return value.image + "d";
case EtchGrammarConstants.STRING:
return protectString( value.image );
default:
return value.image;
}
}
private String protectString( String s )
{
// System.out.println( "protectString called with: "+s );
StringBuffer sb = new StringBuffer();
sb.append( "\"" );
for (char c : s.toCharArray())
{
if (c == '\t')
{
sb.append( "\\t" );
continue;
}
if (c == '\r')
{
sb.append( "\\r" );
continue;
}
if (c == '\n')
{
sb.append( "\\n" );
continue;
}
if (c == '\"')
{
sb.append( "\\\"" );
continue;
}
if (c == '\\')
{
sb.append( "\\\\" );
continue;
}
if (c >= 32 && c < 127)
{
sb.append( c );
continue;
}
sb.append( String.format( "\\u%04x", (int) c ) );
}
sb.append( "\"" );
return sb.toString();
}
/**
* @param type
* @return type name appropriate for use as a structure element or exception
* parameter or function parameter or result.
*/
@Override
public String getTypeName( TypeRef type )
{
if (type.dim() > 0)
return getNativeTypeName( type ) + dim2spec( type.dim() );
return getRefTypeName( type );
}
/**
* @param type the etch type
* @return the fundamental native type for java. so etch int -> java int,
* while etch string -> java String.
*/
@Override
public String getNativeTypeName( TypeRef type )
{
Token t = type.type();
switch (t.kind)
{
case EtchGrammarConstants.VOID:
return "void";
case EtchGrammarConstants.BOOLEAN:
return "boolean";
case EtchGrammarConstants.BYTE:
return "byte";
case EtchGrammarConstants.SHORT:
return "short";
case EtchGrammarConstants.INT:
return "int";
case EtchGrammarConstants.LONG:
return "long";
case EtchGrammarConstants.FLOAT:
return "float";
case EtchGrammarConstants.DOUBLE:
return "double";
case EtchGrammarConstants.STRING:
return "String";
case EtchGrammarConstants.OBJECT:
return "Object";
default:
{
// we have to use a fully qualified name here.
// find the actual type...
Named<?> n = type.intf().get( t.image );
if (n == null)
throw new IllegalArgumentException( String.format(
"undefined or ambiguous name at line %d: %s",
t.beginLine, t.image ) );
return n.efqname( this );
}
}
}
/**
* @param type the etch type
* @return the fundamental native reference type for java. so etch int ->
* java Integer, while etch string -> java String.
*/
private String getRefTypeName( TypeRef type )
{
Token t = type.type();
switch (t.kind)
{
case EtchGrammarConstants.VOID:
return "void";
case EtchGrammarConstants.BOOLEAN:
return "Boolean";
case EtchGrammarConstants.BYTE:
return "Byte";
case EtchGrammarConstants.SHORT:
return "Short";
case EtchGrammarConstants.INT:
return "Integer";
case EtchGrammarConstants.LONG:
return "Long";
case EtchGrammarConstants.FLOAT:
return "Float";
case EtchGrammarConstants.DOUBLE:
return "Double";
case EtchGrammarConstants.STRING:
return "String";
case EtchGrammarConstants.OBJECT:
return "Object";
default:
{
// we have to use a fully qualified name here.
// find the actual type...
Named<?> n = type.intf().get( t.image );
if (n == null)
throw new IllegalArgumentException( String.format(
"undefined or ambiguous name at line %d: %s",
t.beginLine, t.image ) );
return n.efqname( this );
}
}
}
private String dim2spec( int i )
{
String s = "";
while (i-- > 0)
s += "[]";
return s;
}
@Override
public String formatString( ParamList<Service> n, boolean isExcept )
throws ParseException, IOException
{
ToString ts = (ToString) n.getOpt( "ToString" );
List<FmtItem> list;
if (ts != null)
{
list = ts.getFormat();
n.checkFormatList( ts.lineno(), list );
}
else if (isExcept)
list = n.mkFormatList( true, ((Except)n).hasExtends() );
else
list = n.mkFormatList( false, ((Struct)n).hasExtends() );
if (list.size() == 1)
{
return list.get( 0 ).value();
}
StringBuffer sb = new StringBuffer();
sb.append( "String.format( " );
sb.append( "\"" );
for (FmtItem i : list)
{
if (i instanceof FieldItem)
{
sb.append( "%s" );
}
else
{
escape( sb, ((StringItem) i).value() );
}
}
sb.append( "\"" );
for (FmtItem i : list)
{
if (i instanceof FieldItem)
{
sb.append( ", " );
sb.append( ((FieldItem) i).value() );
}
}
sb.append( " )" );
return sb.toString();
}
private void escape( StringBuffer sb, String s ) throws IOException
{
StringReader rdr = new StringReader( s );
int c;
while ((c = rdr.read()) >= 0)
{
if (c == '"')
sb.append( "\\\"" );
else if (c == '\\')
sb.append( "\\\\" );
else if (c == '\t')
sb.append( "\\t" );
else if (c == '\r')
sb.append( "\\r" );
else if (c == '\n')
sb.append( "\\n" );
else
sb.append( (char) c );
}
}
@Override
public String mfvname( String vname )
{
return "_mf_" + vname;
}
@Override
public String mtvname( String vname )
{
return "_mt_" + vname;
}
@Override
public String getLang()
{
return "java";
}
@Override
public String enum_efqname( String fqname, String moduleName,
String serviceName, String enumName )
{
return fqname;
}
@Override
public String except_efqname( String fqname, String moduleName,
String serviceName, String exceptName )
{
return fqname;
}
@Override
public String struct_efqname( String fqname, String moduleName,
String serviceName, String enumName )
{
return fqname;
}
@Override
public String qualifyParameterName( Token name )
{
return name.image;
}
@Override
public String qualifyConstantName( Service intf, Token name )
{
return intf.fqname() + '.' + name.image;
}
@Override
public String qualifyEnumName( Service intf, Token name )
{
return intf.fqname() + '.' + name.image;
}
@Override
public String getValidator( Named<?> named )
{
if (named instanceof Parameter)
{
Parameter param = (Parameter) named;
TypeRef type = param.type();
if (type.isBuiltin())
return String.format( "Validator_%s.get( %d )",
type.type(), type.dim() );
Named<?> n = type.getNamed( type.intf() );
if (n.isBuiltin())
{
Builtin b = (Builtin) n;
String cn = b.className();
int i = cn.indexOf( '<' );
if (i >= 0)
cn = cn.substring( 0, i );
return String.format( "Validator_custom.get( %s.class, %d, %s )",
cn, type.dim(), b.allowSubclass() );
}
// Allow subclassing for etch defined structs and externs.
if (n.isStruct() || n.isExcept())
return String.format( "Validator_custom.get( %s.class, %d, true )",
n.efqname( this ), type.dim() );
// Don't allow subclassing for externs or etch defined enums.
if (!(n.isExtern() || n.isEnumx()))
Assertion.check( n.isExtern() || n.isEnumx(),
"n.isExtern() || n.isEnumx(): "+n );
return String.format( "Validator_custom.get( %s.class, %d, false )",
n.efqname( this ), type.dim() );
}
if (named instanceof Thrown)
{
Thrown thrown = (Thrown) named;
Except e = (Except) thrown.getNamed();
return String.format( "Validator_custom.get( %s.class, 0, true )", e.efqname( this ) );
}
if (named instanceof Item)
return "Validator_boolean.get( 0 )";
return "null";
}
/**
* @param name
* @return the appropriate name for a getter method.
*/
public String getGetterName( Name name )
{
String s = name.name;
return "get"+s.substring( 0, 1 ).toUpperCase()+s.substring( 1 );
}
/**
* @param name
* @return the appropriate name for a setter method.
*/
public String getSetterName( Name name )
{
String s = name.name;
return "set"+s.substring( 0, 1 ).toUpperCase()+s.substring( 1 );
}
@Override
public void addDefaults( Service service ) throws ParseException
{
addBuiltin( service, newName( "List" ), "java.util.List<?>", true );
addBuiltin( service, newName( "Map" ), "java.util.Map<?, ?>", true );
addBuiltin( service, newName( "Set" ), "java.util.Set<?>", true );
addBuiltin( service, newName( "Datetime" ), "java.util.Date", false );
}
}