import java.util.*; import Jakarta.util.*; import java.io.*; public class program { // has file already been formatted for mixin composition? public boolean alreadyPrepared( JTSParseTree t ) { // arg[2].arg[0] is the optionally specified AST_Class declaration AST_Class ac = ( AST_Class ) arg[2].arg[0]; if ( ac == null ) { AstNode.error( "file has no base or refinement declaration" ); return true; } return ac.alreadyPrepared( t ); } // updates firstType, lastType data members of JTSParseTree given // TypeDeclaration r private void updateTypes( JTSParseTree t, TypeDeclaration r ) { if ( t.firstType == null ) t.firstType = r; t.lastType = r; } private void initTypes( JTSParseTree t ) { t.firstType = null; t.lastType = null; } // this method is called if it has already been prepared -- this // is possible only if mixin was used to compose a set of layers // previously. public void processAlreadyPrepared( JTSParseTree t ) { // the primary objective of this method is to set the firstType // and lastType values of t. As a secondary objective, we do // some simple error checking. // Step 1: iterate through the AST_Class list. This list must // be an alternating set of SoUrCe and ModTypeDecl // declarations. Along the way, update firstType, lastType. AstCursor k = new AstCursor(); AST_Class sd = ( AST_Class ) arg[2].arg[0]; for ( k.FirstElement( sd ); k.MoreElement(); k.NextElement() ) { if ( k.node instanceof SourceDecl ) { // now extract the name of the layer (aspect) so that we can // tag each of its AST nodes with the appropriate name String lName = ( ( AST_QualifiedName ) k.node.arg[0] ).GetName(); k.NextElement(); updateTypes( t, ( TypeDeclaration ) k.node ); k.node.setSource( lName ); } else AstNode.error( "file was incorrectly created/composed" ); } } // prepare file for mixin composition // this amounts to introduceing SoUrCe declarations and // rewriting the TypeDeclaration extensions into its java equivalent public void prepare( JTSParseTree t ) { AST_Class sd; TypeDeclaration repl; // Step 1: first check to see if the parsed file is already // prepared. We will know this if "SoUrCe" nodes exist. initTypes( t ); if ( alreadyPrepared( t ) ) { processAlreadyPrepared( t ); return; } // Step 2: so the file needs to be prepared. This means that // there is one TypeDeclaration. We need to place a // SoUrCe statement in front of it, and convert it // into a normal class, interface, etc. declaration. // along the way, remember the first and last type // declarations on the list. Doing so is useful as // we will need these references later to do mixin // parameter substitution AstCursor k = new AstCursor(); int count = 0; sd = ( AST_Class ) arg[2].arg[0]; for ( k.FirstElement( sd ); k.MoreElement(); k.NextElement() ) { // Step 2.1: keep a count of the number of type declarations. // also, generate a relative URI for the universal // to increase portability of source filename. count++; String universalName = Main.file2uri( t.filepath ) ; // Step 2.2: before we proceed, make sure that the element // we are examining is indeed a ModTypeDecl. If not, // the .jak file is in the wrong format. if ( ! ( k.node instanceof ModTypeDecl ) ) AstNode.fatalError( k.node.findToken(), ".jak file has incorrect format: " + "expected a modified type declaration (ModTypeDecl) but found " + k.node.getClass().getName() ); // Step 2.3: is the type declaration an extension or not? String isRoot = ""; if ( ! ( ( ModTypeDecl ) k.node ).isExtension() ) isRoot = "RooT"; // Step 2.4 form the SoUrCe declaration sd = AST_Class.MakeAST( "\n\nSoUrCe " + isRoot + " " + getSource() + " " + "\"" + universalName + "\";" ); k.AddBefore( sd ); repl = ( ( TypeDeclaration ) k.node ).prepareReplace( t ); // Step 2.5: now remember the first and last type declaration. // this information is critical for instantiating // mixin layer parameters updateTypes( t, repl ); } // Step 3: if there is more than one type declaration, it's an error if ( count==0 ) AstNode.error( "file has no base or refinement declaration" ); else if ( count >1 ) AstNode.error( "file has 2 or more root or refinement declarations" ); } // compose base tree with extension tree public void compose( AstNode etree, JTSParseTree base, JTSParseTree ext ) { // composition involves // (a) doing nothing about aspect declaration // (b) compose the imports lists // (c) compose the AST_Class declarations // Step 1: do preliminary testing // make sure argument is correct type program e = ( program ) etree; // Step 2: compose the imports list and AST_Class declarations arg[1].compose( e.arg[1], base, ext ); arg[2].compose( e.arg[2], base, ext ); /* // Step 3: add a blank line to separate imports and packages if (arg[1].arg[0] != null) arg[1].arg[0].prependComment("\n"); */ } // can remove into its own layer -- stolen from preprocess public boolean isExtension() { if ( arg[2].arg[0] == null ) // missing TypeDeclaration AstNode.fatalError( "file has no base or refinement declaration" ); return ( ( AST_Class ) arg[2].arg[0] ).isExtension(); } }