/* * @(#)TimerPointSourceGenerator.java 1.6 06/03/23 * * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package com.sun.corba.ee.impl.orbutil.newtimer ; import java.io.IOException ; import java.io.File ; import java.io.PrintStream ; import org.xml.sax.SAXException ; import org.xml.sax.Attributes ; import org.xml.sax.helpers.DefaultHandler ; import javax.xml.parsers.SAXParserFactory ; import javax.xml.parsers.ParserConfigurationException ; import javax.xml.parsers.SAXParser ; import java.util.List ; import java.util.ArrayList ; import java.util.Map ; import java.util.HashMap ; import java.util.Set ; import java.util.HashSet ; import java.util.Collections ; import java.util.Properties ; import java.io.IOException ; import com.sun.corba.ee.spi.orbutil.newtimer.Controllable ; import com.sun.corba.ee.spi.orbutil.newtimer.Timer ; import com.sun.corba.ee.spi.orbutil.newtimer.TimerGroup ; import com.sun.corba.ee.spi.orbutil.newtimer.TimerFactory ; import com.sun.corba.ee.spi.orbutil.newtimer.TimerFactoryBuilder ; import com.sun.corba.ee.spi.orbutil.generic.Pair ; import com.sun.corba.ee.impl.codegen.Identifier ; import com.sun.corba.ee.spi.codegen.Type ; import com.sun.corba.ee.spi.codegen.Expression ; import com.sun.corba.ee.spi.codegen.Signature ; import static java.lang.reflect.Modifier.* ; import static com.sun.corba.ee.spi.codegen.Wrapper.* ; /** Class used to compile an XML description of timer and timergroup * information into a Java source file. Uses the codegen library * to generate the source file. * * Note on bootstrapping: the time point file needs to be generated * before the bulk of the ORB is built. This requires compiling * the newtimer and codegen classes into a library file that is * checked into SCCS. */ public class TimerPointSourceGenerator { private static class TimingInfoProcessor { private boolean done = false ; private String pkg ; private TimerFactory tf ; private Map<String,List<String>> contents ; private TimerGroup currentTimerGroup ; private void checkForValidIdentifier( String name ) { if (!Identifier.isValidIdentifier( name )) throw new IllegalArgumentException( "name " + name + " is not a valid Java identifier" ) ; } private void checkDone() { if (done) throw new IllegalStateException( "past getResult: no other methods may be called" ) ; } public TimingInfoProcessor( String name, String pkg ) { this.done = false ; this.pkg = pkg ; checkForValidIdentifier( name ) ; if (!Identifier.isValidFullIdentifier( pkg )) throw new IllegalArgumentException( pkg + " is not a valid package name" ) ; this.tf = TimerFactoryBuilder.make( name, name ) ; this.contents = new HashMap<String,List<String>>() ; this.currentTimerGroup = null ; } public void addTimer( String name, String desc ) { checkDone() ; checkForValidIdentifier( name ) ; tf.makeTimer( name, desc ) ; currentTimerGroup = null ; } public void addTimerGroup( String name, String desc ) { checkDone() ; checkForValidIdentifier( name ) ; currentTimerGroup = tf.makeTimerGroup( name, desc ) ; } public void contains( String name ) { checkDone() ; if (currentTimerGroup == null) { throw new IllegalStateException( "contains must be called after an addTimerGroup call" ) ; } else { String cname = currentTimerGroup.name() ; List<String> list = contents.get( cname ) ; if (list == null) { list = new ArrayList<String>() ; contents.put( cname, list ) ; } list.add( name ) ; } } private static Controllable getControllable( TimerFactory tf, String name ) { Controllable result = tf.timers().get( name ) ; if (result == null) result = tf.timerGroups().get( name ) ; if (result == null) throw new IllegalArgumentException( name + " is not a valid Timer or TimerGroup name" ) ; return result ; } private void updateTimerFactoryContents() { // Use the Map<String,List<String>> to fill in the TimerGroup // containment relation for (String str : contents.keySet()) { List<String> list = contents.get(str) ; TimerGroup tg = tf.timerGroups().get( str ) ; for (String content : list) { tg.add( getControllable( tf, content ) ) ; } } } public Pair<String,TimerFactory> getResult() { checkDone() ; done = true ; updateTimerFactoryContents() ; Pair<String,TimerFactory> result = new Pair<String,TimerFactory>( pkg, tf ) ; return result ; } } private static class Handler extends DefaultHandler { private static final int WIDTH = 4 ; // Names of XML elements and attributes private static final String TIMER_ELEMENT = "timer" ; private static final String TIMING_ELEMENT = "timing" ; private static final String TIMER_GROUP_ELEMENT = "timerGroup" ; private static final String CONTAINS_ELEMENT = "contains" ; private static final String NAME_ATTR = "name" ; private static final String DESCRIPTION_ATTR = "description" ; private static final String PACKAGE_ATTR = "package" ; private boolean debug ; private int level ; private char[] pad ; private TimingInfoProcessor tip ; private Pair<String,TimerFactory> result ; public Handler( boolean debug ) { this.debug = debug ; this.level = 0 ; setPad() ; this.tip = null ; this.result = null ; } private void indent() { level++ ; setPad() ; } private void undent() { level-- ; setPad() ; } private void setPad() { int length = WIDTH * level ; pad = new char[length] ; for (int ctr=0; ctr<length; ctr++) pad[ctr] = ' ' ; } private void dprint( String msg ) { if (debug) { System.out.print( pad ) ; System.out.println( msg ) ; } } public void startDocument() throws SAXException { dprint( "startDocument called" ) ; } public void endDocument() throws SAXException { dprint( "endDocument called" ) ; result = tip.getResult() ; } public void startElement( String namespaceURI, String lName, String qName, Attributes attrs ) throws SAXException { indent() ; // only qName is useful dprint( "namespaceURI=" + namespaceURI ) ; dprint( "lName=" + lName ) ; dprint( "qName=" + qName ) ; dprint( "Attributes:" ) ; // only local name, value are useful for (int ctr=0; ctr<attrs.getLength(); ctr++) { dprint( "\tlocal name =" + attrs.getLocalName(ctr) ) ; dprint( "\tqualified name=" + attrs.getQName(ctr) ) ; dprint( "\tvalue =" + attrs.getValue(ctr) ) ; } if (qName.equals( TIMING_ELEMENT )) { String name = attrs.getValue( NAME_ATTR ) ; String pkg = attrs.getValue( PACKAGE_ATTR ) ; tip = new TimingInfoProcessor( name, pkg ) ; } else if (qName.equals( TIMER_ELEMENT )) { String name = attrs.getValue( NAME_ATTR ) ; String desc = attrs.getValue( DESCRIPTION_ATTR ) ; tip.addTimer( name, desc ) ; } else if (qName.equals( TIMER_GROUP_ELEMENT )) { String name = attrs.getValue( NAME_ATTR ) ; String desc = attrs.getValue( DESCRIPTION_ATTR ) ; tip.addTimerGroup( name, desc ) ; } else if (qName.equals( CONTAINS_ELEMENT )) { String name = attrs.getValue( NAME_ATTR ) ; tip.contains( name ) ; } else { throw new IllegalStateException( "Unknown XML element: " + qName ) ; } } public void endElement( String namespaceURI, String lName, String qName ) throws SAXException { undent() ; } public Pair<String,TimerFactory> getResult() { return result ; } } public static Pair<String,TimerFactory> parseDescription( String fileName ) throws IOException { return parseDescription( fileName, false ) ; } /** Return the package for the file to generate and a TimerFactory * that contains all of the information from the XML source file * given by fileName. */ public static Pair<String,TimerFactory> parseDescription( String fileName, boolean debug ) throws IOException { Handler handler = new Handler( debug ) ; SAXParserFactory factory = SAXParserFactory.newInstance() ; factory.setValidating( true ) ; File file = null ; try { SAXParser saxParser = factory.newSAXParser() ; file = new File( fileName ) ; saxParser.parse( file, handler ) ; } catch (Exception exc) { System.out.println( "Exception in processing " + file + ": " + exc ) ; exc.printStackTrace() ; return null ; } return handler.getResult() ; } /** Generate the source file for the Timers in the TimerFactory from the description. * The file is generated in the directory given by the package from the description * starting at the dirName. The name of the file is NAME.java, where NAME * is the TimerFactory name from the description. */ public static void generateSourceFile( String dirName, Pair<String,TimerFactory> description ) throws IOException { _clear() ; _package( description.first() ) ; _import( "java.lang.Object" ) ; _import( "java.lang.String" ) ; _import( "com.sun.corba.ee.spi.orb.ORB" ) ; _import( "com.sun.corba.ee.spi.orbutil.newtimer.Controllable" ) ; _import( "com.sun.corba.ee.spi.orbutil.newtimer.TimerManager" ) ; _import( "com.sun.corba.ee.spi.orbutil.newtimer.TimerFactory" ) ; _import( "com.sun.corba.ee.spi.orbutil.newtimer.Timer" ) ; _import( "com.sun.corba.ee.spi.orbutil.newtimer.TimerEventController" ) ; _import( "com.sun.corba.ee.spi.orbutil.newtimer.TimerGroup" ) ; TimerFactory tf = description.second() ; _class( PUBLIC, tf.name(), _t("Object") ) ; // no static initializer needed generateFields( tf ) ; generateConstructor( tf ) ; generateEnterExitMethods( tf ) ; _end() ; // of class generation File dir = new File( dirName ) ; String pkg = description.first() ; String fileDir = pkg.replace( '.', File.separatorChar ) ; File fdir = new File( dir, fileDir ) ; // mkdirs only returns true if dirs were created, so // false may or may not indicate an error. fdir.mkdirs() ; File file = new File( fdir, tf.name() + ".java" ) ; PrintStream ps = new PrintStream( file ) ; _sourceCode( ps, new Properties() ) ; } private static void generateFields( TimerFactory tf ) { _data( PRIVATE|FINAL, _t("TimerEventController"), "controller" ) ; for (Timer t : tf.timers().values()) { Type type = _t("Timer") ; _data( PUBLIC|FINAL, type, t.name() ) ; } for (TimerGroup tg : tf.timerGroups().values()) { Type type = _t("TimerGroup") ; _data( PUBLIC|FINAL, type, tg.name() ) ; } } private static void generateConstructor( TimerFactory tf ) { _constructor( PUBLIC ) ; _arg( _t("TimerFactory"), "tf" ) ; _arg( _t("TimerEventController"), "controller" ) ; _body() ; // set up orb and tm _assign( _field( _this(), "controller" ), _v( "controller" ) ) ; // create all timers Signature tsig = _s( _t("Timer"), _t("String"), _t("String") ) ; for (Timer t : tf.timers().values() ) { _assign( _v( t.name() ), _call( _v("tf"), "makeTimer", tsig, _const(t.name()), _const(t.description()))) ; } // create all timer groups Signature tgsig = _s( _t("TimerGroup"), _t("String"), _t("String") ) ; for (TimerGroup tg : tf.timerGroups().values() ) { _assign( _v( tg.name() ), _call( _v("tf"), "makeTimerGroup", tgsig, _const(tg.name()), _const(tg.description()))) ; } // fill in timer group containment Signature addSig = _s( _void(), _t("Controllable")) ; for (TimerGroup tg : tf.timerGroups().values() ) { for (Controllable c : tg.contents() ) { _expr( _call( _v(tg.name()), "add", addSig, _v(c.name()))) ; } } _end() ; } private static void generateEnterExitMethods( TimerFactory tf ) { // generate enter and exit method for each Timer for (Timer t : tf.timers().values() ) { generateMethod( "enter", t.name() ) ; generateMethod( "exit", t.name() ) ; } } // XXX we should add JavaDoc support to codegen, and use it // for the enter/exit methods. private static void generateMethod( String op, String timer ) { _method( PUBLIC, _void(), op+"_"+timer ) ; _body() ; Signature sig = _s(_void(), _t("TimerEventController")) ; _expr( _call( _v("controller"), op, sig, _v(timer))) ; _end() ; } public static void main( String[] args ) { // arguments: infile outdir debug if (args.length != 3) { System.out.println( "Required arguments: input-file output-directory" ) ; System.exit( 1 ) ; } else { try { String infile = args[0] ; String outdir = args[1] ; boolean debug = Boolean.parseBoolean( args[2] ) ; Pair<String,TimerFactory> result = parseDescription( infile, debug ) ; generateSourceFile( outdir, result ) ; } catch (Exception exc) { System.out.println( "Failed with exception: " + exc ) ; exc.printStackTrace() ; System.exit( 1 ) ; } } } }