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; } }