Search code examples
scalafilewriter

Scala writing and reading from a file inside a while loop


I have an application where I am to write values into a file and read them back into the program in a while loop. This fails because the file is only written when I exit the loop and not in every iteration. Therefore, in the next iterations, I cannot access values that were supposed to be written into the file in the previous iterations. How can I make every iteration write into the file as opposed to writing all values at the end of the while loop?

My application uses Scalafix. It reads in a test suite Scala file and duplicates its test cases at each iteration. The important details are explained by my series of 8 comments. Is there something about the working of the FileWriter that makes it wait until the last round of the loop to write back to file as it does not write back to file at every iteration of the loop?

   object Printer{
          //1 . This is my filePrinter which I call at every iteration to print the new test file with its test cases duplicated.
          def saveFile(filename:String, data: String): Unit ={
            val fileWritter: FileWriter = new FileWriter(filename)
            val bufferWritter: BufferedWriter = new BufferedWriter(fileWritter)
            bufferWritter.write(data)
            bufferWritter.flush()
            bufferWritter.close()
         }
    }
    object Main extends App {
      //2. my loop starts here.
       var n = 2
     do {
       // read in a semanticDocument (function provided below)
       val ( sdoc1,base,filename)=SemanticDocumentBuilder.buildSemanticDocument()
       implicit val sdoc = sdoc1           //4. P3 is a scalafix "patch" that collects all the test cases of
        // test suite and duplicates them. It works just fine, see the next comment.
        val p3 =sdoc.tree.collect {
              case [email protected](Term.ApplyInfix(_,Term.Name(smc), _, 
                 List(Lit.String(_))), Term.Name("in"), _, params) =>
                 Patch.addRight(test,"\n" +test.toString())
              }.asPatch
        //5. I collect the test cases in the next line and print 
        //out how many they are. At this moment, I have not
       // applied the duplicate function, so they are still as 
       //originally read from the test file.
       val staticAnalyzer = new StaticAnalyzer()
       val testCases: List[Term.ApplyInfix] = 
       staticAnalyzer.collectTestCases()
       println("Tests cases count: "+ testCases.length)

       val r3 = RuleName(List(RuleIdentifier("r3")))
       val map:Map[RuleName, Patch] = Map(r3->p3)
       val r = PatchInternals(map, v0.RuleCtx(sdoc.tree), None)
      //6. After applying the p3 patch in the previous three lines, 
      //I indeed print out the newly created test suite file 
      //and it contains each test case  duplicated as shown
      // by the below println(r._1.getClass). 
       println(r._1.getClass)
       //7. I then call the my save file (see this function above - first lines of this code)

        Printer.saveFile(base+"src/test/scala/"+filename,r._1)

        n-=1
        //8. Since I have saved my file with the duplicates, 
        //I would expect that it will save the file back to the 
        //file (overwrite the original file as I have not used "append = true". 
        //I would then expect that the next length of test cases will 
        //have doubled but this is never the case. 
        //The save function with FileWriter only works in the last loop. 
       //Therefore, no matter the number of loops, it only doubles once!

        println("Loop: "+ n)
  } while(n>0)
}

**Edit factored out the reading in of the semanticDocument ** This function simply returns a SemanticDocument and two strings representing my file path and filename.

object SemanticDocumentBuilder{
def buildSemanticDocument(): (SemanticDocument,String,String) ={

val base  = "/Users/soft/Downloads/simpleAkkaProject/"                                     
val local = new File(base)                                                                 
//val dependenceisSBTCommand = s"sbt -ivy ./.ivy2 -Dsbt.ivy.home=./.ivy2 -Divy.home=./.ivy2
//val sbtCmd = s"sbt -ivy ./ivy2 -Dsbt.ivy.home=./ivy2 -Divy.home=./ivy2 -Dsbt.boot.directo
val result = sys.process.Process(Seq("sbt","semanticdb"), local).!                         
val jars = FileUtils.listFiles(local, Array("jar"), true).toArray(new Array[File](0))      
  .toList                                                                                  
  .map(f => Classpath(f.getAbsolutePath))                                                  
  .reduceOption(_ ++ _)                                                                    
val classes = FileUtils.listFilesAndDirs(local, TrueFileFilter.INSTANCE, DirectoryFileFilte
  .toList                                                                                  
  .filter(p => p.isDirectory && !p.getAbsolutePath.contains(".sbt") && p.getAbsolutePath.co
  .map(f => Classpath(f.getAbsolutePath))                                                  
  .reduceOption(_ ++ _)                                                                                                                                                            
val classPath = ClassLoader.getSystemClassLoader.asInstanceOf[URLClassLoader].getURLs      
  .map(url => Classpath(url.getFile))                                                      
  .reduceOption(_ ++ _)                                                                    
val all = (jars ++ classes ++ classPath).reduceOption(_ ++ _).getOrElse(Classpath(""))     
val symbolTable = GlobalSymbolTable(all)                                                   
val filename = "AkkaQuickstartSpec.scala"                                                  
val root = AbsolutePath(base).resolve("src/test/scala/")                                   
println(root)                                                                              
val abspath = root.resolve(filename)                                                       
println(root)                                                                              
val relpath = abspath.toRelative(AbsolutePath(base))                                       
println(relpath)                                                                           
val sourceFile = new File(base+"src/test/scala/"+filename)                                 
val input = Input.File(sourceFile)                                                         
println(input)                                                                             
if (n == firstRound){                                                                      
  doc = SyntacticDocument.fromInput(input)                                                 
}                                                                                          
//println(doc.tree.structure(30))                                                          
var documents: Map[String, TextDocument] = Map.empty                                       

Locator.apply(local.toPath)((path, db) => db.documents.foreach({                           
  case document@TextDocument(_, uri, text, md5, _, _, _, _, _) if !md5.isEmpty => { // skip
    if (n == firstRound){                                                                  
      ast= sourceFile.parse[Source].getOrElse(Source(List()))                              
    }                                                                                      
    documents = documents + (uri -> document)                                              
    println(uri)                                                                           
  }                                                                                        
   println(local.canWrite)                                                                 
    if (editedSuite != null){                                                              
      Printer.saveFile(sourceFile,editedSuite)                                             
    }                                                                                      
}))                                                                                        

//println(documents)                                                                       
val impl = new InternalSemanticDoc(doc, documents(relpath.toString()), symbolTable)        
implicit val sdoc = new SemanticDocument(impl)                                             
val symbols = sdoc.tree.collect {                                                          
  case t@ Term.Name("<") => {                                                              
    println(s"symbol for $t")                                                              
    println(t.symbol.value)                                                                
    println(symbolTable.info(t.symbol.value))                                              
  }                                                                                        
}                                                                                          
(sdoc,base,filename)
 }
}



Solution

  • In saveFile you need to close the fileWriter after closing the bufferedWriter. You don't need to flush because close will do this for you.


    You should also close all the other File objects that you create in the loop, because they may be holding on to stale file handles. (e.g. local, ast)

    More generally, clean up the code by putting code in functions with meaningful names. There is also a lot of code that can be outside the loop. Doing this will make it easier to see what is going on and allow you create a Minimal, Complete, and Verifiable example. As it stands it is really difficult to work out what is going on.