Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial import.

git-svn-id: https://liftweb.googlecode.com/svn/trunk/liftweb@3 a1e4567e-5129-0410-9a71-e7582cf7e72e
  • Loading branch information...
commit 7ad63bd2146f2a4f028ba83db02f63fda2236b98 1 parent 6e3ea90
pollak authored
Showing with 4,680 additions and 0 deletions.
  1. +12 −0 .classpath
  2. +30 −0 .project
  3. +3 −0  .settings/org.eclipse.jdt.core.prefs
  4. +178 −0 LICENSE.txt
  5. +10 −0 OtherLicensedWorks.txt
  6. BIN  lib/commons-codec-1.3.jar
  7. BIN  lib/scala-actors.jar
  8. BIN  lib/servlet-api.jar
  9. +63 −0 lift/pom.xml
  10. +163 −0 lift/src/main/scala/net/liftweb/http/ControllerActor.scala
  11. +16 −0 lift/src/main/scala/net/liftweb/http/ControllerFactory.scala
  12. +225 −0 lift/src/main/scala/net/liftweb/http/Page.scala
  13. +49 −0 lift/src/main/scala/net/liftweb/http/ProtoController.scala
  14. +9 −0 lift/src/main/scala/net/liftweb/http/RedirectException.scala
  15. +53 −0 lift/src/main/scala/net/liftweb/http/RequestState.scala
  16. +50 −0 lift/src/main/scala/net/liftweb/http/RequestType.scala
  17. +12 −0 lift/src/main/scala/net/liftweb/http/Response.scala
  18. +139 −0 lift/src/main/scala/net/liftweb/http/Servlet.scala
  19. +440 −0 lift/src/main/scala/net/liftweb/http/Session.scala
  20. +13 −0 lift/src/main/scala/net/liftweb/http/XmlResponse.scala
  21. +101 −0 lift/src/main/scala/net/liftweb/mapper/DB.scala
  22. +144 −0 lift/src/main/scala/net/liftweb/mapper/MappedField.scala
  23. +104 −0 lift/src/main/scala/net/liftweb/mapper/Mapper.scala
  24. +413 −0 lift/src/main/scala/net/liftweb/mapper/MetaMapper.scala
  25. +68 −0 lift/src/main/scala/net/liftweb/mapper/Safe.scala
  26. +32 −0 lift/src/main/scala/net/liftweb/proto/MappedEmail.scala
  27. +93 −0 lift/src/main/scala/net/liftweb/proto/MappedInt.scala
  28. +114 −0 lift/src/main/scala/net/liftweb/proto/MappedPassword.scala
  29. +55 −0 lift/src/main/scala/net/liftweb/proto/MappedString.scala
  30. +26 −0 lift/src/main/scala/net/liftweb/proto/MappedTextarea.scala
  31. +27 −0 lift/src/main/scala/net/liftweb/proto/ProtoUser.scala
  32. +28 −0 lift/src/main/scala/net/liftweb/tests/RegExTests.scala
  33. +1,509 −0 lift/src/main/scala/net/liftweb/textile/TextileParser.scala
  34. +329 −0 lift/src/main/scala/net/liftweb/util/Helpers.scala
  35. +85 −0 lift/src/main/scala/net/liftweb/util/RE.scala
  36. +80 −0 pom.xml
  37. +7 −0 readme.txt
View
12 .classpath
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="lift/src/main/scala"/>
+ <classpathentry kind="con" path="ch.epfl.lamp.sdt.launching.SCALA_CONTAINER"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="lib" path="lib/commons-codec-1.3.jar"/>
+ <classpathentry kind="lib" path="lib/scala-actors.jar"/>
+ <classpathentry kind="lib" path="lib/servlet-api.jar"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3.8.1"/>
+ <classpathentry kind="output" path="bin"/>
+ <classpathentry kind="lib" path="scala-output"/>
+</classpath>
View
30 .project
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>lift</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>ch.epfl.lamp.sdt.core.scalabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>ch.epfl.lamp.sdt.core.scalanature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+ <linkedResources>
+ <link>
+ <name>scala-output</name>
+ <type>2</type>
+ <location>/home/dpp/lang/workspace/lift/bin</location>
+ </link>
+ </linkedResources>
+</projectDescription>
View
3  .settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,3 @@
+#Tue Feb 13 16:20:15 PST 2007
+eclipse.preferences.version=1
+org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch,*.scala,*.scala,*.scala,*.scala
View
178 LICENSE.txt
@@ -0,0 +1,178 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+
View
10 OtherLicensedWorks.txt
@@ -0,0 +1,10 @@
+Lift is distributed under an Apache License V2.0 (See LICENSE.txt)
+
+*** Tomcat Servlet JAR file (lib/servlet-api.jar) ***
+*** Apache Commons Codec (lib/commons-codec-1.3.jar) ***
+Licensed under the Apache License v2.0
+
+*** Scala Actors Library (lib/scala-actors.jar) ***
+Copyright EPFL and licensed under a BSD-style license
+http://www.scala-lang.org/downloads/license.html
+
View
BIN  lib/commons-codec-1.3.jar
Binary file not shown
View
BIN  lib/scala-actors.jar
Binary file not shown
View
BIN  lib/servlet-api.jar
Binary file not shown
View
63 lift/pom.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <artifactId>lift</artifactId>
+ <groupId>net.liftweb</groupId>
+ <version>0.1.0</version>
+ </parent>
+
+ <groupId>net.liftweb</groupId>
+ <artifactId>lift-core</artifactId>
+ <packaging>jar</packaging>
+ <name>Lift Core</name>
+ <version>0.1.0</version>
+
+ <dependencies>
+ <dependency>
+ <groupId>scala</groupId>
+ <artifactId>scala-compiler</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>scala</groupId>
+ <artifactId>scala-library</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>scala</groupId>
+ <artifactId>scala-actors</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ <version>2.4</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ <version>1.3</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>iodp.usio</groupId>
+ <artifactId>maven-scala-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>compile</phase>
+ <goals>
+ <goal>compile</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
View
163 lift/src/main/scala/net/liftweb/http/ControllerActor.scala
@@ -0,0 +1,163 @@
+package net.liftweb.http
+
+/* *\
+ (c) 2007 WorldWide Conferencing, LLC
+ Distributed under an Apache License
+ http://www.apache.org/licenses/LICENSE-2.0
+\* */
+
+import scala.actors.{Actor, Exit}
+import scala.actors.Actor._
+import net.liftweb.util.Helpers._
+import scala.xml.{NodeSeq, Text}
+import scala.collection.immutable.TreeMap
+
+trait ControllerActor extends Actor {
+ private object Never
+
+ val uniqueId = randomString(20)
+
+ private var globalState: Map[String, Any] = _
+
+ private var owner_i: Page = _
+ private var defaultXml_i: NodeSeq = _
+ private var localFunctionMap: Map[String, (List[String]) => boolean] = _
+
+ private var askingWho: ControllerActor = _
+ private var whosAsking: Actor = _
+ private var answerWith: (Any) => boolean = _
+
+ def act = loop
+
+ def loop : Unit = {
+ react(composeFunction)
+ }
+
+ def highPriority : PartialFunction[Any, Unit] = {
+ case Never => loop
+ }
+
+ def lowPriority : PartialFunction[Any, Unit] = {
+ case Never => loop
+ }
+
+ def mediumPriority : PartialFunction[Any, Unit] = {
+ case SetupController(owner, defaultXml) => {
+ owner_i = owner
+ defaultXml_i = defaultXml
+ Console.println("Hey... I'm in the setup")
+ localSetup
+ loop
+ }
+
+ case r @ Render(state) => {
+ if (askingWho != null) {
+ askingWho forward r
+ } else {
+ this.globalState = state
+ reply(buildRendered(render))
+ this.globalState = null
+ }
+ loop
+ }
+
+ case ActionMessage(name, value, _, replyTo) => {
+ replyTo match {
+ case Some(p:Page) => p ! StartedUpdate(uniqueId)
+ }
+ localFunctionMap.get(name) match {
+ case Some(f) => {
+
+ if (f(value)) reRender
+ }
+ case _ => None
+ }
+ replyTo match {
+ case Some(p:Page) => p ! FinishedUpdate(uniqueId)
+ }
+
+ loop
+ }
+
+ case Exit(_, reason) => {
+ self.exit(reason)
+ loop
+ }
+
+ case Ask(what, who) => {
+ startQuestion(what)
+ whosAsking = who
+ loop
+ }
+
+ case Answer(what) => {
+ askingWho.unlink(self)
+ askingWho ! Exit(self, "bye")
+ askingWho = null
+ if (answerWith(what)) reRender
+ answerWith = null
+ reply("Done")
+ loop
+ }
+ }
+
+ def render: XmlAndMap
+
+ def compute: Map[String, Any] = Map.empty[String, Any]
+
+ def reRender = {
+ if (owner != null) owner ! buildRendered(render)
+ }
+
+ def startQuestion(what: Any) {}
+
+ def localSetup {}
+
+ def composeFunction = composeFunction_i
+
+ val composeFunction_i = {
+ val cleanUp: PartialFunction[Any, Unit] = {
+ case _ => loop
+ }
+ highPriority orElse mediumPriority orElse lowPriority orElse cleanUp
+ }
+
+ def owner = owner_i
+ def defaultXml = defaultXml_i
+
+ private def buildRendered(in: XmlAndMap): Rendered = {
+ localFunctionMap = in.map
+ val newMap = TreeMap.Empty[String, ActionMessage] ++ in.map.keys.map{key => {key, ActionMessage(key, Nil, self, None)}}
+ Rendered(in.xml, newMap)
+ }
+
+ def ask(who: ControllerActor, what: Any)(answerWith: (Any) => boolean) {
+ who.start
+ who.link(self)
+ who ! SetupController(owner, Text(""))
+ askingWho = who
+ this.answerWith = answerWith
+ who ! Ask(what, self)
+ }
+
+ def answer(answer: Any) {
+ whosAsking !? Answer(answer)
+ whosAsking = null
+ reRender
+ }
+
+}
+
+sealed abstract class ControllerMessage
+
+case class Render(state: Map[String, Any]) extends ControllerMessage
+case class ActionMessage(name: String, value: List[String], target: Actor, sender: Option[Page]) extends ControllerMessage
+case class Rendered(xml : NodeSeq, messages: Map[String, ActionMessage] ) extends ControllerMessage
+case class SetupController(owner: Page, defaultXml: NodeSeq) extends ControllerMessage
+case class XmlAndMap(xml: NodeSeq, map: Map[String, Function1[List[String], boolean]]) extends ControllerMessage
+case class Ask(what: Any, who: Actor) extends ControllerMessage
+case class Answer(what: Any) extends ControllerMessage
+case class StartedUpdate(id: String) extends ControllerMessage
+case class FinishedUpdate(id: String) extends ControllerMessage
+
+
View
16 lift/src/main/scala/net/liftweb/http/ControllerFactory.scala
@@ -0,0 +1,16 @@
+package net.liftweb.http
+
+/* *\
+ (c) 2007 WorldWide Conferencing, LLC
+ Distributed under an Apache License
+ http://www.apache.org/licenses/LICENSE-2.0
+\* */
+
+import scala.xml.Node
+
+/**
+ * Constructs controllers
+ */
+trait ControllerFactory {
+ def construct(contType: Option[Seq[Node]]): Option[ControllerActor]
+}
View
225 lift/src/main/scala/net/liftweb/http/Page.scala
@@ -0,0 +1,225 @@
+package net.liftweb.http
+
+/* *\
+ (c) 2007 WorldWide Conferencing, LLC
+ Distributed under an Apache License
+ http://www.apache.org/licenses/LICENSE-2.0
+ \* */
+
+import scala.actors.Actor
+import scala.actors.Actor._
+import scala.xml.{NodeSeq, Elem, Node, Comment}
+import scala.collection.immutable.{TreeMap, ListMap}
+import scala.collection.mutable.{HashMap}
+import net.liftweb.util.Helpers._
+
+class Page extends Actor {
+ private var pageXml : NodeSeq = _
+ private var theSession: Session = _
+ private var globalState: TreeMap[String, Any] = _
+ private var localState = new TreeMap[String, Any]
+ private var request: RequestState = _
+
+ private var messageCallback: Map[String, ActionMessage] = TreeMap.Empty[String, ActionMessage]
+
+ def act = loop
+
+ def loop {
+ react(dispatcher)
+ }
+
+ def dispatcher: PartialFunction[Any, Unit] = {
+ case "shutdown" => self.exit("shutdown")
+ case SetupPage(page, session) => {
+ pageXml = page
+ this.theSession = session
+ loop
+ }
+
+ case RenderPage(state, sessionState, sender) =>
+ this.globalState = sessionState
+ this.request = state
+ try {
+ val resp : Response = try {
+ processParameters(state)
+
+ Response(processControllers(pageXml), TreeMap.empty, 200)
+
+ } catch {
+ case rd : RedirectException => {
+ Response(<html><body>{state.uri} Not Found</body></html>,
+ ListMap("Location" -> rd.to),
+ 302)
+ }
+ }
+
+ sender ! RenderedPage(state, resp, sender)
+ } finally {
+ this.globalState = null
+ this.request = null
+ }
+ loop
+
+
+ case _ => loop
+ }
+
+ def apply[T](name: String): Option[T] = {
+ (localState.get(name) match {
+ case None => globalState.get(name)
+ case s @ Some(_) => s
+ }) match {
+ case None => None
+ case Some(s) if (s.isInstanceOf[T]) => Some(s.asInstanceOf[T])
+ case _ => None
+ }
+ }
+
+ def update(key: String, value: Any) = {
+ localState= (localState(key) = value)
+ }
+
+ def globalUpdate(key: String, value: Any) = {
+ globalState = (globalState(key) = value)
+ theSession ! SetGlobal(key, value)
+ }
+
+ def globalRemove(key: String) = {
+ globalState = (globalState - key)
+ theSession ! UnsetGlobal(key)
+ }
+
+
+ private def processControllers(xml : NodeSeq) : NodeSeq = {
+ xml.flatMap {
+ v =>
+ v match {
+ case Elem("mondo", "controller", attr @ _, _, kids @ _*) => {findController(attr.get("type"), attr.get("name"), attr.get("factory"), processControllers(kids))}
+ case Elem(_,_,_,_,_*) => {Elem(v.prefix, v.label, v.attributes, v.scope, processControllers(v.child) : _*)}
+ case _ => {v}
+ }
+ }
+ }
+
+ private var controllers = TreeMap.Empty[String, ControllerActor]
+
+ private def locateController(name : String, contType: Option[Seq[Node]], factory: Option[Seq[Node]], kids: NodeSeq) : Option[ControllerActor] = {
+ controllers.get(name) match {
+ case s @ Some(c : ControllerActor) => s
+ case None => {
+ searchFactoryForController(contType, factory) match {
+ case None => None
+ case Some(ret) =>
+ ret.start
+ ret ! SetupController(this, kids)
+ controllers = controllers(name) = ret
+ Console.println("Updated our list of controllers")
+ Some(ret)
+ }
+ }
+ }
+ }
+
+ private def findFactory(factory: Option[Seq[Node]]) : Option[ControllerFactory] = {
+ if (factory == None) None
+ else {
+ findClass(factory.get.text, "mondo.app.factory" :: Nil, {c : Class => classOf[ControllerFactory].isAssignableFrom(c)}) match {
+ case None => None
+ case Some(cls) => {
+ try {
+ Some(cls.newInstance.asInstanceOf[ControllerFactory])
+ } catch {
+ case _ => None
+ }
+ }
+ }
+ }
+ }
+
+ private def findControllerByType(contType: Option[Seq[Node]]): Option[ControllerActor] = {
+ if (contType == None) None
+ else {
+ Console.println("Looking for "+contType)
+ findClass(contType.get.text, "mondo.app.controller" :: Nil, {c : Class => classOf[ControllerActor].isAssignableFrom(c)}) match {
+ case None => None
+ case Some(cls) => {
+ Console.println("Found "+cls)
+ try {
+ val ret = Some(cls.newInstance.asInstanceOf[ControllerActor])
+ Console.println("Ret is "+ret)
+ ret
+ } catch {
+ case _ => None
+ }
+ }
+ }
+ }
+ }
+
+ private def searchFactoryForController(contType: Option[Seq[Node]], factory: Option[Seq[Node]]) : Option[ControllerActor] = {
+ if (contType == None && factory == None) None
+ else {
+ findFactory(factory) match {
+ case Some(f) => {
+ f.construct(contType)
+ }
+ case None => {
+ findControllerByType(contType) match {
+ case None => None
+ case s @ Some(c : ControllerActor) => s
+ }
+ }
+ }
+ }
+ }
+
+ private def findController(controllerType : Option[Seq[Node]], controllerName : Option[Seq[Node]], controllerFactory : Option[Seq[Node]], kids : NodeSeq) : NodeSeq = {
+ if (controllerType == None && controllerName == None) Comment("FIX"+"ME No controller name specified") concat kids
+ else {
+ val actualName = controllerName match {
+ case Some(s) => s.text
+ case None => controllerType.get.text
+ }
+
+ try {
+ locateController(actualName, controllerType, controllerFactory, kids) match {
+ case None => Comment("FIX"+"ME Can't find controller "+actualName) concat kids
+ case Some(controller) => <span id={controller.uniqueId}>{
+ (controller !? (600L, Render(globalState ++ localState.keys.map{k => {k, localState(k)}}))) match {
+ case Some(view: Rendered) => updateCallbacks(view.messages) ; view.xml
+ case s2 @ _ => Comment("FIX"+"ME controller "+actualName+" timeout") concat kids
+ }
+ }</span>
+ }
+ } catch {
+ case e @ _ => {
+
+ <pre>{e.getMessage+"\n"}
+ {e.getStackTrace.toList.map{st => st.toString+"\n"}}
+ </pre>
+ }
+ }
+ }
+ }
+
+ private def updateCallbacks(in: Map[String, ActionMessage]) {
+ messageCallback = messageCallback ++ in
+ }
+
+ private def processParameters(r: RequestState) {
+ r.paramNames.filter{n => messageCallback.contains(n)}.foreach{
+ n =>
+ val v = messageCallback(n)
+ v.target !? (100l, ActionMessage(v.name, r.params(n), self, Some(this)))
+ }
+ messageCallback = TreeMap.Empty[String, ActionMessage]
+ }
+}
+
+abstract class PageMessage
+
+case class SetupPage(page: NodeSeq, session: Session) extends PageMessage
+case class Perform(localFunc: String, params: List[String]) extends PageMessage
+//case class Render extends PageMessage
+//case class Rendering(info: String, tpe: String) extends PageMessage
+// case class ComponentUpdated(view: NodeSeq, component: Component) extends PageMessage
View
49 lift/src/main/scala/net/liftweb/http/ProtoController.scala
@@ -0,0 +1,49 @@
+package net.liftweb.http
+
+/* *\
+ (c) 2007 WorldWide Conferencing, LLC
+ Distributed under an Apache License
+ http://www.apache.org/licenses/LICENSE-2.0
+\* */
+
+import scala.collection.immutable.TreeMap
+
+/**
+ * The base trait of Controllers that handle pre-view requests
+ */
+trait ProtoController {
+ var request: RequestState = _
+ var session = TreeMap.empty[String, Any]
+
+ def params(name: String): Option[String] = {
+ request.params.get(name) match {
+ case None => None
+ case Some(nl) => nl.take(1) match {
+ case Nil => None
+ case l => Some(l.head)
+ }
+ }
+ }
+
+ def post_? : boolean = request.post_?
+
+ /*
+ def apply(name: String): Option[Any] = {
+ session.get(name)
+ }*/
+
+ def apply[T](name: String): Option[T] = {
+ session.get(name) match {
+ case None => None
+ case Some(n) => {
+ if (n.isInstanceOf[T]) Some(n.asInstanceOf[T])
+ else None
+ }
+ }
+ }
+
+ def update(name: String, value: Any) {
+ session = (session(name) = value)
+ // FIXME send the change to the actual session
+ }
+}
View
9 lift/src/main/scala/net/liftweb/http/RedirectException.scala
@@ -0,0 +1,9 @@
+package net.liftweb.http
+
+/* *\
+ (c) 2007 WorldWide Conferencing, LLC
+ Distributed under an Apache License
+ http://www.apache.org/licenses/LICENSE-2.0
+\* */
+
+class RedirectException(val msg : String,val to : String) extends Exception(msg)
View
53 lift/src/main/scala/net/liftweb/http/RequestState.scala
@@ -0,0 +1,53 @@
+package net.liftweb.http
+
+/* *\
+ (c) 2007 WorldWide Conferencing, LLC
+ Distributed under an Apache License
+ http://www.apache.org/licenses/LICENSE-2.0
+\* */
+
+import javax.servlet.http.{HttpServlet, HttpServletRequest , HttpServletResponse}
+import scala.collection.Map
+// import scala.collection.mutable.HashMap
+import scala.collection.immutable.TreeMap
+import net.liftweb.util.Helpers._
+import java.io.InputStream
+
+object RequestState {
+ def apply(request: HttpServletRequest, resourceFinder: (String) => InputStream): RequestState = {
+ val session = request.getSession
+
+ val paramNames = enumToStringList(request.getParameterNames).sort{(s1, s2) => s1 < s2}
+ val tmp = paramNames.map{n => {n, request.getParameterValues(n).toList}}
+ val params = TreeMap.Empty[String, List[String]] ++ paramNames.map{n => {n, request.getParameterValues(n).toList}}
+ val uri = request.getRequestURI.substring(request.getContextPath.length)
+ val contextPath = request.getContextPath
+ val path = uri.split("/").toList.filter{n => n != null && n.length > 0}
+
+ new RequestState(paramNames, params,uri,path,contextPath, RequestType(request), resourceFinder,
+ path.take(1) match {case List("rest") | List("soap") => true; case _ => false})
+ }
+}
+
+class RequestState(val paramNames: List[String],val params: Map[String, List[String]],
+ val uri: String,val path: List[String],
+ val contextPath: String,
+ val requestType: RequestType,
+ val resourceFinder: (String) => InputStream,
+ val webServices_? : boolean) {
+
+ val section = path(0) match {case null => "default"; case s => s}
+ val view = path(1) match {case null => "index"; case s @ _ => s}
+ val id = pathParam(0)
+ def pathParam(n: int) = head(path.drop(n + 2), "")
+ def path(n: int) = head(path.drop(n), null)
+
+ def createNotFound = {
+ Response(<html><body>The Requested URL {this.uri} was not found on this server</body></html>, TreeMap.Empty, 404)
+ }
+
+ def post_? = requestType.post_?
+ def get_? = requestType.get_?
+ def put_? = requestType.put_?
+ def ajax_? = requestType.ajax_?
+}
View
50 lift/src/main/scala/net/liftweb/http/RequestType.scala
@@ -0,0 +1,50 @@
+package net.liftweb.http
+
+/* *\
+ (c) 2007 WorldWide Conferencing, LLC
+ Distributed under an Apache License
+ http://www.apache.org/licenses/LICENSE-2.0
+\* */
+
+import javax.servlet.http.{HttpServletRequest}
+
+abstract class RequestType(val ajax_? : boolean) {
+ def post_? : boolean = false
+ def get_? : boolean = false
+ def head_? : boolean = false
+ def put_? : boolean = false
+ def delete_? : boolean = false
+}
+
+case class GetRequest(aj: boolean) extends RequestType(aj) {
+ override def get_? = true
+}
+case class PostRequest(aj: boolean) extends RequestType(aj) {
+ override def post_? = true
+}
+case class HeadRequest(aj: boolean) extends RequestType(aj) {
+ override def head_? = true
+}
+case class PutRequest(aj: boolean) extends RequestType(aj) {
+ override def put_? = true
+}
+case class DeleteRequest(aj: boolean) extends RequestType(aj) {
+ override def delete_? = true
+}
+
+object RequestType {
+ def apply(req: HttpServletRequest): RequestType = {
+ val ajax = req.getHeader("x-requested-with") match {
+ case null => false
+ case s @ _ => s.toUpperCase == "XMLHttpRequest".toUpperCase
+ }
+
+ req.getMethod.toUpperCase match {
+ case "GET" => GetRequest(ajax)
+ case "POST" => PostRequest(ajax)
+ case "HEAD" => HeadRequest(ajax)
+ case "PUT" => PutRequest(ajax)
+ case "DELETE" => DeleteRequest(ajax)
+ }
+ }
+}
View
12 lift/src/main/scala/net/liftweb/http/Response.scala
@@ -0,0 +1,12 @@
+package net.liftweb.http
+
+/* *\
+ (c) 2007 WorldWide Conferencing, LLC
+ Distributed under an Apache License
+ http://www.apache.org/licenses/LICENSE-2.0
+\* */
+
+import scala.xml.NodeSeq
+import scala.collection.immutable.Map
+
+case class Response(out : NodeSeq, headers : Map[String, String], code : int)
View
139 lift/src/main/scala/net/liftweb/http/Servlet.scala
@@ -0,0 +1,139 @@
+package net.liftweb.http;
+
+/* *\
+ (c) 2007 WorldWide Conferencing, LLC
+ Distributed under an Apache License
+ http://www.apache.org/licenses/LICENSE-2.0
+\* */
+
+ import javax.servlet.http.{HttpServlet, HttpServletRequest , HttpServletResponse, HttpSession}
+ import scala.collection.immutable.{Map, ListMap}
+ import scala.collection.mutable.{HashSet, HashMap}
+ import java.net.URLDecoder
+ import scala.xml.{Node, NodeSeq, Elem, MetaData, Null, UnprefixedAttribute, XML, Comment}
+ import scala.xml.transform._
+ import scala.actors._
+ import scala.actors.Actor._
+ import net.liftweb.util.Helpers._
+
+
+ /**
+ * An implementation of HttpServlet. Just drop this puppy into
+ * your Java web container, do a little magic in web.xml, and
+ * ta-da, you've got a scala-powered Servlet
+ *
+ */
+ class Servlet extends HttpServlet {
+ private val actorNameConst = "the_actor"
+
+ /**
+ * Forward the GET request to the POST handler
+ */
+ override def doGet(request : HttpServletRequest , response : HttpServletResponse ) = {
+ if (isExistingFile_?(request)) doServiceFile(request, response) else
+ doService(request, response, RequestType(request ))
+ }
+
+ /**
+ * Is the file an existing file in the WAR?
+ */
+ private def isExistingFile_?(request : HttpServletRequest) : boolean = {
+ goodPath_?(request.getRequestURI) &&
+ (getServletContext.getResource(request.getRequestURI.substring(request.getContextPath.length)) ne null)
+ }
+
+ private def doServiceFile(request : HttpServletRequest , response : HttpServletResponse) = {
+ val in = getServletContext.getResourceAsStream(request.getRequestURI.substring(request.getContextPath.length))
+
+ val li = request.getRequestURI.lastIndexOf('.')
+ if (li != -1) {
+ response.setContentType(request.getRequestURI.substring(li + 1) match {
+ case "js" => "text/javascript"
+ case "css" => "text/css"
+ case "png" => "image/png"
+ case _ => "text/html"
+ })
+ }
+ val out = response.getOutputStream
+ val ba = new Array[byte](2048)
+
+ def readAndWrite {
+ val len = in.read(ba)
+ if (len >= 0) {
+ if (len > 0) {
+ out.write(ba, 0, len)
+ }
+ readAndWrite
+ }
+ }
+
+ // a "while-less" read and write loop
+ readAndWrite
+
+ out.flush
+ }
+
+
+ def getActor(session: HttpSession) = {
+ session.getValue(actorNameConst) match {
+ case r : Session => r
+ case _ => {val ret = new Session; ret.start; ret ! "Hello"; session.putValue(actorNameConst, ret); ret}
+ }
+ }
+
+ override def service(req: HttpServletRequest,resp: HttpServletResponse) {
+ Console.println("Servicing...")
+ req.getMethod.toUpperCase match {
+ case "GET" => doGet(req, resp)
+ case _ => doService(req, resp, RequestType(req))
+ }
+ }
+
+ /**
+ * Service the HTTP request
+ */
+ def doService(request:HttpServletRequest , response: HttpServletResponse, requestType: RequestType ) : unit = {
+ val session = RequestState(request, getServletContext.getResourceAsStream)
+
+ val sessionActor = getActor(request.getSession)
+ Console.println("Sending request")
+
+ if (false) {
+ val he = request.getHeaderNames
+ while (he.hasMoreElements) {
+ val e = he.nextElement.asInstanceOf[String];
+ val hv = request.getHeaders(e)
+ while (hv.hasMoreElements) {
+ val v = hv.nextElement
+ Console.println(e+": "+v)
+ }
+ }
+ }
+
+ // FIXME -- in Jetty, use continuations
+ sessionActor ! session
+
+ val resp = receiveWithin(10000l) {
+ case r @ Response(_,_,_) => Some(r)
+ case Some(r : Response) => Some(r)
+ case TIMEOUT => None
+ } match {
+ case Some(r: Response) => r
+ case _ => {Console.println("Got a None back"); session.createNotFound}
+ }
+
+ val bytes = resp.out.toString.getBytes("UTF-8")
+ val len = bytes.length
+ // insure that certain header fields are set
+ val header = insureField(resp.headers, List({"Content-Type", "text/html"},
+ {"Content-Encoding", "UTF-8"},
+ {"Content-Length", len.toString}));
+
+ // send the response
+ header.elements.foreach {n => response.setHeader(n._1, n._2)}
+ response setStatus resp.code
+ response.getOutputStream.write(bytes)
+ }
+ }
+
+
View
440 lift/src/main/scala/net/liftweb/http/Session.scala
@@ -0,0 +1,440 @@
+package net.liftweb.http
+
+/* *\
+ (c) 2007 WorldWide Conferencing, LLC
+ Distributed under an Apache License
+ http://www.apache.org/licenses/LICENSE-2.0
+ \* */
+
+import scala.actors.Actor
+import scala.actors.Actor._
+import javax.servlet.http.{HttpSessionBindingListener, HttpSessionBindingEvent}
+import scala.collection.Map
+import scala.collection.mutable.{HashMap}
+import scala.collection.immutable.{TreeMap}
+import scala.xml.{NodeSeq}
+import net.liftweb.util.Helpers._
+import java.lang.reflect.{Method, Modifier, InvocationTargetException}
+import scala.collection.immutable.{ListMap}
+import scala.xml.{Node, NodeSeq, Elem, MetaData, Null, UnprefixedAttribute, XML, Comment}
+
+class Session extends Actor with HttpSessionBindingListener {
+ private val pages = new HashMap[String, Page]
+ private var sessionState: TreeMap[String, Any] = TreeMap.empty
+ private var running_? = false
+
+ /**
+ * What happens when this controller is bound to the HTTP session?
+ */
+ def valueBound(event: HttpSessionBindingEvent) {
+ // ignore this event
+ }
+
+ /**
+ * When the session is unbound the the HTTP controller, stop us
+ */
+ def valueUnbound(event: HttpSessionBindingEvent) {
+ // stop running
+ if (running_?) this ! "shutdown"
+ }
+
+ /**
+ * called when the Actor is started
+ */
+ def act = {
+ running_? = true
+ loop
+ }
+
+ /**
+ * The loop for the actor. Dispatches messages using Scala's event-based Actors
+ */
+ final def loop {
+ react(dispatcher)
+ }
+
+ def dispatcher: PartialFunction[Any, Unit] = {
+ case "shutdown" => self.exit("shutdown")
+ loop
+
+ case r : RequestState =>
+ processRequest(r)
+ loop
+
+ case RenderedPage(state: RequestState, thePage: Response, sender: Actor) =>
+ val updatedPage = fixResponse(thePage, state)
+ sender ! Some(updatedPage)
+ loop
+
+ case SetGlobal(name, value) => sessionState = (sessionState(name) = value); loop
+ case UnsetGlobal(name) => sessionState = (sessionState - name); loop
+ case _ => loop
+ }
+
+ private def processRequest(state: RequestState) = {
+
+ val page = pages.get(state.uri) match {
+ case None => {
+ createPage(state) match {
+ case None => None
+ case s @ Some(p: Page) =>
+ pages(state.uri) = p
+ s
+ }
+ }
+ case Some(p : Page) => {
+ findVisibleTemplate(state.uri, state) match {
+ case Some(xml: NodeSeq) => p ! SetupPage(processSurroundAndInclude(xml, state), this) // FIXME reloads the page... maybe we don't always want to do this
+ case _ => {}
+ }
+ Some(p)
+ }
+ }
+
+ page match {
+ case Some(p) => p ! RenderPage(state, sessionState, sender)
+ case _ => sender ! Some(state.createNotFound)
+ }
+ }
+
+ /**
+ * Create a page based on the uri
+ */
+ private def createPage(state: RequestState): Option[Page] = {
+ findVisibleTemplate(state.uri, state) match {
+ case None => None
+ case Some(xml: NodeSeq) => {
+ val ret = new Page // FIXME we really want a Page factory here so we can build other Page types
+ ret.link(self)
+ ret.start
+ val realXml : NodeSeq = processSurroundAndInclude(xml, state)
+ ret ! SetupPage(realXml, this)
+ Some(ret)
+ }
+ }
+ }
+
+ private def findVisibleTemplate(name : String, session : RequestState) : Option[NodeSeq] = {
+ val splits = name.split("/").toList.drop(1) match {
+ case s @ _ if (!s.isEmpty) => s.filter {a => !a.startsWith("_") && !a.startsWith(".") && a.toLowerCase.indexOf("template") == -1}
+ case _ => List("index")
+ }
+
+ findAnyTemplate(splits, session)
+ }
+
+ private def findTemplate(name : String, session : RequestState) : Option[NodeSeq] = {
+ val splits = (if (name.startsWith("/")) name else "/"+name).split("/").toList.drop(1) match {
+ case s @ _ if (!s.isEmpty) => s
+ case _ => List("index")
+ }
+
+ findAnyTemplate(splits, session) match {
+ case s @ Some(_) => s
+ case None => findAnyTemplate("template" :: splits, session)
+ }
+
+ }
+
+ def couldBeHtml(in : Map[String, String]) : boolean = {
+ in match {
+ case null => true
+ case _ => {
+ in.get("Content-Type") match {
+ case s @ Some(_) => {s.get.toLowerCase == "text/html"}
+ case None | null => true
+ }
+ }
+ }
+ }
+
+ def fixResponse(resp: Response, state: RequestState): Response = {
+ val newHeaders = fixHeaders(resp.headers, state)
+ val newXml = if (couldBeHtml(resp.headers) && state.contextPath.length > 0) fixHtml(resp.out, state.contextPath) else resp.out
+ Response(resp.out, newHeaders, resp.code)
+ }
+
+ /**
+ * Update any "Location" headers to add the Context path
+ */
+ def fixHeaders(h: Map[String, String], state: RequestState) = {
+ h match {
+ case null => Map.empty[String, String]
+ case _ => Map.empty[String, String] ++ h.map{p =>
+ p match {
+ case {"Location", v} if (v != null && v.startsWith("/")) => {"Location", "/"+state.contextPath+v}
+ case _ => p
+ }
+ }
+ }
+ }
+
+ def fixHtml(in : NodeSeq, path: String) : NodeSeq = {
+ def fixHref(v : Seq[Node]) : String = {
+ val hv = v.elements.next.text
+ if (hv.startsWith("/")) "/"+path+hv
+ else hv
+ }
+
+ def fixAttrs(toFix : String, attrs : MetaData) : MetaData = {
+ if (attrs == Null) Null else
+ if (attrs.key == toFix) new UnprefixedAttribute(toFix,fixHref(attrs.value),fixAttrs(toFix, attrs.next))
+ else attrs.copy(fixAttrs(toFix, attrs.next))
+ }
+ in.map{
+ v =>
+ v match {
+ case <form>{ _* }</form> => {Elem(v.prefix, v.label, fixAttrs("action", v.attributes), v.scope, fixHtml(v.child, path) : _* )}
+ case <a>{ _* }</a> => {Elem(v.prefix, v.label, fixAttrs("href", v.attributes), v.scope, fixHtml(v.child, path) : _* );}
+ case <link/> => {Elem(v.prefix, v.label, fixAttrs("href", v.attributes), v.scope, fixHtml(v.child, path) : _* )}
+ /*
+ case <input>{ _* }</input> | <textarea>{ _* }</textarea> =>
+ {v.attribute("name") match {case null => {} ; case s @ _ => {inputNames += s.elements.next.text}}; Elem(v.prefix, v.label, v.attributes, v.scope, fixHtml(v.child) : _*)}
+ */
+ case Elem(_,_,_,_,_*) => {Elem(v.prefix, v.label, v.attributes, v.scope, fixHtml(v.child, path) : _*)}
+ case _ => {v}
+ }
+ }
+ }
+
+ /*
+ private def processTemplate(xml : NodeSeq, session: RequestState) : Option[Response] = {
+ val surrounded = processSurroundAndInclude(xml, session)
+ val withController = processControllers(surrounded)
+
+ Some(Response(withController, ListMap.Empty, 200))
+ }*/
+
+ private def findAndImbed(templateName : Option[Seq[Node]], kids : NodeSeq, session : RequestState) : NodeSeq = {
+ templateName match {
+ case None => Comment("FIX"+"ME No named specified for embedding") concat kids
+ case Some(s) => {
+ findTemplate(s.text, session) match {
+ case None => Comment("FIX"+"ME Unable to find template named "+s.text) concat kids
+ case Some(s) => processSurroundAndInclude(s, session)
+ }
+ }
+ }
+ }
+
+
+ private def processSurroundAndInclude(in : NodeSeq, session : RequestState) : NodeSeq = {
+ in.flatMap{
+ v =>
+ v match {
+ case Elem("mondo", "surround", attr @ _, _, kids @ _*) => {findAndMerge(attr.get("with"), attr.get("at"), processSurroundAndInclude(kids, session), session)}
+ case Elem("mondo", "embed", attr @ _, _, kids @ _*) => {findAndImbed(attr.get("what"), processSurroundAndInclude(kids, session), session)}
+ case Elem(_,_,_,_,_*) => {Elem(v.prefix, v.label, v.attributes, v.scope, processSurroundAndInclude(v.child, session) : _*)}
+ case _ => {v}
+ }
+ }
+ }
+
+ def findAndMerge(templateName : Option[Seq[Node]], at : Option[Seq[Node]], kids : NodeSeq, session : RequestState) : NodeSeq = {
+ val name : String = templateName match {
+ case None => "/template/Default"
+ case Some(s) => if (s.text.startsWith("/")) s.text else "/"+ s.text
+ }
+
+ findTemplate(name, session) match {
+ case None => kids
+ case Some(s) => processBind(processSurroundAndInclude(s, session), at match {case None => "main"; case _ => at.get.text}, kids)
+ }
+ }
+
+
+ /*
+ private def serviceRequestWithTemplate(session: RequestState) : Option[Response] = {
+ findVisibleTemplate(session.uri, session) match {
+ case None => None
+ case Some(s) => {
+ processTemplate(s, session) match {
+ case None => None
+ case s @ Some(_) => s
+ }
+ }
+ }
+ }
+
+ private def serviceRequest(session: RequestState) : Response = {
+ serviceRequestWithTemplate(session) match {
+ case Some(s) => s
+ case None => doRailsStyleDispatch(session) match {
+ case None => session.createNotFound
+ case Some(s) => s
+ }
+ }
+ }*/
+
+
+
+
+ private val suffixes = List("xhtml", "htm", "_html", "xml", "html", "")
+
+ def findAnyTemplate(places : List[String], session : RequestState) : Option[NodeSeq] = {
+ val toTry = (List.range(0, places.length).map {
+ i =>
+ places.take(i + 1).mkString("/","/","")
+ }).flatMap {
+ i =>
+ suffixes.map {
+ s =>
+ i + (if (s.length > 0) "." + s else "")
+ }
+ }
+
+ first(toTry) {
+ s =>
+ session.resourceFinder(s) match {
+ case null => None
+ case s @ _ => Some(s)
+ }
+ } match {
+ case None => lookForClasses(places)
+ case Some(s) => Some(XML.load(s))
+ }
+ }
+
+ def lookForClasses(places : List[String]) : Option[NodeSeq] = {
+ val toTry = List.range(0, places.length).map {
+ i =>
+ "mondo.app.view."+ (places.take(i + 1).mkString("", ".", ""))
+ }
+ first(toTry) {
+ clsName =>
+ try {
+ Class.forName(clsName) match {
+ case null => None
+ case c @ _ => {
+ val inst = c.newInstance
+ c.getMethod("render", null).invoke(inst, null) match {
+ case null | None => None
+ case s : NodeSeq => Some(s)
+ case Some(n : Seq[Node]) => Some(n)
+ case Some(n : NodeSeq) => Some(n)
+ case _ => None
+ }
+ }
+ }
+ } catch {
+ case _ => None
+ }
+ }
+ }
+
+
+
+
+
+ /**
+ * Look for a class that matches the URL pattern and
+ * calls that class
+ */
+ /*
+ private def doRailsStyleDispatch(session : RequestState) : Option[Response] = {
+ // get the first element (the controller)
+ val controller = session.controller
+ // get the second element (the view, and put in 'index' if it doesn't exist)
+ val page = session.view
+
+ // invoke the named controller and view
+ invokeControllerAndView(controller, page, session)
+ }*/
+
+ /*
+ /**
+ * Invoke the named controller and optionally its view
+ */
+ private def invokeControllerAndView(controller : String, page : String, session: RequestState) : Option[Response] = {
+ // invoke the controller
+ invokeController(controller, page, session) match {
+ case Some(r : Response) => Some(r)
+ case s: Response => Some(s) // if it returns a Response, return that response
+ case s: NodeSeq => Some(Response(s, null, 200)) // if it returns an XML (XHTML) object, make a response out of it
+ case false => None // if it returns 'false' we don't go on to running the view
+ case f : Function0[_] => dealWithViewRet(f.apply)
+
+ case s: Map[_, _] => invokeView(controller, page, session) // run a view associated with the controller
+ case _ => None
+ }
+ }
+
+
+
+
+ /**
+ * invoke the named controller
+ */
+ private def invokeController(controller : String, page : String, session: RequestState) : Any = {
+ // find the controller
+ findClass(controller, "mondo.app.controller" :: "base.app.controller" :: "app.controller" :: Nil) match {
+ case None => {null}
+ case Some(clz) => {
+ if (classHasControllerMethod(clz, page)) {
+ invokeControllerMethod(clz, page) // invoke it
+ } else null
+ }
+ }
+ }
+
+ private def dealWithViewRet(in : Any) : Option[Response] = {
+ in match {
+ case Some(r: Response) => Some(r)
+ case s: Response => Some(s) // if it returns a Response, return that response
+ case s: NodeSeq => Some(Response(s, null, 200)) // if it returns an XML (XHTML) object, make a response out of it
+ case _ => None
+ }
+ }*/
+
+ /*
+ // invoke the "render" method on the named view
+ private def invokeView(controller : String, page : String, session: RequestState ) : Option[Response] = {
+ findClass(page, "app.view."+controller :: "base.app.view."+controller :: "mondo.app.view."+controller :: Nil) match {
+ case None => None
+ case Some(v) => invokeRenderMethod(v)
+ }
+ }*/
+
+ /*
+ def invokeRenderMethod(clz : Class) : Option[Response] = {
+ if (clz == null) null else {
+ try {
+ val meth = clz.getMethod("render", null)
+ if (!callableMethod_?(meth) ) null else {
+ dealWithViewRet(meth.invoke(clz.newInstance, null))
+ }
+ } catch {
+ case c : InvocationTargetException => {def findRoot(e : Throwable) : Option[Response] = {if (e.getCause == null || e.getCause == e) throw e else findRoot(e.getCause)}; findRoot(c)}
+ }
+ }
+ }*/
+
+}
+
+abstract class SessionMessage
+
+case class Setup(page: NodeSeq, http: Actor) extends SessionMessage
+case class SetGlobal(name: String, value: Any) extends SessionMessage
+case class UnsetGlobal(name: String) extends SessionMessage
+/**
+ * Sent from a session to a Page to tell the page to render itself and includes the sender that
+ * the rendered response should be sent to
+ */
+case class RenderPage(state: RequestState, sessionState: TreeMap[String, Any], sender: Actor) extends SessionMessage
+
+/**
+ * The response from a page saying that it's been rendered
+ */
+case class RenderedPage(state: RequestState, thePage: Response, sender: Actor) extends SessionMessage
+/*
+case class RequestPending(url: String,
+ pathPrefix: String,
+ attributes: Map[String, List[String]],
+ parameters: Map[String, List[String]],
+ headers: Map[String, List[String]],
+ method: String) extends SessionMessage
+case class RequestUnpending extends SessionMessage
+case class TheRendering(content: Array[byte],
+ headers: List[{String, String}],
+ response: int) extends SessionMessage
+*/
View
13 lift/src/main/scala/net/liftweb/http/XmlResponse.scala
@@ -0,0 +1,13 @@
+package net.liftweb.http
+
+/* *\
+ (c) 2007 WorldWide Conferencing, LLC
+ Distributed under an Apache License
+ http://www.apache.org/licenses/LICENSE-2.0
+ \* */
+
+import scala.xml.NodeSeq
+
+case class XmlResponse(xml: NodeSeq)
+
+
View
101 lift/src/main/scala/net/liftweb/mapper/DB.scala
@@ -0,0 +1,101 @@
+package net.liftweb.mapper
+
+/* *\
+ (c) 2006 WorldWide Conferencing, LLC
+ Distributed under an Apache License
+ http://www.apache.org/licenses/LICENSE-2.0
+ \* */
+
+import java.sql.{Connection, ResultSet, Statement, PreparedStatement}
+import javax.sql.{ DataSource}
+import javax.naming.{Context, InitialContext}
+import scala.collection.mutable._
+
+object DB {
+ private val threadStore = new ThreadLocal
+ private val envContext = (new InitialContext).lookup("java:/comp/env").asInstanceOf[Context];
+
+ private def info : HashMap[String, Pair[Connection, int]] = {
+ threadStore.get.asInstanceOf[HashMap[String, Pair[Connection, int]]] match {
+ case null => {
+ val tinfo = new HashMap[String, Pair[Connection, int]];
+ threadStore.set(tinfo)
+ tinfo
+ }
+ case v @ _ => {v}
+ }
+ }
+
+ private def whichName(name : String) = if (name == null || name.length == 0) "scalawithsails" else name
+
+ private def newConnection(name : String) : Connection = envContext.lookup(whichName(name)).asInstanceOf[DataSource].getConnection
+
+ private def releaseConnection(conn : Connection) : unit = conn.close
+
+ private def getPairForName(name : String) : Pair[Connection, int] = {
+ var ret = info.get(name) match {
+ case None => {Pair(newConnection(name), 1)}
+ case c => {Pair(c.get._1, c.get._2 + 1)}
+ }
+ info += name -> ret
+ ret
+ }
+
+ def releaseConnectionNamed(name : String) {
+ info.get(name) match {
+ case None => {}
+ case c => {c.get match {
+ case Pair(c , 1) => {releaseConnection(c); info -= name}
+ case Pair(c, cnt) => {info(name) = Pair(c,cnt - 1)}
+ }
+ }
+ }
+ }
+
+ def statement[T](db : Connection)(f : (Statement) => T) : T = {
+ val st = db.createStatement
+ try {
+ f(st)
+ } finally {
+ st.close
+ }
+ }
+
+ def exec[T](db : Connection, query : String)(f : (ResultSet) => T) : T = {
+ statement(db) {st =>
+ f(st.executeQuery(query))
+ }
+ }
+
+ def exec[T](statement : PreparedStatement)(f : (ResultSet) => T) : T = {
+ val rs = statement.executeQuery
+ try {
+ f(rs)
+ } finally {
+ rs.close
+ }
+ }
+
+ def prepareStatement[T](statement : String)(f : (PreparedStatement) => T) : T = {
+ use {
+ conn =>
+ val st = conn.prepareStatement(statement)
+ try {
+ f(st)
+ } finally {
+ st.close
+ }
+ }
+ }
+
+ def use[T](f : (Connection) => T) : T = {
+ this.use("")(f)
+ }
+ def use[T](name : String)(f : (Connection) => T) : T = {
+ try {
+ f(getPairForName(name)._1)
+ } finally {
+ releaseConnectionNamed(name)
+ }
+ }
+}
View
144 lift/src/main/scala/net/liftweb/mapper/MappedField.scala
@@ -0,0 +1,144 @@
+package net.liftweb.mapper
+
+/* *\
+ (c) 2006 WorldWide Conferencing, LLC
+ Distributed under an Apache License
+ http://www.apache.org/licenses/LICENSE-2.0
+ \* */
+
+import scala.collection.mutable._
+import java.lang.reflect.Method
+import java.sql.{ResultSet, Types}
+import scala.xml.{Elem, Text, Node, NodeSeq}
+
+
+trait MappedField[T <: Any,O] {
+ def ignoreField = false
+ def defaultValue : T
+
+ /**
+ * Get the JDBC SQL Type for this field
+ */
+ def getTargetSQLType(field : String) : int
+
+ private var _dirty_? = false
+ def dirty_? = !db_index_? && _dirty_?
+ protected def dirty_?(b : boolean) = _dirty_? = b
+
+ /**
+ * override this method in indexed fields to indicate that the field has been saved
+ */
+ def db_index_field_indicates_saved_? = false
+
+ def owner : Mapper[O]
+ final def safe_? : boolean = {
+ owner.safe_?
+ }
+
+ def write_permission_? = false
+ def read_permission_? = false
+
+ /**
+ * pascal-style assignment for syntactic sugar
+ */
+ def :=(v : T) : T = {
+ if (safe_? || write_permission_?) i_set_!(v)
+ v
+ }
+
+ private var _name : String = null
+
+ final def i_name_! = {_name}
+
+ final def name = synchronized {
+ if (_name == null) owner.checkNames
+ _name
+ }
+ final def setName_!(newName : String) : String = {
+ if(safe_?) _name = newName.toLowerCase
+ _name
+ }
+
+ def displayName = name
+
+ def resetDirty {
+ if (safe_?) dirty_?(false)
+ }
+
+ def db_display_? = true
+
+ /**
+ * pascal-style assignment for syntactic sugar
+ */
+ def ::=(v : Any) : T
+
+ /**
+ * Create an input field for the item
+ */
+ def i : NodeSeq = {
+ // <input type='text' name={S.ae({s => this ::= s(0)})} value={get.toString}/>
+ <span>FIXME</span>
+ }
+
+ def set(value : T) : T = {
+ if (safe_? || write_permission_?) i_set_!(value)
+ else throw new Exception("Do not have permissions to set this field")
+ }
+
+ protected def i_set_!(value : T) : T
+
+ def buildSetActualValue(accessor : Method, inst : AnyRef, columnName : String) : (Mapper[O], AnyRef) => unit
+ protected def getField(inst : Mapper[O], meth : Method) = meth.invoke(inst, null).asInstanceOf[MappedField[T,O]];
+
+ def get : T = {
+ if (safe_? || read_permission_?) i_get_!
+ else i_obscure_!(i_get_!)
+ }
+
+ protected def i_get_! : T
+
+ // def changed_? = {i_get_! != defaultValue}
+
+ protected def i_obscure_!(in : T) : T
+
+ def asString = displayName + "=" + get match {
+ case null => ""
+ case v @ _ => v.toString}
+
+ def db_column_count = 1
+
+ def db_column_names(in : String) = List(in.toLowerCase)
+
+ def db_index_? = false
+
+ def db_autogenerated_index_? = false
+
+ def getJDBCFriendly(field : String) : Object
+
+ override def toString : String = {
+ val t = get
+ if (t == null) "" else t.toString
+ }
+
+ def sws_validate : List[ValidationIssues[T,O]] = Nil
+
+ def convertToJDBCFriendly(value: T): Object
+
+ def asHtml : Node = Text(asString)
+}
+
+object MappedField {
+ implicit def mapToType[T, A](in : MappedField[T, A]) : T = in.get
+ implicit def mapFromOption[T, A](in : Option[MappedField[T, A]]) : MappedField[T,A] = in.get
+}
+
+case class ValidationIssues[T,O](field : MappedField[T,O], msg : String)
+
+trait IndexedField[O] {
+ def convertKey(in : String) : Option[O]
+ def convertKey(in : int) : Option[O]
+ def convertKey(in : AnyRef) : Option[O];
+ def makeKeyJDBCFriendly(in : O) : AnyRef
+ def db_display_? = false
+}
+
View
104 lift/src/main/scala/net/liftweb/mapper/Mapper.scala
@@ -0,0 +1,104 @@
+package net.liftweb.mapper
+
+/* *\
+ (c) 2006 WorldWide Conferencing, LLC
+ Distributed under an Apache License
+ http://www.apache.org/licenses/LICENSE-2.0
+ \* */
+
+import scala.collection.mutable._
+import java.lang.reflect.Method
+import java.sql.{ResultSet, Types}
+import scala.xml.{Elem, Node}
+
+trait Mapper[A] {
+ private val secure_# = Safe.next
+ private var was_deleted_? = false
+ def getSingleton : MetaMapper[A];
+ final def safe_? : boolean = {
+ Safe.safe_?(secure_#)
+ }
+
+ def runSafe[T](f : => T) : T = {
+ Safe.runSafe(secure_#)(f)
+ }
+
+ def onFormPost(f : (A) => boolean) : Mapper[A] = {
+ this
+ }
+
+ def save : boolean = {
+ runSafe {
+ getSingleton.save(this)
+ }
+ }
+
+ def htmlLine : Seq[Node] = {
+ getSingleton.doHtmlLine(this)
+ }
+
+ def asHtml : Seq[Node] = {
+ getSingleton.asHtml(this)
+ }
+
+ def sws_validate : List[ValidationIssues[AnyRef, A]] = {
+ runSafe {
+ getSingleton.sws_validate(this)
+ }
+ }
+
+ def generateInputTable : Seq[Node] = {
+ getSingleton.generateInputTable(this)
+ }
+
+ def delete_! : boolean = {
+ if (!db_can_delete_?) false else
+ runSafe {
+ was_deleted_? = getSingleton.delete_!(this)
+ was_deleted_?
+ }
+ }
+
+ def db_can_delete_? : boolean = {
+ getSingleton.saved_?(this) && !was_deleted_?
+ }
+
+ /*
+ def i(f : (Array[String]) => unit) : Elem = {
+ <input type='hidden' name={S.ae(f)} value="na"/>
+ }
+
+ def a(f : (Array[String]) => unit) : String = {
+ S.ae(f)
+ }
+ */
+
+ def dirty_? : boolean = getSingleton.dirty_?(this)
+
+ override def toString = {
+ val ret = new StringBuilder
+
+ ret.append(this.getClass.getName)
+
+ ret.append("={")
+
+ ret.append(getSingleton.appendFieldToStrings(this))
+
+ ret.append("}")
+
+ ret.toString
+ }
+
+ def checkNames : unit = {
+ runSafe {
+ if ((getSingleton ne null)) {
+ getSingleton.checkFieldNames(this)
+ }
+ }
+ }
+}
+
+object Mapper {
+ implicit def fromSome[T](in : Option[Mapper[T]]) : Mapper[T] = in.get
+}
+
View
413 lift/src/main/scala/net/liftweb/mapper/MetaMapper.scala
@@ -0,0 +1,413 @@
+package net.liftweb.mapper
+
+/* *\
+ (c) 2006-2007 WorldWide Conferencing, LLC
+ Distributed under an Apache License
+ http://www.apache.org/licenses/LICENSE-2.0
+ \* */
+
+import scala.collection.mutable._
+import java.lang.reflect.Method
+import java.sql.{ResultSet, Types, PreparedStatement}
+import scala.xml.{Elem, Node, Text}
+import net.liftweb.util.Helpers._
+
+trait MetaMapper[A] extends Mapper[A] {
+ def findAll() : List[A] = {
+ DB.use {db =>
+
+ DB.exec(db, "SELECT * FROM "+tableName_$) { rs =>
+ createInstances(rs)
+ }
+ }
+ }
+
+ def count : int = {
+ DB.use {db =>
+ DB.exec(db, "SELECT COUNT(*) FROM "+tableName_$) {
+ rs =>
+ if (rs.next) rs.getInt(1)
+ else 0
+ }
+ }
+ }
+
+ private def addFields(what: String, by: List[QueryParam]): String = {
+ by match {
+ case Nil => what
+ case x :: xs => {
+ var updatedWhat = what
+ x match {
+ case ByField(field, _) =>
+ (0 to field.db_column_count).foreach {
+ cn =>
+ updatedWhat = updatedWhat + " AND "+field.db_column_names(field.name)(0)+" = ? "
+ }
+ case _ => {}
+ }
+ addFields(updatedWhat, xs)
+ }
+ }
+ }
+
+ private def setStatementFields(st: PreparedStatement, by: List[QueryParam], curPos: int) {
+ by match {
+ case Nil => {}
+ case ByField(field, value) :: xs => {
+ st.setObject(1, field.convertToJDBCFriendly(value), field.getTargetSQLType(field.db_column_names(field.name)(0)))
+ setStatementFields(st, xs, curPos + 1)
+ }
+ case _ :: xs => {
+ setStatementFields(st, xs, curPos)
+ }
+ }
+ }
+
+ def find(by: List[QueryParam]): Option[A] = {
+
+ val query = addFields("SELECT * FROM "+tableName_$+" WHERE TRUE ", by)
+
+ DB.prepareStatement(query) {
+ st =>
+ setStatementFields(st, by, 1)
+ DB.exec(st) {
+ rs =>
+ if (rs.next) createInstance(rs)
+ else None
+ }
+ }
+
+
+ None // FIXME -- find by
+ }
+
+ def delete_!(toDelete : Mapper[A]) : boolean = {
+
+ DB.prepareStatement("DELETE FROM "+tableName_$ +" WHERE "+indexMap+" = ?") {
+ st =>
+ val indVal = indexedField(toDelete)
+ st.setObject(1, indVal.getJDBCFriendly(indexMap), indVal.get.getTargetSQLType(indexMap))
+
+ st.executeUpdate == 1
+ }
+ }
+
+ def find(key: Any) : Option[A] = {
+ key match {
+ case null => None
+ case v => find(v.toString)
+ }
+ }
+
+ def find(key : String) : Option[A] = {
+ if (indexMap eq null) None
+ else {
+ val field = mappedColumnInfo(indexMap).asInstanceOf[MappedField[AnyRef,A] with IndexedField[AnyRef]]
+ val convertedKey = field.convertKey(key)
+ if (convertedKey eq None) None else
+ {
+ DB.prepareStatement("SELECT * FROM "+tableName_$+" WHERE "+indexMap+" = ?") {
+ st =>
+ st.setObject(1, field.makeKeyJDBCFriendly(convertedKey.get), field.getTargetSQLType(indexMap))
+ DB.exec(st) {
+ rs =>
+ if (rs.next) createInstance(rs)
+ else None
+ }
+ }
+ }
+ }
+ }
+
+ private def ??(meth : Method, inst : Mapper[A]) = {
+ meth.invoke(inst, null).asInstanceOf[MappedField[AnyRef, A]]
+ }
+
+ def dirty_?(toTest : Mapper[A]) : boolean = {
+ mappedFieldArray.foreach {
+ mft =>
+ if (??(mft._2, toTest).dirty_?) return true
+ }
+ false
+ }
+
+ def indexedField(toSave : Mapper[A]) : Option[MappedField[AnyRef, A]] = {
+ if (indexMap eq null) None else
+ Some(??(mappedColumns(indexMap), toSave))
+ }
+
+
+ def saved_?(toSave : Mapper[A]) : boolean = {
+ if (indexMap eq null) true else {
+ indexedField(toSave).db_index_field_indicates_saved_?
+ }
+ }
+
+ def whatToSet(toSave : Mapper[A]) : String = {
+ mappedColumns.elements.filter{c => ??(c._2, toSave).dirty_?}.map{c => c._1 + " = ?"}.toList.mkString("", ",", "")
+ }
+
+ def sws_validate(toValidate : Mapper[A]) : List[ValidationIssues[AnyRef, A]] = {
+ var ret : List[ValidationIssues[AnyRef, A]] = Nil
+
+ mappedFieldArray.foreach{f => ret = ret ::: ??(f._2, toValidate).sws_validate}
+
+ ret
+ }
+
+
+ def save(toSave : Mapper[A]) : boolean = {
+ if (saved_?(toSave)) {
+ if (!dirty_?(toSave)) true else {
+ DB.prepareStatement("UPDATE "+tableName_$+" SET "+whatToSet(toSave)+" WHERE "+indexMap+" = ?") {
+ st =>
+ var colNum = 1
+
+ for (val col <- mappedColumns.elements) {
+ val colVal = ??(col._2, toSave)
+ if (!columnIndex_?(col._1) && colVal.dirty_?) {
+ st.setObject(colNum, colVal.getJDBCFriendly(col._1), colVal.getTargetSQLType(col._1))
+ colNum = colNum + 1
+ }
+ }
+
+ val indVal = indexedField(toSave)
+ st.setObject(colNum, indVal.get.getJDBCFriendly(indexMap), indVal.get.getTargetSQLType(indexMap))
+ 1 == st.executeUpdate
+ }
+ }
+ } else {
+ DB.prepareStatement("INSERT INTO "+tableName_$+" ("+columnNamesForInsert+") VALUES ("+columnQueriesForInsert+")") {
+ st =>
+ var colNum = 1
+ for (val col <- mappedColumns.elements) {
+ if (!columnIndex_?(col._1)) {
+ val colVal = col._2.invoke(toSave, null).asInstanceOf[MappedField[AnyRef, A]]
+ st.setObject(colNum, colVal.getJDBCFriendly(col._1), colVal.getTargetSQLType(col._1))
+ colNum = colNum + 1
+ }
+ }
+
+ val updateCnt = st.executeUpdate
+ if (indexMap ne null) {
+ val rs = st.getGeneratedKeys
+ try {
+ if (rs.next) {
+ val meta = rs.getMetaData
+ toSave.runSafe {
+ findApplier(indexMap, rs.getObject(1)) match {
+ case null => {}
+ case ap @ _ => ap.get.apply(toSave, rs.getObject(1))
+
+ }
+ }
+ }
+ } finally {
+ rs.close
+ }
+ }
+
+ updateCnt == 1
+ }
+ }
+ }
+
+ def columnIndex_?(name : String) = {
+
+ mappedColumnInfo.get(name) match {
+ case None => false
+ case v @ _ => v.db_index_?
+ }
+ }
+
+
+ def createInstances(rs: ResultSet) : List[A] = {
+ var ret = new ArrayBuffer[A]
+ while (rs.next()) {
+ createInstance(rs) match {
+ case None => {}
+ case s @ Some(_) => {ret += s.get}
+ }
+ }
+ ret.toList
+ }
+
+ def appendFieldToStrings(in : Mapper[A]) : String = {
+ (mappedFieldArray.elements.map{p => ??(p._2, in).asString}).toList.mkString("", ",", "")
+ }
+
+ def createInstance(rs : ResultSet) : Option[A] = {
+ val ret = createInstance
+ val meta = rs.getMetaData
+ ret.asInstanceOf[Mapper[A]].runSafe {
+ var cnt = meta.getColumnCount
+ while (cnt > 0) {
+ findApplier(meta.getColumnName(cnt), rs.getObject(cnt)) match
+ {
+ case None => {null}
+ case ap @ Some(_) => {ap.get.apply(ret.asInstanceOf[Mapper[A]], rs.getObject(cnt))}
+ }
+ cnt = cnt - 1
+ }
+ }
+ Some(ret)
+ }
+
+ protected def findApplier(name : String, inst : AnyRef) : Option[((Mapper[A], AnyRef) => unit)] = synchronized {
+ val clz = inst match {
+ case null => null
+ case _ => inst.getClass
+ }
+ val look = Pair(name.toLowerCase, if (clz != null) Some(clz) else None)
+ mappedAppliers.get(look) match {
+ case s @ Some(_) => s
+ case None => {
+ val newFunc = createApplier(name, inst, clz)
+ mappedAppliers(look) = newFunc
+ Some(newFunc)
+ }
+ }
+ }
+
+
+ private def createApplier(name : String, inst : AnyRef, clz : Class) : (Mapper[A], AnyRef) => unit = {
+ val accessor = mappedColumns.get(name)
+ if (accessor == null || accessor == None) null else {
+ (accessor.get.invoke(this, null).asInstanceOf[MappedField[AnyRef, A]]).buildSetActualValue(accessor.get, inst, name)
+ }
+ }
+
+
+
+ def checkFieldNames(in : Mapper[A]) : unit = {
+ mappedFieldArray.foreach {
+ f =>
+ val field = ??(f._2, in);
+ field match {
+ case null => {}
+ case _ if (field.i_name_! == null) => field.setName_!(f._1)
+ }
+ }
+ }
+
+
+ def createInstance : A = {
+ val ret = rootClass.newInstance.asInstanceOf[A];
+ val mr = ret.asInstanceOf[Mapper[A]]
+ mr.runSafe {
+ checkFieldNames(mr)
+ }
+
+ ret
+ }
+
+
+ def sws_fieldOrder : List[AnyRef] = Nil
+
+ protected val rootClass = this.getClass.getSuperclass
+
+ private val mappedAppliers = new HashMap[Pair[String, Option[Class]], (Mapper[A], AnyRef) => unit];
+
+ // private val mappedFields = new HashMap[String, Method];
+ private var mappedFieldArray : Array[Triple[String, Method, MappedField[AnyRef,A]]] = null; // new Array[Triple[String, Method, MappedField[Any,Any]]]();
+
+ private val mappedColumns = new HashMap[String, Method];
+
+ // private val mappedFieldInfo = new HashMap[String, MappedField[AnyRef, A]]
+ private val mappedColumnInfo = new HashMap[String, MappedField[AnyRef, A]]
+
+
+
+ private var indexMap : String = null
+
+ {
+
+ this.runSafe {
+ val tArray = new ArrayBuffer[Triple[String, Method, MappedField[AnyRef,A]]]
+ for (val v <- this.getClass.getSuperclass.getMethods) {
+ if (classOf[MappedField[AnyRef, A]].isAssignableFrom(v.getReturnType) && v.getParameterTypes.length == 0) {
+ val mf = v.invoke(this, null).asInstanceOf[MappedField[AnyRef, A]];
+ if (mf != null && !mf.ignoreField) {
+ mf.setName_!(v.getName)
+ val trp = Triple(mf.i_name_!, v, mf)
+ tArray += trp
+ for (val colName <- mf.db_column_names(v.getName)) {
+ mappedColumnInfo(colName) = mf
+ mappedColumns(colName) = v
+ }
+ if (mf.db_index_?) {
+ indexMap = v.getName
+ }
+ }
+ }
+ }
+ def findPos(in : AnyRef) : Option[int] = {
+ tArray.elements.zipWithIndex.foreach {mft => if (in eq mft._1._3) return Some(mft._2)}
+ None
+ }
+
+ val resArray = new ArrayBuffer[Triple[String, Method, MappedField[AnyRef,A]]];
+
+ sws_fieldOrder.foreach {
+ f =>
+ findPos(f).foreach{pos => resArray += tArray.remove(pos)}
+ }
+
+ tArray.foreach {mft => resArray += mft}
+
+ mappedFieldArray = resArray.toArray
+ }
+ }
+
+ val columnNamesForInsert = {
+ (mappedColumnInfo.elements.filter{c => !c._2.db_index_?}.map{p => p._1}).toList.mkString("", ",", "")
+ }
+
+ val columnQueriesForInsert = {
+ (mappedColumnInfo.elements.filter{c => !c._2.db_index_?}.map{p => "?"}).toList.mkString("", ",", "")
+ }
+
+ private def fixTableName(name : String) = clean(name.toLowerCase)
+
+ protected def internalTableName_$ = getClass.getSuperclass.getName.split("\\.").toList.last
+
+ def htmlHeaders : Seq[Node] = {
+ mappedFieldArray.filter{mft => mft._3.db_display_?}.map {mft => <th>{mft._3.displayName}</th>}.toList
+ // mappedFieldInfo.elements.filter{e => e._2.db_display_?}. map {e => <th>{e._1}</th>}.toList
+ }
+
+ def doHtmlLine(toLine : Mapper[A]) : Seq[Node] = {
+ mappedFieldArray.filter{mft => mft._3.db_display_?}.map {mft => <td>{??(mft._2, toLine).asHtml}</td>}.toList
+ }
+
+ def asHtml(toLine : Mapper[A]) : Seq[Node] = {
+ Text(internalTableName_$) :: Text("={ ") ::
+ mappedFieldArray.filter{mft => mft._3.db_display_?}.map {mft =>
+ val field = ??(mft._2, toLine)
+ <span>{field.displayName}={field.asHtml}&nbsp;</span>}.toList :::
+ List(Text(" }"))
+ }
+
+ def generateInputTable(toMap : Mapper[A]) : Seq[Node] = {
+
+ mappedFieldArray.filter{e => e._3.db_display_?}.map {
+ e =>
+ val field = ??(e._2, toMap)
+ <tr>
+ <td>{field.displayName}</td>
+ <td>{field.i}</td>
+ </tr>}.toList
+ }
+
+ val tableName_$ : String = {
+ fixTableName(internalTableName_$)
+ }
+
+
+
+ // protected def getField(inst : Mapper[A], meth : Method) = meth.invoke(inst, null).asInstanceOf[MappedField[AnyRef,A]]
+}
+
+abstract class QueryParam
+case class ByField[T <: Any, O](field: MappedField[T,O], value: T) extends QueryParam
+
View
68 lift/src/main/scala/net/liftweb/mapper/Safe.scala
@@ -0,0 +1,68 @@
+package net.liftweb.mapper
+
+/* *\
+ (c) 2006-2007 WorldWide Conferencing, LLC
+ Distributed under an Apache License
+ http://www.apache.org/licenses/LICENSE-2.0
+ \* */
+
+import java.security.{SecureRandom, MessageDigest}
+import org.apache.commons.codec.binary.Base64
+
+/**
+ * Manage the current "safety" state of the stack
+ */
+object Safe {
+ private val rand = new SecureRandom
+ /**
+ * Get the next "safe" number
+ */
+ def next = rand.nextLong
+ private val threadLocal = new ThreadLocal
+
+ /**
+ * Is the current context "safe" for the object with the
+ * given safety code?
+ */
+ def safe_?(test : long) : boolean = test == threadLocal.get
+
+ /**
+ * Marks access to a given object as safe for the duration of the function
+ */
+ def runSafe[T](x : long)(f : => T) : T = {
+ // FIXME -- this should collect all the objects that have been marked safe such that
+ // multiple objects can be safe at the same time
+ val old = threadLocal.get
+ try {
+ threadLocal.set(x)
+ f
+ } finally {
+ threadLocal.set(old)
+ }
+ }
+
+ def randomString(len : int) : String = {
+ len match {
+ case 0 => ""
+ case _ => randomChar + randomString(len - 1)
+ }
+ }
+
+ def randomChar : String = {
+ rand.nextInt(62) match {
+ case s @ _ if (s < 26) => {('A' + s).asInstanceOf[char].toString}
+ case s @ _ if (s < 52) => {('a' + (s - 26)).asInstanceOf[char].toString}
+ case s @ _ => {('0' + (s - 52)).asInstanceOf[char].toString}
+ }
+ }
+
+ /*
+ def generateUniqueName = {
+ S.nc+"_inp_"+randomString(15)+"_$"
+ }
+ */
+
+ def hash(in : String) : String = {
+ new String((new Base64) encode (MessageDigest.getInstance("SHA")).digest(in.getBytes("UTF-8")))
+ }
+}
View
32 lift/src/main/scala/net/liftweb/proto/MappedEmail.scala
@@ -0,0 +1,32 @@
+package net.liftweb.proto
+
+/* *\
+ (c) 2006-2007 WorldWide Conferencing, LLC
+ Distributed under an Apache License
+ http://www.apache.org/licenses/LICENSE-2.0
+\* */
+
+import net.liftweb.mapper._
+import java.util.regex._
+
+object MappedEmail {
+ val emailPattern = Pattern.compile("^[a-z0-9._%-]+@(?:[a-z0-9-]+\\.)+[a-z]{2,4}$")
+}
+
+class MappedEmail[T](owner : Mapper[T]) extends MappedString[T](owner) {
+
+ override protected def i_set_!(value : String) : String = {
+ super.i_set_!(value match {
+ case null => ""
+ case _ => value.toLowerCase.trim
+ })
+ }
+
+ override def sws_validate : List[ValidationIssues[String, T]] = {
+ MappedEmail.emailPattern.matcher(i_get_!).matches match {
+ case true => Nil
+ case false => List(ValidationIssues(this, "Invalid Email Address"))
+ }
+ }
+
+}
View
93 lift/src/main/scala/net/liftweb/proto/MappedInt.scala
@@ -0,0 +1,93 @@
+package net.liftweb.proto
+
+/* *\
+ (c) 2006-2007 WorldWide Conferencing, LLC
+ Distributed under an Apache License
+ http://www.apache.org/licenses/LICENSE-2.0
+\* */
+
+import net.liftweb.mapper.{Mapper, MappedField, IndexedField}
+import java.sql.{ResultSet, Types}
+import java.lang.reflect.Method
+import net.liftweb.util.Helpers._
+
+class MappedIntIndex[T](owner : Mapper[T]) extends MappedInt[T](owner) with IndexedField[int] {
+
+ override def write_permission_? = false // not writable
+
+ override def db_index_? = true
+
+ override def db_autogenerated_index_? = true
+
+ override def defaultValue = -1
+
+ override def db_index_field_indicates_saved_? = {i_get_! != defaultValue}
+
+ def makeKeyJDBCFriendly(in : int) = new Integer(in)
+
+ def convertKey(in : String) : Option[int] = {
+ if (in eq null) None
+ try {
+ val what = if (in.startsWith(name + "=")) in.substring((name + "=").length) else in
+ Some(Integer.parseInt(what))
+ } catch {
+ case _ => {None}
+ }
+ }
+
+ override def db_display_? = false
+
+ def convertKey(in : int) : Option[int] = {
+ Some(in)
+ }
+ def convertKey(in : AnyRef) : Option[int] = {
+ if ((in eq null) || (in eq None)) None
+ try {
+ convertKey(in.toString)
+ } catch {
+ case _ => {None}
+ }
+ }
+}
+
+
+class MappedInt[T](val owner : Mapper[T]) extends MappedField[int, T] {
+ private var data : int = defaultValue
+ def defaultValue = 0
+
+ /**
+ * Get the JDBC SQL Type for this field
+ */
+ def getTargetSQLType(field : String) = Types.INTEGER
+
+ protected def i_get_! = data
+
+ protected def i_set_!(value : int) : int = {
+ if (value != data) {
+ data = value
+ this.dirty_?( true)
+ }
+ data
+ }
+ override def read_permission_? = true
+ override def write_permission_? = true
+
+ def convertToJDBCFriendly(value: int): Object = new Integer(value)
+
+
+ def getJDBCFriendly(field : String) = new Integer(get)
+
+ def ::=(f : Any) : int = {
+ this := tryn {if (f != null) Integer.parseInt(f.toString) else 0}
+ }
+ protected def i_obscure_!(in : int) = 0
+
+ def buildSetActualValue(accessor : Method, inst : AnyRef, columnName : String) : (Mapper[T], AnyRef) => unit = {
+ inst match {
+ case null => {(inst : Mapper[T], v : AnyRef) => {val tv = getField(inst, accessor); tv.set(0); tv.resetDirty}}
+ case _ if (inst.isInstanceOf[Number]) => {(inst : Mapper[T], v : AnyRef) => {val tv = getField(inst, accessor); tv.set(if (v == null) 0 else v.asInstanceOf[Number].intValue); tv.resetDirty}}
+ case _ => {(inst : Mapper[T], v : AnyRef) => {val tv = getField(inst, accessor); tv.set(tryn(Integer.parseInt(v.toString))); tv.resetDirty}}
+ }