/*******************************************************************************
* ALMA - Atacama Large Millimeter Array
* Copyright (c) ESO - European Southern Observatory, 2011
* (in the framework of the ALMA collaboration).
* All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*******************************************************************************/
package org.jacorb.idl;
import java.io.PrintWriter;
import java.util.Enumeration;
import java.util.Set;
import java.util.logging.Level;
import alma.tools.idlgen.AcsXmlNamingExpert;
/**
* This class encapsulates printing of an {@link Interface} in the ACS-xml way.
* It assumes that the interface is already renamed to end in "J".
* <p>
* We (re-)use the jacorb package <code>org.jacorb.idl</code> to access protected methods,
* which is an attempt to not duplicate even more jacorb code.
* It is a pity that JacORB does not use a level of indirection when instantiating
* the Interface objects in CUP$actions, which would allow us to use a custom Interface type.
*
* @author hsommer
* @author jschwarz
*/
public class AcsInterfacePrinter
{
private final Interface interfce;
private final Set<AliasTypeSpec> entityTypes;
private final Set<StructType> xmlAwareStructs;
private final Set<Interface> xmlAwareIFs;
private final AcsXmlNamingExpert namingExpert;
public AcsInterfacePrinter(Interface interfce, final Set<AliasTypeSpec> entityTypes,
final Set<StructType> xmlAwareStructs, final Set<Interface> xmlAwareIFs, final AcsXmlNamingExpert namingExpert) {
this.interfce = interfce;
this.entityTypes = entityTypes;
this.xmlAwareStructs = xmlAwareStructs;
this.xmlAwareIFs = xmlAwareIFs;
this.namingExpert = namingExpert;
}
/**
* Todo:
* - Substitute types in operations parameters and returns
*
* The code was copied from {@link Interface#printOperations()}
* and then modified to not append "Operations"
* (plus "interfce." scope resolution to fix compile errors)
*/
public void printAcsJInterface() {
PrintWriter ps = interfce.openOutput(interfce.name);
if (ps == null)
{
return;
}
interfce.printPackage(ps);
interfce.printSuperclassImports(ps);
interfce.printImport(ps);
// TODO: Print ACS comment
interfce.printClassComment("interface", interfce.name, ps);
ps.println("public interface " + interfce.name);
if (interfce.inheritanceSpec.v.size() > 0)
{
ps.print("\textends ");
Enumeration e = interfce.inheritanceSpec.v.elements();
do
{
ScopedName sne = (ScopedName) e.nextElement();
// See description of abstractInterfaces for logic here.
if (interfce.abstractInterfaces != null &&
interfce.abstractInterfaces.contains(sne.toString()))
{
ps.print(sne);
}
else
{
if (sne.resolvedTypeSpec() instanceof ReplyHandlerTypeSpec && parser.generate_ami_callback)
{
ps.print(sne + "Operations");
}
else
{
ConstrTypeSpec ts = unwindTypedefs(sne);
ps.print(ts + "Operations");
}
}
if (e.hasMoreElements())
{
ps.print(" , ");
}
}
while (e.hasMoreElements());
ps.print(Environment.NL);
}
ps.println("{");
if (interfce.body != null)
{
// forward declaration
interfce.body.printConstants(ps);
// ACS hack
// interfce.body.printOperationSignatures(ps);
printOperationSignatures(interfce.body, ps);
// end ACS hack
}
ps.println("}");
ps.close();
}
/**
* Copied from {@link Interface#unwindTypedefs}, without ACS changes except "interfce." resolution.
* @param scopedName
* @return
*/
private ConstrTypeSpec unwindTypedefs(ScopedName scopedName)
{
TypeSpec resolvedTSpec = scopedName.resolvedTypeSpec();
//unwind any typedefs
while (resolvedTSpec instanceof AliasTypeSpec )
{
resolvedTSpec =
((AliasTypeSpec)resolvedTSpec).originalType();
}
if (! (resolvedTSpec instanceof ConstrTypeSpec))
{
if (interfce.logger.isLoggable(Level.FINE)) {
interfce.logger.fine("Illegal inheritance spec in Interface.unwindTypeDefs, not a constr. type but " +
resolvedTSpec.getClass() + ", name " + scopedName );
}
parser.fatal_error("Illegal inheritance spec in Interface.unwindTypeDefs (not a constr. type): " +
interfce.inheritanceSpec, interfce.token);
}
return (ConstrTypeSpec) resolvedTSpec;
}
/**
* Copied from {@link InterfaceBody#printOperationSignatures(PrintWriter)}.
* We probably don't need this if we can modify the OpDecl objects before generating interface code.
* @param ps
*/
void printOperationSignatures(InterfaceBody ifb, PrintWriter ps )
{
if( ifb.v.size() > 0 )
{
ps.println( "\t/* operations */" );
}
for( Enumeration<Definition> e = ifb.v.elements(); e.hasMoreElements(); )
{
Definition d = e.nextElement();
if( d.get_declaration() instanceof OpDecl )
{
// ( (OpDecl)d.get_declaration() ).printSignature( ps );
OpDecl opdecl = (OpDecl)d.get_declaration();
printSignature( ps, opdecl );
}
else if( d.get_declaration() instanceof AttrDecl )
{
for( Enumeration m = ( (AttrDecl)d.get_declaration() ).getOperations();
m.hasMoreElements(); )
{
( (Operation)m.nextElement() ).printSignature( ps );
}
}
}
}
public void printSignature( PrintWriter ps, OpDecl opdecl )
{
printSignature( ps, false, opdecl );
}
/**
* @param printModifiers whether "public abstract" should be added
*/
public void printSignature( PrintWriter ps, boolean printModifiers, OpDecl opdecl )
{
ps.print( "\t" );
if( printModifiers )
ps.print( "public abstract " );
else
ps.print("public ");
TypeSpec ts = opdecl.opTypeSpec;
sanitizeOpNames(ps, opdecl, ts);
for( Enumeration e = opdecl.paramDecls.elements(); e.hasMoreElements(); )
{
printParam( ps, (ParamDecl)e.nextElement() );
if( e.hasMoreElements() ) ps.print( ", " );
}
ps.print( ")" );
opdecl.raisesExpr.print( ps );
ps.println( ";" );
}
private void sanitizeOpNames(PrintWriter ps, OpDecl opdecl, TypeSpec ts) {
String acsTypeName = namingExpert.getAcsTypeName(ts, entityTypes, xmlAwareStructs, xmlAwareIFs);
ps.print(acsTypeName + " " + opdecl.name + "(");
}
private void printParam( PrintWriter ps, ParamDecl pdecl )
{
TypeSpec ts = pdecl.paramTypeSpec;
switch( pdecl.paramAttribute )
{
case ParamDecl.MODE_IN:
// The following line is copied from AcsStructPrinter to fix the type used for struct parameters,
// where the trailing J was missing in case of xml-aware structs.
// This change also fixes an issue with forward declared interfaces as we had it
// in XmlOffshootReferencingOffshootJ from xmltest.idl.
String acsTypeName = namingExpert.getAcsTypeName(ts, entityTypes, xmlAwareStructs, xmlAwareIFs);
ps.print(acsTypeName + " " + pdecl.name);
break;
case ParamDecl.MODE_OUT:
case ParamDecl.MODE_INOUT:
if (pdecl.paramTypeSpec instanceof AliasTypeSpec && entityTypes.contains(pdecl.paramTypeSpec)) {
AliasTypeSpec alias = (AliasTypeSpec)pdecl.paramTypeSpec;
String theHolderName = namingExpert.getHolderClassNameForXmlTypedef(alias);
ps.print(theHolderName);
} else if (pdecl.paramTypeSpec instanceof ConstrTypeSpec
&& ((ConstrTypeSpec)pdecl.paramTypeSpec).c_type_spec instanceof StructType
&& xmlAwareStructs.contains(((ConstrTypeSpec)pdecl.paramTypeSpec).c_type_spec)){
StructType stype = (StructType)(((ConstrTypeSpec)pdecl.paramTypeSpec).c_type_spec);
ps.print(namingExpert.getJavaPackageForStruct(stype)+"."+
namingExpert.getHolderClassNameForStruct(stype));
} else
ps.print( pdecl.paramTypeSpec.holderName() ); // TODO replace & merge w/rest of if stmt
break;
}
ps.print(" ");
ps.print(pdecl.simple_declarator);
}
/**
* Exposes {@link Interface#javaName()} to other packages,
* to be used by {@link AcsXmlNamingExpert}.
*/
public static String java_name(Interface interfce) {
return interfce.javaName();
}
}