diff --git a/project-code/app/play/utils/meta/cp/ClasspathScanningControllerRegistry.java b/project-code/app/play/utils/meta/cp/ClasspathScanningControllerRegistry.java index 6e8485a..43af4db 100644 --- a/project-code/app/play/utils/meta/cp/ClasspathScanningControllerRegistry.java +++ b/project-code/app/play/utils/meta/cp/ClasspathScanningControllerRegistry.java @@ -144,8 +144,13 @@ public ControllerProxyCRUD getCrudController(K keyClass, M modelCla @SuppressWarnings({ "rawtypes", "unchecked" }) private Map, ControllerProxyCRUD> scanCrud(GlobalSettings global, Class superType, ClassLoader... cls) { - final Reflections reflections = new Reflections(new ConfigurationBuilder().setUrls( - ClasspathHelper.forPackage("", cls)).setScanners(new SubTypesScanner(), + + ConfigurationBuilder builder = new ConfigurationBuilder().setUrls( + ClasspathHelper.forPackage("", cls)); + builder.addUrls(ClasspathHelper.forPackage("org.mef.twixt")); + + + final Reflections reflections = new Reflections(builder.setScanners(new SubTypesScanner(), new TypeAnnotationsScanner()).addClassLoaders(cls)); Map, ControllerProxyCRUD> map = Maps.newHashMap(); @@ -153,7 +158,7 @@ public ControllerProxyCRUD getCrudController(K keyClass, M modelCla for (Class controllerClass : controllerClasses) { try { if (log.isDebugEnabled()) - log.debug("controllerClass : " + controllerClass); + log.debug("pqpcontrollerClass : " + controllerClass); if (controllerClass.isAnnotationPresent(Dynamic.class)) { if (log.isDebugEnabled()) diff --git a/samples/issue54/m8/.gitignore b/samples/issue54/m8/.gitignore new file mode 100644 index 0000000..fe7c646 --- /dev/null +++ b/samples/issue54/m8/.gitignore @@ -0,0 +1,13 @@ +/logs +/project/project +/project/target +/target +/*.iml +/.idea +/.idea_modules +/.settings +/db.h2.db +/testdb.h2.db +/.target +/bin/ +/db diff --git a/samples/issue54/m8/LICENSE.md b/samples/issue54/m8/LICENSE.md new file mode 100644 index 0000000..48f6cf9 --- /dev/null +++ b/samples/issue54/m8/LICENSE.md @@ -0,0 +1,24 @@ +play2-crud-activator +---------- + +The MIT License (MIT) +http://opensource.org/licenses/MIT + +Copyright (C) 2012 Hakan Dilek + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in the +Software without restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies +or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/samples/issue54/m8/README.md b/samples/issue54/m8/README.md new file mode 100644 index 0000000..0231c2b --- /dev/null +++ b/samples/issue54/m8/README.md @@ -0,0 +1,8 @@ +play2-crud-activator +==================== + +[Typesafe Activator](http://typesafe.com/activator) for [play2-crud](https://github.com/hakandilek/play2-crud). + +HOW-TO +================ +Please follow [these instructions](http://typesafe.com/activator/template/play2-crud-activator) in order to create an application with typesafe activator. diff --git a/samples/issue54/m8/activator b/samples/issue54/m8/activator new file mode 100644 index 0000000..59c131b --- /dev/null +++ b/samples/issue54/m8/activator @@ -0,0 +1,334 @@ +#!/bin/bash + +### ------------------------------- ### +### Helper methods for BASH scripts ### +### ------------------------------- ### + +realpath () { +( + TARGET_FILE="$1" + + cd $(dirname "$TARGET_FILE") + TARGET_FILE=$(basename "$TARGET_FILE") + + COUNT=0 + while [ -L "$TARGET_FILE" -a $COUNT -lt 100 ] + do + TARGET_FILE=$(readlink "$TARGET_FILE") + cd $(dirname "$TARGET_FILE") + TARGET_FILE=$(basename "$TARGET_FILE") + COUNT=$(($COUNT + 1)) + done + + if [ "$TARGET_FILE" == "." -o "$TARGET_FILE" == ".." ]; then + cd "$TARGET_FILE" + TARGET_FILEPATH= + else + TARGET_FILEPATH=/$TARGET_FILE + fi + + # make sure we grab the actual windows path, instead of cygwin's path. + if ! is_cygwin; then + echo "$(pwd -P)/$TARGET_FILE" + else + echo $(cygwinpath "$(pwd -P)/$TARGET_FILE") + fi +) +} + +# TODO - Do we need to detect msys? + +# Uses uname to detect if we're in the odd cygwin environment. +is_cygwin() { + local os=$(uname -s) + case "$os" in + CYGWIN*) return 0 ;; + *) return 1 ;; + esac +} + +# This can fix cygwin style /cygdrive paths so we get the +# windows style paths. +cygwinpath() { + local file="$1" + if is_cygwin; then + echo $(cygpath -w $file) + else + echo $file + fi +} + +# Make something URI friendly +make_url() { + url="$1" + local nospaces=${url// /%20} + if is_cygwin; then + echo "/${nospaces//\\//}" + else + echo "$nospaces" + fi +} + +# Detect if we should use JAVA_HOME or just try PATH. +get_java_cmd() { + if [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]]; then + echo "$JAVA_HOME/bin/java" + else + echo "java" + fi +} + +echoerr () { + echo 1>&2 "$@" +} +vlog () { + [[ $verbose || $debug ]] && echoerr "$@" +} +dlog () { + [[ $debug ]] && echoerr "$@" +} +execRunner () { + # print the arguments one to a line, quoting any containing spaces + [[ $verbose || $debug ]] && echo "# Executing command line:" && { + for arg; do + if printf "%s\n" "$arg" | grep -q ' '; then + printf "\"%s\"\n" "$arg" + else + printf "%s\n" "$arg" + fi + done + echo "" + } + + exec "$@" +} +addJava () { + dlog "[addJava] arg = '$1'" + java_args=( "${java_args[@]}" "$1" ) +} +addApp () { + dlog "[addApp] arg = '$1'" + sbt_commands=( "${app_commands[@]}" "$1" ) +} +addResidual () { + dlog "[residual] arg = '$1'" + residual_args=( "${residual_args[@]}" "$1" ) +} +addDebugger () { + addJava "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=$1" +} +addConfigOpts () { + dlog "[addConfigOpts] arg = '$*'" + for item in $* + do + addJava "$item" + done +} +# a ham-fisted attempt to move some memory settings in concert +# so they need not be messed around with individually. +get_mem_opts () { + local mem=${1:-1024} + local meta=$(( $mem / 4 )) + (( $meta > 256 )) || meta=256 + (( $meta < 1024 )) || meta=1024 + + # default is to set memory options but this can be overridden by code section below + memopts="-Xms${mem}m -Xmx${mem}m" + if [[ "${java_version}" > "1.8" ]]; then + extmemopts="-XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=${meta}m" + else + extmemopts="-XX:PermSize=64m -XX:MaxPermSize=${meta}m" + fi + + if [[ "${java_opts}" == *-Xmx* ]] || [[ "${java_opts}" == *-Xms* ]] || [[ "${java_opts}" == *-XX:MaxPermSize* ]] || [[ "${java_opts}" == *-XX:ReservedCodeCacheSize* ]] || [[ "${java_opts}" == *-XX:MaxMetaspaceSize* ]]; then + # if we detect any of these settings in ${java_opts} we need to NOT output our settings. + # The reason is the Xms/Xmx, if they don't line up, cause errors. + memopts="" + extmemopts="" + fi + + echo "${memopts} ${extmemopts}" +} +require_arg () { + local type="$1" + local opt="$2" + local arg="$3" + if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then + die "$opt requires <$type> argument" + fi +} +is_function_defined() { + declare -f "$1" > /dev/null +} + +# If we're *not* running in a terminal, and we don't have any arguments, then we need to add the 'ui' parameter +detect_terminal_for_ui() { + [[ ! -t 0 ]] && [[ "${#residual_args}" == "0" ]] && { + addResidual "ui" + } + # SPECIAL TEST FOR MAC + [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]] && [[ "${#residual_args}" == "0" ]] && { + echo "Detected MAC OSX launched script...." + echo "Swapping to UI" + addResidual "ui" + } +} + +# Processes incoming arguments and places them in appropriate global variables. called by the run method. +process_args () { + while [[ $# -gt 0 ]]; do + case "$1" in + -h|-help) usage; exit 1 ;; + -v|-verbose) verbose=1 && shift ;; + -d|-debug) debug=1 && shift ;; + -mem) require_arg integer "$1" "$2" && app_mem="$2" && shift 2 ;; + -jvm-debug) + if echo "$2" | grep -E ^[0-9]+$ > /dev/null; then + addDebugger "$2" && shift + else + addDebugger 9999 + fi + shift ;; + -java-home) require_arg path "$1" "$2" && java_cmd="$2/bin/java" && shift 2 ;; + -D*) addJava "$1" && shift ;; + -J*) addJava "${1:2}" && shift ;; + *) addResidual "$1" && shift ;; + esac + done + + is_function_defined process_my_args && { + myargs=("${residual_args[@]}") + residual_args=() + process_my_args "${myargs[@]}" + } +} + +# Actually runs the script. +run() { + # TODO - check for sane environment + + # process the combined args, then reset "$@" to the residuals + process_args "$@" + detect_terminal_for_ui + set -- "${residual_args[@]}" + argumentCount=$# + + #check for jline terminal fixes on cygwin + if is_cygwin; then + stty -icanon min 1 -echo > /dev/null 2>&1 + addJava "-Djline.terminal=jline.UnixTerminal" + addJava "-Dsbt.cygwin=true" + fi + + # run sbt + execRunner "$java_cmd" \ + "-Dactivator.home=$(make_url "$activator_home")" \ + $(get_mem_opts $app_mem) \ + ${java_opts[@]} \ + ${java_args[@]} \ + -jar "$app_launcher" \ + "${app_commands[@]}" \ + "${residual_args[@]}" + + local exit_code=$? + if is_cygwin; then + stty icanon echo > /dev/null 2>&1 + fi + exit $exit_code +} + +# Loads a configuration file full of default command line options for this script. +loadConfigFile() { + cat "$1" | sed '/^\#/d' +} + +### ------------------------------- ### +### Start of customized settings ### +### ------------------------------- ### +usage() { + cat < [options] + + Command: + ui Start the Activator UI + new [name] [template-id] Create a new project with [name] using template [template-id] + list-templates Print all available template names + -h | -help Print this message + + Options: + -v | -verbose Make this runner chattier + -d | -debug Set sbt log level to debug + -mem Set memory options (default: $sbt_mem, which is $(get_mem_opts $sbt_mem)) + -jvm-debug Turn on JVM debugging, open at the given port. + + # java version (default: java from PATH, currently $(java -version 2>&1 | grep version)) + -java-home Alternate JAVA_HOME + + # jvm options and output control + -Dkey=val Pass -Dkey=val directly to the java runtime + -J-X Pass option -X directly to the java runtime + (-J is stripped) + + # environment variables (read from context) + JAVA_OPTS Environment variable, if unset uses "" + SBT_OPTS Environment variable, if unset uses "" + ACTIVATOR_OPTS Environment variable, if unset uses "" + +In the case of duplicated or conflicting options, the order above +shows precedence: environment variables lowest, command line options highest. +EOM +} + +### ------------------------------- ### +### Main script ### +### ------------------------------- ### + +declare -a residual_args +declare -a java_args +declare -a app_commands +declare -r real_script_path="$(realpath "$0")" +declare -r activator_home="$(realpath "$(dirname "$real_script_path")")" +declare -r app_version="1.2.12" + +declare -r app_launcher="${activator_home}/activator-launch-${app_version}.jar" +declare -r script_name=activator +declare -r java_cmd=$(get_java_cmd) +declare -r java_opts=( "${ACTIVATOR_OPTS[@]}" "${SBT_OPTS[@]}" "${JAVA_OPTS[@]}" "${java_opts[@]}" ) +userhome="$HOME" +if is_cygwin; then + # cygwin sets home to something f-d up, set to real windows homedir + userhome="$USERPROFILE" +fi +declare -r activator_user_home_dir="${userhome}/.activator" +declare -r java_opts_config_home="${activator_user_home_dir}/activatorconfig.txt" +declare -r java_opts_config_version="${activator_user_home_dir}/${app_version}/activatorconfig.txt" + +# Now check to see if it's a good enough version +declare -r java_version=$("$java_cmd" -version 2>&1 | awk -F '"' '/version/ {print $2}') +if [[ "$java_version" == "" ]]; then + echo + echo No java installations was detected. + echo Please go to http://www.java.com/getjava/ and download + echo + exit 1 +elif [[ ! "$java_version" > "1.6" ]]; then + echo + echo The java installation you have is not up to date + echo Activator requires at least version 1.6+, you have + echo version $java_version + echo + echo Please go to http://www.java.com/getjava/ and download + echo a valid Java Runtime and install before running Activator. + echo + exit 1 +fi + +# if configuration files exist, prepend their contents to the java args so it can be processed by this runner +# a "versioned" config trumps one on the top level +if [[ -f "$java_opts_config_version" ]]; then + addConfigOpts $(loadConfigFile "$java_opts_config_version") +elif [[ -f "$java_opts_config_home" ]]; then + addConfigOpts $(loadConfigFile "$java_opts_config_home") +fi + +run "$@" diff --git a/samples/issue54/m8/activator-launch-1.2.12.jar b/samples/issue54/m8/activator-launch-1.2.12.jar new file mode 100644 index 0000000..4a8963c Binary files /dev/null and b/samples/issue54/m8/activator-launch-1.2.12.jar differ diff --git a/samples/issue54/m8/activator.bat b/samples/issue54/m8/activator.bat new file mode 100644 index 0000000..d078287 --- /dev/null +++ b/samples/issue54/m8/activator.bat @@ -0,0 +1,227 @@ +@REM activator launcher script +@REM +@REM Envioronment: +@REM JAVA_HOME - location of a JDK home dir (optional if java on path) +@REM CFG_OPTS - JVM options (optional) +@REM Configuration: +@REM activatorconfig.txt found in the ACTIVATOR_HOME or ACTIVATOR_HOME/ACTIVATOR_VERSION +@setlocal enabledelayedexpansion + +@echo off + +set "var1=%~1" +if defined var1 ( + if "%var1%"=="help" ( + echo. + echo Usage activator [options] [command] + echo. + echo Commands: + echo ui Start the Activator UI + echo new [name] [template-id] Create a new project with [name] using template [template-id] + echo list-templates Print all available template names + echo help Print this message + echo. + echo Options: + echo -jvm-debug [port] Turn on JVM debugging, open at the given port. Defaults to 9999 if no port given. + echo. + echo Environment variables ^(read from context^): + echo JAVA_OPTS Environment variable, if unset uses "" + echo SBT_OPTS Environment variable, if unset uses "" + echo ACTIVATOR_OPTS Environment variable, if unset uses "" + echo. + goto :end + ) +) + +if "%ACTIVATOR_HOME%"=="" ( + set "ACTIVATOR_HOME=%~dp0" + @REM remove trailing "\" from path + set ACTIVATOR_HOME=!ACTIVATOR_HOME:~0,-1! +) + +set ERROR_CODE=0 +set APP_VERSION=1.2.12 +set ACTIVATOR_LAUNCH_JAR=activator-launch-%APP_VERSION%.jar + +rem Detect if we were double clicked, although theoretically A user could +rem manually run cmd /c +for %%x in (%cmdcmdline%) do if %%~x==/c set DOUBLECLICKED=1 + +rem FIRST we load a config file of extra options (if there is one) +set "CFG_FILE_HOME=%UserProfile%\.activator\activatorconfig.txt" +set "CFG_FILE_VERSION=%UserProfile%\.activator\%APP_VERSION%\activatorconfig.txt" +set CFG_OPTS= +if exist %CFG_FILE_VERSION% ( + FOR /F "tokens=* eol=# usebackq delims=" %%i IN ("%CFG_FILE_VERSION%") DO ( + set DO_NOT_REUSE_ME=%%i + rem ZOMG (Part #2) WE use !! here to delay the expansion of + rem CFG_OPTS, otherwise it remains "" for this loop. + set CFG_OPTS=!CFG_OPTS! !DO_NOT_REUSE_ME! + ) +) +if "%CFG_OPTS%"=="" ( + if exist %CFG_FILE_HOME% ( + FOR /F "tokens=* eol=# usebackq delims=" %%i IN ("%CFG_FILE_HOME%") DO ( + set DO_NOT_REUSE_ME=%%i + rem ZOMG (Part #2) WE use !! here to delay the expansion of + rem CFG_OPTS, otherwise it remains "" for this loop. + set CFG_OPTS=!CFG_OPTS! !DO_NOT_REUSE_ME! + ) + ) +) + +rem We use the value of the JAVACMD environment variable if defined +set _JAVACMD=%JAVACMD% + +if "%_JAVACMD%"=="" ( + if not "%JAVA_HOME%"=="" ( + if exist "%JAVA_HOME%\bin\java.exe" set "_JAVACMD=%JAVA_HOME%\bin\java.exe" + + rem if there is a java home set we make sure it is the first picked up when invoking 'java' + SET "PATH=%JAVA_HOME%\bin;%PATH%" + ) +) + +if "%_JAVACMD%"=="" set _JAVACMD=java + +rem Detect if this java is ok to use. +for /F %%j in ('"%_JAVACMD%" -version 2^>^&1') do ( + if %%~j==Java set JAVAINSTALLED=1 +) + +rem Detect the same thing about javac +if "%_JAVACCMD%"=="" ( + if not "%JAVA_HOME%"=="" ( + if exist "%JAVA_HOME%\bin\javac.exe" set "_JAVACCMD=%JAVA_HOME%\bin\javac.exe" + ) +) +if "%_JAVACCMD%"=="" set _JAVACCMD=javac +for /F %%j in ('"%_JAVACCMD%" -version 2^>^&1') do ( + if %%~j==javac set JAVACINSTALLED=1 +) + +rem BAT has no logical or, so we do it OLD SCHOOL! Oppan Redmond Style +set JAVAOK=true +if not defined JAVAINSTALLED set JAVAOK=false +if not defined JAVACINSTALLED set JAVAOK=false + +if "%JAVAOK%"=="false" ( + echo. + echo A Java JDK is not installed or can't be found. + if not "%JAVA_HOME%"=="" ( + echo JAVA_HOME = "%JAVA_HOME%" + ) + echo. + echo Please go to + echo http://www.oracle.com/technetwork/java/javase/downloads/index.html + echo and download a valid Java JDK and install before running Activator. + echo. + echo If you think this message is in error, please check + echo your environment variables to see if "java.exe" and "javac.exe" are + echo available via JAVA_HOME or PATH. + echo. + if defined DOUBLECLICKED pause + exit /B 1 +) + +rem Check what Java version is being used to determine what memory options to use +for /f "tokens=3" %%g in ('java -version 2^>^&1 ^| findstr /i "version"') do ( + set JAVA_VERSION=%%g +) + +rem Strips away the " characters +set JAVA_VERSION=%JAVA_VERSION:"=% + +rem TODO Check if there are existing mem settings in JAVA_OPTS/CFG_OPTS and use those instead of the below +for /f "delims=. tokens=1-3" %%v in ("%JAVA_VERSION%") do ( + set MAJOR=%%v + set MINOR=%%w + set BUILD=%%x + + set META_SIZE=-XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=256M + if "!MINOR!" LSS "8" ( + set META_SIZE=-XX:PermSize=64M -XX:MaxPermSize=256M + ) + + set MEM_OPTS=!META_SIZE! + ) + +rem We use the value of the JAVA_OPTS environment variable if defined, rather than the config. +set _JAVA_OPTS=%JAVA_OPTS% +if "%_JAVA_OPTS%"=="" set _JAVA_OPTS=%CFG_OPTS% + +set DEBUG_OPTS= + +rem Loop through the arguments, building remaining args in args variable +set args= +:argsloop +if not "%~1"=="" ( + rem Checks if the argument contains "-D" and if true, adds argument 1 with 2 and puts an equal sign between them. + rem This is done since batch considers "=" to be a delimiter so we need to circumvent this behavior with a small hack. + set arg1=%~1 + if "!arg1:~0,2!"=="-D" ( + set "args=%args% "%~1"="%~2"" + shift + shift + goto argsloop + ) + + if "%~1"=="-jvm-debug" ( + if not "%~2"=="" ( + rem This piece of magic somehow checks that an argument is a number + for /F "delims=0123456789" %%i in ("%~2") do ( + set var="%%i" + ) + if defined var ( + rem Not a number, assume no argument given and default to 9999 + set JPDA_PORT=9999 + ) else ( + rem Port was given, shift arguments + set JPDA_PORT=%~2 + shift + ) + ) else ( + set JPDA_PORT=9999 + ) + shift + + set DEBUG_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=!JPDA_PORT! + goto argsloop + ) + rem else + set "args=%args% "%~1"" + shift + goto argsloop +) + +:run + +if "!args!"=="" ( + if defined DOUBLECLICKED ( + set CMDS="ui" + ) else set CMDS=!args! +) else set CMDS=!args! + +rem We add a / in front, so we get file:///C: instead of file://C: +rem Java considers the later a UNC path. +rem We also attempt a solid effort at making it URI friendly. +rem We don't even bother with UNC paths. +set JAVA_FRIENDLY_HOME_1=/!ACTIVATOR_HOME:\=/! +set JAVA_FRIENDLY_HOME=/!JAVA_FRIENDLY_HOME_1: =%%20! + +rem Checks if the command contains spaces to know if it should be wrapped in quotes or not +set NON_SPACED_CMD=%_JAVACMD: =% +if "%_JAVACMD%"=="%NON_SPACED_CMD%" %_JAVACMD% %DEBUG_OPTS% %MEM_OPTS% %ACTIVATOR_OPTS% %SBT_OPTS% %_JAVA_OPTS% "-Dactivator.home=%JAVA_FRIENDLY_HOME%" -jar "%ACTIVATOR_HOME%\%ACTIVATOR_LAUNCH_JAR%" %CMDS% +if NOT "%_JAVACMD%"=="%NON_SPACED_CMD%" "%_JAVACMD%" %DEBUG_OPTS% %MEM_OPTS% %ACTIVATOR_OPTS% %SBT_OPTS% %_JAVA_OPTS% "-Dactivator.home=%JAVA_FRIENDLY_HOME%" -jar "%ACTIVATOR_HOME%\%ACTIVATOR_LAUNCH_JAR%" %CMDS% + +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end + +@endlocal + +exit /B %ERROR_CODE% diff --git a/samples/issue54/m8/activator.properties b/samples/issue54/m8/activator.properties new file mode 100644 index 0000000..4204feb --- /dev/null +++ b/samples/issue54/m8/activator.properties @@ -0,0 +1,4 @@ +name=play2-crud-activator +title=Play + Java + CRUD +description=This template is an easy way to get started with Play Framework, Java, using play2-crud module +tags=Sample,playframework,java,crud diff --git a/samples/issue54/m8/app/controllers/Application.java b/samples/issue54/m8/app/controllers/Application.java new file mode 100644 index 0000000..68f4b59 --- /dev/null +++ b/samples/issue54/m8/app/controllers/Application.java @@ -0,0 +1,31 @@ +package controllers; + +import org.mef.twixt.binder.TwixtForm; +import org.mef.twixt.validate.ValContext; + +import models.Taxi; +import play.Logger; +import play.api.Play; +import play.mvc.Controller; +import play.mvc.Result; + +public class Application extends Controller { + + public static Result index() { + + ClassLoader appClassloader = Play.classloader(Play.current()); + + Taxi obj = new Taxi(); + ClassLoader libClassloader2 = obj.getClass().getClassLoader(); + + ValContext vtx = new ValContext(); + ClassLoader libClassloader3 = obj.getClass().getClassLoader(); + + Logger.info("A: " + appClassloader.getClass().getName()); + Logger.info("B: " + libClassloader2.getClass().getName()); + Logger.info("C: " + libClassloader3.getClass().getName()); + + return ok(views.html.index.render()); + } + +} \ No newline at end of file diff --git a/samples/issue54/m8/app/controllers/MyDynamicTwixtController.java b/samples/issue54/m8/app/controllers/MyDynamicTwixtController.java new file mode 100644 index 0000000..858c187 --- /dev/null +++ b/samples/issue54/m8/app/controllers/MyDynamicTwixtController.java @@ -0,0 +1,170 @@ +package controllers; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.TreeMap; + +import org.mef.twixt.BooleanValue; +import org.mef.twixt.DateValue; +import org.mef.twixt.FileValue; +import org.mef.twixt.IntegerValue; +import org.mef.twixt.LongSelectValue; +import org.mef.twixt.SelectValue; +import org.mef.twixt.Value; +import org.mef.twixt.ValueContainer; +import org.mef.twixt.widget.MySelectWidget; +import org.springframework.util.ReflectionUtils; + +import play.Logger; +import play.data.Form; +import play.twirl.api.Content; +import play.utils.dao.BasicModel; +import play.utils.dao.DAO; +import play.utils.meta.FieldMetadata; +import play.utils.meta.form.CheckboxWidget; +import play.utils.meta.form.DateWidget; +import play.utils.meta.form.FileWidget; +import play.utils.meta.form.FormFieldWidget; +import play.utils.meta.form.NumberWidget; +import play.utils.meta.form.TextWidget; + +public abstract class MyDynamicTwixtController,T extends ValueContainer> extends MyTwixtController implements ReflectionUtils.FieldCallback, ReflectionUtils.FieldFilter +{ + private class PreRender implements ReflectionUtils.FieldCallback + { + @Override + public void doWith(Field arg0) throws IllegalArgumentException, IllegalAccessException + { + Class clazz = arg0.getType(); + if (LongSelectValue.class.isAssignableFrom(clazz)) + { + LongSelectValue val = (LongSelectValue) arg0.get(preRenderTwixt); + FieldMetadata meta = findMeta(arg0); + MySelectWidget w = (MySelectWidget) meta.getWidget(); + + w.options = new TreeMap(); + for(Long key : val.options().keySet()) + { + w.options.put(key.toString(), val.options().get(key)); //put key as string because form.value will be string + } + } + else if (SelectValue.class.isAssignableFrom(clazz)) + { + SelectValue val = (SelectValue) arg0.get(preRenderTwixt); + FieldMetadata meta = findMeta(arg0); + MySelectWidget w = (MySelectWidget) meta.getWidget(); + + w.options = new TreeMap(); + for(String key : val.options().keySet()) + { + w.options.put(key, val.options().get(key)); + } + } + } + + private FieldMetadata findMeta(Field arg0) + { + for(FieldMetadata meta : metaL) + { + if (arg0.getName().equals(meta.getField().getName())) + { + return meta; + } + } + return null; + } + } + + + List metaL = new ArrayList(); + private T preRenderTwixt; + + public MyDynamicTwixtController(DAO dao, Class keyClass, Class modelClass, ClasstwixtClass, int pageSize, String orderBy) + { + super(dao, keyClass, modelClass, twixtClass, pageSize, orderBy); + ReflectionUtils.doWithFields(twixtClass, this, this); + } + + @Override + public void doWith(Field field) throws IllegalArgumentException, + IllegalAccessException + { + addFieldToMetaL(field); + } + + @Override + public boolean matches(Field arg0) + { + return (Value.class.isAssignableFrom(arg0.getType())); + } + + private void addFieldToMetaL(Field f) + { + FieldMetadata meta = new FieldMetadata(f, null); //new StringConverter()); + FormFieldWidget w = createWidget(f, meta); + try { + forceSetWidget(meta, w); + Logger.info("ffff: " + f.getName()); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + metaL.add(meta); + } + + protected FormFieldWidget createWidget(Field f, FieldMetadata meta) + { + Class clazz = f.getType(); + + if (clazz.equals(IntegerValue.class)) + { + return new NumberWidget(meta); + } + else if (clazz.equals(BooleanValue.class)) + { + return new CheckboxWidget(meta); + } + else if (clazz.equals(DateValue.class)) + { + return new DateWidget(meta); //yyyy-mm-dd + } + else if (clazz.isAssignableFrom(SelectValue.class)) + { + return new MySelectWidget(meta); + } + else if (LongSelectValue.class.isAssignableFrom(clazz)) + { + return new MySelectWidget(meta); + } + else if (FileValue.class.isAssignableFrom(clazz)) + { + return new FileWidget(meta); + } + else + { + TextWidget w = new TextWidget(meta); + return w; + } + } + + private void forceSetWidget(FieldMetadata meta2, FormFieldWidget w) throws Exception + { + Field f = ReflectionUtils.findField(meta2.getClass(), "widget"); + f.setAccessible(true); //force! + f.set(meta2, w); + } + + @Override + protected Content xrenderForm(K key, Form form) + { + //need to assign options map to any selectvalues + preRenderTwixt = form.get(); + ReflectionUtils.doWithFields(preRenderTwixt.getClass(), new PreRender(), ReflectionUtils.COPYABLE_FIELDS); + + return render(templateForForm(), with(getKeyClass(), key).and(Form.class, form).and(metaL.getClass(), metaL)); + } + + + +} diff --git a/samples/issue54/m8/app/controllers/MyTwixtController.java b/samples/issue54/m8/app/controllers/MyTwixtController.java new file mode 100644 index 0000000..979ba1a --- /dev/null +++ b/samples/issue54/m8/app/controllers/MyTwixtController.java @@ -0,0 +1,197 @@ +package controllers; + +import static play.data.Form.form; + +import javax.inject.Inject; + +import org.mef.twixt.ValueContainer; + + + +import org.mef.twixt.binder.TwixtBinder; + +import play.Logger; +import play.Logger.ALogger; +import play.data.Form; +import play.mvc.Call; +import play.mvc.Result; +import play.twirl.api.Content; +import play.utils.crud.CRUDController; +import play.utils.dao.BasicModel; +import play.utils.dao.DAO; + +public abstract class MyTwixtController,T extends ValueContainer> extends CRUDController +{ + protected Class twixtClass; + + @Inject + public MyTwixtController(DAO dao, Class keyClass, Class modelClass, ClasstwixtClass, int pageSize, String orderBy) { + super(dao, form(modelClass), keyClass, modelClass, pageSize, orderBy); + this.twixtClass = twixtClass; + } + + private static ALogger xlog = Logger.of(CRUDController.class); + + protected Content xrenderForm(K key, Form form) + { + return render(templateForForm(), with(getKeyClass(), key).and(Form.class, form)); + } + + + public Result newForm() { + if (xlog.isDebugEnabled()) + xlog.debug("xnewForm() <-"); + Form form = createForm(null); + + return ok(xrenderForm(null, form)); + } + + private Form createForm(M model) + { + T twixt = createTwixt(); + if (model != null) + { + twixt.copyFrom(model); + } + Form frm = Form.form(this.twixtClass).fill(twixt); + return frm; + + } + + @Override + public Result create() + { + if (xlog.isDebugEnabled()) + xlog.debug("ccXcreate() <-"); + + TwixtBinder binder = new TwixtBinder(twixtClass); + boolean b = binder.bind(); + Form filledForm = binder.getForm(); + + if (!b) { + if (xlog.isDebugEnabled()) + xlog.debug("Xvalidation errors occured: " + binder.getValidationErrors()); // filledForm.errors()); + + return badRequest(xrenderForm(null, filledForm)); + } else { + T twixt = filledForm.get(); + M model = createModel(); + if (model == null) + { + return badRequest(xrenderForm(null, filledForm)); + } + + twixt.copyTo(model); + + DAO dao = getDao(); + dao.create(model); + if (xlog.isDebugEnabled()) + xlog.debug("Xentity created"); + + Call index = toIndex(); + if (xlog.isDebugEnabled()) + xlog.debug("Xindex : " + index); + return redirect(index); + } + } + + public Result editForm(K key) { + if (xlog.isDebugEnabled()) + xlog.debug("editForm() <-" + key); + + M model = this.getDao().get(key); + if (xlog.isDebugEnabled()) + xlog.debug("model : " + model); + + Form frm = createForm(model); + return ok(xrenderForm(key, frm)); + } + + public Result update(K key) { + if (xlog.isDebugEnabled()) + xlog.debug("update() <-" + key); + + M model = getDao().get(key); + T twixt = createTwixt(); + twixt.copyFrom(model); + + TwixtBinder binder = new TwixtBinder(twixtClass, twixt); + boolean b = binder.bind(); + Form filledForm = binder.getForm(); + + if (! b) { + if (xlog.isDebugEnabled()) + xlog.debug("validation errors occured: " + binder.getValidationErrors()); + + return badRequest(xrenderForm(key, filledForm)); + } else { + twixt = filledForm.get(); + twixt.copyTo(model); + if (xlog.isDebugEnabled()) + xlog.debug("model : " + model); + getDao().update(model); + if (xlog.isDebugEnabled()) + xlog.debug("entity updated"); + + Call index = toIndex(); + if (xlog.isDebugEnabled()) + xlog.debug("index : " + index); + return redirect(index); + } + } + + + protected M createModel() + { + M model = null; + try { + model = this.getModelClass().newInstance(); + } catch (Exception e) { + e.printStackTrace(); + } + return model; + } + + protected T createTwixt() + { + T twixt = null; + try { + twixt = this.twixtClass.newInstance(); + } catch (Exception e) { + e.printStackTrace(); + } + return twixt; + } + + protected String getModelTemplatePrefix() + { + return this.getModelClass().getSimpleName(); + } + protected String genTemplate(String s) + { + s = getModelTemplatePrefix() + s; + + String tmp = s.substring(0, 1).toLowerCase(); + tmp += s.substring(1); + + return tmp; + } + + @Override + protected String templateForList() { + return genTemplate("List"); + } + + @Override + protected String templateForForm() { + return genTemplate("Form"); + } + + @Override + protected String templateForShow() { + return genTemplate("Show"); + } + + + +} diff --git a/samples/issue54/m8/app/controllers/SampleController.java b/samples/issue54/m8/app/controllers/SampleController.java new file mode 100644 index 0000000..0b4938e --- /dev/null +++ b/samples/issue54/m8/app/controllers/SampleController.java @@ -0,0 +1,41 @@ +package controllers; + +import javax.inject.Inject; + +import org.mef.twixt.controllers.TwixtController; + +import mef.validate.SampleTwixt; +import models.Sample; +import models.dao.SampleDAO; +import play.mvc.Call; + +public class SampleController extends TwixtController { + + @Inject + public SampleController(SampleDAO dao) + { + super(dao, Long.class, Sample.class, SampleTwixt.class, 10, "name"); + } + + + @Override + protected Call toIndex() { + return routes.Application.index(); + } + + @Override + protected String templateForList() { + return genTemplate("List"); + } + + @Override + protected String templateForForm() { + return genTemplate("Form"); + } + + @Override + protected String templateForShow() { + return genTemplate("Show"); + } + +} diff --git a/samples/issue54/m8/app/controllers/SimpleController.java b/samples/issue54/m8/app/controllers/SimpleController.java new file mode 100644 index 0000000..7ed0ccc --- /dev/null +++ b/samples/issue54/m8/app/controllers/SimpleController.java @@ -0,0 +1,33 @@ +package controllers; + +import javax.inject.Inject; + +import models.Simple; +import models.dao.SimpleDAO; +import play.mvc.Call; +import play.utils.crud.CRUDController; +import static play.data.Form.*; + +public class SimpleController extends CRUDController { + @Inject + public SimpleController(SimpleDAO dao) { + super(dao, form(Simple.class), Long.class, Simple.class, 10, "name"); + } + + protected String templateForList() { + return "sampleList"; + } + + protected String templateForForm() { + return "sampleForm"; + } + + protected String templateForShow() { + return "sampleShow"; + } + + @Override + protected Call toIndex() { + return routes.Application.index(); + } +} \ No newline at end of file diff --git a/samples/issue54/m8/app/controllers/TaxiController.java b/samples/issue54/m8/app/controllers/TaxiController.java new file mode 100644 index 0000000..acc38f8 --- /dev/null +++ b/samples/issue54/m8/app/controllers/TaxiController.java @@ -0,0 +1,67 @@ +package controllers; + +import java.lang.reflect.Field; + +import javax.inject.Inject; + + + +import org.mef.twixt.controllers.DynamicTwixtController; + +import mef.validate.TaxiTwixt; +import models.Taxi; +import models.dao.TaxiDAO; +import play.mvc.Call; +import play.utils.meta.FieldMetadata; +import play.utils.meta.form.FormFieldWidget; +import play.utils.meta.form.TextAreaWidget; + +//change base class and this controller is no longer seen by ClasspathScanningControllerRegistry +//public class TaxiController extends DynamicTwixtController +public class TaxiController extends MyDynamicTwixtController +{ + @Inject + public TaxiController(TaxiDAO dao) + { + super(dao, Long.class, Taxi.class, TaxiTwixt.class, 10, "name"); + templatePackageName = "views.html.taxi."; + } + + @Override + protected FormFieldWidget createWidget(Field f, FieldMetadata meta) + { + if (f.getName().equals("name")) + { + TextAreaWidget w = new TextAreaWidget(meta); + return w; + } + else + { + return super.createWidget(f, meta); + } + } + + + @Override + protected String templateForList() { + return genTemplate("List"); + } + + @Override + protected String templateForForm() { + return genTemplate("Form"); + } + + @Override + protected String templateForShow() { + return genTemplate("Show"); + } + + + + @Override + protected Call toIndex() { + return routes.Application.index(); + } + +} diff --git a/samples/issue54/m8/app/mef/validate/AccountType.java b/samples/issue54/m8/app/mef/validate/AccountType.java new file mode 100644 index 0000000..c1c100f --- /dev/null +++ b/samples/issue54/m8/app/mef/validate/AccountType.java @@ -0,0 +1,28 @@ +package mef.validate; + +import java.util.HashMap; +import java.util.Map; + +import org.mef.twixt.LongSelectValue; + + +public class AccountType extends LongSelectValue +{ + public AccountType() + { + this(1L); + } + public AccountType(Long id) + { + super(id, accountTypes()); + } + + private static Map accountTypes() + { + Map map = new HashMap(); + map.put(1L, "normal"); + map.put(2L, "premium"); + map.put(3L, "executive"); + return map; + } +} \ No newline at end of file diff --git a/samples/issue54/m8/app/mef/validate/SampleTwixt.java b/samples/issue54/m8/app/mef/validate/SampleTwixt.java new file mode 100644 index 0000000..bc8ca6d --- /dev/null +++ b/samples/issue54/m8/app/mef/validate/SampleTwixt.java @@ -0,0 +1,42 @@ +package mef.validate; +import org.mef.twixt.StringValue; +import org.mef.twixt.ValueContainer; +import org.mef.twixt.validate.ValContext; + +import models.Sample; + + +public class SampleTwixt implements ValueContainer +{ + public StringValue name; + + public SampleTwixt() + { + this(""); + } + + public SampleTwixt(String namex) + { + this.name = new StringValue(namex); + } + + @Override + public void validate(ValContext arg0) + { + name.validate(arg0); + } + + @Override + public void copyTo(Object model) + { + Sample m = (Sample)model; + m.setName(this.name.get()); + } + + @Override + public void copyFrom(Object model) + { + Sample m = (Sample)model; + this.name.set(m.getName()); + } +} diff --git a/samples/issue54/m8/app/mef/validate/TaxiTwixt.java b/samples/issue54/m8/app/mef/validate/TaxiTwixt.java new file mode 100644 index 0000000..8dee3d4 --- /dev/null +++ b/samples/issue54/m8/app/mef/validate/TaxiTwixt.java @@ -0,0 +1,96 @@ +package mef.validate; +import java.util.Date; + +import org.mef.twixt.BooleanValue; +import org.mef.twixt.DateValue; +import org.mef.twixt.FileValue; +import org.mef.twixt.IntegerValue; +import org.mef.twixt.StringValue; +import org.mef.twixt.Value; +import org.mef.twixt.binder.TwixtForm; +import org.mef.twixt.validate.ValContext; +import org.mef.twixt.validate.Validator; + +import models.Taxi; + + +import play.Logger; + +public class TaxiTwixt extends TwixtForm +{ + public static class EvenValidator implements Validator + { + @Override + public void validate(ValContext valctx, Value arg1) + { + IntegerValue val = (IntegerValue) arg1; + int n = val.get(); + if (n % 2 != 0) + { + valctx.addError("val {0} not even!", n); + } + } + } + public static class NoAValidator implements Validator + { + @Override + public void validate(ValContext valctx, Value arg1) + { + StringValue val = (StringValue) arg1; + String s = val.get(); + if (s.contains("a")) + { + valctx.addError("val contains a"); + } + } + } + + public StringValue name; + public IntegerValue size; + public DateValue startDate; + public BooleanValue isAdmin; + public AccountType accountTypeId; //name must match Taxi + public FileValue path; + + public String ball; + + public TaxiTwixt() + { + this("", 0); + ball = "red"; + size.setValidator(new EvenValidator()); + name.setValidator(new NoAValidator()); + + size.set(144); + } + + public TaxiTwixt(String namex, int size) + { + this.name = new StringValue(namex); + this.size = new IntegerValue(size); + this.startDate = new DateValue(new Date()); + this.isAdmin = new BooleanValue(false); //MUST be false + this.accountTypeId = new AccountType(); + this.path = new FileValue(); + } + + +// @Override +// public void copyTo(Object model) +// { +// Taxi m = (Taxi)model; +// m.setName(this.name.get()); +// m.setSize(size.get()); +// m.setStartDate(this.startDate.get()); +// } +// +// @Override +// public void copyFrom(Object model) +// { +// Taxi m = (Taxi)model; +// this.name.set(m.getName()); +// this.size.set(m.getSize()); +// this.startDate.set(m.getStartDate()); +// Logger.info("AA: " + this.name); +// } +} diff --git a/samples/issue54/m8/app/models/Complex.java b/samples/issue54/m8/app/models/Complex.java new file mode 100644 index 0000000..f176f8b --- /dev/null +++ b/samples/issue54/m8/app/models/Complex.java @@ -0,0 +1,127 @@ +package models; + +import java.util.Date; + +import javax.persistence.Basic; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; + +import com.avaje.ebean.annotation.EnumValue; + +import play.data.validation.Constraints.MaxLength; +import play.data.validation.Constraints.Required; +import play.db.ebean.Model; +import play.utils.dao.BasicModel; + +@Entity +@SuppressWarnings("serial") +public class Complex extends Model implements BasicModel { + + @Id + private Long key; + + @Basic + @Required + private String stringField; + + @Basic + @Required + @MaxLength(256) + @Column(length = 256) + private String longStringField; + + @Basic + @Required + private Integer integerField; + + @Basic + @Required + private Double doubleField; + + @Basic + @Required + private Boolean booleanField; + + @Basic + @Required + private Date dateField; + + public Long getKey() { + return key; + } + + public void setKey(Long key) { + this.key = key; + } + + public String getStringField() { + return stringField; + } + + public void setStringField(String stringField) { + this.stringField = stringField; + } + + public Integer getIntegerField() { + return integerField; + } + + public void setIntegerField(Integer integerField) { + this.integerField = integerField; + } + + public Double getDoubleField() { + return doubleField; + } + + public void setDoubleField(Double doubleField) { + this.doubleField = doubleField; + } + + public Boolean getBooleanField() { + return booleanField; + } + + public void setBooleanField(Boolean booleanField) { + this.booleanField = booleanField; + } + + public String getLongStringField() { + return longStringField; + } + + public void setLongStringField(String longStringField) { + this.longStringField = longStringField; + } + + public Date getDateField() { + return dateField; + } + + public void setDateField(Date dateField) { + this.dateField = dateField; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("Complex [key=").append(key).append(", stringField=").append(stringField) + .append(", integerField=").append(integerField).append(", doubleField=").append(doubleField) + .append(", booleanField=").append(booleanField).append("]"); + return builder.toString(); + } + + static enum Select { + @EnumValue("1") + OPTION1, + + @EnumValue("2") + OPTION2, + + @EnumValue("3") + OPTION3, + + ; + } +} diff --git a/samples/issue54/m8/app/models/Sample.java b/samples/issue54/m8/app/models/Sample.java new file mode 100644 index 0000000..940d93a --- /dev/null +++ b/samples/issue54/m8/app/models/Sample.java @@ -0,0 +1,53 @@ +package models; + +import javax.persistence.Basic; +import javax.persistence.Entity; +import javax.persistence.Id; + +import play.data.validation.Constraints.Required; +import play.db.ebean.Model; +import play.utils.dao.BasicModel; + +@Entity +@SuppressWarnings("serial") +public class Sample extends Model implements BasicModel { + + @Id + private Long key; + + @Basic + @Required + private String name; + + @Basic + private int randomValue; + + public Long getKey() { + return key; + } + + public void setKey(Long key) { + this.key = key; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getRandomValue() { + return randomValue; + } + + public void setRandomValue(int randomValue) { + this.randomValue = randomValue; + } + + @Override + public String toString() { + return "Sample [key=" + key + ", name=" + name + "]"; + } +} diff --git a/samples/issue54/m8/app/models/Simple.java b/samples/issue54/m8/app/models/Simple.java new file mode 100644 index 0000000..e91b3b1 --- /dev/null +++ b/samples/issue54/m8/app/models/Simple.java @@ -0,0 +1,42 @@ +package models; + +import javax.persistence.Basic; +import javax.persistence.Entity; +import javax.persistence.Id; + +import play.data.validation.Constraints.Required; +import play.db.ebean.Model; +import play.utils.dao.BasicModel; + +@Entity +@SuppressWarnings("serial") +public class Simple extends Model implements BasicModel { + + @Id + private Long key; + + @Basic + @Required + private String name; + + public Long getKey() { + return key; + } + + public void setKey(Long key) { + this.key = key; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "Simple [key=" + key + ", name=" + name + "]"; + } +} diff --git a/samples/issue54/m8/app/models/Taxi.java b/samples/issue54/m8/app/models/Taxi.java new file mode 100644 index 0000000..5748ce6 --- /dev/null +++ b/samples/issue54/m8/app/models/Taxi.java @@ -0,0 +1,100 @@ +package models; + +import java.util.Date; + +import javax.persistence.Basic; +import javax.persistence.Entity; +import javax.persistence.Id; + +import play.data.validation.Constraints.Required; +import play.db.ebean.Model; +import play.utils.dao.BasicModel; + +@Entity +@SuppressWarnings("serial") +public class Taxi extends Model implements BasicModel { + + @Id + private Long key; + + @Basic + @Required + private String name; + + @Basic + private int size; + + @Basic + private Date startDate; + + @Basic + private Boolean isAdmin; + + @Basic + private Long accountTypeId; + + @Basic + private String path; + + + public Date getStartDate() { + return startDate; + } + + public void setStartDate(Date startDate) { + this.startDate = startDate; + } + + public Long getKey() { + return key; + } + + public void setKey(Long key) { + this.key = key; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getSize() { + return size; + } + + public void setSize(int size) { + this.size = size; + } + + @Override + public String toString() { + return "Taxi [key=" + key + ", name=" + name + String.format(", size=%d", size) + "]"; + } + + public Boolean getIsAdmin() { + return isAdmin; + } + + public void setIsAdmin(Boolean isAdmin) { + this.isAdmin = isAdmin; + } + + public Long getAccountTypeId() { + return accountTypeId; + } + + public void setAccountTypeId(Long accountTypeId) { + this.accountTypeId = accountTypeId; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } +} diff --git a/samples/issue54/m8/app/models/dao/SampleDAO.java b/samples/issue54/m8/app/models/dao/SampleDAO.java new file mode 100644 index 0000000..6b94d96 --- /dev/null +++ b/samples/issue54/m8/app/models/dao/SampleDAO.java @@ -0,0 +1,13 @@ +package models.dao; + +import play.utils.dao.BasicDAO; +import models.Sample; + +public class SampleDAO extends BasicDAO { + + public SampleDAO() { + super(Long.class, Sample.class); + addListener(new SampleDAOListener()); + } + +} diff --git a/samples/issue54/m8/app/models/dao/SampleDAOListener.java b/samples/issue54/m8/app/models/dao/SampleDAOListener.java new file mode 100644 index 0000000..9ea1844 --- /dev/null +++ b/samples/issue54/m8/app/models/dao/SampleDAOListener.java @@ -0,0 +1,39 @@ +package models.dao; + +import java.util.Random; + +import models.Sample; +import play.utils.dao.DAOListener; + +public class SampleDAOListener implements DAOListener { + + Random random = new Random(); + + + @Override + public void beforeCreate(Sample m) { + m.setRandomValue(random.nextInt()); + } + + @Override + public void beforeUpdate(Sample m) { + m.setRandomValue(random.nextInt()); + } + + @Override + public void afterCreate(Long key, Sample m) { + } + + @Override + public void afterRemove(Long key, Sample m) { + } + + @Override + public void afterUpdate(Sample m) { + } + + @Override + public void beforeRemove(Long key) { + } + +} diff --git a/samples/issue54/m8/app/models/dao/SimpleDAO.java b/samples/issue54/m8/app/models/dao/SimpleDAO.java new file mode 100644 index 0000000..ba1b490 --- /dev/null +++ b/samples/issue54/m8/app/models/dao/SimpleDAO.java @@ -0,0 +1,12 @@ +package models.dao; + +import play.utils.dao.BasicDAO; +import models.Simple; + +public class SimpleDAO extends BasicDAO { + + public SimpleDAO() { + super(Long.class, Simple.class); + } + +} diff --git a/samples/issue54/m8/app/models/dao/TaxiDAO.java b/samples/issue54/m8/app/models/dao/TaxiDAO.java new file mode 100644 index 0000000..596f865 --- /dev/null +++ b/samples/issue54/m8/app/models/dao/TaxiDAO.java @@ -0,0 +1,13 @@ +package models.dao; + +import play.utils.dao.BasicDAO; +import models.Taxi; + +public class TaxiDAO extends BasicDAO { + + public TaxiDAO() { + super(Long.class, Taxi.class); + //addListener(new TaxiDAOListener()); + } + +} diff --git a/samples/issue54/m8/app/views/SimpleForm.scala.html b/samples/issue54/m8/app/views/SimpleForm.scala.html new file mode 100644 index 0000000..68f2a71 --- /dev/null +++ b/samples/issue54/m8/app/views/SimpleForm.scala.html @@ -0,0 +1,16 @@ +@(key: Long, simpleForm: Form[Simple]) +@import helper._ + +@main(if(key != null) "update simple" else "new simple") { + +

@if(key != null) { update simple } else { new simple }

+ + @form(if(key != null) { play.utils.crud.routes.RouterCrudController.update("Simple", simpleForm.get().getKey() + "") } + else { play.utils.crud.routes.RouterCrudController.create("Simple") }) { + + @inputText(simpleForm("name"), args = '_label -> "name") +
RRR + +
+ } +} diff --git a/samples/issue54/m8/app/views/SimpleList.scala.html b/samples/issue54/m8/app/views/SimpleList.scala.html new file mode 100644 index 0000000..0acebb2 --- /dev/null +++ b/samples/issue54/m8/app/views/SimpleList.scala.html @@ -0,0 +1,39 @@ +@(page: com.avaje.ebean.Page[Simple]) +@import helper._ +@import helper.twitterBootstrap._ + +@main("list") { + +

@page.getTotalRowCount() objects

+ + @if(page.getTotalRowCount() == 0) { + +
+ xxNothing to display +
+ + } else { + + + + + + + + + + @for(simple <- page.getList()) { + + + + + } + +
keyname
TT + @simple.getKey() + + @simple.getName() +
+ + } +} diff --git a/samples/issue54/m8/app/views/index.scala.html b/samples/issue54/m8/app/views/index.scala.html new file mode 100644 index 0000000..4eda399 --- /dev/null +++ b/samples/issue54/m8/app/views/index.scala.html @@ -0,0 +1,12 @@ + + + + + play2-crud + + + + + diff --git a/samples/issue54/m8/app/views/main.scala.html b/samples/issue54/m8/app/views/main.scala.html new file mode 100644 index 0000000..00ffff7 --- /dev/null +++ b/samples/issue54/m8/app/views/main.scala.html @@ -0,0 +1,15 @@ +@(title: String)(content: Html) + + + + + + @title + + + + + + @content + + diff --git a/samples/issue54/m8/app/views/sampleForm.scala.html b/samples/issue54/m8/app/views/sampleForm.scala.html new file mode 100644 index 0000000..958e47c --- /dev/null +++ b/samples/issue54/m8/app/views/sampleForm.scala.html @@ -0,0 +1,16 @@ +@(key: Long, sampleForm: Form[mef.validate.SampleTwixt]) +@import helper._ + +@main(if(key != null) "update sample" else "new sample") { + +

@if(key != null) { update sample } else { new sample } RTT

+ + @form(if(key != null) { play.utils.crud.routes.RouterCrudController.update("Sample", key.toString() + "") } + else { play.utils.crud.routes.RouterCrudController.create("Sample") }) { + + @inputText(sampleForm("name"), args = '_label -> "name") +
+ +
+ } +} diff --git a/samples/issue54/m8/app/views/sampleList.scala.html b/samples/issue54/m8/app/views/sampleList.scala.html new file mode 100644 index 0000000..02601c3 --- /dev/null +++ b/samples/issue54/m8/app/views/sampleList.scala.html @@ -0,0 +1,48 @@ +@(page: com.avaje.ebean.Page[Sample]) +@import helper._ +@import helper.twitterBootstrap._ + + + +@main("list") { + +

@page.getTotalRowCount() objects

+ + @if(page.getTotalRowCount() == 0) { + +
+ Nothing to display +
+ + } else { + + + + + + + + + + @for(sample <- page.getList()) { + + + + + + + } + +
namerandom value
+ @util.linkShow(sample.getKey(), sample) + + @sample.getName() + + @sample.getRandomValue() + + @util.linkEdit(sample.getKey(), sample) + @util.linkDelete(sample.getKey(), sample) +
+ + } +} diff --git a/samples/issue54/m8/app/views/sampleShow.scala.html b/samples/issue54/m8/app/views/sampleShow.scala.html new file mode 100644 index 0000000..0248568 --- /dev/null +++ b/samples/issue54/m8/app/views/sampleShow.scala.html @@ -0,0 +1,11 @@ +@(sample: Sample) +@import helper._ + +@main("show") { + +
+
Sample
+
@sample.getName()
+
+ +} diff --git a/samples/issue54/m8/app/views/taxi/taxiForm.scala.html b/samples/issue54/m8/app/views/taxi/taxiForm.scala.html new file mode 100644 index 0000000..6dc910e --- /dev/null +++ b/samples/issue54/m8/app/views/taxi/taxiForm.scala.html @@ -0,0 +1,20 @@ +@(key: Long, taxiForm: Form[mef.validate.TaxiTwixt], fieldL:ArrayList[play.utils.meta.FieldMetadata]) +@import helper._ + +@main(if(key != null) "update taxi" else "new taxi") { + +

@if(key != null) { update taxi } else { new taxi } RTT

+ + @form(if(key != null) { play.utils.crud.routes.RouterCrudController.update("Taxi", key.toString() + "") } + else { play.utils.crud.routes.RouterCrudController.create("Taxi") }) { + + @for(field <- fieldL) { +
B + @field.getWidget.render(taxiForm) +
+ } +
+ +
+ } +} diff --git a/samples/issue54/m8/app/views/taxi/taxiList.scala.html b/samples/issue54/m8/app/views/taxi/taxiList.scala.html new file mode 100644 index 0000000..ec096a0 --- /dev/null +++ b/samples/issue54/m8/app/views/taxi/taxiList.scala.html @@ -0,0 +1,57 @@ +@(page: com.avaje.ebean.Page[Taxi]) +@import helper._ +@import helper.twitterBootstrap._ + + + +@main("list") { + +

@page.getTotalRowCount() objects

+ + @if(page.getTotalRowCount() == 0) { + +
+ Nothing to display +
+ + } else { + + + + + + + + + + @for(sample <- page.getList()) { + + + + + + + + + + } + +
namerandom value
+ @util.linkShow(sample.getKey(), sample) + + @sample.getName() + + @sample.getSize() + + @sample.getStartDate() + + @sample.getIsAdmin() + + @sample.getAccountTypeId() + + @util.linkEdit(sample.getKey(), sample) + @util.linkDelete(sample.getKey(), sample) +
+ + } +} diff --git a/samples/issue54/m8/app/views/util/linkDelete.scala.html b/samples/issue54/m8/app/views/util/linkDelete.scala.html new file mode 100644 index 0000000..e1ca0fe --- /dev/null +++ b/samples/issue54/m8/app/views/util/linkDelete.scala.html @@ -0,0 +1,2 @@ +@(key:Long, model:Object) +Delete diff --git a/samples/issue54/m8/app/views/util/linkEdit.scala.html b/samples/issue54/m8/app/views/util/linkEdit.scala.html new file mode 100644 index 0000000..b53ab1d --- /dev/null +++ b/samples/issue54/m8/app/views/util/linkEdit.scala.html @@ -0,0 +1,2 @@ +@(key:Long, model:Object) +Edit diff --git a/samples/issue54/m8/app/views/util/linkShow.scala.html b/samples/issue54/m8/app/views/util/linkShow.scala.html new file mode 100644 index 0000000..5e1897b --- /dev/null +++ b/samples/issue54/m8/app/views/util/linkShow.scala.html @@ -0,0 +1,2 @@ +@(key:Long, model:Object) +@key.toString() diff --git a/samples/issue54/m8/build.sbt b/samples/issue54/m8/build.sbt new file mode 100644 index 0000000..9781f7c --- /dev/null +++ b/samples/issue54/m8/build.sbt @@ -0,0 +1,24 @@ +name := "issue54" + +version := "0.7.4-SNAPSHOT" + +scalaVersion := "2.11.4" + +libraryDependencies ++= Seq( + javaCore, javaJdbc, javaEbean, + "play2-crud" %% "play2-crud" % "0.7.4-SNAPSHOT", + "play2-crud" %% "play2-crud" % "0.7.4-SNAPSHOT" classifier "assets", + "twixt54" % "twixt54_2.11" % "0.1.0-SNAPSHOT" +) + +lazy val root = (project in file(".")).enablePlugins(PlayJava) + +//resolvers += "release repository" at "http://hakandilek.github.com/maven-repo/releases/" +// +//resolvers += "snapshot repository" at "http://hakandilek.github.com/maven-repo/snapshots/" + +//for heroku, uncomment next line and do +// play eclipse clean compile +//resolvers += Resolver.url("Mettle Repository", url("http://ianrae.github.io/snapshot/"))(Resolver.ivyStylePatterns) + + diff --git a/samples/issue54/m8/conf/application.conf b/samples/issue54/m8/conf/application.conf new file mode 100644 index 0000000..0bd4220 --- /dev/null +++ b/samples/issue54/m8/conf/application.conf @@ -0,0 +1,74 @@ +# This is the main configuration file for the application. +# ~~~~~ + +# Secret key +# ~~~~~ +# The secret key is used to secure cryptographics functions. +# If you deploy your application to several instances be sure to use the same key! +application.secret="Rf3yXR0uSe:<7Se28ifC6[?HNcP / play.crud.Routes diff --git a/samples/issue54/m8/project/build.properties b/samples/issue54/m8/project/build.properties new file mode 100644 index 0000000..bb200f4 --- /dev/null +++ b/samples/issue54/m8/project/build.properties @@ -0,0 +1,2 @@ +sbt.version=0.13.7 + diff --git a/samples/issue54/m8/project/plugins.sbt b/samples/issue54/m8/project/plugins.sbt new file mode 100644 index 0000000..14a9e93 --- /dev/null +++ b/samples/issue54/m8/project/plugins.sbt @@ -0,0 +1,9 @@ +// Comment to get more information during initialization +logLevel := Level.Warn + +// The Typesafe repository +resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/" + +// The Play plugin +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.7") + diff --git a/samples/issue54/m8/public/images/favicon.png b/samples/issue54/m8/public/images/favicon.png new file mode 100644 index 0000000..c7d92d2 Binary files /dev/null and b/samples/issue54/m8/public/images/favicon.png differ diff --git a/samples/issue54/m8/public/javascripts/jquery-1.7.1.min.js b/samples/issue54/m8/public/javascripts/jquery-1.7.1.min.js new file mode 100644 index 0000000..198b3ff --- /dev/null +++ b/samples/issue54/m8/public/javascripts/jquery-1.7.1.min.js @@ -0,0 +1,4 @@ +/*! jQuery v1.7.1 jquery.com | jquery.org/license */ +(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!ck[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cl||(cl=c.createElement("iframe"),cl.frameBorder=cl.width=cl.height=0),b.appendChild(cl);if(!cm||!cl.createElement)cm=(cl.contentWindow||cl.contentDocument).document,cm.write((c.compatMode==="CSS1Compat"?"":"")+""),cm.close();d=cm.createElement(a),cm.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cl)}ck[a]=e}return ck[a]}function cu(a,b){var c={};f.each(cq.concat.apply([],cq.slice(0,b)),function(){c[this]=a});return c}function ct(){cr=b}function cs(){setTimeout(ct,0);return cr=f.now()}function cj(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ci(){try{return new a.XMLHttpRequest}catch(b){}}function cc(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border")for(;g=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.1",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
a",d=q.getElementsByTagName("*"),e=q.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=q.getElementsByTagName("input")[0],b={leadingWhitespace:q.firstChild.nodeType===3,tbody:!q.getElementsByTagName("tbody").length,htmlSerialize:!!q.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:q.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete q.test}catch(s){b.deleteExpando=!1}!q.addEventListener&&q.attachEvent&&q.fireEvent&&(q.attachEvent("onclick",function(){b.noCloneEvent=!1}),q.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),q.appendChild(i),k=c.createDocumentFragment(),k.appendChild(q.lastChild),b.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,k.removeChild(i),k.appendChild(q),q.innerHTML="",a.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",q.style.width="2px",q.appendChild(j),b.reliableMarginRight=(parseInt((a.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(q.attachEvent)for(o in{submit:1,change:1,focusin:1})n="on"+o,p=n in q,p||(q.setAttribute(n,"return;"),p=typeof q[n]=="function"),b[o+"Bubbles"]=p;k.removeChild(q),k=g=h=j=q=i=null,f(function(){var a,d,e,g,h,i,j,k,m,n,o,r=c.getElementsByTagName("body")[0];!r||(j=1,k="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",m="visibility:hidden;border:0;",n="style='"+k+"border:5px solid #000;padding:0;'",o="
"+""+"
",a=c.createElement("div"),a.style.cssText=m+"width:0;height:0;position:static;top:0;margin-top:"+j+"px",r.insertBefore(a,r.firstChild),q=c.createElement("div"),a.appendChild(q),q.innerHTML="
t
",l=q.getElementsByTagName("td"),p=l[0].offsetHeight===0,l[0].style.display="",l[1].style.display="none",b.reliableHiddenOffsets=p&&l[0].offsetHeight===0,q.innerHTML="",q.style.width=q.style.paddingLeft="1px",f.boxModel=b.boxModel=q.offsetWidth===2,typeof q.style.zoom!="undefined"&&(q.style.display="inline",q.style.zoom=1,b.inlineBlockNeedsLayout=q.offsetWidth===2,q.style.display="",q.innerHTML="
",b.shrinkWrapBlocks=q.offsetWidth!==2),q.style.cssText=k+m,q.innerHTML=o,d=q.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,i={doesNotAddBorder:e.offsetTop!==5,doesAddBorderForTableAndCells:h.offsetTop===5},e.style.position="fixed",e.style.top="20px",i.fixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",i.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,i.doesNotIncludeMarginInBodyOffset=r.offsetTop!==j,r.removeChild(a),q=a=null,f.extend(b,i))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;h=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/\bhover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")}; +f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;le&&i.push({elem:this,matches:d.slice(e)});for(j=0;j0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
","
"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function() +{for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||!bc.test("<"+a.nodeName)?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!_.test(k))k=b.createTextNode(k);else{k=k.replace(Y,"<$1>");var l=(Z.exec(k)||["",""])[1].toLowerCase(),m=bg[l]||bg._default,n=m[0],o=b.createElement("div");b===c?bh.appendChild(o):U(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=$.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&X.test(k)&&o.insertBefore(b.createTextNode(X.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return br.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bq,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bq.test(g)?g.replace(bq,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bz(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bA=function(a,b){var c,d,e;b=b.replace(bs,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b)));return c}),c.documentElement.currentStyle&&(bB=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bt.test(f)&&bu.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bz=bA||bB,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bD=/%20/g,bE=/\[\]$/,bF=/\r?\n/g,bG=/#.*$/,bH=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bI=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bJ=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bK=/^(?:GET|HEAD)$/,bL=/^\/\//,bM=/\?/,bN=/)<[^<]*)*<\/script>/gi,bO=/^(?:select|textarea)/i,bP=/\s+/,bQ=/([?&])_=[^&]*/,bR=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bS=f.fn.load,bT={},bU={},bV,bW,bX=["*/"]+["*"];try{bV=e.href}catch(bY){bV=c.createElement("a"),bV.href="",bV=bV.href}bW=bR.exec(bV.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bS)return bS.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bN,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bO.test(this.nodeName)||bI.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bF,"\r\n")}}):{name:b.name,value:c.replace(bF,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b_(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b_(a,b);return a},ajaxSettings:{url:bV,isLocal:bJ.test(bW[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bX},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bZ(bT),ajaxTransport:bZ(bU),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cb(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cc(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bH.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bG,"").replace(bL,bW[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bP),d.crossDomain==null&&(r=bR.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bW[1]&&r[2]==bW[2]&&(r[3]||(r[1]==="http:"?80:443))==(bW[3]||(bW[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),b$(bT,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bK.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bM.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bQ,"$1_="+x);d.url=y+(y===d.url?(bM.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bX+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=b$(bU,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)ca(g,a[g],c,e);return d.join("&").replace(bD,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cd=f.now(),ce=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cd++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ce.test(b.url)||e&&ce.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ce,l),b.url===j&&(e&&(k=k.replace(ce,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cf=a.ActiveXObject?function(){for(var a in ch)ch[a](0,1)}:!1,cg=0,ch;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ci()||cj()}:ci,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cf&&delete ch[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cg,cf&&(ch||(ch={},f(a).unload(cf)),ch[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var ck={},cl,cm,cn=/^(?:toggle|show|hide)$/,co=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cp,cq=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cr;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cu("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cy(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cy(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); \ No newline at end of file diff --git a/samples/issue54/m8/tutorial/index.html b/samples/issue54/m8/tutorial/index.html new file mode 100644 index 0000000..7dab1ea --- /dev/null +++ b/samples/issue54/m8/tutorial/index.html @@ -0,0 +1,123 @@ + + + Play + Java + Spring + + +
+

View the App

+ +

+ THIS TUTORIAL IS UNDER CONSTRUCTION!
+ + Once the application has been compiled and the server started, your application can be accessed at: http://localhost:9000
+ Check in Run to see the server status.
+ This example app stores and displays a list of "Bar" objects in a database. +

+
+
+

Handling Requests

+ +

+ When you make an HTTP request to that URL, the Play server figures out what code to execute which will handle the request and return a response. In this application the request handler for requests to the root URL (e.g. "/") are handled by a Java Controller. You can use Java and Scala to create your controllers.
+ Controllers asynchronously return HTTP responses of any content type (i.e. HTML, JSON, binary). To see a JSON response produced by a Scala controller, click the "Get JSON Message" button. This uses AJAX via jQuery to get data from the server and then display that on the web page. +

+
+
+

Code Overview

+ +

A Play application has very few required files. This example tries to illustrate a number of key concepts in Play so it has a handful of files to explain. The app directory contains the application's source code, including Java, Scala, server-side templates, and server-side compiled assets for the browser (e.g. JavaScript, CoffeeScript, LESS). The conf directory contains the application.conf config file and the routes file. The project directory contains the SBT build information for the app. The default place for static assets (e.g. images, HTML, CSS, JavaScript) is the public directory. Unit, functional, and integration tests (Java and Scala) go in the test directory.

+
+
+

HTTP Routing

+ +

+ The routes file contains mappings between HTTP request verbs & paths and the code that controller method that handles the requests. Route paths can be parametrized to support RESTful style URLs. The following mapping will handle HTTP GET requests to "/" using the controllers.MainController.index method: +

GET   /    controllers.MainController.index
+ You can try out the routing by first making a request to an unmatched route, like: http://localhost:9000/foo
+ This should produce an error because there isn't a route that matches the request. + Then edit the routes to include a new route: +
GET   /foo    controllers.MainController.index
+ Now retry the request and you should see the default index page.
+ By default the routes are compiled by Play so that they can be used in a type-safe way. This prevents issues caused by incorrect URL usages. + +

+
+
+

Controllers

+ +

+ HTTP GET requests to http://localhost:9000/ are handled by the index method defined in the MainController.java file. This static method returns a Result which in this case consists of a server-side rendered template and an HTTP 200 response which is represented by the ok method call. You can also use non-static methods along with Dependency Injection frameworks like Spring, Guice, and CDI.
+ You can change the "Hello from Java" string in MainController.java a different value, like "Hello from Play", then save the file and reload the index page in your browser. Play will automatically recompile the controller and you should immediately see your changes.
+ If your changes result in a compile error you will see the error in your browser (after a refresh) and in the Compile Plugin. Try it by removing one of the double-quotes in the index method's string, save the file, and reload the index page. Fix the error before you continue.
+ There is also a Scala controller which provides an example JSON service. (Note: Play also has built-in JSON support in Java controllers.) You can see the JSON by opening http://localhost:9000/message in your browser. This route is also mapped in the routes file but this time the request is handled by the MessageController.scala file's getMessage function. This controller returns an Action which is responsible for producing a HTTP response. In this case the response is a case class which is serialized to JSON. You will also notice the MessageController contains a function called javascriptRoutes which provides a mechanism for using the type-safe routes in JavaScript. + +

+
+
+

Templates

+ +

+ The MainController.index method uses a template which is compiled by Play into a function. The source is in the index.scala.html file and is available by calling the views.html.index.render function. This template takes a parameter of a String so the compiled function also takes a String parameter. The default templating language in Play is Scala, but a number of other templating languages (like Java, Groovy, and Dust) can also be plugged in.
+ The index template uses the main template by calling the main function with the required parameters.
+ Notice that the templates do not use hard-coded URLs and instead use Play's reverse routing, like: +

@routes.Assets.at("images/favicon.png")
+ This provides a type-safe way to specify URLs in an application. If the URL to a file changes, then automatically, the new value will be used throughout all of the URL references. You can use the reverse router anywhere in your code, like redirects in controllers.
+ Like controllers (and all source code in Play) you can make changes to templates which triggers a recompile. Then simply refresh the page to see the changes. Try it out by making a change to the index template, saving the file, and then refreshing the index page in your browser. + +

+
+
+

Static Assets

+ +

+ This application has a route to handle loading static assets from the public directory: +

GET /assets/*file controllers.Assets.at(path="/public", file)
+ A request to http://localhost:9000/assets/images/favicon.png will use this route to find the favicon.png file in the public/images directory.
+ You can put any static assets into the public directory and have them accessible in this way. + +

+
+
+

Asset Compiler

+ +

+ Modern web applications often use compilers to generate static assets for the browser. Play has a built-in asset compiler which can automatically syntax check & minify JavaScript, compile CoffeeScript to JavaScript, and compile LESS to CSS. You can also plug in other compilers into the Play's asset compiler.
+ This application contains two example uses of the asset compiler. The index.js file will automatically be syntax checked and minified. The minified version is can be referenced in a template with: +

<script type='text/javascript'
+src='@routes.Assets.at("javascripts/index.min.js")'>
+</script>
+ You can also put CoffeeScript files with a .coffee extension in the app/assets/javascripts directory to have them automatically compiled and minified.
+ The index.less file will be compiled into both a index.css and a index.min.css file. + +

+
+
+

Test the App

+ +

+ This application includes a number of tests which are automatically run after each successful compile. You can see the test results in the Test plugin. Due to changes that you may have made to your application source code, some of the tests might now be failing. The source for the tests is located in the test directory and contains both Java and Scala tests. The test/MainControllerTest.java Java class contains a number of unit and functional tests for the MainController. The IntegrationTest.java file contains an test which uses Solenium to test the actual UI in a browser. The default testing framework for Java in Play is JUnit, but you can plug any testing framework in.
+ The MessageControllerSpec.scala file contains a functional test for the MessageController.
+ If you modify a test and save the changes, then the tests will automatically be re-run. + +

+
+
+

SBT Build

+ +

+ SBT is the build system underneath Play applications. It is responsible for resolving dependencies, compiling the project, running the tests, etc. The primary build definition file is the Build.scala file. In that file you will see a comma-seperated list of dependencies which can be modified to suit your needs. Play includes a number of libraries which can selectively be turned on/off. For instance if you want to use JPA with Play, uncomment the javaJpa dependency. You can also add your own dependencies in the SBT form: +

"group" % "artifact" % "version"
+ +

+
+
+

Further Learning

+ +

+ The Play Documentation contains much more exhaustive details and also covers a number of other topics which haven't been addressed in this tutorial.
+ StackOverflow is a great place ask questions about Play.
+ The play-framework Google Group is a great place to discuss Play.
+

+
+ + diff --git a/samples/issue54/twixt/.gitignore b/samples/issue54/twixt/.gitignore new file mode 100644 index 0000000..580b7fb --- /dev/null +++ b/samples/issue54/twixt/.gitignore @@ -0,0 +1,18 @@ +logs +project/project +project/target +target +tmp +.history +dist +/.idea +/.cache +/.target +/*.iml +/out +/.idea_modules +/.classpath +/.project +/RUNNING_PID +/.settings +/project/*-shim.sbt diff --git a/samples/issue54/twixt/README.md b/samples/issue54/twixt/README.md new file mode 100644 index 0000000..d3cce57 --- /dev/null +++ b/samples/issue54/twixt/README.md @@ -0,0 +1,87 @@ +Twixt is an alternative binding framework for Java Play apps. + +Play documentation recommends making forms from model objects. The model's validation annotations are used +during binding. This works well in simple CRUD scenarios, but has limitations in larger applications: + + * the form may only have a subset of model fields. Binding may fail due to missing @Required fields. + * the same field may be used on multiple forms with different validation requirements. + * a form may contain fields from multiple models. + +A twixt is an object that sits between the model and the view. Twixt fields have their own validation and formatting, separate + from the model. A typical edit action would get a model from the database, load it into a twixt, and render the twixt in a form. + + User user = User.find().getById(id); //get model + UserTwixt twixt = new UserTwixt(); //create twixt + twixt.copyFromModel(user); //load twixt + Form form = Form.form(UserTwixt.class).fill(twixt); + //..render the form + +copyFromModel copies fields automatically, or can be overridden to do custom copying. + +On post-back, use TwixtBinder to bind the HTTP request into your TwixtForm. copyToModel will +copy the validated data into the model. + + TwixtBinder binder = new TwixtBinder(); + if (! binder.bind()) { + Form form = Form.form(UserTwixt.class).fill(binder.get()); + //render the form again with validation errors and user's input data + } else { + UserTwixt twixt = binder.get(); + User user = User.find().getById(id); + twixt.copyToModel(user); + user.update(); + } + + Use Twixt value object as public fields in your twixt class. A value class contains its own validation and formatting. + Twixt provides BooleanValue, IntegerValue, StringValue, ListValue, and others. + Create your own classes to represent the types of data you need: + + public class PhoneNumberValue extends StringValue + { + class PhoneValidator { + public void validate(ValContext vtx, Value val) { + Pattern p = Pattern.compile("^(?=.{7,32}$)(\\(?\\+?[0-9]*\\)?)?[0-9_\\- \\(\\)]*((\\s?x\\s?|ext\\s?|extension\\s?)\\d{1,5}){0,1}$"); + String phone = val.toString(); + boolean matchFound = p.matcher(phone).matches(); + if (! matchFound) { + vtx.addError("Not a valid phone number."); + } + } + } + + public PhoneNumberValue() + { + super(); + setValidator(new PhoneValidator()); + } + } + +Validation is code-based, not annotation-based. + +## Controllers and Views +Use twixt objects in your controllers. The view renders the value field in the normal way. +For example, if the twixt form had a StringValue field named name, render +it like this: + + @inputText(sampleForm("name")) + +## Automatic CRUD Controllers with play-crud +Twixt integrates with play-crud (https://github.com/hakandilek/play2-crud) to provide automatic CRUD. +This can be done in two ways. + +### Dynamic CRUD +The simplest approach is to create a controller based on DynamicTwixtController, telling it which model and twixt to use. This controller +standard CRUD actions (index, newForm, create, edit, update, show, and index), and views for each. +The model must derive from BasicModel. + +### Custom Controller and View +To extend or customize, create a controller class based on TwixtController. It defines the same CRUD actions as DynamicTwixtController. +You must define the views for each. + + + + + + + + diff --git a/samples/issue54/twixt/code/.gitignore b/samples/issue54/twixt/code/.gitignore new file mode 100644 index 0000000..580b7fb --- /dev/null +++ b/samples/issue54/twixt/code/.gitignore @@ -0,0 +1,18 @@ +logs +project/project +project/target +target +tmp +.history +dist +/.idea +/.cache +/.target +/*.iml +/out +/.idea_modules +/.classpath +/.project +/RUNNING_PID +/.settings +/project/*-shim.sbt diff --git a/samples/issue54/twixt/code/LICENSE b/samples/issue54/twixt/code/LICENSE new file mode 100644 index 0000000..a021544 --- /dev/null +++ b/samples/issue54/twixt/code/LICENSE @@ -0,0 +1,13 @@ +Copyright 2013 Typesafe, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/samples/issue54/twixt/code/activator-launch-1.2.12.jar b/samples/issue54/twixt/code/activator-launch-1.2.12.jar new file mode 100644 index 0000000..4a8963c Binary files /dev/null and b/samples/issue54/twixt/code/activator-launch-1.2.12.jar differ diff --git a/samples/issue54/twixt/code/activator.bat b/samples/issue54/twixt/code/activator.bat new file mode 100644 index 0000000..d078287 --- /dev/null +++ b/samples/issue54/twixt/code/activator.bat @@ -0,0 +1,227 @@ +@REM activator launcher script +@REM +@REM Envioronment: +@REM JAVA_HOME - location of a JDK home dir (optional if java on path) +@REM CFG_OPTS - JVM options (optional) +@REM Configuration: +@REM activatorconfig.txt found in the ACTIVATOR_HOME or ACTIVATOR_HOME/ACTIVATOR_VERSION +@setlocal enabledelayedexpansion + +@echo off + +set "var1=%~1" +if defined var1 ( + if "%var1%"=="help" ( + echo. + echo Usage activator [options] [command] + echo. + echo Commands: + echo ui Start the Activator UI + echo new [name] [template-id] Create a new project with [name] using template [template-id] + echo list-templates Print all available template names + echo help Print this message + echo. + echo Options: + echo -jvm-debug [port] Turn on JVM debugging, open at the given port. Defaults to 9999 if no port given. + echo. + echo Environment variables ^(read from context^): + echo JAVA_OPTS Environment variable, if unset uses "" + echo SBT_OPTS Environment variable, if unset uses "" + echo ACTIVATOR_OPTS Environment variable, if unset uses "" + echo. + goto :end + ) +) + +if "%ACTIVATOR_HOME%"=="" ( + set "ACTIVATOR_HOME=%~dp0" + @REM remove trailing "\" from path + set ACTIVATOR_HOME=!ACTIVATOR_HOME:~0,-1! +) + +set ERROR_CODE=0 +set APP_VERSION=1.2.12 +set ACTIVATOR_LAUNCH_JAR=activator-launch-%APP_VERSION%.jar + +rem Detect if we were double clicked, although theoretically A user could +rem manually run cmd /c +for %%x in (%cmdcmdline%) do if %%~x==/c set DOUBLECLICKED=1 + +rem FIRST we load a config file of extra options (if there is one) +set "CFG_FILE_HOME=%UserProfile%\.activator\activatorconfig.txt" +set "CFG_FILE_VERSION=%UserProfile%\.activator\%APP_VERSION%\activatorconfig.txt" +set CFG_OPTS= +if exist %CFG_FILE_VERSION% ( + FOR /F "tokens=* eol=# usebackq delims=" %%i IN ("%CFG_FILE_VERSION%") DO ( + set DO_NOT_REUSE_ME=%%i + rem ZOMG (Part #2) WE use !! here to delay the expansion of + rem CFG_OPTS, otherwise it remains "" for this loop. + set CFG_OPTS=!CFG_OPTS! !DO_NOT_REUSE_ME! + ) +) +if "%CFG_OPTS%"=="" ( + if exist %CFG_FILE_HOME% ( + FOR /F "tokens=* eol=# usebackq delims=" %%i IN ("%CFG_FILE_HOME%") DO ( + set DO_NOT_REUSE_ME=%%i + rem ZOMG (Part #2) WE use !! here to delay the expansion of + rem CFG_OPTS, otherwise it remains "" for this loop. + set CFG_OPTS=!CFG_OPTS! !DO_NOT_REUSE_ME! + ) + ) +) + +rem We use the value of the JAVACMD environment variable if defined +set _JAVACMD=%JAVACMD% + +if "%_JAVACMD%"=="" ( + if not "%JAVA_HOME%"=="" ( + if exist "%JAVA_HOME%\bin\java.exe" set "_JAVACMD=%JAVA_HOME%\bin\java.exe" + + rem if there is a java home set we make sure it is the first picked up when invoking 'java' + SET "PATH=%JAVA_HOME%\bin;%PATH%" + ) +) + +if "%_JAVACMD%"=="" set _JAVACMD=java + +rem Detect if this java is ok to use. +for /F %%j in ('"%_JAVACMD%" -version 2^>^&1') do ( + if %%~j==Java set JAVAINSTALLED=1 +) + +rem Detect the same thing about javac +if "%_JAVACCMD%"=="" ( + if not "%JAVA_HOME%"=="" ( + if exist "%JAVA_HOME%\bin\javac.exe" set "_JAVACCMD=%JAVA_HOME%\bin\javac.exe" + ) +) +if "%_JAVACCMD%"=="" set _JAVACCMD=javac +for /F %%j in ('"%_JAVACCMD%" -version 2^>^&1') do ( + if %%~j==javac set JAVACINSTALLED=1 +) + +rem BAT has no logical or, so we do it OLD SCHOOL! Oppan Redmond Style +set JAVAOK=true +if not defined JAVAINSTALLED set JAVAOK=false +if not defined JAVACINSTALLED set JAVAOK=false + +if "%JAVAOK%"=="false" ( + echo. + echo A Java JDK is not installed or can't be found. + if not "%JAVA_HOME%"=="" ( + echo JAVA_HOME = "%JAVA_HOME%" + ) + echo. + echo Please go to + echo http://www.oracle.com/technetwork/java/javase/downloads/index.html + echo and download a valid Java JDK and install before running Activator. + echo. + echo If you think this message is in error, please check + echo your environment variables to see if "java.exe" and "javac.exe" are + echo available via JAVA_HOME or PATH. + echo. + if defined DOUBLECLICKED pause + exit /B 1 +) + +rem Check what Java version is being used to determine what memory options to use +for /f "tokens=3" %%g in ('java -version 2^>^&1 ^| findstr /i "version"') do ( + set JAVA_VERSION=%%g +) + +rem Strips away the " characters +set JAVA_VERSION=%JAVA_VERSION:"=% + +rem TODO Check if there are existing mem settings in JAVA_OPTS/CFG_OPTS and use those instead of the below +for /f "delims=. tokens=1-3" %%v in ("%JAVA_VERSION%") do ( + set MAJOR=%%v + set MINOR=%%w + set BUILD=%%x + + set META_SIZE=-XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=256M + if "!MINOR!" LSS "8" ( + set META_SIZE=-XX:PermSize=64M -XX:MaxPermSize=256M + ) + + set MEM_OPTS=!META_SIZE! + ) + +rem We use the value of the JAVA_OPTS environment variable if defined, rather than the config. +set _JAVA_OPTS=%JAVA_OPTS% +if "%_JAVA_OPTS%"=="" set _JAVA_OPTS=%CFG_OPTS% + +set DEBUG_OPTS= + +rem Loop through the arguments, building remaining args in args variable +set args= +:argsloop +if not "%~1"=="" ( + rem Checks if the argument contains "-D" and if true, adds argument 1 with 2 and puts an equal sign between them. + rem This is done since batch considers "=" to be a delimiter so we need to circumvent this behavior with a small hack. + set arg1=%~1 + if "!arg1:~0,2!"=="-D" ( + set "args=%args% "%~1"="%~2"" + shift + shift + goto argsloop + ) + + if "%~1"=="-jvm-debug" ( + if not "%~2"=="" ( + rem This piece of magic somehow checks that an argument is a number + for /F "delims=0123456789" %%i in ("%~2") do ( + set var="%%i" + ) + if defined var ( + rem Not a number, assume no argument given and default to 9999 + set JPDA_PORT=9999 + ) else ( + rem Port was given, shift arguments + set JPDA_PORT=%~2 + shift + ) + ) else ( + set JPDA_PORT=9999 + ) + shift + + set DEBUG_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=!JPDA_PORT! + goto argsloop + ) + rem else + set "args=%args% "%~1"" + shift + goto argsloop +) + +:run + +if "!args!"=="" ( + if defined DOUBLECLICKED ( + set CMDS="ui" + ) else set CMDS=!args! +) else set CMDS=!args! + +rem We add a / in front, so we get file:///C: instead of file://C: +rem Java considers the later a UNC path. +rem We also attempt a solid effort at making it URI friendly. +rem We don't even bother with UNC paths. +set JAVA_FRIENDLY_HOME_1=/!ACTIVATOR_HOME:\=/! +set JAVA_FRIENDLY_HOME=/!JAVA_FRIENDLY_HOME_1: =%%20! + +rem Checks if the command contains spaces to know if it should be wrapped in quotes or not +set NON_SPACED_CMD=%_JAVACMD: =% +if "%_JAVACMD%"=="%NON_SPACED_CMD%" %_JAVACMD% %DEBUG_OPTS% %MEM_OPTS% %ACTIVATOR_OPTS% %SBT_OPTS% %_JAVA_OPTS% "-Dactivator.home=%JAVA_FRIENDLY_HOME%" -jar "%ACTIVATOR_HOME%\%ACTIVATOR_LAUNCH_JAR%" %CMDS% +if NOT "%_JAVACMD%"=="%NON_SPACED_CMD%" "%_JAVACMD%" %DEBUG_OPTS% %MEM_OPTS% %ACTIVATOR_OPTS% %SBT_OPTS% %_JAVA_OPTS% "-Dactivator.home=%JAVA_FRIENDLY_HOME%" -jar "%ACTIVATOR_HOME%\%ACTIVATOR_LAUNCH_JAR%" %CMDS% + +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end + +@endlocal + +exit /B %ERROR_CODE% diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/BooleanValue.java b/samples/issue54/twixt/code/app/org/mef/twixt/BooleanValue.java new file mode 100644 index 0000000..185877c --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/BooleanValue.java @@ -0,0 +1,38 @@ +package org.mef.twixt; + +public class BooleanValue extends Value +{ + public BooleanValue() + { + this(false); + } + public BooleanValue(Boolean n) + { + super(n); + } + + @Override + protected String render() + { + Boolean n = get(); + return n.toString(); + } + + @Override + protected void parse(String input) + { + Boolean n = Boolean.parseBoolean(input); + this.setUnderlyingValue(n); + } + + //return in our type + public boolean get() + { + Boolean bVal = (Boolean)obj; + return bVal; + } + public void set(boolean b) + { + setUnderlyingValue(new Boolean(b)); + } +} diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/Converter.java b/samples/issue54/twixt/code/app/org/mef/twixt/Converter.java new file mode 100644 index 0000000..6c576e3 --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/Converter.java @@ -0,0 +1,8 @@ +package org.mef.twixt; + + +public interface Converter +{ + String print(Object obj); + Object parse(String s); +} diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/DateValue.java b/samples/issue54/twixt/code/app/org/mef/twixt/DateValue.java new file mode 100644 index 0000000..d37c2dd --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/DateValue.java @@ -0,0 +1,52 @@ +package org.mef.twixt; + +import java.text.SimpleDateFormat; +import java.util.Date; + +public class DateValue extends Value +{ + public DateValue() + { + this(new Date()); + } + public DateValue(Date n) + { + super(n); + } + + @Override + protected String render() + { + Date dt = get(); + //default rendering is the same as HTML5 date input: yyyy-mm-dd + SimpleDateFormat dateFormat = createFormatter(); + String s = dateFormat.format(dt); + return s; + } + + @Override + //input has not yet been validated so parsing may fail and throw an exception + protected void parse(String input) throws Exception + { + SimpleDateFormat dateFormat = createFormatter(); + Date dt = dateFormat.parse(input); + this.setUnderlyingValue(dt); + } + + private SimpleDateFormat createFormatter() + { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + return dateFormat; + } + + //return in our type + public Date get() + { + Date nVal = (Date)obj; + return nVal; + } + public void set(Date dt) + { + setUnderlyingValue(dt); + } +} diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/DoubleValue.java b/samples/issue54/twixt/code/app/org/mef/twixt/DoubleValue.java new file mode 100644 index 0000000..450a76f --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/DoubleValue.java @@ -0,0 +1,38 @@ +package org.mef.twixt; + +public class DoubleValue extends Value +{ + public DoubleValue() + { + this(0.0); + } + public DoubleValue(Double n) + { + super(n); + } + + @Override + protected String render() + { + Double n = get(); + return n.toString(); + } + + @Override + protected void parse(String input) + { + Double n = Double.parseDouble(input); + this.setUnderlyingValue(n); + } + + //return in our type + public double get() + { + Double nVal = (Double)obj; + return nVal; + } + public void set(double val) + { + setUnderlyingValue(new Double(val)); + } +} diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/EnumValue.java b/samples/issue54/twixt/code/app/org/mef/twixt/EnumValue.java new file mode 100644 index 0000000..506fa88 --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/EnumValue.java @@ -0,0 +1,33 @@ +package org.mef.twixt; +//package org.mef.framework.metadata; +// +//import org.mef.framework.metadata.validate.ValidationErrors; +// +// +//public abstract class EnumValue extends IntegerValueAndValidator +//{ +// public EnumValue() +// { +// this(0, ""); //oops, how get itemName!! +// } +// public EnumValue(int val, String itemName) +// { +// super(val, itemName); +// } +// +// @Override +// public boolean validate(Object val, ValidationErrors errors) +// { +// int n = (Integer)val; +// if (! onValidate(n)) +// { +// errors.addError(String.format("%d is not a valid enum value", n)); +// return false; +// } +// return true; +// } +// +// protected abstract boolean onValidate(int val); +// +// //how handle toString()?? +//} \ No newline at end of file diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/FileValue.java b/samples/issue54/twixt/code/app/org/mef/twixt/FileValue.java new file mode 100644 index 0000000..73b8d4a --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/FileValue.java @@ -0,0 +1,14 @@ +package org.mef.twixt; + +public class FileValue extends StringValue +{ + public FileValue() + { + this(""); + } + public FileValue(String n) + { + super(n); + } + +} diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/IntegerValue.java b/samples/issue54/twixt/code/app/org/mef/twixt/IntegerValue.java new file mode 100644 index 0000000..7047fe6 --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/IntegerValue.java @@ -0,0 +1,38 @@ +package org.mef.twixt; + +public class IntegerValue extends Value +{ + public IntegerValue() + { + this(0); + } + public IntegerValue(Integer n) + { + super(n); + } + + @Override + protected String render() + { + Integer n = get(); + return n.toString(); + } + + @Override + protected void parse(String input) + { + Integer n = Integer.parseInt(input); + this.setUnderlyingValue(n); + } + + //return in our type + public int get() + { + Integer nVal = (Integer)obj; + return nVal; + } + public void set(int val) + { + setUnderlyingValue(new Integer(val)); + } +} diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/ListValue.java b/samples/issue54/twixt/code/app/org/mef/twixt/ListValue.java new file mode 100644 index 0000000..235b10e --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/ListValue.java @@ -0,0 +1,76 @@ +package org.mef.twixt; + +import java.util.ArrayList; +import java.util.List; + +import org.mef.twixt.validate.ValContext; + + +public class ListValue extends Value +{ + public ListValue() + { + this(new ArrayList()); + } + public ListValue(List L) + { + super(L); + } + + @Override + protected String render() + { + return null; //!!what should we do + } + + @Override + protected void parse(String input) + { //!! + } + + //return in our type + public List get() + { + //should we return a copy?? + List L = (List)obj; + return L; + } + public void set(List L) + { + setUnderlyingValue(L); + } + + public void addElement(Value val) + { + List L = get(); + L.add(val); + } + + public Value getIth(int index) + { + List L = get(); + return L.get(index); + } + + public Object size() + { + List L = get(); + return L.size(); + } + + + @Override + public void validate(ValContext valctx) + { + if (validator != null) + { + validator.validate(valctx, this); + return; + } + + for(Value val : get()) + { + val.validate(valctx); + } + } +} diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/LongSelectValue.java b/samples/issue54/twixt/code/app/org/mef/twixt/LongSelectValue.java new file mode 100644 index 0000000..879e7b6 --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/LongSelectValue.java @@ -0,0 +1,57 @@ +package org.mef.twixt; + +import java.util.Map; + +import org.mef.twixt.validate.Validator; +import org.mef.twixt.validate.ValContext; + + +public class LongSelectValue extends LongValue +{ + private class SelectValidator implements Validator + { + @Override + public void validate(ValContext valctx, Value value) + { + if (options == null) + { + return; + } + + LongValue lval = (LongValue)value; + Long tmp = lval.get(); + boolean b = options.containsKey(tmp); + if (!b) + { + valctx.addError("select: unknown id: " + tmp.toString()); + } + } + } + + protected Map options; + + public LongSelectValue() + { + this(0L, null); + } + public LongSelectValue(Long id) + { + this(id, null); + } + public LongSelectValue(Long id, Map options) + { + super(id); + this.options = options; + setValidator(new SelectValidator()); + } + + + public Map options() + { + return options; + } + public void setOptions(Map map) + { + options = map; + } +} diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/LongValue.java b/samples/issue54/twixt/code/app/org/mef/twixt/LongValue.java new file mode 100644 index 0000000..71d3084 --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/LongValue.java @@ -0,0 +1,38 @@ +package org.mef.twixt; + +public class LongValue extends Value +{ + public LongValue() + { + this(0L); + } + public LongValue(Long n) + { + super(n); + } + + @Override + protected String render() + { + Long n = get(); + return n.toString(); + } + + @Override + protected void parse(String input) + { + Long n = Long.parseLong(input); + this.setUnderlyingValue(n); + } + + //return in our type + public long get() + { + Long nVal = (Long)obj; + return nVal; + } + public void set(long nVal) + { + setUnderlyingValue(new Long(nVal)); + } +} diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/SelectValue.java b/samples/issue54/twixt/code/app/org/mef/twixt/SelectValue.java new file mode 100644 index 0000000..4d3ec2a --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/SelectValue.java @@ -0,0 +1,57 @@ +package org.mef.twixt; + +import java.util.Map; + +import org.mef.twixt.validate.Validator; +import org.mef.twixt.validate.ValContext; + + +public class SelectValue extends StringValue +{ + class SelectValidator implements Validator + { + @Override + public void validate(ValContext valctx, Value value) + { + if (options == null) + { + return; + } + + String id = (String)value.toString(); + boolean b = options.containsKey(id); + if (!b) + { + valctx.addError("select: unknown id: " + id); + } + } + } + + protected Map options; + + public SelectValue() + { + this("", null); + } + public SelectValue(String id) + { + this(id, null); + } + + public SelectValue(String id, Map options) + { + super(id); + this.options = options; + setValidator(new SelectValidator()); + } + + + public Map options() + { + return options; + } + public void setOptions(Map map) + { + options = map; + } +} diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/StringValue.java b/samples/issue54/twixt/code/app/org/mef/twixt/StringValue.java new file mode 100644 index 0000000..8605e4e --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/StringValue.java @@ -0,0 +1,37 @@ +package org.mef.twixt; + +public class StringValue extends Value +{ + public StringValue() + { + this(""); + } + public StringValue(String n) + { + super(n); + } + + @Override + protected String render() + { + String n = get(); + return n.toString(); + } + + @Override + protected void parse(String input) + { + this.setUnderlyingValue(input); + } + + //return in our type + public String get() + { + String nVal = (String)obj; + return nVal; + } + public void set(String nVal) + { + setUnderlyingValue(nVal); + } +} diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/TupleValue.java b/samples/issue54/twixt/code/app/org/mef/twixt/TupleValue.java new file mode 100644 index 0000000..a4a5b24 --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/TupleValue.java @@ -0,0 +1,63 @@ +package org.mef.twixt; +//package org.mef.framework.metadata; +//import java.util.HashMap; +// +//import org.mef.framework.metadata.validate.ValContext; +// +// +//public class TupleValue implements ValueContainer +//{ +// private HashMap map; +// +// public TupleValue() +// { +// map = new HashMap(); +// } +// +// public TupleValue(TupleValue src) +// { +// map = new HashMap(); +// for(String fieldName : src.map.keySet()) +// { +// Value val = src.map.get(fieldName); +// Value copy = new Value(val); +// map.put(fieldName, copy); +// } +// } +// +// public void addField(String fieldName, Value val) +// { +// map.put(fieldName, val); +// } +// +// public Value field(String fieldName) +// { +// Value field = map.get(fieldName); +// return field; +// } +// +// //validation +// public void validateContainer(ValContext vtx) +// { +// for(String fieldName : map.keySet()) +// { +// Value val = map.get(fieldName); +// vtx.validate(val); +// } +// } +// +// @Override +// public void copyTo(Object model) { +// // TODO Auto-generated method stub +// +// } +// +// @Override +// public void copyFrom(Object model) { +// // TODO Auto-generated method stub +// +// } +// +// //how handle toString? +// +//} \ No newline at end of file diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/Value.java b/samples/issue54/twixt/code/app/org/mef/twixt/Value.java new file mode 100644 index 0000000..bb8eed9 --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/Value.java @@ -0,0 +1,78 @@ +package org.mef.twixt; + +import org.mef.twixt.validate.Validator; +import org.mef.twixt.validate.ValContext; + + +public abstract class Value +{ + protected Object obj; + protected Converter converter; + protected Validator validator; + + public Value() + {} + public Value(Object obj) + { + this.obj = obj; + } + + //??deep copy needed!! + + public Object getUnderlyingValue() + { + return obj; + } + public void setUnderlyingValue(Object obj) + { + this.obj = obj; + } + + public void validate(ValContext valctx) + { + if (validator != null) + { + validator.validate(valctx, this); + } + } + + protected abstract void parse(String input) throws Exception; + protected abstract String render(); + @Override + public String toString() + { + if (converter != null) + { + return converter.print(obj); + } + else + { + return render(); + } + } + public void fromString(String input) throws Exception + { + if (converter != null) + { + Object object = converter.parse(input); + setUnderlyingValue(object); + } + else + { + parse(input); + } + } + public Converter getConverter() { + return converter; + } + public void setConverter(Converter converter) { + this.converter = converter; + } + public Validator getValidator() { + return validator; + } + public void setValidator(Validator validator) { + this.validator = validator; + } + +} \ No newline at end of file diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/ValueContainer.java b/samples/issue54/twixt/code/app/org/mef/twixt/ValueContainer.java new file mode 100644 index 0000000..42362ac --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/ValueContainer.java @@ -0,0 +1,11 @@ +package org.mef.twixt; + +import org.mef.twixt.validate.ValContext; + + +public interface ValueContainer +{ + void validate(ValContext vtx); + void copyTo(Object model); + void copyFrom(Object model); +} \ No newline at end of file diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/binder/FormCopier.java b/samples/issue54/twixt/code/app/org/mef/twixt/binder/FormCopier.java new file mode 100644 index 0000000..1c14cd3 --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/binder/FormCopier.java @@ -0,0 +1,206 @@ +package org.mef.twixt.binder; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Date; + +import org.mef.twixt.Value; +import org.springframework.util.ReflectionUtils; + +import play.Logger; + +public class FormCopier implements ReflectionUtils.FieldCallback +{ + public interface FieldCopier + { + void copyFieldFromModel(FormCopier copier, Field field); + void copyFieldToModel(FormCopier copier, Field field); + } + + TwixtForm form; + private Object modelToCopyFrom; + private Object modelToCopyTo; + private FieldCopier fieldCopier; + private String[] fieldsToNotCopy; + + public FormCopier(FieldCopier fieldCopier) + { + this.fieldCopier = fieldCopier; + } + + public void copyToModel(TwixtForm twixtForm, Object model, String[] fieldsToNotCopy) + { + form = twixtForm; + modelToCopyTo = model; + this.fieldsToNotCopy = fieldsToNotCopy; + ReflectionUtils.doWithFields(form.getClass(), this, ReflectionUtils.COPYABLE_FIELDS); + } + + public void copyFromModel(Object model, TwixtForm twixtForm, String[] fieldsToNotCopy) + { + form = twixtForm; + modelToCopyFrom = model; + this.fieldsToNotCopy = fieldsToNotCopy; + ReflectionUtils.doWithFields(form.getClass(), this, ReflectionUtils.COPYABLE_FIELDS); + } + + @Override + public void doWith(Field field) + { + if (this.fieldsToNotCopy != null) + { + for(String fieldName : fieldsToNotCopy) + { + if (field.getName().equals(fieldName)) + { + return; + } + } + } + + if (modelToCopyFrom != null) + { + fieldCopier.copyFieldFromModel(this, field); + } + else if (modelToCopyTo != null) + { + fieldCopier.copyFieldToModel(this, field); + } + } + + public void copyFieldFromModel(Field field) + { + Class clazz = field.getType(); + if (Value.class.isAssignableFrom(clazz)) + { + try + { + field.setAccessible(true); + Object valueObj = field.get(form); + + String fnName = "get" + uppify(field.getName()); + Method meth = ReflectionUtils.findMethod(modelToCopyFrom.getClass(), fnName); + if (meth != null) + { + Object src = meth.invoke(modelToCopyFrom); + + fnName = "setUnderlyingValue"; + meth = ReflectionUtils.findMethod(clazz, fnName, Object.class); + + meth.invoke(valueObj, src); + } + } + catch (Exception e) + { + e.printStackTrace(); + } + } + } + + public void copyFieldToModel(Field field) + { + Class clazz = field.getType(); + if (Value.class.isAssignableFrom(clazz)) + { + try + { + field.setAccessible(true); + Object valueObj = field.get(form); + + String fnName = "getUnderlyingValue"; + Method meth = ReflectionUtils.findMethod(valueObj.getClass(), fnName); + if (meth != null) + { + Object src = meth.invoke(valueObj); + + fnName = "set" + uppify(field.getName()); + meth = findMatchingMethod(field, src); + if (meth != null) + { + Logger.info("do: " + fnName + "=" + src); + meth.invoke(modelToCopyTo, src); + } + } + } + catch (Exception e) + { + e.printStackTrace(); + } + } + } + + private Method findMatchingMethod(Field field, Object src) + { + String fnName = "set" + uppify(field.getName()); + Method meth = ReflectionUtils.findMethod(modelToCopyTo.getClass(), fnName, src.getClass()); + if (meth != null) + { + return meth; + } + + if (src.getClass().equals(Integer.class)) + { + meth = ReflectionUtils.findMethod(modelToCopyTo.getClass(), fnName, int.class); + if (meth != null) + { + return meth; + } + } + else if (src.getClass().equals(Boolean.class)) + { + meth = ReflectionUtils.findMethod(modelToCopyTo.getClass(), fnName, boolean.class); + if (meth != null) + { + return meth; + } +// meth = ReflectionUtils.findMethod(modelToCopyTo.getClass(), fnName, Boolean.class); +// if (meth != null) +// { +// return meth; +// } + } + else if (src.getClass().equals(Long.class)) + { + meth = ReflectionUtils.findMethod(modelToCopyTo.getClass(), fnName, long.class); + if (meth != null) + { + return meth; + } + } + else if (src.getClass().equals(Double.class)) + { + meth = ReflectionUtils.findMethod(modelToCopyTo.getClass(), fnName, double.class); + if (meth != null) + { + return meth; + } + } + else if (src.getClass().equals(Date.class)) + { + meth = ReflectionUtils.findMethod(modelToCopyTo.getClass(), fnName, Date.class); + if (meth != null) + { + return meth; + } + } + else if (src.getClass().equals(String.class)) + { + meth = ReflectionUtils.findMethod(modelToCopyTo.getClass(), fnName, String.class); + if (meth != null) + { + return meth; + } + } + + return null; + } + + private String uppify(String name) + { + String upper = name.toUpperCase(); + String s = upper.substring(0, 1); + s += name.substring(1); + return s; + } + +} diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/binder/IFormBinder.java b/samples/issue54/twixt/code/app/org/mef/twixt/binder/IFormBinder.java new file mode 100644 index 0000000..bf03f90 --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/binder/IFormBinder.java @@ -0,0 +1,16 @@ +package org.mef.twixt.binder; + +import play.data.Form; + + +public interface IFormBinder +{ + boolean bind(); + + Object getValidationErrors(); + + T get(); //return even if bind failed. may be partially filled + Form getForm(); + + Form fillForm(T input); +} \ No newline at end of file diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/binder/MockTwixtBinder.java b/samples/issue54/twixt/code/app/org/mef/twixt/binder/MockTwixtBinder.java new file mode 100644 index 0000000..2817b8e --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/binder/MockTwixtBinder.java @@ -0,0 +1,24 @@ +package org.mef.twixt.binder; + +import java.util.Map; + +import org.mef.twixt.ValueContainer; + + +public class MockTwixtBinder extends TwixtBinder + { + private Map mockData; + + public MockTwixtBinder(Class clazz, Map anyData) + { + super(clazz); + this.mockData = anyData; + } + + @Override + public boolean bind() + { + return bindFromMap(mockData); + } + + } \ No newline at end of file diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/binder/ReflectionBinder.java b/samples/issue54/twixt/code/app/org/mef/twixt/binder/ReflectionBinder.java new file mode 100644 index 0000000..4b880a9 --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/binder/ReflectionBinder.java @@ -0,0 +1,83 @@ +package org.mef.twixt.binder; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.mef.twixt.*; +import org.mef.twixt.validate.*; +import org.reflections.Reflections; + +public class ReflectionBinder +{ + ValContext vtx; + private Field fieldBeingParsed; + + public ReflectionBinder() + { + vtx = new ValContext(); + } + + public Map> getErrors() + { + return vtx.getErrors(); + } + public ValContext getContext() + { + return vtx; + } + + boolean bind(ValueContainer input, Map map) + { + boolean ok = false; + + try { + ok = bindImpl(input, map); + } catch (Exception e) { + e.printStackTrace(); + if (fieldBeingParsed != null) //failed in fromString? + { + vtx.setCurrentItemName(fieldBeingParsed.getName()); + vtx.addError(String.format("%s: invalid input", this.fieldBeingParsed.getName())); + } + } + + //and validate + if (ok) + { + //TODO if validateContainer exists call it, else use reflection + input.validate(vtx); + + ok = (vtx.getFailCount() == 0); + } + + return ok; + } + + boolean bindImpl(ValueContainer input, Map map) throws Exception + { + boolean ok = true; + Set list = Reflections.getAllFields(input.getClass(), Reflections.withModifier(Modifier.PUBLIC)); + for(Field fld : list) + { + String fieldName = fld.getName(); + + String s = map.get(fieldName); + if (s != null) + { + Object obj = fld.get(input); + if (obj instanceof Value) + { + Value val = (Value)obj; + fieldBeingParsed = fld; + val.fromString(s); + fieldBeingParsed = null; + } + } + } + + return ok; + } +} \ No newline at end of file diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/binder/TwixtBinder.java b/samples/issue54/twixt/code/app/org/mef/twixt/binder/TwixtBinder.java new file mode 100644 index 0000000..bf0a414 --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/binder/TwixtBinder.java @@ -0,0 +1,108 @@ +package org.mef.twixt.binder; + +import java.util.List; +import java.util.Map; + +import org.mef.twixt.ValueContainer; +import org.mef.twixt.validate.ValidationErrorSpec; + +import play.data.DynamicForm; +import play.data.Form; +import play.data.validation.ValidationError; + +public class TwixtBinder implements IFormBinder + { + private T entity; + private Class clazz; + ReflectionBinder binder = new ReflectionBinder(); + private DynamicForm form; + + public TwixtBinder(Class clazz, T original) + { + this.clazz = clazz; + this.entity = original; + } + public TwixtBinder(Class clazz) + { + this.clazz = clazz; + } + + @Override + public boolean bind() + { + DynamicForm form = Form.form().bindFromRequest(); + return bindFromDynamicForm(form); + } + protected boolean bindFromMap(Map anyData) + { + DynamicForm form = Form.form().bind(anyData); + return bindFromDynamicForm(form); + } + + private T blankInstance() + { + try { + return (T) clazz.newInstance(); + } catch(Exception e) { + throw new RuntimeException("Cannot instantiate " + clazz + ". It must have a default constructor", e); + } + } + protected boolean bindFromDynamicForm(DynamicForm form) + { + this.entity = (entity != null) ? entity : blankInstance(); //throw exception if fails + this.form = form; + + boolean b = binder.bind(entity, form.data()); + + //get errors (in getForm) + return b; + } + + @Override + public T get() + { + return entity; + } + + @Override + public Object getValidationErrors() + { + Form frm = getForm(); + return frm.errors(); + } + + @Override + public Form getForm() + { + Form frm; + if (this.entity != null) + { + frm = this.fillForm(this.entity); //copy over field values + } + else + { + frm = Form.form(clazz); + } + + //and add errors + Map> errorMap = binder.getErrors(); + for(String key : errorMap.keySet()) + { + List specL = errorMap.get(key); + for(ValidationErrorSpec spec : specL) + { + ValidationError error = new ValidationError(spec.key, spec.message); + frm.reject(error); + } + } + + return frm; + } + + public Form fillForm(T input) + { + Form form = Form.form(clazz); + form = form.fill(input); + return form; + } + } \ No newline at end of file diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/binder/TwixtForm.java b/samples/issue54/twixt/code/app/org/mef/twixt/binder/TwixtForm.java new file mode 100644 index 0000000..845cea2 --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/binder/TwixtForm.java @@ -0,0 +1,126 @@ +package org.mef.twixt.binder; + +import java.lang.reflect.Field; + +import org.mef.twixt.*; +import org.mef.twixt.validate.ValContext; +import org.springframework.util.ReflectionUtils; + +public abstract class TwixtForm implements ValueContainer +{ + private class Facade implements FormCopier.FieldCopier, ReflectionUtils.FieldCallback + { + @Override + public void doWith(Field field) + { + Class clazz = field.getType(); + if (Value.class.isAssignableFrom(clazz)) + { + try + { + field.setAccessible(true); + if (field.get(TwixtForm.this) != null) + { + return; //skip ones that are already not null + } + + Object obj = clazz.newInstance(); + field.set(TwixtForm.this, obj); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + } + + @Override + public void copyFieldFromModel(FormCopier copier, Field field) + { + copier.copyFieldFromModel(field); + } + @Override + public void copyFieldToModel(FormCopier copier, Field field) + { + copier.copyFieldToModel(field); + } + } + + private class ValidationFacade implements ReflectionUtils.FieldCallback + { + private ValContext valctx; + + public ValidationFacade(ValContext valctx) + { + this.valctx = valctx; + } + + @Override + public void doWith(Field field) + { + Class clazz = field.getType(); + if (Value.class.isAssignableFrom(clazz)) + { + try + { + field.setAccessible(true); + Value value = (Value) field.get(TwixtForm.this); + + if (value != null) + { + valctx.setCurrentItemName(field.getName()); + value.validate(valctx); + } + } + catch (Exception e) + { + e.printStackTrace(); + } + } + } + } + + private Facade _facade = new Facade(); //avoid name clash, use _ + + public TwixtForm() + {} + + protected void initFields() + { + ReflectionUtils.doWithFields(this.getClass(), _facade, ReflectionUtils.COPYABLE_FIELDS); + } + + + @Override + public void copyFrom(Object model) + { + this.copyFieldsFromModel(model); + } + + @Override + public void copyTo(Object model) + { + this.copyFieldsToModel(model); + } + + protected void copyFieldsFromModel(Object model, String... fieldsToNotCopy) + { + FormCopier copier = new FormCopier(_facade); + copier.copyFromModel(model, this, fieldsToNotCopy); + } + protected void copyFieldsToModel(Object model, String... fieldsToNotCopy) + { + FormCopier copier = new FormCopier(_facade); + copier.copyToModel(this, model, fieldsToNotCopy); + } + + + @Override + public void validate(ValContext valctx) + { + //use reflection so we can set itemName for each Value + ValidationFacade valfacade = new ValidationFacade(valctx); + ReflectionUtils.doWithFields(this.getClass(), valfacade, ReflectionUtils.COPYABLE_FIELDS); + } + +} diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/controllers/DynamicTwixtController.java b/samples/issue54/twixt/code/app/org/mef/twixt/controllers/DynamicTwixtController.java new file mode 100644 index 0000000..de328db --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/controllers/DynamicTwixtController.java @@ -0,0 +1,168 @@ +package org.mef.twixt.controllers; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.TreeMap; + + + +import org.mef.twixt.*; +import org.mef.twixt.widget.MySelectWidget; +import org.springframework.util.ReflectionUtils; + +import play.Logger; +import play.data.Form; +import play.mvc.Call; +import play.twirl.api.Content; +import play.utils.dao.BasicModel; +import play.utils.dao.DAO; +import play.utils.meta.FieldMetadata; +import play.utils.meta.form.CheckboxWidget; +import play.utils.meta.form.DateWidget; +import play.utils.meta.form.FileWidget; +import play.utils.meta.form.FormFieldWidget; +import play.utils.meta.form.NumberWidget; +import play.utils.meta.form.SelectWidget; +import play.utils.meta.form.TextWidget; + +public abstract class DynamicTwixtController,T extends ValueContainer> extends TwixtController implements ReflectionUtils.FieldCallback, ReflectionUtils.FieldFilter +{ + private class PreRender implements ReflectionUtils.FieldCallback + { + @Override + public void doWith(Field arg0) throws IllegalArgumentException, IllegalAccessException + { + Class clazz = arg0.getType(); + if (LongSelectValue.class.isAssignableFrom(clazz)) + { + LongSelectValue val = (LongSelectValue) arg0.get(preRenderTwixt); + FieldMetadata meta = findMeta(arg0); + MySelectWidget w = (MySelectWidget) meta.getWidget(); + + w.options = new TreeMap(); + for(Long key : val.options().keySet()) + { + w.options.put(key.toString(), val.options().get(key)); //put key as string because form.value will be string + } + } + else if (SelectValue.class.isAssignableFrom(clazz)) + { + SelectValue val = (SelectValue) arg0.get(preRenderTwixt); + FieldMetadata meta = findMeta(arg0); + MySelectWidget w = (MySelectWidget) meta.getWidget(); + + w.options = new TreeMap(); + for(String key : val.options().keySet()) + { + w.options.put(key, val.options().get(key)); + } + } + } + + private FieldMetadata findMeta(Field arg0) + { + for(FieldMetadata meta : metaL) + { + if (arg0.getName().equals(meta.getField().getName())) + { + return meta; + } + } + return null; + } + } + + + List metaL = new ArrayList(); + private T preRenderTwixt; + + public DynamicTwixtController(DAO dao, Class keyClass, Class modelClass, ClasstwixtClass, int pageSize, String orderBy) + { + super(dao, keyClass, modelClass, twixtClass, pageSize, orderBy); + ReflectionUtils.doWithFields(twixtClass, this, this); + } + + @Override + public void doWith(Field field) throws IllegalArgumentException, + IllegalAccessException + { + addFieldToMetaL(field); + } + + @Override + public boolean matches(Field arg0) + { + return (Value.class.isAssignableFrom(arg0.getType())); + } + + private void addFieldToMetaL(Field f) + { + FieldMetadata meta = new FieldMetadata(f, null); //new StringConverter()); + FormFieldWidget w = createWidget(f, meta); + try { + forceSetWidget(meta, w); + Logger.info("ffff: " + f.getName()); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + metaL.add(meta); + } + + protected FormFieldWidget createWidget(Field f, FieldMetadata meta) + { + Class clazz = f.getType(); + + if (clazz.equals(IntegerValue.class)) + { + return new NumberWidget(meta); + } + else if (clazz.equals(BooleanValue.class)) + { + return new CheckboxWidget(meta); + } + else if (clazz.equals(DateValue.class)) + { + return new DateWidget(meta); //yyyy-mm-dd + } + else if (clazz.isAssignableFrom(SelectValue.class)) + { + return new MySelectWidget(meta); + } + else if (LongSelectValue.class.isAssignableFrom(clazz)) + { + return new MySelectWidget(meta); + } + else if (FileValue.class.isAssignableFrom(clazz)) + { + return new FileWidget(meta); + } + else + { + TextWidget w = new TextWidget(meta); + return w; + } + } + + private void forceSetWidget(FieldMetadata meta2, FormFieldWidget w) throws Exception + { +// Field f = ReflectionUtils.findField(meta2.getClass(), "widget"); +// f.setAccessible(true); //force! +// f.set(meta2, w); + meta2.setWidget(w); + } + + @Override + protected Content xrenderForm(K key, Form form) + { + //need to assign options map to any selectvalues + preRenderTwixt = form.get(); + ReflectionUtils.doWithFields(preRenderTwixt.getClass(), new PreRender(), ReflectionUtils.COPYABLE_FIELDS); + + return render(templateForForm(), with(getKeyClass(), key).and(Form.class, form).and(metaL.getClass(), metaL)); + } + + + +} diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/controllers/TwixtController.java b/samples/issue54/twixt/code/app/org/mef/twixt/controllers/TwixtController.java new file mode 100644 index 0000000..0464d9a --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/controllers/TwixtController.java @@ -0,0 +1,197 @@ +package org.mef.twixt.controllers; + +import static play.data.Form.form; + +import javax.inject.Inject; + +import org.mef.twixt.ValueContainer; + + + +import org.mef.twixt.binder.TwixtBinder; + +import play.Logger; +import play.Logger.ALogger; +import play.data.Form; +import play.mvc.Call; +import play.mvc.Result; +import play.twirl.api.Content; +import play.utils.crud.CRUDController; +import play.utils.dao.BasicModel; +import play.utils.dao.DAO; + +public abstract class TwixtController,T extends ValueContainer> extends CRUDController +{ + protected Class twixtClass; + + @Inject + public TwixtController(DAO dao, Class keyClass, Class modelClass, ClasstwixtClass, int pageSize, String orderBy) { + super(dao, form(modelClass), keyClass, modelClass, pageSize, orderBy); + this.twixtClass = twixtClass; + } + + private static ALogger xlog = Logger.of(CRUDController.class); + + protected Content xrenderForm(K key, Form form) + { + return render(templateForForm(), with(getKeyClass(), key).and(Form.class, form)); + } + + + public Result newForm() { + if (xlog.isDebugEnabled()) + xlog.debug("xnewForm() <-"); + Form form = createForm(null); + + return ok(xrenderForm(null, form)); + } + + private Form createForm(M model) + { + T twixt = createTwixt(); + if (model != null) + { + twixt.copyFrom(model); + } + Form frm = Form.form(this.twixtClass).fill(twixt); + return frm; + + } + + @Override + public Result create() + { + if (xlog.isDebugEnabled()) + xlog.debug("ccXcreate() <-"); + + TwixtBinder binder = new TwixtBinder(twixtClass); + boolean b = binder.bind(); + Form filledForm = binder.getForm(); + + if (!b) { + if (xlog.isDebugEnabled()) + xlog.debug("Xvalidation errors occured: " + binder.getValidationErrors()); // filledForm.errors()); + + return badRequest(xrenderForm(null, filledForm)); + } else { + T twixt = filledForm.get(); + M model = createModel(); + if (model == null) + { + return badRequest(xrenderForm(null, filledForm)); + } + + twixt.copyTo(model); + + DAO dao = getDao(); + dao.create(model); + if (xlog.isDebugEnabled()) + xlog.debug("Xentity created"); + + Call index = toIndex(); + if (xlog.isDebugEnabled()) + xlog.debug("Xindex : " + index); + return redirect(index); + } + } + + public Result editForm(K key) { + if (xlog.isDebugEnabled()) + xlog.debug("editForm() <-" + key); + + M model = this.getDao().get(key); + if (xlog.isDebugEnabled()) + xlog.debug("model : " + model); + + Form frm = createForm(model); + return ok(xrenderForm(key, frm)); + } + + public Result update(K key) { + if (xlog.isDebugEnabled()) + xlog.debug("update() <-" + key); + + M model = getDao().get(key); + T twixt = createTwixt(); + twixt.copyFrom(model); + + TwixtBinder binder = new TwixtBinder(twixtClass, twixt); + boolean b = binder.bind(); + Form filledForm = binder.getForm(); + + if (! b) { + if (xlog.isDebugEnabled()) + xlog.debug("validation errors occured: " + binder.getValidationErrors()); + + return badRequest(xrenderForm(key, filledForm)); + } else { + twixt = filledForm.get(); + twixt.copyTo(model); + if (xlog.isDebugEnabled()) + xlog.debug("model : " + model); + getDao().update(model); + if (xlog.isDebugEnabled()) + xlog.debug("entity updated"); + + Call index = toIndex(); + if (xlog.isDebugEnabled()) + xlog.debug("index : " + index); + return redirect(index); + } + } + + + protected M createModel() + { + M model = null; + try { + model = this.getModelClass().newInstance(); + } catch (Exception e) { + e.printStackTrace(); + } + return model; + } + + protected T createTwixt() + { + T twixt = null; + try { + twixt = this.twixtClass.newInstance(); + } catch (Exception e) { + e.printStackTrace(); + } + return twixt; + } + + protected String getModelTemplatePrefix() + { + return this.getModelClass().getSimpleName(); + } + protected String genTemplate(String s) + { + s = getModelTemplatePrefix() + s; + + String tmp = s.substring(0, 1).toLowerCase(); + tmp += s.substring(1); + + return tmp; + } + + @Override + protected String templateForList() { + return genTemplate("List"); + } + + @Override + protected String templateForForm() { + return genTemplate("Form"); + } + + @Override + protected String templateForShow() { + return genTemplate("Show"); + } + + + +} diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/validate/ErrorMessages.java b/samples/issue54/twixt/code/app/org/mef/twixt/validate/ErrorMessages.java new file mode 100644 index 0000000..5de07d3 --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/validate/ErrorMessages.java @@ -0,0 +1,11 @@ +package org.mef.twixt.validate; + +public class ErrorMessages { + + //all messages used by this framework should be declared here + //You should add them to your conf/messages file(s) + public static final String REQUIRED = "mycroft-error.required"; + public static final String NOTEMPTY = "mycroft-error.string-not-empty"; + public static final String RANGE_INT = "mycroft-error.range-int"; + +} diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/validate/ValContext.java b/samples/issue54/twixt/code/app/org/mef/twixt/validate/ValContext.java new file mode 100644 index 0000000..c33f5b4 --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/validate/ValContext.java @@ -0,0 +1,65 @@ +package org.mef.twixt.validate; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + + +public class ValContext +{ + private int failCount; + private Map> mapErrors; + private String currentItemName; + + public ValContext() + { + mapErrors = new HashMap>(); + } + + public void setCurrentItemName(String itemName) + { + currentItemName = itemName; + } + + public void addError(String fmt, Object...strings) + { + ValidationErrors errors = new ValidationErrors(); + errors.map = mapErrors; + errors.setItemName(currentItemName); + + errors.addError(fmt, strings); + failCount++; + } + + public int getFailCount() + { + return failCount; + } + + public Map> getErrors() + { + return mapErrors; + } + + public List getFlattendErrorList() + { + List resultL = new ArrayList(); + + for(String key : mapErrors.keySet()) + { + List L = mapErrors.get(key); + for(ValidationErrorSpec spec : L) + { + resultL.add(spec); + } + } + return resultL; + } + + public Object succeeded() + { + return failCount == 0; + } +} \ No newline at end of file diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/validate/ValidationErrorSpec.java b/samples/issue54/twixt/code/app/org/mef/twixt/validate/ValidationErrorSpec.java new file mode 100644 index 0000000..3ac1bb9 --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/validate/ValidationErrorSpec.java @@ -0,0 +1,7 @@ +package org.mef.twixt.validate; + +public class ValidationErrorSpec +{ + public String key; + public String message; +} \ No newline at end of file diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/validate/ValidationErrors.java b/samples/issue54/twixt/code/app/org/mef/twixt/validate/ValidationErrors.java new file mode 100644 index 0000000..c246ba8 --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/validate/ValidationErrors.java @@ -0,0 +1,53 @@ +package org.mef.twixt.validate; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import play.i18n.Messages; + + +public class ValidationErrors +{ + public Map> map; + String itemName; + + public String getItemName() + { + return itemName; + } + public void setItemName(String itemName) + { + this.itemName = itemName; + } + + public void addError(String message, Object... arguments) + { + ValidationErrorSpec spec = new ValidationErrorSpec(); + spec.key = itemName; + spec.message = getMessageFromConf(message, arguments); + + List L = map.get(itemName); + if (L == null) + { + L = new ArrayList(); + } + L.add(spec); + map.put(itemName, L); + } + + private String getMessageFromConf(String message, Object... arguments) + { + if (Messages.isDefined(message)) + { + String s = Messages.get(message, arguments); + return s; + } + else + { + String s = MessageFormat.format(message, arguments); + return s; + } + } + +} \ No newline at end of file diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/validate/Validator.java b/samples/issue54/twixt/code/app/org/mef/twixt/validate/Validator.java new file mode 100644 index 0000000..6574607 --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/validate/Validator.java @@ -0,0 +1,10 @@ +package org.mef.twixt.validate; + +import org.mef.twixt.Value; + + + +public interface Validator +{ + void validate(ValContext valctx, Value value); +} \ No newline at end of file diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/validators/NoneValidator.java b/samples/issue54/twixt/code/app/org/mef/twixt/validators/NoneValidator.java new file mode 100644 index 0000000..3759581 --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/validators/NoneValidator.java @@ -0,0 +1,13 @@ +package org.mef.twixt.validators; +import org.mef.twixt.Value; +import org.mef.twixt.validate.Validator; +import org.mef.twixt.validate.ValContext; + + +public class NoneValidator implements Validator +{ + + @Override + public void validate(ValContext valctx, Value obj) { + } +} \ No newline at end of file diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/validators/NotEmptyStringValidator.java b/samples/issue54/twixt/code/app/org/mef/twixt/validators/NotEmptyStringValidator.java new file mode 100644 index 0000000..2f0778d --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/validators/NotEmptyStringValidator.java @@ -0,0 +1,19 @@ +package org.mef.twixt.validators; +import org.mef.twixt.Value; +import org.mef.twixt.validate.ErrorMessages; +import org.mef.twixt.validate.Validator; +import org.mef.twixt.validate.ValContext; + + +public class NotEmptyStringValidator implements Validator +{ + @Override + public void validate(ValContext valctx, Value obj) + { + String s = obj.toString(); + if (s == null || s.isEmpty()) + { + valctx.addError(ErrorMessages.NOTEMPTY); + } + } +} \ No newline at end of file diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/validators/RangeIntValidator.java b/samples/issue54/twixt/code/app/org/mef/twixt/validators/RangeIntValidator.java new file mode 100644 index 0000000..fe1ab79 --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/validators/RangeIntValidator.java @@ -0,0 +1,33 @@ +package org.mef.twixt.validators; + +import org.mef.twixt.IntegerValue; +import org.mef.twixt.Value; +import org.mef.twixt.validate.ErrorMessages; +import org.mef.twixt.validate.Validator; +import org.mef.twixt.validate.ValContext; + + +public class RangeIntValidator implements Validator +{ + private int min; + private int max; + + public RangeIntValidator(int min, int max) + { + this.min = min; + this.max = max; + } + @Override + public void validate(ValContext valctx, Value obj) + { + IntegerValue value = (IntegerValue) obj; + int n = value.get(); + + boolean ok = (n >= min && n <= max); + if (! ok) + { +// errors.addError(String.format("value %d not in range %d to %d", n, min, max)); + valctx.addError(ErrorMessages.RANGE_INT, n, min, max); + } + } +} \ No newline at end of file diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/validators/RequiredStringValidator.java b/samples/issue54/twixt/code/app/org/mef/twixt/validators/RequiredStringValidator.java new file mode 100644 index 0000000..6437dcd --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/validators/RequiredStringValidator.java @@ -0,0 +1,19 @@ +package org.mef.twixt.validators; +import org.mef.twixt.Value; +import org.mef.twixt.validate.ErrorMessages; +import org.mef.twixt.validate.Validator; +import org.mef.twixt.validate.ValContext; + + +public class RequiredStringValidator implements Validator +{ + @Override + public void validate(ValContext valctx, Value obj) + { + String s = obj.toString(); + if (s == null) + { + valctx.addError(ErrorMessages.REQUIRED); + } + } +} \ No newline at end of file diff --git a/samples/issue54/twixt/code/app/org/mef/twixt/widget/MySelectWidget.java b/samples/issue54/twixt/code/app/org/mef/twixt/widget/MySelectWidget.java new file mode 100644 index 0000000..81330f8 --- /dev/null +++ b/samples/issue54/twixt/code/app/org/mef/twixt/widget/MySelectWidget.java @@ -0,0 +1,35 @@ +package org.mef.twixt.widget; + +import java.util.Map; +import java.util.TreeMap; + +import play.api.i18n.Lang; +import play.twirl.api.Html; +import play.data.Form.Field; +import play.utils.meta.FieldMetadata; +import play.utils.meta.form.FormFieldWidget; +import scala.Symbol; +import scala.Tuple2; +import views.html.helper.FieldConstructor; + +public class MySelectWidget extends FormFieldWidget { + + public Map options; + + public MySelectWidget(FieldMetadata fieldMetadata) { + super(fieldMetadata); + Class declaringClass = field().getDeclaringClass(); +// String parentClassName = declaringClass.getSimpleName(); +// Object[] constants=fieldMetadata.getType().getEnumConstants(); +// options = new TreeMap(); +// for (Object constant : constants) { +// options.put(constant, parentClassName + "." + constant); +// } + } + + @Override + protected Html render(Field formField, Tuple2[] args, FieldConstructor fieldConstructor, Lang lang) { + return play.utils.meta.form.html.selectWidget.render(formField, options, args, fieldConstructor, lang); + } + +} diff --git a/samples/issue54/twixt/code/build.sbt b/samples/issue54/twixt/code/build.sbt new file mode 100644 index 0000000..2d4cfe4 --- /dev/null +++ b/samples/issue54/twixt/code/build.sbt @@ -0,0 +1,20 @@ +name := "twixt54" + +version := "0.1.0-SNAPSHOT" + +scalaVersion := "2.11.4" + +libraryDependencies ++= Seq( + javaCore, javaJdbc, javaEbean, + "play2-crud" %% "play2-crud" % "0.7.4-SNAPSHOT", + "play2-crud" %% "play2-crud" % "0.7.4-SNAPSHOT" classifier "assets" +) + +lazy val root = (project in file(".")).enablePlugins(PlayJava) + +//resolvers += "release repository" at "http://hakandilek.github.com/maven-repo/releases/" +// +//resolvers += "snapshot repository" at "http://hakandilek.github.com/maven-repo/snapshots/" + + + diff --git a/samples/issue54/twixt/code/conf/application.conf b/samples/issue54/twixt/code/conf/application.conf new file mode 100644 index 0000000..e69de29 diff --git a/samples/issue54/twixt/code/project/build.properties b/samples/issue54/twixt/code/project/build.properties new file mode 100644 index 0000000..f8a91ed --- /dev/null +++ b/samples/issue54/twixt/code/project/build.properties @@ -0,0 +1,4 @@ +#Activator-generated Properties +#Sun Mar 01 17:57:57 EST 2015 +template.uuid=b7274e52-c226-4deb-bb0e-ab2fdb8f4767 +sbt.version=0.13.7 diff --git a/samples/issue54/twixt/code/project/plugins.sbt b/samples/issue54/twixt/code/project/plugins.sbt new file mode 100644 index 0000000..1ef84a4 --- /dev/null +++ b/samples/issue54/twixt/code/project/plugins.sbt @@ -0,0 +1,8 @@ +// Comment to get more information during initialization +logLevel := Level.Warn + +// The Typesafe repository +resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/" + +// Use the Play sbt plugin for Play projects +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.7") diff --git a/samples/issue54/twixt/code/public/images/favicon.png b/samples/issue54/twixt/code/public/images/favicon.png new file mode 100644 index 0000000..c7d92d2 Binary files /dev/null and b/samples/issue54/twixt/code/public/images/favicon.png differ diff --git a/samples/issue54/twixt/code/test/BasicTests.java b/samples/issue54/twixt/code/test/BasicTests.java new file mode 100644 index 0000000..3f20a3a --- /dev/null +++ b/samples/issue54/twixt/code/test/BasicTests.java @@ -0,0 +1,57 @@ +import static org.junit.Assert.*; + +import org.junit.Test; +import org.mef.twixt.StringValue; +import org.mef.twixt.Value; +import org.mef.twixt.validate.Validator; +import org.mef.twixt.validate.ValContext; +import org.mef.twixt.validate.ValidationErrors; + +public class BasicTests +{ + public static class PhoneNum extends StringValue + { + private class MyValidator implements Validator + { + + @Override + public void validate(ValContext valctx, Value obj) + { + StringValue val = (StringValue) obj; + String s = val.get(); + if (s.length() != 8) //258-1833 + { + valctx.addError("sdfdfs"); + } + } + + } + + public PhoneNum(String val) + { + super(val); + this.setValidator(new MyValidator()); + } + } + + @Test + public void test() throws Exception + { + String s = "258-9099"; + PhoneNum ph = new PhoneNum(s); + assertEquals(s, ph.get()); + + String s2 = ph.toString(); + assertEquals(s, s2); + + ValContext vtx = new ValContext(); + ph.validate(vtx); + assertEquals(true, vtx.succeeded()); + + ph.fromString("555-66"); + assertEquals("555-66", ph.get()); + ph.validate(vtx); + assertEquals(false, vtx.succeeded()); + } + +} diff --git a/samples/issue54/twixt/code/test/TwixtFormTests.java b/samples/issue54/twixt/code/test/TwixtFormTests.java new file mode 100644 index 0000000..6d6ceba --- /dev/null +++ b/samples/issue54/twixt/code/test/TwixtFormTests.java @@ -0,0 +1,54 @@ +import static org.junit.Assert.*; + +import java.util.Map; +import java.util.TreeMap; + +import org.junit.Test; +import org.mef.twixt.StringValue; +import org.mef.twixt.binder.MockTwixtBinder; +import org.mef.twixt.binder.TwixtBinder; +import org.mef.twixt.binder.TwixtForm; + +import base.BaseTest; + + +public class TwixtFormTests extends BaseTest +{ + public static class CarTwixt extends TwixtForm + { + public StringValue a; + public StringValue b; + + public CarTwixt() + { + a = new StringValue(); + b = new StringValue(); + } + } + + @Test + public void test() + { + CarTwixt twixt = new CarTwixt(); + assertEquals("", twixt.a.get()); + + MockTwixtBinder binder = new MockTwixtBinder(CarTwixt.class, buildMap()); + + boolean b = binder.bind(); + assertTrue(b); + twixt = binder.get(); + + assertEquals("abc", twixt.a.get()); + assertEquals("def", twixt.b.get()); + } + + + private Map buildMap() + { + Map map = new TreeMap(); + map.put("a", "abc"); + map.put("b", "def"); + + return map; + } +} diff --git a/samples/issue54/twixt/code/test/TwixtTests.java b/samples/issue54/twixt/code/test/TwixtTests.java new file mode 100644 index 0000000..99f0e83 --- /dev/null +++ b/samples/issue54/twixt/code/test/TwixtTests.java @@ -0,0 +1,173 @@ + +import static org.junit.Assert.*; + +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.TreeMap; + +import org.junit.Test; +import org.mef.twixt.BooleanValue; +import org.mef.twixt.Converter; +import org.mef.twixt.DateValue; +import org.mef.twixt.DoubleValue; +import org.mef.twixt.IntegerValue; +import org.mef.twixt.ListValue; +import org.mef.twixt.LongSelectValue; +import org.mef.twixt.LongValue; +import org.mef.twixt.SelectValue; +import org.mef.twixt.StringValue; +import org.mef.twixt.Value; +import org.mef.twixt.validate.ValContext; +import org.mef.twixt.validate.ValidationErrors; +import base.BaseTest; + + +public class TwixtTests extends BaseTest +{ + + public class CommaIntegerValue extends IntegerValue + { + private class Conv implements Converter + { + + @Override + public String print(Object obj) + { + Integer n = get(); + String s = NumberFormat.getNumberInstance(Locale.US).format(n); + return s; + } + + @Override + public Object parse(String s) + { + s = s.replace(",", ""); + Integer n = Integer.parseInt(s); + return n; + } + + } + public CommaIntegerValue() + { + this(0); + } + public CommaIntegerValue(Integer n) + { + super(n); + setConverter(new Conv()); + } + + + } + + @Test + public void test() + { + IntegerValue v = new IntegerValue(); + v.set(44); + assertEquals(44, v.get()); + + LongValue v2 = new LongValue(); + v2.set(456L); + assertEquals(456L, v2.get()); + + BooleanValue v3 = new BooleanValue(); + assertEquals(false, v3.get()); + v3.set(true); + assertEquals(true, v3.get()); + + StringValue v4 = new StringValue(); + assertEquals("", v4.get()); + v4.set("sdf"); + assertEquals("sdf", v4.get()); + + DateValue v5 = new DateValue(); + assertNotNull(v5.get()); + + Date dt = new Date(); + int yr = dt.getYear(); + v5.set(dt); + assertEquals(yr, v5.get().getYear()); + + DoubleValue v6 = new DoubleValue(); + v6.set(45.6); + assertEquals(45.6, v6.get(), 0.001); + + List L = new ArrayList(); + L.add(v3); + L.add(v4); + ListValue v7 = new ListValue(); + assertEquals(0, v7.size()); + v7.set(L); + assertEquals(2, v7.size()); + } + + @Test + public void testDate() throws Exception + { + Date dt = new Date(115, 01, 21, 8, 30); //Thu Feb 21 08:30:00 EST 2015 + log(dt.toString()); + + DateValue val = new DateValue(dt); + String s = val.toString(); + assertEquals("2015-02-21", s); + + val.fromString("2014-12-25"); + s = val.toString(); + assertEquals("2014-12-25", s); + } + + @Test + public void testComma() throws Exception + { + CommaIntegerValue v = new CommaIntegerValue(12345); + assertEquals("12,345", v.toString()); + + v.fromString("4,5678"); + assertEquals(45678, v.get()); + } + + @Test + public void testSelect() throws Exception + { + Map map = new TreeMap(); + map.put("1", "apple"); + map.put("2", "banana"); + map.put("3", "cherry"); + + SelectValue v = new SelectValue("2", map); + assertEquals("2", v.get()); + + ValContext vtx = new ValContext(); + v.validate(vtx); + assertEquals(true, vtx.succeeded()); + v.set("4"); + v.validate(vtx); + assertEquals(false, vtx.succeeded()); + + assertEquals(false, SelectValue.class.isAssignableFrom(StringValue.class)); + assertEquals(true, StringValue.class.isAssignableFrom(SelectValue.class)); //StringValue v = vselect; + } + @Test + public void testLongSelect() throws Exception + { + Map map = new TreeMap(); + map.put(1L, "apple"); + map.put(2L, "banana"); + map.put(3L, "cherry"); + + LongSelectValue v = new LongSelectValue(2L, map); + assertEquals(2L, v.get()); + + ValContext vtx = new ValContext(); + v.validate(vtx); + assertEquals(true, vtx.succeeded()); + v.set(4L); + v.validate(vtx); + assertEquals(false, vtx.succeeded()); + } +} diff --git a/samples/issue54/twixt/code/test/ValidationTests.java b/samples/issue54/twixt/code/test/ValidationTests.java new file mode 100644 index 0000000..9a3af08 --- /dev/null +++ b/samples/issue54/twixt/code/test/ValidationTests.java @@ -0,0 +1,73 @@ +import static org.junit.Assert.*; + +import java.util.List; + +import org.junit.Test; +import org.springframework.validation.BindException; +import org.springframework.validation.Errors; +import org.springframework.validation.FieldError; +import org.springframework.validation.ObjectError; +import org.springframework.validation.ValidationUtils; +import org.springframework.validation.Validator; +import org.hibernate.validator.*; + + + +public class ValidationTests +{ + public class Carrot + { + public String a; + } + + public static class MyVal implements Validator + { + + @Override + public boolean supports(Class arg0) + { + if (arg0 == Carrot.class) + { + return true; + } + return false; + } + + @Override + public void validate(Object arg0, Errors arg1) + { + Carrot carrot = (Carrot) arg0; + + if (carrot.a.equals("none")) + { + arg1.reject("a", "none is not allowed"); + } + + } + + } + @Test + public void test() + { + Carrot carrot = new Carrot(); + carrot.a = "none"; + BindException errors = new BindException(carrot, "address"); + + MyVal validator = new MyVal(); + ValidationUtils.invokeValidator(validator, carrot, errors); + + assertEquals(1, errors.getErrorCount()); + + for(ObjectError err : errors.getAllErrors()) + { + log(err.toString()); + } + } + + + protected void log(String s) + { + System.out.println(s); + } + +} diff --git a/samples/issue54/twixt/code/test/base/BaseTest.java b/samples/issue54/twixt/code/test/base/BaseTest.java new file mode 100644 index 0000000..f760af1 --- /dev/null +++ b/samples/issue54/twixt/code/test/base/BaseTest.java @@ -0,0 +1,66 @@ +package base; + +import static org.junit.Assert.*; + +import java.io.File; + +import org.junit.Test; + + + +public class BaseTest +{ + protected void createContext() + { + } + + protected void log(String s) + { + System.out.println(s); + } + protected String getCurrentDirectory() + { + File f = new File("."); + return f.getAbsolutePath(); + } + + protected String getTestFile(String filepath) + { + String path = this.getCurrentDirectory(); + path = pathCombine(path, "test\\tools\\testfiles"); + path = pathCombine(path, filepath); + return path; + } + protected String getUnitTestDir(String filepath) + { + String path = this.getCurrentDirectory(); + path = pathCombine(path, "test\\unittests"); + if (filepath != null) + { + path = pathCombine(path, filepath); + } + return path; + } + protected String getCurrentDir(String filepath) + { + String path = this.getCurrentDirectory(); + if (filepath != null) + { + path = pathCombine(path, filepath); + } + return path; + } + + + + protected String pathCombine(String path1, String path2) + { + if (! path1.endsWith("\\")) + { + path1 += "\\"; + } + String path = path1 + path2; + return path; + } + +}