www.pudn.com > cryptix-asn1-0.1.11.zip > Generator.java
/* $Id: Generator.java,v 1.4 2001/06/30 01:56:55 raif Exp $
*
* Copyright (C) 1997-2001 The Cryptix Foundation Limited. All rights reserved.
*
* Use, modification, copying and distribution of this software is subject to
* the terms and conditions of the Cryptix General Licence. You should have
* received a copy of the Cryptix General Licence along with this library; if
* not, you can download a copy from http://www.cryptix.org/
*/
package cryptix.asn1.tool;
import cryptix.asn1.analysis.DepthFirstAdapter;
import cryptix.asn1.lang.NodeInfo;
import cryptix.asn1.node.*;
import org.apache.log4j.Category;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
/**
* A class to interpret ASN.1 specifications and generate Java source code.
*
* @version $Revision: 1.4 $
* @author Raif S. Naffah
*/
public class Generator extends DepthFirstAdapter {
// Constants and vars
// -------------------------------------------------------------------------
private static Category cat = Category.getInstance(Generator.class);
private static final int NOT_GENERATING = 0;
private static final int GENERATING = 1;
private static final int REFERENCE_ONLY = 2;
/** The output stream where code is generated. */
private PrintWriter pw;
Interpreter ast;
int indent = 0;
int state;
String moduleName; // name of the package wehere class belongs
String pkgName; // name (dotted string) of mapped package wehere class belongs
String className; // name of the class we're generating
LinkedList data; // vector of element data objects
String lower; // sequence/set element name
String type; // sequence/set element type
String tag; // tag constructor for a sequence/set element
// Constructor(s)
// -------------------------------------------------------------------------
/**
* Constructs a code generator for a designated language operating on the
* designated AST.
*
* @param ast the parsed AST of the ASN.1 definitions as a
* cryptix.asn1.tools.Interpreter.
* @param languageID the constant defining a programming language in which
* to generate the source objects that will map to the ASN.1 constructs.
*/
public Generator(Interpreter ast) {
this.ast = ast;
data = new LinkedList();
}
// Class methods
// -------------------------------------------------------------------------
public static final String toJava(String name) {
cat.debug("==> toJava("+String.valueOf(name)+")");
if (name != null) {
// 1. replace multiple hyphens with 1 hyphen
int i = name.indexOf("--");
while (i > -1) {
name = name.substring(0, i) + name.substring(i+1);
i = name.indexOf("--");
}
// 2. replace hyphen-character with uppercase character
i = name.indexOf("-");
while (i > -1) {
name = name.substring(0, i) + toProper(name.substring(i+1));
i = name.indexOf("-");
}
}
cat.debug("<== toJava() --> "+String.valueOf(name));
return name;
}
private static final String toProper(String name) {
if (name.equals(""))
return name;
return name.substring(0, 1).toUpperCase() + name.substring(1);
}
// Instance methods
// -------------------------------------------------------------------------
/**
* Generates Java code for all components in all modules interpreted so
* far by the underlying Interpreter.
*
* Gets all modules from the underlying Interpreter, and repeatedly invokes
* the method with same name and 3 arguments.
*
* @param destination the directory where generated files will be stored.
* @param dict a java.util.Map that maps package names to ASN.1
* module names. If a module name is not found in this map, then the same
* name string will be used as the name of its enclosing package.
* @exception IOException if an I/O error occurs during (a) the creation of
* a new file (that will contain generated code) or its parent directory,
* (b) the construction of a FileWriter object wrapping a new file, or
* (c) the process of generating the code per-se.
*/
public void generate(File destination, Map dict)
throws IOException {
Map modules = ast.getModules();
Set keys = modules.keySet();
Object[] moduleNames = keys.toArray();
int limit = moduleNames.length;
for (int i = 0; i < limit; i++)
generate(destination, (String) moduleNames[i], dict);
}
/**
* Generates code for all constructs of a designated ASN.1 module, including
* a convenience Module Java interface which shall group all OIDs
* defined in that designated ASN.1 module.
*
* If any file at the same path location as any one that should be
* generated already exists, it is over-written.
*
* @param destination the directory where generated files will be stored.
* @param module the name of the ASN.1 module, which will become the name of
* the package containg the new classes.
* @param dict a java.util.Map that maps package names to ASN.1
* module names. If a module name is not found in this map, then the same
* name string will be used as the name of its enclosing package.
* @exception IOException if an I/O error occurs during (a) the creation of
* a new file (that will contain generated code) or its parent directory,
* (b) the construction of a FileWriter object wrapping a new file, or
* (c) the process of generating the code per-se.
*/
public void generate(File destination, String module, Map dict)
throws IOException {
Map moduleTypes = ast.getComponents(module);
Set types = moduleTypes.keySet();
cat.debug("Types defined in module "+String.valueOf(module)+":\n"+String.valueOf(moduleTypes));
Object[] typeNames = types.toArray();
for (int i = 0, limit = typeNames.length; i < limit; i++) {
String type = (String) typeNames[i];
if (isPermissibleType(type)) {
Node n = (Node) moduleTypes.get(type);
generate(destination, module, dict, toJava(type), n);
}
}
generateModule(destination, module, dict);
}
/**
* Generates code for a designated ASN.1 construct.
*
* If a file at the same path location as the one that should be generated
* already exists, it is over-written.
*
* @param destination the directory where generated files will be stored.
* @param module the name of the ASN.1 module, which will become the name of
* the package containg the new class.
* @param type the name of the defined ASN.1 type within the designated
* module, which will become the name of the Java Class (or other language
* equivalent) --defined within the designated package-- to generate.
* @param dict a java.util.Map that maps package names to ASN.1
* module names. If a module name is not found in this map, then the same
* name string will be used as the name of its enclosing package.
* @exception IOException if an I/O error occurs during (a) the creation of
* the new file (that will contain the generated code) or its parent
* directory, (b) the construction of a FileWriter object wrapping this new
* file, or (c) the process of generating the code per-se.
* @exception IllegalArgumentException if the type string does not start with
* an uppercase, or contains a dot.
*/
public void generate(File destination, String module, String type, Map dict)
throws IOException {
if (!isPermissibleType(type))
throw new IllegalArgumentException();
Node n = ast.getComponent(module, type);
generate(destination, module, dict, type, n);
}
// Overriden DepthFirstAdapter methods
// -------------------------------------------------------------------------
public void defaultIn(Node node) {
indent++;
String indentation = "";
for (int i = 0; i < indent; i++)
indentation += " ";
cat.debug(indentation+"--> "+node.getClass().getName());
}
public void defaultOut(Node node) {
String indentation = "";
for (int i = 0; i < indent; i++)
indentation += " ";
cat.debug(indentation+"<-- "+node.getClass().getName()+": "+node);
indent--;
}
public void inAIntegerBuiltInType(AIntegerBuiltInType node) {
defaultIn(node);
switch (state) {
case GENERATING:
boolean isPublic = ast.isPublic(moduleName, className);
outBuiltInClass(isPublic, className, "INTEGER");
state = REFERENCE_ONLY;
break;
}
}
public void outAIntegerBuiltInType(AIntegerBuiltInType node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
type = "INTEGER";
break;
}
}
public void outANamedNumber(ANamedNumber node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
String var = node.getLower().getText().trim();
Node n = node.getAuxNamedNum();
StringBuffer args = new StringBuffer("new ASNInteger(");
args.append("\"").append(className).append(".").append(var).append("\", ");
if (n instanceof APositiveAuxNamedNum) {
APositiveAuxNamedNum pann = (APositiveAuxNamedNum) n;
args.append("\"")
.append(pann.getNumber().getText().trim())
.append("\"");
} else if (n instanceof ANegativeAuxNamedNum) {
ANegativeAuxNamedNum nann = (ANegativeAuxNamedNum) n;
args.append("\"-")
.append(nann.getNumber().getText().trim())
.append("\"");
} else if (n instanceof ALowerAuxNamedNum) {
ALowerAuxNamedNum lann = (ALowerAuxNamedNum) n;
args.append(lann.getLower().getText().trim());
} else if (n instanceof AUpperAuxNamedNum) {
AUpperAuxNamedNum uann = (AUpperAuxNamedNum) n;
args.append(uann.getUpper().getText().trim())
.append(".")
.append(uann.getLower().getText().trim());
}
args.append(")");
outAttribute(className, var, args.toString());
break;
}
}
public void outANullType(ANullType node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
type = "NULL";
break;
}
}
public void inABooleanBuiltInType(ABooleanBuiltInType node) {
defaultIn(node);
switch (state) {
case GENERATING:
boolean isPublic = ast.isPublic(moduleName, className);
outBuiltInClass(isPublic, className, "BOOLEAN");
state = REFERENCE_ONLY;
break;
}
}
public void outABooleanBuiltInType(ABooleanBuiltInType node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
type = "BOOLEAN";
break;
}
}
public void inABitBuiltInType(ABitBuiltInType node) {
defaultIn(node);
switch (state) {
case GENERATING:
boolean isPublic = ast.isPublic(moduleName, className);
outBuiltInClass(isPublic, className, "BIT_STRING");
state = REFERENCE_ONLY;
break;
}
}
public void outABitBuiltInType(ABitBuiltInType node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
type = "BIT_STRING";
break;
}
}
public void inAOctetBuiltInType(AOctetBuiltInType node) {
defaultIn(node);
switch (state) {
case GENERATING:
boolean isPublic = ast.isPublic(moduleName, className);
outBuiltInClass(isPublic, className, "OCTET_STRING");
state = REFERENCE_ONLY;
break;
}
}
public void outAOctetBuiltInType(AOctetBuiltInType node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
type = "OCTET_STRING";
break;
}
}
public void inAAnyBuiltInType(AAnyBuiltInType node) {
defaultIn(node);
switch (state) {
case GENERATING:
boolean isPublic = ast.isPublic(moduleName, className);
outBuiltInClass(isPublic, className, "ANY");
state = REFERENCE_ONLY;
break;
}
}
public void outAAnyBuiltInType(AAnyBuiltInType node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
type = "ANY";
break;
}
}
public void inAOidBuiltInType(AOidBuiltInType node) {
defaultIn(node);
switch (state) {
case GENERATING:
boolean isPublic = ast.isPublic(moduleName, className);
outBuiltInClass(isPublic, className, "OBJECT_IDENTIFIER");
state = REFERENCE_ONLY;
break;
}
}
public void outAOidBuiltInType(AOidBuiltInType node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
type = "OBJECT_IDENTIFIER";
break;
}
}
public void inAPrintableBuiltInType(APrintableBuiltInType node) {
defaultIn(node);
switch (state) {
case GENERATING:
boolean isPublic = ast.isPublic(moduleName, className);
outBuiltInClass(isPublic, className, "PRINTABLE_STRING");
state = REFERENCE_ONLY;
break;
}
}
public void outAPrintableBuiltInType(APrintableBuiltInType node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
type = "PRINTABLE_STRING";
break;
}
}
public void inANumericBuiltInType(ANumericBuiltInType node) {
defaultIn(node);
switch (state) {
case GENERATING:
boolean isPublic = ast.isPublic(moduleName, className);
outBuiltInClass(isPublic, className, "NUMERIC_STRING");
state = REFERENCE_ONLY;
break;
}
}
public void outANumericBuiltInType(ANumericBuiltInType node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
type = "NUMERIC_STRING";
break;
}
}
public void inATeletexBuiltInType(ATeletexBuiltInType node) {
defaultIn(node);
switch (state) {
case GENERATING:
boolean isPublic = ast.isPublic(moduleName, className);
outBuiltInClass(isPublic, className, "T61_STRING");
state = REFERENCE_ONLY;
break;
}
}
public void outATeletexBuiltInType(ATeletexBuiltInType node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
type = "T61_STRING";
break;
}
}
public void inAVideotexBuiltInType(AVideotexBuiltInType node) {
defaultIn(node);
switch (state) {
case GENERATING:
boolean isPublic = ast.isPublic(moduleName, className);
outBuiltInClass(isPublic, className, "VIDEOTEX_STRING");
state = REFERENCE_ONLY;
break;
}
}
public void outAVideotexBuiltInType(AVideotexBuiltInType node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
type = "VIDEOTEX_STRING";
break;
}
}
public void inAVisibleBuiltInType(AVisibleBuiltInType node) {
defaultIn(node);
switch (state) {
case GENERATING:
boolean isPublic = ast.isPublic(moduleName, className);
outBuiltInClass(isPublic, className, "ISO646_STRING");
state = REFERENCE_ONLY;
break;
}
}
public void outAVisibleBuiltInType(AVisibleBuiltInType node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
type = "ISO646_STRING";
break;
}
}
public void inAIa5BuiltInType(AIa5BuiltInType node) {
defaultIn(node);
switch (state) {
case GENERATING:
boolean isPublic = ast.isPublic(moduleName, className);
outBuiltInClass(isPublic, className, "IA5_STRING");
state = REFERENCE_ONLY;
break;
}
}
public void outAIa5BuiltInType(AIa5BuiltInType node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
type = "IA5_STRING";
break;
}
}
public void inAGraphicBuiltInType(AGraphicBuiltInType node) {
defaultIn(node);
switch (state) {
case GENERATING:
boolean isPublic = ast.isPublic(moduleName, className);
outBuiltInClass(isPublic, className, "GRAPHIC_STRING");
state = REFERENCE_ONLY;
break;
}
}
public void outAGraphicBuiltInType(AGraphicBuiltInType node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
type = "GRAPHIC_STRING";
break;
}
}
public void inAGeneralBuiltInType(AGeneralBuiltInType node) {
defaultIn(node);
switch (state) {
case GENERATING:
boolean isPublic = ast.isPublic(moduleName, className);
outBuiltInClass(isPublic, className, "GENERAL_STRING");
state = REFERENCE_ONLY;
break;
}
}
public void outAGeneralBuiltInType(AGeneralBuiltInType node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
type = "GENERAL_STRING";
break;
}
}
public void inAUniversalBuiltInType(AUniversalBuiltInType node) {
defaultIn(node);
switch (state) {
case GENERATING:
boolean isPublic = ast.isPublic(moduleName, className);
outBuiltInClass(isPublic, className, "UNIVERSAL_STRING");
state = REFERENCE_ONLY;
break;
}
}
public void outAUniversalBuiltInType(AUniversalBuiltInType node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
type = "UNIVERSAL_STRING";
break;
}
}
public void inABmpBuiltInType(ABmpBuiltInType node) {
defaultIn(node);
switch (state) {
case GENERATING:
boolean isPublic = ast.isPublic(moduleName, className);
outBuiltInClass(isPublic, className, "BMP_STRING");
state = REFERENCE_ONLY;
break;
}
}
public void outABmpBuiltInType(ABmpBuiltInType node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
type = "BMP_STRING";
break;
}
}
public void inAGmtBuiltInType(AGmtBuiltInType node) {
defaultIn(node);
switch (state) {
case GENERATING:
boolean isPublic = ast.isPublic(moduleName, className);
outBuiltInClass(isPublic, className, "GENERALIZED_TIME");
state = REFERENCE_ONLY;
break;
}
}
public void outAGmtBuiltInType(AGmtBuiltInType node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
type = "GENERALIZED_TIME";
break;
}
}
public void inAUtcBuiltInType(AUtcBuiltInType node) {
defaultIn(node);
switch (state) {
case GENERATING:
boolean isPublic = ast.isPublic(moduleName, className);
outBuiltInClass(isPublic, className, "UTC_TIME");
state = REFERENCE_ONLY;
break;
}
}
public void outAUtcBuiltInType(AUtcBuiltInType node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
type = "UTC_TIME";
break;
}
}
public void inAChoiceBuiltInType(AChoiceBuiltInType node) {
defaultIn(node);
switch (state) {
case GENERATING:
data.clear();
boolean isPublic = ast.isPublic(moduleName, className);
outBuiltInClass(isPublic, className, "CHOICE");
state = REFERENCE_ONLY;
break;
case REFERENCE_ONLY:
break;
}
}
public void outAChoiceBuiltInType(AChoiceBuiltInType node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
//
// should handle inner classes here
//
if (data.isEmpty())
throw new RuntimeException("CHOICE with no alternatives");
generateChoice(data);
type = "CHOICE";
state = NOT_GENERATING;
break;
}
}
public void inATaggedAuxType(ATaggedAuxType node) {
StringBuffer sb = new StringBuffer("new Tag(");
Node c = node.getClazz();
Node n = node.getClassNumber();
Node t = node.getTagDefault();
if (c == null)
sb.append("Tag.CONTEXT, ");
else if (c instanceof AUniversalClazz)
sb.append("Tag.UNIVERSAL, ");
else if (c instanceof AAppClazz)
sb.append("Tag.APPLICATION, ");
else if (c instanceof APrivateClazz)
sb.append("Tag.PRIVATE, ");
if (n instanceof ANumericClassNumber)
sb.append(((ANumericClassNumber) n).getNumber().getText().trim());
else
throw new RuntimeException("Dont know how to handle this yet");
if (t == null) // should use module default
sb.append(", Module.EXPLICIT_TAGGING");
else if (t instanceof AExplicitTagDefault)
sb.append(", true");
else
sb.append(", false");
tag = sb.append(")").toString();
}
public void inASetOfAuxType(ASetOfAuxType node) {
defaultIn(node);
switch (state) {
case GENERATING:
boolean isPublic = ast.isPublic(moduleName, className);
outBuiltInClass(isPublic, className, "SET_OF");
data.clear();
state = REFERENCE_ONLY;
break;
case REFERENCE_ONLY:
break;
}
}
public void outASetOfAuxType(ASetOfAuxType node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
//
// should handle inner classes here
//
if (data.isEmpty())
generateCompound(type);
else
generateCompound(data);
type = "SET_OF";
state = NOT_GENERATING;
break;
}
}
public void inASetAuxType(ASetAuxType node) {
defaultIn(node);
switch (state) {
case GENERATING:
boolean isPublic = ast.isPublic(moduleName, className);
String superType = "SET";
Node n = node.getTypeSuf();
if (n != null && n instanceof ASizeTypeSuf)
superType += "_OF";
outBuiltInClass(isPublic, className, superType);
data.clear();
state = REFERENCE_ONLY;
break;
case REFERENCE_ONLY:
break;
}
}
public void outASetAuxType(ASetAuxType node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
//
// should handle inner classes here
//
String superType = "SET";
Node n = node.getTypeSuf();
if (n != null && n instanceof ASizeTypeSuf)
superType += "_OF";
if (data.isEmpty())
generateCompound(type);
else
generateCompound(data);
type = superType;
state = NOT_GENERATING;
break;
}
}
public void inASequenceOfAuxType(ASequenceOfAuxType node) {
defaultIn(node);
switch (state) {
case GENERATING:
boolean isPublic = ast.isPublic(moduleName, className);
outBuiltInClass(isPublic, className, "SEQUENCE_OF");
data.clear();
state = REFERENCE_ONLY;
break;
case REFERENCE_ONLY:
break;
}
}
public void outASequenceOfAuxType(ASequenceOfAuxType node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
//
// should handle inner classes here
//
if (data.isEmpty())
generateCompound(type);
else
generateCompound(data);
type = "SEQUENCE_OF";
state = NOT_GENERATING;
break;
}
}
public void inASequenceAuxType(ASequenceAuxType node) {
defaultIn(node);
switch (state) {
case GENERATING:
boolean isPublic = ast.isPublic(moduleName, className);
Node n = node.getTypeSuf();
String superType = "SEQUENCE";
if (n != null && n instanceof ASizeTypeSuf)
superType += "_OF";
outBuiltInClass(isPublic, className, superType);
data.clear();
state = REFERENCE_ONLY;
break;
case REFERENCE_ONLY:
break;
}
}
public void outASequenceAuxType(ASequenceAuxType node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
//
// should handle inner classes here
//
// boolean isPublic = ast.isPublic(moduleName, className);
String superType = "SEQUENCE";
Node n = node.getTypeSuf();
if (n != null && n instanceof ASizeTypeSuf)
superType += "_OF";
//
// outBuiltInClass(isPublic, className, superType);
if (data.isEmpty())
generateCompound(type);
else
generateCompound(data);
type = superType;
state = NOT_GENERATING;
break;
}
}
public void inANamedElementType(ANamedElementType node) {
defaultIn(node);
lower = null;
type = null;
tag = null;
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
break;
}
}
public void outANamedElementType(ANamedElementType node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
try {
NodeInfo lastLower = (NodeInfo) data.getLast();
if (lastLower.getElementName().equals(lower))
data.removeLast();
} catch (NoSuchElementException ignored) {
}
data.add(new NodeInfo(lower, type, tag, node.getElementTypeSuf()));
break;
}
}
public void outAUpperType(AUpperType node) {
defaultOut(node);
type = node.getUpper().getText().trim();
Node at = node.getAccessType();
if (at != null)
type += "."+((AAccessType) at).getUpper().getText().trim();
switch (state) {
case GENERATING:
boolean isPublic = ast.isPublic(moduleName, className);
outClass(isPublic, className, type);
outUpperConstructors(className, type);
state = REFERENCE_ONLY;
break;
case REFERENCE_ONLY:
break;
}
}
public void outALowerNamedType(ALowerNamedType node) {
defaultOut(node);
switch (state) {
case GENERATING:
break;
case REFERENCE_ONLY:
lower = node.getLower().getText().trim();
data.add(new NodeInfo(lower, type, tag, null));
break;
}
}
// Other instance methods -- Java source code generation per-se
// -------------------------------------------------------------------------
private void activate(PrintWriter pw) {
if (this.pw != null)
throw new IllegalStateException();
this.pw = pw;
}
private void passivate() {
if (pw != null)
pw.close();
pw = null;
}
private void outBeginClass(String pkg) {
cat.info("==> outBeginClass("+String.valueOf(pkg)+")");
out("package "+pkg+"; // machine generated code. DO NOT EDIT");
out();
out("import cryptix.asn1.lang.*;");
out();
cat.info("<== outBeginClass()");
}
private void outBuiltInClass(boolean isPublic, String className, String type) {
cat.info("==> outBuiltInClass("+String.valueOf(isPublic)+", "+String.valueOf(className)+", "+String.valueOf(type)+")");
outClass(isPublic, className, type);
outConstructors(className, type);
cat.info("<== outBuiltInClass()");
}
private void outClass(boolean isPublic, String className, String type) {
cat.info("==> outClass("+String.valueOf(isPublic)+", "+String.valueOf(className)+", "+String.valueOf(type)+")");
String superclass = superclassFromType(type);
StringBuffer sb = new StringBuffer();
if (isPublic)
sb.append("public ");
sb.append("class ").append(className).append(" extends ").append(superclass).append(" {");
out(sb.toString());
out();
cat.info("<== outClass()");
}
private void outEndClass() {
cat.info("==> outEndClass()");
out("}");
out();
out("// Generated by the cryptix ASN.1 kit on "+String.valueOf(new Date()));
cat.info("<== outEndClass()");
}
private void outConstructors(String className, String type) {
cat.info("==> outConstructors("+String.valueOf(className)+", "+String.valueOf(type)+")");
cat.info(" tag: "+String.valueOf(tag));
String superclass = superclassFromType(type);
out(" // Constructor(s)");
out(" // -------------------------------------------------------------------------");
out();
out(" /**");
out(" * Constructs a new instance of this type with a blank Name.");
out(" */");
out(" public "+className+"() {");
if (type.equals("CHOICE") || type.equals("ANY"))
out(" super(\"\", null);");
else
if (tag != null)
out(" super(\"\", "+tag+");");
else
out(" super(\"\", new Tag(Tag."+type+"));");
out(" }");
out();
out(" /**");
out(" * Constructs a new instance of this type with a designated Name.");
out(" *");
out(" * @param name the designated Name for this new instance.");
out(" */");
out(" public "+className+"(String name) {");
if (type.equals("CHOICE") || type.equals("ANY"))
out(" super(name, null);");
else
if (tag != null)
out(" super(name, "+tag+");");
else
out(" super(name, new Tag(Tag."+type+"));");
out(" }");
out();
out(" /**");
out(" * Constructs a new instance of this type with a designated Name and Tag.");
out(" *");
out(" * @param name the designated Name for this new instance.");
out(" * @param tag the designated tag for this new instance.");
out(" */");
out(" public "+className+"(String name, Tag tag) {");
out(" super(name, tag);");
out(" }");
out();
out(" /**");
out(" * Constructs a new instance of this type with a trivial Name and an");
out(" * initial value.");
out(" *");
out(" * @param value the initial value of this instance.");
out(" */");
out(" public "+className+"("+superclass+" value) {");
out(" this(\"\", value);");
out(" }");
out();
out(" /**");
out(" * Constructs a new instance of this type with a designated Name and an");
out(" * initial value.");
out(" *");
out(" * @param name the designated Name for this new instance.");
out(" * @param value the initial value of this instance.");
out(" */");
out(" public "+className+"(String name, "+superclass+" value) {");
if (type.equals("CHOICE") || type.equals("ANY"))
out(" this(name, null, value);");
else
if (tag != null)
out(" this(name, "+tag+", value);");
else
out(" this(name, new Tag(Tag."+type+"), value);");
out(" }");
out();
out(" /**");
out(" * Constructs a new instance of this type given its Name, Tag and initial");
out(" * value.");
out(" *");
out(" * @param name the designated Name for this new instance.");
out(" * @param tag the specific tag for this instance.");
out(" * @param value the initial value for this instance.");
out(" */");
out(" public "+className+"(String name, Tag tag, "+superclass+" value) {");
out(" super(name, tag, value == null ? null : value.value());");
out(" }");
out();
outBeginAttributes();
cat.info("<== outConstructors()");
}
private void outUpperConstructors(String className, String type) {
cat.info("==> outUpperConstructors("+String.valueOf(className)+", "+String.valueOf(type)+")");
String superclass = superclassFromType(type);
out(" // Constructor(s)");
out(" // -------------------------------------------------------------------------");
out();
out(" /**");
out(" * Constructs a new instance of this type with a blank Name.");
out(" */");
out(" public "+className+"() {");
out(" super();");
out(" }");
out();
out(" /**");
out(" * Constructs a new instance of this type with a designated Name.");
out(" *");
out(" * @param name the designated Name for this new instance.");
out(" */");
out(" public "+className+"(String name) {");
out(" super(name);");
out(" }");
out();
out(" /**");
out(" * Constructs a new instance of this type with a designated Name and Tag.");
out(" *");
out(" * @param name the designated Name for this new instance.");
out(" * @param tag the designated tag for this new instance.");
out(" */");
out(" public "+className+"(String name, Tag tag) {");
out(" super(name, tag);");
out(" }");
out();
out(" /**");
out(" * Constructs a new instance of this type with a trivial Name and an ");
out(" * initial value.");
out(" *");
out(" * @param value the initial value of this instance.");
out(" */");
out(" public "+className+"("+superclass+" value) {");
out(" super(value);");
out(" }");
out();
out(" /**");
out(" * Constructs a new instance of this type with a designated Name and an ");
out(" * initial value.");
out(" *");
out(" * @param name the designated Name for this new instance.");
out(" * @param value the initial value of this instance.");
out(" */");
out(" public "+className+"(String name, "+superclass+" value) {");
out(" super(name, value);");
out(" }");
out();
out(" /**");
out(" * Constructs a new instance of this type given its Name, Tag and initial");
out(" * values.");
out(" *");
out(" * @param name the designated Name for this new instance.");
out(" * @param tag the specific tag for this instance.");
out(" * @param value the initial value for this instance.");
out(" */");
out(" public "+className+"(String name, Tag tag, "+superclass+" value) {");
out(" super(name, tag, value);");
out(" }");
out();
outBeginAttributes();
cat.info("<== outUpperConstructors()");
}
private void outBeginAttributes() {
cat.info("==> outBeginAttributes()");
out(" // Constants and variables");
out(" // -------------------------------------------------------------------------");
out();
cat.info("<== outBeginAttributes()");
}
private void outAttribute(String className, String var, String literal) {
cat.info("==> outAttribute("+String.valueOf(className)+", "+String.valueOf(var)+", "+String.valueOf(literal)+")");
StringBuffer sb = new StringBuffer(" public static final ");
sb.append(className).append(" ").append(var)
.append(" = new ").append(className).append("(")
.append("\"").append(var).append("\", ")
.append(literal)
.append(");");
out(sb.toString());
cat.info("<== outAttribute()");
}
/**
* Generates Java source code for a CHOICE ASN.1 construct.
*
* @param data a LinkedList containing the CHOICE alternatives.
*/
private void generateChoice(LinkedList data) {
cat.info("==> generateChoice()");
generateCompound(data);
// generate convenience isXXX() methods
out(" // CHOICE-specific convenience methods");
out(" // -------------------------------------------------------------------------");
out();
int i = 0;
for (Iterator ei = data.iterator(); ei.hasNext(); i++) {
NodeInfo ed = (NodeInfo) ei.next();
String lower = toJava(ed.getElementName());
String type = ed.getElementType();
String superclass = superclassFromType(type);
String proper = properFromLower(lower);
out(" /**");
out(" * Returns true iff this CHOICE instance has been decoded, and its (only)");
out(" * concrete alternative is the designated one. False otherwise.");
out(" *");
out(" * @return true iff this CHOICE instance has been decoded, and its (only)");
out(" * concrete alternative is the designated one. False otherwise.");
out(" */");
out(" public boolean is"+proper+"() {");
out(" return !get"+proper+"().isBlank();");
out(" }");
}
cat.info("<== generateChoice()");
}
private void generateCompound(String type) {
cat.info("==> generateCompound("+String.valueOf(type)+")");
LinkedList data = new LinkedList();
String name = type.substring(0, 1).toLowerCase() + type.substring(1);
data.add(new NodeInfo(name, type, null, null));
generateCompound(data);
cat.info("<== generateCompound()");
}
private void generateCompound(LinkedList data) {
cat.info("==> generateCompound(LinkedList)");
Iterator ei;
out();
out(" // Over-loaded implementation of methods defined in superclass");
out(" // -------------------------------------------------------------------------");
out();
// generate components declarations
out(" protected void initInternal() {");
out(" super.initInternal();");
out();
for (ei = data.iterator(); ei.hasNext(); ) {
NodeInfo ed = (NodeInfo) ei.next();
String asnLower = ed.getElementName();
String lower = toJava(asnLower);
String type = ed.getElementType();
String superclass = superclassFromType(type);
String tag = ed.getTagValue();
StringBuffer sb = new StringBuffer();
sb.append(" IType ").append(lower).append(" = new ")
.append(superclass).append("(\"").append(asnLower).append("\"");
if (tag != null)
sb.append(", ").append(tag);
//---
Node ets = ed.getTypeSpecs();
if (ets != null && (ets instanceof ADefaultElementTypeSuf)) {
Node v = ((ADefaultElementTypeSuf) ets).getValue();
String tc = String.valueOf(v).trim();
sb.append(", ").append(superclass).append(".").append(tc);
}
//---
sb.append(");");
out(sb.toString());
//---
if (ets != null
&& ((ets instanceof AOptionalElementTypeSuf)
|| (ets instanceof ADefaultElementTypeSuf))) {
sb = new StringBuffer();
sb.append(" ").append(lower).append(".optional(true);");
out(sb.toString());
}
sb = new StringBuffer();
sb.append(" components.add(").append(lower).append(");");
out(sb.toString());
}
out(" }");
out();
// generate accessors
out(" // Accessor methods");
out(" // -------------------------------------------------------------------------");
out();
int i = 0;
for (ei = data.iterator(); ei.hasNext(); i++) {
NodeInfo ed = (NodeInfo) ei.next();
String lower = ed.getElementName();
String type = ed.getElementType();
String superclass = superclassFromType(type);
String proper = toJava(properFromLower(lower));
out(" public "+superclass+" get"+proper+"() {");
out(" return ("+superclass+") components.get("+i+");");
out(" }");
out();
out(" public void set"+proper+"("+superclass+" obj) {");
out(" "+superclass+" it = get"+proper+"();");
out(" it.value(obj.value());");
out(" components.set("+i+", it);");
out(" }");
out();
}
cat.info("<== generateCompound()");
}
/**
* Generates a convenience interface Java source file, grouping all defined
* OIDs in the designated ASN.1 module name.
*
* The name of the generate file is Module to be located in the
* designated destination directory, and belonging to a resolved package
* name (from the designated map). If such a file already exists it is
* over-written with just a warning printed to trace/debug output stream.
*
* @param destination the directory where generated files will be stored.
* @param module the name of the ASN.1 module, which will become the name of
* the package containg the new classes.
* @param dict a java.util.Map that maps package names to ASN.1
* module names. If a module name is not found in this map, then the same
* name string will be used as the name of its enclosing package.
* @exception IOException if an I/O error occurs during (a) the creation of
* a new file (that will contain generated code) or its parent directory,
* (b) the construction of a FileWriter object wrapping a new file, or
* (c) the process of generating the code per-se.
*/
private void generateModule(File destination, String module, Map dict)
throws IOException {
File f = setupVars(destination, module, dict, "Module");
activate(new PrintWriter(new FileWriter(f)));
outBeginClass(this.pkgName);
// the Module as a Java interface
// out("public interface Module {");
//
// Map oids = ast.getOIDs(module);
// Set keys = oids.keySet();
// for (Iterator it = keys.iterator(); it.hasNext(); ) {
// String name = (String) it.next();
// String value = (String) oids.get(name);
// out();
// out(" // OID "+name);
// name = name.toUpperCase();
// name = name.replace('-', '_');
// out(" ObjectIdentifier "+name+" = ObjectIdentifier.getInstance(\""+value+"\");");
// }
// or as a static class. this is preferred since no transformation of
// the name is necessary. user just refers to the ASN.1 specs file!
// on the other hand, we already transform the specs so this argument
// is not infallible.
out("public final class Module {");
out();
out(" // Constants and variables");
out(" // -------------------------------------------------------------------------");
out();
out(" /** If module has explicit tagging (true) or not (false). */");
out(" public static final boolean EXPLICIT_TAGGING = "+String.valueOf(!ast.getImplicitTagging(module))+";");
out();
out(" /** The class Singleton. */");
out(" private static Module singleton = null;");
out();
out(" /**");
out(" * The Map containing the name and string representations of OIDs defined.");
out(" * in this ASN.1 module.");
out(" */");
out(" private static java.util.Map map;");
out();
out(" // Constructor(s)");
out(" // -------------------------------------------------------------------------");
out();
out(" /** Trivial private constructor to enforce Singleton pattern. */");
out(" private Module() {");
out(" super();");
out();
out(" map = new java.util.HashMap();");
Map oids = ast.getOIDs(module);
Set keys = oids.keySet();
for (Iterator it = keys.iterator(); it.hasNext(); ) {
String name = (String) it.next();
out(" map.put(\""+name+"\", \""+(String) oids.get(name)+"\");");
}
out(" }");
out();
out(" // Class methods");
out(" // -------------------------------------------------------------------------");
out();
out(" /** @return the Singleton instance. */");
out(" public static synchronized Module instance() {");
out(" if (singleton == null)");
out(" singleton = new Module();");
out();
out(" return singleton;");
out(" }");
out();
out(" // Instance methods");
out(" // -------------------------------------------------------------------------");
out();
out(" /**");
out(" * @return the OID value for the designated user-defined OBJECT IDENTIFIER");
out(" * name.");
out(" */");
out(" public ObjectIdentifier getOID(String name) {");
out(" String oid = (String) map.get(name);");
out(" return ((oid == null) ? null : ObjectIdentifier.getInstance(oid));");
out(" }");
outEndClass();
passivate();
}
// other instance methods
// -------------------------------------------------------------------------
private void out() {
pw.println();
}
private void out(String s) {
pw.println(s);
}
/**
* Returns true if the given string is a permissible top-level ASN.1 type
* name; ie. it starts with an uppercase and does not contain dots.
*/
private boolean isPermissibleType(String type) {
return (Character.isUpperCase(type.charAt(0)) && type.indexOf(".") == -1);
}
private String superclassFromType(String type) {
String result;
if (type == null) result = "???";
else if (type.equals("BOOLEAN")) result = "ASNBoolean";
else if (type.equals("INTEGER")) result = "ASNInteger";
else if (type.equals("CHOICE")) result = "Choice";
else if (type.equals("BIT_STRING")) result = "BitString";
else if (type.equals("OCTET_STRING")) result = "OctetString";
else if (type.equals("NULL")) result = "Null";
else if (type.equals("OBJECT_IDENTIFIER")) result = "ObjectIdentifier";
else if (type.equals("SEQUENCE")) result = "Sequence";
else if (type.equals("SEQUENCE_OF")) result = "SequenceOf";
else if (type.equals("SET")) result = "Set";
else if (type.equals("SET_OF")) result = "SetOf";
else if (type.equals("PRINTABLE_STRING")) result = "PrintableString";
else if (type.equals("NUMERIC_STRING")) result = "NumericString";
else if (type.equals("T61_STRING")) result = "TeletexString";
else if (type.equals("VIDEOTEX_STRING")) result = "VideotexString";
else if (type.equals("IA5_STRING")) result = "IA5String";
else if (type.equals("GRAPHIC_STRING")) result = "GraphicString";
else if (type.equals("ISO646_STRING")) result = "VisibleString";
else if (type.equals("GENERAL_STRING")) result = "GeneralString";
else if (type.equals("UNIVERSAL_STRING")) result = "UniversalString";
else if (type.equals("BMP_STRING")) result = "BMPString";
else if (type.equals("UTC_TIME")) result = "UTCTime";
else if (type.equals("GENERALIZED_TIME")) result = "GeneralizedTime";
else if (type.equals("ANY")) result = "Any";
else result = type;
return result;
}
private String properFromLower(String lower) {
return (Character.toUpperCase(lower.charAt(0))+lower.substring(1));
}
private void generate(File destination, String module, Map dict, String type, Node n)
throws IOException {
File f = setupVars(destination, module, dict, type);
activate(new PrintWriter(new FileWriter(f)));
outBeginClass(this.pkgName);
state = GENERATING;
n.apply(this);
outEndClass();
passivate();
}
private File setupVars(File dir, String module, Map dict, String fileName)
throws IOException {
this.tag = null;
this.moduleName = module;
this.className = fileName;
this.pkgName = (String) dict.get(module);
if (this.pkgName == null)
this.pkgName = module;
cat.info("Generating java code for "+this.pkgName+"."+this.className);
File result = null;
String path = this.pkgName.replace('.', File.separatorChar);
File pkg = new File(dir, path);
pkg.mkdirs();
result = new File(pkg, this.className + ".java");
if (result.exists() && result.isFile()) {
cat.warn("Destination file "+result.getPath()+" already exists. Re-creating...");
result.delete();
result = new File(pkg, this.className + ".java");
}
result.createNewFile();
return result;
}
}