blob: 0a110fc6048a54cd046d5eb5f6bff2f0d901f1ac [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.opt;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import org.apache.etch.compiler.EtchGrammarConstants;
import org.apache.etch.compiler.ParseException;
import org.apache.etch.compiler.Token;
import org.apache.etch.compiler.ast.Except;
import org.apache.etch.compiler.ast.Name;
import org.apache.etch.compiler.ast.Opt;
import org.apache.etch.compiler.ast.Struct;
/**
* Option which sets the string format of a struct or exception.
*/
public class ToString extends Opt
{
/**
* Constructs the ToString.
*
* @param name
* @param args
* @throws ParseException
*/
public ToString( Name name, Token... args ) throws ParseException
{
super( name.name, name.token.beginLine );
addType( Except.class );
addType( Struct.class );
if (args.length != 1)
throw new ParseException( String.format(
"ToString accepts one string argument at line %d",
name.token.beginLine ) );
Token arg = args[0];
if (arg.kind != EtchGrammarConstants.STR)
throw new ParseException( String.format(
"ToString accepts one string argument at line %d",
arg.beginLine ) );
try
{
format = parse( arg.image );
}
catch ( IOException e )
{
throw new ParseException( String.format(
"ToString: problem with format string at line %d: %s",
arg.beginLine, e.getMessage() ) );
}
}
private final List<FmtItem> format;
@Override
public void dump( String indent )
{
System.out.printf( "%s@ToString( %s )\n", indent, format );
}
@Override
public String toString()
{
return String.format( "@ToString( %s )", format );
}
/**
* @return the format for the ToString.
*/
public List<FmtItem> getFormat()
{
return format;
}
private static List<FmtItem> parse( String s ) throws IOException
{
if (s.startsWith( "\"" ))
s = s.substring( 1 );
if (s.endsWith( "\"" ))
s = s.substring( 0, s.length()-1 );
List<FmtItem> list = new ArrayList<FmtItem>();
StringReader rdr = new StringReader( s );
while (stringItem( list, rdr ) && fieldItem( list, rdr ))
{
// nothing else to do.
}
return list;
}
private static boolean stringItem( List<FmtItem> list, StringReader rdr )
throws IOException
{
StringBuffer sb = new StringBuffer();
int c;
boolean escape = false;
while ((c = rdr.read()) >= 0)
{
if (!escape)
{
if (c == '{')
{
// unescaped brace is start of format item.
break;
}
if (c == '\\')
{
// unescaped backslash is start of escape
escape = true;
continue;
}
}
else
{
if (c == 't') c = '\t';
else if (c == 'r') c = '\r';
else if (c == 'n') c = '\n';
else if (c == '{') c = '{';
else if (c == '\\') c = '\\';
else throw new IOException( "bad escape. expecting one of t, r, n, {, or \\. got '"+(char) c+"'" );
escape = false;
}
sb.append( (char) c );
}
if (escape)
throw new IOException( "partial escape at end of string" );
if (sb.length() > 0)
list.add( new StringItem( sb.toString() ) );
return c >= 0;
}
private static boolean fieldItem( List<FmtItem> list, StringReader rdr )
throws IOException
{
StringBuffer sb = new StringBuffer();
int c;
while ((c = rdr.read()) >= 0)
{
if (c == '}')
break;
sb.append( (char) c );
}
if (c != '}')
throw new IOException( "unclosed field item at end of string" );
if (sb.length() > 0)
list.add( new FieldItem( sb.toString() ) );
return c >= 0;
}
/**
* A format item.
*/
public interface FmtItem
{
/**
* @return the value of the format item.
*/
public String value();
}
/**
* A string format item.
*/
public static class StringItem implements FmtItem
{
/**
* Constructs the string format item.
* @param s the string format item.
*/
public StringItem( String s )
{
this.s = s;
}
private final String s;
@Override
public String toString()
{
return "String: '"+s+"'";
}
public String value()
{
return s;
}
}
/**
* A field format item.
*/
public static class FieldItem implements FmtItem
{
/**
* Constructs the field format item.
* @param s the name of the field.
*/
public FieldItem( String s )
{
this.s = s;
}
private final String s;
@Override
public String toString()
{
return "Field: "+s;
}
public String value()
{
return s;
}
}
}