/* * ALMA - Atacama Large Millimiter Array * (c) European Southern Observatory, 2002 * Copyright by ESO (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 alma.tools.idlgen; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.jacorb.idl.AcsAdapterForOldJacorb; import org.jacorb.idl.AcsInterfacePrinter; import org.jacorb.idl.AcsJacorbParser; import org.jacorb.idl.AcsStructPrinter; import org.jacorb.idl.AcsTypedefPrinter; import org.jacorb.idl.AliasTypeSpec; import org.jacorb.idl.Interface; import org.jacorb.idl.StructType; import alma.tools.idlgen.comphelpgen.ComponentHelperGeneratorProxy; /** * TODO: Update for JacORB!! * * Main class of the ACS IDL compiler that produces interfaces, structs, and holders * for use with XML binding classes. * The generated code will be similar but different from the output of a regular CORBA IDL compiler. * IDL interfaces, structs, etc. that contain a field or parameter of type <code>XmlEntityStruct</code> * will be compiled into source code that contains a corresponding XML binding class. * <p> * In collaboration with the other classes from this package, this class * performs the following steps for each IDL file specified: * <ol> * <li>parses the IDL file (using an {@link org.openorb.compiler.parser.IdlParser}). The intermediate * result is a tree of {@link org.openorb.compiler.object.IdlObject} nodes.</li> * <li>identifies IDL <code>typedef</code>s for the type <code>XmlEntityStruct</code>, * e.g. the definition of the type <code>SchedBlock</code>. * (see {@link IdlTreeManipulator#findXmlTypedefNodes})</li> * <li>marks the nodes in the parse tree * that are affected by the XML entity types * (using {@link IdlTreeManipulator#findXmlEntityNodes}). * This ensures that later we don't generate code that would be functionally identical * to the code generated by a standard IDL compiler.<br> * Note that the identification of parse tree nodes works upward-recursive: for example, if an IDL * struct contains another struct which has a member of type <code>SchedBlock</code>, then * both IDL structs will get marked.</li> * <li>translates the relevant nodes of the manipulated IDL parse tree into Java code * (using {@link JavaGenerator#translateData}). * <br> * Note that the logic of mapping IDL to Java and the code generation itself are not separated.</li> * <li>optionally runs the code generator for component helper class templates, * see {@link alma.tools.idlgen.comphelpgen.ComponentHelperGeneratorProxy} and * {@link alma.acs.tools.comphelpergen.CompHelperGenerator}.<br> * Information about (component) interfaces and Java packages is transmitted as an XML string * that conforms to the schema <code>HelperInfo</code> in the package * <code>alma.acs.tools.comphelpergen</code>.</li> * </ol> * <p> * <b>Parameters and properties</b>: * <ul> * <li>Takes all command line arguments that are valid for {@link org.openorb.compiler.IdlCompiler}, * although some of them don't apply and will be ignored.<br> * Interesting for tracing is <code>-verbose</code>.</li> * <li>The property <code>ACS.idl2jbind</code> specifies the mappings from typedef'd * <code>XmlEntityStruct</code>s to Java binding classes.<br> * For example, the value * "<code>SchedBlock=alma.xmljbind.test.schedblock.SchedBlock; * ObsProject=alma.xmljbind.test.obsproject.ObsProject</code>" * instructs the compiler to use the Java class * <code>alma.xmljbind.test.schedblock.SchedBlock</code> * whereever an <code>XmlEntityStruct</code> typedef'd to <code>SchedBlock</code> * is found.</li> * <li>Two properties control the optional invocation of the code generator for * component helper classes: * <ul> * <li><code>alma.acs.tools.comphelpergen.doGenerate</code>: if <code>true</code>, * the ACS IDL compiler will run the generator after compiling an IDL file. * See {@link alma.tools.idlgen.comphelpgen.ComponentHelperGeneratorProxy}.</li> * <li><code>alma.acs.tools.comphelpergen.outRootDir</code> specifies the directory under which * the component helper classes will be generated according to their Java packages.</li> * </ul></li> * </ul> * In order to keep things simple, the implementation uses the OpenORB IDL compiler * (<code>org.openorb.compiler.IdlCompiler</code> and related classes) * whereever this is possible. * No OpenORB code is modified; this goal results in some less * intuitive handling of parse tree nodes, since new behavior could not be attached to the * existing classes. On the other hand, we won't have to insert our patches into any new version * of OpenORB that we might want to use in the future. * <p> * A half-inspired attempt has been made to pave the road for code generation for languages other than Java. * The tree manipulations could also be used by other code generators. * For Java we reused OpenORB's implementation of the IDL2Java mapping logic * (see {@link JavaGenerator}), but similar generators for other languages would be much more complex. * <p> * Remark on the proprietary code generation strategy used: * Since OpenORB does not separate IDL2Java mapping logic from Java code generation, we can't * use any standard code generation framework without reimplementing the IDL2Java logic ourselves. * The choice was to accept being different from other ACS code generators, * with the advantage of not writing lots of new code. * * @author hsommer */ public class XmlIdlCompiler { public static final String PROP_DO_GENERATE_COMP_HELPERS = "alma.acs.tools.comphelpergen.doGenerate"; public static final String PROP_COMP_HELPERS_OUTDIR = "alma.acs.tools.comphelpergen.outRootDir"; /** * See {@link #translateFromOpenORB(String[])} about missing verbosity setting coming from the Makefile */ private boolean verbose = false; private final AcsXmlNamingExpert namingExpert; /** * Constructor for XmlIdlCompiler. */ public XmlIdlCompiler() { namingExpert = new AcsXmlNamingExpert(); } /** * This operation is used to generate code for an IDL file that has been parsed already. * @throws Exception */ public void generateCode(JacorbVisitor jacorbVisitor) throws Exception { // -- options that are outside the OpenORB command line option handling String idl2jbindMapping = System.getProperty("ACS.idl2jbind"); namingExpert.setIdlStruct2JavaBindingClassMappings(idl2jbindMapping); boolean doGenerateCompHelpers = Boolean.getBoolean(PROP_DO_GENERATE_COMP_HELPERS); String compHelperOutDir = System.getProperty(PROP_COMP_HELPERS_OUTDIR); if (verbose) { // Print the xml affected nodes System.out.println("\nXML typedefs found: "); for (AliasTypeSpec type : jacorbVisitor.xmlTypedefs) { System.out.println("\t"+type.name()); } System.out.println("\nXML-aware Structs found: "); for (StructType str : jacorbVisitor.xmlAwareStructs) { System.out.println("\t"+str.name()); } System.out.println("\nXML-aware IFs found: "); for (Interface interfce : jacorbVisitor.xmlAwareIFs) { System.out.println("\t"+interfce.name()); } System.out.println("\nXmlIdl options (non-JacORB):" + "\n idl2jbindMapping = " + idl2jbindMapping + "\n doGenerateCompHelpers = " + doGenerateCompHelpers + "\n compHelperOutDir = " + compHelperOutDir); } if (doGenerateCompHelpers && compHelperOutDir == null) { System.err.println("\nCan't generate component helper class(es) because " + "no output directory is given in alma.acs.tools.comphelpergen.outRootDir"); doGenerateCompHelpers = false; } // -- init component helper generator with interfaces and Java packages -- ComponentHelperGeneratorProxy compHelpGen = null; if (doGenerateCompHelpers) { compHelpGen = new ComponentHelperGeneratorProxy(compHelperOutDir, verbose); try { compHelpGen.setOriginalParseTree(jacorbVisitor.acsCompIFs); } catch (RuntimeException e) { System.err.println("failed to parse Java packages:"); System.err.println(e.toString()); System.err.println("Will disable component helper generation."); compHelpGen = null; doGenerateCompHelpers = false; } } else if (verbose) { System.out.println("no code generation for component helper classes was selected..."); } // -- Start to generate Java code, the main purpose of all this -- if (verbose) { System.out.println("\n**** Java code generation ****"); } // -- Rename nodes affected by XML. // -- We do this before starting the code generation to get also nested structs and inherited AcsJ interfaces right. for (Interface interfce : jacorbVisitor.xmlAwareIFs) { interfce.set_name(interfce.name() + "J"); } // Generate code for each xml typedef for (AliasTypeSpec alias : jacorbVisitor.xmlTypedefs) { AcsTypedefPrinter printer = new AcsTypedefPrinter(alias, namingExpert); printer.printHolderClass(); } // Generate code for each xml struct for (StructType struct : jacorbVisitor.xmlAwareStructs) { AcsStructPrinter printer = new AcsStructPrinter(struct, jacorbVisitor.xmlTypedefs, jacorbVisitor.xmlAwareStructs, jacorbVisitor.xmlAwareIFs, namingExpert); printer.printStructClass(); printer.printHolderClass(); } // Generate code for each interface for (Interface interfce : jacorbVisitor.xmlAwareIFs) { AcsInterfacePrinter printer = new AcsInterfacePrinter(interfce, jacorbVisitor.xmlTypedefs, jacorbVisitor.xmlAwareStructs, jacorbVisitor.xmlAwareIFs, namingExpert); printer.printAcsJInterface(); } if (verbose) { System.out.println("\n**** done with Java code generation ****"); } // -- component helper code generation -- if (compHelpGen != null) { System.out.println("ALMA IDL compiler done, now invoking component helper code generator..."); compHelpGen.generateComponentHelperCode(); } } /** * Utility method that creates a File object for code generation. * @throws IOException */ public static File createFile(String jpackage, String className) throws IOException { String fname = className + ".java"; String path = AcsAdapterForOldJacorb.getParserOutDir() + File.separatorChar + jpackage.replace('.', File.separatorChar ); File dir = new File(path); if (!dir.exists()) { if (!dir.mkdirs()) { throw new IOException("Unable to create " + path); } } File f = new File(dir, fname); return f; } /** * The main method gets called from ACS/LGPL/Kit/acs/include/acsMakefileCore.mk * with the following JVM properties and application arguments: * <ul> * <li><code>-d <outputDir></code> with the root of the directory tree for the generated output. * <li>JVM property <code>ACS.idl2jbind</code> that contains the ';'-separated bindings of XmlEntityStruct typedefs * to XML java binding classes as obtained from the Makefile directive <code>XML_IDL</code>; <br> * Example value: "ObsProposal=alma.xmljbind.test.obsproposal.ObsProposal; SchedBlock=alma.xmljbind.test.schedblock.SchedBlock" * <li>Optional JVM properties <code>alma.acs.tools.comphelpergen.doGenerate<code> and <code>alma.acs.tools.comphelpergen.outRootDir</code> * <li>The command line also contains <code>$(verboseDef)</code> but it seems to be always empty. * </ul> * It runs the JacORB IDL parser with the custom ACS plugin and generates code for the nodes involved in * XML translation. * @param externalArgs -d <outdir> -I<myIdlIncludePath1> -I<myIdlIncludePath2> <myIdlFile> */ public static void main(String[] args) { try { XmlIdlCompiler inst = new XmlIdlCompiler(); String[] jarcorbArgs = inst.processAndPruneAcsArgs(args); JacorbVisitor jacorbVisitor = new JacorbVisitor(); AcsJacorbParser parser = new AcsJacorbParser(); parser.init(jarcorbArgs, jacorbVisitor); // Run the jacorb IDL parser with the ACS plugin System.out.println("ACS IDL compilation for " + parser.idlFile); parser.parse(); // XML manipulation and code generation inst.generateCode(jacorbVisitor); } catch (Throwable thr) { System.err.println("ACS IDL compilation failed!"); thr.printStackTrace(System.err); System.exit(1); } } /** * Processes those arg from the given argument list that target ACS. * The returned list of args is meant to be passed to JacORB. */ private String[] processAndPruneAcsArgs(String[] args) { List<String> argListIn = Arrays.asList(args); List<String> argListOut = new ArrayList<String>(); for (String arg : argListIn) { if (arg.equals("-verbose")) { // verbosity for ACS code verbose = true; // verbosity for jacorb code int jacorbDebugLevel = 1; // 0...4 argListOut.add("-W"); argListOut.add(Integer.toString(jacorbDebugLevel)); } else { argListOut.add(arg); } } return argListOut.toArray(new String[argListOut.size()]); } }