import java.util.*; import Jakarta.util.FixDosOutputStream; import java.io.*; public class UmodSmDecl { // this method is called on parse tress of the SoUrCe file // the idea is for this method to propagate the contents of // this UmodSmDecl AST to the file referenced in the SoUrCe file. public void propagateChanges() { String myName = null; String referencedName = null; // Step 1: fetch the referenced file, parse it, and locate // the lone ModTypeDecl declaration UnMixinUtil u = new UnMixinUtil(); // Step 2: do some error checking. The ModTypeDecl must reference // a UmodSmDecl or UmodSmExt with the same QName as // our parse tree // note u.location is of type ModTypeDecl, whose first // argument is an UnmodifiedTypeDeclaration -- in our // case, it should be a UmodSmDecl if ( kernelConstants.globals().unmixin.base ) { // testing for UmodSmDecl if ( u.location.arg[1] instanceof UmodSmDecl ) { referencedName = u.location.arg[1].arg[0].tok[0].tokenName(); myName = arg[0].tok[0].tokenName(); if ( !referencedName.equals( myName ) ) AstNode.fatalError( tok[0], "expecting state machine " + myName + " but got " + referencedName + " file " + kernelConstants.globals().unmixin.fileName + " not updated" ); } else AstNode.fatalError( tok[0], "expecting UmodSmDecl but got " + u.location.arg[1].getClass().getName() + " file " + kernelConstants.globals().unmixin.fileName + " not updated" ); } else { // testing for UmodSmExt if ( u.location.arg[1] instanceof Ute && u.location.arg[1].arg[0] instanceof UmodSmExt ) { referencedName = u.location.arg[1].arg[0].arg[0].tok[0].tokenName(); myName = arg[0].tok[0].tokenName(); if ( !referencedName.equals( myName ) ) AstNode.fatalError( tok[0], "expecting state machine extension" + myName + " but got " + referencedName + " file " + kernelConstants.globals().unmixin.fileName + " not updated" ); } else AstNode.fatalError( tok[0], "expecting Ute (or rather UmodSmExt) but got " + u.location.arg[1].getClass().getName() + " file " + kernelConstants.globals().unmixin.fileName + " not updated" ); } // Step 3: we're ready to make the changes! // see if there is an extends clause -- there shouldn't // be if a Sm appears to extend itself. (This is // possible because of name mangling -- when names are // unmangled, a Sm appears to extend itself). SmExtendsClause k = ( SmExtendsClause ) arg[1].arg[0]; if ( k != null ) k = k.repairExtendsClause( myName ); boolean updated = false; if ( kernelConstants.globals().unmixin.base ) updated = ( ( UmodSmDecl ) u.location.arg[1] ). propagateChanges( k, ( ImplementsClause ) arg[2].arg[0], ( SmClassBody ) arg[3] ); else updated = ( ( UmodSmExt ) u.location.arg[1].arg[0] ). propagateChanges( ( ImplementsClause ) arg[2].arg[0], ( SmClassBody ) arg[3] ); // Step 4: last thing we do -- propagate the changes back to the file if ( updated ) u.output(); } // this method is called on the original AST, not the one with // SoUrCe declarations public boolean propagateChanges( SmExtendsClause k, ImplementsClause i, SmClassBody b ) { // because there are side effects, don't make just one // boolean expression as Java will optimize (and not propagate // side-effects boolean u = oneChange( arg[1],k ); u = oneChange( arg[2],i ) || u; u = oneChange( arg[3],b ) || u; return u; } public static boolean oneChange( AstNode old, AstNode nu ) { boolean updated = false; if ( nu == null ) { if ( !old.toString().trim().equals( "" ) ) { old.Delete(); updated = true; } } else { if ( nu!=null && ! ( old.toString().trim().equals( nu.toString().trim() ) ) ) { old.Replace( nu ); updated = true; } } return updated; } }