blob: fe0b7841e55b24793df2cbf6a5977e2d3883abe8 [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.compiler;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
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.Name;
import org.apache.etch.compiler.ast.Named;
import org.apache.etch.compiler.ast.Opt;
import org.apache.etch.compiler.ast.ParamList;
import org.apache.etch.compiler.ast.Service;
import org.apache.etch.compiler.ast.TypeRef;
import org.apache.etch.compiler.opt.Extern;
import org.apache.etch.util.Assertion;
/**
* Generic interface to language (or language/runtime) bindings.
*/
abstract public class Backend
{
private final static String tmplPath1 = "org/apache/etch/compiler/";
private final static String tmplPath2 = "resources/org/apache/etch/compiler/";
//////////////////////////
// WHAT DIRECTION GROUP //
//////////////////////////
/**
* Constant for option --what: generate both client and server files.
*/
public static final String WHAT_BOTH = "BOTH";
/**
* Constant for option --what: generate client files.
*/
public static final String WHAT_CLIENT = "CLIENT";
/**
* Constant for option --what: generate generate server files.
*/
public static final String WHAT_SERVER = "SERVER";
//////////////////////
// WHAT FILES GROUP //
//////////////////////
/**
* Constant for option --what: generate all files.
*/
public static final String WHAT_ALL = "ALL";
/**
* Constant for option --what: generate no files.
*/
public static final String WHAT_NONE = "NONE";
/**
* Constant for option --what: generate intf files.
*/
public static final String WHAT_INTF = "INTF";
/**
* Constant for option --what: generate main file.
*/
public static final String WHAT_MAIN = "MAIN";
/**
* Constant for option --what: generate impl files.
*/
public static final String WHAT_IMPL = "IMPL";
/////////////////////
// WHAT MISC GROUP //
/////////////////////
/**
* Constant for option --what: overwrite template files.
*/
public static final String WHAT_FORCE = "FORCE";
/**
* Constant for option --what: give help on what.
*/
public static final String WHAT_HELP = "HELP";
/**
* Constructs the Backend.
* @throws Exception
*/
protected Backend()
throws Exception
{
String[] path = { tmplPath1, tmplPath2 };
global_kwd = getPath(path, "globalKeywords.kwd");
}
/**
* Path to global keyword resource.
*/
protected final String global_kwd;
@Override
public String toString()
{
return getLang();
}
/**
* Generates code for this binding of the idl.
* @param module the module that we are generating code for.
* @param options the options used to configure the compiler.
* @throws Exception
*/
abstract public void generate( Module module, CmdLineOptions options )
throws Exception;
/**
* Runs the generator for a particular file with the specified
* output stream.
*/
public interface Gen
{
/**
* @param pw
* @throws Exception
*/
void run( PrintWriter pw ) throws Exception;
}
/**
* Helper method. Return true if the md is SERVER.
* @param md
* @return true if MessageDirection is SERVER.
*/
public boolean isServer(MessageDirection md)
{
if (md == null)
throw new NullPointerException( "md == null" );
return md == MessageDirection.SERVER;
}
/**
* Helper method. Return true if the md is CLIENT.
* @param md
* @return true if MessageDirection is CLIENT.
*/
public boolean isClient(MessageDirection md)
{
if (md == null)
throw new NullPointerException( "md == null" );
return md == MessageDirection.CLIENT;
}
/**
* Helper methods. Return true if the md is BOTH.
* @param md
* @return true if MessageDirection is BOTH.
*/
public boolean isBoth( MessageDirection md )
{
if ( md == null )
throw new NullPointerException( "md == null" );
return md == MessageDirection.BOTH;
}
/**
* Returns the correct path to a resource.
* @param path the paths to try for opening the resources
* @param fn the file name of the file to open
* @return the correct path to a resource
* @throws FileNotFoundException
*/
protected String getPath(String[] path, String fn)
throws FileNotFoundException
{
for (String p: path)
{
if (p == null)
continue;
String s = p + fn;
// System.out.println( "trying "+s );
try
{
URL temp = getClass().getClassLoader().getResource(s);
if (temp != null)
return s;
}
catch ( Exception e )
{
// ignore
// System.out.println( "ignoring "+e );
}
}
throw new FileNotFoundException(fn);
}
/**
* Creates (or deletes) the specified file.
* @param dir
* @param fn
* @param gen
* @throws Exception
*/
protected void doFile( Output dir, String fn, LogHandler lh, Gen gen )
throws Exception
{
PrintWriter pw = new PrintWriter( dir.newOutputStream( fn ) );
try
{
gen.run( pw );
}
finally
{
pw.close();
}
}
/**
* Augments the mapping from user defined reserved words to the reserved
* words used by the compiler as parsed from a reserved word list stored in
* a file located at the specified path.
* @param filePath the path to the file containing reserved word mapping
* @param wordMap
* @throws FileNotFoundException
* @throws IOException
*/
protected void mapWords(String filePath, Map<String, String> wordMap)
throws FileNotFoundException, IOException
{
String commentMarker = "#";
String valuePrefix = "x";
String keyValueDelimeter = "= \t";
// Open file for parsing or throw an FileNotFoundError if resources is
// unavailable.
BufferedReader fileReader = null;
try
{
// Assume file is internal to project:
// EG) org/apache/etch/compiler/globalKeywords
InputStream input = getClass().getClassLoader().getResourceAsStream(filePath);
// Otherwise it's external
// EG) ../foo/bar/someKeywords
if (null == input)
input = new FileInputStream(filePath);
//fileReader = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream(filePath)));
fileReader = new BufferedReader(new InputStreamReader(input));
String currentLine = fileReader.readLine();
while (null != currentLine)
{
// Strip trailing comments and padded whitespace
currentLine = currentLine.trim();
if (currentLine.contains(commentMarker))
currentLine = currentLine.substring(0, currentLine.indexOf(commentMarker));
// Skip blank lines
StringTokenizer currentTokens = new StringTokenizer(currentLine, keyValueDelimeter);
if (currentTokens.hasMoreTokens())
{
String key = currentTokens.nextToken();
String value;
if (currentTokens.hasMoreTokens())
value = currentTokens.nextToken();
else
value = Character.isUpperCase(key.codePointAt(0)) ?
valuePrefix.toUpperCase() + key :
valuePrefix + key;
wordMap.put(key, value);
}
currentLine = fileReader.readLine();
}
}
catch (NullPointerException e)
{
throw new FileNotFoundException(filePath);
}
finally
{
if (null != fileReader)
fileReader.close();
}
}
/**
* @param what
* @return the appropriate direction give --what command line options.
*/
protected MessageDirection getMessageDirection(Set<String> what)
{
if ( what.contains( WHAT_BOTH ) )
return MessageDirection.BOTH;
if ( what.contains( WHAT_SERVER ) )
return MessageDirection.SERVER;
if ( what.contains( WHAT_CLIENT ) )
return MessageDirection.CLIENT;
throw new IllegalArgumentException( "what not correctly specified" );
}
/**
* @param what
* @return what modified to account for interaction of options.
*/
protected Set<String> populateWhat( Set<String> what ) throws Exception
{
checkOption( what );
if (what.contains( WHAT_HELP ))
throw new Exception(
"specify what as one or more of "+whatHelp() );
// direction group
if (!what.contains( WHAT_CLIENT )
&& !what.contains( WHAT_SERVER )
&& !what.contains( WHAT_BOTH ) )
{
// default to BOTH if none of the direction group is present.
what.add( WHAT_BOTH );
what.add( WHAT_CLIENT );
what.add( WHAT_SERVER );
}
else if (what.contains( WHAT_BOTH ))
{
// BOTH implies CLIENT and SERVER
what.add( WHAT_CLIENT );
what.add( WHAT_SERVER );
}
else if (what.contains( WHAT_CLIENT ) && what.contains( WHAT_SERVER ))
{
// CLIENT and SERVER together implies BOTH
what.add( WHAT_BOTH );
}
// there are three reasonable outcomes of the above logic:
// CLIENT
// SERVER
// BOTH, CLIENT, SERVER
Assertion.check(
what.contains( WHAT_CLIENT ) && !what.contains( WHAT_SERVER ) && !what.contains( WHAT_BOTH )
|| !what.contains( WHAT_CLIENT ) && what.contains( WHAT_SERVER ) && !what.contains( WHAT_BOTH )
|| what.contains( WHAT_CLIENT ) && what.contains( WHAT_SERVER ) && what.contains( WHAT_BOTH )
, "what files is {CLIENT}, {SERVER}, OR {CLIENT,SERVER,BOTH}" );
// files group
if (!what.contains( WHAT_ALL )
&& !what.contains( WHAT_NONE )
&& !what.contains( WHAT_INTF )
&& !what.contains( WHAT_MAIN )
&& !what.contains( WHAT_IMPL ))
{
// default to INTF if none of the file group is present.
what.add( WHAT_INTF );
}
if (what.contains( WHAT_ALL ))
{
what.add( WHAT_INTF );
what.add( WHAT_MAIN );
what.add( WHAT_IMPL );
what.remove( WHAT_ALL );
what.remove( WHAT_NONE );
}
if (what.contains( WHAT_NONE ))
{
what.remove( WHAT_INTF );
what.remove( WHAT_MAIN );
what.remove( WHAT_IMPL );
what.remove( WHAT_NONE );
}
return what;
}
private void checkOption( Set<String> what ) throws Exception
{
Set<String> w = new HashSet<String>( what );
// what direction group
w.remove( WHAT_CLIENT );
w.remove( WHAT_SERVER );
w.remove( WHAT_BOTH );
// what files group
w.remove( WHAT_ALL );
w.remove( WHAT_NONE );
w.remove( WHAT_INTF );
w.remove( WHAT_MAIN );
w.remove( WHAT_IMPL );
w.remove( WHAT_FORCE );
w.remove( WHAT_HELP );
if (!w.isEmpty())
throw new Exception(
"bad what option value(s): "+w
+"; specify what as one or more of "
+ whatHelp() );
}
private String whatHelp()
{
return WHAT_CLIENT
+", "
+WHAT_SERVER
+", "
+WHAT_BOTH
+", "
+WHAT_ALL
+", "
+WHAT_NONE
+", "
+WHAT_INTF
+", "
+WHAT_MAIN
+", "
+WHAT_IMPL
+", "
+WHAT_FORCE
+", "
+WHAT_HELP;
}
/**
* @param vname
* @return generic type vname into a binding appropriate
* vname.
*/
abstract public String mtvname( String vname );
/**
* @param vname
* @return generic field vname into a binding appropriate
* vname.
*/
abstract public String mfvname( String vname );
/**
* @return the lang being compiled.
*/
abstract public String getLang();
/**
* @param msg a Message with the async receiver property.
* @return the name of the async receiver thread pool name to
* use for this async receiver method.
*/
abstract public String asyncReceiverPoolName( Message msg );
/**
* @param type
* @param value
* @return the value properly formatted for the specified type.
* @throws IOException
* @throws Exception
*/
abstract public String getTypeValue( TypeRef type, Token value )
throws IOException, Exception;
/**
* @param n
* @param isExcept
* @return the properly formatted "toString()" format string.
* @throws ParseException
* @throws IOException
*/
abstract public String formatString( ParamList<Service> n,
boolean isExcept ) throws ParseException, IOException;
/**
* @param type
* @return type name appropriate for use as a structure
* element or exception parameter or function parameter
* or result.
*/
abstract public String getTypeName( TypeRef type );
/**
* @param type the etch type
* @return the fundamental native type for java. so etch
* int -> java int, while etch string -> java String. this
* is used when typing constants that don't want ref types.
*/
abstract public String getNativeTypeName( TypeRef type );
/**
* Returns the external fully qualified name for the enum. This might
* have to be modified over the normal fqname for the enum, which would
* be moduleName+'.'+serviceName+'.'+enumName.
* @param fqname
* @param moduleName
* @param serviceName
* @param enumName
* @return the external fqname.
*/
abstract public String enum_efqname( String fqname, String moduleName,
String serviceName, String enumName );
/**
* Returns the external fully qualified name for the enum. This might
* have to be modified over the normal fqname for the enum, which would
* be moduleName+'.'+serviceName+'.'+enumName.
* @param fqname
* @param moduleName
* @param serviceName
* @param enumName
* @return the external fqname.
*/
abstract public String struct_efqname( String fqname, String moduleName,
String serviceName, String enumName );
/**
* Returns the external fully qualified name for the enum. This might
* have to be modified over the normal fqname for the enum, which would
* be moduleName+'.'+serviceName+'.'+enumName.
* @param fqname
* @param moduleName
* @param serviceName
* @param enumName
* @return the external fqname.
*/
abstract public String except_efqname( String fqname, String moduleName,
String serviceName, String enumName );
/**
* @param intf the interface of the constant.
* @param name an id or qid which references a constant.
* @return the qualified external name.
*/
abstract public String qualifyConstantName( Service intf, Token name );
/**
* @param intf the interface of the enum.
* @param name an id or qid which references an enum.
* @return the qualified external name.
*/
abstract public String qualifyEnumName( Service intf, Token name );
/**
* @param name an id or qid which references a parameter of
* a method.
* @return the qualified parameter name.
*/
abstract public String qualifyParameterName( Token name );
/**
* @param named
* @return the validator for the parameter.
*/
abstract public String getValidator( Named<?> named );
/**
* Once a service is created, the binding may add default
* items to it such as standard types, externs, etc.
* @param service a newly created service, ready for
* defaults.
* @throws ParseException
*/
abstract public void addDefaults( Service service ) throws ParseException;
/**
* Adds an extern declaration to the service.
* @param service the server to be modified.
* @param name the name of the type of the extern.
* @param language the language binding of this specification.
* @param xname the binding specific name of the type.
* @param xnameImport the "import" of the type if needed.
* @param serializer the binding specific name of the import/export helper.
* @param serializerImport the "import" of the import/export helper if needed.
* @throws ParseException
*/
protected void addExtern( Service service, Name name, Token language,
Token xname, Token xnameImport, Token serializer,
Token serializerImport ) throws ParseException
{
Map<String, Opt> opts = new HashMap<String, Opt>();
opts.put( "Extern."+language.image, new Extern( name, language, xname,
xnameImport, serializer, serializerImport ) );
service.addExtern( name, opts );
}
/**
* Adds a built-in declaration to the service.
* @param service
* @param name
* @param bindingName
* @param allowSubclass
* @throws ParseException
*/
protected void addBuiltin( Service service, Name name, String bindingName,
boolean allowSubclass ) throws ParseException
{
service.addBuiltin( name, bindingName, allowSubclass );
}
/**
* Constructs a name (with associated Id token).
* @param name
* @return the Name with ID token.
*/
protected Name newName( String name )
{
Token t = newId( name );
return new Name( t, name );
}
/**
* Constructs an ID token.
* @param id
* @return the ID token.
*/
protected Token newId( String id )
{
return newToken( EtchGrammarConstants.ID, id );
}
/**
* Constructs a STR token.
* @param s
* @return the STR token.
*/
protected Token newString( String s )
{
return newToken( EtchGrammarConstants.STR, s );
}
/**
* Constructs a token of specified kind.
* @param kind
* @param image
* @return the token.
*/
protected Token newToken( int kind, String image )
{
Token t = new Token();
t.kind = kind;
t.image = image;
return t;
}
}