blob: 4204277978ff50419472ea6057e83c80d9ad0c5a [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.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import org.apache.etch.compiler.ast.Constant;
import org.apache.etch.compiler.ast.Enumx;
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.Module;
import org.apache.etch.compiler.ast.Name;
import org.apache.etch.compiler.ast.Opt;
import org.apache.etch.compiler.ast.ParamList;
import org.apache.etch.compiler.ast.Parameter;
import org.apache.etch.compiler.ast.Service;
import org.apache.etch.compiler.ast.Struct;
import org.apache.etch.compiler.ast.Thrown;
/**
* Compiler support routines.
*/
public class EtchHelper
{
// /** TODO static global variable lh in compiler needs to go away. */
// public static LogHandler lh;
// /** TODO static global variable cl in compiler needs to go away. */
// public static CmdLineOptions cl;
/**
* Parses and saves a module comment.
* @param m
* @param specialToken
*/
protected static void moduleComment( Module m, Token specialToken )
{
if (specialToken == null)
return;
List<String> list = deComment( specialToken.image );
if (list == null)
return;
m.setDescr( list );
}
/**
* Parses and saves a service comment.
* @param s
* @param specialToken
*/
protected static void serviceComment( Service s, Token specialToken )
{
if (specialToken == null)
return;
List<String> list = deComment( specialToken.image );
if (list == null)
return;
s.setDescr( list );
}
/**
* Processes the formal comment for a constant.
* @param c
* @param specialToken
*/
protected static void constantComment( Constant c, Token specialToken )
{
if (specialToken == null)
return;
List<String> list = deComment( specialToken.image );
if (list == null)
return;
c.setDescr( list );
}
/**
* Processes the formal comment for an enum.
* @param e
* @param specialToken
*/
protected static void enumComment( Enumx e, Token specialToken )
{
if (specialToken == null)
return;
List<String> list = deComment( specialToken.image );
if (list == null)
return;
// TODO if the items are described in @item tags here
// add that to the appropriate item. This will look
// alot like paramListComment.
e.setDescr( list );
}
/**
* Processes the formal comment for an enum item.
* @param i
* @param specialToken
*/
protected static void itemComment( Item i, Token specialToken )
{
if (specialToken == null)
return;
List<String> list = deComment( specialToken.image );
if (list == null)
return;
i.setDescr( list );
}
/**
* Processes the formal comment for a parameter of a struct, exception,
* or message.
* @param p
* @param specialToken
*/
protected static void parameterComment( Parameter p, Token specialToken )
{
if (specialToken == null)
return;
List<String> list = deComment( specialToken.image );
if (list == null)
return;
p.setDescr( list );
}
/**
* Processes the formal comment for a thrown exception.
* @param t
* @param specialToken
*/
protected static void thrownComment( Thrown t, Token specialToken )
{
if (specialToken == null)
return;
List<String> list = deComment( specialToken.image );
if (list == null)
return;
t.setDescr( list );
}
/**
* Processes the formal comment for a struct.
* @param s
* @param specialToken
*/
protected static void structComment( Struct s, Token specialToken )
{
paramListComment( s, specialToken, false );
}
/**
* Processes the formal comment for an exception.
* @param e
* @param specialToken
*/
protected static void exceptComment( Except e, Token specialToken )
{
paramListComment( e, specialToken, false );
}
/**
* Processes the formal comment for a message.
* @param m
* @param specialToken
*/
protected static void messageComment( Message m, Token specialToken )
{
paramListComment( m, specialToken, true );
}
/**
* @param t
* @throws IOException
*/
protected static void fixString( Token t ) throws ParseException
{
if (t.kind != EtchGrammarConstants.STR)
return;
try
{
t.image = unescapeStr( t.image );
}
catch ( IOException e)
{
throw new ParseException( String.format(
"Problem with string '%s' at line %d", e.getMessage(), t.beginLine ) );
}
}
private static String unescapeStr( String s ) throws IOException
{
// remove the quotes.
if (!s.startsWith( "\"" ) || !s.endsWith( "\"" ))
throw new IllegalArgumentException( "string image is not quoted" );
s = s.substring( 1, s.length()-1 );
StringReader rdr = new StringReader( s );
StringBuffer sb = new StringBuffer();
int c;
while ((c = rdr.read()) >= 0)
{
if (c != '\\')
{
sb.append( (char) c );
continue;
}
// c == '\\': two escape formats: uxxxx and k (k=["\trn])
c = rdr.read();
if (c == 'u')
{
// unicode escape (need for hex chars)
c = fromHex( rdr.read() );
// System.out.println( "c1: "+c );
c = (c << 4) + fromHex( rdr.read() );
// System.out.println( "c2: "+c );
c = (c << 4) + fromHex( rdr.read() );
// System.out.println( "c3: "+c );
c = (c << 4) + fromHex( rdr.read() );
// System.out.println( "c4: "+c );
sb.append( (char) c );
}
else switch (c)
{
// plain escape
case '"': sb.append( "\"" ); break;
case '\\': sb.append( "\\" ); break;
case 't': sb.append( "\t" ); break;
case 'r': sb.append( "\r" ); break;
case 'n': sb.append( "\n" ); break;
default: throw new IOException( "bad escape char: "+c );
}
}
return sb.toString();
}
private static int fromHex( int c ) throws IOException
{
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
throw new IOException( "bad hex char: "+c );
}
/**
* Formal comment types (tags).
*/
enum Type
{
/**
* The main body of the comment before any tags.
*/
Main,
/**
* The type denoting an @param tag.
*/
Param,
/**
* The type denoting an @return tag.
*/
Return,
/**
* The type denoting an @throws tag.
*/
Throws,
/**
* The type denoting an unrecognized tag.
*/
None
}
/**
* Processes any saved message comment.
* @param m
*/
private static void paramListComment( ParamList<Service> m,
Token specialToken, boolean isMessage )
{
if (specialToken == null)
return;
List<String> list = deComment( specialToken.image );
if (list == null)
return;
// System.out.println( list );
Type type = Type.Main;
String name = null;
for (String s: list)
{
String x = s;
// System.out.printf( "processing: %s\n", s );
if (s.startsWith( "@" ))
{
StringTokenizer st = new StringTokenizer( s, " \t" );
String k = st.nextToken();
if (k.equals( "@param" ))
{
type = Type.Param;
if (!st.hasMoreTokens())
{
System.out.printf( "*** warning: parameter name missing on @param at line %d\n", specialToken.beginLine );
return;
}
name = st.nextToken();
if (st.hasMoreTokens())
s = st.nextToken( "\r\n" ).trim();
else
s = "";
}
else if (k.equals( "@return" ))
{
type = Type.Return;
if (!isMessage || !((Message) m).hasReturn())
{
System.out.printf( "*** warning: @return used on non-message or void message at line %d\n", specialToken.beginLine );
return;
}
if (st.hasMoreTokens())
s = st.nextToken( "\r\n" ).trim();
else
s = "";
}
else if (k.equals( "@throws" ))
{
type = Type.Throws;
if (!isMessage)
{
System.out.printf( "*** warning: @throws used on non-message at line %d\n", specialToken.beginLine );
return;
}
if (!st.hasMoreTokens())
{
System.out.printf( "*** warning: exception name missing on @throws at line %d\n", specialToken.beginLine );
return;
}
name = st.nextToken();
if (st.hasMoreTokens())
s = st.nextToken( "\r\n" ).trim();
else
s = "";
}
else
{
type = Type.None;
System.out.printf( "*** warning: %s not understood in this context (%s): %s\n", k, m.name(), x );
}
}
// System.out.printf( "type %d\n", type );
if (type == Type.Main)
{
//System.out.printf( "adding to m.descr: %s\n", s );
m.descr().add( s );
}
else if (type == Type.Param)
{
// System.out.printf( "adding to p.descr (%s): %s\n", name, s );
Parameter p = m.getParameter( name );
if (p != null)
p.descr().add( s );
else
System.out.printf( "*** warning: cannot document parameter %s of %s\n", name, m.name() );
}
else if (type == Type.Return)
{
// System.out.printf( "adding to m.returnDescr: %s\n", s );
((Message) m).returnDescr().add( s );
}
else if (type == Type.Throws)
{
// System.out.printf( "adding to t.descr (%s): %s\n", name, s );
Thrown t = ((Message) m).getThrown( name );
if (t != null)
t.descr().add( s );
else
System.out.printf( "*** warning: cannot document thrown %s of %s\n", name, m.name() );
}
else if (type != Type.None)
{
System.out.printf( "*** warning: comment type %s not understood\n", type );
}
}
}
/**
* Splits a formal comment into its lines, stripping
* off the comment gunk.
* @param s
* @return an array of lines of the comment, without
* the EOL characters and the comment gunk.
*/
protected static List<String> deComment( String s )
{
if (!s.startsWith( "/** " ) &&
!s.startsWith( "/**\t" ) &&
!s.startsWith( "/**\r" ) &&
!s.startsWith( "/**\n" ))
return null;
s = s.substring( 3 );
if (s.endsWith( "*/" ))
s = s.substring( 0, s.length() - 2 );
s = s.trim();
// now we have a bunch of lines that look like this:
// .*(\r\n\w[*](\W.*)?)*
// undo all that by chopping up into lines, then removing
// leading white space up to the * and then the space directly
// after the * if there is one.
List<String> list = new ArrayList<String>();
StringTokenizer st = new StringTokenizer( s, "\r\n", true );
while (st.hasMoreTokens())
{
String t = st.nextToken();
if (t.equals( "\r" ))
{
// ignore the EOL marks.
}
else if (t.equals( "\n" ))
{
// ignore the EOL marks.
}
else
// not an EOL marker...
{
t = t.trim();
// this is a line that looks like "*blah" or
// "* blah" or "*".
if (t.startsWith( "* " ))
list.add( t.substring( 2 ) );
else if (t.startsWith( "*" ))
list.add( t.substring( 1 ) );
else
list.add( t );
}
}
return list;
}
/**
* @return the last saved comment.
*/
protected Token getComment()
{
Token comment = savedComment;
savedComment = null;
return comment;
}
private Token savedComment;
/**
* Saves a comment to be used later. Only the
* most recently saved comment will be available.
* @param comment
*/
protected void saveComment( Token comment )
{
if (comment != null)
savedComment = comment;
}
/**
* @param n the name of the opt.
* @param list the list of opt arguments.
* @return an Opt subclass
* @throws ParseException
*/
protected Opt makeOpt( Name n, List<Token> list ) throws ParseException
{
String cn = "org.apache.etch.compiler.opt."+n.token.image;
Class<?> c;
try
{
c = Class.forName( cn );
}
catch ( NoClassDefFoundError e )
{
throw new ParseException( String.format(
"option class %s not found at line %d",
n, n.token.beginLine ) );
}
catch ( ClassNotFoundException e )
{
throw new ParseException( String.format(
"option class %s not found at line %d",
n, n.token.beginLine ) );
}
Class<?>[] params = { Name.class, Token[].class };
Constructor<?> k;
try
{
k = c.getConstructor( params );
}
catch ( SecurityException e )
{
throw new ParseException( String.format(
"security rules block access to the constructor of option class %s at line %d",
n, n.token.beginLine ) );
}
catch ( NoSuchMethodException e )
{
throw new ParseException( String.format(
"missing constructor for option class %s at line %d",
n, n.token.beginLine ) );
}
Object[] args = { n, list.toArray( new Token[list.size()] ) };
try
{
return (Opt) k.newInstance( args );
}
catch ( IllegalArgumentException e )
{
throw new ParseException( String.format(
"illegal argument to newInstance for option class %s at line %d: %s",
n, n.token.beginLine, e ) );
}
catch ( InstantiationException e )
{
throw new ParseException( String.format(
"cannot instantiate option class %s at line %d: %s",
n, n.token.beginLine, e ) );
}
catch ( IllegalAccessException e )
{
throw new ParseException( String.format(
"illegal access of constructor for option class %s at line %d: %s",
n, n.token.beginLine, e ) );
}
catch ( InvocationTargetException e )
{
if (e.getTargetException() instanceof ParseException)
throw (ParseException) e.getTargetException();
throw new ParseException( String.format(
"exception caught from constructor for option class %s at line %d: %s",
n, n.token.beginLine, e.getTargetException() ) );
}
}
protected void doStartService()
{
}
protected void doEndService(Service oServ)
{
}
/**
* @param f name of the filename for the include.
*/
protected void doInclude( Token fileName, Service servobj) throws ParseException
{
//Get the Gramar Obj
EtchGrammar oGramar = (EtchGrammar) this;
servobj.getCmdLineOptions().lh.push( fileName.toString(), fileName.beginLine );
//System.out.println("------Start Do Include-------");
//Get the file name
String oStr = fileName.toString().replaceAll("\"", "");
// System.out.println("Including " + oStr);
servobj.getCmdLineOptions().lh.report( LogHandler.LEVEL_INFO, "Including %s", oStr );
//Create the input string and read it in
InputStream oStream = null;
// Search the etch path for the file
for (File f : servobj.getCmdLineOptions().effectiveIncludePath)
{
try
{
oStream = new java.io.FileInputStream(new File(f, oStr));
break;
}
catch (FileNotFoundException e)
{
}
}
if (oStream == null)
throw new ParseException(" Included File " + oStr + " Not Found\n");
//Keep for debugging
//StackInputStream oStStream = new StackInputStream(oStream);
SimpleCharStream curStream = null;
try
{
curStream = new SimpleCharStream(oStream, null, 1, 1);
}
catch ( UnsupportedEncodingException e )
{
throw new ParseException(" Included File " + oStr + " is not correctly encoded\n");
}
//Create a new token manager with the new include stream
EtchGrammarTokenManager oTM = new EtchGrammarTokenManager(curStream);
//Keep the old token manager
EtchGrammarTokenManager oOldTM = oGramar.token_source;
//Keep the old token
Token token = oGramar.token;
//reinit with the new token manager
oGramar.ReInit( oTM );
//Proc the stream
oGramar.stmts( servobj );
//Put the old data back
oGramar.ReInit( oOldTM );
oGramar.token = token;
servobj.getCmdLineOptions().lh.pop( fileName.toString() );
//System.out.println("------End Do Include---------");
}
}