diff --git a/framework/project/Build.scala b/framework/project/Build.scala index c7f9b62cc9e..f96cb0db0a7 100644 --- a/framework/project/Build.scala +++ b/framework/project/Build.scala @@ -12,20 +12,22 @@ object PlayBuild extends Build { import LocalSBT._ import Tasks._ - lazy val PlayUtilsProject = Project( - "Utils", - file("src/play-utils"), - settings = buildSettings ++ Seq( - previousArtifact := Some("play" % {"utils_"+previousScalaVersion} % previousVersion), - publishTo := Some(playRepository), - scalacOptions ++= Seq("-encoding", "UTF-8", "-Xlint","-deprecation", "-unchecked"), - publishArtifact in (Compile, packageDoc) := false, - publishArtifact in (Compile, packageSrc) := true, - resolvers += typesafe - ) + lazy val SbtLinkProject = Project( + "SBT-link", + file("src/sbt-link"), + settings = buildSettingsWithMIMA ++ Seq( + autoScalaLibrary := false, + previousArtifact := Some("play" % {"play_"+previousScalaVersion} % previousVersion), + libraryDependencies := link, + publishTo := Some(playRepository), + javacOptions ++= Seq("-source", "1.6", "-target", "1.6", "-encoding", "UTF-8"), + publishArtifact in (Compile, packageDoc) := false, + publishArtifact in (Compile, packageSrc) := true, + resolvers += typesafe, + crossPaths := false + ) ).settings(com.typesafe.sbtscalariform.ScalariformPlugin.defaultScalariformSettings: _*) - lazy val TemplatesProject = Project( "Templates", file("src/templates"), @@ -35,7 +37,6 @@ object PlayBuild extends Build { libraryDependencies := templatesDependencies, publishArtifact in (Compile, packageDoc) := false, publishArtifact in (Compile, packageSrc) := false, - unmanagedJars in Compile <+= (baseDirectory) map { b => compilerJar(b / "../..") }, scalacOptions ++= Seq("-encoding", "UTF-8", "-Xlint","-deprecation", "-unchecked"), resolvers += typesafe ) @@ -45,6 +46,8 @@ object PlayBuild extends Build { "Routes-Compiler", file("src/routes-compiler"), settings = buildSettingsWithMIMA ++ Seq( + scalaVersion := buildScalaVersionForSbt, + scalaBinaryVersion := CrossVersion.binaryScalaVersion(buildScalaVersionForSbt), previousArtifact := Some("play" % {"routes-compiler_"+previousScalaVersion} % previousVersion), publishTo := Some(playRepository), libraryDependencies := routersCompilerDependencies, @@ -55,6 +58,23 @@ object PlayBuild extends Build { ) ).settings(com.typesafe.sbtscalariform.ScalariformPlugin.defaultScalariformSettings: _*) + lazy val TemplatesCompilerProject = Project( + "Templates-Compiler", + file("src/templates-compiler"), + settings = buildSettingsWithMIMA ++ Seq( + scalaVersion := buildScalaVersionForSbt, + scalaBinaryVersion := CrossVersion.binaryScalaVersion(buildScalaVersionForSbt), + previousArtifact := Some("play" % {"templates-compiler_"+previousScalaVersion} % previousVersion), + publishTo := Some(playRepository), + libraryDependencies := templatesCompilerDependencies, + publishArtifact in (Compile, packageDoc) := false, + publishArtifact in (Compile, packageSrc) := false, + unmanagedJars in Compile <+= (baseDirectory) map { b => compilerJar(b / "../..") }, + scalacOptions ++= Seq("-encoding", "UTF-8", "-Xlint","-deprecation", "-unchecked"), + resolvers += typesafe + ) + ).settings(com.typesafe.sbtscalariform.ScalariformPlugin.defaultScalariformSettings: _*) + lazy val AnormProject = Project( "Anorm", file("src/anorm"), @@ -67,6 +87,36 @@ object PlayBuild extends Build { ) ).settings(com.typesafe.sbtscalariform.ScalariformPlugin.defaultScalariformSettings: _*) + lazy val PlayExceptionsProject = Project( + "Play-Exceptions", + file("src/play-exceptions"), + settings = buildSettingsWithMIMA ++ Seq( + autoScalaLibrary := false, + previousArtifact := Some("play" % {"play-exceptions"+previousScalaVersion} % previousVersion), + publishTo := Some(playRepository), + javacOptions ++= Seq("-source","1.6","-target","1.6", "-encoding", "UTF-8"), + publishArtifact in (Compile, packageDoc) := false, + publishArtifact in (Compile, packageSrc) := true, + crossPaths := false + ) + ).settings(com.typesafe.sbtscalariform.ScalariformPlugin.defaultScalariformSettings: _*) + + /** Let's remove this after the migration to Scala 2.10 **/ + lazy val Sip14Backport = Project( + "SIP14-Backport", + file("src/sip14-backport"), + settings = buildSettingsWithMIMA ++ Seq( + publishTo := Some(playRepository), + scalaVersion := buildScalaVersionForSbt, + scalaBinaryVersion := CrossVersion.binaryScalaVersion(buildScalaVersionForSbt), + scalacOptions ++= Seq("-encoding", "UTF-8", "-Xlint","-deprecation", "-unchecked"), + javacOptions ++= Seq("-source","1.6","-target","1.6", "-encoding", "UTF-8"), + publishArtifact in (Compile, packageDoc) := false, + publishArtifact in (Compile, packageSrc) := true, + resolvers += typesafe + ) + ) + lazy val PlayProject = Project( "Play", file("src/play"), @@ -80,62 +130,69 @@ object PlayBuild extends Build { publishArtifact in (Compile, packageDoc) := false, publishArtifact in (Compile, packageSrc) := true, resolvers += typesafe, - sourceGenerators in Compile <+= (dependencyClasspath in TemplatesProject in Runtime, packageBin in TemplatesProject in Compile, scalaSource in Compile, sourceManaged in Compile, streams) map ScalaTemplates, + sourceGenerators in Compile <+= (dependencyClasspath in TemplatesCompilerProject in Runtime, packageBin in TemplatesCompilerProject in Compile, scalaSource in Compile, sourceManaged in Compile, streams) map ScalaTemplates, compile in (Compile) <<= PostCompile ) - ).settings(com.typesafe.sbtscalariform.ScalariformPlugin.defaultScalariformSettings: _*).dependsOn(RoutesCompilerProject, PlayUtilsProject, TemplatesProject, AnormProject) + ).settings(com.typesafe.sbtscalariform.ScalariformPlugin.defaultScalariformSettings: _*) + .dependsOn({ + Seq[sbt.ClasspathDep[sbt.ProjectReference]](SbtLinkProject, PlayExceptionsProject, TemplatesProject, AnormProject) ++ { + if(experimental) Nil else Seq[sbt.ClasspathDep[sbt.ProjectReference]](Sip14Backport) + } + }:_*) lazy val PlayTestProject = Project( - "Play-Test", - file("src/play-test"), - settings = buildSettingsWithMIMA ++ Seq( - previousArtifact := Some("play" % {"play-test_"+previousScalaVersion} % previousVersion), - libraryDependencies := testDependencies, - publishTo := Some(playRepository), - scalacOptions ++= Seq("-encoding", "UTF-8", "-Xlint","-deprecation", "-unchecked"), - javacOptions ++= Seq("-source","1.6","-target","1.6", "-encoding", "UTF-8","-Xlint:unchecked", "-Xlint:deprecation"), - publishArtifact in (Compile, packageDoc) := false, - publishArtifact in (Compile, packageSrc) := true, - resolvers += typesafe - ) + "Play-Test", + file("src/play-test"), + settings = buildSettingsWithMIMA ++ Seq( + previousArtifact := Some("play" % {"play-test_"+previousScalaVersion} % previousVersion), + libraryDependencies := testDependencies, + publishTo := Some(playRepository), + scalacOptions ++= Seq("-encoding", "UTF-8", "-Xlint","-deprecation", "-unchecked"), + javacOptions ++= Seq("-source","1.6","-target","1.6", "-encoding", "UTF-8","-Xlint:unchecked", "-Xlint:deprecation"), + publishArtifact in (Compile, packageDoc) := false, + publishArtifact in (Compile, packageSrc) := true, + resolvers += typesafe + ) ).settings(com.typesafe.sbtscalariform.ScalariformPlugin.defaultScalariformSettings: _*).dependsOn(PlayProject) - - lazy val SbtPluginProject = Project( - "SBT-Plugin", - file("src/sbt-plugin"), - settings = buildSettings ++ Seq( - sbtPlugin := true, - publishMavenStyle := false, - libraryDependencies := sbtDependencies, - addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.1.0"), - addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.1.0-TYPESAFE"), - unmanagedJars in Compile <++= (baseDirectory) map { b => sbtJars(b / "../..") }, - publishTo := Some(playIvyRepository), - scalacOptions ++= Seq("-encoding", "UTF-8", "-Xlint","-deprecation", "-unchecked"), - publishArtifact in (Compile, packageDoc) := false, - publishArtifact in (Compile, packageSrc) := false, - resolvers += typesafe - ) - ).settings(com.typesafe.sbtscalariform.ScalariformPlugin.defaultScalariformSettings: _*).dependsOn(PlayUtilsProject, RoutesCompilerProject, PlayProject, TemplatesProject, ConsoleProject) - - + "SBT-Plugin", + file("src/sbt-plugin"), + settings = buildSettings ++ Seq( + scalaVersion := buildScalaVersionForSbt, + scalaBinaryVersion := CrossVersion.binaryScalaVersion(buildScalaVersionForSbt), + sbtPlugin := true, + publishMavenStyle := false, + libraryDependencies := sbtDependencies, + libraryDependencies += "com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.1.0" extra("sbtVersion" -> buildSbtVersionBinaryCompatible, "scalaVersion" -> buildScalaVersionForSbt), + libraryDependencies += "com.github.mpeltonen" % "sbt-idea" % "1.1.0-TYPESAFE" extra("sbtVersion" -> buildSbtVersionBinaryCompatible, "scalaVersion" -> buildScalaVersionForSbt), + unmanagedJars in Compile <++= (baseDirectory) map { b => sbtJars(b / "../..") }, + publishTo := Some(playIvyRepository), + scalacOptions ++= Seq("-encoding", "UTF-8", "-Xlint","-deprecation", "-unchecked"), + publishArtifact in (Compile, packageDoc) := false, + publishArtifact in (Compile, packageSrc) := false, + resolvers += typesafe + ) + ).settings(com.typesafe.sbtscalariform.ScalariformPlugin.defaultScalariformSettings: _*) + .dependsOn(SbtLinkProject, PlayExceptionsProject, RoutesCompilerProject, TemplatesCompilerProject, ConsoleProject) + lazy val ConsoleProject = Project( - "Console", - file("src/console"), - settings = buildSettings ++ Seq( - libraryDependencies := consoleDependencies, - sourceGenerators in Compile <+= sourceManaged in Compile map PlayVersion, - unmanagedJars in Compile <++= (baseDirectory) map { b => sbtJars(b / "../..") }, - publishTo := Some(playRepository), - scalacOptions ++= Seq("-encoding", "UTF-8", "-Xlint","-deprecation", "-unchecked"), - publishArtifact in (Compile, packageDoc) := false, - publishArtifact in (Compile, packageSrc) := true, - resolvers += typesafe - ) - ).settings(com.typesafe.sbtscalariform.ScalariformPlugin.defaultScalariformSettings: _*).dependsOn(PlayUtilsProject) + "Console", + file("src/console"), + settings = buildSettings ++ Seq( + scalaVersion := buildScalaVersionForSbt, + scalaBinaryVersion := CrossVersion.binaryScalaVersion(buildScalaVersionForSbt), + libraryDependencies := consoleDependencies, + sourceGenerators in Compile <+= sourceManaged in Compile map PlayVersion, + unmanagedJars in Compile <++= (baseDirectory) map { b => sbtJars(b / "../..") }, + publishTo := Some(playRepository), + scalacOptions ++= Seq("-encoding", "UTF-8", "-Xlint","-deprecation", "-unchecked"), + publishArtifact in (Compile, packageDoc) := false, + publishArtifact in (Compile, packageSrc) := true, + resolvers += typesafe + ) + ).settings(com.typesafe.sbtscalariform.ScalariformPlugin.defaultScalariformSettings: _*) val Root = Project( "Root", @@ -147,28 +204,32 @@ object PlayBuild extends Build { buildRepositoryTask, distTask, generateAPIDocsTask, - publish <<= (publish in PlayProject, publish in TemplatesProject, publish in AnormProject, publish in SbtPluginProject, publish in ConsoleProject, publish in PlayTestProject, publish in PlayUtilsProject, publish in RoutesCompilerProject) map { (_,_,_,_,_,_,_,_) => }, - publishLocal <<= (publishLocal in PlayProject, publishLocal in TemplatesProject, publishLocal in AnormProject, publishLocal in SbtPluginProject, publishLocal in ConsoleProject, publishLocal in PlayUtilsProject, publishLocal in RoutesCompilerProject) map { (_,_,_,_,_,_,_) => } + publish <<= (publish in SbtLinkProject, publish in PlayProject, publish in TemplatesProject, publish in AnormProject, publish in SbtPluginProject, publish in ConsoleProject, publish in PlayTestProject, publish in RoutesCompilerProject, publish in TemplatesCompilerProject, publish in Sip14Backport, publish in PlayExceptionsProject) map { (_,_,_,_,_,_,_,_,_,_,_) => }, + publishLocal <<= (publishLocal in SbtLinkProject, publishLocal in PlayProject, publishLocal in TemplatesProject, publishLocal in AnormProject, publishLocal in SbtPluginProject, publishLocal in ConsoleProject, publishLocal in RoutesCompilerProject, publishLocal in TemplatesCompilerProject, publishLocal in Sip14Backport, publishLocal in PlayExceptionsProject) map { (_,_,_,_,_,_,_,_,_,_) => } ) ).settings(com.typesafe.sbtscalariform.ScalariformPlugin.defaultScalariformSettings: _*) - .dependsOn(PlayProject).aggregate(PlayUtilsProject, AnormProject, TemplatesProject, RoutesCompilerProject, PlayProject, SbtPluginProject, ConsoleProject, PlayTestProject) + .dependsOn(PlayProject).aggregate(SbtLinkProject, AnormProject, TemplatesProject, TemplatesCompilerProject, RoutesCompilerProject, PlayProject, SbtPluginProject, ConsoleProject, PlayTestProject) object BuildSettings { + val experimental = Option(System.getProperty("experimental")).filter(_ == "true").map(_ => true).getOrElse(false) + val buildOrganization = "play" val buildVersion = Option(System.getProperty("play.version")).filterNot(_.isEmpty).getOrElse("2.0-unknown") val previousVersion = "2.0.3" val previousScalaVersion = "2.9.1" - val buildScalaVersion = Option(System.getProperty("scala.version")).getOrElse("2.9.2") + val buildScalaVersion = if(experimental) "2.10.0-M7" else "2.9.2" val buildScalaVersionForSbt = "2.9.2" val buildSbtVersion = "0.12.0" + val buildSbtVersionBinaryCompatible = "0.12" val buildSettings = Defaults.defaultSettings ++ Seq ( - organization := buildOrganization, - version := buildVersion, - scalaVersion := buildScalaVersion, - logManager <<= extraLoggers(PlayLogManager.default), - ivyLoggingLevel := UpdateLogging.DownloadOnly + organization := buildOrganization, + version := buildVersion, + scalaVersion := buildScalaVersion, + scalaBinaryVersion := CrossVersion.binaryScalaVersion(buildScalaVersion), + logManager <<= extraLoggers(PlayLogManager.default), + ivyLoggingLevel := UpdateLogging.DownloadOnly ) val buildSettingsWithMIMA = buildSettings ++ mimaDefaultSettings } @@ -208,7 +269,6 @@ object PlayBuild extends Build { object Dependencies { - val runtime = Seq( "io.netty" % "netty" % "3.5.0.Final", "org.slf4j" % "slf4j-api" % "1.6.4", @@ -216,9 +276,9 @@ object PlayBuild extends Build { "org.slf4j" % "jcl-over-slf4j" % "1.6.4", "ch.qos.logback" % "logback-core" % "1.0.3", "ch.qos.logback" % "logback-classic" % "1.0.3", - "com.github.scala-incubator.io" %% "scala-io-file" % "0.4.0", - "com.typesafe.akka" % "akka-actor" % "2.0.2", - "com.typesafe.akka" % "akka-slf4j" % "2.0.2", + "com.github.scala-incubator.io" %% "scala-io-file" % "0.4.1", + "com.typesafe.akka" % (if(experimental) "akka-actor_2.10.0-M7" else "akka-actor" ) % (if(experimental) "2.1-M2" else "2.0.2"), + "com.typesafe.akka" % (if(experimental) "akka-actor_2.10.0-M7" else "akka-slf4j" ) % (if(experimental) "2.1-M2" else "2.0.2"), ("com.google.guava" % "guava" % "10.0.1" notTransitive()) .exclude("com.google.code.findbugs", "jsr305") @@ -232,7 +292,12 @@ object PlayBuild extends Build { "org.hibernate.javax.persistence" % "hibernate-jpa-2.0-api" % "1.0.1.Final", "com.h2database" % "h2" % "1.3.158", - "org.scala-tools" %% "scala-stm" % "0.5", + + if(experimental) { + "org.scala-tools" % "scala-stm_2.10.0-M7" % "0.6" + } else { + "org.scala-tools" %% "scala-stm" % "0.6" + }, ("com.jolbox" % "bonecp" % "0.7.1.RELEASE" notTransitive()) .exclude("com.google.guava", "guava") @@ -284,15 +349,26 @@ object PlayBuild extends Build { "net.sf.ehcache" % "ehcache-core" % "2.5.0", - "org.specs2" %% "specs2" % "1.9" % "test", + "org.specs2" %% "specs2" % "1.11" % "test", "org.mockito" % "mockito-all" % "1.9.0" % "test", - "com.novocode" % "junit-interface" % "0.8" % "test", + "com.novocode" % "junit-interface" % "0.8" % "test", "org.fluentlenium" % "fluentlenium-festassert" % "0.6.0" % "test" ) + + val link = Seq( + "org.javassist" % "javassist" % "3.16.1-GA" + ) + val routersCompilerDependencies = Seq( - "com.github.scala-incubator.io" %% "scala-io-file" % "0.4.0" - ) + "com.github.scala-incubator.io" %% "scala-io-file" % "0.4.1" + ) + + val templatesCompilerDependencies = Seq( + "com.github.scala-incubator.io" %% "scala-io-file" % "0.4.1", + "org.specs2" %% "specs2" % "1.11" % "test" + ) + val sbtDependencies = Seq( "com.typesafe.config" % "config" % "0.2.1", "rhino" % "js" % "1.7R2", @@ -308,7 +384,11 @@ object PlayBuild extends Build { .exclude("junit", "junit") , - "com.github.scala-incubator.io" %% "scala-io-file" % "0.4.0", + ("com.google.guava" % "guava" % "10.0.1" notTransitive()) + .exclude("com.google.code.findbugs", "jsr305") + , + + "com.github.scala-incubator.io" %% "scala-io-file" % "0.4.1", ("org.avaje" % "ebean" % "2.8.1" notTransitive()) .exclude("javax.persistence", "persistence-api") @@ -322,19 +402,20 @@ object PlayBuild extends Build { ) val consoleDependencies = Seq( - "com.github.scala-incubator.io" %% "scala-io-file" % "0.4.0", + "com.github.scala-incubator.io" %% "scala-io-file" % "0.4.1", "net.databinder.giter8" % "giter8_2.9.1" % "0.5.0" ) val templatesDependencies = Seq( - "com.github.scala-incubator.io" %% "scala-io-file" % "0.4.0", - "org.specs2" %% "specs2" % "1.9" % "test" + "com.github.scala-incubator.io" %% "scala-io-file" % "0.4.1", + "org.specs2" %% "specs2" % "1.11" % "test" ) val testDependencies = Seq( "junit" % "junit-dep" % "4.10", - "org.specs2" %% "specs2" % "1.9", + "org.specs2" %% "specs2" % "1.11", "com.novocode" % "junit-interface" % "0.8" exclude ("junit", "junit"), + // junit is literally evil because it bundles hamcrest classes that creates classloader hell. // junit-interface brings in junit-dep, which fixes this silliness, so we just exclude it from // FluentLenium, until https://github.com/FluentLenium/FluentLenium/pull/43 is accepted and @@ -353,8 +434,9 @@ object PlayBuild extends Build { | |object PlayVersion { | val current = "%s" + | val scalaVersion = "%s" |} - """.stripMargin.format(BuildSettings.buildVersion) + """.stripMargin.format(BuildSettings.buildVersion, BuildSettings.buildScalaVersion) ) Seq(file) } diff --git a/framework/src/console/src/main/scala/Console.scala b/framework/src/console/src/main/scala/Console.scala index 7b32c5078f0..b1426ca0f1b 100644 --- a/framework/src/console/src/main/scala/Console.scala +++ b/framework/src/console/src/main/scala/Console.scala @@ -20,13 +20,13 @@ object Console { || __/|_|\____|\__ (_) ||_| |__/ | - |""".stripMargin) + ("play! " + play.core.PlayVersion.current + ", http://www.playframework.org") + |""".stripMargin) + ("play! " + play.core.PlayVersion.current + " (using Scala " + play.core.PlayVersion.scalaVersion + "), http://www.playframework.org") // -- Commands def replace(file: File, tokens: (String, String)*) { if (file.exists) { - Path(file).write(tokens.foldLeft(Path(file).slurpString) { (state, token) => + Path(file).write(tokens.foldLeft(Path(file).string) { (state, token) => state.replace("%" + token._1 + "%", token._2) }) } diff --git a/framework/src/play-exceptions/src/main/java/play/api/PlayException.java b/framework/src/play-exceptions/src/main/java/play/api/PlayException.java new file mode 100644 index 00000000000..5b9ac26f68f --- /dev/null +++ b/framework/src/play-exceptions/src/main/java/play/api/PlayException.java @@ -0,0 +1,159 @@ +package play.api; + +import java.io.*; +import java.util.*; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * Helper for `PlayException`. + */ +public class PlayException extends UsefulException { + + private final AtomicLong generator = new AtomicLong(System.currentTimeMillis()); + + /** + * Generates a new unique exception ID. + */ + private String nextId() { + return java.lang.Long.toString(generator.incrementAndGet(), 26); + } + + public PlayException(String title, String description, Throwable cause) { + super(title + "[" + description + "]",cause); + this.title = title; + this.description = description; + this.id = nextId(); + this.cause = cause; + } + + public PlayException(String title, String description) { + super(title + "[" + description + "]"); + this.title = title; + this.description = description; + this.id = nextId(); + this.cause = null; + } + + /** + * Adds source attachment to a Play exception. + */ + public static abstract class ExceptionSource extends PlayException { + + public ExceptionSource(String title, String description, Throwable cause) { + super(title, description,cause); + } + + public ExceptionSource(String title, String description) { + super(title, description); + } + + /** + * Error line number, if defined. + */ + public abstract Integer line(); + + /** + * Column position, if defined. + */ + public abstract Integer position(); + + /** + * Input stream used to read the source content. + */ + public abstract String input(); + + /** + * The source file name if defined. + */ + public abstract String sourceName(); + + /** + * Extracts interesting lines to be displayed to the user. + * + * @param border number of lines to use as a border + */ + public InterestingLines interestingLines(int border) { + try { + if(input() == null || line() == null) { + return null; + } + String[] lines = input().split("\n"); + int firstLine = Math.max(0, line() - border); + int lastLine = Math.min(lines.length - 1, line() + border); + List focusOn = new ArrayList(); + for(int i = firstLine; i <= lastLine; i++) { + focusOn.add(lines[i]); + } + return new InterestingLines(firstLine + 1, focusOn.toArray(new String[focusOn.size()]), line() - firstLine - 1); + } catch(Throwable e) { + e.printStackTrace(); + return null; + } + } + + public String toString() { + return super.toString() + " in " + sourceName() + ":" + line(); + } + } + + /** + * Adds any attachment to a Play exception. + */ + public static abstract class ExceptionAttachment extends PlayException { + + public ExceptionAttachment(String title, String description, Throwable cause) { + super(title, description, cause); + } + + public ExceptionAttachment(String title, String description) { + super(title, description); + } + + /** + * Content title. + */ + public abstract String subTitle(); + + /** + * Content to be displayed. + */ + public abstract String content(); + + } + + /** + * Adds a rich HTML description to a Play exception. + */ + public static abstract class RichDescription extends ExceptionAttachment { + + public RichDescription(String title, String description, Throwable cause) { + super(title, description, cause); + } + + public RichDescription(String title, String description) { + super(title, description); + } + + /** + * The new description formatted as HTML. + */ + public abstract String htmlDescription(); + + } + + public static class InterestingLines { + + public final int firstLine; + public final int errorLine; + public final String[] focus; + + public InterestingLines(int firstLine, String[] focus, int errorLine){ + this.firstLine = firstLine; + this.errorLine = errorLine; + this.focus = focus; + } + + } + +} \ No newline at end of file diff --git a/framework/src/play-exceptions/src/main/java/play/api/UsefulException.java b/framework/src/play-exceptions/src/main/java/play/api/UsefulException.java new file mode 100644 index 00000000000..bd2df40a8b5 --- /dev/null +++ b/framework/src/play-exceptions/src/main/java/play/api/UsefulException.java @@ -0,0 +1,40 @@ +package play.api; + +/** +* A UsefulException is something useful to display in the User browser. +*/ +public abstract class UsefulException extends RuntimeException { + + /** + * Exception title. + */ + public String title; + + /** + * Exception description. + */ + public String description; + + /** + * Exception cause if defined. + */ + public Throwable cause; + + /** + * Unique id for this exception. + */ + public String id; + + public UsefulException(String message, Throwable cause) { + super(message, cause); + } + + public UsefulException(String message) { + super(message); + } + + public String toString() { + return "@" + id + ": " + title; + } + +} \ No newline at end of file diff --git a/framework/src/play/src/main/scala/play/api/Application.scala b/framework/src/play/src/main/scala/play/api/Application.scala index d54e312dbf0..da4082e2fb4 100644 --- a/framework/src/play/src/main/scala/play/api/Application.scala +++ b/framework/src/play/src/main/scala/play/api/Application.scala @@ -52,10 +52,11 @@ trait WithDefaultGlobal { javaGlobal.map(new j.JavaGlobalSettingsAdapter(_)).getOrElse(scalaGlobal) } catch { case e: PlayException => throw e - case e => throw PlayException( + case e => throw new PlayException( "Cannot init the Global object", e.getMessage, - Some(e)) + e + ) } } @@ -76,6 +77,7 @@ trait WithDefaultConfiguration { } def configuration: Configuration = fullConfiguration + } trait WithDefaultPlugins { @@ -92,7 +94,7 @@ trait WithDefaultPlugins { val pluginFiles = self.classloader.getResources("play.plugins").asScala.toList ++ self.classloader.getResources("conf/play.plugins").asScala.toList pluginFiles.distinct.map { plugins => - (plugins.asInput.slurpString.split("\n").map(_.trim)).filterNot(_.isEmpty).map { + (plugins.asInput.string.split("\n").map(_.trim)).filterNot(_.isEmpty).map { case PluginDeclaration(priority, className) => (priority.toInt, className) } }.flatten.sortBy(_._1).map(_._2) @@ -133,21 +135,21 @@ trait WithDefaultPlugins { if (plugin.enabled) Some(plugin) else { Logger("play").warn("Plugin [" + className + "] is disabled"); None } } catch { case e: PlayException => throw e - case e => throw PlayException( + case e => throw new PlayException( "Cannot load plugin", "Plugin [" + className + "] cannot been instantiated.", - Some(e)) + e) } } - case e: InvocationTargetException => throw PlayException( + case e: InvocationTargetException => throw new PlayException( "Cannot load plugin", "An exception occurred during Plugin [" + className + "] initialization", - Some(e.getTargetException)) + e.getTargetException) case e: PlayException => throw e - case e => throw PlayException( + case e => throw new PlayException( "Cannot load plugin", "Plugin [" + className + "] cannot been instantiated.", - Some(e)) + e) } }.flatten @@ -262,19 +264,19 @@ trait Application { */ private[play] def handleError(request: RequestHeader, e: Throwable): Result = try { e match { - case e: PlayException.UsefulException => throw e + case e: UsefulException => throw e case e: Throwable => { val source = sources.flatMap(_.sourceFor(e)) - throw new PlayException( + throw new PlayException.ExceptionSource( "Execution exception", "[%s: %s]".format(e.getClass.getSimpleName, e.getMessage), - Some(e)) with PlayException.ExceptionSource { - def line = source.map(_._2) - def position = None - def input = source.map(_._1).map(scalax.file.Path(_)) - def sourceName = source.map(_._1.getAbsolutePath) + e) { + def line = source.flatMap(_._2).map(_.asInstanceOf[java.lang.Integer]).orNull + def position = null + def input = source.map(_._1).map(scalax.file.Path(_).string).orNull + def sourceName = source.map(_._1.getAbsolutePath).orNull } } } @@ -288,8 +290,8 @@ trait Application { case p: PlayException => "@" + p.id + " - " case _ => "" }, request.method, request.uri), - e) - + e + ) global.onError(request, e) } catch { case e => DefaultGlobal.onError(request, e) @@ -413,4 +415,4 @@ class DefaultApplication( override val classloader: ClassLoader, override val sources: Option[SourceMapper], override val mode: Mode.Mode -) extends Application with WithDefaultConfiguration with WithDefaultGlobal with WithDefaultPlugins \ No newline at end of file +) extends Application with WithDefaultConfiguration with WithDefaultGlobal with WithDefaultPlugins diff --git a/framework/src/play/src/main/scala/play/api/Configuration.scala b/framework/src/play/src/main/scala/play/api/Configuration.scala index ed38412d3a5..ed1c7a4cd87 100644 --- a/framework/src/play/src/main/scala/play/api/Configuration.scala +++ b/framework/src/play/src/main/scala/play/api/Configuration.scala @@ -68,11 +68,11 @@ object Configuration { private def configError(origin: ConfigOrigin, message: String, e: Option[Throwable] = None): PlayException = { import scalax.io.JavaConverters._ - new PlayException("Configuration error", message, e) with PlayException.ExceptionSource { - def line = Option(origin.lineNumber) - def position = None - def input = Option(origin.url).map(_.asInput) - def sourceName = Option(origin.filename) + new PlayException.ExceptionSource("Configuration error", message, e.orNull) { + def line = Option(origin.lineNumber:java.lang.Integer).orNull + def position = null + def input = Option(origin.url).map(_.asInput.string).orNull + def sourceName = Option(origin.filename).orNull override def toString = "Configuration error: " + getMessage } } diff --git a/framework/src/play/src/main/scala/play/api/Exceptions.scala b/framework/src/play/src/main/scala/play/api/Exceptions.scala index f63604bb07a..68af8929d02 100644 --- a/framework/src/play/src/main/scala/play/api/Exceptions.scala +++ b/framework/src/play/src/main/scala/play/api/Exceptions.scala @@ -1,155 +1,5 @@ package play.api -import java.io.File - -/** - * Helper for `PlayException`. - */ -object PlayException { - - private val generator = new java.util.concurrent.atomic.AtomicLong(System.currentTimeMillis) - - /** - * Generates a new unique exception ID. - */ - def nextId = java.lang.Long.toString(generator.incrementAndGet, 26) - - /** - * A UsefulException is something useful to display in the User browser. - */ - trait UsefulException { - - /** - * Exception title. - */ - def title: String - - /** - * Exception description. - */ - def description: String - - /** - * Exception cause if defined. - */ - def cause: Option[Throwable] - - /** - * Unique id for this exception. - */ - def id: String - - } - - /** - * Adds source attachment to a Play exception. - */ - trait ExceptionSource { - self: PlayException => - - /** - * Error line number, if defined. - */ - def line: Option[Int] - - /** - * Column position, if defined. - */ - def position: Option[Int] - - /** - * Input stream used to read the source content. - */ - def input: Option[scalax.io.Input] - - /** - * The source file name if defined. - */ - def sourceName: Option[String] - - /** - * Extracts interesting lines to be displayed to the user. - * - * @param border number of lines to use as a border - */ - def interestingLines(border: Int = 4): Option[(Int, Seq[String], Int)] = { - for (f <- input; l <- line; val (first, last) = f.slurpString.split('\n').splitAt(l - 1); focus <- last.headOption) yield { - val before = first.takeRight(border) - val after = last.drop(1).take(border) - val firstLine = l - before.size - val errorLine = before.size - (firstLine, (before :+ focus) ++ after, errorLine) - } - } - - override def toString = "in " + sourceName.getOrElse("") + line.map(":" + _).getOrElse("") + " - " + super.toString() - } - - /** - * Adds any attachment to a Play exception. - */ - trait ExceptionAttachment { - self: PlayException => - - /** - * Content title. - */ - def subTitle: String - - /** - * Content to be displayed. - */ - def content: String - - } - - /** - * Adds a rich HTML description to a Play exception. - */ - trait RichDescription { - self: PlayException => - - /** - * The new description formatted as HTML. - */ - def htmlDescription: String - - } - - /** - * Create a new PlayException. - */ - def apply(title: String, description: String, cause: Option[Throwable] = None): PlayException = { - new PlayException(title, description, cause) - } - - /** - * PlayException extractor. - */ - def unapply(ex: PlayException): Option[(String, String, Option[Throwable])] = { - Some(ex.title, ex.description, ex.cause) - } - -} - -/** - * Root exception for all Play problems. - * - * @param title the problem title - * @param description the problem description - * @param cause the underlying cause, if it exists - */ -class PlayException(val title: String, val description: String, val cause: Option[Throwable] = None) extends RuntimeException("%s [%s]".format(title, description), cause.orNull) with PlayException.UsefulException { - - /** - * The exception ID, useful for retrieving problems in log files. - */ - val id = PlayException.nextId - - override def toString = "PlayException: " + getMessage - -} - /** * Generic exception for unexpected error cases. */ @@ -158,5 +8,5 @@ case class UnexpectedException(message: Option[String] = None, unexpected: Optio message.getOrElse { unexpected.map(t => "%s: %s".format(t.getClass.getSimpleName, t.getMessage)).getOrElse("") }, - unexpected) - + unexpected.orNull +) diff --git a/framework/src/play/src/main/scala/play/api/GlobalSettings.scala b/framework/src/play/src/main/scala/play/api/GlobalSettings.scala index 69678e1de71..de4189be182 100644 --- a/framework/src/play/src/main/scala/play/api/GlobalSettings.scala +++ b/framework/src/play/src/main/scala/play/api/GlobalSettings.scala @@ -80,15 +80,22 @@ trait GlobalSettings { * @return The result to send to the client */ def onError(request: RequestHeader, ex: Throwable): Result = { - InternalServerError(Play.maybeApplication.map { - case app if app.mode != Mode.Prod => views.html.defaultpages.devError.f - case app => views.html.defaultpages.error.f - }.getOrElse(views.html.defaultpages.devError.f) { - ex match { - case e: PlayException.UsefulException => e - case e => UnexpectedException(unexpected = Some(e)) + try { + InternalServerError(Play.maybeApplication.map { + case app if app.mode != Mode.Prod => views.html.defaultpages.devError.f + case app => views.html.defaultpages.error.f + }.getOrElse(views.html.defaultpages.devError.f) { + ex match { + case e: UsefulException => e + case e => UnexpectedException(unexpected = Some(e)) + } + }) + } catch { + case e: Throwable => { + Logger.error("Error while rendering default error page", e) + InternalServerError } - }) + } } /** diff --git a/framework/src/play/src/main/scala/play/api/db/evolutions/Evolutions.scala b/framework/src/play/src/main/scala/play/api/db/evolutions/Evolutions.scala index 4b648dc2af7..969be13450b 100644 --- a/framework/src/play/src/main/scala/play/api/db/evolutions/Evolutions.scala +++ b/framework/src/play/src/main/scala/play/api/db/evolutions/Evolutions.scala @@ -361,7 +361,7 @@ object Evolutions { Option(new File(path, "conf/evolutions/" + db + "/" + revision + ".sql")).filter(_.exists).map(new FileInputStream(_)).orElse { Option(applicationClassloader.getResourceAsStream("evolutions/" + db + "/" + revision + ".sql")) }.map { stream => - (revision + 1, (revision, stream.asInput.slurpString)) + (revision + 1, (revision, stream.asInput.string)) } }.sortBy(_._1).map { case (revision, script) => { @@ -545,10 +545,9 @@ object OfflineEvolutions { * @param db the database name * @param script the script to be run to resolve the conflict. */ -case class InvalidDatabaseRevision(db: String, script: String) extends PlayException( +case class InvalidDatabaseRevision(db: String, script: String) extends PlayException.RichDescription( "Database '" + db + "' needs evolution!", - "An SQL script need to be run on your database.", - None) with PlayException.ExceptionAttachment with PlayException.RichDescription { + "An SQL script need to be run on your database.") { def subTitle = "This SQL script must be run:" def content = script @@ -571,10 +570,9 @@ case class InvalidDatabaseRevision(db: String, script: String) extends PlayExcep * * @param db the database name */ -case class InconsistentDatabase(db: String, script: String, error: String, rev: Int) extends PlayException( +case class InconsistentDatabase(db: String, script: String, error: String, rev: Int) extends PlayException.RichDescription( "Database '" + db + "' is in inconsistent state!", - "An evolution has not been applied properly. Please check the problem and resolve it manually before marking it as resolved.", - None) with PlayException.ExceptionAttachment with PlayException.RichDescription { + "An evolution has not been applied properly. Please check the problem and resolve it manually before marking it as resolved.") { def subTitle = "We got the following error: " + error + ", while trying to run this SQL script:" def content = script diff --git a/framework/src/play/src/main/scala/play/api/i18n/Messages.scala b/framework/src/play/src/main/scala/play/api/i18n/Messages.scala index 6826bfb2ff9..9f801ebc749 100644 --- a/framework/src/play/src/main/scala/play/api/i18n/Messages.scala +++ b/framework/src/play/src/main/scala/play/api/i18n/Messages.scala @@ -196,14 +196,14 @@ object Messages { } def parse = { - parser(new CharSequenceReader(messageInput.slurpString + "\n")) match { + parser(new CharSequenceReader(messageInput.string + "\n")) match { case Success(messages, _) => messages case NoSuccess(message, in) => { - throw new PlayException("Configuration error", message) with PlayException.ExceptionSource { - def line = Some(in.pos.line) - def position = Some(in.pos.column - 1) - def input = Some(messageInput) - def sourceName = Some(messageSourceName) + throw new PlayException.ExceptionSource("Configuration error", message) { + def line = in.pos.line + def position = in.pos.column - 1 + def input = messageInput.string + def sourceName = messageSourceName } } } diff --git a/framework/src/play/src/main/scala/play/api/libs/Crypto.scala b/framework/src/play/src/main/scala/play/api/libs/Crypto.scala index 7aa21138a4d..7311466d771 100644 --- a/framework/src/play/src/main/scala/play/api/libs/Crypto.scala +++ b/framework/src/play/src/main/scala/play/api/libs/Crypto.scala @@ -28,7 +28,7 @@ object Crypto { */ def sign(message: String): String = { secret.map(secret => sign(message, secret.getBytes)).getOrElse { - throw PlayException("Configuration error", "Missing application.secret") + throw new PlayException("Configuration error", "Missing application.secret") } } @@ -39,7 +39,7 @@ object Crypto { */ def encryptAES(value: String): String = { secret.map(secret => encryptAES(value, secret.substring(0, 16))).getOrElse { - throw PlayException("Configuration error", "Missing application.secret") + throw new PlayException("Configuration error", "Missing application.secret") } } @@ -64,7 +64,7 @@ object Crypto { */ def decryptAES(value: String): String = { secret.map(secret => decryptAES(value, secret.substring(0, 16))).getOrElse { - throw PlayException("Configuration error", "Missing application.secret") + throw new PlayException("Configuration error", "Missing application.secret") } } diff --git a/framework/src/play/src/main/scala/play/api/libs/Files.scala b/framework/src/play/src/main/scala/play/api/libs/Files.scala index fdfece7b3ef..a5d6e3d0556 100644 --- a/framework/src/play/src/main/scala/play/api/libs/Files.scala +++ b/framework/src/play/src/main/scala/play/api/libs/Files.scala @@ -82,7 +82,7 @@ object Files { * @param path the file to read. * @return the file contents */ - def readFile(path: File): String = Path(path).slurpString + def readFile(path: File): String = Path(path).string /** * Write a file’s contents as a `String`. diff --git a/framework/src/play/src/main/scala/play/api/mvc/Http.scala b/framework/src/play/src/main/scala/play/api/mvc/Http.scala index fb722e66f27..852de3d3097 100644 --- a/framework/src/play/src/main/scala/play/api/mvc/Http.scala +++ b/framework/src/play/src/main/scala/play/api/mvc/Http.scala @@ -300,7 +300,7 @@ package play.api.mvc { /** * Retrieve all header values associated with the given key. */ - def getAll(key: String): Seq[String] = toMap.get(key).flatten.toSeq + def getAll(key: String): Seq[String] = (toMap.get(key):Option[Seq[String]]).flatten.toSeq /** * Retrieve all header keys diff --git a/framework/src/play/src/main/scala/play/core/router/Router.scala b/framework/src/play/src/main/scala/play/core/router/Router.scala index 57317d36a1c..8dd4358bb7c 100644 --- a/framework/src/play/src/main/scala/play/core/router/Router.scala +++ b/framework/src/play/src/main/scala/play/core/router/Router.scala @@ -4,6 +4,60 @@ import play.api.mvc._ import play.api.mvc.Results._ import org.apache.commons.lang3.reflect.MethodUtils +import scala.util.parsing.input._ +import scala.util.parsing.combinator._ +import scala.util.matching._ + +trait PathPart + +case class DynamicPart(name: String, constraint: String) extends PathPart { + override def toString = """DynamicPart("""" + name + "\", \"\"\"" + constraint + "\"\"\")" // " +} + +case class StaticPart(value: String) extends PathPart { + override def toString = """StaticPart("""" + value + """")""" +} + +case class PathPattern(parts: Seq[PathPart]) { + + import java.util.regex._ + + lazy val (regex, groups) = { + Some(parts.foldLeft("", Map.empty[String, Int], 0) { (s, e) => + e match { + case StaticPart(p) => ((s._1 + Pattern.quote(p)), s._2, s._3) + case DynamicPart(k, r) => { + ((s._1 + "(" + r + ")"), (s._2 + (k -> (s._3 + 1))), s._3 + 1 + Pattern.compile(r).matcher("").groupCount) + } + } + }).map { + case (r, g, _) => Pattern.compile("^" + r + "$") -> g + }.get + } + + def apply(path: String): Option[Map[String, String]] = { + val matcher = regex.matcher(path) + if (matcher.matches) { + Some(groups.map { + case (name, g) => name -> matcher.group(g) + }.toMap) + } else { + None + } + } + + def has(key: String): Boolean = parts.exists { + case DynamicPart(name, _) if name == key => true + case _ => false + } + + override def toString = parts.map { + case DynamicPart(name, constraint) => "$" + name + "<" + constraint + ">" + case StaticPart(path) => path + }.mkString + +} + /** * provides Play's router implementation */ @@ -14,7 +68,7 @@ object Router { trait ParamsExtractor { def unapply(request: RequestHeader): Option[RouteParams] } - def apply(method: String, pathPattern: play.router.PathPattern) = new ParamsExtractor { + def apply(method: String, pathPattern: PathPattern) = new ParamsExtractor { def unapply(request: RequestHeader): Option[RouteParams] = { if (method == request.method) { diff --git a/framework/src/play/src/main/scala/play/core/system/ApplicationProvider.scala b/framework/src/play/src/main/scala/play/core/system/ApplicationProvider.scala index cd7ab83ad31..65f22861baa 100644 --- a/framework/src/play/src/main/scala/play/core/system/ApplicationProvider.scala +++ b/framework/src/play/src/main/scala/play/core/system/ApplicationProvider.scala @@ -15,17 +15,11 @@ import play.api.mvc._ */ trait SourceMapper { - def sourceOf(className: String): Option[File] - - def sourceFor(e: Throwable): Option[(File, Int)] = { - e.getStackTrace.find(element => sourceOf(element.getClassName).isDefined).map { interestingStackTrace => - sourceOf(interestingStackTrace.getClassName).get -> interestingStackTrace.getLineNumber - }.map { - case (source, line) => { - play.templates.MaybeGeneratedSource.unapply(source).map { generatedSource => - generatedSource.source.get -> generatedSource.mapLine(line) - }.getOrElse(source -> line) - } + def sourceOf(className: String, line: Option[Int] = None): Option[(File, Option[Int])] + + def sourceFor(e: Throwable): Option[(File, Option[Int])] = { + e.getStackTrace.find(element => sourceOf(element.getClassName).isDefined).flatMap { interestingStackTrace => + sourceOf(interestingStackTrace.getClassName, Option(interestingStackTrace.getLineNumber)) } } @@ -71,12 +65,22 @@ class TestApplication(application: Application) extends ApplicationProvider { */ class ReloadableApplication(sbtLink: SBTLink) extends ApplicationProvider { + // Use plain Java call here in case of scala classloader mess + { + if(System.getProperty("play.debug.classpath") == "true") { + System.out.println("\n---- Current ClassLoader ----\n") + System.out.println(this.getClass.getClassLoader) + System.out.println("\n---- The where is Scala? test ----\n") + System.out.println(this.getClass.getClassLoader.getResource("scala/Predef$.class")) + } + } + lazy val path = sbtLink.projectPath println(play.utils.Colors.magenta("--- (Running the application from SBT, auto-reloading is enabled) ---")) println() - var lastState: Either[Throwable, Application] = Left(PlayException("Not initialized", "?")) + var lastState: Either[Throwable, Application] = Left(new PlayException("Not initialized", "?")) def get = { @@ -112,7 +116,13 @@ class ReloadableApplication(sbtLink: SBTLink) extends ApplicationProvider { val reloadable = this val newApplication = new DefaultApplication(reloadable.path, projectClassloader, Some(new SourceMapper { - def sourceOf(className: String) = Option(sbtLink.findSource(className)) + def sourceOf(className: String, line: Option[Int]) = { + Option(sbtLink.findSource(className, line.map(_.asInstanceOf[java.lang.Integer]).orNull)).flatMap { + case Array(file: java.io.File, null) => Some((file, None)) + case Array(file: java.io.File, line: java.lang.Integer) => Some((file, Some(line))) + case _ => None + } + } }),Mode.Dev) Play.start(newApplication) @@ -199,7 +209,7 @@ class ReloadableApplication(sbtLink: SBTLink) extends ApplicationProvider { documentationHome.flatMap { home => Option(new java.io.File(home, "manual/book/Book")).filter(_.exists) }.map { book => - val pages = Path(book).slurpString.split('\n').toSeq.map(_.trim) + val pages = Path(book).string.split('\n').toSeq.map(_.trim) Ok(views.html.play20.book(pages)) }.getOrElse(NotFound("Resource not found [Book]")) } @@ -282,8 +292,8 @@ class ReloadableApplication(sbtLink: SBTLink) extends ApplicationProvider { Ok( views.html.play20.manual( page, - Some(sbtLink.markdownToHtml(pageSource.slurpString/*, linkRender*/)), - maybeSidebar.map(s => sbtLink.markdownToHtml(s.slurpString/*, linkRender*/)) + Some(sbtLink.markdownToHtml(pageSource.string/*, linkRender*/)), + maybeSidebar.map(s => sbtLink.markdownToHtml(s.string/*, linkRender*/)) ) ) } diff --git a/framework/src/play-utils/src/main/scala/play/utils/CaseInstensitiveOrdered.scala b/framework/src/play/src/main/scala/play/utils/CaseInstensitiveOrdered.scala similarity index 100% rename from framework/src/play-utils/src/main/scala/play/utils/CaseInstensitiveOrdered.scala rename to framework/src/play/src/main/scala/play/utils/CaseInstensitiveOrdered.scala diff --git a/framework/src/play-utils/src/main/scala/play/utils/Color.scala b/framework/src/play/src/main/scala/play/utils/Color.scala similarity index 100% rename from framework/src/play-utils/src/main/scala/play/utils/Color.scala rename to framework/src/play/src/main/scala/play/utils/Color.scala diff --git a/framework/src/play-utils/src/main/scala/play/utils/Conversions.scala b/framework/src/play/src/main/scala/play/utils/Conversions.scala similarity index 100% rename from framework/src/play-utils/src/main/scala/play/utils/Conversions.scala rename to framework/src/play/src/main/scala/play/utils/Conversions.scala diff --git a/framework/src/play-utils/src/main/scala/play/utils/FormUrlEncodedParser.scala b/framework/src/play/src/main/scala/play/utils/FormUrlEncodedParser.scala similarity index 100% rename from framework/src/play-utils/src/main/scala/play/utils/FormUrlEncodedParser.scala rename to framework/src/play/src/main/scala/play/utils/FormUrlEncodedParser.scala diff --git a/framework/src/play-utils/src/main/scala/play/utils/ProxyDriver.scala b/framework/src/play/src/main/scala/play/utils/ProxyDriver.scala similarity index 100% rename from framework/src/play-utils/src/main/scala/play/utils/ProxyDriver.scala rename to framework/src/play/src/main/scala/play/utils/ProxyDriver.scala diff --git a/framework/src/play-utils/src/main/scala/play/utils/Threads.scala b/framework/src/play/src/main/scala/play/utils/Threads.scala similarity index 100% rename from framework/src/play-utils/src/main/scala/play/utils/Threads.scala rename to framework/src/play/src/main/scala/play/utils/Threads.scala diff --git a/framework/src/play/src/main/scala/views/defaultpages/devError.scala.html b/framework/src/play/src/main/scala/views/defaultpages/devError.scala.html index 74283d26f4f..264a6ec9573 100644 --- a/framework/src/play/src/main/scala/views/defaultpages/devError.scala.html +++ b/framework/src/play/src/main/scala/views/defaultpages/devError.scala.html @@ -2,7 +2,7 @@ * Default page for 500 Internal Server Error responses, in development mode. * This page display the error in the source code context. *@ -@(error: play.api.PlayException.UsefulException) +@(error: play.api.UsefulException) @@ -125,27 +125,27 @@

@error.title

case source:play.api.PlayException.ExceptionSource => { - @source.sourceName.map { name => + @Option(source.sourceName).map { name =>

- In @name @source.line.map { line => + In @name @Option(source.line).map { line => at line @line. }

- @source.interestingLines().map { + @Option(source.interestingLines(4)).map { - case (first,lines,errorIndex) => { + case interesting => { - @lines.zipWithIndex.map { + @interesting.focus.zipWithIndex.map { - case (line,index) if index == errorIndex => { -
 data-column="@c" }>@(first+index)@(source.position.map(pos => (line+" ").zipWithIndex.map{ case (c,i) if i == pos => Html("""""" + c + """"""); case (c,_) => c}).getOrElse(line))
+ case (line,index) if index == interesting.errorLine => { +
 data-column="@c" }>@(interesting.firstLine+index)@(Option(source.position).map(pos => (line+" ").zipWithIndex.map{ case (c,i) if i == pos => Html("""""" + c + """"""); case (c,_) => c}).getOrElse(line))
} case (line, index) => { -
@(first+index)@line
+
@(interesting.firstLine+index)@line
} } @@ -174,7 +174,7 @@

@attachment.subTitle

} - case play.api.PlayException(_, _ , Some(throwable)) => { + case exception: play.api.PlayException if exception.cause != null => {

No source available, here is the exception stack trace: @@ -182,9 +182,9 @@

-
->@throwable.getClass.getName: @throwable.getMessage
+
->@exception.cause.getClass.getName: @exception.cause.getMessage
- @throwable.getStackTrace.map { line => + @exception.cause.getStackTrace.map { line =>
     @line
}
diff --git a/framework/src/play/src/main/scala/views/defaultpages/error.scala.html b/framework/src/play/src/main/scala/views/defaultpages/error.scala.html index 9963ce1ef1f..07a91dffc16 100644 --- a/framework/src/play/src/main/scala/views/defaultpages/error.scala.html +++ b/framework/src/play/src/main/scala/views/defaultpages/error.scala.html @@ -1,7 +1,7 @@ @** * Default page for 500 Internal Server Error responses, in production mode. *@ -@(error: play.api.PlayException.UsefulException) +@(error: play.api.UsefulException) diff --git a/framework/src/routes-compiler/src/main/scala/play/router/PathParts.scala b/framework/src/routes-compiler/src/main/scala/play/router/PathParts.scala index 0b6be524707..b0875bf5a32 100644 --- a/framework/src/routes-compiler/src/main/scala/play/router/PathParts.scala +++ b/framework/src/routes-compiler/src/main/scala/play/router/PathParts.scala @@ -4,54 +4,55 @@ import scala.util.parsing.input._ import scala.util.parsing.combinator._ import scala.util.matching._ +/** + * captures URL parts + */ +trait PathPart - - /** - * captures URL parts - */ - trait PathPart - case class DynamicPart(name: String, constraint: String) extends PathPart with Positional { - override def toString = """DynamicPart("""" + name + "\", \"\"\"" + constraint + "\"\"\")" - } - case class StaticPart(value: String) extends PathPart { - override def toString = """StaticPart("""" + value + """")""" - } - case class PathPattern(parts: Seq[PathPart]) { +case class DynamicPart(name: String, constraint: String) extends PathPart with Positional { + override def toString = """DynamicPart("""" + name + "\", \"\"\"" + constraint + "\"\"\")" //" +} - import java.util.regex._ +case class StaticPart(value: String) extends PathPart { + override def toString = """StaticPart("""" + value + """")""" +} - lazy val (regex, groups) = { - Some(parts.foldLeft("", Map.empty[String, Int], 0) { (s, e) => - e match { - case StaticPart(p) => ((s._1 + Pattern.quote(p)), s._2, s._3) - case DynamicPart(k, r) => { - ((s._1 + "(" + r + ")"), (s._2 + (k -> (s._3 + 1))), s._3 + 1 + Pattern.compile(r).matcher("").groupCount) - } - } - }).map { - case (r, g, _) => Pattern.compile("^" + r + "$") -> g - }.get - } +case class PathPattern(parts: Seq[PathPart]) { + + import java.util.regex._ - def apply(path: String): Option[Map[String, String]] = { - val matcher = regex.matcher(path) - if (matcher.matches) { - Some(groups.map { - case (name, g) => name -> matcher.group(g) - }.toMap) - } else { - None + lazy val (regex, groups) = { + Some(parts.foldLeft("", Map.empty[String, Int], 0) { (s, e) => + e match { + case StaticPart(p) => ((s._1 + Pattern.quote(p)), s._2, s._3) + case DynamicPart(k, r) => { + ((s._1 + "(" + r + ")"), (s._2 + (k -> (s._3 + 1))), s._3 + 1 + Pattern.compile(r).matcher("").groupCount) + } } - } + }).map { + case (r, g, _) => Pattern.compile("^" + r + "$") -> g + }.get + } - def has(key: String): Boolean = parts.exists { - case DynamicPart(name, _) if name == key => true - case _ => false + def apply(path: String): Option[Map[String, String]] = { + val matcher = regex.matcher(path) + if (matcher.matches) { + Some(groups.map { + case (name, g) => name -> matcher.group(g) + }.toMap) + } else { + None } + } - override def toString = parts.map { - case DynamicPart(name, constraint) => "$" + name + "<" + constraint + ">" - case StaticPart(path) => path - }.mkString - + def has(key: String): Boolean = parts.exists { + case DynamicPart(name, _) if name == key => true + case _ => false } + + override def toString = parts.map { + case DynamicPart(name, constraint) => "$" + name + "<" + constraint + ">" + case StaticPart(path) => path + }.mkString + +} diff --git a/framework/src/routes-compiler/src/main/scala/play/router/RoutesCompiler.scala b/framework/src/routes-compiler/src/main/scala/play/router/RoutesCompiler.scala index 24f2344efee..d2d90f765c5 100644 --- a/framework/src/routes-compiler/src/main/scala/play/router/RoutesCompiler.scala +++ b/framework/src/routes-compiler/src/main/scala/play/router/RoutesCompiler.scala @@ -4,371 +4,401 @@ import scala.util.parsing.input._ import scala.util.parsing.combinator._ import scala.util.matching._ - /** - * provides a compiler for routes - */ - object RoutesCompiler { +/** + * provides a compiler for routes + */ +object RoutesCompiler { - case class HttpVerb(value: String) { - override def toString = value - } - case class HandlerCall(packageName: String, controller: String, instantiate: Boolean, method: String, field: Option[String], parameters: Option[Seq[Parameter]]) extends Positional { - val dynamic = if (instantiate) "@" else "" - override def toString = dynamic + packageName + "." + controller + dynamic + "." + field.map(_ + ".").getOrElse("") + method + parameters.map { params => - "(" + params.mkString(", ") + ")" - }.getOrElse("") - } - case class Parameter(name: String, typeName: String, fixed: Option[String], default: Option[String]) extends Positional { - override def toString = name + ":" + typeName + fixed.map(" = " + _).getOrElse("") + default.map(" ?= " + _).getOrElse("") - } - - sealed trait Rule extends Positional + case class HttpVerb(value: String) { + override def toString = value + } + case class HandlerCall(packageName: String, controller: String, instantiate: Boolean, method: String, field: Option[String], parameters: Option[Seq[Parameter]]) extends Positional { + val dynamic = if (instantiate) "@" else "" + override def toString = dynamic + packageName + "." + controller + dynamic + "." + field.map(_ + ".").getOrElse("") + method + parameters.map { params => + "(" + params.mkString(", ") + ")" + }.getOrElse("") + } + case class Parameter(name: String, typeName: String, fixed: Option[String], default: Option[String]) extends Positional { + override def toString = name + ":" + typeName + fixed.map(" = " + _).getOrElse("") + default.map(" ?= " + _).getOrElse("") + } - case class Route(verb: HttpVerb, path: PathPattern, call: HandlerCall, comments: List[Comment] = List()) extends Rule - case class Include(prefix: String, router: String) extends Rule + sealed trait Rule extends Positional - case class Comment(comment: String) + case class Route(verb: HttpVerb, path: PathPattern, call: HandlerCall, comments: List[Comment] = List()) extends Rule + case class Include(prefix: String, router: String) extends Rule + case class Comment(comment: String) - object Hash { - def apply(bytes: Array[Byte]): String = { - import java.security.MessageDigest - val digest = MessageDigest.getInstance("SHA-1") - digest.reset() - digest.update(bytes) - digest.digest().map(0xFF & _).map { "%02x".format(_) }.foldLeft("") { _ + _ } - } + object Hash { + def apply(bytes: Array[Byte]): String = { + import java.security.MessageDigest + val digest = MessageDigest.getInstance("SHA-1") + digest.reset() + digest.update(bytes) + digest.digest().map(0xFF & _).map { "%02x".format(_) }.foldLeft("") { _ + _ } } - // --- Parser - - private[router] class RouteFileParser extends JavaTokenParsers { - - override def skipWhitespace = false - override val whiteSpace = """[ \t]+""".r - - override def phrase[T](p: Parser[T]) = new Parser[T] { - lastNoSuccess = null - def apply(in: Input) = p(in) match { - case s @ Success(out, in1) => - if (in1.atEnd) - s - else if (lastNoSuccess == null || lastNoSuccess.next.pos < in1.pos) - Failure("end of input expected", in1) - else - lastNoSuccess - case _ => lastNoSuccess - } + } + + // --- Parser + + private[router] class RouteFileParser extends JavaTokenParsers { + + override def skipWhitespace = false + override val whiteSpace = """[ \t]+""".r + + override def phrase[T](p: Parser[T]) = new Parser[T] { + lastNoSuccess = null + def apply(in: Input) = p(in) match { + case s @ Success(out, in1) => + if (in1.atEnd) + s + else if (lastNoSuccess == null || lastNoSuccess.next.pos < in1.pos) + Failure("end of input expected", in1) + else + lastNoSuccess + case _ => lastNoSuccess } + } - def EOF: util.matching.Regex = "\\z".r + def EOF: util.matching.Regex = "\\z".r - def namedError[A](p: Parser[A], msg: String): Parser[A] = Parser[A] { i => - p(i) match { - case Failure(_, in) => Failure(msg, in) - case o => o - } + def namedError[A](p: Parser[A], msg: String): Parser[A] = Parser[A] { i => + p(i) match { + case Failure(_, in) => Failure(msg, in) + case o => o } + } - def several[T](p: => Parser[T]): Parser[List[T]] = Parser { in => - import scala.collection.mutable.ListBuffer - val elems = new ListBuffer[T] - def continue(in: Input): ParseResult[List[T]] = { - val p0 = p // avoid repeatedly re-evaluating by-name parser - @scala.annotation.tailrec - def applyp(in0: Input): ParseResult[List[T]] = p0(in0) match { - case Success(x, rest) => elems += x; applyp(rest) - case Failure(_, _) => Success(elems.toList, in0) - case err: Error => err - } - applyp(in) + def several[T](p: => Parser[T]): Parser[List[T]] = Parser { in => + import scala.collection.mutable.ListBuffer + val elems = new ListBuffer[T] + def continue(in: Input): ParseResult[List[T]] = { + val p0 = p // avoid repeatedly re-evaluating by-name parser + @scala.annotation.tailrec + def applyp(in0: Input): ParseResult[List[T]] = p0(in0) match { + case Success(x, rest) => elems += x; applyp(rest) + case Failure(_, _) => Success(elems.toList, in0) + case err: Error => err } - continue(in) + applyp(in) } + continue(in) + } - def separator: Parser[String] = namedError(whiteSpace, "Whitespace expected") - - def ignoreWhiteSpace: Parser[Option[String]] = opt(whiteSpace) + def separator: Parser[String] = namedError(whiteSpace, "Whitespace expected") - def identifier: Parser[String] = namedError(ident, "Identifier expected") + def ignoreWhiteSpace: Parser[Option[String]] = opt(whiteSpace) - def end: util.matching.Regex = """\s*""".r + def identifier: Parser[String] = namedError(ident, "Identifier expected") - def comment: Parser[Comment] = "#" ~> ".*".r ^^ { - case c => Comment(c) - } + def end: util.matching.Regex = """\s*""".r - def newLine: Parser[String] = namedError((("\r"?) ~> "\n"), "End of line expected") + def comment: Parser[Comment] = "#" ~> ".*".r ^^ { + case c => Comment(c) + } - def blankLine: Parser[Unit] = ignoreWhiteSpace ~> newLine ^^ { case _ => () } + def newLine: Parser[String] = namedError((("\r"?) ~> "\n"), "End of line expected") - def parentheses: Parser[String] = { - "(" ~ (several((parentheses | not(")") ~> """.""".r))) ~ commit(")") ^^ { - case p1 ~ charList ~ p2 => p1 + charList.mkString + p2 - } - } + def blankLine: Parser[Unit] = ignoreWhiteSpace ~> newLine ^^ { case _ => () } - def brackets: Parser[String] = { - "[" ~ (several((parentheses | not("]") ~> """.""".r))) ~ commit("]") ^^ { - case p1 ~ charList ~ p2 => p1 + charList.mkString + p2 - } + def parentheses: Parser[String] = { + "(" ~ (several((parentheses | not(")") ~> """.""".r))) ~ commit(")") ^^ { + case p1 ~ charList ~ p2 => p1 + charList.mkString + p2 } + } - def string: Parser[String] = { - "\"" ~ (several((parentheses | not("\"") ~> """.""".r))) ~ commit("\"") ^^ { - case p1 ~ charList ~ p2 => p1 + charList.mkString + p2 - } + def brackets: Parser[String] = { + "[" ~ (several((parentheses | not("]") ~> """.""".r))) ~ commit("]") ^^ { + case p1 ~ charList ~ p2 => p1 + charList.mkString + p2 } + } - def multiString: Parser[String] = { - "\"\"\"" ~ (several((parentheses | not("\"\"\"") ~> """.""".r))) ~ commit("\"\"\"") ^^ { - case p1 ~ charList ~ p2 => p1 + charList.mkString + p2 - } + def string: Parser[String] = { + "\"" ~ (several((parentheses | not("\"") ~> """.""".r))) ~ commit("\"") ^^ { + case p1 ~ charList ~ p2 => p1 + charList.mkString + p2 } + } - def httpVerb: Parser[HttpVerb] = namedError("GET" | "POST" | "PUT" | "PATCH" | "HEAD" | "DELETE" | "OPTIONS", "HTTP Verb expected") ^^ { - case v => HttpVerb(v) + def multiString: Parser[String] = { + "\"\"\"" ~ (several((parentheses | not("\"\"\"") ~> """.""".r))) ~ commit("\"\"\"") ^^ { + case p1 ~ charList ~ p2 => p1 + charList.mkString + p2 } + } - def singleComponentPathPart: Parser[DynamicPart] = (":" ~> identifier) ^^ { - case name => DynamicPart(name, """[^/]+""") - } + def httpVerb: Parser[HttpVerb] = namedError("GET" | "POST" | "PUT" | "PATCH" | "HEAD" | "DELETE" | "OPTIONS", "HTTP Verb expected") ^^ { + case v => HttpVerb(v) + } - def multipleComponentsPathPart: Parser[DynamicPart] = ("*" ~> identifier) ^^ { - case name => DynamicPart(name, """.+""") - } + def singleComponentPathPart: Parser[DynamicPart] = (":" ~> identifier) ^^ { + case name => DynamicPart(name, """[^/]+""") + } - def regexComponentPathPart: Parser[DynamicPart] = "$" ~> identifier ~ ("<" ~> (not(">") ~> """[^\s]""".r +) <~ ">" ^^ { case c => c.mkString }) ^^ { - case name ~ regex => DynamicPart(name, regex) - } + def multipleComponentsPathPart: Parser[DynamicPart] = ("*" ~> identifier) ^^ { + case name => DynamicPart(name, """.+""") + } - def staticPathPart: Parser[StaticPart] = (not(":") ~> not("*") ~> not("$") ~> """[^\s]""".r +) ^^ { - case chars => StaticPart(chars.mkString) - } + def regexComponentPathPart: Parser[DynamicPart] = "$" ~> identifier ~ ("<" ~> (not(">") ~> """[^\s]""".r +) <~ ">" ^^ { case c => c.mkString }) ^^ { + case name ~ regex => DynamicPart(name, regex) + } - def path: Parser[PathPattern] = "/" ~ ((positioned(singleComponentPathPart) | positioned(multipleComponentsPathPart) | positioned(regexComponentPathPart) | staticPathPart) *) ^^ { - case _ ~ parts => PathPattern(parts) - } + def staticPathPart: Parser[StaticPart] = (not(":") ~> not("*") ~> not("$") ~> """[^\s]""".r +) ^^ { + case chars => StaticPart(chars.mkString) + } - def parameterType: Parser[String] = ":" ~> ignoreWhiteSpace ~> rep1sep(identifier, ".") ~ opt(brackets) ^^ { - case t ~ g => t.mkString(".") + g.getOrElse("") - } + def path: Parser[PathPattern] = "/" ~ ((positioned(singleComponentPathPart) | positioned(multipleComponentsPathPart) | positioned(regexComponentPathPart) | staticPathPart) *) ^^ { + case _ ~ parts => PathPattern(parts) + } - def expression: Parser[String] = (multiString | string | parentheses | brackets | """[^),?=\n]""".r +) ^^ { - case p => p.mkString - } + def parameterType: Parser[String] = ":" ~> ignoreWhiteSpace ~> rep1sep(identifier, ".") ~ opt(brackets) ^^ { + case t ~ g => t.mkString(".") + g.getOrElse("") + } - def parameterFixedValue: Parser[String] = "=" ~ ignoreWhiteSpace ~ expression ^^ { - case a ~ _ ~ b => a + b - } + def expression: Parser[String] = (multiString | string | parentheses | brackets | """[^),?=\n]""".r +) ^^ { + case p => p.mkString + } - def parameterDefaultValue: Parser[String] = "?=" ~ ignoreWhiteSpace ~ expression ^^ { - case a ~ _ ~ b => a + b - } + def parameterFixedValue: Parser[String] = "=" ~ ignoreWhiteSpace ~ expression ^^ { + case a ~ _ ~ b => a + b + } - def parameter: Parser[Parameter] = (identifier <~ ignoreWhiteSpace) ~ opt(parameterType) ~ (ignoreWhiteSpace ~> opt(parameterDefaultValue | parameterFixedValue)) ^^ { - case name ~ t ~ d => Parameter(name, t.getOrElse("String"), d.filter(_.startsWith("=")).map(_.drop(1)), d.filter(_.startsWith("?")).map(_.drop(2))) - } + def parameterDefaultValue: Parser[String] = "?=" ~ ignoreWhiteSpace ~ expression ^^ { + case a ~ _ ~ b => a + b + } - def parameters: Parser[List[Parameter]] = "(" ~> repsep(ignoreWhiteSpace ~> positioned(parameter) <~ ignoreWhiteSpace, ",") <~ ")" + def parameter: Parser[Parameter] = (identifier <~ ignoreWhiteSpace) ~ opt(parameterType) ~ (ignoreWhiteSpace ~> opt(parameterDefaultValue | parameterFixedValue)) ^^ { + case name ~ t ~ d => Parameter(name, t.getOrElse("String"), d.filter(_.startsWith("=")).map(_.drop(1)), d.filter(_.startsWith("?")).map(_.drop(2))) + } - def packageName: Parser[List[String]] = namedError(rep1sep("[a-z]+".r, "."), "Package name expected") + def parameters: Parser[List[Parameter]] = "(" ~> repsep(ignoreWhiteSpace ~> positioned(parameter) <~ ignoreWhiteSpace, ",") <~ ")" - def className: Parser[String] = namedError("[a-zA-Z]+".r, "Class name expected") + def packageName: Parser[List[String]] = namedError(rep1sep("[a-z]+".r, "."), "Package name expected") - def call: Parser[HandlerCall] = opt("@") ~ packageName ~ "." ~ className ~ "." ~ rep1sep(identifier, ".") ~ opt(parameters) ^^ { - case instantiate ~ pkgName ~ _ ~ className ~ _ ~ fieldMethod ~ parameters => - { - val packageName = pkgName.mkString(".") - val dynamic = !instantiate.isEmpty - val field = Option(fieldMethod.dropRight(1).mkString(".")).filterNot(_.isEmpty) - val methodName = fieldMethod.takeRight(1).mkString - HandlerCall(packageName, className, dynamic, methodName, field, parameters) - } - } + def className: Parser[String] = namedError("[a-zA-Z]+".r, "Class name expected") - def router: Parser[String] = rep1sep(identifier, ".") ^^ { - case parts => parts.mkString(".") - } + def call: Parser[HandlerCall] = opt("@") ~ packageName ~ "." ~ className ~ "." ~ rep1sep(identifier, ".") ~ opt(parameters) ^^ { + case instantiate ~ pkgName ~ _ ~ className ~ _ ~ fieldMethod ~ parameters => + { + val packageName = pkgName.mkString(".") + val dynamic = !instantiate.isEmpty + val field = Option(fieldMethod.dropRight(1).mkString(".")).filterNot(_.isEmpty) + val methodName = fieldMethod.takeRight(1).mkString + HandlerCall(packageName, className, dynamic, methodName, field, parameters) + } + } - def route = httpVerb ~! separator ~ path ~ separator ~ positioned(call) ~ ignoreWhiteSpace ^^ { - case v ~ _ ~ p ~ _ ~ c ~ _ => Route(v, p, c) - } + def router: Parser[String] = rep1sep(identifier, ".") ^^ { + case parts => parts.mkString(".") + } - def include = "->" ~! separator ~ path ~ separator ~ router ~ ignoreWhiteSpace ^^ { - case _ ~ _ ~ p ~ _ ~ r ~ _ => Include(p.toString, r) - } + def route = httpVerb ~! separator ~ path ~ separator ~ positioned(call) ~ ignoreWhiteSpace ^^ { + case v ~ _ ~ p ~ _ ~ c ~ _ => Route(v, p, c) + } - def sentence: Parser[Product with Serializable] = namedError((comment | positioned(include) | positioned(route)), "HTTP Verb (GET, POST, ...), include (->) or comment (#) expected") <~ (newLine | EOF) - - def parser: Parser[List[Rule]] = phrase((blankLine | sentence *) <~ end) ^^ { - case routes => - routes.reverse.foldLeft(List[(Option[Rule],List[Comment])]()) { - case (s,r@Route(_,_,_,_)) => (Some(r),List()) :: s - case (s,i@Include(_,_)) => (Some(i),List()) :: s - case ( s, c@()) => (None, List()) :: s - case ( (r,comments) :: others, c@Comment(_)) => (r, c:: comments) :: others - case (s,_) => s - }.collect { - case (Some(r@Route(_,_,_,_)), comments) => r.copy(comments = comments).setPos(r.pos) - case (Some(i@Include(_,_)),_) => i - } - } + def include = "->" ~! separator ~ path ~ separator ~ router ~ ignoreWhiteSpace ^^ { + case _ ~ _ ~ p ~ _ ~ r ~ _ => Include(p.toString, r) + } - def parse(text: String): ParseResult[List[Rule]] = { - parser(new CharSequenceReader(text)) - } + def sentence: Parser[Product with Serializable] = namedError((comment | positioned(include) | positioned(route)), "HTTP Verb (GET, POST, ...), include (->) or comment (#) expected") <~ (newLine | EOF) + + def parser: Parser[List[Rule]] = phrase((blankLine | sentence *) <~ end) ^^ { + case routes => + routes.reverse.foldLeft(List[(Option[Rule],List[Comment])]()) { + case (s,r@Route(_,_,_,_)) => (Some(r),List()) :: s + case (s,i@Include(_,_)) => (Some(i),List()) :: s + case ( s, c@()) => (None, List()) :: s + case ( (r,comments) :: others, c@Comment(_)) => (r, c:: comments) :: others + case (s,_) => s + }.collect { + case (Some(r@Route(_,_,_,_)), comments) => r.copy(comments = comments).setPos(r.pos) + case (Some(i@Include(_,_)),_) => i + } } + def parse(text: String): ParseResult[List[Rule]] = { + parser(new CharSequenceReader(text)) + } + } - import scalax.file._ - import java.io.File - case class RoutesCompilationError(source: File, message: String, line: Option[Int], column: Option[Int]) extends RuntimeException(message) + import scalax.file._ + import java.io.File - case class GeneratedSource(file: File) { + case class RoutesCompilationError(source: File, message: String, line: Option[Int], column: Option[Int]) extends RuntimeException(message) - val lines = if (file.exists) Path(file).slurpString.split('\n').toList else Nil - val source = lines.headOption.filter(_.startsWith("// @SOURCE:")).map(m => Path.fromString(m.trim.drop(11))) + case class GeneratedSource(file: File) { - def isGenerated: Boolean = source.isDefined + val lines = if (file.exists) Path(file).string.split('\n').toList else Nil + val source = lines.headOption.filter(_.startsWith("// @SOURCE:")).map(m => Path.fromString(m.trim.drop(11))) - def sync(): Boolean = { - if (!source.get.exists) file.delete() else false - } + def isGenerated: Boolean = source.isDefined - def needsRecompilation: Boolean = { - val hash = lines.find(_.startsWith("// @HASH:")).map(m => m.trim.drop(9)).getOrElse("") - source.filter(_.exists).map { p => - Hash(p.byteArray) != hash - }.getOrElse(true) - } + def sync(): Boolean = { + if (!source.get.exists) file.delete() else false + } - def mapLine(generatedLine: Int): Option[Int] = { - lines.take(generatedLine).reverse.collect { - case l if l.startsWith("// @LINE:") => Integer.parseInt(l.trim.drop(9)) - }.headOption - } + def needsRecompilation: Boolean = { + val hash = lines.find(_.startsWith("// @HASH:")).map(m => m.trim.drop(9)).getOrElse("") + source.filter(_.exists).map { p => + Hash(p.byteArray) != hash + }.getOrElse(true) + } + def mapLine(generatedLine: Int): Option[Int] = { + lines.take(generatedLine).reverse.collect { + case l if l.startsWith("// @LINE:") => Integer.parseInt(l.trim.drop(9)) + }.headOption } - object MaybeGeneratedSource { + } - def unapply(source: File): Option[GeneratedSource] = { - val generated = GeneratedSource(source) - if (generated.isGenerated) { - Some(generated) - } else { - None - } - } + object MaybeGeneratedSource { + def unapply(source: File): Option[GeneratedSource] = { + val generated = GeneratedSource(source) + if (generated.isGenerated) { + Some(generated) + } else { + None + } } + } - def compile(file: File, generatedDir: File, additionalImports: Seq[String]) { - val generated = GeneratedSource(new File(generatedDir, "routes_routing.scala")) + def compile(file: File, generatedDir: File, additionalImports: Seq[String]) { - if (generated.needsRecompilation) { + val generated = GeneratedSource(new File(generatedDir, "routes_routing.scala")) - val parser = new RouteFileParser - val routeFile = Path(file).toAbsolute - val routesContent = routeFile.slurpString + if (generated.needsRecompilation) { - (parser.parse(routesContent) match { - case parser.Success(parsed, _) => generate(routeFile, parsed, additionalImports) - case parser.NoSuccess(message, in) => { - throw RoutesCompilationError(file, message, Some(in.pos.line), Some(in.pos.column)) - } - }).foreach { item => - Path(new File(generatedDir, item._1)).write(item._2) - } + val parser = new RouteFileParser + val routeFile = Path(file).toAbsolute + val routesContent = routeFile.string + (parser.parse(routesContent) match { + case parser.Success(parsed, _) => generate(routeFile, parsed, additionalImports) + case parser.NoSuccess(message, in) => { + throw RoutesCompilationError(file, message, Some(in.pos.line), Some(in.pos.column)) + } + }).foreach { item => + Path(new File(generatedDir, item._1)).write(item._2) } } - /** - * Precheck routes coherence or throw exceptions early - */ - private def check(file: java.io.File, routes: List[Route]) { - routes.foreach { route => - - if (route.call.packageName.isEmpty) { - throw RoutesCompilationError( - file, - "Missing package name", - Some(route.call.pos.line), - Some(route.call.pos.column)) - } + } - if (route.call.controller.isEmpty) { - throw RoutesCompilationError( - file, - "Missing Controller", - Some(route.call.pos.line), - Some(route.call.pos.column)) - } + /** + * Precheck routes coherence or throw exceptions early + */ + private def check(file: java.io.File, routes: List[Route]) { + routes.foreach { route => + + if (route.call.packageName.isEmpty) { + throw RoutesCompilationError( + file, + "Missing package name", + Some(route.call.pos.line), + Some(route.call.pos.column)) + } + + if (route.call.controller.isEmpty) { + throw RoutesCompilationError( + file, + "Missing Controller", + Some(route.call.pos.line), + Some(route.call.pos.column)) + } - route.path.parts.collect { - case part @ DynamicPart(name, regex) => { - route.call.parameters.getOrElse(Nil).find(_.name == name).map { p => - if (p.fixed.isDefined || p.default.isDefined) { + route.path.parts.collect { + case part @ DynamicPart(name, regex) => { + route.call.parameters.getOrElse(Nil).find(_.name == name).map { p => + if (p.fixed.isDefined || p.default.isDefined) { + throw RoutesCompilationError( + file, + "It is not allowed to specify a fixed or default value for parameter: '" + name + "' extracted from the path", + Some(p.pos.line), + Some(p.pos.column)) + } + try { + java.util.regex.Pattern.compile(regex) + } catch { + case e => { throw RoutesCompilationError( file, - "It is not allowed to specify a fixed or default value for parameter: '" + name + "' extracted from the path", - Some(p.pos.line), - Some(p.pos.column)) - } - try { - java.util.regex.Pattern.compile(regex) - } catch { - case e => { - throw RoutesCompilationError( - file, - e.getMessage, - Some(part.pos.line), - Some(part.pos.column)) - } + e.getMessage, + Some(part.pos.line), + Some(part.pos.column)) } - }.getOrElse { - throw RoutesCompilationError( - file, - "Missing parameter in call definition: " + name, - Some(part.pos.line), - Some(part.pos.column)) } + }.getOrElse { + throw RoutesCompilationError( + file, + "Missing parameter in call definition: " + name, + Some(part.pos.line), + Some(part.pos.column)) } } - } - } - private def markLines(routes: Rule*): String = { - routes.map("// @LINE:" + _.pos.line).reverse.mkString("\n") } + } - /** - * Generate the actual Scala code for this router - */ - private def generate(file: Path, rules: List[Rule], additionalImports: Seq[String]): Seq[(String, String)] = { - - check(new File(file.path), rules.collect { case r: Route => r }); - - val (path, hash, date) = (file.path.replace(File.separator, "/"), Hash(file.byteArray), new java.util.Date().toString) + private def markLines(routes: Rule*): String = { + routes.map("// @LINE:" + _.pos.line).reverse.mkString("\n") + } - Seq(("routes_reverseRouting.scala", + /** + * Generate the actual Scala code for this router + */ + private def generate(file: Path, rules: List[Rule], additionalImports: Seq[String]): Seq[(String, String)] = { + + check(new File(file.path), rules.collect { case r: Route => r }); + + val (path, hash, date) = (file.path.replace(File.separator, "/"), Hash(file.byteArray), new java.util.Date().toString) + + Seq(("routes_reverseRouting.scala", + """ |// @SOURCE:%s + |// @HASH:%s + |// @DATE:%s + | + |%s + |import play.core._ + |import play.core.Router._ + |import play.core.j._ + | + |import play.api.mvc._ + |%s + | + |import Router.queryString + | + |%s + | + |%s + | + |%s + """.stripMargin.format( + path, + hash, + date, + Option(file.name).filter(_.endsWith(".routes")).map(_.dropRight(".routes".size)).map("import " + _ + ".Routes").getOrElse(""), + additionalImports.map("import " + _).mkString("\n"), + reverseRouting(rules.collect { case r: Route => r }), + javaScriptReverseRouting(rules.collect { case r: Route => r }), + refReverseRouting(rules.collect { case r: Route => r }) + ) + ), + ("routes_routing.scala", """ |// @SOURCE:%s |// @HASH:%s |// @DATE:%s - | |%s + | |import play.core._ |import play.core.Router._ - |import play.router._ |import play.core.j._ | |import play.api.mvc._ @@ -376,682 +406,650 @@ import scala.util.matching._ | |import Router.queryString | - |%s + |object Routes extends Router.Routes { | - |%s + |private var _prefix = "/" + | + |def setPrefix(prefix: String) { + | _prefix = prefix + | List[(String,Routes)](%s).foreach { + | case (p, router) => router.setPrefix(prefix + (if(prefix.endsWith("/")) "" else "/") + p) + | } + |} | + |def prefix = _prefix + | + |lazy val defaultPrefix = { if(Routes.prefix.endsWith("/")) "" else "/" } + | + |%s + | + |def routes:PartialFunction[RequestHeader,Handler] = { |%s + |} + | + |} """.stripMargin.format( path, hash, date, - Option(file.name).filter(_.endsWith(".routes")).map(_.dropRight(".routes".size)).map("import " + _ + ".Routes").getOrElse(""), + Option(file.name).filter(_.endsWith(".routes")).map(_.dropRight(".routes".size)).map("package " + _).getOrElse(""), additionalImports.map("import " + _).mkString("\n"), - reverseRouting(rules.collect { case r: Route => r }), - javaScriptReverseRouting(rules.collect { case r: Route => r }), - refReverseRouting(rules.collect { case r: Route => r }) + rules.collect { case Include(p, r) => "(\"" + p + "\"," + r + ")" }.mkString(","), + routeDefinitions(rules), + routing(rules) ) - ), - ("routes_routing.scala", - """ |// @SOURCE:%s - |// @HASH:%s - |// @DATE:%s - |%s - | - |import play.core._ - |import play.core.Router._ - |import play.router._ - |import play.core.j._ - | - |import play.api.mvc._ - |%s - | - |import Router.queryString - | - |object Routes extends Router.Routes { - | - |private var _prefix = "/" - | - |def setPrefix(prefix: String) { - | _prefix = prefix - | List[(String,Routes)](%s).foreach { - | case (p, router) => router.setPrefix(prefix + (if(prefix.endsWith("/")) "" else "/") + p) - | } - |} - | - |def prefix = _prefix - | - |lazy val defaultPrefix = { if(Routes.prefix.endsWith("/")) "" else "/" } - | - |%s - | - |def routes:PartialFunction[RequestHeader,Handler] = { - |%s - |} - | - |} - """.stripMargin.format( - path, - hash, - date, - Option(file.name).filter(_.endsWith(".routes")).map(_.dropRight(".routes".size)).map("package " + _).getOrElse(""), - additionalImports.map("import " + _).mkString("\n"), - rules.collect { case Include(p, r) => "(\"" + p + "\"," + r + ")" }.mkString(","), - routeDefinitions(rules), - routing(rules) - ) - )) ++ { - - // Generate Java wrappers - - rules.collect { case r: Route => r }.groupBy(_.call.packageName).map { - case (packageName, routes) => { - - (packageName.replace(".", "/") + "/routes.java") -> { - - """ |// @SOURCE:%s - |// @HASH:%s - |// @DATE:%s - | - |package %s; - | - |public class routes { - |%s - |public static class javascript { - |%s - |} - |public static class ref { - |%s - |} - |} - """.stripMargin.format( - path, hash, date, - packageName, - - routes.groupBy(_.call.controller).map { - case (controller, routes) => { - val fields = routes.groupBy(_.call.field) - if (fields.size == 1 && fields.keys.head == None) { - "public static final " + packageName + ".Reverse" + controller + " " + controller + " = new " + packageName + ".Reverse" + controller + "();" - } else { - "public static class Reverse" + controller + " extends " + packageName + ".Reverse" + controller + " {\n" + { - fields.keys.collect { case Some(f) => f }.map { field => - "public Reverse" + controller + "_" + field + " " + field + " = this.new Reverse" + controller + "_" + field + "();" - }.mkString("\n") - } + "\n}\n" + - "public static final Reverse" + controller + " " + controller + " = new Reverse" + controller + "();" - } - } - }.mkString("\n"), - - routes.groupBy(_.call.controller).map { - case (controller, _) => { - "public static final " + packageName + ".javascript.Reverse" + controller + " " + controller + " = new " + packageName + ".javascript.Reverse" + controller + "();" + )) ++ { + + // Generate Java wrappers + + rules.collect { case r: Route => r }.groupBy(_.call.packageName).map { + case (packageName, routes) => { + + (packageName.replace(".", "/") + "/routes.java") -> { + + """ |// @SOURCE:%s + |// @HASH:%s + |// @DATE:%s + | + |package %s; + | + |public class routes { + |%s + |public static class javascript { + |%s + |} + |public static class ref { + |%s + |} + |} + """.stripMargin.format( + path, hash, date, + packageName, + + routes.groupBy(_.call.controller).map { + case (controller, routes) => { + val fields = routes.groupBy(_.call.field) + if (fields.size == 1 && fields.keys.head == None) { + "public static final " + packageName + ".Reverse" + controller + " " + controller + " = new " + packageName + ".Reverse" + controller + "();" + } else { + "public static class Reverse" + controller + " extends " + packageName + ".Reverse" + controller + " {\n" + { + fields.keys.collect { case Some(f) => f }.map { field => + "public Reverse" + controller + "_" + field + " " + field + " = this.new Reverse" + controller + "_" + field + "();" + }.mkString("\n") + } + "\n}\n" + + "public static final Reverse" + controller + " " + controller + " = new Reverse" + controller + "();" } - }.mkString("\n"), + } + }.mkString("\n"), - routes.groupBy(_.call.controller).map { - case (controller, _) => { - "public static final " + packageName + ".ref.Reverse" + controller + " " + controller + " = new " + packageName + ".ref.Reverse" + controller + "();" - } - }.mkString("\n") + routes.groupBy(_.call.controller).map { + case (controller, _) => { + "public static final " + packageName + ".javascript.Reverse" + controller + " " + controller + " = new " + packageName + ".javascript.Reverse" + controller + "();" + } + }.mkString("\n"), - ) + routes.groupBy(_.call.controller).map { + case (controller, _) => { + "public static final " + packageName + ".ref.Reverse" + controller + " " + controller + " = new " + packageName + ".ref.Reverse" + controller + "();" + } + }.mkString("\n") - } + ) } - } + } } - } + } - /** - * Generate the reverse routing operations - */ - def javaScriptReverseRouting(routes: List[Route]): String = { - - routes.groupBy(_.call.packageName).map { - case (packageName, routes) => { - - """ - |%s - |package %s.javascript { - |%s - |} - """.stripMargin.format( - markLines(routes: _*), - packageName, - - routes.groupBy(_.call.controller).map { - case (controller, routes) => - """ - |%s - |class Reverse%s { - | - |%s - | - |} - """.stripMargin.format( - markLines(routes: _*), - - // alias - controller.replace(".", "_"), - - // group by field - routes.groupBy(_.call.field).map { - case (field, routes) => { - - """ - |%s - |%s - |%s - """.stripMargin.format( - - field.map(f => "class Reverse" + controller.replace(".", "_") + "_" + f + " {").getOrElse(""), - - // reverse method - routes.groupBy(r => r.call.method -> r.call.parameters.getOrElse(Nil).map(p => p.typeName)).map { - case ((m, _), routes) => - - assert(routes.size > 0, "Empty routes set???") - - val parameters = routes(0).call.parameters.getOrElse(Nil) - - val reverseParameters = parameters.zipWithIndex.filterNot { - case (p, i) => { - val fixeds = routes.map(_.call.parameters.get(i).fixed).distinct - fixeds.size == 1 && fixeds(0) != None - } - } + } - def genCall(route: Route, localNames: Map[String, String] = Map()) = " return _wA({method:\"%s\", url:%s%s})".format( - route.verb.value, - "\"\"\"\" + Routes.prefix + " + { if (route.path.parts.isEmpty) "" else "{ Routes.defaultPrefix } + " } + "\"\"\"\"" + route.path.parts.map { - case StaticPart(part) => " + \"" + part + "\"" - case DynamicPart(name, _) => { - route.call.parameters.getOrElse(Nil).find(_.name == name).map { param => - " + (\"\"\" + implicitly[PathBindable[" + param.typeName + "]].javascriptUnbind + \"\"\")" + """("""" + param.name + """", """ + localNames.get(param.name).getOrElse(param.name) + """)""" - }.getOrElse { - throw new Error("missing key " + name) - } - } - }.mkString, - - { - val queryParams = route.call.parameters.getOrElse(Nil).filterNot { p => - p.fixed.isDefined || - route.path.parts.collect { - case DynamicPart(name, _) => name - }.contains(p.name) - } + /** + * Generate the reverse routing operations + */ + def javaScriptReverseRouting(routes: List[Route]): String = { - if (queryParams.size == 0) { - "" - } else { - """ + _qS([%s])""".format( - queryParams.map { p => - ("(\"\"\" + implicitly[QueryStringBindable[" + p.typeName + "]].javascriptUnbind + \"\"\")" + """("""" + p.name + """", """ + localNames.get(p.name).getOrElse(p.name) + """)""") -> p - }.map { - case (u, Parameter(name, typeName, None, Some(default))) => """(""" + localNames.get(name).getOrElse(name) + " == null ? \"\"\" + implicitly[JavascriptLitteral[" + typeName + "]].to(" + default + ") + \"\"\" : " + u + ")" - case (u, Parameter(name, typeName, None, None)) => u - }.mkString(", ")) + routes.groupBy(_.call.packageName).map { + case (packageName, routes) => { - } + """ + |%s + |package %s.javascript { + |%s + |} + """.stripMargin.format( + markLines(routes: _*), + packageName, + + routes.groupBy(_.call.controller).map { + case (controller, routes) => + """ + |%s + |class Reverse%s { + | + |%s + | + |} + """.stripMargin.format( + markLines(routes: _*), + + // alias + controller.replace(".", "_"), + + // group by field + routes.groupBy(_.call.field).map { + case (field, routes) => { + + """ + |%s + |%s + |%s + """.stripMargin.format( - }) - - routes match { - - case Seq(route) => { - """ - |%s - |def %s : JavascriptReverseRoute = JavascriptReverseRoute( - | "%s", - | %s - | function(%s) { - |%s - | } - | %s - |) - """.stripMargin.format( - markLines(route), - route.call.method, - packageName + "." + controller + "." + route.call.field.map(_ + ".").getOrElse("") + route.call.method, - "\"\"\"", - reverseParameters.map(_._1.name).mkString(","), - genCall(route), - "\"\"\"") - } + field.map(f => "class Reverse" + controller.replace(".", "_") + "_" + f + " {").getOrElse(""), - case Seq(route, routes @ _*) => { - """ - |%s - |def %s : JavascriptReverseRoute = JavascriptReverseRoute( - | "%s", - | %s - | function(%s) { - |%s - | } - | %s - |) - """.stripMargin.format( - markLines((route +: routes): _*), - route.call.method, - packageName + "." + controller + "." + route.call.method, - "\"\"\"", - reverseParameters.map(_._1.name).mkString(", "), - - // route selection - (route +: routes).map { route => - - val localNames = reverseParameters.map { - case (lp, i) => route.call.parameters.get(i).name -> lp.name - }.toMap - - " if (%s) {\n%s\n }".format( - - // Fixed constraints - Option(route.call.parameters.getOrElse(Nil).filter { p => - localNames.contains(p.name) && p.fixed.isDefined - }.map { p => - p.name + " == \"\"\" + implicitly[JavascriptLitteral[" + p.typeName + "]].to(" + p.fixed.get + ") + \"\"\"" - }).filterNot(_.isEmpty).map(_.mkString(" && ")).getOrElse("true"), - - genCall(route, localNames)) - - }.mkString("\n"), - - "\"\"\"") - } + // reverse method + routes.groupBy(r => r.call.method -> r.call.parameters.getOrElse(Nil).map(p => p.typeName)).map { + case ((m, _), routes) => + + assert(routes.size > 0, "Empty routes set???") + val parameters = routes(0).call.parameters.getOrElse(Nil) + + val reverseParameters = parameters.zipWithIndex.filterNot { + case (p, i) => { + val fixeds = routes.map(_.call.parameters.get(i).fixed).distinct + fixeds.size == 1 && fixeds(0) != None } + } + + def genCall(route: Route, localNames: Map[String, String] = Map()) = " return _wA({method:\"%s\", url:%s%s})".format( + route.verb.value, + "\"\"\"\" + Routes.prefix + " + { if (route.path.parts.isEmpty) "" else "{ Routes.defaultPrefix } + " } + "\"\"\"\"" + route.path.parts.map { + case StaticPart(part) => " + \"" + part + "\"" + case DynamicPart(name, _) => { + route.call.parameters.getOrElse(Nil).find(_.name == name).map { param => + " + (\"\"\" + implicitly[PathBindable[" + param.typeName + "]].javascriptUnbind + \"\"\")" + """("""" + param.name + """", """ + localNames.get(param.name).getOrElse(param.name) + """)""" + }.getOrElse { + throw new Error("missing key " + name) + } + } + }.mkString, + + { + val queryParams = route.call.parameters.getOrElse(Nil).filterNot { p => + p.fixed.isDefined || + route.path.parts.collect { + case DynamicPart(name, _) => name + }.contains(p.name) + } - }.mkString("\n"), + if (queryParams.size == 0) { + "" + } else { + """ + _qS([%s])""".format( + queryParams.map { p => + ("(\"\"\" + implicitly[QueryStringBindable[" + p.typeName + "]].javascriptUnbind + \"\"\")" + """("""" + p.name + """", """ + localNames.get(p.name).getOrElse(p.name) + """)""") -> p + }.map { + case (u, Parameter(name, typeName, None, Some(default))) => """(""" + localNames.get(name).getOrElse(name) + " == null ? \"\"\" + implicitly[JavascriptLitteral[" + typeName + "]].to(" + default + ") + \"\"\" : " + u + ")" + case (u, Parameter(name, typeName, None, None)) => u + }.mkString(", ")) - field.map(_ => "}").getOrElse("")) + } - } - }.mkString("\n")) - }.mkString("\n")) + }) - } - }.mkString("\n") + routes match { - } + case Seq(route) => { + """ + |%s + |def %s : JavascriptReverseRoute = JavascriptReverseRoute( + | "%s", + | %s + | function(%s) { + |%s + | } + | %s + |) + """.stripMargin.format( + markLines(route), + route.call.method, + packageName + "." + controller + "." + route.call.field.map(_ + ".").getOrElse("") + route.call.method, + "\"\"\"", + reverseParameters.map(_._1.name).mkString(","), + genCall(route), + "\"\"\"") + } - /** - * Generate the routing refs - */ - def refReverseRouting(routes: List[Route]): String = { + case Seq(route, routes @ _*) => { + """ + |%s + |def %s : JavascriptReverseRoute = JavascriptReverseRoute( + | "%s", + | %s + | function(%s) { + |%s + | } + | %s + |) + """.stripMargin.format( + markLines((route +: routes): _*), + route.call.method, + packageName + "." + controller + "." + route.call.method, + "\"\"\"", + reverseParameters.map(_._1.name).mkString(", "), - routes.groupBy(_.call.packageName).map { - case (packageName, routes) => { + // route selection + (route +: routes).map { route => - """ - |%s - |package %s.ref { - |%s - |} - """.stripMargin.format( - markLines(routes: _*), - packageName, + val localNames = reverseParameters.map { + case (lp, i) => route.call.parameters.get(i).name -> lp.name + }.toMap - routes.groupBy(_.call.controller).map { - case (controller, routes) => - """ - |%s - |class Reverse%s { - | - |%s - | - |} - """.stripMargin.format( - markLines(routes: _*), + " if (%s) {\n%s\n }".format( - // alias - controller.replace(".", "_"), + // Fixed constraints + Option(route.call.parameters.getOrElse(Nil).filter { p => + localNames.contains(p.name) && p.fixed.isDefined + }.map { p => + p.name + " == \"\"\" + implicitly[JavascriptLitteral[" + p.typeName + "]].to(" + p.fixed.get + ") + \"\"\"" + }).filterNot(_.isEmpty).map(_.mkString(" && ")).getOrElse("true"), - // group by field - routes.groupBy(_.call.field).map { - case (field, routes) => { + genCall(route, localNames)) - """ - |%s - |%s - |%s - """.stripMargin.format( - field.map(f => "class Reverse" + controller.replace(".", "_") + "_" + f + " {").getOrElse(""), + }.mkString("\n"), - // reverse method - routes.groupBy(r => (r.call.method, r.call.parameters.getOrElse(Nil).map(p => p.typeName))).map { - case ((m, _), routes) => + "\"\"\"") + } - assert(routes.size > 0, "Empty routes set???") + } - val route = routes(0) + }.mkString("\n"), - val parameters = route.call.parameters.getOrElse(Nil) + field.map(_ => "}").getOrElse("")) - val reverseSignature = parameters.map(p => p.name + ":" + p.typeName).mkString(", ") + } + }.mkString("\n")) + }.mkString("\n")) - val controllerCall = if (route.call.instantiate) { - "play.api.Play.maybeApplication.map(_.global).getOrElse(play.api.DefaultGlobal).getControllerInstance(classOf[" + packageName + "." + controller + "])." + route.call.field.map(_ + ".").getOrElse("") + route.call.method + "(" + { parameters.map(_.name).mkString(", ") } + ")" - } else { - packageName + "." + controller + "." + route.call.field.map(_ + ".").getOrElse("") + route.call.method + "(" + { parameters.map(_.name).mkString(", ") } + ")" - } + } + }.mkString("\n") - """ - |%s - |def %s(%s): play.api.mvc.HandlerRef[_] = new play.api.mvc.HandlerRef( - | %s, HandlerDef(this, "%s", "%s", %s, "%s", "%s", Routes.prefix + "%s") - |) - """.stripMargin.format( - markLines(route), - route.call.method, - reverseSignature, - controllerCall, - packageName + "." + controller + route.call.field.map(_ + ".").getOrElse(""), - route.call.method, - "Seq(" + { parameters.map("classOf[" + _.typeName + "]").mkString(", ") } + ")", - route.verb, - "\"\"\""+route.comments.map(_.comment).mkString("\n")+"\"\"\"", - route.path - ) - - }.mkString("\n"), - - field.map(_ => "}").getOrElse("")) + } - } + /** + * Generate the routing refs + */ + def refReverseRouting(routes: List[Route]): String = { + + routes.groupBy(_.call.packageName).map { + case (packageName, routes) => { + + """ + |%s + |package %s.ref { + |%s + |} + """.stripMargin.format( + markLines(routes: _*), + packageName, + + routes.groupBy(_.call.controller).map { + case (controller, routes) => + """ + |%s + |class Reverse%s { + | + |%s + | + |} + """.stripMargin.format( + markLines(routes: _*), + + // alias + controller.replace(".", "_"), + + // group by field + routes.groupBy(_.call.field).map { + case (field, routes) => { + + """ + |%s + |%s + |%s + """.stripMargin.format( + field.map(f => "class Reverse" + controller.replace(".", "_") + "_" + f + " {").getOrElse(""), - }.mkString("\n")) - }.mkString("\n")) + // reverse method + routes.groupBy(r => (r.call.method, r.call.parameters.getOrElse(Nil).map(p => p.typeName))).map { + case ((m, _), routes) => - } - }.mkString("\n") - } + assert(routes.size > 0, "Empty routes set???") - /** - * Generate the reverse routing operations - */ - def reverseRouting(routes: List[Route]): String = { + val route = routes(0) - routes.groupBy(_.call.packageName).map { - case (packageName, routes) => { + val parameters = route.call.parameters.getOrElse(Nil) - """ - |%s - |package %s { - |%s - |} - """.stripMargin.format( - markLines(routes: _*), - packageName, + val reverseSignature = parameters.map(p => p.name + ":" + p.typeName).mkString(", ") - routes.groupBy(_.call.controller).map { - case (controller, routes) => - """ - |%s - |class Reverse%s { - | + val controllerCall = if (route.call.instantiate) { + "play.api.Play.maybeApplication.map(_.global).getOrElse(play.api.DefaultGlobal).getControllerInstance(classOf[" + packageName + "." + controller + "])." + route.call.field.map(_ + ".").getOrElse("") + route.call.method + "(" + { parameters.map(_.name).mkString(", ") } + ")" + } else { + packageName + "." + controller + "." + route.call.field.map(_ + ".").getOrElse("") + route.call.method + "(" + { parameters.map(_.name).mkString(", ") } + ")" + } + + """ |%s - | - |} + |def %s(%s): play.api.mvc.HandlerRef[_] = new play.api.mvc.HandlerRef( + | %s, HandlerDef(this, "%s", "%s", %s, "%s", "%s", Routes.prefix + "%s") + |) """.stripMargin.format( - markLines(routes: _*), + markLines(route), + route.call.method, + reverseSignature, + controllerCall, + packageName + "." + controller + route.call.field.map(_ + ".").getOrElse(""), + route.call.method, + "Seq(" + { parameters.map("classOf[" + _.typeName + "]").mkString(", ") } + ")", + route.verb, + "\"\"\""+route.comments.map(_.comment).mkString("\n")+"\"\"\"", + route.path + ) - // alias - controller.replace(".", "_"), + }.mkString("\n"), - // group by field - routes.groupBy(_.call.field).map { - case (field, routes) => { + field.map(_ => "}").getOrElse("")) - """ - |%s - |%s - |%s - """.stripMargin.format( - field.map(f => "class Reverse" + controller.replace(".", "_") + "_" + f + " {").getOrElse(""), + } - // reverse method - routes.groupBy(r => (r.call.method, r.call.parameters.getOrElse(Nil).map(p => p.typeName))).map { - case ((m, _), routes) => + }.mkString("\n")) + }.mkString("\n")) - assert(routes.size > 0, "Empty routes set???") - - val parameters = routes(0).call.parameters.getOrElse(Nil) + } + }.mkString("\n") + } - val reverseParameters = parameters.zipWithIndex.filterNot { - case (p, i) => { - val fixeds = routes.map(_.call.parameters.get(i).fixed).distinct - fixeds.size == 1 && fixeds(0) != None - } - } + /** + * Generate the reverse routing operations + */ + def reverseRouting(routes: List[Route]): String = { + + routes.groupBy(_.call.packageName).map { + case (packageName, routes) => { + + """ + |%s + |package %s { + |%s + |} + """.stripMargin.format( + markLines(routes: _*), + packageName, + + routes.groupBy(_.call.controller).map { + case (controller, routes) => + """ + |%s + |class Reverse%s { + | + |%s + | + |} + """.stripMargin.format( + markLines(routes: _*), + + // alias + controller.replace(".", "_"), + + // group by field + routes.groupBy(_.call.field).map { + case (field, routes) => { + + """ + |%s + |%s + |%s + """.stripMargin.format( + field.map(f => "class Reverse" + controller.replace(".", "_") + "_" + f + " {").getOrElse(""), - val reverseSignature = reverseParameters.map(p => p._1.name + ":" + p._1.typeName + { - Option(routes.map(_.call.parameters.get(p._2).default).distinct).filter(_.size == 1).flatMap(_.headOption).map { - case None => "" - case Some(default) => " = " + default - }.getOrElse("") - }).mkString(", ") - - def genCall(route: Route, localNames: Map[String, String] = Map()) = """Call("%s", %s%s)""".format( - route.verb.value, - "Routes.prefix" + { if (route.path.parts.isEmpty) "" else """ + { Routes.defaultPrefix } + """ } + route.path.parts.map { - case StaticPart(part) => "\"" + part + "\"" - case DynamicPart(name, _) => { - route.call.parameters.getOrElse(Nil).find(_.name == name).map { param => - """implicitly[PathBindable[""" + param.typeName + """]].unbind("""" + param.name + """", """ + localNames.get(param.name).getOrElse(param.name) + """)""" - }.getOrElse { - throw new Error("missing key " + name) - } + // reverse method + routes.groupBy(r => (r.call.method, r.call.parameters.getOrElse(Nil).map(p => p.typeName))).map { + case ((m, _), routes) => - } - }.mkString(" + "), - - { - val queryParams = route.call.parameters.getOrElse(Nil).filterNot { p => - p.fixed.isDefined || - route.path.parts.collect { - case DynamicPart(name, _) => name - }.contains(p.name) - } + assert(routes.size > 0, "Empty routes set???") - if (queryParams.size == 0) { - "" - } else { - """ + queryString(List(%s))""".format( - queryParams.map { p => - ("""implicitly[QueryStringBindable[""" + p.typeName + """]].unbind("""" + p.name + """", """ + localNames.get(p.name).getOrElse(p.name) + """)""") -> p - }.map { - case (u, Parameter(name, typeName, None, Some(default))) => """if(""" + localNames.get(name).getOrElse(name) + """ == """ + default + """) None else Some(""" + u + """)""" - case (u, Parameter(name, typeName, None, None)) => "Some(" + u + ")" - }.mkString(", ")) + val parameters = routes(0).call.parameters.getOrElse(Nil) + val reverseParameters = parameters.zipWithIndex.filterNot { + case (p, i) => { + val fixeds = routes.map(_.call.parameters.get(i).fixed).distinct + fixeds.size == 1 && fixeds(0) != None + } + } + + val reverseSignature = reverseParameters.map(p => p._1.name + ":" + p._1.typeName + { + Option(routes.map(_.call.parameters.get(p._2).default).distinct).filter(_.size == 1).flatMap(_.headOption).map { + case None => "" + case Some(default) => " = " + default + }.getOrElse("") + }).mkString(", ") + + def genCall(route: Route, localNames: Map[String, String] = Map()) = """Call("%s", %s%s)""".format( + route.verb.value, + "Routes.prefix" + { if (route.path.parts.isEmpty) "" else """ + { Routes.defaultPrefix } + """ } + route.path.parts.map { + case StaticPart(part) => "\"" + part + "\"" + case DynamicPart(name, _) => { + route.call.parameters.getOrElse(Nil).find(_.name == name).map { param => + """implicitly[PathBindable[""" + param.typeName + """]].unbind("""" + param.name + """", """ + localNames.get(param.name).getOrElse(param.name) + """)""" + }.getOrElse { + throw new Error("missing key " + name) } - }) - - routes match { - - case Seq(route) => { - """ - |%s - |def %s(%s): Call = { - | %s - |} - """.stripMargin.format( - markLines(route), - route.call.method, - reverseSignature, - genCall(route)) } + }.mkString(" + "), + + { + val queryParams = route.call.parameters.getOrElse(Nil).filterNot { p => + p.fixed.isDefined || + route.path.parts.collect { + case DynamicPart(name, _) => name + }.contains(p.name) + } + + if (queryParams.size == 0) { + "" + } else { + """ + queryString(List(%s))""".format( + queryParams.map { p => + ("""implicitly[QueryStringBindable[""" + p.typeName + """]].unbind("""" + p.name + """", """ + localNames.get(p.name).getOrElse(p.name) + """)""") -> p + }.map { + case (u, Parameter(name, typeName, None, Some(default))) => """if(""" + localNames.get(name).getOrElse(name) + """ == """ + default + """) None else Some(""" + u + """)""" + case (u, Parameter(name, typeName, None, None)) => "Some(" + u + ")" + }.mkString(", ")) - case Seq(route, routes @ _*) => { - """ - |%s - |def %s(%s): Call = { - | (%s) match { - |%s - | } - |} - """.stripMargin.format( - markLines((route +: routes): _*), - route.call.method, - reverseSignature, - reverseParameters.map(_._1.name).mkString(", "), - - // route selection - (route +: routes).map { route => - - val localNames = reverseParameters.map { - case (lp, i) => route.call.parameters.get(i).name -> lp.name - }.toMap; - - """ |%s - |case (%s) %s => %s - """.stripMargin.format( - markLines(route), - reverseParameters.map(_._1.name).mkString(", "), - - // Fixed constraints - Option(route.call.parameters.getOrElse(Nil).filter { p => - localNames.contains(p.name) && p.fixed.isDefined - }.map { p => - p.name + " == " + p.fixed.get - }).filterNot(_.isEmpty).map("if " + _.mkString(" && ")).getOrElse("if true"), - - genCall(route, localNames)) - - }.mkString("\n")) } + }) + + routes match { + + case Seq(route) => { + """ + |%s + |def %s(%s): Call = { + | %s + |} + """.stripMargin.format( + markLines(route), + route.call.method, + reverseSignature, + genCall(route)) } - }.mkString("\n"), + case Seq(route, routes @ _*) => { + """ + |%s + |def %s(%s): Call = { + | (%s) match { + |%s + | } + |} + """.stripMargin.format( + markLines((route +: routes): _*), + route.call.method, + reverseSignature, + reverseParameters.map(_._1.name).mkString(", "), + + // route selection + (route +: routes).map { route => + + val localNames = reverseParameters.map { + case (lp, i) => route.call.parameters.get(i).name -> lp.name + }.toMap; + + """ |%s + |case (%s) %s => %s + """.stripMargin.format( + markLines(route), + reverseParameters.map(_._1.name).mkString(", "), + + // Fixed constraints + Option(route.call.parameters.getOrElse(Nil).filter { p => + localNames.contains(p.name) && p.fixed.isDefined + }.map { p => + p.name + " == " + p.fixed.get + }).filterNot(_.isEmpty).map("if " + _.mkString(" && ")).getOrElse("if true"), + + genCall(route, localNames)) + + }.mkString("\n")) + } - field.map(_ => "}").getOrElse("")) + } - } + }.mkString("\n"), - }.mkString("\n")) - }.mkString("\n")) + field.map(_ => "}").getOrElse("")) - } - }.mkString("\n") + } - } + }.mkString("\n")) + }.mkString("\n")) + + } + }.mkString("\n") + + } + + /** + * Generate the routes definitions + */ + def routeDefinitions(rules: List[Rule]): String = { + rules.zipWithIndex.map { + case (r @ Route(_, _, _, _), i) => + """ + |%s + |private[this] lazy val %s%s = Route("%s", %s) + """.stripMargin.format( + markLines(r), + r.call.packageName.replace(".", "_") + "_" + r.call.controller.replace(".", "_") + "_" + r.call.method, + i, + r.verb.value, + "PathPattern(List(StaticPart(Routes.prefix)" + { if (r.path.parts.isEmpty) "" else """,StaticPart(Routes.defaultPrefix),""" } + r.path.parts.map(_.toString).mkString(",") + "))") + case (r @ Include(_, _), i) => + """ + |%s + |lazy val %s%s = Include(%s) + """.stripMargin.format( + markLines(r), + r.router.replace(".", "_"), + i, + r.router + ) + }.mkString("\n") + + """| + |def documentation = List(%s).foldLeft(List.empty[(String,String,String)]) { (s,e) => e match { + | case r @ (_,_,_) => s :+ r.asInstanceOf[(String,String,String)] + | case l => s ++ l.asInstanceOf[List[(String,String,String)]] + |}} + """.stripMargin.format( + rules.map { + case Route(verb, path, call, _) if path.parts.isEmpty => "(\"\"\"" + verb + "\"\"\", prefix,\"\"\"" + call + "\"\"\")" + case Route(verb, path, call, _) => "(\"\"\"" + verb + "\"\"\", prefix + (if(prefix.endsWith(\"/\")) \"\" else \"/\") + \"\"\"" + path + "\"\"\",\"\"\"" + call + "\"\"\")" + case Include(prefix, router) => router + ".documentation" + }.mkString(",")) + } - /** - * Generate the routes definitions - */ - def routeDefinitions(rules: List[Rule]): String = { - rules.zipWithIndex.map { - case (r @ Route(_, _, _, _), i) => - """ + /** + * Generate the routing stuff + */ + def routing(routes: List[Rule]): String = { + Option(routes.zipWithIndex.map { + case (r @ Include(_, _), i) => + """ |%s - |private[this] lazy val %s%s = Route("%s", %s) - """.stripMargin.format( - markLines(r), - r.call.packageName.replace(".", "_") + "_" + r.call.controller.replace(".", "_") + "_" + r.call.method, - i, - r.verb.value, - "PathPattern(List(StaticPart(Routes.prefix)" + { if (r.path.parts.isEmpty) "" else """,StaticPart(Routes.defaultPrefix),""" } + r.path.parts.map(_.toString).mkString(",") + "))") - case (r @ Include(_, _), i) => - """ + |case %s%s(handler) => handler + """.stripMargin.format( + markLines(r), + r.router.replace(".", "_"), + i + ) + case (r @ Route(_, _, _, _), i) => + """ |%s - |lazy val %s%s = Include(%s) - """.stripMargin.format( - markLines(r), - r.router.replace(".", "_"), - i, - r.router - ) - }.mkString("\n") + - """| - |def documentation = List(%s).foldLeft(List.empty[(String,String,String)]) { (s,e) => e match { - | case r @ (_,_,_) => s :+ r.asInstanceOf[(String,String,String)] - | case l => s ++ l.asInstanceOf[List[(String,String,String)]] - |}} + |case %s%s(params) => { + | call%s { %s + | invokeHandler(%s%s, %s) + | } + |} """.stripMargin.format( - rules.map { - case Route(verb, path, call, _) if path.parts.isEmpty => "(\"\"\"" + verb + "\"\"\", prefix,\"\"\"" + call + "\"\"\")" - case Route(verb, path, call, _) => "(\"\"\"" + verb + "\"\"\", prefix + (if(prefix.endsWith(\"/\")) \"\" else \"/\") + \"\"\"" + path + "\"\"\",\"\"\"" + call + "\"\"\")" - case Include(prefix, router) => router + ".documentation" - }.mkString(",")) - } - - /** - * Generate the routing stuff - */ - def routing(routes: List[Rule]): String = { - Option(routes.zipWithIndex.map { - case (r @ Include(_, _), i) => - """ - |%s - |case %s%s(handler) => handler - """.stripMargin.format( - markLines(r), - r.router.replace(".", "_"), - i - ) - case (r @ Route(_, _, _, _), i) => - """ - |%s - |case %s%s(params) => { - | call%s { %s - | invokeHandler(%s%s, %s) - | } - |} - """.stripMargin.format( - markLines(r), - - // alias - r.call.packageName.replace(".", "_") + "_" + r.call.controller.replace(".", "_") + "_" + r.call.method, - i, - - // binding - r.call.parameters.filterNot(_.isEmpty).map { params => - params.map { p => - p.fixed.map { v => - """Param[""" + p.typeName + """]("""" + p.name + """", Right(""" + v + """))""" - }.getOrElse { - """params.""" + (if (r.path.has(p.name)) "fromPath" else "fromQuery") + """[""" + p.typeName + """]("""" + p.name + """", """ + p.default.map("Some(" + _ + ")").getOrElse("None") + """)""" - } - }.mkString(", ") - }.map("(" + _ + ")").getOrElse(""), - - // local names - r.call.parameters.filterNot(_.isEmpty).map { params => - params.map(_.name).mkString(", ") - }.map("(" + _ + ") =>").getOrElse(""), - - // call - if (r.call.instantiate) { - "play.api.Play.maybeApplication.map(_.global).getOrElse(play.api.DefaultGlobal).getControllerInstance(classOf[" + r.call.packageName + "." + r.call.controller + "])." + r.call.field.map(_ + ".").getOrElse("") + r.call.method - } else { - r.call.packageName + "." + r.call.controller + "." + r.call.field.map(_ + ".").getOrElse("") + r.call.method - }, - - // call parameters - r.call.parameters.map { params => - params.map(_.name).mkString(", ") - }.map("(" + _ + ")").getOrElse(""), - - // definition - """HandlerDef(this, """" + r.call.packageName + "." + r.call.controller + r.call.field.map("." + _).getOrElse("") + """", """" + r.call.method + """", """ + r.call.parameters.filterNot(_.isEmpty).map { params => - params.map("classOf[" + _.typeName + "]").mkString(", ") - }.map("Seq(" + _ + ")").getOrElse("Nil") + ""","""" + r.verb + """", """ +"\"\"\""+ r.comments.map(_.comment).mkString("\n")+"\"\"\""+ """ , Routes.prefix + """" + r.path + """")""") - }.mkString("\n")).filterNot(_.isEmpty).getOrElse { - - """Map.empty""" // Empty partial function + markLines(r), + + // alias + r.call.packageName.replace(".", "_") + "_" + r.call.controller.replace(".", "_") + "_" + r.call.method, + i, + + // binding + r.call.parameters.filterNot(_.isEmpty).map { params => + params.map { p => + p.fixed.map { v => + """Param[""" + p.typeName + """]("""" + p.name + """", Right(""" + v + """))""" + }.getOrElse { + """params.""" + (if (r.path.has(p.name)) "fromPath" else "fromQuery") + """[""" + p.typeName + """]("""" + p.name + """", """ + p.default.map("Some(" + _ + ")").getOrElse("None") + """)""" + } + }.mkString(", ") + }.map("(" + _ + ")").getOrElse(""), + + // local names + r.call.parameters.filterNot(_.isEmpty).map { params => + params.map(_.name).mkString(", ") + }.map("(" + _ + ") =>").getOrElse(""), + + // call + if (r.call.instantiate) { + "play.api.Play.maybeApplication.map(_.global).getOrElse(play.api.DefaultGlobal).getControllerInstance(classOf[" + r.call.packageName + "." + r.call.controller + "])." + r.call.field.map(_ + ".").getOrElse("") + r.call.method + } else { + r.call.packageName + "." + r.call.controller + "." + r.call.field.map(_ + ".").getOrElse("") + r.call.method + }, + + // call parameters + r.call.parameters.map { params => + params.map(_.name).mkString(", ") + }.map("(" + _ + ")").getOrElse(""), + + // definition + """HandlerDef(this, """" + r.call.packageName + "." + r.call.controller + r.call.field.map("." + _).getOrElse("") + """", """" + r.call.method + """", """ + r.call.parameters.filterNot(_.isEmpty).map { params => + params.map("classOf[" + _.typeName + "]").mkString(", ") + }.map("Seq(" + _ + ")").getOrElse("Nil") + ""","""" + r.verb + """", """ +"\"\"\""+ r.comments.map(_.comment).mkString("\n")+"\"\"\""+ """ , Routes.prefix + """" + r.path + """")""") + }.mkString("\n")).filterNot(_.isEmpty).getOrElse { + + """Map.empty""" // Empty partial function - } } - - } + +} + diff --git a/framework/src/play/src/main/java/play/core/SBTLink.java b/framework/src/sbt-link/src/main/java/play/core/SBTLink.java similarity index 81% rename from framework/src/play/src/main/java/play/core/SBTLink.java rename to framework/src/sbt-link/src/main/java/play/core/SBTLink.java index 0e2c0e21095..b3950333f69 100644 --- a/framework/src/play/src/main/java/play/core/SBTLink.java +++ b/framework/src/sbt-link/src/main/java/play/core/SBTLink.java @@ -16,8 +16,11 @@ public interface SBTLink { // - null -> if nothing changed public Object reload(); - // Can return null - public File findSource(String className); + // Will return either: + // - [File, Integer] + // - [File, null] + // - null + public Object[] findSource(String className, Integer line); public File projectPath(); diff --git a/framework/src/play/src/main/java/play/core/enhancers/PropertiesEnhancer.java b/framework/src/sbt-link/src/main/java/play/core/enhancers/PropertiesEnhancer.java similarity index 100% rename from framework/src/play/src/main/java/play/core/enhancers/PropertiesEnhancer.java rename to framework/src/sbt-link/src/main/java/play/core/enhancers/PropertiesEnhancer.java diff --git a/framework/src/sbt-link/src/main/java/play/core/server/ServerWithStop.java b/framework/src/sbt-link/src/main/java/play/core/server/ServerWithStop.java new file mode 100644 index 00000000000..483c2ef11fa --- /dev/null +++ b/framework/src/sbt-link/src/main/java/play/core/server/ServerWithStop.java @@ -0,0 +1,8 @@ +package play.core.server; + +public interface ServerWithStop { + + public void stop(); + public java.net.InetSocketAddress mainAddress(); + +} \ No newline at end of file diff --git a/framework/src/play/src/main/resources/coffee-script.js b/framework/src/sbt-plugin/src/main/resources/coffee-script.js similarity index 100% rename from framework/src/play/src/main/resources/coffee-script.js rename to framework/src/sbt-plugin/src/main/resources/coffee-script.js diff --git a/framework/src/play/src/main/resources/less-1.3.0.js b/framework/src/sbt-plugin/src/main/resources/less-1.3.0.js similarity index 100% rename from framework/src/play/src/main/resources/less-1.3.0.js rename to framework/src/sbt-plugin/src/main/resources/less-1.3.0.js diff --git a/framework/src/play/src/main/resources/r.js b/framework/src/sbt-plugin/src/main/resources/r.js similarity index 100% rename from framework/src/play/src/main/resources/r.js rename to framework/src/sbt-plugin/src/main/resources/r.js diff --git a/framework/src/sbt-plugin/src/main/scala/PlayAssetsCompiler.scala b/framework/src/sbt-plugin/src/main/scala/PlayAssetsCompiler.scala index 258c81c73dd..e47f25c8351 100644 --- a/framework/src/sbt-plugin/src/main/scala/PlayAssetsCompiler.scala +++ b/framework/src/sbt-plugin/src/main/scala/PlayAssetsCompiler.scala @@ -115,14 +115,14 @@ trait PlayAssetsCompiler { coffeescriptOptions ) - def reportCompilationError(state: State, error: PlayException with PlayException.ExceptionSource) = { + def reportCompilationError(state: State, error: PlayException.ExceptionSource) = { val log = state.log // log the source file and line number with the error message - log.error(error.sourceName.getOrElse("") + error.line.map(":" + _).getOrElse("") + ": " + error.getMessage) - error.interestingLines(0).flatMap(_._2.headOption) map { line => + log.error(Option(error.sourceName).getOrElse("") + Option(error.line).map(":" + _).getOrElse("") + ": " + error.getMessage) + Option(error.interestingLines(0).focus).flatMap(_.headOption) map { line => // log the line log.error(line) - error.position map { pos => + Option(error.position).map { pos => // print a carat under the offending character val spaces = (line: Seq[Char]).take(pos).map { case '\t' => '\t' diff --git a/framework/src/sbt-plugin/src/main/scala/PlayCommands.scala b/framework/src/sbt-plugin/src/main/scala/PlayCommands.scala index eeaf3a477bf..7bf6d504470 100644 --- a/framework/src/sbt-plugin/src/main/scala/PlayCommands.scala +++ b/framework/src/sbt-plugin/src/main/scala/PlayCommands.scala @@ -10,7 +10,7 @@ import sbinary.DefaultProtocol.StringFormat import play.api._ import play.core._ -import play.utils.Colors +import play.console.Colors import PlayExceptions._ import PlayKeys._ @@ -66,13 +66,13 @@ trait PlayCommands extends PlayAssetsCompiler with PlayEclipse { private[this] var commonClassLoader: ClassLoader = _ val playCommonClassloader = TaskKey[ClassLoader]("play-common-classloader") - val playCommonClassloaderTask = (scalaInstance, dependencyClasspath in Compile) map { (si, classpath) => + val playCommonClassloaderTask = (dependencyClasspath in Compile) map { classpath => lazy val commonJars: PartialFunction[java.io.File, java.net.URL] = { case jar if jar.getName.startsWith("h2-") || jar.getName == "h2.jar" => jar.toURI.toURL } if (commonClassLoader == null) { - commonClassLoader = new java.net.URLClassLoader(classpath.map(_.data).collect(commonJars).toArray, si.loader) { + commonClassLoader = new java.net.URLClassLoader(classpath.map(_.data).collect(commonJars).toArray, null /* important here, don't depend of the sbt classLoader! */) { override def toString = "Common ClassLoader: " + getURLs.map(_.toString).mkString(",") } } @@ -484,7 +484,10 @@ exec java $* -cp $classpath """ + customFileName.map(fn => "-Dconfig.file=`dirna val sharedClasses = Seq( classOf[play.core.SBTLink].getName, classOf[play.core.server.ServerWithStop].getName, - classOf[play.api.PlayException.UsefulException].getName, + classOf[play.api.UsefulException].getName, + classOf[play.api.PlayException].getName, + classOf[play.api.PlayException.InterestingLines].getName, + classOf[play.api.PlayException.RichDescription].getName, classOf[play.api.PlayException.ExceptionSource].getName, classOf[play.api.PlayException.ExceptionAttachment].getName) @@ -528,7 +531,7 @@ exec java $* -cp $classpath """ + customFileName.map(fn => "-Dconfig.file=`dirna lazy val reloader = newReloader(state, playReload, applicationLoader) - val mainClass = applicationLoader.loadClass(classOf[play.core.server.NettyServer].getName) + val mainClass = applicationLoader.loadClass("play.core.server.NettyServer") val mainDev = mainClass.getMethod("mainDev", classOf[SBTLink], classOf[Int]) // Run in DEV diff --git a/framework/src/sbt-plugin/src/main/scala/PlayExceptions.scala b/framework/src/sbt-plugin/src/main/scala/PlayExceptions.scala index cd55b169963..030a554dd33 100644 --- a/framework/src/sbt-plugin/src/main/scala/PlayExceptions.scala +++ b/framework/src/sbt-plugin/src/main/scala/PlayExceptions.scala @@ -13,37 +13,46 @@ trait PlayExceptions { } } - case class CompilationException(problem: xsbti.Problem) extends PlayException( - "Compilation error", filterAnnoyingErrorMessages(problem.message)) with PlayException.ExceptionSource { - def line = problem.position.line.map(m => m.asInstanceOf[Int]) - def position = problem.position.pointer.map(m => m.asInstanceOf[Int]) - def input = problem.position.sourceFile.map(scalax.file.Path(_)) - def sourceName = problem.position.sourceFile.map(_.getAbsolutePath) + case class UnexpectedException(message: Option[String] = None, unexpected: Option[Throwable] = None) extends PlayException( + "Unexpected exception", + message.getOrElse { + unexpected.map(t => "%s: %s".format(t.getClass.getSimpleName, t.getMessage)).getOrElse("") + }, + unexpected.orNull + ) + + case class CompilationException(problem: xsbti.Problem) extends PlayException.ExceptionSource( + "Compilation error", filterAnnoyingErrorMessages(problem.message)) { + def line = problem.position.line.map(m => m.asInstanceOf[java.lang.Integer]).orNull + def position = problem.position.pointer.map(m => m.asInstanceOf[java.lang.Integer]).orNull + def input = problem.position.sourceFile.map(scalax.file.Path(_).string).orNull + def sourceName = problem.position.sourceFile.map(_.getAbsolutePath).orNull } - case class TemplateCompilationException(source: File, message: String, atLine: Int, column: Int) extends PlayException( - "Compilation error", message) with PlayException.ExceptionSource with FeedbackProvidedException { - def line = Some(atLine) - def position = Some(column) - def input = Some(scalax.file.Path(source)) - def sourceName = Some(source.getAbsolutePath) - } - - case class RoutesCompilationException(source: File, message: String, atLine: Option[Int], column: Option[Int]) extends PlayException( - "Compilation error", message) with PlayException.ExceptionSource with FeedbackProvidedException { + case class TemplateCompilationException(source: File, message: String, atLine: Int, column: Int) extends PlayException.ExceptionSource( + "Compilation error", message) with FeedbackProvidedException { def line = atLine def position = column - def input = Some(scalax.file.Path(source)) - def sourceName = Some(source.getAbsolutePath) + def input = scalax.file.Path(source).string + def sourceName = source.getAbsolutePath } - case class AssetCompilationException(source: Option[File], message: String, atLine: Option[Int], atColumn: Option[Int]) extends PlayException( - "Compilation error", message) with PlayException.ExceptionSource with FeedbackProvidedException { - def line = atLine - def position = atColumn - def input = source.filter(_.exists()).map(scalax.file.Path(_)) - def sourceName = source.map(_.getAbsolutePath) + case class RoutesCompilationException(source: File, message: String, atLine: Option[Int], column: Option[Int]) extends PlayException.ExceptionSource( + "Compilation error", message) with FeedbackProvidedException { + def line = atLine.map(_.asInstanceOf[java.lang.Integer]).orNull + def position = column.map(_.asInstanceOf[java.lang.Integer]).orNull + def input = scalax.file.Path(source).string + def sourceName = source.getAbsolutePath + } + + case class AssetCompilationException(source: Option[File], message: String, atLine: Option[Int], column: Option[Int]) extends PlayException.ExceptionSource( + "Compilation error", message) with FeedbackProvidedException { + def line = atLine.map(_.asInstanceOf[java.lang.Integer]).orNull + def position = column.map(_.asInstanceOf[java.lang.Integer]).orNull + def input = source.filter(_.exists()).map(scalax.file.Path(_).string).orNull + def sourceName = source.map(_.getAbsolutePath).orNull } } + object PlayExceptions extends PlayExceptions diff --git a/framework/src/sbt-plugin/src/main/scala/PlayProject.scala b/framework/src/sbt-plugin/src/main/scala/PlayProject.scala index df7eb87986d..54e29171f9b 100644 --- a/framework/src/sbt-plugin/src/main/scala/PlayProject.scala +++ b/framework/src/sbt-plugin/src/main/scala/PlayProject.scala @@ -6,11 +6,19 @@ import jline._ import play.api._ import play.core._ -import play.utils.Colors +import play.console.Colors object PlayProject extends Plugin with PlayExceptions with PlayKeys with PlayReloader with PlayCommands with PlaySettings with PlayPositionMapper { + if(Option(System.getProperty("play.debug.classpath")).filter(_ == "true").isDefined) { + println() + this.getClass.getClassLoader.asInstanceOf[sbt.PluginManagement.PluginClassLoader].getURLs.foreach { el => + println(Colors.green(el.toString)) + } + println() + } + Option(System.getProperty("play.version")).map { case badVersion if badVersion != play.core.PlayVersion.current => { println( diff --git a/framework/src/sbt-plugin/src/main/scala/PlayReloader.scala b/framework/src/sbt-plugin/src/main/scala/PlayReloader.scala index a11539d2814..418c24526ba 100644 --- a/framework/src/sbt-plugin/src/main/scala/PlayReloader.scala +++ b/framework/src/sbt-plugin/src/main/scala/PlayReloader.scala @@ -88,21 +88,6 @@ trait PlayReloader { // --- Utils - /*def markdownToHtml(markdown: String, link: String => (String, String)) = { - import org.pegdown._ - import org.pegdown.ast._ - - val processor = new PegDownProcessor(Extensions.ALL) - val links = new LinkRenderer { - override def render(node: WikiLinkNode) = { - val (href, text) = link(node.getText) - new LinkRenderer.Rendering(href, text) - } - } - - processor.markdownToHtml(markdown, links) - }*/ - def markdownToHtml(markdown: String) = markdown // --- @@ -137,16 +122,26 @@ trait PlayReloader { updated } - def findSource(className: String) = { + def findSource(className: String, line: java.lang.Integer): Array[java.lang.Object] = { val topType = className.split('$').head currentAnalysis.flatMap { analysis => analysis.apis.internal.flatMap { case (sourceFile, source) => { source.api.definitions.find(defined => defined.name == topType).map(_ => { sourceFile: java.io.File - }) + } -> line) + } + }.headOption.map { + case (source, maybeLine) => { + play.templates.MaybeGeneratedSource.unapply(source).map { generatedSource => + generatedSource.source.get -> Option(maybeLine).map(l => generatedSource.mapLine(l):java.lang.Integer).orNull + }.getOrElse(source -> maybeLine) } - }.headOption + } + }.map { + case (file, line) => { + Array[java.lang.Object](file, line) + } }.orNull } @@ -161,15 +156,20 @@ trait PlayReloader { } getOrElse problem } - private def allProblems(inc: Incomplete): Seq[xsbti.Problem] = + private def allProblems(inc: Incomplete): Seq[xsbti.Problem] = { allProblems(inc :: Nil) - private def allProblems(incs: Seq[Incomplete]): Seq[xsbti.Problem] = + } + + private def allProblems(incs: Seq[Incomplete]): Seq[xsbti.Problem] = { problems(Incomplete.allExceptions(incs).toSeq) - private def problems(es: Seq[Throwable]): Seq[xsbti.Problem] = + } + + private def problems(es: Seq[Throwable]): Seq[xsbti.Problem] = { es flatMap { case cf: xsbti.CompileFailed => cf.problems case _ => Nil } + } def getProblems(incomplete: Incomplete): Seq[xsbti.Problem] = { (allProblems(incomplete) ++ { diff --git a/framework/src/sbt-plugin/src/main/scala/PlaySettings.scala b/framework/src/sbt-plugin/src/main/scala/PlaySettings.scala index 23e6a643682..4f5f35af51f 100644 --- a/framework/src/sbt-plugin/src/main/scala/PlaySettings.scala +++ b/framework/src/sbt-plugin/src/main/scala/PlaySettings.scala @@ -59,6 +59,8 @@ trait PlaySettings { lazy val defaultSettings = Seq[Setting[_]]( + scalaVersion := play.core.PlayVersion.scalaVersion, + playPlugin := false, resolvers ++= Seq( @@ -132,7 +134,7 @@ trait PlaySettings { copyResources in Compile <<= (copyResources in Compile, playCopyAssets) map { (r, pr) => r ++ pr }, - mainClass in (Compile, run) := Some(classOf[play.core.server.NettyServer].getName), + mainClass in (Compile, run) := Some("play.core.server.NettyServer"), compile in (Compile) <<= PostCompile(scope = Compile), diff --git a/framework/src/sbt-plugin/src/main/scala/coffeescript/CoffeescriptCompiler.scala b/framework/src/sbt-plugin/src/main/scala/coffeescript/CoffeescriptCompiler.scala index 2a8289537c0..6f6c9a8e2cc 100644 --- a/framework/src/sbt-plugin/src/main/scala/coffeescript/CoffeescriptCompiler.scala +++ b/framework/src/sbt-plugin/src/main/scala/coffeescript/CoffeescriptCompiler.scala @@ -4,7 +4,6 @@ import java.io._ import play.api._ import sbt.PlayExceptions.AssetCompilationException - object CoffeescriptCompiler { import org.mozilla.javascript._ @@ -33,7 +32,7 @@ object CoffeescriptCompiler { Context.exit (source: File, bare: Boolean) => { - val coffeeCode = Path(source).slurpString.replace("\r", "") + val coffeeCode = Path(source).string.replace("\r", "") val options = ctx.newObject(scope) options.put("bare", options, bare) Context.call(null, compilerFunction, scope, scope, Array(coffeeCode, options)).asInstanceOf[String] @@ -77,4 +76,4 @@ object CoffeescriptCompiler { } } -} \ No newline at end of file +} diff --git a/framework/src/sbt-plugin/src/main/scala/jscompile/JavascriptCompiler.scala b/framework/src/sbt-plugin/src/main/scala/jscompile/JavascriptCompiler.scala index 47f52dabe44..0adcd2096b6 100644 --- a/framework/src/sbt-plugin/src/main/scala/jscompile/JavascriptCompiler.scala +++ b/framework/src/sbt-plugin/src/main/scala/jscompile/JavascriptCompiler.scala @@ -25,7 +25,7 @@ object JavascriptCompiler { val simpleCheck = simpleCompilerOptions.contains("rjs") - val origin = Path(source).slurpString + val origin = Path(source).string val options = fullCompilerOptions.getOrElse { val defaultOptions = new CompilerOptions() @@ -82,12 +82,12 @@ object JavascriptCompiler { } } - case class CompilationException(message: String, jsFile: File, atLine: Option[Int]) extends PlayException( - "JS Compilation error", message) with PlayException.ExceptionSource { - def line = atLine - def position = None - def input = Some(scalax.file.Path(jsFile)) - def sourceName = Some(jsFile.getAbsolutePath) + case class CompilationException(message: String, jsFile: File, atLine: Option[Int]) extends PlayException.ExceptionSource( + "JS Compilation error", message) { + def line = atLine.map(_.asInstanceOf[java.lang.Integer]).orNull + def position = null + def input = scalax.file.Path(jsFile).string + def sourceName = jsFile.getAbsolutePath } /* diff --git a/framework/src/sbt-plugin/src/main/scala/less/LessCompiler.scala b/framework/src/sbt-plugin/src/main/scala/less/LessCompiler.scala index f0c41569497..e0e380291fc 100644 --- a/framework/src/sbt-plugin/src/main/scala/less/LessCompiler.scala +++ b/framework/src/sbt-plugin/src/main/scala/less/LessCompiler.scala @@ -128,7 +128,7 @@ object LessCompiler { } } - def readContent(file: File) = Path(file).slurpString.replace("\r", "") + def readContent(file: File) = Path(file).string.replace("\r", "") def resolve(originalSource: File, imported: String) = new File(originalSource.getParentFile, imported) } diff --git a/framework/src/sbt-plugin/src/main/scala/test/JUnitXmlTestListener.scala b/framework/src/sbt-plugin/src/main/scala/test/JUnitXmlTestListener.scala index 091ae222cfd..131c7e733c5 100644 --- a/framework/src/sbt-plugin/src/main/scala/test/JUnitXmlTestListener.scala +++ b/framework/src/sbt-plugin/src/main/scala/test/JUnitXmlTestListener.scala @@ -12,7 +12,7 @@ import scala.collection.mutable.ListBuffer import scala.xml.{ Elem, Node, XML } import org.scalatools.testing.{ Event => TEvent, Result => TResult, Logger => TLogger } -import play.utils.Colors +import play.console.Colors /** * A tests listener that outputs the results it receives in junit xml diff --git a/framework/src/play/src/main/scala/NotImplementedError.scala b/framework/src/sip14-backport/src/main/scala/NotImplementedError.scala similarity index 100% rename from framework/src/play/src/main/scala/NotImplementedError.scala rename to framework/src/sip14-backport/src/main/scala/NotImplementedError.scala diff --git a/framework/src/play/src/main/scala/concurrent/Awaitable.scala b/framework/src/sip14-backport/src/main/scala/concurrent/Awaitable.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/Awaitable.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/Awaitable.scala diff --git a/framework/src/play/src/main/scala/concurrent/BlockContext.scala b/framework/src/sip14-backport/src/main/scala/concurrent/BlockContext.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/BlockContext.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/BlockContext.scala diff --git a/framework/src/play/src/main/scala/concurrent/Channel.scala b/framework/src/sip14-backport/src/main/scala/concurrent/Channel.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/Channel.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/Channel.scala diff --git a/framework/src/play/src/main/scala/concurrent/DelayedLazyVal.scala b/framework/src/sip14-backport/src/main/scala/concurrent/DelayedLazyVal.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/DelayedLazyVal.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/DelayedLazyVal.scala diff --git a/framework/src/play/src/main/scala/concurrent/ExecutionContext.scala b/framework/src/sip14-backport/src/main/scala/concurrent/ExecutionContext.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/ExecutionContext.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/ExecutionContext.scala diff --git a/framework/src/play/src/main/scala/concurrent/Future.scala b/framework/src/sip14-backport/src/main/scala/concurrent/Future.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/Future.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/Future.scala diff --git a/framework/src/play/src/main/scala/concurrent/FutureTaskRunner.scala b/framework/src/sip14-backport/src/main/scala/concurrent/FutureTaskRunner.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/FutureTaskRunner.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/FutureTaskRunner.scala diff --git a/framework/src/play/src/main/scala/concurrent/JavaConversions.scala b/framework/src/sip14-backport/src/main/scala/concurrent/JavaConversions.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/JavaConversions.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/JavaConversions.scala diff --git a/framework/src/play/src/main/scala/concurrent/Lock.scala b/framework/src/sip14-backport/src/main/scala/concurrent/Lock.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/Lock.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/Lock.scala diff --git a/framework/src/play/src/main/scala/concurrent/ManagedBlocker.scala b/framework/src/sip14-backport/src/main/scala/concurrent/ManagedBlocker.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/ManagedBlocker.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/ManagedBlocker.scala diff --git a/framework/src/play/src/main/scala/concurrent/Promise.scala b/framework/src/sip14-backport/src/main/scala/concurrent/Promise.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/Promise.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/Promise.scala diff --git a/framework/src/play/src/main/scala/concurrent/Scheduler.scala b/framework/src/sip14-backport/src/main/scala/concurrent/Scheduler.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/Scheduler.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/Scheduler.scala diff --git a/framework/src/play/src/main/scala/concurrent/SyncChannel.scala b/framework/src/sip14-backport/src/main/scala/concurrent/SyncChannel.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/SyncChannel.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/SyncChannel.scala diff --git a/framework/src/play/src/main/scala/concurrent/SyncVar.scala b/framework/src/sip14-backport/src/main/scala/concurrent/SyncVar.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/SyncVar.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/SyncVar.scala diff --git a/framework/src/play/src/main/scala/concurrent/Task.scala b/framework/src/sip14-backport/src/main/scala/concurrent/Task.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/Task.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/Task.scala diff --git a/framework/src/play/src/main/scala/concurrent/TaskRunner.scala b/framework/src/sip14-backport/src/main/scala/concurrent/TaskRunner.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/TaskRunner.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/TaskRunner.scala diff --git a/framework/src/play/src/main/scala/concurrent/TaskRunners.scala b/framework/src/sip14-backport/src/main/scala/concurrent/TaskRunners.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/TaskRunners.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/TaskRunners.scala diff --git a/framework/src/play/src/main/scala/concurrent/ThreadPoolRunner.scala b/framework/src/sip14-backport/src/main/scala/concurrent/ThreadPoolRunner.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/ThreadPoolRunner.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/ThreadPoolRunner.scala diff --git a/framework/src/play/src/main/scala/concurrent/ThreadRunner.scala b/framework/src/sip14-backport/src/main/scala/concurrent/ThreadRunner.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/ThreadRunner.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/ThreadRunner.scala diff --git a/framework/src/play/src/main/scala/concurrent/impl/AbstractPromise.java b/framework/src/sip14-backport/src/main/scala/concurrent/impl/AbstractPromise.java similarity index 100% rename from framework/src/play/src/main/scala/concurrent/impl/AbstractPromise.java rename to framework/src/sip14-backport/src/main/scala/concurrent/impl/AbstractPromise.java diff --git a/framework/src/play/src/main/scala/concurrent/impl/ExecutionContextImpl.scala b/framework/src/sip14-backport/src/main/scala/concurrent/impl/ExecutionContextImpl.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/impl/ExecutionContextImpl.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/impl/ExecutionContextImpl.scala diff --git a/framework/src/play/src/main/scala/concurrent/impl/Future.scala b/framework/src/sip14-backport/src/main/scala/concurrent/impl/Future.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/impl/Future.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/impl/Future.scala diff --git a/framework/src/play/src/main/scala/concurrent/impl/Promise.scala b/framework/src/sip14-backport/src/main/scala/concurrent/impl/Promise.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/impl/Promise.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/impl/Promise.scala diff --git a/framework/src/play/src/main/scala/concurrent/ops.scala b/framework/src/sip14-backport/src/main/scala/concurrent/ops.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/ops.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/ops.scala diff --git a/framework/src/play/src/main/scala/concurrent/package.scala b/framework/src/sip14-backport/src/main/scala/concurrent/package.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/package.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/package.scala diff --git a/framework/src/play/src/main/scala/concurrent/util/Duration.scala b/framework/src/sip14-backport/src/main/scala/concurrent/util/Duration.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/util/Duration.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/util/Duration.scala diff --git a/framework/src/play/src/main/scala/concurrent/util/duration/Classifier.scala b/framework/src/sip14-backport/src/main/scala/concurrent/util/duration/Classifier.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/util/duration/Classifier.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/util/duration/Classifier.scala diff --git a/framework/src/play/src/main/scala/concurrent/util/duration/IntMult.scala b/framework/src/sip14-backport/src/main/scala/concurrent/util/duration/IntMult.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/util/duration/IntMult.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/util/duration/IntMult.scala diff --git a/framework/src/play/src/main/scala/concurrent/util/duration/package.scala b/framework/src/sip14-backport/src/main/scala/concurrent/util/duration/package.scala similarity index 100% rename from framework/src/play/src/main/scala/concurrent/util/duration/package.scala rename to framework/src/sip14-backport/src/main/scala/concurrent/util/duration/package.scala diff --git a/framework/src/play/src/main/scala/java/forkjoin/ForkJoinPool.java b/framework/src/sip14-backport/src/main/scala/java/forkjoin/ForkJoinPool.java similarity index 100% rename from framework/src/play/src/main/scala/java/forkjoin/ForkJoinPool.java rename to framework/src/sip14-backport/src/main/scala/java/forkjoin/ForkJoinPool.java diff --git a/framework/src/play/src/main/scala/java/forkjoin/ForkJoinTask.java b/framework/src/sip14-backport/src/main/scala/java/forkjoin/ForkJoinTask.java similarity index 100% rename from framework/src/play/src/main/scala/java/forkjoin/ForkJoinTask.java rename to framework/src/sip14-backport/src/main/scala/java/forkjoin/ForkJoinTask.java diff --git a/framework/src/play/src/main/scala/java/forkjoin/ForkJoinWorkerThread.java b/framework/src/sip14-backport/src/main/scala/java/forkjoin/ForkJoinWorkerThread.java similarity index 100% rename from framework/src/play/src/main/scala/java/forkjoin/ForkJoinWorkerThread.java rename to framework/src/sip14-backport/src/main/scala/java/forkjoin/ForkJoinWorkerThread.java diff --git a/framework/src/play/src/main/scala/java/forkjoin/LinkedTransferQueue.java b/framework/src/sip14-backport/src/main/scala/java/forkjoin/LinkedTransferQueue.java similarity index 100% rename from framework/src/play/src/main/scala/java/forkjoin/LinkedTransferQueue.java rename to framework/src/sip14-backport/src/main/scala/java/forkjoin/LinkedTransferQueue.java diff --git a/framework/src/play/src/main/scala/java/forkjoin/RecursiveAction.java b/framework/src/sip14-backport/src/main/scala/java/forkjoin/RecursiveAction.java similarity index 100% rename from framework/src/play/src/main/scala/java/forkjoin/RecursiveAction.java rename to framework/src/sip14-backport/src/main/scala/java/forkjoin/RecursiveAction.java diff --git a/framework/src/play/src/main/scala/java/forkjoin/RecursiveTask.java b/framework/src/sip14-backport/src/main/scala/java/forkjoin/RecursiveTask.java similarity index 100% rename from framework/src/play/src/main/scala/java/forkjoin/RecursiveTask.java rename to framework/src/sip14-backport/src/main/scala/java/forkjoin/RecursiveTask.java diff --git a/framework/src/play/src/main/scala/java/forkjoin/ThreadLocalRandom.java b/framework/src/sip14-backport/src/main/scala/java/forkjoin/ThreadLocalRandom.java similarity index 100% rename from framework/src/play/src/main/scala/java/forkjoin/ThreadLocalRandom.java rename to framework/src/sip14-backport/src/main/scala/java/forkjoin/ThreadLocalRandom.java diff --git a/framework/src/play/src/main/scala/java/forkjoin/TransferQueue.java b/framework/src/sip14-backport/src/main/scala/java/forkjoin/TransferQueue.java similarity index 100% rename from framework/src/play/src/main/scala/java/forkjoin/TransferQueue.java rename to framework/src/sip14-backport/src/main/scala/java/forkjoin/TransferQueue.java diff --git a/framework/src/play/src/main/scala/java/forkjoin/package-info.java b/framework/src/sip14-backport/src/main/scala/java/forkjoin/package-info.java similarity index 100% rename from framework/src/play/src/main/scala/java/forkjoin/package-info.java rename to framework/src/sip14-backport/src/main/scala/java/forkjoin/package-info.java diff --git a/framework/src/play/src/main/scala/java/util/Unsafe.java b/framework/src/sip14-backport/src/main/scala/java/util/Unsafe.java similarity index 100% rename from framework/src/play/src/main/scala/java/util/Unsafe.java rename to framework/src/sip14-backport/src/main/scala/java/util/Unsafe.java diff --git a/framework/src/play/src/main/scala/util/Try.scala b/framework/src/sip14-backport/src/main/scala/util/Try.scala similarity index 100% rename from framework/src/play/src/main/scala/util/Try.scala rename to framework/src/sip14-backport/src/main/scala/util/Try.scala diff --git a/framework/src/play/src/main/scala/util/control/NonFatal.scala b/framework/src/sip14-backport/src/main/scala/util/control/NonFatal.scala similarity index 100% rename from framework/src/play/src/main/scala/util/control/NonFatal.scala rename to framework/src/sip14-backport/src/main/scala/util/control/NonFatal.scala diff --git a/framework/src/templates/src/main/scala/play/api/templates/ScalaTemplateCompiler.scala b/framework/src/templates-compiler/src/main/scala/play/templates/ScalaTemplateCompiler.scala similarity index 82% rename from framework/src/templates/src/main/scala/play/api/templates/ScalaTemplateCompiler.scala rename to framework/src/templates-compiler/src/main/scala/play/templates/ScalaTemplateCompiler.scala index dbd475a9aae..c3a214eb867 100644 --- a/framework/src/templates/src/main/scala/play/api/templates/ScalaTemplateCompiler.scala +++ b/framework/src/templates-compiler/src/main/scala/play/templates/ScalaTemplateCompiler.scala @@ -1,33 +1,5 @@ import scala.util.parsing.input.OffsetPosition -package play.api.templates { - - trait Template0[Result] { def render(): Result } - trait Template1[A, Result] { def render(a: A): Result } - trait Template2[A, B, Result] { def render(a: A, b: B): Result } - trait Template3[A, B, C, Result] { def render(a: A, b: B, c: C): Result } - trait Template4[A, B, C, D, Result] { def render(a: A, b: B, c: C, d: D): Result } - trait Template5[A, B, C, D, E, Result] { def render(a: A, b: B, c: C, d: D, e: E): Result } - trait Template6[A, B, C, D, E, F, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F): Result } - trait Template7[A, B, C, D, E, F, G, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G): Result } - trait Template8[A, B, C, D, E, F, G, H, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H): Result } - trait Template9[A, B, C, D, E, F, G, H, I, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I): Result } - trait Template10[A, B, C, D, E, F, G, H, I, J, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J): Result } - trait Template11[A, B, C, D, E, F, G, H, I, J, K, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K): Result } - trait Template12[A, B, C, D, E, F, G, H, I, J, K, L, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L): Result } - trait Template13[A, B, C, D, E, F, G, H, I, J, K, L, M, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M): Result } - trait Template14[A, B, C, D, E, F, G, H, I, J, K, L, M, N, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N): Result } - trait Template15[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O): Result } - trait Template16[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O, p: P): Result } - trait Template17[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O, p: P, q: Q): Result } - trait Template18[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O, p: P, q: Q, r: R): Result } - trait Template19[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O, p: P, q: Q, r: R, s: S): Result } - trait Template20[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O, p: P, q: Q, r: R, s: S, t: T): Result } - trait Template21[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O, p: P, q: Q, r: R, s: S, t: T, u: U): Result } - trait Template22[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O, p: P, q: Q, r: R, s: S, t: T, u: U, v: V): Result } - -} - package play.templates { import scalax.file._ @@ -131,7 +103,7 @@ package play.templates { case class GeneratedSource(file: File) extends AbstractGeneratedSource{ - def content = Path(file).slurpString + def content = Path(file).string def needRecompilation: Boolean = (!file.exists || // A generated source already exist but @@ -142,7 +114,7 @@ package play.templates { def toSourcePosition(marker: Int): (Int, Int) = { try { val targetMarker = mapPosition(marker) - val line = Path(source.get).slurpString.substring(0, targetMarker).split('\n').size + val line = Path(source.get).string.substring(0, targetMarker).split('\n').size (line, targetMarker) } catch { case _ => (0, 0) @@ -200,7 +172,7 @@ package play.templates { def compile(source: File, sourceDirectory: File, generatedDirectory: File, resultType: String, formatterType: String, additionalImports: String = "") = { val (templateName, generatedSource) = generatedFile(source, sourceDirectory, generatedDirectory) if (generatedSource.needRecompilation) { - val generated = parseAndGenerateCode(templateName, Path(source).slurpString, source.getAbsolutePath, resultType, formatterType, additionalImports) + val generated = parseAndGenerateCode(templateName, Path(source).string, source.getAbsolutePath, resultType, formatterType, additionalImports) Path(generatedSource.file).write(generated.toString) @@ -582,7 +554,7 @@ object """ :+ name :+ """ extends BaseScalaTemplate[""" :+ resultType :+ """,For @deprecated("use generateFinalTemplate with 8 parameters instead", "Play 2.1") def generateFinalTemplate(template: File, packageName: String, name: String, root: Template, resultType: String, formatterType: String, additionalImports: String): String = { - generateFinalTemplate(template.getAbsolutePath, Path(template).slurpString, packageName, name, root, resultType, formatterType, additionalImports) + generateFinalTemplate(template.getAbsolutePath, Path(template).string, packageName, name, root, resultType, formatterType, additionalImports) } def generateFinalTemplate(absolutePath: String, contents: String, packageName: String, name: String, root: Template, resultType: String, formatterType: String, additionalImports: String): String = { @@ -782,105 +754,4 @@ object """ :+ name :+ """ extends BaseScalaTemplate[""" :+ resultType :+ """,For } - /* ------ */ - - trait Appendable[T] { - def +(other: T): T - override def equals(x: Any): Boolean = super.equals(x) - override def hashCode() = super.hashCode() - } - - trait Format[T <: Appendable[T]] { - def raw(text: String): T - def escape(text: String): T - } - - case class BaseScalaTemplate[T <: Appendable[T], F <: Format[T]](format: F) { - - def _display_(o: Any)(implicit m: Manifest[T]): T = { - o match { - case escaped if escaped != null && escaped.getClass == m.erasure => escaped.asInstanceOf[T] - case () => format.raw("") - case None => format.raw("") - case Some(v) => _display_(v) - case xml: scala.xml.NodeSeq => format.raw(xml.toString) - case escapeds: TraversableOnce[_] => escapeds.foldLeft(format.raw(""))(_ + _display_(_)) - case escapeds: Array[_] => escapeds.foldLeft(format.raw(""))(_ + _display_(_)) - case string: String => format.escape(string) - case v if v != null => _display_(v.toString) - case _ => format.raw("") - } - } - - } - - /* ------ */ - - object TemplateMagic { - - // --- UTILS - - def defining[T](t: T)(handler: T => Any) = { - handler(t) - } - - def using[T](t: T) = t - - // --- IF - - implicit def iterableToBoolean(x: Iterable[_]) = x != null && !x.isEmpty - implicit def optionToBoolean(x: Option[_]) = x != null && x.isDefined - implicit def stringToBoolean(x: String) = x != null && !x.isEmpty - - // --- JAVA - - implicit def javaCollectionToScala[T](x: java.lang.Iterable[T]) = { - import scala.collection.JavaConverters._ - x.asScala - } - - // --- DEFAULT - - case class Default(default: Any) { - def ?:(x: Any) = x match { - case "" => default - case Nil => default - case false => default - case 0 => default - case None => default - case _ => x - } - } - - implicit def anyToDefault(x: Any) = Default(x) - - // --- DATE - - class RichDate(date: java.util.Date) { - - def format(pattern: String) = { - new java.text.SimpleDateFormat(pattern).format(date) - } - - } - - implicit def richDate(date: java.util.Date) = new RichDate(date) - - // --- STRING - - class RichString(string: String) { - - def when(predicate: => Boolean) = { - predicate match { - case true => string - case false => "" - } - } - - } - - implicit def richString(string: String) = new RichString(string) - - } - } diff --git a/framework/src/templates-compiler/src/test/scala/FakeRuntime.scala b/framework/src/templates-compiler/src/test/scala/FakeRuntime.scala new file mode 100644 index 00000000000..5ba5d0870ca --- /dev/null +++ b/framework/src/templates-compiler/src/test/scala/FakeRuntime.scala @@ -0,0 +1,130 @@ +package play.api.templates { + + trait Template0[Result] { def render(): Result } + trait Template1[A, Result] { def render(a: A): Result } + trait Template2[A, B, Result] { def render(a: A, b: B): Result } + trait Template3[A, B, C, Result] { def render(a: A, b: B, c: C): Result } + trait Template4[A, B, C, D, Result] { def render(a: A, b: B, c: C, d: D): Result } + trait Template5[A, B, C, D, E, Result] { def render(a: A, b: B, c: C, d: D, e: E): Result } + trait Template6[A, B, C, D, E, F, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F): Result } + trait Template7[A, B, C, D, E, F, G, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G): Result } + trait Template8[A, B, C, D, E, F, G, H, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H): Result } + trait Template9[A, B, C, D, E, F, G, H, I, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I): Result } + trait Template10[A, B, C, D, E, F, G, H, I, J, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J): Result } + trait Template11[A, B, C, D, E, F, G, H, I, J, K, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K): Result } + trait Template12[A, B, C, D, E, F, G, H, I, J, K, L, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L): Result } + trait Template13[A, B, C, D, E, F, G, H, I, J, K, L, M, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M): Result } + trait Template14[A, B, C, D, E, F, G, H, I, J, K, L, M, N, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N): Result } + trait Template15[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O): Result } + trait Template16[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O, p: P): Result } + trait Template17[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O, p: P, q: Q): Result } + trait Template18[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O, p: P, q: Q, r: R): Result } + trait Template19[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O, p: P, q: Q, r: R, s: S): Result } + trait Template20[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O, p: P, q: Q, r: R, s: S, t: T): Result } + trait Template21[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O, p: P, q: Q, r: R, s: S, t: T, u: U): Result } + trait Template22[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O, p: P, q: Q, r: R, s: S, t: T, u: U, v: V): Result } + +} + +package play.templates { + + trait Appendable[T] { + def +(other: T): T + override def equals(x: Any): Boolean = super.equals(x) + override def hashCode() = super.hashCode() + } + + trait Format[T <: Appendable[T]] { + def raw(text: String): T + def escape(text: String): T + } + + case class BaseScalaTemplate[T <: Appendable[T], F <: Format[T]](format: F) { + + def _display_(o: Any)(implicit m: Manifest[T]): T = { + o match { + case escaped if escaped != null && escaped.getClass == m.erasure => escaped.asInstanceOf[T] + case () => format.raw("") + case None => format.raw("") + case Some(v) => _display_(v) + case xml: scala.xml.NodeSeq => format.raw(xml.toString) + case escapeds: TraversableOnce[_] => escapeds.foldLeft(format.raw(""))(_ + _display_(_)) + case escapeds: Array[_] => escapeds.foldLeft(format.raw(""))(_ + _display_(_)) + case string: String => format.escape(string) + case v if v != null => _display_(v.toString) + case _ => format.raw("") + } + } + + } + + /* ------ */ + + object TemplateMagic { + + // --- UTILS + + def defining[T](t: T)(handler: T => Any) = { + handler(t) + } + + def using[T](t: T) = t + + // --- IF + + implicit def iterableToBoolean(x: Iterable[_]) = x != null && !x.isEmpty + implicit def optionToBoolean(x: Option[_]) = x != null && x.isDefined + implicit def stringToBoolean(x: String) = x != null && !x.isEmpty + + // --- JAVA + + implicit def javaCollectionToScala[T](x: java.lang.Iterable[T]) = { + import scala.collection.JavaConverters._ + x.asScala + } + + // --- DEFAULT + + case class Default(default: Any) { + def ?:(x: Any) = x match { + case "" => default + case Nil => default + case false => default + case 0 => default + case None => default + case _ => x + } + } + + implicit def anyToDefault(x: Any) = Default(x) + + // --- DATE + + class RichDate(date: java.util.Date) { + + def format(pattern: String) = { + new java.text.SimpleDateFormat(pattern).format(date) + } + + } + + implicit def richDate(date: java.util.Date) = new RichDate(date) + + // --- STRING + + class RichString(string: String) { + + def when(predicate: => Boolean) = { + predicate match { + case true => string + case false => "" + } + } + + } + + implicit def richString(string: String) = new RichString(string) + + } + +} diff --git a/framework/src/templates/src/test/scala/TemplateCompilerSpec.scala b/framework/src/templates-compiler/src/test/scala/TemplateCompilerSpec.scala similarity index 95% rename from framework/src/templates/src/test/scala/TemplateCompilerSpec.scala rename to framework/src/templates-compiler/src/test/scala/TemplateCompilerSpec.scala index ba5fb8aba57..ae0dddc8c6a 100644 --- a/framework/src/templates/src/test/scala/TemplateCompilerSpec.scala +++ b/framework/src/templates-compiler/src/test/scala/TemplateCompilerSpec.scala @@ -9,9 +9,9 @@ object TemplateCompilerSpec extends Specification { import Helper._ - val sourceDir = new File("src/templates/src/test/templates") - val generatedDir = new File("src/templates/target/test/generated-templates") - val generatedClasses = new File("src/templates/target/test/generated-classes") + val sourceDir = new File("src/templates-compiler/src/test/templates") + val generatedDir = new File("src/templates-compiler/target/test/generated-templates") + val generatedClasses = new File("src/templates-compiler/target/test/generated-classes") scalax.file.Path(generatedDir).deleteRecursively() scalax.file.Path(generatedClasses).deleteRecursively() scalax.file.Path(generatedClasses).createDirectory() diff --git a/framework/src/templates/src/test/scala/TemplateParserSpec.scala b/framework/src/templates-compiler/src/test/scala/TemplateParserSpec.scala similarity index 97% rename from framework/src/templates/src/test/scala/TemplateParserSpec.scala rename to framework/src/templates-compiler/src/test/scala/TemplateParserSpec.scala index ed184696ef6..2e050b3d5f0 100644 --- a/framework/src/templates/src/test/scala/TemplateParserSpec.scala +++ b/framework/src/templates-compiler/src/test/scala/TemplateParserSpec.scala @@ -13,7 +13,7 @@ object TemplateParserSpec extends Specification { val parser = ScalaTemplateCompiler.templateParser def get(templateName: String) = { - new CharSequenceReader(scalax.file.Path.fromString("src/templates/src/test/templates/" + templateName).slurpString) + new CharSequenceReader(scalax.file.Path.fromString("src/templates-compiler/src/test/templates/" + templateName).string) } def parse(templateName: String) = { diff --git a/framework/src/templates/src/test/scala/TemplateUtilsSpec.scala b/framework/src/templates-compiler/src/test/scala/TemplateUtilsSpec.scala similarity index 100% rename from framework/src/templates/src/test/scala/TemplateUtilsSpec.scala rename to framework/src/templates-compiler/src/test/scala/TemplateUtilsSpec.scala diff --git a/framework/src/templates/src/test/templates/complicated.scala.html b/framework/src/templates-compiler/src/test/templates/complicated.scala.html similarity index 100% rename from framework/src/templates/src/test/templates/complicated.scala.html rename to framework/src/templates-compiler/src/test/templates/complicated.scala.html diff --git a/framework/src/templates/src/test/templates/error.scala.html b/framework/src/templates-compiler/src/test/templates/error.scala.html similarity index 100% rename from framework/src/templates/src/test/templates/error.scala.html rename to framework/src/templates-compiler/src/test/templates/error.scala.html diff --git a/framework/src/templates/src/test/templates/hello.scala.html b/framework/src/templates-compiler/src/test/templates/hello.scala.html similarity index 100% rename from framework/src/templates/src/test/templates/hello.scala.html rename to framework/src/templates-compiler/src/test/templates/hello.scala.html diff --git a/framework/src/templates/src/test/templates/invalidAt.scala.html b/framework/src/templates-compiler/src/test/templates/invalidAt.scala.html similarity index 100% rename from framework/src/templates/src/test/templates/invalidAt.scala.html rename to framework/src/templates-compiler/src/test/templates/invalidAt.scala.html diff --git a/framework/src/templates/src/test/templates/patternMatching.scala.html b/framework/src/templates-compiler/src/test/templates/patternMatching.scala.html similarity index 100% rename from framework/src/templates/src/test/templates/patternMatching.scala.html rename to framework/src/templates-compiler/src/test/templates/patternMatching.scala.html diff --git a/framework/src/templates/src/test/templates/real.scala.html b/framework/src/templates-compiler/src/test/templates/real.scala.html similarity index 100% rename from framework/src/templates/src/test/templates/real.scala.html rename to framework/src/templates-compiler/src/test/templates/real.scala.html diff --git a/framework/src/templates/src/test/templates/set.scala.html b/framework/src/templates-compiler/src/test/templates/set.scala.html similarity index 100% rename from framework/src/templates/src/test/templates/set.scala.html rename to framework/src/templates-compiler/src/test/templates/set.scala.html diff --git a/framework/src/templates/src/test/templates/simple.scala.html b/framework/src/templates-compiler/src/test/templates/simple.scala.html similarity index 100% rename from framework/src/templates/src/test/templates/simple.scala.html rename to framework/src/templates-compiler/src/test/templates/simple.scala.html diff --git a/framework/src/templates/src/test/templates/static.scala.html b/framework/src/templates-compiler/src/test/templates/static.scala.html similarity index 100% rename from framework/src/templates/src/test/templates/static.scala.html rename to framework/src/templates-compiler/src/test/templates/static.scala.html diff --git a/framework/src/templates/src/test/templates/unclosedBracket.scala.html b/framework/src/templates-compiler/src/test/templates/unclosedBracket.scala.html similarity index 100% rename from framework/src/templates/src/test/templates/unclosedBracket.scala.html rename to framework/src/templates-compiler/src/test/templates/unclosedBracket.scala.html diff --git a/framework/src/templates/src/test/templates/unclosedBracket2.scala.html b/framework/src/templates-compiler/src/test/templates/unclosedBracket2.scala.html similarity index 100% rename from framework/src/templates/src/test/templates/unclosedBracket2.scala.html rename to framework/src/templates-compiler/src/test/templates/unclosedBracket2.scala.html diff --git a/framework/src/templates/src/main/scala/play/api/templates/ScalaTemplate.scala b/framework/src/templates/src/main/scala/play/api/templates/ScalaTemplate.scala new file mode 100644 index 00000000000..1622222e8e0 --- /dev/null +++ b/framework/src/templates/src/main/scala/play/api/templates/ScalaTemplate.scala @@ -0,0 +1,131 @@ + +package play.api.templates { + + trait Template0[Result] { def render(): Result } + trait Template1[A, Result] { def render(a: A): Result } + trait Template2[A, B, Result] { def render(a: A, b: B): Result } + trait Template3[A, B, C, Result] { def render(a: A, b: B, c: C): Result } + trait Template4[A, B, C, D, Result] { def render(a: A, b: B, c: C, d: D): Result } + trait Template5[A, B, C, D, E, Result] { def render(a: A, b: B, c: C, d: D, e: E): Result } + trait Template6[A, B, C, D, E, F, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F): Result } + trait Template7[A, B, C, D, E, F, G, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G): Result } + trait Template8[A, B, C, D, E, F, G, H, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H): Result } + trait Template9[A, B, C, D, E, F, G, H, I, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I): Result } + trait Template10[A, B, C, D, E, F, G, H, I, J, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J): Result } + trait Template11[A, B, C, D, E, F, G, H, I, J, K, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K): Result } + trait Template12[A, B, C, D, E, F, G, H, I, J, K, L, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L): Result } + trait Template13[A, B, C, D, E, F, G, H, I, J, K, L, M, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M): Result } + trait Template14[A, B, C, D, E, F, G, H, I, J, K, L, M, N, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N): Result } + trait Template15[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O): Result } + trait Template16[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O, p: P): Result } + trait Template17[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O, p: P, q: Q): Result } + trait Template18[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O, p: P, q: Q, r: R): Result } + trait Template19[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O, p: P, q: Q, r: R, s: S): Result } + trait Template20[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O, p: P, q: Q, r: R, s: S, t: T): Result } + trait Template21[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O, p: P, q: Q, r: R, s: S, t: T, u: U): Result } + trait Template22[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, Result] { def render(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O, p: P, q: Q, r: R, s: S, t: T, u: U, v: V): Result } + +} + +package play.templates { + + trait Appendable[T] { + def +(other: T): T + override def equals(x: Any): Boolean = super.equals(x) + override def hashCode() = super.hashCode() + } + + trait Format[T <: Appendable[T]] { + def raw(text: String): T + def escape(text: String): T + } + + case class BaseScalaTemplate[T <: Appendable[T], F <: Format[T]](format: F) { + + def _display_(o: Any)(implicit m: Manifest[T]): T = { + o match { + case escaped if escaped != null && escaped.getClass == m.erasure => escaped.asInstanceOf[T] + case () => format.raw("") + case None => format.raw("") + case Some(v) => _display_(v) + case xml: scala.xml.NodeSeq => format.raw(xml.toString) + case escapeds: TraversableOnce[_] => escapeds.foldLeft(format.raw(""))(_ + _display_(_)) + case escapeds: Array[_] => escapeds.foldLeft(format.raw(""))(_ + _display_(_)) + case string: String => format.escape(string) + case v if v != null => _display_(v.toString) + case _ => format.raw("") + } + } + + } + + /* ------ */ + + object TemplateMagic { + + // --- UTILS + + def defining[T](t: T)(handler: T => Any) = { + handler(t) + } + + def using[T](t: T) = t + + // --- IF + + implicit def iterableToBoolean(x: Iterable[_]) = x != null && !x.isEmpty + implicit def optionToBoolean(x: Option[_]) = x != null && x.isDefined + implicit def stringToBoolean(x: String) = x != null && !x.isEmpty + + // --- JAVA + + implicit def javaCollectionToScala[T](x: java.lang.Iterable[T]) = { + import scala.collection.JavaConverters._ + x.asScala + } + + // --- DEFAULT + + case class Default(default: Any) { + def ?:(x: Any) = x match { + case "" => default + case Nil => default + case false => default + case 0 => default + case None => default + case _ => x + } + } + + implicit def anyToDefault(x: Any) = Default(x) + + // --- DATE + + class RichDate(date: java.util.Date) { + + def format(pattern: String) = { + new java.text.SimpleDateFormat(pattern).format(date) + } + + } + + implicit def richDate(date: java.util.Date) = new RichDate(date) + + // --- STRING + + class RichString(string: String) { + + def when(predicate: => Boolean) = { + predicate match { + case true => string + case false => "" + } + } + + } + + implicit def richString(string: String) = new RichString(string) + + } + +}