blob: 9918d4580b6cd6bbcf51ade7259cdd3d7272a1e0 [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.ast;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.etch.compiler.Backend;
import org.apache.etch.compiler.EtchCompiler;
import org.apache.etch.compiler.EtchGrammarConstants;
import org.apache.etch.compiler.ParseException;
import org.apache.etch.compiler.Token;
/**
* Declaration of a service from the etch idl.
*/
public class Service extends Named<Module> implements OptList, Iterable<Named<?>>
{
/**
* Constructs the service.
*
* @param module
* @param name
* @param opts
*/
public Service( Module module, Name name, Map<String, Opt> opts )
{
super( module, name, opts );
}
@Override
public String vname( Backend helper )
{
return null;
}
@Override
public String fqname()
{
return parent().fqname() + '.' + name().name;
}
// @Override
// public Integer id()
// {
// return null;
// }
/**
* Adds a mixin declaration.
*
* @param n
* @param nOpts
* @return the added mixin.
* @throws ParseException
*/
public Mixin addMixin( Name n, Map<String, Opt> nOpts )
throws ParseException
{
optsCheck( Mixin.class, nOpts );
try
{
Module module = EtchCompiler.parseModule( this, n, nOpts );
if (module == null)
throw new ParseException( String.format(
"could not find mixin '%s' at line %d",
n.name, n.token.beginLine ) );
Mixin mixin = new Mixin( this, n, nOpts, module );
nameList.add( n, mixin );
return mixin;
}
catch ( ParseException e )
{
throw e;
}
catch ( Exception e )
{
throw new ParseException( String.format(
"could not find mixin '%s' at line %d: %s",
n.name, n.token.beginLine, e ) );
}
}
/**
* Adds a constant declaration.
* @param n
* @param nOpts
* @param tr
* @param value
* @return the added constant.
* @throws ParseException
*/
public Constant addConstant( Name n, Map<String, Opt> nOpts, TypeRef tr, Token value ) throws ParseException
{
nameList.check( n );
optsCheck( Constant.class, nOpts );
Constant c = new Constant( this, n, nOpts, tr, value );
nameList.add( n, c );
return c;
}
private void optsCheck( Class<?> type, Map<String, Opt> nOpts ) throws ParseException
{
for (Opt opt: nOpts.values())
optCheck( type, opt );
}
private void optCheck( Class<?> type, Opt opt ) throws ParseException
{
opt.check( type );
}
/**
* Adds an enumeration declaration.
* @param n
* @param nOpts
* @return the added enumx.
* @throws ParseException
*/
public Enumx addEnum( Name n, Map<String, Opt> nOpts ) throws ParseException
{
nameList.check( n );
optsCheck( Enumx.class, nOpts );
Enumx e = new Enumx( this, n, nOpts );
nameList.add( n, e );
return e;
}
/**
* Adds a struct declaration.
* @param n
* @param nOpts
* @return the added struct.
* @throws ParseException
*/
public Struct addStruct( Name n, Map<String, Opt> nOpts ) throws ParseException
{
nameList.check( n );
optsCheck( Struct.class, nOpts );
Struct s = new Struct( this, n, nOpts );
nameList.add( n, s );
return s;
}
/**
* Adds an extern declaration.
* @param n
* @param nOpts
* @return the added extern.
* @throws ParseException
*/
public Extern addExtern( Name n, Map<String, Opt> nOpts ) throws ParseException
{
nameList.check( n );
optsCheck( Extern.class, nOpts );
Extern s = new Extern( this, n, nOpts );
nameList.add( n, s );
return s;
}
/**
* Adds a built-in declaration.
* @param n
* @param bindingName
* @param allowSubclass
* @return the Builtin that was added.
* @throws ParseException
*/
public Builtin addBuiltin( Name n, String bindingName, boolean allowSubclass )
throws ParseException
{
nameList.check( n );
Builtin b = new Builtin( this, n, bindingName, allowSubclass );
nameList.add( n, b );
return b;
}
/**
* Adds an exception declaration.
* @param n
* @param nOpts
* @return the added exception.
* @throws ParseException
*/
public Except addExcept( Name n, Map<String, Opt> nOpts ) throws ParseException
{
nameList.check( n );
optsCheck( Except.class, nOpts );
Except e = new Except( this, n, nOpts );
nameList.add( n, e );
return e;
}
/**
* Adds a message declaration.
* @param n
* @param rt
* @param nOpts
* @return the added message.
* @throws ParseException
*/
public Message addMessage( Name n, Map<String, Opt> nOpts, TypeRef rt ) throws ParseException
{
nameList.check( n );
optsCheck( Message.class, nOpts );
Message m = new Message( this, n, nOpts, rt );
nameList.add( n, m );
if (!m.isOneway())
addResultMessage( m );
else if (m.hasReturn())
throw new ParseException( String.format(
"Oneway message cannot have a non-void return type at line %d", n.token.beginLine ) );
return m;
}
/**
* @param reqMsg
* @throws ParseException
*/
public void addResultMessage( Message reqMsg ) throws ParseException
{
if (reqMsg.getResultMessage() != null)
return;
Name rmName = reqMsg.getResultMessageName();
Token rmType = new Token();
rmType.kind = EtchGrammarConstants.VOID;
rmType.image = "void";
TypeRef rmTypeRef = new TypeRef( this, rmType );
nameList.check( rmName );
Message rm = new Message( this, rmName, new HashMap<String, Opt>(),
rmTypeRef );
rm.setMsgDir( getResultMessageDirection( reqMsg.msgDir() ) );
nameList.add( rmName, rm );
rm.addParameter( reqMsg.getResultParamName(), reqMsg.type() );
reqMsg.setResultMessage( rm );
rm.setRequestMessage( reqMsg );
}
/**
* @param md
* @return message direction opposite to the supplied message direction.
*/
public MessageDirection getResultMessageDirection( MessageDirection md )
{
switch (md)
{
case CLIENT: return MessageDirection.SERVER;
case SERVER: return MessageDirection.CLIENT;
case BOTH: return MessageDirection.BOTH;
default: throw new IllegalArgumentException( "unknown message direction "+md );
}
}
public Iterator<Named<?>> iterator()
{
return nameList.iterator();
}
/**
* @param recursive if true, recurse into mixed in services, too.
* @return an iterator over all the methods of this service.
*/
public Iterator<Named<?>> messages( boolean recursive )
{
List<Named<?>> list = new ArrayList<Named<?>>();
getMessages( list, recursive );
return list.iterator();
}
private void getMessages( List<Named<?>> list, boolean recursive )
{
for (Named<?> n: nameList)
{
if (n.isMessage())
list.add( n );
else if (recursive && n.isMixin())
((Mixin) n).getModule().iterator().next().getMessages( list, recursive );
}
}
/**
* @param recursive if true, recurse into mixed in services, too
* @return an iterator over all the structs of this service
*/
public Iterator<Named<?>> structs( boolean recursive )
{
List<Named<?>> list = new ArrayList<Named<?>>();
getStructs( list, recursive );
return list.iterator();
}
private void getStructs( List<Named<?>> list, boolean recursive )
{
for ( Named<?> n: nameList )
{
if ( n.isStruct() )
list.add(n);
else if ( recursive && n.isMixin() )
((Mixin)n).getModule().iterator().next().getStructs( list, recursive );
}
}
/**
* @param recursive if true, recurse into mixed in services, too
* @return an iterator over all the enums of this service
*/
public Iterator<Named<?>> enums( boolean recursive )
{
List<Named<?>> list = new ArrayList<Named<?>>();
getEnums( list, recursive );
return list.iterator();
}
private void getEnums( List<Named<?>> list, boolean recursive )
{
for ( Named<?> n: nameList )
{
if ( n.isEnumx() )
list.add(n);
else if ( recursive && n.isMixin() )
((Mixin)n).getModule().iterator().next().getEnums( list, recursive );
}
}
/**
* @param recursive if true, recurse into mixed in services, too
* @return an iterator over all the externs of this service
*/
public Iterator<Named<?>> externs( boolean recursive )
{
List<Named<?>> list = new ArrayList<Named<?>>();
getExterns( list, recursive );
return list.iterator();
}
private void getExterns( List<Named<?>> list, boolean recursive )
{
for ( Named<?> n: nameList )
{
if ( n.isExtern() )
list.add(n);
else if ( recursive && n.isMixin() )
((Mixin)n).getModule().iterator().next().getExterns( list, recursive );
}
}
/**
* @param recursive if true, recurse into mixed in services, too
* @return an iterator over all the constants of this service
*/
public Iterator<Named<?>> constants( boolean recursive )
{
List<Named<?>> list = new ArrayList<Named<?>>();
getConstants( list, recursive );
return list.iterator();
}
private void getConstants( List<Named<?>> list, boolean recursive )
{
for ( Named<?> n: nameList )
{
if ( n.isConstant() )
list.add(n);
else if ( recursive && n.isMixin() )
((Mixin)n).getModule().iterator().next().getConstants( list, recursive );
}
}
/**
* @param recursive if true, recurse into mixed in services, too
* @return an iterator over all the exceptions of this service
*/
public Iterator<Named<?>> exceptions( boolean recursive )
{
List<Named<?>> list = new ArrayList<Named<?>>();
getExceptions( list, recursive );
return list.iterator();
}
private void getExceptions( List<Named<?>> list, boolean recursive )
{
for ( Named<?> n: nameList )
{
if ( n.isExcept() )
list.add(n);
else if ( recursive && n.isMixin() )
((Mixin)n).getModule().iterator().next().getExceptions( list, recursive );
}
}
private final NameList<Named<?>> nameList = new NameList<Named<?>>();
/**
* @param mc
* @return true if some of that type exists.
*/
public boolean hasMessageDirection( MessageDirection mc )
{
for (Named<?> n: this)
{
//System.out.printf( "hasMessageDirection (%s): %s\n", mc, n );
if (!(n instanceof Message))
{
if (mc == MessageDirection.BOTH)
return true;
continue;
}
Message m = (Message) n;
if (m.msgDir() == mc && !m.isHidden())
return true;
}
return false;
}
@Override
public void dump( String indent )
{
System.out.printf( "%sservice %s\n", indent, name() );
String sIndent = indent+" ";
for (String s: descr())
System.out.printf( "%sdescr %s\n", sIndent, s );
for (Opt o: opts())
o.dump( sIndent );
for (Named<?> m: this)
m.dump( sIndent );
}
@Override
public void check() throws ParseException
{
checkTypeName( name() );
for (Named<?> n: this)
n.check();
}
/**
* Gets the named thing from the service.
* @param name
* @return the named thing, or null.
*/
public Named<?> get( String name )
{
List<Named<?>> list = new ArrayList<Named<?>>();
Named<?> named = nameList.get( name );
if (named != null)
{
/*System.out.println( "resolved to "+named );
return named;*/
list.add(named);
}
if (isQualifiedName( name ))
{
String x = getSimpleName( name );
// System.out.println( "trying "+x );
named = nameList.get( x );
if (named != null)
{
String y = named.fqname();
if (name.equals( y ))
{
list.add(named);
}
}
}
for (Named<?> n : this)
{
if (n.isMixin())
{
named = ((Mixin) n).getModule().iterator().next().get( name );
if (named != null)
// return named;
{
boolean exists = false;
Iterator<Named<?>> it = list.iterator();
while ( it.hasNext())
{
if ( ((Named<?>)it.next()).fqname().equals( named.fqname() ) )
{
exists = true;
}
}
if ( !exists )
list.add(named);
}
}
}
if (list.size() == 1 )
return list.get( 0 );
return null;
}
private boolean isQualifiedName( String name )
{
return name.lastIndexOf( '.' ) >= 0;
}
private String getSimpleName( String name )
{
return name.substring( name.lastIndexOf( '.' )+1 );
}
/**
* @param walker called with each and every node under this one.
*/
@Override
public void treewalk( TreeWalker walker ) throws ParseException
{
walker.preService( this );
for (Named<?> n: this)
n.treewalk( walker );
walker.postService( this );
}
}