/* * $Header: /cvsroot/remotetea/remotetea/src/org/acplt/oncrpc/apps/jrpcgen/jrpcgen.java,v 1.4 2005/11/11 21:28:48 haraldalbrecht Exp $ * * Copyright (c) 1999, 2000 * Lehrstuhl fuer Prozessleittechnik (PLT), RWTH Aachen * D-52064 Aachen, Germany. * All rights reserved. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this program (see the file COPYING.LIB for more * details); if not, write to the Free Software Foundation, Inc., * 675 Mass Ave, Cambridge, MA 02139, USA. */ // // Personal note: this class probably suffers from a flashback on // procedural programming ... but where do we need to be today? // package org.acplt.oncrpc.apps.jrpcgen; import org.acplt.oncrpc.apps.jrpcgen.cup_runtime.Symbol; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.io.Writer; import java.math.BigInteger; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; /** * The class <code>jrpcgen</code> implements a Java-based rpcgen RPC protocol * compiler. jrpcgen is a Java-based tool that generates source code of Java * classes to implement an RPC protocol. The input to jrpcgen is a language * similar to C (but more probably much more similar to FORTRAN) known as * the RPC language (Remote Procedure Call Language). * * @version $Revision: 1.4 $ $Date: 2005/11/11 21:28:48 $ $State: Exp $ $Locker: $ * @author Harald Albrecht */ public class jrpcgen { /** * Print the help message describing the available command line options. */ public static void printHelp() { System.out.println("Usage: jrpcgen [-options] x-file"); System.out.println(); System.out.println("where options include:"); System.out.println(" -c <classname> specify class name of client proxy stub"); System.out.println(" -d <dir> specify directory where to place generated source code files"); System.out.println(" -p <package> specify package name for generated source code files"); System.out.println(" -s <classname> specify class name of server proxy stub"); System.out.println(" -ser tag generated XDR classes as serializable"); System.out.println(" -bean generate accessors for usage as bean, implies -ser"); System.out.println(" -noclamp do not clamp version number in client method stubs"); System.out.println(" -initstrings initialize all strings to be empty instead of null"); System.out.println(" -nobackup do not make backups of old source code files"); System.out.println(" -noclient do not create client proxy stub"); System.out.println(" -noserver do not create server proxy stub"); System.out.println(" -parseonly parse x-file only but do not create source code files"); System.out.println(" -asyncfuture generate async client methods that return Futures"); System.out.println(" -asynccallback generate async client methods that accept callback handlers"); System.out.println(" -oneway generate client methods that are one-way"); System.out.println(" -nosync do not generate sync client methods (which are generated by default)"); System.out.println(" -timeouts generate methods that accept a timeout argument (except one-way methods)"); System.out.println(" -percallauth generate methods that accept an auth argument (null for default)"); System.out.println(" -verbose enable verbose output about what jrpcgen is doing"); System.out.println(" -version print jrpcgen version and exit"); System.out.println(" -debug enables printing of diagnostic messages"); System.out.println(" -? -help print this help message and exit"); System.out.println(" -- end options"); System.out.println(); } /** * Current version of jrpcgen. */ public static final String VERSION = "1.0.7+"; /** * A remote procedure has no parameters and thus needs to use the * XDR void wrapper class as a dummy. */ public static final int PARAMS_VOID = 0; /** * A remote procedure expects only a single parameter, which is a * complex type (class). */ public static final int PARAMS_SINGLE = 1; /** * A remote procedure expects only a single parameter, which is of * a base type, like integer, boolean, string, et cetera. */ public static final int PARAMS_SINGLE_BASETYPE = 2; /** * A remote procedure expects more than one parameter and thus needs * an XDR wrapping class. */ public static final int PARAMS_MORE = 3; /** * String containing date/time when a jrpcgen run was started. This string * is used in the headers of the generated source code files. */ public static final String startDate = (new SimpleDateFormat()).format(new Date()); /** * Contains all global identifiers for type, structure and union specifiers * as well as for constants and enumeration members. This static attribute * is directly manipulated by the parser. */ public static Map globalIdentifiers = new HashMap(); /** * Disable automatic backup of old source code files, if <code>true</code>. */ public static boolean noBackups = false; /** * Holds information about the remote program defined in the jrpcgen * x-file. */ public static List<JrpcgenProgramInfo> programInfos = null; /** * Enable diagnostic messages when parsing the x-file. */ public static boolean debug = false; /** * Verbosity flag. If <code>true</code>, then jrpcgen will report about * the steps it is taking when generating all the source code files. */ public static boolean verbose = false; /** * Parse x-file only but do not create source code files if set to * <code>true</code>. */ public static boolean parseOnly = false; /** * The x-file to parse (not: the X Files, the latter ones are something * completely different). */ public static File xFile = null; /** * Destination directory where to place the generated files. */ public static File destinationDir = new File("."); /** * Current FileWriter object receiving generated source code. */ public static Writer currentFileWriter = null; /** * Current PrintWriter object sitting on top of the * {@link #currentFileWriter} object receiving generated source code. */ public static PrintWriter currentPrintWriter = null; /** * Full name of the current source code file. */ public static String currentFilename = null; /** * Specifies package name for generated source code, if not * <code>null</code>. If <code>null</code>, then no package statement * is emitted. */ public static String packageName = null; /** * Name of class containing global constants. It is derived from the * filename with the extension (".x") and path removed. */ public static String baseClassname = null; /** * Do not generate source code for the client proxy stub if * <code>true</code>. */ public static boolean noClient = false; /** * Do not generate source code for the server proxy stub if * <code>true</code>. */ public static boolean noServer = false; /** * Name of class containing the ONC/RPC server stubs. */ public static String serverClass = null; /** * Name of class containing the ONC/RPC client stubs. */ public static String clientClass = null; /** * Enable tagging of XDR classes as being Serializable */ public static boolean makeSerializable = false; /** * Enable generation of accessors in order to use XDR classes as beans. */ public static boolean makeBean = false; /** * Enable automatic initialization of String with empty Strings * instead of null reference. */ public static boolean initStrings = false; /** * Enable generation of sync client methods (default) */ public static boolean generateSyncClient = true; /** * Enable generation of async client methods that return Futures */ public static boolean generateAsyncFutureClient = false; /** * Enable generation of async client methods that accept a callback handler */ public static boolean generateAsyncCallbackClient = false; /** * Enable generation of async client methods that are one-way (do not wait for a return value) */ public static boolean generateOneWayClient = false; /** * generate all methods with timeout arguments (affects all methods generated) */ public static boolean generateTimeoutSupport = false; /** * generate all methods with an auth argument (nullable for default) to support per-call auth */ public static boolean generatePerCallAuthSupport = false; /** * Creates a new source code file for a Java class based on its class * name. Same as {@link #createJavaSourceFile(String, boolean)} with * the <code>emitImport</code> parameter set to <code>true</code>. * * @param classname Name of Java class to generate. Must not contain * a file extension -- especially ".java" is invalid. When the source * code file is created, ".java" is appended automatically. * * @return PrintWriter to send source code to. */ public static PrintWriter createJavaSourceFile(String classname) { return createJavaSourceFile(classname, true); } /** * Creates a new source code file for a Java class based on its class * name. If an old version of the source file exists, it is renamed first. * The backup will have the same name as the original file with "~" * appended. * * @param classname Name of Java class to generate. Must not contain * a file extension -- especially ".java" is invalid. When the source * code file is created, ".java" is appended automatically. * @param emitImports if <code>true</code>, then import statements for * the remotetea ONC/RPC package and IOExceptions. * * @return PrintWriter to send source code to. */ public static PrintWriter createJavaSourceFile(String classname, boolean emitImports) { String filename = classname + ".java"; if (debug) { System.out.println("Generating source code for \"" + filename + "\" in \"" + destinationDir + "\""); } File file = new File(destinationDir, filename); // // If an old file of the same name already exists, then rename it // before creating the new file. // if (file.exists() && !noBackups) { if (!file.isFile()) { // // If the file to be created already exists and is not a // regular file, then bail out with an error. // System.err.println("error: source file \"" + filename + "\"already exists and is not a regular file"); System.exit(1); } File oldBackup = new File(destinationDir, filename + "~"); if (oldBackup.isFile()) { oldBackup.delete(); } else if (oldBackup.exists()) { System.err.println("error: backup source file \"" + filename + "~\" is not a regular file"); System.exit(1); } if (!file.renameTo(new File(destinationDir, filename + "~"))) { System.err.println("error: can not rename old source code file \"" + filename + "\""); System.exit(1); } if (verbose) { System.out.println("Saved old source code file as \"" + filename + "~\""); } } // // Now create a new source code file... // try { currentFileWriter = new FileWriter(file); } catch (IOException e) { System.err.println("error: can not create \"" + filename + "\": " + e.getLocalizedMessage()); System.exit(1); } if (verbose) { System.out.print("Creating source code file \"" + filename + "\"..."); } currentFilename = filename; PrintWriter out = new PrintWriter(currentFileWriter, true); currentPrintWriter = out; // // Create automatic header(s)... // Note that we always emit the import statements, regardless of // whether we're emitting a class file or an interface file consisting // of an enumeration. // out.println("/*"); out.println(" * Automatically generated by jrpcgen " + VERSION + " on " + startDate); out.println(" * jrpcgen is part of the \"Remote Tea\" ONC/RPC package for Java"); out.println(" * See http://remotetea.sourceforge.net for details"); out.println(" *"); out.println(" * This version of jrpcgen adopted by dCache project"); out.println(" * See http://www.dCache.ORG for details"); out.println(" */"); // // Only generated package statement if a package name has been specified. // if ((packageName != null) && (packageName.length() > 0)) { out.println("package " + packageName + ";"); } if (emitImports) { out.println("import org.dcache.xdr.*;"); out.println("import java.io.IOException;"); out.println(); } return out; } /** * Create a new hash function object and initialize it using a class * and package name. * * @param classname Name of class. * * @return hash function object. */ public static JrpcgenSHA createSHA(String classname) { JrpcgenSHA hash = new JrpcgenSHA(); if ((packageName != null) && (packageName.length() > 0)) { hash.update(packageName + "." + classname); } else { hash.update(classname); } return hash; } /** * Closes the source code file previously opened with * <code>createJavaSourceFile</code>. This method writes a trailer * before closing the file. */ public static void closeJavaSourceFile() { // // Create automatic footer before closing the file. // currentPrintWriter.println("// End of " + currentFilename); if (verbose) { System.out.println(); } try { currentPrintWriter.close(); currentFileWriter.close(); } catch (IOException e) { System.err.println("Can not close source code file: " + e.getLocalizedMessage()); } } /** * Dump the value of a constant and optionally first dump all constants * it depends on. */ public static void dumpConstantAndDependency(JrpcgenConst c, List<String> declarations, Set<String> imports) { // // This simple test avoids endless recursions: we already dumped this // particular constant in some place, so we should not proceed. // if (c.dontTraverseAnyMore) { return; } // // Since we will dump the constant below, we already set the flag, // to avoid endless recursions. // c.dontTraverseAnyMore = true; String dependencyIdentifier = c.getDependencyIdentifier(); if (dependencyIdentifier != null) { // // There is a dependency, so try to resolve that first. In case // we depend on another identifier belonging to the same enclosure, // we dump this other identifier first. However, if the identifier // we depend on belongs to a different enclosure, then we must not // dump it: this will be the job of a later call when the proper // enclosure is in the works. // JrpcgenConst dc = (JrpcgenConst) globalIdentifiers.get(dependencyIdentifier); if (dc != null) { if (!c.enclosure.equalsIgnoreCase(dc.enclosure)) { // // In case we depend on a constant which belongs to a // different enclosure then also dump the enclosure (that // is, "enclosure.valueidentifier"). // // Note that this code depends on the "value" starts // with the identifier we depend on (which is currently // the case), so we just need to prepend the enclosure. // translateConstant(c, declarations, imports, dc.enclosure + "." + c.value); return; } // // Only dump the identifier we're dependent on, if it's in // the same enclosure. // dumpConstantAndDependency(dc, declarations, imports); } } // // Just dump the plain value (without enclosure). // translateConstant(c, declarations, imports, null); } public static void translateConstant(JrpcgenConst constDecl, List<String> declarations, Set<String> imports, String valueOverride) { String rawValue = constDecl.resolveValue(); String str = rawValue.toLowerCase(Locale.ROOT); //parse the numeric value of the constant BigInteger value; String valueSansPrefix; int radix; boolean hexOrOctal = true; if (str.startsWith("0x")) { //hex valueSansPrefix = rawValue.substring(2); radix = 16; value = new BigInteger(str.substring(2), 16); } else if (str.length() > 1 && str.startsWith("0")) { //octal valueSansPrefix = rawValue.substring(1); radix = 8; value = new BigInteger(str.substring(1), 8); } else { //decimal (possibly negative) hexOrOctal = false; valueSansPrefix = rawValue; radix = 10; value = new BigInteger(str, 10); } //now figure out what type the constant fits in BigInteger maxLongBound = hexOrOctal ? new BigInteger("FFFFFFFFFFFFFFFF", 16) : BigInteger.valueOf(Long.MAX_VALUE); BigInteger maxIntBound = hexOrOctal ? new BigInteger("FFFFFFFF", 16) : BigInteger.valueOf(Integer.MAX_VALUE); //remember hex and octal constants are unsigned and so positive, so we dont bother fixing up the negative range ends if (value.compareTo(maxLongBound) > 0 || value.compareTo(BigInteger.valueOf(Long.MIN_VALUE)) < 0) { //outside long representation range. use BigInt imports.add("import " + BigInteger.class.getCanonicalName() + ";"); String val = valueOverride != null ? valueOverride : "new BigInteger(\"" + valueSansPrefix + "\", " + radix + ")"; declarations.add(" public static final BigInteger " + constDecl.identifier + " = " + val + ";"); } else if (value.compareTo(maxIntBound) > 0 || value.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0) { //outside int range, use long String val = valueOverride != null ? valueOverride : rawValue + "L"; declarations.add(" public static final long " + constDecl.identifier + " = " + val + ";"); } else { //default to int String val = valueOverride != null ? valueOverride : rawValue; declarations.add(" public static final int " + constDecl.identifier + " = " + val + ";"); } } /** * Generate source code file containing all constants defined in the * x-file as well as all implicitely defined constants, like program, * version and procedure numbers, etc. This method creates a public * interface with the constants as public static final integers. */ public static void dumpConstants() { // // Create new source code file containing a Java interface representing // all XDR constants. // PrintWriter out = createJavaSourceFile(baseClassname, false); // // Spit out some description for javadoc & friends... // out.println("/**"); out.println(" * A collection of constants used by the \"" + baseClassname + "\" ONC/RPC program."); out.println(" */"); List<String> declarations = new ArrayList<>(); Set<String> imports = new HashSet<>(); for (Object o: globalIdentifiers.values()) { if (o instanceof JrpcgenConst) { JrpcgenConst c = (JrpcgenConst) o; // // Dump only such constants which belong to the global // constants enclosure. Ignore all other constants, as those // belong to other Java class enclosures. // if (baseClassname.equals(c.enclosure)) { dumpConstantAndDependency(c, declarations, imports); } } } if (!imports.isEmpty()) { out.println(""); for (String importLine : imports) { out.println(importLine); } out.println(""); } out.println("public interface " + baseClassname + " {"); for (String declLine : declarations) { out.println(declLine); } out.println("}"); closeJavaSourceFile(); } /** * Generate a source code file containing all elements of an enumeration * defined in a x-file. * * @param e {@link JrpcgenEnum Description} of XDR enumeration. */ public static void dumpEnum(JrpcgenEnum e) { // // Create new source code file containing a Java interface representing // the XDR enumeration. // PrintWriter out = createJavaSourceFile(e.identifier, false); // // Spit out some description for javadoc & friends... // out.println("/**"); out.println(" * Enumeration (collection of constants)."); out.println(" */"); out.println("public interface " + e.identifier + " {"); out.println(); Enumeration enums = e.enums.elements(); while (enums.hasMoreElements()) { JrpcgenConst c = (JrpcgenConst) enums.nextElement(); // // In case an element depends on a global constant, then // this constant will automatically be duplicated as part // of this enumeration. // List<String> declLines = new ArrayList<>(); Set<String> imports = Collections.emptySet(); //will throw if modified //enums are ints in xdrs, so we dont expect to need imports, hence the unmodifiable set above dumpConstantAndDependency(c, declLines, imports); for (String line : declLines) { out.println(line); } } // // Close class... // out.println(); out.println("}"); closeJavaSourceFile(); } /** * Java base data types for which are XDR encoding and decoding helper * methods available. */ private static String[] baseTypes = { "void", "boolean", "byte", "short", "int", "long", "float", "double", "String" }; /** * Given a name of a data type return the name of the equivalent Java * data type (if it exists), otherwise return <code>null</code>. * * NOTE: "opaque" is considered like "byte" to be a base type... * FIXME: char/byte? * * @return Name of Java base data type or <code>null</code> if the * given data type is not equivalent to one of Java's base data * types. */ public static String xdrBaseType(String type) { int size = baseTypes.length; if ("opaque".compareTo(type) == 0) { type = "byte"; } for (int idx = 0; idx < size; ++idx) { if (baseTypes[idx].compareTo(type) == 0) { // // For base data types simply convert the first letter to // an upper case letter. // return "Xdr" + type.substring(0, 1).toUpperCase() + type.substring(1); } } return null; } /** * Return the en-/decoding syllable XXX appropriate for a base data * type including arrays of base data types. * * @param decl declaration of a member of RPC struct or union. * * @return <code>null</code>, if the declaration does not specify a base data * type. Otherwise a three-element String array, with [0] containing * the type syllable for base type (including arrays), [1] containing * parameter options when encoding (like maximum sizes, etc), and [2] * containing options for decoding. */ public static JrpcgenEnDecodingInfo baseEnDecodingSyllable(JrpcgenDeclaration decl) { String syllable = decl.type; boolean isBase = false; // // Check for Java base data types... if a match is found, then convert // the data type name, so that it becomes a valid syllable for use // with XDR en-/decoding functions xdrEncodingXXX() etc. // Example: "int" --> "Int" (because of xdrEncodingInt()) // NOTE: we consider "opaque" to be a base type here... // int size = baseTypes.length; String type = decl.type; if ("opaque".compareTo(type) == 0) { type = "byte"; } for (int idx = 0; idx < size; ++idx) { if (baseTypes[idx].compareTo(type) == 0) { // // For base data types simply convert the first letter to // an upper case letter. // isBase = true; syllable = syllable.substring(0, 1).toUpperCase() + syllable.substring(1); break; } } // // Handle special case of enumerations, which have to be represented // using ints in the Java language. // if (!isBase) { Object o = globalIdentifiers.get(decl.type); if (o instanceof JrpcgenEnum) { isBase = true; syllable = "Int"; } } // // In case we're dealing with an array, then add "Vector" to // the syllable to use the appropriate vector en-/decoding method // for base data types. // NOTE: unfortunately, strings do not adhere to this scheme, as // they are considered to be arrays of characters... what a silly // idea, as this makes a typedef necessary in case someone needs // an array of strings. // NOTE: also opaques break the scheme somehow, but the char=byte // versus opaque schisma anyhow drives me crazy... // if (isBase) { String encodingOpts = null; String decodingOpts = null; if ((decl.kind == JrpcgenDeclaration.FIXEDVECTOR) || (decl.kind == JrpcgenDeclaration.DYNAMICVECTOR)) { if ("opaque".equals(decl.type)) { if (decl.kind == JrpcgenDeclaration.FIXEDVECTOR) { syllable = "Opaque"; encodingOpts = checkForEnumValue(decl.size); decodingOpts = checkForEnumValue(decl.size); } else { syllable = "DynamicOpaque"; encodingOpts = null; decodingOpts = null; } } else if (!"String".equals(decl.type)) { if (decl.kind == JrpcgenDeclaration.FIXEDVECTOR) { syllable = syllable + "Fixed"; encodingOpts = checkForEnumValue(decl.size); decodingOpts = checkForEnumValue(decl.size); } syllable = syllable + "Vector"; } } return new JrpcgenEnDecodingInfo(syllable, encodingOpts, decodingOpts); } return null; } /** * Return en- or decoding method appropriate for a struct or union member. */ public static String codingMethod(JrpcgenDeclaration decl, boolean encode) { return codingMethod(decl, encode, null); } /** * Return en- or decoding method appropriate for a struct or union member. * * @param decl declaration for which the en-/decoding Java source code be * returned. * @param encode <code>true</code> if encoding method should be returned, * <code>false</code> if decoding method is to be returned. * @param oref name of object reference or <code>null</code> if * "this" should be used instead. */ public static String codingMethod(JrpcgenDeclaration decl, boolean encode, String oref) { // // Skip entries for void arms etc... // if (decl.identifier == null) { return ""; } StringBuilder code = new StringBuilder(); JrpcgenEnDecodingInfo data = baseEnDecodingSyllable(decl); // // In case no type was specified for the outer element, assume no // name, otherwise convert into a suitable prefix for code generation // by appending a dot. // if (oref == null) { oref = ""; } else { oref = oref + "."; } if (data != null) { // // It's a base data type (including vectors). So we can use the // predefined en-/decoding methods: // - xdr.xdrEncodeXXX(value); // - value = xdr.xdrDecodeXXX(value); // if (encode) { code.append(" xdr.xdrEncode"); code.append(data.syllable); code.append("("); code.append(oref).append(decl.identifier); if (data.encodingOptions != null) { code.append(", "); code.append(data.encodingOptions); } code.append(");\n"); } else { code.append(" "); code.append(oref).append(decl.identifier); code.append(" = xdr.xdrDecode"); code.append(data.syllable); code.append("("); if (data.decodingOptions != null) { code.append(data.decodingOptions); } code.append(");\n"); } return code.toString(); } else { // // It's not a built-in base data type but instead something that // is represented by a class. // - foo.xdrEncode(xdr); // - foo = new FOO(); // foo.xdrDecode(xdr); // In case of arrays, this is going to be hairy... // if (decl.kind == JrpcgenDeclaration.SCALAR) { code.append(" "); if (encode) { code.append(oref).append(decl.identifier); code.append(".xdrEncode(xdr);\n"); } else { code.append(oref).append(decl.identifier); code.append(" = new "); code.append(decl.type); code.append("(xdr);\n"); } return code.toString(); // // It's not a built-in base data type but instead an indirection // (reference) to some instance (optional data). // } else if (decl.kind == JrpcgenDeclaration.INDIRECTION) { code.append(" "); if (encode) { code.append("if ( "); code.append(oref).append(decl.identifier); code.append(" != null ) { "); code.append("xdr.xdrEncodeBoolean(true); "); code.append(oref).append(decl.identifier); code.append(".xdrEncode(xdr);"); code.append(" } else { "); code.append("xdr.xdrEncodeBoolean(false);"); code.append(" };\n"); } else { code.append(oref).append(decl.identifier); code.append(" = xdr.xdrDecodeBoolean() ? new "); code.append(decl.type); code.append("(xdr) : null;\n"); } return code.toString(); } // // Array case... Urgh! // if (encode) { code.append(" { "); code.append("int $size = "); if (decl.kind == JrpcgenDeclaration.DYNAMICVECTOR) { // // Dynamic array size. So we need to use the current size // of the Java array. // code.append(oref).append(decl.identifier); code.append(".length"); } else { code.append(checkForEnumValue(decl.size)); } code.append("; "); if (decl.kind == JrpcgenDeclaration.DYNAMICVECTOR) { // // Dynamic array size. So we need to encode size information. // code.append("xdr.xdrEncodeInt($size); "); } // // Now encode all elements. // code.append("for ( int $idx = 0; $idx < $size; ++$idx ) { "); code.append(oref).append(decl.identifier); code.append("[$idx].xdrEncode(xdr); "); code.append("} }\n"); } else { code.append(" { "); code.append("int $size = "); if (decl.kind == JrpcgenDeclaration.DYNAMICVECTOR) { // // Dynamic array size. So we need to decode size information. // code.append("xdr.xdrDecodeInt()"); } else { code.append(checkForEnumValue(decl.size)); } code.append("; "); // // Now encode all elements. // code.append(oref).append(decl.identifier); code.append(" = new "); code.append(decl.type); code.append("[$size]; "); code.append("for ( int $idx = 0; $idx < $size; ++$idx ) { "); code.append(oref).append(decl.identifier); code.append("[$idx] = new "); code.append(decl.type); code.append("(xdr); "); code.append("} }\n"); } return code.toString(); } } /** * Checks whether a given data type identifier refers to an enumeration * type and then returns Java's int data type instead. In case of the * pseudo-type "opaque" return Java's byte data type. For all other * data types, the data type identifier is returned unaltered. * * @param dataType data type identifier to check. * * @return data type identifier. */ public static String checkForSpecials(String dataType) { if (globalIdentifiers.get(dataType) instanceof JrpcgenEnum) { return "int"; } else if ("opaque".equals(dataType)) { return "byte"; } return dataType; } /** * given a java type, return the XdrAble wrapper for it. * this applies mostly to java primitives and immutables * (like int and String respectively) * otherwise return the original (assumed to be XdrAble) * @param javaType a java type (ex "int") * @return the boxed, XdrAble type, or the argument, if none required */ public static String boxForTransport(String javaType) { String xdrWrapper = xdrBaseType(javaType); if (xdrWrapper != null) { return xdrWrapper; } return javaType; } /** * Checks whether a given value references an identifier and then * returns the qualified identifier (interface where the value is * defined in) or simply the value in case of an integer literal. * * @param value Either an identifier to resolve or an integer literal. * * @return Integer literal or qualified identifier. */ public static String checkForEnumValue(String value) { if (value.length() > 0) { // // If the value is an integer literal, then we just have to // return it. That's it. // if (Character.isDigit(value.charAt(0)) || (value.charAt(0) == '-')) { return value; } // // It's an identifier: we now need to find out in which // enclosure it lives, so we can return a qualified identifier. // Object id = jrpcgen.globalIdentifiers.get(value); if ((id != null) && (id instanceof JrpcgenConst)) { JrpcgenConst c = (JrpcgenConst) id; if (c.enclosure == null) { return c.value; } return c.enclosure + "." + c.identifier; } } return value; } /** * Generate a source code file containing all elements of a struct * defined in a x-file. * * @param s {@link JrpcgenStruct Description} of XDR struct. */ public static void dumpStruct(JrpcgenStruct s) { // // Create new source code file containing a Java class representing // the XDR struct. // String access = " public "; // modify encapsulation with beans PrintWriter out = createJavaSourceFile(s.identifier); out.print("public class " + s.identifier + " implements XdrAble"); if (makeSerializable) { out.print(", java.io.Serializable"); } if (makeBean) { access = " protected "; } out.println(" {"); // // Generate declarations of all members of this XDR struct. This the // perfect place to also update the hash function using the elements // together with their type. // boolean useIteration = false; JrpcgenSHA hash = createSHA(s.identifier); Enumeration decls = s.elements.elements(); while (decls.hasMoreElements()) { JrpcgenDeclaration d = (JrpcgenDeclaration) decls.nextElement(); hash.update(d.type); hash.update(d.kind); hash.update(d.identifier); out.print(access + checkForSpecials(d.type) + " "); if (((d.kind == JrpcgenDeclaration.FIXEDVECTOR) || (d.kind == JrpcgenDeclaration.DYNAMICVECTOR)) && !d.type.equals("String")) { out.print("[] "); } if (initStrings && d.type.equals("String")) { out.println(d.identifier + " = \"\"; "); } else { out.println(d.identifier + ";"); } // // If the last element in the XDR struct is a reference to // the type of the XDR struct (that is, a linked list), then // we can convert this tail recursion into an iteration, // avoiding deep recursions for large lists. // if (!decls.hasMoreElements() && d.kind == JrpcgenDeclaration.INDIRECTION && d.type.equals(s.identifier)) { useIteration = true; } } // // Generate serial version unique identifier // if (makeSerializable) { out.println(); out.println(" private static final long serialVersionUID = " + hash.getHash() + "L;"); if (makeBean) { // // Also generate accessors (getters and setters) so that // class can be used as a bean. // decls = s.elements.elements(); while (decls.hasMoreElements()) { out.println(); JrpcgenDeclaration d = (JrpcgenDeclaration) decls.nextElement(); String jbName = d.identifier.substring(0, 1).toUpperCase() + d.identifier.substring(1); boolean isArray = (((d.kind == JrpcgenDeclaration.FIXEDVECTOR) || (d.kind == JrpcgenDeclaration.DYNAMICVECTOR)) && !d.type.equals("String")); // // Generate the setter(s) // if (isArray) { out.println(" public void set" + jbName + "(" + checkForSpecials(d.type) + "[] x) { this." + d.identifier + " = x; }"); out.println(" public void set" + jbName + "(int index, " + checkForSpecials(d.type) + " x) { this." + d.identifier + "[index] = x; }"); } else { out.println(" public void set" + jbName + "(" + checkForSpecials(d.type) + " x) { this." + d.identifier + " = x; }"); } // // Generate the getter(s) // if (isArray) { out.println(" public " + checkForSpecials(d.type) + "[] get" + jbName + "() { return this." + d.identifier + "; }"); out.println(" public " + checkForSpecials(d.type) + " get" + jbName + "(int index) { return this." + d.identifier + "[index]; }"); } else { out.println(" public " + checkForSpecials(d.type) + " get" + jbName + "() { return this." + d.identifier + "; }"); } } } } // // Now generate code for encoding and decoding this class (structure). // out.println(); out.println(" public " + s.identifier + "() {"); out.println(" }"); out.println(); out.println(" public " + s.identifier + "(XdrDecodingStream xdr)"); out.println(" throws OncRpcException, IOException {"); out.println(" xdrDecode(xdr);"); out.println(" }"); out.println(); out.println(" public void xdrEncode(XdrEncodingStream xdr)"); out.println(" throws OncRpcException, IOException {"); decls = s.elements.elements(); if (useIteration) { out.println(" " + s.identifier + " $this = this;"); out.println(" do {"); JrpcgenDeclaration decl = null; // // when using the iteration loop for serializing emit code for // all but the tail element, which is the reference to our type. // for (int size = s.elements.size(); size > 1; --size) { decl = (JrpcgenDeclaration) decls.nextElement(); out.print(" if( $this." + decl.identifier + " != null) " + codingMethod(decl, true, "$this")); } decl = (JrpcgenDeclaration) decls.nextElement(); out.println(" $this = $this." + decl.identifier + ";"); out.println(" xdr.xdrEncodeBoolean($this != null);"); out.println(" } while ( $this != null );"); } else { while (decls.hasMoreElements()) { out.print(codingMethod((JrpcgenDeclaration) decls.nextElement(), true)); } } out.println(" }"); out.println(); out.println(" public void xdrDecode(XdrDecodingStream xdr)"); out.println(" throws OncRpcException, IOException {"); decls = s.elements.elements(); if (useIteration) { out.println(" " + s.identifier + " $this = this;"); out.println(" " + s.identifier + " $next;"); out.println(" do {"); JrpcgenDeclaration decl = null; // // when using the iteration loop for serializing emit code for // all but the tail element, which is the reference to our type. // for (int size = s.elements.size(); size > 1; --size) { decl = (JrpcgenDeclaration) decls.nextElement(); out.print(" " + codingMethod(decl, false, "$this")); } decl = (JrpcgenDeclaration) decls.nextElement(); out.println(" $next = xdr.xdrDecodeBoolean() ? new " + s.identifier + "() : null;"); out.println(" $this." + decl.identifier + " = $next;"); out.println(" $this = $next;"); out.println(" } while ( $this != null );"); } else { while (decls.hasMoreElements()) { out.print(codingMethod((JrpcgenDeclaration) decls.nextElement(), false)); } } out.println(" }"); // // Close class... // out.println(); out.println("}"); closeJavaSourceFile(); } /** * Generate a source code file containing all elements of a union * defined in a x-file. * * @param u {@link JrpcgenUnion Description} of XDR union. */ public static void dumpUnion(JrpcgenUnion u) { // // Create new source code file containing a Java class representing // the XDR union. // PrintWriter out = createJavaSourceFile(u.identifier); out.print("public class " + u.identifier + " implements XdrAble"); if (makeSerializable) { out.print(", java.io.Serializable"); } out.println(" {"); // // Note that the descriminant can not be of an array type, string, etc. // so we don't have to handle all the special cases here. // out.println(" public " + checkForSpecials(u.descriminant.type) + " " + u.descriminant.identifier + ";"); boolean boolDescriminant = u.descriminant.type.equals("boolean"); JrpcgenSHA hash = createSHA(u.identifier); Enumeration arms = u.elements.elements(); while (arms.hasMoreElements()) { JrpcgenUnionArm a = (JrpcgenUnionArm) arms.nextElement(); // // Skip all arms which do not contain a variable but are // declared as "void" instead. Also skip all arms which are // mapped to another arm. // if ((a.element == null) || (a.element.identifier == null)) { continue; } // // In case we are working on the default arm and this arm // contains some variables, we hash the dummy descriminator // value "default". // if (a.value != null) { hash.update(a.value); } else { hash.update("default"); } hash.update(a.element.type); hash.update(a.element.kind); hash.update(a.element.identifier); out.print(" public " + checkForSpecials(a.element.type) + " "); if (((a.element.kind == JrpcgenDeclaration.FIXEDVECTOR) || (a.element.kind == JrpcgenDeclaration.DYNAMICVECTOR)) && !a.element.type.equals("String")) { out.print("[] "); } out.println(a.element.identifier + ";"); } // // Generate serial version unique identifier // if (makeSerializable) { out.println(); out.println(" private static final long serialVersionUID = " + hash.getHash() + "L;"); } // // Now generate code for encoding and decoding this class (structure). // out.println(); out.println(" public " + u.identifier + "() {"); out.println(" }"); out.println(); out.println(" public " + u.identifier + "(XdrDecodingStream xdr)"); out.println(" throws OncRpcException, IOException {"); out.println(" xdrDecode(xdr);"); out.println(" }"); out.println(); out.println(" public void xdrEncode(XdrEncodingStream xdr)"); out.println(" throws OncRpcException, IOException {"); out.print(codingMethod(u.descriminant, true)); if (!boolDescriminant) { // // Produce code using an ordinary switch statement... // out.println(" switch ( " + u.descriminant.identifier + " ) {"); arms = u.elements.elements(); while (arms.hasMoreElements()) { JrpcgenUnionArm a = (JrpcgenUnionArm) arms.nextElement(); if (a.value != null) { out.println(" case " + checkForEnumValue(a.value) + ":"); } else { // // It's the default arm. // out.println(" default:"); } // // Only emit code if arm does not map to another arm. // if (a.element != null) { if (a.element.identifier != null) { // // Arm does not contain void, so we need to spit out // encoding instructions. // out.print(" "); out.print(codingMethod(a.element, true)); } out.println(" break;"); } } out.println(" }"); } else { // // boolean descriminant: here we can have at most two arms, guess // why. // boolean firstArm = true; arms = u.elements.elements(); while (arms.hasMoreElements()) { JrpcgenUnionArm a = (JrpcgenUnionArm) arms.nextElement(); if (a.value == null) { // // Skip default branch this time... // continue; } if (a.element.identifier != null) { // // Arm contains data, so we need to create encoding // instructions. // out.print(" "); if (!firstArm) { out.print("else "); } else { firstArm = false; } out.println("if ( " + u.descriminant.identifier + " == " + checkForEnumValue(a.value) + " ) {"); out.print(" "); out.print(codingMethod(a.element, true)); out.println(" }"); } } arms = u.elements.elements(); while (arms.hasMoreElements()) { JrpcgenUnionArm a = (JrpcgenUnionArm) arms.nextElement(); if ((a.value == null) && (a.element.identifier != null)) { out.print(" "); if (!firstArm) { out.print("else "); } out.println("{"); out.print(" "); out.print(codingMethod(a.element, true)); out.println(" }"); } } } out.println(" }"); out.println(); out.println(" public void xdrDecode(XdrDecodingStream xdr)"); out.println(" throws OncRpcException, IOException {"); out.print(codingMethod(u.descriminant, false)); if (!boolDescriminant) { // // Produce code using an ordinary switch statement... // out.println(" switch ( " + u.descriminant.identifier + " ) {"); arms = u.elements.elements(); while (arms.hasMoreElements()) { JrpcgenUnionArm a = (JrpcgenUnionArm) arms.nextElement(); if (a.value != null) { out.println(" case " + checkForEnumValue(a.value) + ":"); } else { // // It's the default arm. // out.println(" default:"); } // // Only emit code if arm does not map to another arm. // if (a.element != null) { if (a.element.identifier != null) { // // Arm does not contain void, so we need to spit out // encoding instructions. // out.print(" "); out.print(codingMethod(a.element, false)); } out.println(" break;"); } } out.println(" }"); } else { // // boolean descriminant: here we can have at most two arms, guess // why. // boolean firstArm = true; arms = u.elements.elements(); while (arms.hasMoreElements()) { JrpcgenUnionArm a = (JrpcgenUnionArm) arms.nextElement(); if (a.value == null) { // // Skip default branch this time... // continue; } if (a.element.identifier != null) { // // Arm contains data, so we need to create encoding // instructions. // out.print(" "); if (!firstArm) { out.print("else "); } else { firstArm = false; } out.println("if ( " + u.descriminant.identifier + " == " + checkForEnumValue(a.value) + " ) {"); out.print(" "); out.print(codingMethod(a.element, false)); out.println(" }"); } } arms = u.elements.elements(); while (arms.hasMoreElements()) { JrpcgenUnionArm a = (JrpcgenUnionArm) arms.nextElement(); if ((a.value == null) && (a.element.identifier != null)) { out.print(" "); if (!firstArm) { out.print("else "); } out.println("{"); out.print(" "); out.print(codingMethod(a.element, false)); out.println(" }"); } } } out.println(" }"); // // Close class... // out.println(); out.println("}"); closeJavaSourceFile(); } /** * Generate a source code file containing a wrapper class for a typedef * defined in a x-file. * * @param d {@link JrpcgenDeclaration Description} of XDR typedef. */ public static void dumpTypedef(JrpcgenDeclaration d) { // // Create new source code file containing a Java class representing // the XDR struct. // PrintWriter out = createJavaSourceFile(d.identifier); out.print("public class " + d.identifier + " implements XdrAble"); if (makeSerializable) { out.print(", java.io.Serializable"); } out.println(" {"); out.println(); String paramType = checkForSpecials(d.type); if (((d.kind == JrpcgenDeclaration.FIXEDVECTOR) || (d.kind == JrpcgenDeclaration.DYNAMICVECTOR)) && !d.type.equals("String")) { paramType += " []"; } out.print(" public " + paramType + " value;"); out.println(); // // Generate serial version unique identifier // if (makeSerializable) { JrpcgenSHA hash = createSHA(d.identifier); hash.update(d.type); hash.update(d.kind); out.println(); out.println(" private static final long serialVersionUID = " + hash.getHash() + "L;"); } // // Now generate code for encoding and decoding this class (typedef). // JrpcgenDeclaration dstar = null; try { dstar = (JrpcgenDeclaration) d.clone(); } catch (CloneNotSupportedException e) { throw (new RuntimeException("fatal: can not clone JrpcgenDeclaration")); } dstar.identifier = "value"; out.println(); out.println(" public " + d.identifier + "() {"); out.println(" }"); out.println(); out.println(" public " + d.identifier + "(" + paramType + " value) {"); out.println(" this.value = value;"); out.println(" }"); out.println(); out.println(" public " + d.identifier + "(XdrDecodingStream xdr)"); out.println(" throws OncRpcException, IOException {"); out.println(" xdrDecode(xdr);"); out.println(" }"); out.println(); out.println(" public void xdrEncode(XdrEncodingStream xdr)"); out.println(" throws OncRpcException, IOException {"); out.print(codingMethod(dstar, true)); out.println(" }"); out.println(); out.println(" public void xdrDecode(XdrDecodingStream xdr)"); out.println(" throws OncRpcException, IOException {"); out.print(codingMethod(dstar, false)); out.println(" }"); // // Close class... // out.println(); out.println("}"); closeJavaSourceFile(); } /** * Generate source code files for all structures, unions and enumerations * as well as constants. All constants, which do not belong to enumerations, * are emitted to a single interface. */ public static void dumpClasses() { for(Object o : globalIdentifiers.values()) { if (o instanceof JrpcgenEnum) { dumpEnum((JrpcgenEnum) o); } else if (o instanceof JrpcgenStruct) { dumpStruct((JrpcgenStruct) o); } else if (o instanceof JrpcgenUnion) { dumpUnion((JrpcgenUnion) o); } else if (o instanceof JrpcgenDeclaration) { dumpTypedef((JrpcgenDeclaration) o); } } } /** * Generate source code for client-side stub methods for a particular * remote program version. The client-side stub methods take the * parameter(s) from the caller, encode them and throw them over to the * server. After receiving a reply, they will unpack and return it as * the outcome of the method call. * * @param out Printer writer to send source code to. * @param versionInfo Information about the remote program version for * which source code is to be generated. */ public static void dumpClientStubMethods(PrintWriter out, JrpcgenVersionInfo versionInfo) { int size = versionInfo.procedures.size(); for (int idx = 0; idx < size; ++idx) { JrpcgenProcedureInfo proc = (JrpcgenProcedureInfo) versionInfo.procedures.elementAt(idx); if (generateSyncClient) { dumpClientStubMethod(out, proc, ClientMethodType.SYNC); } if (generateAsyncFutureClient) { dumpClientStubMethod(out, proc, ClientMethodType.FUTURE); } if (generateAsyncCallbackClient) { dumpClientStubMethod(out, proc, ClientMethodType.CALLBACK); } if (generateOneWayClient) { dumpClientStubMethod(out, proc, ClientMethodType.ONE_WAY); } } } private enum ClientMethodType { SYNC, FUTURE, CALLBACK, ONE_WAY } private static void dumpClientStubMethod(PrintWriter out, JrpcgenProcedureInfo proc, ClientMethodType methodType) { // // First spit out the stub method. While we don't need to // fiddle around with the data types of the method's // parameter(s) and result, we later have to care about // some primitive data types when serializing them. // String resultType = checkForSpecials(proc.resultType); boolean voidMethod = proc.resultType.compareTo("void") != 0; out.println(" /**"); out.println(" * Call remote procedure " + proc.procedureId + "."); // // If there are no parameters, skip the parameter documentation // section, otherwise dump javadoc @param entries for every // parameter encountered. // if (proc.parameters != null) { Enumeration params = proc.parameters.elements(); while (params.hasMoreElements()) { JrpcgenParamInfo param = (JrpcgenParamInfo) params.nextElement(); out.println(" * @param " + param.parameterName + " parameter (of type " + param.parameterType + ") to the remote procedure call."); } } if (generateTimeoutSupport && !ClientMethodType.ONE_WAY.equals(methodType) && !ClientMethodType.FUTURE.equals(methodType)) { out.println(" * @param _timeoutValue timeout value. 0 means no timeout."); out.println(" * @param _timeoutUnits units for _timeoutValue"); } if (generatePerCallAuthSupport) { out.println(" * @param _auth authentication to use for call. null means constructor-provided default"); } // // Only generate javadoc for result, when it is non-void. // if (voidMethod) { out.println(" * @return Result from remote procedure call (of type " + proc.resultType + ")."); } out.println(" * @throws OncRpcException if an ONC/RPC error occurs."); out.println(" * @throws IOException if an I/O error occurs."); if (generateTimeoutSupport && ClientMethodType.SYNC.equals(methodType)) { out.println(" * @throws TimeoutException in case of timeout."); } out.println(" */"); String returnType; String methodName; switch (methodType) { case SYNC: returnType = resultType; methodName = proc.procedureId; break; case FUTURE: returnType = "Future<"+ boxForTransport(resultType)+">"; methodName = proc.procedureId+"_future"; break; case CALLBACK: returnType = "void"; methodName = proc.procedureId+"_callback"; break; case ONE_WAY: returnType = "void"; methodName = proc.procedureId+"_oneway"; break; default: throw new IllegalStateException("unhandled: "+methodType); } out.print(" public " + returnType + " " + methodName + "("); // // If the remote procedure does not have any parameters, then // parameters will be null. Otherwise it contains a vector with // information about the individual parameters, which we use // in order to generate the parameter list. Note that all // parameters are named at this point (they will either have a // user supplied name, or an automatically generated one). // int paramsKind; boolean anyParamsPrinted = false; if (proc.parameters != null) { int psize = proc.parameters.size(); for (int pidx = 0; pidx < psize; ++pidx) { JrpcgenParamInfo paramInfo = (JrpcgenParamInfo) proc.parameters.elementAt(pidx); if (pidx > 0) { out.print(", "); } out.print(checkForSpecials(paramInfo.parameterType)); out.print(" "); out.print(paramInfo.parameterName); } // // Now find out what kind of parameter(s) we have. In case // the remote procedure only expects a single parameter, check // whether it is a base type. In this case we later need to // wrap the single parameter. If the remote procedure expects // more than a single parameter, then we always need a // XDR wrapper. // if (psize > 1) { paramsKind = PARAMS_MORE; } else { // // psize must be equal to one, otherwise proc.parameters // must have been null. // String firstParamType = ((JrpcgenParamInfo) proc.parameters.elementAt(0)).parameterType; if (xdrBaseType(checkForSpecials(firstParamType)) == null) { // // No, it is not a base type, so we don't need one // of the special XDR wrapper classes. // paramsKind = PARAMS_SINGLE; } else { // // The single parameter to the remote procedure is // a base type, so we will later need a wrapper. // paramsKind = PARAMS_SINGLE_BASETYPE; } } anyParamsPrinted = true; } else { // // Remote procedure does not expect parameters at all. // paramsKind = PARAMS_VOID; } if (ClientMethodType.CALLBACK.equals(methodType)) { //add callback handler argument if (anyParamsPrinted) { out.print(", "); } out.print("CompletionHandler<RpcReply, XdrTransport> completionHandler"); anyParamsPrinted = true; } if (generateTimeoutSupport && !ClientMethodType.ONE_WAY.equals(methodType) && !ClientMethodType.FUTURE.equals(methodType)) { //add timeout arguments if (anyParamsPrinted) { out.print(", "); } out.print("long _timeoutValue, TimeUnit _timeoutUnit"); anyParamsPrinted = true; } if (generatePerCallAuthSupport) { //add auth argument if (anyParamsPrinted) { out.print(", "); } out.print("RpcAuth _auth"); anyParamsPrinted = true; } out.println(")"); if (generateTimeoutSupport && ClientMethodType.SYNC.equals(methodType)) { out.println(" throws OncRpcException, IOException, TimeoutException {"); } else { out.println(" throws OncRpcException, IOException {"); } // // Do generate code for wrapping parameters here, if necessary. // String xdrParamsName = null; // Name of variable representing XDR-able arguments switch (paramsKind) { case PARAMS_VOID: xdrParamsName = "args$"; out.println(" XdrVoid args$ = XdrVoid.XDR_VOID;"); break; case PARAMS_SINGLE: { JrpcgenParamInfo paramInfo = (JrpcgenParamInfo) proc.parameters.elementAt(0); xdrParamsName = paramInfo.parameterName; // // We do not need to emit an args$ declaration here, as we // can immediately make use of the one and only argument // the remote procedure expects. // break; } case PARAMS_SINGLE_BASETYPE: { JrpcgenParamInfo paramInfo = (JrpcgenParamInfo) proc.parameters.elementAt(0); xdrParamsName = "args$"; String xdrParamsType = xdrBaseType(checkForSpecials(paramInfo.parameterType)); out.println(" " + xdrParamsType + " args$ = new " + xdrParamsType + "(" + paramInfo.parameterName + ");"); break; } case PARAMS_MORE: xdrParamsName = "args$"; out.println(" class XdrAble$ implements XdrAble {"); int psize = proc.parameters.size(); for (int pidx = 0; pidx < psize; ++pidx) { JrpcgenParamInfo pinfo = (JrpcgenParamInfo) proc.parameters.elementAt(pidx); out.println(" public " + checkForSpecials(pinfo.parameterType) + " " + pinfo.parameterName + ";"); } out.println(" public void xdrEncode(XdrEncodingStream xdr)"); out.println(" throws OncRpcException, IOException {"); // // Emit serialization code for all parameters. // Note that not we do not need to deal with all kinds of // parameters here, as things like "int<5>" are invalid, // a typedef declaration is then necessary. // JrpcgenDeclaration decl = new JrpcgenDeclaration(null, null); for (int pidx = 0; pidx < psize; ++pidx) { JrpcgenParamInfo pinfo = (JrpcgenParamInfo) proc.parameters.elementAt(pidx); decl.kind = JrpcgenDeclaration.SCALAR; decl.identifier = pinfo.parameterName; decl.type = pinfo.parameterType; out.print(" "); out.print(codingMethod(decl, true)); } out.println(" }"); out.println(" public void xdrDecode(XdrDecodingStream xdr)"); out.println(" throws OncRpcException, IOException {"); out.println(" }"); out.println(" };"); out.println(" XdrAble$ args$ = new XdrAble$();"); for (int pidx = 0; pidx < psize; ++pidx) { JrpcgenParamInfo pinfo = (JrpcgenParamInfo) proc.parameters.elementAt(pidx); out.println(" args$." + pinfo.parameterName + " = " + pinfo.parameterName + ";"); } break; } // // Check the return data type of the result to be of one of // the base data types, like int, boolean, etc. In this case we // have to unwrap the result from one of the special XDR wrapper // classes and return the base data type instead. // String xdrResultType = xdrBaseType(resultType); String timeoutArgsBit = generateTimeoutSupport ? ", _timeoutValue, _timeoutUnit" : ""; String authArgBit = generatePerCallAuthSupport ? ", _auth" : ""; switch (methodType) { case SYNC: // // Handle the result of the method: similiar to what we did // above. However, in all other cases we always need to // create a result object, regardless of whether we have to // deal with a basic data type (except void) or with some // "complex" data type. // if (resultType.equals("void")) { out.println(" XdrVoid result$ = XdrVoid.XDR_VOID;"); } else if (xdrResultType != null) { out.println(" " + xdrResultType + " result$ = new " + xdrResultType + "();"); } else { out.println(" " + resultType + " result$ = new " + resultType + "();"); } // // Now emit the real ONC/RPC call using the (optionally // wrapped) parameter and (optionally wrapped) result. // out.println(" client.call(" + baseClassname + "." + proc.procedureId + ", " + xdrParamsName + ", result$" + timeoutArgsBit + authArgBit + ");"); // // In case of a wrapped result we need to return the value // of the wrapper, otherwise we can return the result // itself (which then is not a base data type). As a special // case, we can not return void values...anyone for a // language design with first class void objects?! // if (xdrResultType != null) { // // Data type of result is a Java base data type, so we need // to unwrap the XDR-able result -- if it's not a void, which // we do not need to return at all. // if (!resultType.equals("void")) { out.println(" return result$." + resultType.toLowerCase() + "Value();"); } } else { // // Data type of result is a complex type (class), so we // do not unwrap it but can return it immediately. // out.println(" return result$;"); } break; case FUTURE: out.println(" return client.call(" + baseClassname + "." + proc.procedureId + ", " + xdrParamsName + ", " + boxForTransport(resultType) + ".class" + authArgBit + ");"); break; case CALLBACK: out.println(" client.call(" + baseClassname + "." + proc.procedureId + ", " + xdrParamsName + ", completionHandler" + timeoutArgsBit + authArgBit + ");"); break; case ONE_WAY: out.println(" client.call(" + baseClassname + "." + proc.procedureId + ", " + xdrParamsName + ", (CompletionHandler) null" + authArgBit + ");"); break; default: throw new IllegalStateException("unhandled: "+methodType); } // // Close the stub method (just as a hint, as it is // getting rather difficult to see what code is produced // at this stage...) // out.println(" }"); out.println(); } /** * Generate source code for the client stub proxy object. This client * stub proxy object is then used by client applications to make remote * procedure (aka method) calls to an ONC/RPC server. */ public static void dumpClient(JrpcgenProgramInfo programInfo) { // // When several versions of a program are defined, we search for the // latest and greatest one. This highest version number ist then // used to create the necessary <code>OncRpcClient</code> for // communication when the client proxy stub is constructed. // int numVersions = programInfo.versions.size(); JrpcgenVersionInfo maxVersionInfo = null; int maxVersion = Integer.MIN_VALUE; for (Object o : programInfo.versions) { JrpcgenVersionInfo versionInfo = (JrpcgenVersionInfo) o; int version = Integer.parseInt(versionInfo.versionNumber); if (maxVersionInfo == null || version > maxVersion) { maxVersionInfo = versionInfo; maxVersion = version; } } // // Create new source code file containing a Java class representing // the XDR struct. // In case we have several programs defines, build the source code // file name from the program's name (this case is identified by a // null clientClass name). // String clientClass = jrpcgen.clientClass; if (clientClass == null) { clientClass = baseClassname + "_" + programInfo.programId + "_Client"; System.out.println("CLIENT: " + clientClass); } PrintWriter out = createJavaSourceFile(clientClass); out.println("import java.io.Closeable;"); out.println("import java.net.InetAddress;"); if (generateAsyncFutureClient) { out.println("import java.util.concurrent.Future;"); } if (generateAsyncCallbackClient || generateOneWayClient) { out.println("import java.nio.channels.CompletionHandler;"); } if (generateTimeoutSupport) { out.println("import java.util.concurrent.TimeUnit;"); out.println("import java.util.concurrent.TimeoutException;"); } out.println(); out.println("/**"); out.println(" * The class <code>" + clientClass + "</code> implements the client stub proxy"); out.println(" * for the " + programInfo.programId + " remote program. It provides method stubs"); out.println(" * which, when called, in turn call the appropriate remote method (procedure)."); out.println(" */"); out.println("public class " + clientClass + " implements Closeable {"); out.println(); // generated class fields out.println(" private final OncRpcClient rpcClient;"); out.println(" private final RpcCall client;"); out.println(); // // Generate constructors... // out.println(" /**"); out.println(" * see {@link #" + clientClass + "(InetAddress, int, RpcAuth, int, int, int , int, IoStrategy)}"); out.println(" */"); out.println(" public " + clientClass + "(InetAddress host, int port)"); out.println(" throws OncRpcException, IOException {"); out.println(" this(host, port, new RpcAuthTypeNone(), " + baseClassname + "." + programInfo.programId + ", " + baseClassname + "." + maxVersionInfo.versionId + ", IpProtocolType.TCP, 0, IoStrategy.SAME_THREAD);"); out.println(" }"); out.println(); out.println(" /**"); out.println(" * see {@link #" + clientClass + "(InetAddress, int, RpcAuth, int, int, int , int, IoStrategy)}"); out.println(" */"); out.println(" public " + clientClass + "(InetAddress host, int port, int version)"); out.println(" throws OncRpcException, IOException {"); out.println(" this(host, port, new RpcAuthTypeNone(), " + baseClassname + "." + programInfo.programId + ", version, IpProtocolType.TCP, 0, IoStrategy.SAME_THREAD);"); out.println(" }"); out.println(); out.println(" /**"); out.println(" * see {@link #" + clientClass + "(InetAddress, int, RpcAuth, int, int, int , int, IoStrategy)}"); out.println(" */"); out.println(" public " + clientClass + "(InetAddress host, int port, int program, int version, int protocol)"); out.println(" throws OncRpcException, IOException {"); out.println(" this(host, port, new RpcAuthTypeNone(), program, version, protocol, 0, IoStrategy.SAME_THREAD);"); out.println(" }"); out.println(); out.println(" /**"); out.println(" * see {@link #" + clientClass + "(InetAddress, int, RpcAuth, int, int, int , int, IoStrategy)}"); out.println(" */"); out.println(" public " + clientClass + "(InetAddress host, int port, RpcAuth auth, int program, int version, int protocol)"); out.println(" throws OncRpcException, IOException {"); out.println(" this(host, port, auth, program, version, protocol, 0, IoStrategy.SAME_THREAD);"); out.println(" }"); out.println(); out.println(" /**"); out.println(" * see {@link #" + clientClass + "(InetAddress, int, RpcAuth, int, int, int , int, IoStrategy)}"); out.println(" */"); out.println(" public " + clientClass + "(InetAddress host, int port, RpcAuth auth, int program, int version, int protocol, int localPort)"); out.println(" throws OncRpcException, IOException {"); out.println(" this(host, port, auth, program, version, protocol, localPort, IoStrategy.SAME_THREAD);"); out.println(" }"); out.println(); out.println(" /**"); out.println(" * Constructs a <code>" + clientClass + "</code> client stub proxy object"); out.println(" * from which the " + programInfo.programId + " remote program can be accessed."); out.println(" * @param host Internet address of host where to contact the remote program."); out.println(" * @param port Port number at host where the remote program can be reached."); out.println(" * @param auth {@link RpcAuth} to be used for RPC client authentication."); out.println(" * @param program Remote program number."); out.println(" * @param version Remote program version number."); out.println(" * @param protocol {@link org.dcache.xdr.IpProtocolType} to be"); out.println(" * used for ONC/RPC calls."); out.println(" * @param localPort local port to bind to. <=0 for any (ephemeral)"); out.println(" * @param ioStrategy io handling strategy. null for default"); out.println(" * @throws OncRpcException if an ONC/RPC error occurs."); out.println(" * @throws IOException if an I/O error occurs."); out.println(" */"); out.println(" public " + clientClass + "(InetAddress host, int port, RpcAuth auth, int program, int version, int protocol, int localPort, IoStrategy ioStrategy)"); out.println(" throws OncRpcException, IOException {"); out.println(" rpcClient = new OncRpcClient(host, protocol, port, localPort, ioStrategy);"); out.println(" try {"); out.println(" client = new RpcCall(program, version, auth, rpcClient.connect());"); out.println(" } catch (IOException e) {"); out.println(" rpcClient.close();"); out.println(" throw e;"); out.println(" } "); out.println(" }"); out.println(); out.println(" /**"); out.println(" * Shutdown client connection."); out.println(" *"); out.println(" * @throws IOException"); out.println(" */"); out.println(" public void shutdown() throws IOException {"); out.println(" rpcClient.close();"); out.println(" }"); out.println(); out.println(" @Override"); out.println(" public void close() throws IOException {"); out.println(" shutdown();"); out.println(" }"); out.println(); // // Generate method stubs... This is getting hairy in case someone // uses basic data types as parameters or the procedure's result. // In these cases we need to encapsulate these basic data types in // XDR-able data types. // for (int versionIdx = 0; versionIdx < numVersions; ++versionIdx) { JrpcgenVersionInfo versionInfo = (JrpcgenVersionInfo) programInfo.versions.elementAt(versionIdx); dumpClientStubMethods(out, versionInfo); } // // Close class...done! // out.println("}"); closeJavaSourceFile(); } /** * */ public static void dumpServerStubMethodCall(PrintWriter out, JrpcgenProcedureInfo proc) { // // Check for special return types, like enumerations, which we // map to their corresponding Java base data type. // String resultType = checkForSpecials(proc.resultType); // // If the remote procedure does not have any parameters, then // parameters will be null. Otherwise it contains a vector with // information about the individual parameters, which we use // in order to generate the parameter list. Note that all // parameters are named at this point (they will either have a // user supplied name, or an automatically generated one). // int paramsKind; if (proc.parameters != null) { int psize = proc.parameters.size(); // // Now find out what kind of parameter(s) we have. In case // the remote procedure only expects a single parameter, check // whether it is a base type. In this case we later need to // wrap the single parameter. If the remote procedure expects // more than a single parameter, then we always need a // XDR wrapper. // if (psize > 1) { paramsKind = PARAMS_MORE; } else { // // psize must be equal to one, otherwise proc.parameters // must have been null. // String firstParamType = ((JrpcgenParamInfo) proc.parameters.elementAt(0)).parameterType; if (xdrBaseType(checkForSpecials(firstParamType)) == null) { // // No, it is not a base type, so we don't need one // of the special XDR wrapper classes. // paramsKind = PARAMS_SINGLE; } else { // // The single parameter to the remote procedure is // a base type, so we will later need a wrapper. // paramsKind = PARAMS_SINGLE_BASETYPE; } } } else { // // Remote procedure does not expect parameters at all. // paramsKind = PARAMS_VOID; } // // Do generate code for unwrapping here, if necessary. // String params = ""; switch (paramsKind) { case PARAMS_VOID: // // Almost nothing to do here -- well, we need to retrieve nothing // so the RPC layer can do its book keeping. // out.println(" call.retrieveCall(XdrVoid.XDR_VOID);"); params = "call"; break; case PARAMS_SINGLE: { // // Only a single parameter, which is in addition immediately // ready for serialization. // JrpcgenParamInfo paramInfo = (JrpcgenParamInfo) proc.parameters.elementAt(0); out.println(" " + paramInfo.parameterType + " args$ = new " + paramInfo.parameterType + "();"); out.println(" call.retrieveCall(args$);"); params = "call, args$"; break; } case PARAMS_SINGLE_BASETYPE: { // // Only a single parameter, but we have to unwrap it first. // JrpcgenParamInfo paramInfo = (JrpcgenParamInfo) proc.parameters.elementAt(0); String paramsType = checkForSpecials(paramInfo.parameterType); String xdrParamsType = xdrBaseType(paramsType); out.println(" " + xdrParamsType + " args$ = new " + xdrParamsType + "();"); out.println(" call.retrieveCall(args$);"); params = "call, args$." + paramsType.toLowerCase() + "Value()"; break; } case PARAMS_MORE: { // // // StringBuilder paramsBuff = new StringBuilder(); out.println(" class XdrAble$ implements XdrAble {"); int psize = proc.parameters.size(); for (int pidx = 0; pidx < psize; ++pidx) { JrpcgenParamInfo pinfo = (JrpcgenParamInfo) proc.parameters.elementAt(pidx); out.println(" public " + checkForSpecials(pinfo.parameterType) + " " + pinfo.parameterName + ";"); } out.println(" public void xdrEncode(XdrEncodingStream xdr)"); out.println(" throws OncRpcException, IOException {"); out.println(" }"); out.println(" public void xdrDecode(XdrDecodingStream xdr)"); out.println(" throws OncRpcException, IOException {"); // // Emit serialization code for all parameters. // Note that not we do not need to deal with all kinds of // parameters here, as things like "int<5>" are invalid, // a typedef declaration is then necessary. // JrpcgenDeclaration decl = new JrpcgenDeclaration(null, null); for (int pidx = 0; pidx < psize; ++pidx) { JrpcgenParamInfo pinfo = (JrpcgenParamInfo) proc.parameters.elementAt(pidx); decl.kind = JrpcgenDeclaration.SCALAR; decl.identifier = pinfo.parameterName; decl.type = pinfo.parameterType; out.print(" "); out.print(codingMethod(decl, false)); } out.println(" }"); out.println(" };"); out.println(" XdrAble$ args$ = new XdrAble$();"); out.println(" call.retrieveCall(args$);"); if (psize > 0) { paramsBuff.append("call, "); } else { paramsBuff.append("call"); } for (int pidx = 0; pidx < psize; ++pidx) { JrpcgenParamInfo pinfo = (JrpcgenParamInfo) proc.parameters.elementAt(pidx); if (pidx > 0) { paramsBuff.append(", "); } paramsBuff.append("args$."); paramsBuff.append(pinfo.parameterName); } params = paramsBuff.toString(); break; } } // // Check the return data type of the result to be of one of // the base data types, like int, boolean, etc. In this case we // have to unwrap the result from one of the special XDR wrapper // classes and return the base data type instead. // String xdrResultType = xdrBaseType(resultType); if (resultType.equals("void")) { // // It's a remote procedure, so it does return simply nothing. // We use the singleton XDR_VOID to return a "nothing". // out.println(" " + proc.procedureId + "(" + params + ");"); out.println(" call.reply(XdrVoid.XDR_VOID);"); } else if (xdrResultType != null) { // // The return type is some Java base data type, so we need to // wrap the return value before we can serialize it. // out.println(" " + xdrResultType + " result$ = new " + xdrResultType + "(" + proc.procedureId + "(" + params + "));"); out.println(" call.reply(result$);"); } else { // // The return type is a complex type which supports XdrAble. // out.println(" " + resultType + " result$ = " + proc.procedureId + "(" + params + ");"); out.println(" call.reply(result$);"); } } /** * Generate public abstract method signatures for all remote procedure * calls. This ensures that they have to be implemented before any * derived server class gets useful. */ public static void dumpServerStubMethods(PrintWriter out, JrpcgenVersionInfo versionInfo) { int procSize = versionInfo.procedures.size(); for (int idx = 0; idx < procSize; ++idx) { JrpcgenProcedureInfo proc = (JrpcgenProcedureInfo) versionInfo.procedures.elementAt(idx); // // Fold enumerations et cetera back to their Java base data types. // String resultType = checkForSpecials(proc.resultType); // // Now emit the method signature, checking each argument for // specials, like enumerations. Also take care of no parameters // at all... Fortunately, this is relatively easy as we do not // need to care about parameter wrapping/unwrapping here. // out.print(" public abstract " + resultType + " " + proc.procedureId + "("); if (proc.parameters != null) { out.print("RpcCall call$, "); int psize = proc.parameters.size(); for (int pidx = 0; pidx < psize; ++pidx) { JrpcgenParamInfo paramInfo = (JrpcgenParamInfo) proc.parameters.elementAt(pidx); if (pidx > 0) { out.print(", "); } out.print(checkForSpecials(paramInfo.parameterType)); out.print(" "); out.print(paramInfo.parameterName); } } else { out.print("RpcCall call$"); } out.println(");"); out.println(); } } /** * */ public static void dumpServer(JrpcgenProgramInfo programInfo) { // // Create new source code file containing a Java class representing // the XDR struct. // In case we have several programs defines, build the source code // file name from the program's name (this case is identified by a // null clientClass name). // String serverClass = jrpcgen.serverClass; if (serverClass == null) { serverClass = baseClassname + "_" + programInfo.programId + "_ServerStub"; } PrintWriter out = createJavaSourceFile(serverClass); out.println("import org.dcache.xdr.*;"); out.println(); out.println("/**"); out.println(" */"); out.println("public abstract class " + serverClass + " implements RpcDispatchable {"); out.println(); int versionSize = programInfo.versions.size(); // // Generate dispatcher code... // out.println(" public void dispatchOncRpcCall(RpcCall call)"); out.println(" throws OncRpcException, IOException {"); out.println(); out.println(" int version = call.getProgramVersion();"); out.println(" int procedure = call.getProcedure();"); out.println(); for (int versionIdx = 0; versionIdx < versionSize; ++versionIdx) { JrpcgenVersionInfo versionInfo = (JrpcgenVersionInfo) programInfo.versions.elementAt(versionIdx); out.print(versionIdx == 0 ? " " : " } else "); out.println("if ( version == " + versionInfo.versionNumber + " ) {"); int procSize = versionInfo.procedures.size(); out.println(" switch ( procedure ) {"); for (int procIdx = 0; procIdx < procSize; ++procIdx) { // // Emit case arms for every procedure defined. We have to // take care that the procedure number might be a constant // comming from an enumeration: in this case we need also to // dump the enclosure. // JrpcgenProcedureInfo procInfo = (JrpcgenProcedureInfo) versionInfo.procedures.elementAt(procIdx); out.println(" case " + checkForEnumValue(procInfo.procedureNumber) + ": {"); dumpServerStubMethodCall(out, procInfo); out.println(" break;"); out.println(" }"); } out.println(" default:"); out.println(" call.failProcedureUnavailable();"); out.println(" }"); } out.println(" } else {"); out.println(" call.failProgramUnavailable();"); out.println(" }"); out.println(" }"); out.println(); // // Generate the stub methods for all specified remote procedures. // for (int versionIdx = 0; versionIdx < versionSize; ++versionIdx) { JrpcgenVersionInfo versionInfo = (JrpcgenVersionInfo) programInfo.versions.elementAt(versionIdx); dumpServerStubMethods(out, versionInfo); } // // Close class...done! // out.println("}"); closeJavaSourceFile(); } /** * Create the source code files based on the parsed information from the * x-file. */ public static void dumpFiles() { dumpConstants(); dumpClasses(); for (JrpcgenProgramInfo progInfo: programInfos) { if (!noClient) { dumpClient(progInfo); } if (!noServer) { dumpServer(progInfo); } } } /** * The main part of jrpcgen where all things start. */ public static void main(String[] args) { // // First parse the command line (options)... // int argc = args.length; int argIdx = 0; for (; argIdx < argc; ++argIdx) { // // Check to see whether this is an option... // String arg = args[argIdx]; if ((arg.length() > 0) && (arg.charAt(0) != '-')) { break; } // // ...and which option is it? // if (arg.equals("-d")) { // -d <dir> if (++argIdx >= argc) { System.out.println("jrpcgen: missing directory"); System.exit(1); } destinationDir = new File(args[argIdx]); } else if (arg.equals("-package") || arg.equals("-p")) { // -p <package name> if (++argIdx >= argc) { System.out.println("jrpcgen: missing package name"); System.exit(1); } packageName = args[argIdx]; } else if (arg.equals("-c")) { // -c <class name> if (++argIdx >= argc) { System.out.println("jrpcgen: missing client class name"); System.exit(1); } clientClass = args[argIdx]; } else if (arg.equals("-s")) { // -s <class name> if (++argIdx >= argc) { System.out.println("jrpcgen: missing server class name"); System.exit(1); } serverClass = args[argIdx]; } else if (arg.equals("-ser")) { makeSerializable = true; } else if (arg.equals("-bean")) { makeSerializable = true; makeBean = true; } else if (arg.equals("-initstrings")) { initStrings = true; } else if (arg.equals("-debug")) { debug = true; } else if (arg.equals("-nobackup")) { noBackups = true; } else if (arg.equals("-noclient")) { noClient = true; } else if (arg.equals("-noserver")) { noServer = true; } else if (arg.equals("-parseonly")) { parseOnly = true; } else if (arg.equals("-verbose")) { verbose = true; } else if (arg.equals("-asyncfuture")) { generateAsyncFutureClient = true; } else if (arg.equals("-asynccallback")) { generateAsyncCallbackClient = true; } else if (arg.equals("-oneway")) { generateOneWayClient = true; } else if (arg.equals("-nosync")) { generateSyncClient = false; } else if (arg.equals("-timeouts")) { generateTimeoutSupport = true; } else if (arg.equals("-percallauth")) { generatePerCallAuthSupport = true; } else if (arg.equals("-version")) { System.out.println("jrpcgen version \"" + VERSION + "\""); System.exit(1); } else if (arg.equals("-help") || arg.equals("-?")) { printHelp(); System.exit(1); } else if (arg.equals("--")) { // // End of options... // ++argIdx; break; } else { // // It's an unknown option! // System.out.println("Unrecognized option: " + arg); System.exit(1); } } if (packageName != null && !packageName.isEmpty()) { destinationDir = new File(destinationDir, packageName.replaceAll("\\.", Matcher.quoteReplacement(File.separator))); if (!destinationDir.exists() && !destinationDir.mkdirs()) { throw new IllegalStateException("unable to create path " + destinationDir); } } // // Otherwise we regard the current command line argument to be the // name of the x-file to compile. Check, that there is exactly one // x-file specified. // if ((argIdx >= argc) || (argIdx < argc - 1)) { printHelp(); System.exit(1); } String xfilename = args[argIdx]; xFile = new File(xfilename); // // Try to parse the file and generate the different class source // code files... // try { doParse(); } catch (Throwable t) { System.out.println(t.getMessage()); // // Exit application with non-zero outcome, so in case jrpcgen is // used as part of, for instance, a make process, such tools can // detect that there was a problem. // System.exit(1); } } /** * The real parsing and code generation part. This has been factored out * of main() in order to make it available as an Ant task. */ public static void doParse() throws FileNotFoundException, Exception { // // Get the base name for the client and server classes, it is derived // from the filename. // if (baseClassname == null) { String name = xFile.getName(); int dotIdx = name.lastIndexOf('.'); if (dotIdx < 0) { baseClassname = name; } else { baseClassname = name.substring(0, dotIdx); } } // // // FileInputStream in = null; try { in = new FileInputStream(xFile.getCanonicalPath()); } catch (FileNotFoundException e) { throw (new FileNotFoundException("jrpcgen: can not open source x-file \"" + xFile.getCanonicalPath() + "\"")); } JrpcgenScanner scanner = new JrpcgenScanner(in); JrpcgenParser parser = new JrpcgenParser(scanner); jrpcgen.globalIdentifiers.put("TRUE", new JrpcgenConst("TRUE", "true")); jrpcgen.globalIdentifiers.put("FALSE", new JrpcgenConst("FALSE", "false")); try { Symbol sym = parser.parse(); if (!parseOnly) { if (programInfos.size() <= 1) { if (clientClass == null) { clientClass = baseClassname + "Client"; } if (serverClass == null) { serverClass = baseClassname + "ServerStub"; } } dumpFiles(); } } catch (JrpcgenParserException pe) { throw (new Exception("jrpcgen: compilation aborted (" + pe.getMessage() + ")")); } } } /** * The class <code>JrpcgenEnDecodingInfo</code> contains information which * is necessary to generate source code calling appropriate XDR encoding * and decoding methods. * * @version $Revision: 1.4 $ $Date: 2005/11/11 21:28:48 $ $State: Exp $ $Locker: $ * @author Harald Albrecht */ class JrpcgenEnDecodingInfo { /** * Construct a <code>JrpcgenEnDecodingInfo</code> object containing * information for generating source code for encoding and decoding * of XDR/Java base data types. * * @param syllable Syllable of encoding/decoding method. * @param encodingOptions Optional parameters necessary to encode * base data type. * @param decodingOptions Optional parameters necessary to decode * base data type. */ public JrpcgenEnDecodingInfo(String syllable, String encodingOptions, String decodingOptions) { this.syllable = syllable; this.encodingOptions = encodingOptions; this.decodingOptions = decodingOptions; } /** * (Type) syllable of the encoding or decoding method. The full name * of the encoding or decoding method is always in the form of * "xdrEncodeXXX(...)" or "xdrDecodeXXX(...)", where "XXX" is the * syllable contained in this attribute. */ public String syllable; /** * Optional parameters to use when encoding a base data type. This * typically includes the size parameter for encoding fixed-size * vectors/arrays. When this attribute is not <code>null</code>, then * these parameters need to be appended. The attribute never contains * a leading parameter separator (aka "comma"). */ public String encodingOptions; /** * Optional parameters to use when decoding a base data type. This * typically includes the size parameter for decoding fixed-size * vectors/arrays. When this attribute is not <code>null</code>, then * these parameters need to be appended. The attribute never contains * a leading parameter separator (aka "comma"). */ public String decodingOptions; } // End of file jrpcgen.java