5
5
package scalatron .scalatron .api
6
6
7
7
8
- import scalatron .scalatron .api .Scalatron .{Sample , SourceFile , User }
8
+ import scalatron .scalatron .api .Scalatron .{Sample , User , SourceFileCollection }
9
9
import akka .actor .ActorSystem
10
+ import scala .io .Source
11
+ import java .io .{FileWriter , File }
10
12
11
13
12
14
/** The trait representing the main API entry point of the Scalatron server. */
@@ -70,7 +72,7 @@ trait Scalatron
70
72
* @throws ScalatronException.CreationFailure if the user could not be created (e.g. because /src dir could not be created).
71
73
* @throws IOError if the user's initial source files could not be written to disk.
72
74
*/
73
- def createUser (name : String , password : String , initialSourceFiles : Iterable [ SourceFile ] ): User
75
+ def createUser (name : String , password : String , initialSourceFiles : SourceFileCollection ): User
74
76
75
77
/** Checks if the given string would represent a valid user name by examining the characters
76
78
* in the string. A variety of characters are not allowed primarily for the following reasons:
@@ -98,7 +100,7 @@ trait Scalatron
98
100
99
101
/** Creates a new sample with the given name from the given source file collection.
100
102
* Users can employ this to share bot code across the network. */
101
- def createSample (name : String , sourceFiles : Iterable [ SourceFile ] ): Sample
103
+ def createSample (name : String , sourceFiles : SourceFileCollection ): Sample
102
104
103
105
104
106
// ----------------------------------------------------------------------------------------------
@@ -235,7 +237,7 @@ object Scalatron {
235
237
* @throws IllegalStateException if the source directory does not exist.
236
238
* @throws IOError if the source files could not be read.
237
239
*/
238
- def sourceFiles : Iterable [ SourceFile ]
240
+ def sourceFiles : SourceFileCollection
239
241
240
242
/** Updates the source code of the given user to the given source files.
241
243
* The updated source file(s) are written to e.g. "/Scalatron/users/{user}/src/".
@@ -245,7 +247,7 @@ object Scalatron {
245
247
* before instructing it to build them.
246
248
* @throws IOError if the source files could not be written.
247
249
* */
248
- def updateSourceFiles (sourceFiles : Iterable [ SourceFile ] )
250
+ def updateSourceFiles (sourceFiles : SourceFileCollection )
249
251
250
252
/** Builds a local (unpublished) .jar bot plug-in from the given (in-memory) source files.
251
253
*
@@ -262,7 +264,7 @@ object Scalatron {
262
264
* @throws IllegalStateException if compilation service is unavailable, sources don't exist etc.
263
265
* @throws IOError if source files cannot be read from disk, etc.
264
266
*/
265
- def buildSourceFiles (sourceFiles : Iterable [ SourceFile ] ): BuildResult
267
+ def buildSourceFiles (sourceFiles : SourceFileCollection ): BuildResult
266
268
267
269
/** Builds a local (unpublished) .jar bot plug-in from the sources currently present in
268
270
* the user's workspace.
@@ -316,7 +318,7 @@ object Scalatron {
316
318
* @throws IllegalStateException if version (base) directory could not be created
317
319
* @throws IOError if source files cannot be written to disk, etc.
318
320
* */
319
- def createVersion (label : String , sourceFiles : Iterable [ SourceFile ] ): Version
321
+ def createVersion (label : String , sourceFiles : SourceFileCollection ): Version
320
322
321
323
/** Creates a new version by storing a backup copy of the source files currently present in the source
322
324
* code directory of the user if the given version creation policy requires it. This method is intended as
@@ -332,7 +334,7 @@ object Scalatron {
332
334
* directory could not be read.
333
335
* @throws IOError if source files cannot be written to disk, etc.
334
336
* */
335
- def createBackupVersion (policy : VersionPolicy , label : String , sourceFiles : Iterable [ SourceFile ] ): Option [Version ]
337
+ def createBackupVersion (policy : VersionPolicy , label : String , sourceFiles : SourceFileCollection ): Option [Version ]
336
338
337
339
338
340
// ----------------------------------------------------------------------------------------------
@@ -368,6 +370,7 @@ object Scalatron {
368
370
}
369
371
370
372
373
+
371
374
/** Scalatron.SourceFile: trait that provides an interface for dealing with source code
372
375
* files handed through the API.
373
376
* CBB: do we need to specify some particular kind of encoding?
@@ -381,6 +384,88 @@ object Scalatron {
381
384
}
382
385
383
386
387
+ /** Type alias for a collection of source files; each holds a filename and code. */
388
+ type SourceFileCollection = Iterable [SourceFile ]
389
+
390
+ /** Utility methods for working with collections of source files. */
391
+ object SourceFileCollection
392
+ {
393
+ /** Returns an initial source file collection that incorporates the given user name.
394
+ * @param userName the name of the user to generate an initial source file collection for.
395
+ * @return a non-empty collection of SourceFile objects
396
+ */
397
+ def initial (userName : String ): SourceFileCollection =
398
+ Iterable (SourceFile (
399
+ " Bot.scala" ,
400
+ " // this is the source code for your bot - have fun!\n " +
401
+ " \n " +
402
+ " class ControlFunctionFactory {\n " +
403
+ " def create = new Bot().respond _\n " +
404
+ " }\n " +
405
+ " \n " +
406
+ " class Bot {\n " +
407
+ " def respond(input: String) = \" Status(text=" + userName + " )\"\n " +
408
+ " }\n "
409
+ ))
410
+
411
+ /** Returns a collection of SourceFile objects representing source code files residing in the given directory.
412
+ * Excludes directories and files that have the same name as the config files ("config.txt").
413
+ * If the directory does not exist, is empty or does not contain valid files, an empty collection is returned.
414
+ * @param directoryPath the directory to scan for source files.
415
+ * @param verbose if true, information about the files read is logged to the console.
416
+ * @return a collection of SourceFile objects; may be empty
417
+ * @throws IOError on IO errors encountered while loading source file contents from disk
418
+ */
419
+ def loadFrom (directoryPath : String , verbose : Boolean = false ): SourceFileCollection = {
420
+ val directory = new File (directoryPath)
421
+ if (! directory.exists) {
422
+ System .err.println(" warning: directory expected to contain source files does not exist: %s" .format(directoryPath))
423
+ Iterable .empty
424
+ }
425
+ else
426
+ {
427
+ val sourceFiles = directory.listFiles()
428
+ if ( sourceFiles == null || sourceFiles.isEmpty ) {
429
+ // no source files there!
430
+ Iterable .empty
431
+ } else {
432
+ // read whatever is on disk now
433
+ sourceFiles
434
+ .filter(file => file.isFile && file.getName != Constants .ConfigFilename )
435
+ .map(file => {
436
+ val filename = file.getName
437
+ val code = Source .fromFile(file).mkString
438
+ if (verbose) println(" loaded source code from file: '%s'" .format(file.getAbsolutePath))
439
+ Scalatron .SourceFile (filename, code)
440
+ })
441
+ }
442
+ }
443
+ }
444
+
445
+
446
+ /** Writes the given collection of source files into the given directory, which must exist
447
+ * and should be empty. Throws an exception on IO errors.
448
+ * @param directoryPath the directory to write the source files to.
449
+ * @param sourceFileCollection the collection of source files to write to disk.
450
+ * @param verbose if true, information about the written files is logged to the console.
451
+ * @throws IOError on IO errors encountered while writing source file contents to disk.
452
+ */
453
+ def writeTo (directoryPath : String , sourceFileCollection : SourceFileCollection , verbose : Boolean = false ) {
454
+ sourceFileCollection.foreach(sf => {
455
+ val path = directoryPath + " /" + sf.filename
456
+ val sourceFile = new FileWriter (path)
457
+ sourceFile.append(sf.code)
458
+ sourceFile.close()
459
+ if (verbose) println(" wrote source file: " + path)
460
+ })
461
+ }
462
+ }
463
+
464
+
465
+
466
+
467
+
468
+
384
469
/** Policy used for optional version generation (e.g. when uploading new source files) that dictates whether
385
470
* and under which circumstances a backup version should be generated.
386
471
*/
@@ -424,7 +509,7 @@ object Scalatron {
424
509
def name : String
425
510
426
511
/** Returns the source code files associated with this sample. */
427
- def sourceFiles : Iterable [ SourceFile ]
512
+ def sourceFiles : SourceFileCollection
428
513
429
514
/** Deletes this sample, including all associated source code files. */
430
515
def delete ()
@@ -448,7 +533,7 @@ object Scalatron {
448
533
def user : User
449
534
450
535
/** Returns the source code files associated with this version. */
451
- def sourceFiles : Iterable [ SourceFile ]
536
+ def sourceFiles : SourceFileCollection
452
537
453
538
/** Deletes this version, including all associated source code files. */
454
539
def delete ()
@@ -551,20 +636,6 @@ object Scalatron {
551
636
552
637
val JarFilename = " ScalatronBot.jar"
553
638
val BackupJarFilename = " ScalatronBot.backup.jar"
554
-
555
- def initialSources (userName : String ): Iterable [SourceFile ] =
556
- Iterable (SourceFile (
557
- " Bot.scala" ,
558
- " // this is the source code for your bot - have fun!\n " +
559
- " \n " +
560
- " class ControlFunctionFactory {\n " +
561
- " def create = new Bot().respond _\n " +
562
- " }\n " +
563
- " \n " +
564
- " class Bot {\n " +
565
- " def respond(input: String) = \" Status(text=" + userName + " )\"\n " +
566
- " }\n "
567
- ))
568
639
}
569
640
570
641
0 commit comments