diff --git a/Rapla/.classpath b/Rapla/.classpath new file mode 100644 index 0000000..4922c8f --- /dev/null +++ b/Rapla/.classpath @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Rapla/.project b/Rapla/.project new file mode 100644 index 0000000..f57836a --- /dev/null +++ b/Rapla/.project @@ -0,0 +1,17 @@ + + + Rapla + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/Rapla/INSTALL.txt b/Rapla/INSTALL.txt new file mode 100644 index 0000000..09f019f --- /dev/null +++ b/Rapla/INSTALL.txt @@ -0,0 +1,70 @@ +This is the INSTALLING-GUIDE for the Rapla-BINARY-DISTRIBUTION, + +You will find more information on our documentation pages on +http://rapla.sourceforge.net/documentation.html + +if you have downloaded the SOURCE-DISTRIBUTION you have to +!!! BUILD A BINARY-DISTRIBUTION first !!! +For more information read README-BUILD.txt. + +Requirements: + +You need JAVA: JRE 1.5 at least (1.6 recommended). +You can download it from java.sun.com + +* The SDK works for the binary and source distribution but is very + large (>20 MB) + +* The JRE will only work for the binary distribution (Compiler + missing) but is much smaller (12MB) + + +STARTING RAPLA: + +Start rapla.exe (rapla.sh under Unix). + +To start Rapla from the command-line, you have to set the java command +in your PATH-Variable (if its not already there), + +Example: +setenv PATH $PATH:/usr/local/java/bin (Unix) +set PATH=%PATH%:c:\Programme\Java\jre\bin (Windows) + +Then type: + +rapla.sh (Unix) +rapla.bat (win95/98). +call rapla.bat (win NT/2000) + + +If you want to try out the simpsons-sample change the +following configuration entry in webapp/WEB-INF/rapla.xconf + + + simpsons-data.xml + no + + +or overwrite your existing data.xml file. + +If you want to test multiuser functionality comment out the default login-entry in the facade-entry: + + +You can than use the following logins: + +isa lisa +admin donut +homer duffs (Admin) +skinner edna +monty money (Admin) +abe matlock + +For more information visit the documentation pages on http://rapla.sourceforge.net/ + +Rapla includes free software developed by other projects. +For a complete list of all external libraraies, take a look at legal/LIBRARIES-FAQ + + diff --git a/Rapla/README-Build.txt b/Rapla/README-Build.txt new file mode 100644 index 0000000..d4207f0 --- /dev/null +++ b/Rapla/README-Build.txt @@ -0,0 +1,50 @@ +This README is for building rapla from source. You will find more information on our +documentation pages on + +http://rapla.sourceforge.net/documentation.html + +1. Get the source: + +To get the Source-Distribution download it from +rapla.sourceforge.net/download + +If you want to checkout the latest developmant version read the +faq-entry on rapla.sourceforge.net. + + +2. Build Rapla: + +We use the ant tool for our build process. Ant is the java-equivalent to make +Look at jakarta.apache.org/ant for more information. If you don't want +to use Ant read the FAQS on rapla.sourceforge.net for alternative building. + + +Use the following batch files to start the ant.jar included with +the rapla source distribution. + +build.sh (Linux/Unix) +build.bat (Win) +call build (win NT/2000) + + +Calling build with no arguments will create a Binary-Distribution +in the sub-folder dist. There you will find the scripts to start rapla. (SEE INSTALL.txt) + + +You can call build with different arguments, e.g.: + + choose-target Executes the target specified in build.properties. + (default) Default target is dist-bin. + + dist-bin Creates a new Binary-Distribution in the folder dist. + build Builds a new rapla.jar in the build sub-folder + run Creates a new Binary-Distribution and runs it. + clean Deletes the build sub-folder + clean-dist Deletes the dist sub-folder + javadocs create the Javadoc in the build sub-folder + webapp Build a special distribution for Applet or Webstart deployment (see 3) + +Pass the target as a parameter to ant +e.g. build.sh dist-bin + +For a complete list of all targets use the -projecthelp option diff --git a/Rapla/README-Server.txt b/Rapla/README-Server.txt new file mode 100644 index 0000000..67704e9 --- /dev/null +++ b/Rapla/README-Server.txt @@ -0,0 +1,109 @@ +This README is for running rapla as server. You will find more information on our +documentation pages on + +http://rapla.sourceforge.net/documentation.html + + +The rapla server: +--------------------------- + +Since version 0.10.2 rapla server installation is very easy: +Download the binary-distribution or build one with "build dist-bin" + +You can start it with: + +raplaserver.bat (Windows) +call raplaserver.bat (NT) +raplaserver.sh run (Unix) + +If you want to install Rapla-Server as a SERVICE on Win NT/2000/XP/Vista/7 +use the scripts in the service folder to test/install/uninstall rapla as a service. + + +With a running server point your browser to +http://localhost:8051 + +You should see the rapla welcome screen. Click on start with webstart +and the webstart client should launch. If not launched automatically, +you should choose the webstart program from your +java installation (e.g. C:\Programme\Java\jre1.5.0_06\bin\webstart.exe under Windows). + +You can login with username "admin" and empty password + +Thats it. + +Installing into another servlet container +----------------------------------------- + +If you want to use another servlet container (e.g. tomcat) you can +copy the webapp (without s) folder to the webapps (with s) folder of +your servlet container and rename webapp (without s) to rapla. +See the documentation of your servelt-container for more details + +Installing in Apache or IIS +---------------------------- + +You can install Jetty with apache or IIS by uncommenting +the AJP13 listener in jetty/conf/main.xml. + +See http://jetty.mortbay.org/ for more information + +Troubleshooting: +---------------- +1. The most commen error will be long stacktrace with + "Caused by: java.net.BindException: Could not bind to port 8051" + somewhere hidden between the lines. This means that either another + rapla server is running (so stop or kill this process) or that this + port is blocked by another application. You need to configure + another the port in jetty/conf/main.xml + + + + + + +2. look in the troubleshooting faq under http://rapla.sourceforge.net/documentation.html + +3. Post a question to rapla-developers@lists.sourceforge.net + + +Running Multiple Rapla servers: +------------------------------- + +There is no Problem running multiple rapla servers on one computer. You just need to copy the webapp folder and rename it e.g. webapp2 +and add a new webapplication to jetty/jetty.xml + + + + /webapp2 + /rapla2 + /jetty/webdefault.xml + /webapp2 + + + Of course you need to change the context of the first webapp from / to /rapla + + + + /webapp + /rapla + /jetty/webdefault.xml + /webapp + + + + + +Now you should have two applications running +http://localhost:8051/rapla/ +http://localhost:8051/rapla2/ + + +Acknowlegdement +----------------- + +Rapla includes free software developed by the Apache Software Foundation +(http://www.apache.org/) and other organizations +For more Information see legal/LIBRARIES-FAQ + + diff --git a/Rapla/Trunk.iml b/Rapla/Trunk.iml new file mode 100644 index 0000000..94cc740 --- /dev/null +++ b/Rapla/Trunk.iml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Rapla/bin/ant b/Rapla/bin/ant new file mode 100644 index 0000000..45a180c --- /dev/null +++ b/Rapla/bin/ant @@ -0,0 +1,302 @@ +#! /bin/sh + +# Copyright 2001-2004 The Apache Software Foundation +# +# 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. + +# Extract launch and ant arguments, (see details below). +ant_exec_args= +no_config=false +use_jikes_default=false +ant_exec_debug=false +show_help=false +for arg in "$@" ; do + if [ "$arg" = "--noconfig" ] ; then + no_config=true + elif [ "$arg" = "--usejikes" ] ; then + use_jikes_default=true + elif [ "$arg" = "--execdebug" ] ; then + ant_exec_debug=true + elif [ my"$arg" = my"--h" -o my"$arg" = my"--help" ] ; then + show_help=true + ant_exec_args="$ant_exec_args -h" + else + if [ my"$arg" = my"-h" -o my"$arg" = my"-help" ] ; then + show_help=true + fi + ant_exec_args="$ant_exec_args \"$arg\"" + fi +done + +# Source/default ant configuration +if $no_config ; then + rpm_mode=false + usejikes=$use_jikes_default +else + # load system-wide ant configuration + if [ -f "/etc/ant.conf" ] ; then + . /etc/ant.conf + fi + + # load user ant configuration + if [ -f "$HOME/.ant/ant.conf" ] ; then + . $HOME/.ant/ant.conf + fi + if [ -f "$HOME/.antrc" ] ; then + . "$HOME/.antrc" + fi + + # provide default configuration values + if [ -z "$rpm_mode" ] ; then + rpm_mode=false + fi + if [ -z "$usejikes" ] ; then + usejikes=$use_jikes_default + fi +fi + +# Setup Java environment in rpm mode +if $rpm_mode ; then + if [ -f /usr/share/java-utils/java-functions ] ; then + . /usr/share/java-utils/java-functions + set_jvm + set_javacmd + fi +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +case "`uname`" in + CYGWIN*) cygwin=true ;; + Darwin*) darwin=true + if [ -z "$JAVA_HOME" ] ; then + JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Home + fi + ;; +esac + +if [ -z "$ANT_HOME" -o ! -d "$ANT_HOME" ] ; then + # try to find ANT + if [ -d /opt/ant ] ; then + ANT_HOME=/opt/ant + fi + + if [ -d "${HOME}/opt/ant" ] ; then + ANT_HOME="${HOME}/opt/ant" + fi + + ## resolve links - $0 may be a link to ant's home + PRG="$0" + progname=`basename "$0"` + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi + done + + ANT_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + ANT_HOME=`cd "$ANT_HOME" && pwd` +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$ANT_HOME" ] && + ANT_HOME=`cygpath --unix "$ANT_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# set ANT_LIB location +ANT_LIB="${ANT_HOME}/lib" + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD=`which java 2> /dev/null ` + if [ -z "$JAVACMD" ] ; then + JAVACMD=java + fi + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." + echo " We cannot execute $JAVACMD" + exit 1 +fi + +# Build local classpath using just the launcher in non-rpm mode or +# use the Jpackage helper in rpm mode with basic and default jars +# specified in the ant.conf configuration. Because the launcher is +# used, libraries linked in ANT_HOME will also be include, but this +# is discouraged as it is not java-version safe. A user should +# request optional jars and their dependencies via the OPT_JAR_LIST +# variable +if $rpm_mode && [ -f /usr/bin/build-classpath ] ; then + LOCALCLASSPATH="$(/usr/bin/build-classpath ant ant-launcher jaxp_parser_impl xml-commons-apis)" + # If the user requested to try to add some other jars to the classpath + if [ -n "$OPT_JAR_LIST" ] ; then + _OPTCLASSPATH="$(/usr/bin/build-classpath $OPT_JAR_LIST 2> /dev/null)" + if [ -n "$_OPTCLASSPATH" ] ; then + LOCALCLASSPATH="$LOCALCLASSPATH:$_OPTCLASSPATH" + fi + fi + + # Explicitly add javac path to classpath, assume JAVA_HOME set + # properly in rpm mode + if [ -f "$JAVA_HOME/lib/tools.jar" ] ; then + LOCALCLASSPATH="$LOCALCLASSPATH:$JAVA_HOME/lib/tools.jar" + fi + if [ -f "$JAVA_HOME/lib/classes.zip" ] ; then + LOCALCLASSPATH="$LOCALCLASSPATH:$JAVA_HOME/lib/classes.zip" + fi + + # if CLASSPATH_OVERRIDE env var is set, LOCALCLASSPATH will be + # user CLASSPATH first and ant-found jars after. + # In that case, the user CLASSPATH will override ant-found jars + # + # if CLASSPATH_OVERRIDE is not set, we'll have the normal behaviour + # with ant-found jars first and user CLASSPATH after + if [ -n "$CLASSPATH" ] ; then + # merge local and specified classpath + if [ -z "$LOCALCLASSPATH" ] ; then + LOCALCLASSPATH="$CLASSPATH" + elif [ -n "$CLASSPATH_OVERRIDE" ] ; then + LOCALCLASSPATH="$CLASSPATH:$LOCALCLASSPATH" + else + LOCALCLASSPATH="$LOCALCLASSPATH:$CLASSPATH" + fi + + # remove class path from launcher -lib option + CLASSPATH="" + fi +else + # not using rpm_mode; use launcher to determine classpaths + if [ -z "$LOCALCLASSPATH" ] ; then + LOCALCLASSPATH=$ANT_LIB/ant-launcher.jar + else + LOCALCLASSPATH=$ANT_LIB/ant-launcher.jar:$LOCALCLASSPATH + fi +fi + +if [ -n "$JAVA_HOME" ] ; then + # OSX hack to make Ant work with jikes + if $darwin ; then + OSXHACK="${JAVA_HOME}/../Classes" + if [ -d "${OSXHACK}" ] ; then + for i in "${OSXHACK}"/*.jar + do + JIKESPATH="$JIKESPATH:$i" + done + fi + fi +fi + +# Allow Jikes support (off by default) +if $usejikes; then + ANT_OPTS="$ANT_OPTS -Dbuild.compiler=jikes" +fi + +# For Cygwin, switch paths to appropriate format before running java +if $cygwin; then + if [ "$OS" = "Windows_NT" ] && cygpath -m .>/dev/null 2>/dev/null ; then + format=mixed + else + format=windows + fi + ANT_HOME=`cygpath --$format "$ANT_HOME"` + ANT_LIB=`cygpath --$format "$ANT_LIB"` + JAVA_HOME=`cygpath --$format "$JAVA_HOME"` + LOCALCLASSPATH=`cygpath --path --$format "$LOCALCLASSPATH"` + if [ -n "$CLASSPATH" ] ; then + CLASSPATH=`cygpath --path --$format "$CLASSPATH"` + fi + CYGHOME=`cygpath --$format "$HOME"` +fi + +# Show script help if requested +if $show_help ; then + echo $0 '[script options] [options] [target [target2 [target3] ..]]' + echo 'Script Options:' + echo ' --help, --h print this message and ant help' + echo ' --noconfig suppress sourcing of /etc/ant.conf,' + echo ' $HOME/.ant/ant.conf, and $HOME/.antrc' + echo ' configuration files' + echo ' --usejikes enable use of jikes by default, unless' + echo ' set explicitly in configuration files' + echo ' --execdebug print ant exec line generated by this' + echo ' launch script' + echo ' ' +fi +# add a second backslash to variables terminated by a backslash under cygwin +if $cygwin; then + case "$ANT_HOME" in + *\\ ) + ANT_HOME="$ANT_HOME\\" + ;; + esac + case "$CYGHOME" in + *\\ ) + CYGHOME="$CYGHOME\\" + ;; + esac + case "$JIKESPATH" in + *\\ ) + JIKESPATH="$JIKESPATH\\" + ;; + esac + case "$LOCALCLASSPATH" in + *\\ ) + LOCALCLASSPATH="$LOCALCLASSPATH\\" + ;; + esac + case "$CLASSPATH" in + *\\ ) + CLASSPATH="$CLASSPATH\\" + ;; + esac +fi +# Execute ant using eval/exec to preserve spaces in paths, +# java options, and ant args +ant_sys_opts= +if [ -n "$CYGHOME" ]; then + if [ -n "$JIKESPATH" ]; then + ant_sys_opts="-Djikes.class.path=\"$JIKESPATH\" -Dcygwin.user.home=\"$CYGHOME\"" + else + ant_sys_opts="-Dcygwin.user.home=\"$CYGHOME\"" + fi +else + if [ -n "$JIKESPATH" ]; then + ant_sys_opts="-Djikes.class.path=\"$JIKESPATH\"" + fi +fi +ant_exec_command="exec \"$JAVACMD\" $ANT_OPTS -classpath \"$LOCALCLASSPATH\" -Dant.home=\"$ANT_HOME\" -Dant.library.dir=\"$ANT_LIB\" $ant_sys_opts org.apache.tools.ant.launch.Launcher $ANT_ARGS -lib \"$CLASSPATH\" $ant_exec_args" +if $ant_exec_debug ; then + echo $ant_exec_command +fi +eval $ant_exec_command diff --git a/Rapla/bin/ant.bat b/Rapla/bin/ant.bat new file mode 100644 index 0000000..7e2379a --- /dev/null +++ b/Rapla/bin/ant.bat @@ -0,0 +1,106 @@ +@echo off + +REM Copyright 2001,2004 The Apache Software Foundation +REM +REM Licensed under the Apache License, Version 2.0 (the "License"); +REM you may not use this file except in compliance with the License. +REM You may obtain a copy of the License at +REM +REM http://www.apache.org/licenses/LICENSE-2.0 +REM +REM Unless required by applicable law or agreed to in writing, software +REM distributed under the License is distributed on an "AS IS" BASIS, +REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +REM See the License for the specific language governing permissions and +REM limitations under the License. + +if exist "%HOME%\antrc_pre.bat" call "%HOME%\antrc_pre.bat" + +if "%OS%"=="Windows_NT" @setlocal + +rem %~dp0 is expanded pathname of the current script under NT +set DEFAULT_ANT_HOME=%~dp0.. + +if "%ANT_HOME%"=="" set ANT_HOME=%DEFAULT_ANT_HOME% +set DEFAULT_ANT_HOME= + +rem Slurp the command line arguments. This loop allows for an unlimited number +rem of arguments (up to the command line limit, anyway). +set ANT_CMD_LINE_ARGS=%1 +if ""%1""=="""" goto doneStart +shift +:setupArgs +if ""%1""=="""" goto doneStart +set ANT_CMD_LINE_ARGS=%ANT_CMD_LINE_ARGS% %1 +shift +goto setupArgs +rem This label provides a place for the argument list loop to break out +rem and for NT handling to skip to. + +:doneStart +rem find ANT_HOME if it does not exist due to either an invalid value passed +rem by the user or the %0 problem on Windows 9x +if exist "%ANT_HOME%\lib\ant.jar" goto checkJava + +rem check for ant in Program Files +if not exist "%ProgramFiles%\ant" goto checkSystemDrive +set ANT_HOME=%ProgramFiles%\ant +goto checkJava + +:checkSystemDrive +rem check for ant in root directory of system drive +if not exist %SystemDrive%\ant\lib\ant.jar goto checkCDrive +set ANT_HOME=%SystemDrive%\ant +goto checkJava + +:checkCDrive +rem check for ant in C:\ant for Win9X users +if not exist C:\ant\lib\ant.jar goto noAntHome +set ANT_HOME=C:\ant +goto checkJava + +:noAntHome +echo ANT_HOME is set incorrectly or ant could not be located. Please set ANT_HOME. +goto end + +:checkJava +set _JAVACMD=%JAVACMD% + +if "%JAVA_HOME%" == "" goto noJavaHome +if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome +if "%_JAVACMD%" == "" set _JAVACMD=%JAVA_HOME%\bin\java.exe +goto checkJikes + +:noJavaHome +if "%_JAVACMD%" == "" set _JAVACMD=java.exe + +:checkJikes +if not "%JIKESPATH%"=="" goto runAntWithJikes + +:runAnt +if not "%CLASSPATH%"=="" goto runAntWithClasspath +"%_JAVACMD%" %ANT_OPTS% -classpath "%ANT_HOME%\lib\ant-launcher.jar" "-Dant.home=%ANT_HOME%" org.apache.tools.ant.launch.Launcher %ANT_ARGS% %ANT_CMD_LINE_ARGS% +goto end + +:runAntWithClasspath +"%_JAVACMD%" %ANT_OPTS% -classpath "%ANT_HOME%\lib\ant-launcher.jar" "-Dant.home=%ANT_HOME%" org.apache.tools.ant.launch.Launcher %ANT_ARGS% -lib "%CLASSPATH%" %ANT_CMD_LINE_ARGS% +goto end + +:runAntWithJikes +if not "%CLASSPATH%"=="" goto runAntWithJikesAndClasspath +"%_JAVACMD%" %ANT_OPTS% -classpath "%ANT_HOME%\lib\ant-launcher.jar" "-Dant.home=%ANT_HOME%" "-Djikes.class.path=%JIKESPATH%" org.apache.tools.ant.launch.Launcher %ANT_ARGS% %ANT_CMD_LINE_ARGS% +goto end + +:runAntWithJikesAndClasspath +"%_JAVACMD%" %ANT_OPTS% -classpath "%ANT_HOME%\lib\ant-launcher.jar" "-Dant.home=%ANT_HOME%" "-Djikes.class.path=%JIKESPATH%" org.apache.tools.ant.launch.Launcher %ANT_ARGS% -lib "%CLASSPATH%" %ANT_CMD_LINE_ARGS% +goto end + +:end +set _JAVACMD= +set ANT_CMD_LINE_ARGS= + +if "%OS%"=="Windows_NT" @endlocal + +:mainEnd +if exist "%HOME%\antrc_post.bat" call "%HOME%\antrc_post.bat" + diff --git a/Rapla/bin/ant.cmd b/Rapla/bin/ant.cmd new file mode 100644 index 0000000..94b5a45 --- /dev/null +++ b/Rapla/bin/ant.cmd @@ -0,0 +1,92 @@ +/* + Copyright 2003-2004 The Apache Software Foundation + + 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. + + Run ant +*/ + +'@echo off' +parse arg mode envarg '::' antarg + +if mode\='.' & mode\='..' & mode\='/' then do + envarg = mode envarg + mode = '' +end + +if antarg = '' then do + antarg = envarg + envarg = '' +end + +x = setlocal() + +env="OS2ENVIRONMENT" +antenv = _getenv_('antenv') +if _testenv_() = 0 then interpret 'call "' || antenv || '"' '"' || envarg || '"' + +if mode = '' then mode = _getenv_('ANT_MODE' '..') +if mode \= '/' then do + runrc = _getenv_('runrc') + antrc = _getenv_('antrc' 'antrc.cmd') + if mode = '..' then mode = '-r' + else mode = '' + interpret 'call "' || runrc || '"' antrc '"' || mode || '"' +end + +if _testenv_() = 0 then do + say 'Ant environment is not set properly' + x = endlocal() + exit 16 +end + +settings = '-Dant.home=' || ANT_HOME '-Djava.home=' || JAVA_HOME + +java = _getenv_('javacmd' 'java') +opts = value('ANT_OPTS',,env) +args = value('ANT_ARGS',,env) +lcp = value('LOCALCLASSPATH',,env) +cp = value('CLASSPATH',,env) +if value('ANT_USE_CP',,env) \= '' then do + if lcp \= '' & right(lcp, 1) \= ';' then lcp = lcp || ';' + lcp = lcp || cp + 'SET CLASSPATH=' +end +if lcp\='' then lcp = '-classpath' lcp + +cmd = java opts lcp '-jar' ANT_HOME ||'\lib\ant-launcher.jar' settings args antarg +launcher = stream(ANT_HOME ||'\lib\ant-launcher.jar', 'C', 'query exists') +if launcher = '' then entry = 'org.apache.tools.ant.Main' +else entry = 'org.apache.tools.ant.launch.Launcher' +java opts lcp entry settings args antarg + +x = endlocal() + +return rc + +_testenv_: procedure expose env ANT_HOME JAVA_HOME +ANT_HOME = value('ANT_HOME',,env) +if ANT_HOME = '' then return 0 +JAVA_HOME = value('JAVA_HOME',,env) +if JAVA_HOME = '' then return 0 +cp = translate(value('CLASSPATH',,env)) +if pos(translate(ANT_HOME), cp) = 0 then return 0 +if pos(translate(JAVA_HOME), cp) = 0 then return 0 +return 1 + +_getenv_: procedure expose env +parse arg envar default +if default = '' then default = envar +var = value(translate(envar),,env) +if var = '' then var = default +return var diff --git a/Rapla/bin/antRun b/Rapla/bin/antRun new file mode 100644 index 0000000..a511832 --- /dev/null +++ b/Rapla/bin/antRun @@ -0,0 +1,26 @@ +#!/bin/sh + +# +# Copyright 2001-2002,2004 The Apache Software Foundation +# +# 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. +# +# + +# Args: DIR command +cd "$1" +CMD="$2" +shift +shift + +exec "$CMD" "$@" diff --git a/Rapla/bin/antRun.bat b/Rapla/bin/antRun.bat new file mode 100644 index 0000000..204d9e3 --- /dev/null +++ b/Rapla/bin/antRun.bat @@ -0,0 +1,45 @@ +@echo off + +REM +REM Copyright 2001-2002,2004 The Apache Software Foundation +REM +REM Licensed under the Apache License, Version 2.0 (the "License"); +REM you may not use this file except in compliance with the License. +REM You may obtain a copy of the License at +REM +REM http://www.apache.org/licenses/LICENSE-2.0 +REM +REM Unless required by applicable law or agreed to in writing, software +REM distributed under the License is distributed on an "AS IS" BASIS, +REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +REM See the License for the specific language governing permissions and +REM limitations under the License. +REM +REM + +if "%OS%"=="Windows_NT" @setlocal + +if ""%1""=="""" goto runCommand + +rem Change drive and directory to %1 +if "%OS%"=="Windows_NT" cd /d ""%1"" +if not "%OS%"=="Windows_NT" cd ""%1"" +shift + +rem Slurp the command line arguments. This loop allows for an unlimited number +rem of agruments (up to the command line limit, anyway). +set ANT_RUN_CMD=%1 +if ""%1""=="""" goto runCommand +shift +:loop +if ""%1""=="""" goto runCommand +set ANT_RUN_CMD=%ANT_RUN_CMD% %1 +shift +goto loop + +:runCommand +rem echo %ANT_RUN_CMD% +%ANT_RUN_CMD% + +if "%OS%"=="Windows_NT" @endlocal + diff --git a/Rapla/bin/antenv.cmd b/Rapla/bin/antenv.cmd new file mode 100644 index 0000000..05efa85 --- /dev/null +++ b/Rapla/bin/antenv.cmd @@ -0,0 +1,99 @@ +/* + Copyright 2003-2004 The Apache Software Foundation + + 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. + + Ant environment +*/ + +'@echo off' +call RxFuncAdd "SysLoadFuncs", "RexxUtil", "SysLoadFuncs" +call SysLoadFuncs + +/* Prepare the parameters for later use */ +parse arg argv +mode = '' +args = '' +opts = '' +cp = '' +lcp = '' + +do i = 1 to words(argv) + param = word(argv, i) + select + when param='-lcp' then mode = 'l' + when param='-cp' | param='-classpath' then mode = 'c' + when abbrev('-opts', param, 4) then mode = 'o' + when abbrev('-args', param, 4) then mode = 'a' + otherwise + select + when mode = 'a' then args = space(args param, 1) + when mode = 'c' then cp = space(cp param, 1) + when mode = 'l' then lcp = space(lcp param, 1) + when mode = 'o' then opts = space(opts param, 1) + otherwise + say 'Option' param 'ignored' + end + end +end + +env="OS2ENVIRONMENT" +antconf = _getenv_('antconf' 'antconf.cmd') +runrc = _getenv_('runrc') +interpret 'call "' || runrc || '"' '"' || antconf || '"' 'ETC' +ANT_HOME = value('ANT_HOME',,env) +JAVA_HOME = value('JAVA_HOME',,env) +classpath = value('CLASSPATH',,env) +classes = stream(JAVA_HOME || "\lib\classes.zip", "C", "QUERY EXISTS") +if classes \= '' then classpath = prepend(classpath classes) +classes = stream(JAVA_HOME || "\lib\tools.jar", "C", "QUERY EXISTS") +if classes \= '' then classpath = prepend(classpath classes) + +classpath = prepend(classpath ANT_HOME || '\lib\ant-launcher.jar') +'SET CLASSPATH=' || classpath + +/* Setting classpathes, options and arguments */ +envset = _getenv_('envset') +if cp\='' then interpret 'call "' || envset || '"' '"; CLASSPATH"' '"' || cp || '"' +if lcp\='' then interpret 'call "' || envset || '"' '"; LOCALCLASSPATH"' '"' || lcp || '"' +if opts\='' then interpret 'call "' || envset || '"' '"-D ANT_OPTS"' '"' || opts || '"' +if args\='' then interpret 'call "' || envset || '"' '"ANT_ARGS"' '"' || args || '"' + +exit 0 + +addpath: procedure +parse arg path elem +if elem = '' then do + if path\='' & right(path, 1)\=';' then path = path || ';' + return path +end +if substr(path, length(path)) = ';' then glue = '' +else glue = ';' +if pos(translate(elem), translate(path)) = 0 then path = path || glue || elem || ';' +return path + +prepend: procedure +parse arg path elem +if elem = '' then do + if path\='' & right(path, 1)\=';' then path = path || ';' + return path +end +if pos(translate(elem), translate(path)) = 0 then path = elem || ';' || path +return path + +_getenv_: procedure expose env +parse arg envar default +if default = '' then default = envar +var = value(translate(envar),,env) +if var = '' then var = default +return var diff --git a/Rapla/bin/envset.cmd b/Rapla/bin/envset.cmd new file mode 100644 index 0000000..8fbd4dd --- /dev/null +++ b/Rapla/bin/envset.cmd @@ -0,0 +1,130 @@ +/* + + Copyright 2003-2004 The Apache Software Foundation + + 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. + +SET environment variables +First optional parameter: + ; parameters are considered parts of a path variable, semicolons are + appended to each element if not already present + -D parameters are properties for Java or Makefile etc., -D will be + prepended and the parameters will be separated by a space + =D the same as above but equal sign is not required + , parameters should be comma separated in the environment variable + - parameters should be separated by the next parameter + Other values mean that the first parameter is missing and the environment + variable will be set to the space separated parameters + +Second parameter: name of the environment variable + +Next parameters: values +; implies that the equal sign is considered a part of the parameter and is +not interpreted + +-D requires parameters in the form name=value. If the equal sign is not found, +the parameters are changed to name=expanded_name + +Other options have optional equal sign. If it is found, only the part after +the equal sign will be oprionally expanded. + +If the parameter is the minus sign, the next parameter will not be expanded. +If the parameter is a single dot, it will be replaced with the value of the +environment variable as it existed before envset was invoked. + +For other parameters the batch looks for the environment variable with the +same name (in uppercase). If it is found, it forms the expanded_name. If +the environment variable with such a name does not exist, the expanded_name +will hold the parameter name without case conversion. +*/ + +parse arg mode envar args + +equal = 0 +sep = ' ' + +/* Parse command line parameters */ +select + when mode='-' then do + sep = envar + parse var args envar args + end + when mode=';' then do + sep = '' + equal = -1 + end + when mode='-D' then equal = 1 + when mode='=D' then mode = '-D' + when mode=',' then sep = ',' +otherwise + args = envar args + envar = mode + mode = '' +end + +env = 'OS2ENVIRONMENT' +envar = translate(envar) +orig = value(envar,,env) +newval = '' +expand = 1 + +/* for each parameter... */ +do i = 1 to words(args) + if expand > 0 & word(args, i) = '-' then expand = 0 + else call addval word(args, i) +end + +/* Optionally enclose path variable by quotes */ +if mode = ';' & pos(' ', newval) > 0 then newval = '"' || newval || '"' + +/* Set the new value, 'SET' cannot be used since it does not allow '=' */ +x = value(envar, newval, env) +exit 0 + +addval: procedure expose sep equal orig expand newval mode env +parse arg var + +if var = '.' then expvar = orig +else do + if equal >= 0 then do + parse var var name '=' val + if val = '' then var = name + else var = val + end + if expand = 0 then expvar = var + else expvar = value(translate(var),,env) + if expvar = '' then expvar = var + if equal >= 0 then do + if val = '' then do + parse var expvar key '=' val + if val <> '' then name = key + else do + if equal > 0 then val = key + else name = key + end + end + else val = expvar + if pos(' ', val) > 0 | pos('=', val) > 0 then val = '"' || val || '"' + if val = '' then expvar = name + else expvar = name || '=' || val + end + if mode = '-D' then expvar = '-D' || expvar + if mode = ';' then do + if right(expvar, 1) <> ';' then expvar = expvar || ';' + end +end + +if newval = '' then newval = expvar +else newval = newval || sep || expvar +expand = 1 +return diff --git a/Rapla/bin/lcp.bat b/Rapla/bin/lcp.bat new file mode 100644 index 0000000..6a1f679 --- /dev/null +++ b/Rapla/bin/lcp.bat @@ -0,0 +1,30 @@ +REM +REM Copyright 2001-2004 The Apache Software Foundation +REM +REM Licensed under the Apache License, Version 2.0 (the "License"); +REM you may not use this file except in compliance with the License. +REM You may obtain a copy of the License at +REM +REM http://www.apache.org/licenses/LICENSE-2.0 +REM +REM Unless required by applicable law or agreed to in writing, software +REM distributed under the License is distributed on an "AS IS" BASIS, +REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +REM See the License for the specific language governing permissions and +REM limitations under the License. +REM +REM + +set _CLASSPATHCOMPONENT=%1 +if ""%1""=="""" goto gotAllArgs +shift + +:argCheck +if ""%1""=="""" goto gotAllArgs +set _CLASSPATHCOMPONENT=%_CLASSPATHCOMPONENT% %1 +shift +goto argCheck + +:gotAllArgs +set LOCALCLASSPATH=%_CLASSPATHCOMPONENT%;%LOCALCLASSPATH% + diff --git a/Rapla/bin/runrc.cmd b/Rapla/bin/runrc.cmd new file mode 100644 index 0000000..acdf724 --- /dev/null +++ b/Rapla/bin/runrc.cmd @@ -0,0 +1,59 @@ +/* + Copyright 2003-2004 The Apache Software Foundation + + 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. + + Run RC file, name is in the first arg, second arg is either PATH + ENV or -r or nothing +*/ + +parse arg name path rest + +if name = '' then do + say 'RC file name is missing' + exit 1 +end + +if rest \= '' then do + say 'Too many parameters' + exit 1 +end + +call runit name path +exit 0 + +runit: procedure +parse arg name path dir + +if path \= '' & path \= '-r' then do + dir = value(translate(path),,'OS2ENVIRONMENT') + if dir = '' then return + dir = translate(dir, '\', '/') /* change UNIX-like path to OS/2 */ +end + +if dir = '' then dir = directory() + +if path = '-r' then do /* recursive call */ + subdir = filespec('path', dir) + if subdir \= '\' then do + subdir = left(subdir, length(subdir)-1) + call runit name path filespec('drive', dir) || subdir + end +end + +/* Look for the file and run it */ +if right(dir, 1) \= '\' then dir = dir || '\' +rcfile = stream(dir || name, 'c', 'query exists') +if rcfile \= '' then interpret 'call "' || rcfile || '"' + +return diff --git a/Rapla/build.bat b/Rapla/build.bat new file mode 100644 index 0000000..d194556 --- /dev/null +++ b/Rapla/build.bat @@ -0,0 +1,19 @@ +@echo off +:: ------------------------------------------------------------------------- +:: build.bat Skript for Rapla +:: +:: +:: Usage: +:: for a list of all available build-targets type +:: .\build.bat -projecthelp + +if not "%ANT_HOME%" =="" goto gotAntHome + +set ANT_HOME=. +:gotAntHome +call %ANT_HOME%\bin\ant.bat %1 %2 %3 %4 %5 %6 + + + + + diff --git a/Rapla/build.properties b/Rapla/build.properties new file mode 100644 index 0000000..e704a10 --- /dev/null +++ b/Rapla/build.properties @@ -0,0 +1,14 @@ +# You can modify this settings to configure the build + +#Set the target that will be run by default +target=dist-bin + +#Class for starting rapla when using the "start" target +#start.class=org.rapla.Main + +#Class for testing rapla when using the "test" target +#test.class=org.rapla.entities.PreferencesTest + +#Path and password to the keystore used for signing +#keystore.file=testkeystore +#keystore.password=secret diff --git a/Rapla/build.properties.template b/Rapla/build.properties.template new file mode 100644 index 0000000..e704a10 --- /dev/null +++ b/Rapla/build.properties.template @@ -0,0 +1,14 @@ +# You can modify this settings to configure the build + +#Set the target that will be run by default +target=dist-bin + +#Class for starting rapla when using the "start" target +#start.class=org.rapla.Main + +#Class for testing rapla when using the "test" target +#test.class=org.rapla.entities.PreferencesTest + +#Path and password to the keystore used for signing +#keystore.file=testkeystore +#keystore.password=secret diff --git a/Rapla/build.sh b/Rapla/build.sh new file mode 100644 index 0000000..efa43a0 --- /dev/null +++ b/Rapla/build.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# +# Usage: +# +# for a list of all available build-targets type +# ./build.sh -projecthelp +# +chmod u+x ./bin/antRun +chmod u+x ./bin/ant +export ANT_HOME=. +$PWD/bin/ant -logger org.apache.tools.ant.NoBannerLogger -emacs $@ diff --git a/Rapla/build.xml b/Rapla/build.xml new file mode 100644 index 0000000..36f8a76 --- /dev/null +++ b/Rapla/build.xml @@ -0,0 +1,820 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+
+ + + + + + + + +
+ + +
+
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/Rapla/buildcalendar.xml b/Rapla/buildcalendar.xml new file mode 100644 index 0000000..a08c529 --- /dev/null +++ b/Rapla/buildcalendar.xml @@ -0,0 +1,217 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Rapla/doc.properties b/Rapla/doc.properties new file mode 100644 index 0000000..58682ea --- /dev/null +++ b/Rapla/doc.properties @@ -0,0 +1,8 @@ +doc.name=rapla +doc.version=1.4.3-beta +doc.date=${TODAY} +doc.year=2000-2011 +doc.copyright=Rapla Team +doc.developer-list-link=http://lists.sourceforge.net/lists/listinfo/rapla-developers +doc.developer-list=rapla-developers@lists.sourceforge.net +doc.homepage=http://rapla.org diff --git a/Rapla/doc/ReleaseNotes0.10.0beta.txt b/Rapla/doc/ReleaseNotes0.10.0beta.txt new file mode 100644 index 0000000..b77f971 --- /dev/null +++ b/Rapla/doc/ReleaseNotes0.10.0beta.txt @@ -0,0 +1,40 @@ +Changes: + +- DynamicTypes are now mandatory for reservations, persons and resources. + +- Refresh button moved to options. Disabled when used with remote-operator. + + +New: + +- Integration of org.rapla.calendarview in the main panel. + +- Resource and reservation selection panel in the org.rapla.calendarview. + +- Show periods in org.rapla.calendarview and in the date chooser. + +- Names don't need to be strings. You can combine names freely out of + the dynamic-type attributes (even categories). + +- Filters can be stored. + +- Selecting over multiple columns is possible in the org.rapla.calendarview. + +- Tooltips and Contextmenus on menu components (especially the trees) + +- Date possible as attribute + +- Export plugin for storing the print-views for later update. + +- Experimantal Support for storing in a database. More information + will be soon available on the wiki. + +- Admins can switch to any user + +- Java version will be displayed in Info-dialog ;) + +... and many many more + +Bugfixes: + +Too many to list here. \ No newline at end of file diff --git a/Rapla/doc/ReleaseNotes0.11.3.txt b/Rapla/doc/ReleaseNotes0.11.3.txt new file mode 100644 index 0000000..26250b9 --- /dev/null +++ b/Rapla/doc/ReleaseNotes0.11.3.txt @@ -0,0 +1,14 @@ +Changes since 0.11.2: +*Events won't show for users if the users can't see the allocatables +*Allocatables won't show in weekview if the user can't see them +*Now works and compiles with new JDK 1.5.0beta + +Bugfixes since 0.11.2: +* 890095 NO MORE CLIENT DISCONNECT ON UPDATE +* Delete selection will show the proper names of the objects to remove. +* You can't resize blocks that span more than one day in the weekview. +* 890418 Appointments with 0 duration are forbidden +* Client and Server-Versions are compared before each login, +to avoid the Serialization-Error +* Fixed Calendar-Bug with new JDK 1.5.0beta +* Can now edit category after an unsuccessful delete (caused by an existing dependency) diff --git a/Rapla/doc/ReleaseNotes0.11beta1.txt b/Rapla/doc/ReleaseNotes0.11beta1.txt new file mode 100644 index 0000000..d1b682a --- /dev/null +++ b/Rapla/doc/ReleaseNotes0.11beta1.txt @@ -0,0 +1,36 @@ +New Features since 0.10.2: + +- You can assign groups to users: Create a new main-category with key "user-groups". +- You can set access-permissions to resources and persons by user/group. +- You can specify absolute and relative booking-timeframes for each resource +per user/group. By setting the relative start-time to 0 you can prevent modifing appointments in the past. +- create- and last-changed- times are available for reservations. Right-click and choose + "view" +- You can disable conflict-creation on a per user/group basis for each resource. +- Easy hsqldb Database available (see wiki) +- LDAP Authentication. See wiki. +- Old reservations will only be transfered to the client if needed . + + +Changes: + +- raplaclient.jsp mapped to raplaclient.jnlp to avoid problems with Internet Explorer. + +Bugfixes: + +- saxon-aelfred parser is used per default to overcome xml-parser shortcuts in suns jdk +- ClassCastException during calendar export +- appointment-ids are assigned correctly when storing into the sql-database. No more +mysterious appointment doubles. +- NullPointerException in Email-Notification fixed +- Redraw Problem of weekview +- Problem when assigning 2 appointments to 2 different resoures. Sometimes + the second assignment was silently dropped. +- Broken HTML-Weekview, when appointments span 2 or more days. +- Bug in the weekview that displayed appointments, starting on 0:00 of the first day of week, also in the previous week. +- Error storing filters with no selected category +- Webstart Error with java 1.4.1_05 (see http://sourceforge.net/tracker/index.php?func=detail&aid=844423&group_id=7274&atid=107274) +- Email notification: Two different appointments with a dedicated resource + for each lists 4 instead of the correct 2 allocations. +- Many others + diff --git a/Rapla/doc/ReleaseNotes0.11beta2.txt b/Rapla/doc/ReleaseNotes0.11beta2.txt new file mode 100644 index 0000000..07e0f39 --- /dev/null +++ b/Rapla/doc/ReleaseNotes0.11beta2.txt @@ -0,0 +1,62 @@ +New Features since 0.11beta1: +- Month and Day views available +- Different access-levels for resources and persons: + denied: User can't read allocation of the resource + read: User can only read allocation of the resource + allocate: User can allocate the resource + allocate with conflicts: User can create conflicts + admin: User can modify resource, set permissions, and manipulate + all reservations-entries that allocate the resource. +- non-admin users can be allowed to create resources and person. When + such a user creates a resource it automatically adds a permission with + admin-access for the user. +- More reservation-information available in tooltip +- Users can be autocreated with LDAPAuthentication + +New Features since 0.10.2: + +- You can assign groups to users: Create a new main-category with key "user-groups". +- You can set access-permissions to resources and persons by user/group. +- You can specify absolute and relative booking-timeframes for each resource +per user/group. By setting the relative start-time to 0 you can prevent modifing appointments in the past. +- create- and last-changed- times are available for reservations. Right-click and choose + "view" +- You can disable conflict-creation on a per user/group basis for each resource. +- Easy hsqldb Database available (see wiki) +- LDAP Authentication. See wiki. +- Old reservations will only be transfered to the client if needed . + +Changes since 0.10.2: + +- raplaclient.jsp mapped to raplaclient.jnlp to avoid problems with Internet Explorer. + +Changes since 0.11beta1: + +- new format of data.xml of allocation assignment and create-conflict permisson. + New Version is 0.6 +- New LDAP-Configuration (see wiki) +- If you edit (right-click) one Appointment in the weekview, that appointment will be selected in the event-edit window. + +Bugfixes: + +- saxon-aelfred parser is used per default to overcome xml-parser shortcuts in suns jdk +- ClassCastException during org.rapla.calendarview export +- appointment-ids are assigned correctly when storing into the sql-database. No more +mysterious appointment doubles. +- NullPointerException in Email-Notification fixed +- Redraw Problem of weekview +- Problem when assigning 2 appointments to 2 different resoures. Sometimes + the second assignment was silently dropped. +- Broken HTML-Weekview, when appointments span 2 or more days. +- Bug in the weekview that displayed appointments, starting on 0:00 of the first day of week, also in the previous week. +- Error storing filters with no selected category +- Webstart Error with java 1.4.1_05 (see http://sourceforge.net/tracker/index.php?func=detail&aid=844423&group_id=7274&atid=107274) +- Email notification: Two different appointments with a dedicated resource + for each lists 4 instead of the correct 2 allocations. +- Many others + +Bugfixes since 0.11beta1: + +- Deadlock in client/server AFAIK appeared only in TestCase. +- Converting from old version fails with aelfred parser +- Failed to change ReservationFilter or ResourceFilter after a save. \ No newline at end of file diff --git a/Rapla/doc/ReleaseNotes0.12.1.txt b/Rapla/doc/ReleaseNotes0.12.1.txt new file mode 100644 index 0000000..091b1a9 --- /dev/null +++ b/Rapla/doc/ReleaseNotes0.12.1.txt @@ -0,0 +1,26 @@ +Bugfixes since 0.12.1beta2: +* AM-PM Time is correct in Weekview. +* Bug when removing user. +* Period-Chooser now visible if you switch back from repeat-forever. +* Conversion from Data stored in relation-db works. +* Update from user and periods are displayed in the left view + +New Features since 0.11.3: +* The new org.rapla.calendarview-view "week compact" groups many resources on a smaller space for + a better overview. +* You can now specify the size of the text-fields with string attributes +* Attributes (especialy the long description fields) can be moved to a second input-screen. They won't show up in tooltips. +* Print the detail-view for an appointment or a resource (right click and view). +* The Starttime shows in the tooltip. + +Changes since 0.11.3: +* You can't click on "create appointment" when you have only read access to the resource + +Bugfixes since 0.11.3: +* !!!You can now store the user preferences and create html-exports!!! +* You aren't allowed to create conflicts in the past ( if conflict-creation is disabled) +* The categroy user-groups is created on import, when not already present. +* The header shows the right date in HTML-View. +* HTML-Report only shows the blocks visible in the Calendarview +* many more... + diff --git a/Rapla/doc/ReleaseNotes0.12.2.txt b/Rapla/doc/ReleaseNotes0.12.2.txt new file mode 100644 index 0000000..1a21758 --- /dev/null +++ b/Rapla/doc/ReleaseNotes0.12.2.txt @@ -0,0 +1,35 @@ +New Features since 0.12.1: + +* The Exception-Dates are shown in the detail view of a reservation +* To add a new appointment to an existing event in the calendar, first +open the event in the event-edit window, then switch back to the main +calendar-window and select the proper slot and finaly right click in +the selection to add the new appointment to the open event. +* The total number of occurrances of an event is calculated and displayed + in the reservation-edit window. +* A button to copy the info text into the system-clipboard + +Bugfixes since 0.12.1: +* calendar.css now looks the same under IE and Netscape +* Fixed the empty page printing problem. +* The resource-names are now printed correctly + +New Features since 0.11.3: +* The new calendar-view "week compact" groups many resources on a smaller space for + a better overview. +* You can now specify the size of the text-fields with string attributes +* Attributes (especialy the long description fields) can be moved to a second input-screen. They won't show up in tooltips. +* Print the detail-view for an appointment or a resource (right click and view). +* The Starttime shows in the tooltip. + +Changes since 0.11.3: +* You can't click on "create appointment" when you have only read access to the resource + +Bugfixes since 0.11.3: +* !!!You can now store the user preferences and create html-exports!!! +* You aren't allowed to create conflicts in the past ( if conflict-creation is disabled) +* The categroy user-groups is created on import, when not already present. +* The header shows the right date in HTML-View. +* HTML-Report only shows the blocks visible in the Calendarview +* many more... + diff --git a/Rapla/doc/ReleaseNotes0.12.3.txt b/Rapla/doc/ReleaseNotes0.12.3.txt new file mode 100644 index 0000000..8217f6f --- /dev/null +++ b/Rapla/doc/ReleaseNotes0.12.3.txt @@ -0,0 +1,32 @@ +New Features since 0.12.2: + +* French translation +* You can directly create a weekly event from the weekview +* API new method getReservations() for resource array + +Bugfixes since 0.12.2: + +* ssl support now works +* You can't remove the user-groups category +* The update bug when entering new Period is no more +* In the event-edit window: If you only change to the detail view the save button state won't change. +* Cateroy attributes are now safly converted and removed, when changing or +removing an attribute + +New Features since 0.12.1: + +* The Exception-Dates are shown in the detail view of a reservation +* To add a new appointment to an existing event in the calendar, first +open the event in the event-edit window, then switch back to the main +calendar-window and select the proper slot and finaly right click in +the selection to add the new appointment to the open event. +* The total number of occurrances of an event is calculated and displayed + in the reservation-edit window. +* A button to copy the info text into the system-clipboard + +Bugfixes since 0.12.1: +* calendar.css now looks the same under IE and Netscape +* Fixed the empty page printing problem. +* The resource-names are now printed correctly + + diff --git a/Rapla/doc/ReleaseNotes0.12.4.txt b/Rapla/doc/ReleaseNotes0.12.4.txt new file mode 100644 index 0000000..f680a17 --- /dev/null +++ b/Rapla/doc/ReleaseNotes0.12.4.txt @@ -0,0 +1,52 @@ +Version 0.12.4 Released on 11/20/2004 + +New Features since 0.12.3: +* You can configure the timescale (rows-per-hour) in the rapla*.xconf locale section +* Attributes can be made invisible, so you can only edit but not view them (usefull for attributes like color, see next feature) +* you can assign colors for the resources. To do this add a new attribute with + the KEY "color" (use Advanced settings) and enter the RGB Hex value + for each resource ( i.e. #A2A3FE) + +Changes since 0.12.3: +* In the week-compact view: An event with two resources now appears in both resource-rows. +* The HTML-Calendar now uses 100% width of the page + +Bugfixes since 0.12.3: +* Fixed Nullpointer Exception with java 1.5 when showing the reservation info dialog +* Fixed Unreadable Timescale with java 1.5 +* [Bug 1006389] is fixed, and you can now create daily repeatings +* [data.xml] repeating end dates on the end of month are correctly + stored as 31.1.2004 instead of 0.2.2004 +---------------------------------------- +New Features since 0.12.2: + +* French translation +* You can directly create a weekly event from the weekview +* API new method getReservations() for resource array + +Bugfixes since 0.12.2: + +* ssl support now works +* You can't remove the user-groups category +* The update bug when entering new Period is no more +* In the event-edit window: If you only change to the detail view the save button state won't change. +* Cateroy attributes are now safly converted and removed, when changing or +removing an attribute +---------------------------------------- +New Features since 0.12.1: + +* The Exception-Dates are shown in the detail view of a reservation +* To add a new appointment to an existing event in the org.rapla.calendarview, first +open the event in the event-edit window, then switch back to the main +org.rapla.calendarview-window and select the proper slot and finaly right click in +the selection to add the new appointment to the open event. +* The total number of occurrances of an event is calculated and displayed + in the reservation-edit window. +* A button to copy the info text into the system-clipboard + +Bugfixes since 0.12.1: +* org.rapla.calendarview.css now looks the same under IE and Netscape +* Fixed the empty page printing problem. +* The resource-names are now printed correctly + + diff --git a/Rapla/doc/ReleaseNotes0.12.beta1.txt b/Rapla/doc/ReleaseNotes0.12.beta1.txt new file mode 100644 index 0000000..e73024e --- /dev/null +++ b/Rapla/doc/ReleaseNotes0.12.beta1.txt @@ -0,0 +1,19 @@ +New Features since 0.11.3: +* The new org.rapla.calendarview-view "week compact" groups many resources on a smaller space for + a better overview. +* You can now specify the size of the text-fields with string attributes +* Attributes (especialy the long description fields) can be moved to a second input-screen. They won't show up in tooltips. +* Print the detail-view for an appointment or a resource (right click and view). +* The Starttime shows in the tooltip. + +Changes since 0.11.3: +* You can't click on "create appointment" when you have only read access to the resource + +Bugfixes since 0.11.3: +* !!!You can now store the user preferences and create html-exports!!! +* You aren't allowed to create conflicts in the past ( if conflict-creation is disabled) +* The categroy user-groups is created on import, when not already present. +* The header shows the right date in HTML-View. +* HTML-Report only shows the blocks visible in the Calendarview +* many more... + diff --git a/Rapla/doc/ReleaseNotes0.12.beta2.txt b/Rapla/doc/ReleaseNotes0.12.beta2.txt new file mode 100644 index 0000000..ae0b0ac --- /dev/null +++ b/Rapla/doc/ReleaseNotes0.12.beta2.txt @@ -0,0 +1,23 @@ +Bugfixes since 0.12.1beta1: +* Conversion from Data stored in relation-db works. +* Update from user and periods are displayed in the left view + +New Features since 0.11.3: +* The new calendar-view "week compact" groups many resources on a smaller space for + a better overview. +* You can now specify the size of the text-fields with string attributes +* Attributes (especialy the long description fields) can be moved to a second input-screen. They won't show up in tooltips. +* Print the detail-view for an appointment or a resource (right click and view). +* The Starttime shows in the tooltip. + +Changes since 0.11.3: +* You can't click on "create appointment" when you have only read access to the resource + +Bugfixes since 0.11.3: +* !!!You can now store the user preferences and create html-exports!!! +* You aren't allowed to create conflicts in the past ( if conflict-creation is disabled) +* The categroy user-groups is created on import, when not already present. +* The header shows the right date in HTML-View. +* HTML-Report only shows the blocks visible in the Calendarview +* many more... + diff --git a/Rapla/doc/ReleaseNotes0_6.txt b/Rapla/doc/ReleaseNotes0_6.txt new file mode 100644 index 0000000..bf3dd45 --- /dev/null +++ b/Rapla/doc/ReleaseNotes0_6.txt @@ -0,0 +1,41 @@ +Rapla Version 0.6 released on 26.8.2001 + + +Changes and new Features + +- Changing the name of a reservation is possible. +- Reservation names are now unique according to their semester. +- It is now possible to select start- and end-time in a ComboBox. +- A warning-dialog appears if you want to close the edit-window and there are non-saved changes. +- Printing is now supported (only tested under Windows). The class org.rapla.PrintTools can be used to + print any AWT or Swing component. The component will be printed in + high-resolution. +- On saving of the XML-file there should be a backup of the + old data in data.xml.old +- Added some JUnit-tests. The test-cases are located in the test-src directory. + To run all test cases (expect the DB-Ones) type + build test-all (Windows) + build.sh test-all (Unix) + A test-build can be run before every commit to make the cvs version + more stable. +- For convinience the ok-button is allways left and the abort-button allways right. + + +enhanced i18n support: + +- The timezone of Rapla is set to GMT+00:00, by default. In future versions different + timezones will be supported, but for that we have to deal with some SQL-time-conversion problems. +- The data.xml file is now in english with language-sensitive resource-metadata. +- localization of times in the weekview ( 9 pm instead of 21:00) + + +Bugfixes (only some of them are listed here) + +- 9:00 - 10:00 and 10:00 - 11:00 never create a conflict +- There is always a default-semester +- Title of warnings are localized +- language-change is possible before second login. +- refresh is possible after first logout. +- 5-10 typos corrected (thanks to Magnus) + + diff --git a/Rapla/doc/ReleaseNotes0_61.txt b/Rapla/doc/ReleaseNotes0_61.txt new file mode 100644 index 0000000..644e58b --- /dev/null +++ b/Rapla/doc/ReleaseNotes0_61.txt @@ -0,0 +1,30 @@ +Rapla Version 0.61 released on 9.9.2001 + +Changes and new Features + +- printing-preview with editable title. +- the selected semester will be remembered during the session. +- info dialog. +- Some new tests. + Example: There is an target test-cvs to test the current-version, + in cvs after a commit. +- Allocations with no resources are shown in the reservation-overview, + but not in the resource-overview. +- Ant version changed to 1.4 +- New version of crimson: jaxp.jar and xml-apis.jar are obsolete +- Some smaller changes in the gui. (other gifs, text changes) +- data.xml: xml-header and encoding added (0.6 versions are still supported) +- convertXML2SQL.sh removed: + conversion from xmlFile to db and the other way round is now possible with + start.sh xml2sql + start.sh sql2xml + +Bugfixes: + +- bug in XML-Date-Time-Parsing fixed +- there is no exception on an invalid login +- language-change-error with JDK 1.2.2 +- build possible with JDK 1.2.2 + + + diff --git a/Rapla/doc/ReleaseNotes0_7.txt b/Rapla/doc/ReleaseNotes0_7.txt new file mode 100644 index 0000000..6079b40 --- /dev/null +++ b/Rapla/doc/ReleaseNotes0_7.txt @@ -0,0 +1,29 @@ +Rapla Version 0.7 released on 9.9.2001 + +Changes and new Features + +- ADMIN-Functionality +- PostgreSQL Support +- new language-resource system. It's very easy to add a new or modify + an existing language-resource. Take a look at + src/org/rapla/languages/resources.xml + +- Modified db-schema and xml-file. Nevertheless Version 0.7 can still + read and write the schema from 0.61. + +- Associated persons will be displayed in weekview. +- Weekview Configuration in rapla.propeterties +- Disable-Color-Button in print-preview-dialog + + +Bugfixes: + +- Slightly better line-breaks in weekview. +- Color-chosing-bug. The weekview is not dazzling anymore. +- <, & will be encoded in XML. +- The weekview-popup now works under linux. +- Calendar Component. When changing from december to january the + year will increase. +- Many many more. + + diff --git a/Rapla/doc/ReleaseNotes0_71.txt b/Rapla/doc/ReleaseNotes0_71.txt new file mode 100644 index 0000000..a7499ac --- /dev/null +++ b/Rapla/doc/ReleaseNotes0_71.txt @@ -0,0 +1,19 @@ +Rapla Version 0.71 has been released on 10.12.2001 + +Changes and new Features +- You can create, move, copy or paste the allocations directly in the weekview. +- block-width is configurable in print-preview +- worktime is configurable in weekview +- Better webstart-support +- Users can change their email and their name +- If no reservation ( or resource) is selected, the weekview will + display all reservations ( or resources). + +Bugfixes: +- Resource-Details will be show in the weekview. +- The semester-editing-org.rapla.calendarview displays the correct weekdays. +- The sorting-order of resourcetypes will change on language-change. +- allocationDeleted throws no more ConcurrentModificationException +- Many many more. + + diff --git a/Rapla/doc/ReleaseNotes0_72.txt b/Rapla/doc/ReleaseNotes0_72.txt new file mode 100644 index 0000000..56cebfc --- /dev/null +++ b/Rapla/doc/ReleaseNotes0_72.txt @@ -0,0 +1,13 @@ +Rapla Version 0.72 has been released on 22.04.2002 + +Changes: +- New Postgres JDBC driver + +Bugfixes: +- !!If you use DBOperator and delete a reservation, you will not delete + the link for other reservations!! (This was a serious bug in 0.71) +- DB import and export now work from the start script. +- Dragging-Bug while creating a new appointment in the weekview. +- Now works with older mysql versions. +- You can use the '&' character for resource and reservation names with the XMLOperator. +- Copy and Paste works as expected. \ No newline at end of file diff --git a/Rapla/doc/ReleaseNotes0_8.txt b/Rapla/doc/ReleaseNotes0_8.txt new file mode 100644 index 0000000..f86eb54 --- /dev/null +++ b/Rapla/doc/ReleaseNotes0_8.txt @@ -0,0 +1,13 @@ +Rapla Version 0.8 has been released on 29.08.2002 + +Changes: + +Bugfixes: + +- !!If you use DBOperator and delete a reservation, you will not delete + the link for other reservations!! (This was a serious bug in 0.71) +- DB import and export now work from the start script. +- Dragging-Bug while creating a new appointment in the weekview. +- Now works with older mysql versions. +- You can use the '&' character for resource and reservation names with the XMLOperator. +- Copy and Paste works as expected. \ No newline at end of file diff --git a/Rapla/doc/ReleaseNotes0_81.txt b/Rapla/doc/ReleaseNotes0_81.txt new file mode 100644 index 0000000..9ace9c9 --- /dev/null +++ b/Rapla/doc/ReleaseNotes0_81.txt @@ -0,0 +1,22 @@ +Rapla Version 0.81 has been released on 4.9.2002 + +Changes: +- Date will show next to day-of-week in weekview +- Blocks that allocate the same resources or persons are grouped in one column. +- SansSerif font in wizard-help and Reservation/Resource/Person -info for better + readability. +- Text HTMLViews selectable +- Adding a new resource will now update an open reservation-edit-window. + +Bugfixes: +- Period-edit org.rapla.calendarview-drop-down under windows. +- The selected objects are selected in the filter-dialog. +- Repeating-end is not decreased by one after editing. +- No org.rapla.calendarview popup on doubleclicking a type-node in the resource-tree +- HTMLViews have correct sizes +- The add and remove Buttons are enabled if the selected resource is in the first row +- Restrictions (a resource or person is restricted if its connected to + one or more appointments instead the whole reservation) are correctly + displayed in weekview and in the reservation-html-view. + + diff --git a/Rapla/doc/ReleaseNotes0_9.txt b/Rapla/doc/ReleaseNotes0_9.txt new file mode 100644 index 0000000..d97df4b --- /dev/null +++ b/Rapla/doc/ReleaseNotes0_9.txt @@ -0,0 +1,31 @@ +Rapla Version 0.9 has been released on 22.11.2002 + +Changes: +- Filtering of resources, persons and reservations is possible in all + lists and in the weekview. +- Appointment editing has been rebuild. (Usability increased) +- PeriodWizard is now non modal. +- DropDown Symbols are visible on period-button and in allocation-table. + +Option-Usecase splitted: +- Period-list and reservationtype edit moved to reservation usecase. +- person and reservationtype edit moved to resource/person usecase. +- category-list has moved to a new usecase +- user-list has moved to a new usecase +- person and resource lists have been merged. + +Bugfixes: + +- Appointments which overlap midnight are correctly displayed in weekview +- Correct Redraw of weekview when saturday and sunday are displayed. +- NullPointerException in NumberField fixed +- DynamicTypes will be converted correctly +- "raplaserver.sh stop" works with secure-sockets +- User without admin priviliges can change their password +- PeriodPlugin works after restarting the applet +- No ArrayIndexOutOfBoundException when new period was created +- Workaround for problem with secure-connections in applet or webstart-mode under jdk 1.4.0 +- ... + +Internal: +new saxon.jar and fortress.jar diff --git a/Rapla/doc/ReleaseNotes1.0.txt b/Rapla/doc/ReleaseNotes1.0.txt new file mode 100644 index 0000000..860faec --- /dev/null +++ b/Rapla/doc/ReleaseNotes1.0.txt @@ -0,0 +1,86 @@ +Big Changes since 0.12.4 + + +The calendar stays on top +-------------------------- + +The left bar is no more. It is replaced by a context-sensitive Menubar. +Every click in the left tree affects the displayed events in the right pane + +A click on the users shows reservations of the user +A click on the resource shows all reservations of the resource +A click on an event-type shows all reservations of an event-type + +You can combine them. Clicking on a user, an event-type and a resource to show only +the reservations of the selected event-type, that are reserved by the selected user and allocate +the selected resource. + +Sytem and User Configuration with menu +-------------------------------------- + +Configuration is possible through the menu. You can configure the +plugins and most of the Rapla System through the following menus: +option or admin/option. + +NEW Plugin API! +---------------- + +New powerfull plugin API. See the plugins in the source-distribution +and MyRapla module in CVS. Find an overview of all currently allowed extension points of the Rapla API at + +http://rapla.sourceforge.net/doc/javadocs/org/rapla/plugin/RaplaExtensionPoints.html + +A plugin HOWTO will soon be available in the wiki + +New Views +------------ +- new period-view (Swing and HTML) +- new table-view (replaces event view) + +SQL Mapping +----------- + +A real SQL mapping of the Rapla data + +Other new features +------------------ +- You can now store your view settings (view/save) +- French translation of the plugins (even the period wizard) +- Custom Event and Resource coloring +- New plugin "Copy Periods" to copy the allocations from one period to another +- Reservations from other users will be hidden (if the user is not in the group "can see reservations from others" +- read-only account (If the user can't allocate anything) + +Other Changes +---------------- + +- The HTML report is removed. It will soon be replaced by a new view. +- Stable API +- Jetty Upgrade +- Changed from JSP to Servlets, that better fits in the plugin concept of Rapla +- Edit allocation menu is obsolete. Resource admins can now use edit, but aren't allowed to change the appointments + +And lots of bugfixing + + +Changes since 1.0RC1 +-------------------- + +- New Icons from the eclipse project were introduced +- You can now change the order of the categories. +- Most fonts changed form bold to plain +- The Text "not visible" is displayed on events that are not visible. + Events are not visible, because they are filtered out, not selected or the user + can't see events from others. There is a tooltip help for this event blocks. + +Autoexport: +- When you export, you will see a list of all previous exports. +- The URL of the HTML export is displayed (works only in webstart mode). +- You can replace an existing export. + +Bugfixing of +1356132 Order of resources in left part of Weekcompact is wrong. +1237332 (De)Allocation of unauthorized resource +1356501 a new period only affects gui on restart +1357693 Switching between daily and weekly appointments +- some bugs regarding the period chooser and the period view \ No newline at end of file diff --git a/Rapla/doc/ReleaseNotes1.0RC1.txt b/Rapla/doc/ReleaseNotes1.0RC1.txt new file mode 100644 index 0000000..e2e0e3a --- /dev/null +++ b/Rapla/doc/ReleaseNotes1.0RC1.txt @@ -0,0 +1,65 @@ +Big Changes since 0.12.4 + + +The calendar stays on top +-------------------------- + +The left bar is no more. It is replaced by a context-sensitive Menubar. +Every click in the left tree affects the displayed events in the right pane + +A click on the users shows reservations of the user +A click on the resource shows all reservations of the resource +A click on an event-type shows all reservations of an event-type + +You can combine them. Clicking on a user, an event-type and a resource to show only +the reservations of the selected event-type, that are reserved by the selected user and allocate +the selected resource. + +Sytem and User Configuration with menu +-------------------------------------- + +Configuration is possible through the menu. You can configure the +plugins and most of the Rapla System through the following menus: +option or admin/option. + +NEW Plugin API! +---------------- + +New powerfull plugin API. See the plugins in the source-distribution +and MyRapla module in CVS. Find an overview of all currently allowed extension points of the Rapla API at + +http://rapla.sourceforge.net/doc/javadocs/org/rapla/plugin/RaplaExtensionPoints.html + +A plugin HOWTO will soon be available in the wiki + +New Views +------------ +- new period-view (Swing and HTML) +- new table-view (replaces event view) + +SQL Mapping +----------- + +A real SQL mapping of the Rapla data + +Other new features +------------------ +- You can now store your view settings (view/save) +- French translation of the plugins (even the period wizard) +- Custom Event and Resource coloring +- New plugin "Copy Periods" to copy the allocations from one period to another +- Reservations from other users will be hidden (if the user is not in the group "can see reservations from others" +- read-only account (If the user can't allocate anything) + +Other Changes +---------------- + +- The HTML report is removed. It will soon be replaced by a new view. +- Stable API +- Jetty Upgrade +- Changed from JSP to Servlets, that better fits in the plugin concept of Rapla +- Edit allocation menu is obsolete. Resource admins can now use edit, but aren't allowed to change the appointments + +And lots of bugfixing + + diff --git a/Rapla/doc/ReleaseNotes1.0alpha1.txt b/Rapla/doc/ReleaseNotes1.0alpha1.txt new file mode 100644 index 0000000..119d598 --- /dev/null +++ b/Rapla/doc/ReleaseNotes1.0alpha1.txt @@ -0,0 +1,5 @@ +Bugfixes since 0.12.4: +* NullPointerException when trying to remove a resource that has reservations, that were not transfered to the client before the remove command. + + + diff --git a/Rapla/doc/ReleaseNotes1.1.txt b/Rapla/doc/ReleaseNotes1.1.txt new file mode 100644 index 0000000..05303ea --- /dev/null +++ b/Rapla/doc/ReleaseNotes1.1.txt @@ -0,0 +1,23 @@ +New features in Release 1.1 + +* New Icons +* Attribute fields in reservation-edit-window align +* An active filter is now indicated with an icon left to the tree folder +* Edit Dialogs are not modal any more. So you can have multiple edit dialogs +* You can copy from the edit dialogs to the clipboard even in webstart mode +* You can copy the url from the autoexport +* Print preview now shows the page borders, and how the page will be look when printed (will save a lot of paper) +* Better print support for TableView +* [ 1348082 ] Feedback which events belong together on cursor move +* Events that are filtered out are displayed transparently with their text if they reserve a selected resource +* It is now possible to select the resources root (Same as if all resources were selected) + +Changes + +org.rapla.components.print package renamed to org.rapla.components.iolayer +Save and load file is possible through the iolayer + +Bugfix + +[ 1410063 ] PB with rollover in html view with Mozilla browsers +[ 1392039 ] rapla1.0 webapp problem w/MYSQL \ No newline at end of file diff --git a/Rapla/doc/ReleaseNotes1.2.1.txt b/Rapla/doc/ReleaseNotes1.2.1.txt new file mode 100644 index 0000000..8be05d9 --- /dev/null +++ b/Rapla/doc/ReleaseNotes1.2.1.txt @@ -0,0 +1,66 @@ +New features in Release 1.2 +* Added new Mail Client that uses java mail api for sending +to install just copy the javamail libs to lib/server in the Source or WEB-INF/lib in the Binary Version +* Dutch Version (not complete yet) + +New features in Release 1.2beta2 + +* Czech version +* Performance improvements when dealing with many events +* resource sql table renamed to rapla_resource to remove conflicts with oracle (old name still works) +* You can now change the language in the options menu. Language change will be saved. +* Server starts with 512 mb max memory instead of 64 mb. +* Rapla starts with 128 mb max memory instead of 64 mb. +* Visual today indicator in calendar (today is highlighted) +* Added monthly repeatings +* Added yearly repeatings + +New features in Release 1.2beta1 + +* Spanish version +* Rapla runs completely over http. So you don't need to configure any port other than the servlet containers. +* You now can run multiple Rapla installations on one server by just creating more webapp-folders +* You can configure the column size of a text field +* GUI Pollishing. Replaced some icons. +* There is a new plugin called archive. But at the moment you can only autodelete old events +* The reservation-attribute panel in the event edit window resizes with the number of fields displayed + +Changes + +* Custom communication layer was replaced with http protocol and xml communication. + It is more robust and will causes less overhead on the server side. +* You can set a refresh interval for receiving the changes from other clients (Use a higher value if you have many concurrent access). +* Now we use piccolo as xml parser. Its a lot faster than aelfred +* Upgraded to new jetty version 5.11 +* There is no more webapp version. The webapp is now part of the binary distribution +* You need a new version of nt-service for this version + +Bugfix in 1.2.1 + +* Rapla 1.2 could not start when using the super-category as the reference category for an attribute-type +* [ 1556346 ] 100% system-usage on changing dates... (when using monthly events without ending) +* Under some circumstances Notifications were not triggered if you just move the appointment. + +Bugfix in 1.2 + +* Mozilla Weekview now shows the lines all time +* Fix for bug [ 1552105 ]. URL is not part of copy string in autoexport view. +* MailPlugin now works again +* Resizing of Attribute Panel now works for lager Textfields with more rows. +* You can now right click in the table view to create new reservations even when there is no event in the table + +Bugfix in 1.2beta2 + +* Ordering now works for categories when using database storage +* raplaserver import and raplaserver export now work again +* Fix for bug 1543370 +* Fix for NullPointerException in RaplaSQL + +Bugfix in 1.2beta1 + +Some bugs regarding the change of dynamic types +DB should be more fault tolerant +[ 1421294 ] Searching free appointments in "Weekly period wizard" +[ 1423818 ] repeating events not properly handeled with MySQL backend +[ 1423814 ] Problem with table view date selection +[ 1471848 ] LDAP plugin settings not saving/sticking diff --git a/Rapla/doc/ReleaseNotes1.2.txt b/Rapla/doc/ReleaseNotes1.2.txt new file mode 100644 index 0000000..20f9c2d --- /dev/null +++ b/Rapla/doc/ReleaseNotes1.2.txt @@ -0,0 +1,60 @@ +New features in Release 1.2 +* Added new Mail Client that uses java mail api for sending +to install just copy the javamail libs to lib/server in the Source or WEB-INF/lib in the Binary Version +* Dutch Version (not complete yet) + +New features in Release 1.2beta2 + +* Czech version +* Performance improvements when dealing with many events +* resource sql table renamed to rapla_resource to remove conflicts with oracle (old name still works) +* You can now change the language in the options menu. Language change will be saved. +* Server starts with 512 mb max memory instead of 64 mb. +* Rapla starts with 128 mb max memory instead of 64 mb. +* Visual today indicator in calendar (today is highlighted) +* Added monthly repeatings +* Added yearly repeatings + +New features in Release 1.2beta1 + +* Spanish version +* Rapla runs completely over http. So you don't need to configure any port other than the servlet containers. +* You now can run multiple Rapla installations on one server by just creating more webapp-folders +* You can configure the column size of a text field +* GUI Pollishing. Replaced some icons. +* There is a new plugin called archive. But at the moment you can only autodelete old events +* The reservation-attribute panel in the event edit window resizes with the number of fields displayed + +Changes + +* Custom communication layer was replaced with http protocol and xml communication. + It is more robust and will causes less overhead on the server side. +* You can set a refresh interval for receiving the changes from other clients (Use a higher value if you have many concurrent access). +* Now we use piccolo as xml parser. Its a lot faster than aelfred +* Upgraded to new jetty version 5.11 +* There is no more webapp version. The webapp is now part of the binary distribution +* You need a new version of nt-service for this version + +Bugfix in 1.2 + +* Mozilla Weekview now shows the lines all time +* Fix for bug [ 1552105 ]. URL is not part of copy string in autoexport view. +* MailPlugin now works again +* Resizing of Attribute Panel now works for lager Textfields with more rows. +* You can now right click in the table view to create new reservations even when there is no event in the table + +Bugfix in 1.2beta2 + +* Ordering now works for categories when using database storage +* raplaserver import and raplaserver export now work again +* Fix for bug 1543370 +* Fix for NullPointerException in RaplaSQL + +Bugfix in 1.2beta1 + +Some bugs regarding the change of dynamic types +DB should be more fault tolerant +[ 1421294 ] Searching free appointments in "Weekly period wizard" +[ 1423818 ] repeating events not properly handeled with MySQL backend +[ 1423814 ] Problem with table view date selection +[ 1471848 ] LDAP plugin settings not saving/sticking diff --git a/Rapla/doc/ReleaseNotes1.2beta1.txt b/Rapla/doc/ReleaseNotes1.2beta1.txt new file mode 100644 index 0000000..afa944f --- /dev/null +++ b/Rapla/doc/ReleaseNotes1.2beta1.txt @@ -0,0 +1,27 @@ +New features in Release 1.2beta1 + +* Rapla runs completely over http. So you don't need to configure any port other than the servlet containers. +* You now can run multiple Rapla installations on one server by just creating more webapp-folders +* You can configure the column size of a text field +* GUI Pollishing. Replaced some icons. +* There is a new plugin called archive. But at the moment you can only autodelete old events +* The reservation-attribute panel in the event edit window resizes with the number of fields displayed + +Changes + +* Custom Communication layer was replaced with http protocol and xml communication. + It is more robust and will causes less overhead on the server side. +* You can set a refresh interval for receiving the changes from other clients (Use a higher value if you have many concurrent access). +* Now we use piccolo as xml parser. Its a lot faster than aelfred +* Upgraded to new jetty version 5.11 +* There is no more webapp version. The webapp is now part of the binary distribution +* You need a new version of nt-service for this version + +Bugfix + +Some bugs regarding the change of dynamic types +DB should be more fault tolerant +[ 1421294 ] Searching free appointments in "Weekly period wizard" +[ 1423818 ] repeating events not properly handeled with MySQL backend +[ 1423814 ] Problem with table view date selection +[ 1471848 ] LDAP plugin settings not saving/sticking diff --git a/Rapla/doc/ReleaseNotes1.2beta2.txt b/Rapla/doc/ReleaseNotes1.2beta2.txt new file mode 100644 index 0000000..c595a81 --- /dev/null +++ b/Rapla/doc/ReleaseNotes1.2beta2.txt @@ -0,0 +1,47 @@ +New features in Release 1.2beta2 + +* Czech version +* Performance improvements when dealing with many events +* resource sql table renamed to rapla_resource to remove conflicts with oracle (old name still works) +* You can now change the language in the options menu. Language change will be saved. +* Server starts with 512 mb max memory instead of 64 mb. +* Rapla starts with 128 mb max memory instead of 64 mb. +* Visual today indicator in calendar (today is highlighted) +* Added monthly repeatings +* Added yearly repeatings + +New features in Release 1.2beta1 + +* Spanish version +* Rapla runs completely over http. So you don't need to configure any port other than the servlet containers. +* You now can run multiple Rapla installations on one server by just creating more webapp-folders +* You can configure the column size of a text field +* GUI Pollishing. Replaced some icons. +* There is a new plugin called archive. But at the moment you can only autodelete old events +* The reservation-attribute panel in the event edit window resizes with the number of fields displayed + +Changes + +* Custom communication layer was replaced with http protocol and xml communication. + It is more robust and will causes less overhead on the server side. +* You can set a refresh interval for receiving the changes from other clients (Use a higher value if you have many concurrent access). +* Now we use piccolo as xml parser. Its a lot faster than aelfred +* Upgraded to new jetty version 5.11 +* There is no more webapp version. The webapp is now part of the binary distribution +* You need a new version of nt-service for this version + +Bugfix since 1.2beta1 + +* Ordering now works for categories when using database storage +* raplaserver import and raplaserver export now work again +* Fix for bug 1543370 +* Fix for NullPointerException in RaplaSQL + +Bugfix + +Some bugs regarding the change of dynamic types +DB should be more fault tolerant +[ 1421294 ] Searching free appointments in "Weekly period wizard" +[ 1423818 ] repeating events not properly handeled with MySQL backend +[ 1423814 ] Problem with table view date selection +[ 1471848 ] LDAP plugin settings not saving/sticking diff --git a/Rapla/doc/ReleaseNotes1.3.1.txt b/Rapla/doc/ReleaseNotes1.3.1.txt new file mode 100644 index 0000000..dd90d9d --- /dev/null +++ b/Rapla/doc/ReleaseNotes1.3.1.txt @@ -0,0 +1,37 @@ +New features in Release 1.2.2 + +* Added paste as functionality +* Added Data Source (use datasource tag instead of driver, username and password to enable datasource) +* New plugable Main menu when you go on the first site of the web version +* You can now enable a list of all exported calendars in the main menu of the web version +* Show Navigation bar is now a parameter when exporting calendars to html +* You can now start rapla under windows with rapla.exe +* New service wrapper for starting rapla as a server under unix, osx and windows (load rapla_as_service.zip) +* There is a raplabootstrap.jar for starting rapla with a double click on most systems + +Changes + +* New icon for "new weekly event" +* In source version data.xml and config files are moved to webapp folder +* New jetty is used +* If jndi doesnt work. Authe + +Bugfixes + +* You can now export filename with characters that need to be url encoded +* Fixed Bug with special characters during load under tomcat or jetty +* It is now possible to have working times with a higher start hour than end hour e.g. 20:00 - 04:00 +* [ 1593336 ] even though restricted, user can create conflict by dragging +* Workaround for bug in 1.6 that doesnt render the print dialog correctly when displayed for the first time +* [ 1571870 ] Display Japanese Characters +* [ 1555022 ] TimeField can't instantiate for "CH" locale +* Notification mail handles removes correctly +* You can use Japanese Characters in html +* If an authentication store (e.g. jndi) doesnt work Rapla will still remain usable with the original logins. But not for the jndi users + +Bugfixes since 1.3 + +* java.util.ConcurrentModificationException no longer comes up after one hour of usage +* You can now use https with jetty and the configuration file reads +* Fixed Bug with utf-8 characters when refreshing in multiple clients +* SQL: Old resource table will not be used if new resource table exists. \ No newline at end of file diff --git a/Rapla/doc/ReleaseNotes1.3.2.txt b/Rapla/doc/ReleaseNotes1.3.2.txt new file mode 100644 index 0000000..1cb5c23 --- /dev/null +++ b/Rapla/doc/ReleaseNotes1.3.2.txt @@ -0,0 +1,48 @@ +Changes in 1.3 + +* Added paste as functionality +* Added Data Source (use datasource tag instead of driver, username and password to enable datasource) +* New plugable Main menu when you go on the first site of the web version +* You can now enable a list of all exported calendars in the main menu of the web version +* Show Navigation bar is now a parameter when exporting calendars to html +* You can now start rapla under windows with rapla.exe +* New service wrapper for starting rapla as a server under unix, osx and windows (load rapla_as_service.zip) +* There is a raplabootstrap.jar for starting rapla with a double click on most systems + +Bugfixes in 1.3 + +* You can now export filename with characters that need to be url encoded +* Fixed Bug with special characters during load under tomcat or jetty +* It is now possible to have working times with a higher start hour than end hour e.g. 20:00 - 04:00 +* [ 1593336 ] even though restricted, user can create conflict by dragging +* Workaround for bug in 1.6 that doesnt render the print dialog correctly when displayed for the first time +* [ 1571870 ] Display Japanese Characters +* [ 1555022 ] TimeField can't instantiate for "CH" locale +* Notification mail handles removes correctly +* You can use Japanese Characters in html +* If an authentication store (e.g. jndi) doesnt work Rapla will still remain usable with the original logins. But not for the jndi users + +Changes and new Features in 1.3.2 +#52 Updated Jetty to 6.1.1 +#17 Make the new x (reservation, resource, person, user, period, ...) entries in the popup menu context sensitive +#55 Scrollable Calendar in table view popup +#57 Create a test functionality for LDAP Plugin (You can now test if ldap authentification works without enabling it and restarting rapla) + +Bugfixes in 1.3.2 + +#51 exception day change after restart server +#3 Active Directory (LDAP) authentication [SF Bug 1677143] +#5 suppression of category does not update resources using it [ 1731273 ] +#6 Initial date in reservation Exception days calendar [SF1701420] +#7 Resources cannot be assigned to discrete appointments [1690844] +#18 java.util.ConcurrentModificationException comes up after one hour of usage +#19 Problem reading configuration file with jetty in https mode +#20 Special utf-8 characters are not displayed correctly when refreshing in multiple clients +#21 If you are using a SQL with old and new resource table name schema, the old one is still used +#53 Period View is not possible when start date is after end date of selected period kohlhaas +#56 Users can't see reservations made by other, although they are in the group "can see events from others" + + +For a detailed Explanation of the fixed Bugs see + +http://www.rapla.org/query?milestone=1.3.2 diff --git a/Rapla/doc/ReleaseNotes1.3.txt b/Rapla/doc/ReleaseNotes1.3.txt new file mode 100644 index 0000000..26352c3 --- /dev/null +++ b/Rapla/doc/ReleaseNotes1.3.txt @@ -0,0 +1,30 @@ +New features in Release 1.2.2 + +* Added paste as functionality +* Added Data Source (use datasource tag instead of driver, username and password to enable datasource) +* New plugable Main menu when you go on the first site of the web version +* You can now enable a list of all exported calendars in the main menu of the web version +* Show Navigation bar is now a parameter when exporting calendars to html +* You can now start rapla under windows with rapla.exe +* New service wrapper for starting rapla as a server under unix, osx and windows (load rapla_as_service.zip) +* There is a raplabootstrap.jar for starting rapla with a double click on most systems + +Changes + +* New icon for "new weekly event" +* In source version data.xml and config files are moved to webapp folder +* New jetty is used +* If jndi doesnt work. Authe + +Bugfixes + +* You can now export filename with characters that need to be url encoded +* Fixed Bug with special characters during load under tomcat or jetty +* It is now possible to have working times with a higher start hour than end hour e.g. 20:00 - 04:00 +* [ 1593336 ] even though restricted, user can create conflict by dragging +* Workaround for bug in 1.6 that doesnt render the print dialog correctly when displayed for the first time +* [ 1571870 ] Display Japanese Characters +* [ 1555022 ] TimeField can't instantiate for "CH" locale +* Notification mail handles removes correctly +* You can use Japanese Characters in html +* If an authentication store (e.g. jndi) doesnt work Rapla will still remain usable with the original logins. But not for the jndi users diff --git a/Rapla/doc/ReleaseNotes1.4.0.txt b/Rapla/doc/ReleaseNotes1.4.0.txt new file mode 100644 index 0000000..fabcdf7 --- /dev/null +++ b/Rapla/doc/ReleaseNotes1.4.0.txt @@ -0,0 +1,18 @@ +Changes in 1.4.0 + +- ICal Export Plugin integrated +- New occupation Plugin for planning dayly occupation (e.g. beds) +- Permission can be given to edit reservations to other users (add an category/boolean attribute with the key permission to the event type) +- Flag for whole-day-events +- Default attribute values can be set. +- You can store/and load many calendar-settings. Fast access via a drop down menu. +- Performance improvements with many reservations +- Tooltips and Conflicts can be hidden +- Split repeating into single events button +- Updated the README-Server and INSTALL.txt +GUI changes: +- New Icons and colors and an improved Webcalendar +- New Screen Layout. Splitted the big Tree into Resources, Admin and Conflict sections. +- The Filter-Dialog is replaced with a drop-down popup and allows an instant search feedback (You can use that for finding events by name) +- Replaced the old html/ical export with a new fast ical/html publishing, directly linked to the recallable calendar list + \ No newline at end of file diff --git a/Rapla/doc/ReleaseNotes1.4.trunk.txt b/Rapla/doc/ReleaseNotes1.4.trunk.txt new file mode 100644 index 0000000..b84d418 --- /dev/null +++ b/Rapla/doc/ReleaseNotes1.4.trunk.txt @@ -0,0 +1,25 @@ +Changes in 1.4.Trunk + +- User Import Plugin integrated +- A Login window close problem has been resolved. +- Rapla SQL database support incorrectly loaded the repeating.ntimes instead of repeating.until. + +GUI changes: +- Phonenumber added to User profile. ( LDAP plugin to modify) + Migration: + How to add the phonenumber to the Rapla SQL database? + Use MySql Workbench 5.2 or + MySql Command mode: + mysql -hlocalhost -uroot -p + Enter password: + use rapla_db; + mysql> ALTER TABLE `rapla_db`.`rapla_user` ADD COLUMN `PHONE` VARCHAR(30) NULL AFTER `EMAIL` ; + quit + For other SQL database the same process should be executed with their repective database management tools. + +- Reset to null button added to Category selection panel to Null for Dynamic types ( Resource Type) or resource instances. +- Admin tab is the default tab for administrators +- None Visible fields in a large scrollable resource edit panel will become visible when tab to the next field +- After a password failure the password will be reset to null for security reasons +- The Client OS userid will be set as default set for Rapla single user mode i.e. for rapala.exe or Rapla.bat + diff --git a/Rapla/doc/drafts-gereon-2002.zip b/Rapla/doc/drafts-gereon-2002.zip new file mode 100644 index 0000000..5e1136e Binary files /dev/null and b/Rapla/doc/drafts-gereon-2002.zip differ diff --git a/Rapla/doc/history.txt b/Rapla/doc/history.txt new file mode 100644 index 0000000..2a1f307 --- /dev/null +++ b/Rapla/doc/history.txt @@ -0,0 +1,149 @@ +0.5 from 6/2001 rapla.jar 310 kb +--------------------------------- + +Architectural: +entities, facade, resource-type and multiple storage concept +UseCase Driven GUI (multiple views) + +Features: +weekly events in periods +2 languages +save in xml file or database +calendar +resources, resource-types and periods must be defined in XML File + + + +0.6 from 9/2001 rapla.jar 330 kb +-------------------------------- + +Architectural: +Testing with Junit, +memento pattern +First IoC principle from avalon ( config and context object) + +Features: +Printing + + + +0.7 from 4/2002 rapla.jar 530 kb +--------------------------------- + +Architectural: +seperation of interface and implementation in the entity package. +Different interfaces of the facade object + +Features: +Admin functionality, can create and edit resource-type, periods and resources. +New language resource system +first datepicker component (for period edit) (one class only) + + +0.8 from 9/2002 rapla.jar 950 kb +---------------------------------- + +Architectural: +Client-, Server Design +inclusion of fortress container (IoC priciple, XML Config files) +split into several packages: components, service, plugin, gui/view, gui/edit, gui/toolkit +Component system, calendar component, rpc component, layout component, ... +new xml structure (including upgrade mechanism) + +Features: +Real client server mode +event-types +reservation wizard +first filter concept (only for reservations) +creation of single, dayly and weekly appointments possible +allocation assignment dialog + + + +0.9 from 12/2002 rapla.jar 1000 kb +----------------------------------- + +Architectural: +none + +Features: +categories +new appointment editor +attribute filters + + +0.10.2 from 6/2003 rapla.jar 1111 kb +-------------------------------------- + +Architectural: +- first real plugins autoexport and notification, exposing of services +- concept for storing xml in a database +- new webapp-distribution +- new library concept: different directories: server/client/common Just copy jar file into the appropriate lib dir to +make it available on the classpath + +Features: +Visible Kalendar on start ( with left selection pane) +Filters can be stored +html export +email notification +includes the jetty servlert-server for out-of-the box run. +simplified client/server installation + + +0.11.2 from 2/2004 rapla.jar 1200 kb +------------------------------------ + +Architectural: +none + +Features: +Permission System +Month and Day View + + +0.12.4 from 11/2004 rapla.jar 1240 kb +------------------------------------- + +Architectural: +none + +Features: +French Translation +Compact View + + +1.0RC1 from 11/2005 rapla.jar 1480 kb +-------------------------------------- + +Architectural: +Fortress exchanged with Pico concept (own implementation, constructor injection) +Plugin concept via classpath +extracted many plugins +Extension Point concept of eclipse +API package refactoring +API Stability (Exposed API) +CalendarView Concept +Standard Uniformed Application GUI (only one view, editor like) + +Features: +Menu System +SQL Mapping +New Period View, New Table View +Configuration through menus + +1.1 +--- + +Features: +Spanish Translation +Nicer GUI + +1.2 +--- + +Architectural: + +Custom Commication layer changed with webservice communication (soap like) + + diff --git a/Rapla/doc/kurzanleitung-informatik.txt b/Rapla/doc/kurzanleitung-informatik.txt new file mode 100644 index 0000000..cca2283 --- /dev/null +++ b/Rapla/doc/kurzanleitung-informatik.txt @@ -0,0 +1,51 @@ + +Installation und Start von Rapla: + +Vorraussetung: Java Webstart ist schon auf Ihrem Rechner installiert. + +Vorgehen: Geben Sie http://rapla.iai.uni-bonn.de in Ihrem Browser ein +und drcken auf den roten Start Knopf. Die neuste Rapla Version wird +dann auf ihrem Rechner heruntergeladen und gestartet. + +Nach dem Starten erscheint ein Login Dialog, tragen Sie bitte hier +ihren Informatik Benutzernamen und das ihnen zugesendete Passwort +ein. Sie knnen das Passwort spter auch unter "Optionen" ndern. + +Einen Raum reservieren: + +In der linken Baumansicht sehen Sie unter der Wurzel "alle gefilterten +Objekte" die Knoten "Raum", "Notebook", usw... . Klicken Sie auf das +Symbol neben dem Raum-Eintrag, um den Baum an dieser Stelle +aufzuklappen und die Rume anzuzeigen. + +Wenn Sie nun auf einen Raum klicken, wird in der rechten Hlfte die +Wochenbelegung des Raums angezeigt. In der oberen Leiste befinden sich +die Wochenauswahl. Mit den dort forhandenen "<<" und ">>" Schaltern +knnen Sie eine Woche vor und zurck blttern. Zwischen den +Bltter-Knpfen wird das ausgewhlte Datum, das Semester und die +Semesterwoche angezeit. Hier knnen Sie auch direkt zur ersten Woche +des Semesters springen, in welchem Sie einen Raum belegen mchten. + +Nachdem Sie das richtige Semester, die richtige Woche ausgewhlt +haben, markieren Sie in der Kalendaransicht einen freien Termin, indem +Sie mit Maus die entsprechenden freien Spalten selektieren. Ein +Rechtsklick in den markierten Bereich ffnet ein Kontextmen aus dem +Sie den Menpunkt "neue Versanstaltung" auswhlen knnen. + +Es ffnet sich ein neues Fenster "Neue Veranstaltung". Hier werden die +Informationen zu der Veranstaltung eintragen. Whlen Sie als erstes +die Veranstaltungsart z.B. "Lehrveranstaltung Informatik", danach die +Veranstaltungskategorie, z.B. "Vorlesung Grundstudium", den Titel der +Veranstaltung und den Studiengang/Institut, z.B. "Informatik III" aus. + +Wenn der Termin wchentlich whrend des ganzen Semster stattfinden +soll, klicken Sie in der Terminansicht auf wchentlich, die Start und +Endzeiten werden automatisch auf das jeweilige Semester angepasst. +Wenn die Veranstaltung an mehrere Tagen stattfinden soll z.B. montags +und dienstags, dann klicken Sie links auf den Schalter "Neuer Termin", +um den letzten Termin zu kopieren. Nun knnen Sie im rechten +Terminbereich den Wochentag des 2. Termins von Montag auf Dienstag +verschieben. + +Wenn Sie alle Termine eingetragen haben, drcken Sie "Speichern" und +danach "Besttigen" um diesen Termin in das System einzutragen. Fertig. diff --git a/Rapla/doc/todo b/Rapla/doc/todo new file mode 100644 index 0000000..ca48e35 --- /dev/null +++ b/Rapla/doc/todo @@ -0,0 +1,115 @@ +Doku: +- Indexseite fr alle exportierten Seiten. +- Rapla Plugin Development +- SSL HOWTO +- Anleitung zum BugReporting mit StackTrace +25% Doku der Funktionen +- Eine Note schreiben, dass man die default.css jetzt anpassen kann + +Ideen: +- Reservierungs-Filter soll dieselbe mglichkeit wie Permissionfilter haben. +- Zusaetzliche Moeglichkeit bei String-Filter: Enthaelt nicht +- Moeglichkeit eines Requests. Zuordung ist solange request, bis sie + von einem Admin abgehakt wurden. + Bei einer Veraenderung der Reservierung muessen die Zuordnungen erneut abgehakt werden. +- Zustzlich auch exclusions zu restrictions fr einen einfacheren Austausch von einer Person oder Ressource an einem Tag. +- Ressource-Admin darf Reservierungen bearbeiten, wenn er Admin fuer + alle Personen und Ressourcen ist. +- Persnliche Mitteilung in Konfliktanzeige, als Extension-Point von ReservationController +- Kommentare zu Ausnahmen +- Category auch Classifiable machen (Vermengung von Schema und Daten) +- Admin Flag bei Attributen, dass diese nur vom Admin bearbeitbar sind, damit kann man +zusammen mit dem event-coloring temptative bookings moeglich machen. Allerdings muss +mann dann die events dann read-only machen. Eigentlich brauch man noch ein Plugin dafuer. + +Todo: +- Sichtbarkeiten klaeren +- BlockRendererInterface +- Schriftgroessen austauschbar machen +- SwingTable nach HTML +- Fragen: Knnen user mit admin-access fr eine Ressource, diese Ressource beliebigen Veranstaltungen zuordnen? Ja. +- FAQ-Entry. Was ist wenn der Server nicht funktioniert (in die Logs Dateien schauen) +- Icon im Weekview fr Einzeltermine die Teil eines greren Termins sind +- Label.for benutzen +- Toolkit.copy() paste() und cut() berschreiben, damit Webstart funktioniert. +- Wenn man eine Ressource hinzufgt, erst einmal nur die freien Termine belegen (Nachfrage). +- Vor dem Speichern noch mal ueberpruefen, da ja ein falscher Client das Zeichen uebermittelt haben kann. +- MultiLanguageName auch als Attribut und fuer Reservierungsnamen +- Reihenfolge von DynamicType veraenderbar +- WeeklyWizard muss in der Kalendaransicht auch Navigationsknpfe fr nchste Woche und + vorherige Woche im Zeitraum haben. So kann auch in Zeitrumen die nicht Montags beginnen + korrekt geplant werden. Gut wre, wenn Veranstaltungen die erst in der 2. Woche (oder spter) + beginnen schon in der ersten Woche in einer anderen Farbe angezeigt sind (allerdings nur wenn + an diesem Termin nicht schon eine andere Veranstaltung stattfindet). +- Feiertage (oder Dies-Academicus) im RaplaCalendar anzeigen +50% Ausnahmen und Ferien beruecksichtigen +50% evtl. Dialoge auch mit Tastatur bedienbar machen. (Focus Traversal Policy) + +(Nicht) Trigger auch als Vorbedingungen mglich +(Nicht) StorageOperator,QueryModule, ... als zustzliche services in service.list eintragen. +(Nicht) Classification soll bei Attribut-Aenderung benachrichtigt werden. + +(Nicht) Entsprechende Warnungen bei Benutzung der Admin- Funktionalitaet im + Mulit-User Mode. +(Nicht) Seiteneffektfreiheit des Temp-Verzeichnises. Target site behlt noch das temp Verzeichnis + Site wird in Zukunft nicht mehr im Hauptrepsitory sein, sondern ein extra Projekt. +(Nicht) Seite braucht zuviel Platz in der Print-Ausgabe. Man kann die Seitenrnder + beim Postscript export nicht verndern +(Nicht) Kommandozeilenstart mit resourcename und anzuzeigendem Datum. + Es wird sowieso der Kalender mit den voreingestellten Ressourcen gestartet +(Nicht) Hierachien bei der Planung vorsehen. Profs zuerst dann die + Vorlesungen und dann erst die Uebrigen. + Kann ueber Rechte geregelt werden. +(Nicht) TimeZone Informationen bercksichtigen +(Nicht) Nur ein rapla.properties file, client soll sich die properties von Server holen. +(Nicht) Auswahl verschiedener Wochenansichten: + Alle Belegungen der Reservierung anzeigen. + Nur Ressourcen. + Nur Personen. + Nur ausgewaehlte Objekte. + Nur Titel. +(Nicht) Freie Termine fr ausgewhlte Ressourcen/Personen in der Bearbeitungsansicht suchen +(Nicht, zu spt) Hinweis, dass alle User Admin-Status erhalten, wenn von 0.5 oder 0.6 aufgeruestet wird. +(Nicht, zu spt) Hinweis, dass beim Import von alten Daten die Eigenschaft aus default.language bercksichtigt wird. +(Nicht) Option: Fuer jede Ressource eine Spalte. Frage: Was passiert, wenn mehrere + Ressourcen an einem Termin belegt wurden. +(Nicht) Datum in der Print-Ausgabe anzeigen. + +O.K. Einfuegen als neue Reservierung. +O.K. FAQ entry. Server als Dienst unter windows starten und herunterfahren. +O.K. HTTPS auch mit Webstart ? +O.K. Farben fr Resourcen einstellbar +O.K. Serialisierung-IDS in entities speichern. +O.K. PeriodChooser im Tableview soll nicht immer einen Zeitraum anzeigen +O.K. Serialisierung von einzelnen Entities als XML einfach nur bertragung + der XML Schnippsel +O.K. Rapla als Menapplikation +O.K. Typen/Kategorien/Benutzer/Semester unter Admin-Tasks zusammenfassen. +O.K. Gute Print ansicht fuer die Tabellenansicht +O.K. LoginDialog: "zuletzt ausgewaehlte Sprache" zur Sprachauswahl hinzufuegen und als standard-auswahl + setzen. +O.K. Zeitrahmenstrategie umsetzen + Nur PeriodChooser, die beiden dateChooser in der TableView und eine Period auswahl im Baum aendern den Zeitrahmen. + Alle anderen erweitern immer nur Zeitrahmen ( wenn er nicht auf die Ansicht passt), z.B. dateChooser und conflict select +O.K. Was passiert mit der Versionsnummer wenn der cache neu geladen wird? + Clients muessen neu starten +O.K. Versionsberprfung bei jeder client Server Verbindung +O.K. Entities readonly flag +O.K. Reservierungen werden nur bei Bedarf gelesen +O.K. Bearbeitungsansicht fr Permsission, Kategorien und Dynamische Typen + vereinheitlicht. +O.K. Passwrter verschlsseln +O.K. Der Reservation-Report kann als eine andere Wochenansicht gefasst werden. +O.K. Context Menue plugable machen + +O.K. Verschiedene User fuer verschiedene Export-Ansichten. In den Reservierungen + werden nur die fuer den User sichtbaren Ressourcen und Personen angezeigt. + Eine Reservierung wird nicht angezeigt, wenn sie keine sichtbaren Ressourcen + enthaelt. Wenn eine Export-Ansicht zu viele Informationen enthaelt, dann + sollte sie von einem User mit weniger Lese-Rechten angelegt werden (z.B. keine Laptops). + +Bugfix: + +- Webstart Printing Problem +(Nicht) Applikation beendet nicht unter Linux wenn PrinterJob.pageDialog oder PringerJob.print Dialog aufgerufen wird (Problem tritt nur im build-script auf) + diff --git a/Rapla/generated-src/META-INF/rapla-plugin.list b/Rapla/generated-src/META-INF/rapla-plugin.list new file mode 100644 index 0000000..2bd4950 --- /dev/null +++ b/Rapla/generated-src/META-INF/rapla-plugin.list @@ -0,0 +1,16 @@ +org.rapla.plugin.weekview.WeekViewPlugin +org.rapla.plugin.tableview.TableViewPlugin +org.rapla.plugin.periodwizard.PeriodWizardPlugin +org.rapla.plugin.periodview.PeriodViewPlugin +org.rapla.plugin.periodcopy.PeriodCopyPlugin +org.rapla.plugin.occupationview.OccupationPlugin +org.rapla.plugin.notification.NotificationPlugin +org.rapla.plugin.monthview.MonthViewPlugin +org.rapla.plugin.mail.MailPlugin +org.rapla.plugin.jndi.JNDIPlugin +org.rapla.plugin.importusers.ImportUsersPlugin +org.rapla.plugin.export2ical.Export2iCalPlugin +org.rapla.plugin.compactweekview.CompactWeekViewPlugin +org.rapla.plugin.autoexport.AutoExportPlugin +org.rapla.plugin.archiver.ArchiverPlugin +org.rapla.plugin.appointmentmarker.AppointmentMarkerPlugin diff --git a/Rapla/generated-src/org/rapla/MyResources.java b/Rapla/generated-src/org/rapla/MyResources.java new file mode 100644 index 0000000..2f788a7 --- /dev/null +++ b/Rapla/generated-src/org/rapla/MyResources.java @@ -0,0 +1,17 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class MyResources extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "rapla.welcome","Hello {0}, enjoy yourself!"} + , { "{$i18nbundle_parent$}","org.rapla.RaplaResources"} + }; +} diff --git a/Rapla/generated-src/org/rapla/RaplaResources.java b/Rapla/generated-src/org/rapla/RaplaResources.java new file mode 100644 index 0000000..490479b --- /dev/null +++ b/Rapla/generated-src/org/rapla/RaplaResources.java @@ -0,0 +1,499 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class RaplaResources extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "abort","Abort"} + , { "add","Add"} + , { "additional-view","Additional Information"} + , { "admin","Administrator"} + , { "admin.login","You have adminstrator privileges!"} + , { "administration","Administration"} + , { "all-day","all-day"} + , { "all_filtered","All filtered objects."} + , { "all_users","All users"} + , { "allocatable_in_timeframe","Allocatable in the given timeframe"} + , { "allocation_view","Allocation of {0}"} + , { "alteration","Alteration"} + , { "and","and"} + , { "apply","Apply"} + , { "appointment","Appointment"} + , { "appointment.convert","Convert to single events"} + , { "appointment.day_x","on day x"} + , { "appointment.days","Day(s)"} + , { "appointment.exception.days","Exception days:"} + , { "appointment.exception.general","Exceptions:"} + , { "appointment.exceptions","Exceptions"} + , { "appointment.format.exceptions","{0} Exceptions:"} + , { "appointment.hours","Hour(s)"} + , { "appointment.minutes","Min."} + , { "appointment.next_day","Next day"} + , { "appointment.no_exceptions","No exceptions:"} + , { "appointment.rule","Rule:"} + , { "appointment.same_day","Same day"} + , { "appointment_list","Appointment List"} + , { "appointments","Appointments"} + , { "attribute","Attribute"} + , { "attributes","Attributes"} + , { "available","Available"} + , { "back","Go back"} + , { "bind_with_person","bind with person"} + , { "calendar","Calendar"} + , { "calendar_settings","Calendar settings"} + , { "calendarname","Calendar name: "} + , { "calendarweek.abbreviation","cw {0,date,w}"} + , { "cancel","Cancel"} + , { "categories","Categories"} + , { "category","Category"} + , { "change","Change"} + , { "change.format","Change {0}"} + , { "change_appointment","Change appointment"} + , { "changes","Changes"} + , { "choose_language","Choose your language:"} + , { "choose_root_category","Choose root category:"} + , { "class","Class"} + , { "classification","Classification"} + , { "close","Close"} + , { "color","Color"} + , { "complete_time","Complete time"} + , { "confirm","Confirm"} + , { "confirm-close.ok","Discard changes"} + , { "confirm-close.question","You have not saved your changes."} + , { "confirm-close.title","Changes not saved!"} + , { "confirm.dialog.question","Save this Information?"} + , { "confirm.dialog.title","Confirm {0}"} + , { "conflict.appointment1","When?"} + , { "conflict.appointment2","conflicting allocation"} + , { "conflict.reservation1","Which event?"} + , { "conflict.reservation2","Other event?"} + , { "conflict.resource","What/who?"} + , { "conflict.user","User?"} + , { "conflictUC","Conflicts ({0,number,integer})"} + , { "conflicts","Conflicts"} + , { "connection","Connection with server"} + , { "constraints","Constraints"} + , { "continue","Continue"} + , { "copy","Copy"} + , { "copy_appointment.format","Do you want to copy the series or just the appointment on {0}?"} + , { "copy_to_clipboard","Copy to Clipboard"} + , { "create","Create"} + , { "created_at","Created at"} + , { "customized","Customized"} + , { "daily","Daily"} + , { "database","Database"} + , { "date","Date"} + , { "day","Day"} + , { "days","Days"} + , { "default","Default"} + , { "defaultselection","Default Selection"} + , { "delete","Delete"} + , { "delete.abort","No, don't delete it!"} + , { "delete.format","Delete {0}"} + , { "delete.ok","Yes, delete it!"} + , { "delete.question","You will delete the following objects: "} + , { "delete.title","Delete?"} + , { "delete_appointment.format","Do you want to delete the series or just the appointment on {0}?"} + , { "delete_selection","Delete Selection"} + , { "destination","Destination"} + , { "display_exceptions","Display exceptions in calendar"} + , { "duration","Duration"} + , { "dynamictype","Type"} + , { "dynamictype.annotation.nameformat","Displayed name format"} + , { "dynamictype.annotation.nameformat.description","Enclose the key names in {} to insert attributes, e.g. {myKey}"} + , { "dynamictype.name","Type name"} + , { "edit","Edit"} + , { "edit-view","Edit View"} + , { "edit.format","Edit {0}"} + , { "edit_advanced","Show advanced settings"} + , { "edit_reservation.format","Edit event: {0}"} + , { "edit_reservations","Edit own"} + , { "elementkey","Element key"} + , { "email","Email"} + , { "end_date","End date"} + , { "end_time","End time"} + , { "error","Error"} + , { "error.cant_delete_your_account","You can''t delete [{0}], because you are currently using this account!"} + , { "error.connect","Connection to [{0}] failed! Maybe the host is down and you could try later!"} + , { "error.connection_closed","No or closed connection to [{0}]. Please restart Rapla."} + , { "error.dependencies","Dependencies exist for these objects"} + , { "error.invalid_key","\"{0}\" is not a valid key!" + + " Note: You can only use a combination of max. 50 letters, digits or one of the" + + " following characters {1}. The key must start with {2} or a letter! Example: room_size" + + " "} + , { "error.login","Login failed!"} + , { "error.new_version","The object {0} couldn''t be modified. It was recently modified by someone else." + + " There was a newer version in the storage."} + , { "error.no_appointment","An event has to consist of at least one appointment!"} + , { "error.no_entry_for","You need to provide an entry for: {0}"} + , { "error.no_key","You need to provide a key: {0}"} + , { "error.no_name","Je moet een naam opgeven!"} + , { "error.no_reservation_name","You have to enter a title for your event!"} + , { "error.no_rollback","Error! Database update failed. Because" + + " your database doesn't support transactions, that could lead to" + + " serious failures. Please contact your administrator" + + " immediately!"} + , { "error.not_unique","The name \"{0}\" is already" + + " taken. Please choose a different name!"} + , { "error.one_type_requiered","At least one type is requiered."} + , { "error.passwords_dont_match","The passwords don't match!"} + , { "error.reference_not_stored","Error! Referenced object [{0}] not found in store. It was probably recently removed."} + , { "error.rollback","Database update failed. All changes were canceled."} + , { "error.wrong_password","Wrong password!"} + , { "every_appointment","Every appointment"} + , { "everytime","Every time"} + , { "exception","Exception"} + , { "exchange_allocatables","Change allocation"} + , { "exclamation.format","{0} !"} + , { "exclude_days","Exclude days"} + , { "exit","Exit"} + , { "exit.abort","Continue Rapla >>"} + , { "exit.ok","Quit Rapla"} + , { "exit.question","Do you really want to quit Rapla?"} + , { "exit.title","Quit Rapla?"} + , { "expected_columns","Expected columns"} + , { "expected_rows","Expected rows"} + , { "export","Export"} + , { "file","File"} + , { "filter","Filter"} + , { "filter.contains","contains"} + , { "filter.earlier_than","is earlier than"} + , { "filter.equals","equals"} + , { "filter.greater_or_equals","greater than or equal"} + , { "filter.is_greater_than","is greater than"} + , { "filter.is_smaller_than","is smaller than"} + , { "filter.later_than","is later than"} + , { "filter.not_equals","is not equal to"} + , { "filter.smaller_or_equals","smaller than or equal"} + , { "filter_allocatable","Resources and persons"} + , { "filter_reservation.all","From all users"} + , { "filter_reservation.own","Own events"} + , { "filter_restrictions","Restrict view of {0}"} + , { "filtername","Filter name"} + , { "fixed_date","Fixed Date"} + , { "for","for"} + , { "forename","First name"} + , { "format.repeat_from","from {0}"} + , { "format.repeat_n_times","Repeat {0} times"} + , { "format.repeat_until","until {0}"} + , { "goto_date","Go to Date"} + , { "group","Group"} + , { "groups","Groups"} + , { "help","Help"} + , { "hierarchy","Hierarchy"} + , { "holdbackconflicts","Hold back conflicts"} + , { "hour","Hour"} + , { "hours","Hours"} + , { "icon.abort","gui/images/choice_no.png"} + , { "icon.allocatable_available","gui/images/eclipse-icons/green.gif"} + , { "icon.allocatable_not_always_available","gui/images/eclipse-icons/yellow.gif"} + , { "icon.allocatable_taken","gui/images/eclipse-icons/conflict.gif"} + , { "icon.arrow_left","gui/images/eclipse-icons/arrow_left.gif"} + , { "icon.arrow_right","gui/images/eclipse-icons/arrow_right.gif"} + , { "icon.big_folder","gui/images/eclipse-icons/big_folder.gif"} + , { "icon.big_folder_categories","gui/images/eclipse-icons/big_folder_categories.gif"} + , { "icon.big_folder_conflicts","gui/images/eclipse-icons/big_folder_conflicts.gif"} + , { "icon.big_folder_events","gui/images/eclipse-icons/big_folder_events.gif"} + , { "icon.big_folder_events_filtered","gui/images/eclipse-icons/big_folder_events_filtered.gif"} + , { "icon.big_folder_filtered","gui/images/eclipse-icons/big_folder_filtered.gif"} + , { "icon.big_folder_periods","gui/images/eclipse-icons/big_folder_periods.gif"} + , { "icon.big_folder_resources","gui/images/eclipse-icons/big_folder_resources.gif"} + , { "icon.big_folder_resources_filtered","gui/images/eclipse-icons/big_folder_resources_filtered.gif"} + , { "icon.big_folder_users","gui/images/eclipse-icons/big_folder_users.gif"} + , { "icon.calendar","gui/images/calendar_small.png"} + , { "icon.cancel","gui/images/choice_no.png"} + , { "icon.category","gui/images/categories.png"} + , { "icon.checked","gui/images/eclipse-icons/checked.gif"} + , { "icon.clock","gui/images/eclipse-icons/clock.gif"} + , { "icon.close","gui/images/choice_no.png"} + , { "icon.confirm","gui/images/choice_yes.png"} + , { "icon.copy","gui/images/eclipse-icons/copy.gif"} + , { "icon.delete","gui/images/eclipse-icons/trash.gif"} + , { "icon.edit","gui/images/eclipse-icons/edit.gif"} + , { "icon.edit_window_small","gui/images/edit_window_small.png"} + , { "icon.empty","gui/images/eclipse-icons/blank.gif"} + , { "icon.error","gui/images/error.png"} + , { "icon.exceptionBackground","gui/images/exception.png"} + , { "icon.export","gui/images/eclipse-icons/export.gif"} + , { "icon.filter","gui/images/eclipse-icons/filter.gif"} + , { "icon.folder","gui/images/eclipse-icons/folder_open.gif"} + , { "icon.help","gui/images/eclipse-icons/help.gif"} + , { "icon.import","gui/images/eclipse-icons/import.gif"} + , { "icon.info","gui/images/info.png"} + , { "icon.info_small","gui/images/eclipse-icons/info.gif"} + , { "icon.language-select","gui/images/un_flag.png"} + , { "icon.list","gui/images/list.png"} + , { "icon.mail","gui/images/mail.png"} + , { "icon.new","gui/images/eclipse-icons/new.gif"} + , { "icon.new_repeating","gui/images/new_repeating.gif"} + , { "icon.no_perm","gui/images/eclipse-icons/no_perm.gif"} + , { "icon.options","gui/images/option_small.png"} + , { "icon.paste","gui/images/eclipse-icons/paste.gif"} + , { "icon.paste_new","gui/images/paste_new.gif"} + , { "icon.period","gui/images/period.png"} + , { "icon.print","gui/images/eclipse-icons/print.gif"} + , { "icon.question","gui/images/info.png"} + , { "icon.radio","gui/images/eclipse-icons/radio.gif"} + , { "icon.rapla","gui/images/tafel.png"} + , { "icon.rapla_small","gui/images/rapla_small.png"} + , { "icon.refresh","gui/images/refresh.png"} + , { "icon.remove","gui/images/eclipse-icons/trash.gif"} + , { "icon.repeating","gui/images/eclipse-icons/refresh.gif"} + , { "icon.resources","gui/images/resources.png"} + , { "icon.restart","gui/images/eclipse-icons/update.gif"} + , { "icon.save","gui/images/eclipse-icons/save.gif"} + , { "icon.single","gui/images/single.png"} + , { "icon.tree","gui/images/tree.png"} + , { "icon.tree.default","gui/images/eclipse-icons/green.gif"} + , { "icon.tree.minus","gui/images/eclipse-icons/tree_minus.gif"} + , { "icon.tree.person_not_always_available","gui/images/persons_yellow.png"} + , { "icon.tree.persons","gui/images/persons.png"} + , { "icon.tree.plus","gui/images/eclipse-icons/tree_plus.gif"} + , { "icon.unchecked","gui/images/eclipse-icons/unchecked.gif"} + , { "icon.user","gui/images/users.png"} + , { "icon.warning","gui/images/error.png"} + , { "import","Import"} + , { "in_period.format","in period {0}"} + , { "including_date","Include date: {0}"} + , { "info","About Rapla..."} + , { "info.text","" + + " Rapla version 1.4.3-beta" + + "
" + + " (C) Copyright 2000-2011 Rapla Team" + + "

Rapla homepage: http://rapla.org" + + "

" + + "

Post your feature requests, questions or bug reports" + + " to our developers mailing list:
" + + " rapla-developers@lists.sourceforge.net" + + "

" + + "

Build-Time: 2011-10-05 13:41

" + + "

Signature: {0}

" + + "

Java version {1}

" + + " "} + , { "info.title","" + + " Rapla version 1.4.3-beta" + + " "} + , { "insert","Insert"} + , { "interval","Interval"} + , { "interval.format","Every {0} {1}"} + , { "keep","Keep"} + , { "key","Key"} + , { "language","Language"} + , { "last_changed","Last changed"} + , { "last_changed_by","Last changed by"} + , { "legend","Legend"} + , { "level","Level"} + , { "license.text","" + + " Rapla version 1.4.3-beta (C) Copyright 2000-2011 Rapla Team" + + "

" + + " Rapla comes with NO\u00a0WARRANTY!!" + + " This is free software, you are welcome to redistribute it under certain" + + " conditions.

" + + " "} + , { "licensedialog.title","The GNU-license"} + , { "list.format","{0}: {1}"} + , { "load","Load"} + , { "login","Login"} + , { "logindialog.title","Rapla Login"} + , { "logout","Logout"} + , { "main-view","Main View"} + , { "minute","Minute"} + , { "minutes","Minutes"} + , { "month","Month"} + , { "monthly","Monthly"} + , { "months","Months"} + , { "move","Move"} + , { "move_appointment.format","Do you want to move the series or just the appointment on {0}?"} + , { "name","Name"} + , { "never","Never"} + , { "new","New"} + , { "new_appointment","New appointment"} + , { "new_category","New category"} + , { "new_password","New password"} + , { "new_reservation","New Event..."} + , { "new_reservation.format","New Event: {0}"} + , { "new_rule","New rule for"} + , { "new_sub-category","New sub-category"} + , { "no","No"} + , { "no-view","invisible"} + , { "no_classification","No classification"} + , { "no_repeating","No repeating"} + , { "not_selected.help","Not currently selected.
Unselect \"Only Own Reservations\" or change your filter settings. "} + , { "not_visible","Not visible"} + , { "not_visible.help","Not visible.
Access to the reservation info is restricted."} + , { "nothing_selected","Nothing selected"} + , { "occupation","Occupation"} + , { "ok","OK"} + , { "old_password","Old password"} + , { "only_own_reservations","Only Own Reservations"} + , { "open","Open"} + , { "options","Options"} + , { "or","or"} + , { "password","Password"} + , { "password_verification","Password verification"} + , { "paste","Paste"} + , { "paste_as","Paste As"} + , { "period","Period"} + , { "period.format.end","End of {0}"} + , { "period.format.start","Start of {0}"} + , { "period.format.week","{0}.week {1}"} + , { "period.not_set","Select period"} + , { "period_view","Period"} + , { "periods","Periods"} + , { "permission.access","Access"} + , { "permission.admin","administrator rights"} + , { "permission.allocate","can allocate"} + , { "permission.allocate-conflicts","can allocate & create conflicts"} + , { "permission.denied","Denied"} + , { "permission.read","can read"} + , { "permissions","Permissions"} + , { "person","Person"} + , { "person_type","Person type"} + , { "person_types","Person Types"} + , { "persons","Persons"} + , { "preferences","Preferences"} + , { "print","Print"} + , { "print_to_file","Print to File"} + , { "publish","Publish"} + , { "question","Question"} + , { "rapla.build","2011-10-05 13:41"} + , { "rapla.name","Rapla"} + , { "rapla.title","Rapla, smart resource and event planning"} + , { "rapla.version","1.4.3-beta"} + , { "rapla.welcome","Hello {0}, enjoy planning!"} + , { "refresh","Refresh"} + , { "reload_data","Load data"} + , { "remove","Remove"} + , { "repeat","Repeat"} + , { "repeating","Repeating"} + , { "repeating.end_date","until"} + , { "repeating.forever","Repeats forever"} + , { "repeating.interval.post","."} + , { "repeating.interval.pre","every"} + , { "repeating.n_times","x times"} + , { "repeating.start_date","from the"} + , { "report","Report"} + , { "reservation","Event"} + , { "reservation.allocations","Allocations:"} + , { "reservation.appointments","Appointments:"} + , { "reservation.create_without_wizard","Event"} + , { "reservation.name","Event name"} + , { "reservation.owner","Reserved by"} + , { "reservation_type","Event Type:"} + , { "reservations","Events"} + , { "reservations_from_all_users","Events from all users"} + , { "reset","Reset"} + , { "resource","Resource"} + , { "resource.owner","Registered by"} + , { "resource_type","Resource type"} + , { "resource_types","Resource types"} + , { "resources","Resources"} + , { "resources_persons","Resources and persons"} + , { "restart_client","Restart Rapla client"} + , { "restart_options","You need to restart Rapla/Rapla-server when you change the options!"} + , { "restart_server","Restart Server"} + , { "root","Root"} + , { "rows_per_hour","Rows per hour"} + , { "save","Save"} + , { "search","Search"} + , { "second","Second"} + , { "seconds","Seconds"} + , { "select","Select"} + , { "selectable","Selectable"} + , { "selectable_on","Selectable on"} + , { "selected","Selected"} + , { "selected_on","Selected on"} + , { "selection","Selection"} + , { "selection_resource","Resource selection"} + , { "serie","Series"} + , { "server","Server"} + , { "server_status","" + + " Server Status" + + " "} + , { "show_as","Show As"} + , { "show_conflicts","Display conflicts"} + , { "show_tips","Display mouse-over tips"} + , { "single_appointment","Single appointment"} + , { "source","Source"} + , { "start_date","Start"} + , { "start_rapla_with_applet","" + + " Start Rapla (with Java Plugin)" + + " "} + , { "start_rapla_with_webstart","" + + " Start Rapla (with java webstart)" + + " "} + , { "start_time","Start time"} + , { "surname","Name"} + , { "switch_back","switch back"} + , { "switch_to","Switch to"} + , { "table","Table"} + , { "time_at","at"} + , { "time_until","until"} + , { "today","Today"} + , { "total_occurances","Total occurances"} + , { "translation","Translation"} + , { "translation.format","Translations for \"{0}\":"} + , { "type","Type"} + , { "type.boolean","Yes|No"} + , { "type.date","Date"} + , { "type.int","Integer"} + , { "type.rapla:category","Category"} + , { "type.string","Text"} + , { "types","Types"} + , { "user","User"} + , { "username","Username"} + , { "users","Users"} + , { "view","View"} + , { "warning","Warning"} + , { "warning.conflict","WARNING: Conflicts found!"} + , { "warning.duplicated_appointments","WARNING: The same appointment exists multiple times [{0}]!"} + , { "warning.experienced_users_only","These settings should only be changed by experienced users!"} + , { "warning.max-one-eventtype","Only 1 event type is allowed. Adjust the filter!"} + , { "warning.no_allocatables_selected","You have not selected any resources/persons!"} + , { "warning.no_conflict_permission","You have no permissions to create conflicts for [{0}]!"} + , { "warning.no_reserve_permission","You have no permissions to reserve/change [{0}] an appointment [{1}]!"} + , { "warning.period_shorter_than_week","Period is shorter than 1 week!"} + , { "warning.readonly_storage","Data is only modified in memory. Changes will be lost for future sessions."} + , { "warning.reservation.delete","The event you are editing has been deleted."} + , { "warning.reservation.update","The event you are editing has been changed." + + " The data displayed in this window will be refreshed."} + , { "warning.update","The object \"{0}\" you are editing has been changed outside the window." + + " The current editing will be aborted."} + , { "webinfo.text","" + + "

More information:

" + + "

" + + " Java-Webstart is included in all Java versions since 1.4.0." + + " To run Rapla, you need Java 1.4 or above, which you can download from" + + " java.sun.com." + + "

" + + "

" + + " Check out rapla.sourceforge.net for more information." + + "

" + + " "} + , { "week","Week"} + , { "week_compact","Short week"} + , { "weekday","Day of week"} + , { "weekly","Weekly"} + , { "weeks","Weeks"} + , { "weekview.print.choose_export","Choose your save method!"} + , { "weekview.print.dialog_title","Print Preview"} + , { "weekview.print.format_button","Printer Preferences"} + , { "weekview.print.postscript","Postscript"} + , { "weekview.print.title_textfield","Title"} + , { "width","Width"} + , { "with","with"} + , { "x_days_advance","x Days in advance"} + , { "year","Year"} + , { "yearly","Yearly"} + , { "years","Years"} + , { "yes","Yes"} + , { "zero_appointment","No appointment"} + }; +} diff --git a/Rapla/generated-src/org/rapla/RaplaResources_cs.java b/Rapla/generated-src/org/rapla/RaplaResources_cs.java new file mode 100644 index 0000000..06ed901 --- /dev/null +++ b/Rapla/generated-src/org/rapla/RaplaResources_cs.java @@ -0,0 +1,370 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class RaplaResources_cs extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "abort","Ukon\u010dit"} + , { "add","P\u0159idat"} + , { "additional-view","Dopl\u0148uj\u00edc\u00ed informace"} + , { "admin","Administr\u00e1tor"} + , { "admin.login","M\u00e1te administr\u00e1torsk\u00e1 pr\u00e1va!"} + , { "administration","Administrace"} + , { "all-day","24h"} + , { "all_filtered","V\u0161echny filtrovan\u00e9 objekty."} + , { "all_users","V\u0161ichni u\u017eivatel\u00e9"} + , { "allocatable_in_timeframe","V\u00fdb\u011br dan\u00fd do \u010dasov\u00e9ho r\u00e1mce"} + , { "allocation_view","Um\u00edst\u011bn\u00ed {0}"} + , { "alteration","Zm\u011bna"} + , { "and","a"} + , { "apply","Pou\u017e\u00edt"} + , { "appointment","Rezervace"} + , { "appointment.convert","Prev\u00e9st na jednor\u00e1zov\u00e9 akce"} + , { "appointment.day_x","Za den x"} + , { "appointment.days","Den"} + , { "appointment.exception.days","Vyj\u00edme\u010dn\u00e9 dny:"} + , { "appointment.exception.general","Vyj\u00edmka:"} + , { "appointment.exceptions","Vyj\u00edmka"} + , { "appointment.format.exceptions","{0} Vyj\u00edmka:"} + , { "appointment.hours","Hodina"} + , { "appointment.minutes","Min."} + , { "appointment.next_day","Dal\u0161\u00ed den"} + , { "appointment.no_exceptions","Bez vyj\u00edmky"} + , { "appointment.rule","Pravidlo:"} + , { "appointment.same_day","Stejn\u00fd den"} + , { "appointment_list","Seznam rezervac\u00ed"} + , { "appointments","Rezervace"} + , { "attribute","Vlastnost"} + , { "attributes","Potvrdit"} + , { "available","Dostupn\u00fd"} + , { "back","Zp\u011bt"} + , { "calendar","Kalend\u00e1\u0159"} + , { "calendar_settings","nastaven\u00ed pohledu"} + , { "calendarname","N\u00e1zev kalend\u00e1\u0159e: "} + , { "calendarweek.abbreviation","T{0,date,w}"} + , { "cancel","Zru\u0161it"} + , { "categories","Seznam kategori\u00ed"} + , { "category","Kategorie"} + , { "change","Zm\u011bnit"} + , { "change.format","Zm\u011bnit {0}"} + , { "change_appointment","Zm\u011bna rezervace"} + , { "changes","Zm\u011bny"} + , { "choose_language","Zvolte v\u00e1\u0161 jazyk:"} + , { "choose_root_category","Vyber hlavn\u00ed kategorii:"} + , { "class","T\u0159\u00edda"} + , { "classification","Hodnocen\u00ed"} + , { "close","Zav\u0159\u00edt"} + , { "color","Barva"} + , { "complete_time","Celkov\u00fd \u010das"} + , { "confirm","Potvrdit"} + , { "confirm-close.ok","Ignoruj zm\u011bny"} + , { "confirm-close.question","Neulo\u017eil(a) jste zm\u011bny!"} + , { "confirm-close.title","Zm\u011bny neulo\u017eeny!"} + , { "confirm.dialog.question","Ulo\u017eit tyto informace?"} + , { "confirm.dialog.title","Potvrdit"} + , { "conflict.appointment1","Kdy\u017e"} + , { "conflict.appointment2","Konfiktn\u00ed rozvrh"} + , { "conflict.reservation1","\u010c\u00ed je to ud\u00e1lost?"} + , { "conflict.reservation2","Dal\u0161\u00ed ud\u00e1lost?"} + , { "conflict.resource","U\u017eivatel?"} + , { "conflict.user","U\u017eivatel?"} + , { "conflictUC","Konflikt ({0,number,integer})"} + , { "conflicts","Konflikt"} + , { "constraints","Omezen\u00ed"} + , { "continue","Pokra\u010dovat"} + , { "copy","Kopie"} + , { "copy_appointment.format","Chcete kop\u00edrovat celou s\u00e9rii nebo pouze jeden term\u00edn {0}?"} + , { "copy_to_clipboard","Kop\u00edrovat do schr\u00e1nky"} + , { "create","Vytvo\u0159it"} + , { "created_at","Vytvo\u0159eno"} + , { "customized","P\u0159izp\u016fsoben\u00fd"} + , { "daily","Po dnech"} + , { "database","Datab\u00e1ze"} + , { "date","Datum"} + , { "day","Den"} + , { "days","Dny"} + , { "default","V\u00fdchoz\u00ed"} + , { "defaultselection","V\u00fdb\u011br v\u00fdchoz\u00ed"} + , { "delete","Smazat"} + , { "delete.abort","Ne, ned\u011blej to!!"} + , { "delete.format","Odstranit {0}"} + , { "delete.ok","Ano, sma\u017e to!!"} + , { "delete.question","Bude\u0161 mazat n\u00e1sleduj\u00edc\u00ed objekt"} + , { "delete.title","Smazat?"} + , { "delete_appointment.format","Chcete smazat celou s\u00e9rii nebo pouze vybran\u00fd term\u00edn {0}?"} + , { "delete_selection","Smazat v\u00fdb\u011br"} + , { "destination","C\u00edl"} + , { "display_exceptions","Zobrazit vyj\u00edmky v kalend\u00e1\u0159i"} + , { "duration","Trv\u00e1n\u00ed"} + , { "dynamictype","Typ"} + , { "dynamictype.annotation.nameformat","Zobrazovan\u00fd form\u00e1t jm\u00e9na"} + , { "dynamictype.annotation.nameformat.description","Uzav\u0159en\u00e9 kl\u00ed\u010dov\u00e9 jm\u00e9no v {} k vlo\u017een\u00ed vlastnosti, nap\u0159. {jm\u00e9no}"} + , { "dynamictype.name","Jm\u00e9no typu"} + , { "edit","Upravit"} + , { "edit-view","Upravit pohled"} + , { "edit.format","Upravit {0}"} + , { "edit_advanced","Zobraz pokro\u010dil\u00e9 nastaven\u00ed"} + , { "edit_reservation.format","Upravit ud\u00e1lost: {0}"} + , { "edit_reservations","Zm\u011b\u0148 majitele"} + , { "elementkey","Kl\u00ed\u010d elementu"} + , { "email","e-mail"} + , { "end_date","Konec"} + , { "end_time","\u010cas ukon\u010den\u00ed"} + , { "error","Chyba"} + , { "error.cant_delete_your_account","Nem\u016f\u017eete smazat [{0}], proto\u017ee v\u00fdb\u011br vyu\u017e\u00edv\u00e1 tento \u00fa\u010det!"} + , { "error.connect","P\u0159ipojen\u00ed k [{0}] je \u0161patn\u00e9! Mo\u017en\u00e1 je vypnut\u00fd server. Zkuste to pozd\u011bji."} + , { "error.connection_closed","Neexistuje nebo nefunguje spojen\u00ed [{0}]. Pros\u00edm restartujte rezerva\u010dn\u00ed syst\u00e9m"} + , { "error.dependencies","Z\u00e1vislosti existuj\u00ed pro jin\u00fd objekt"} + , { "error.invalid_key","\"{0}\" je neplatn\u00fd kl\u00ed\u010d!
" + + " Note: M\u016f\u017eete pou\u017e\u00edt pouze v kombinaci s Max. 50 p\u00edsmen, \u010d\u00edslice nebo jeden z" + + " tyto znaky {1}. Kl\u00ed\u010d mus\u00ed za\u010d\u00ednat {2} nebo dopis! Nap\u0159.: mistnost_velikost" + + " "} + , { "error.login","\u0160patn\u00fd login!"} + , { "error.new_version","Objekt {0} nemohl b\u00fdt upraven. Byl upraven ned\u00e1vno n\u011bk\u00fdm ji\u00fdm."} + , { "error.no_entry_for","Nutn\u00e9 poskytnout p\u0159\u00edstup pro:"} + , { "error.no_key","Nutn\u00e9 poskytnout kl\u00ed\u010d:"} + , { "error.no_name","Nutn\u00e9 poskytnout jm\u00e9no"} + , { "error.no_reservation_name","Vlo\u017eit nadpis ud\u00e1losti!"} + , { "error.no_rollback","Chyba! Datab\u00e1ze se neobnovila. Proto\u017ee datab\u00e1ze nepodporuje transakci, chyba by se mohla opakovat, informujte administr\u00e1tora"} + , { "error.not_unique","Jm\u00e9no \"{0}\" je vyu\u017e\u00edv\u00e1no. Vyber jin\u00e9!"} + , { "error.one_type_requiered","Je po\u017eadov\u00e1n alespo\u0148 jeden typ."} + , { "error.passwords_dont_match","Heslo neodpov\u00edd\u00e1"} + , { "error.reference_not_stored","Chyba! Zm\u00edn\u011bn\u00fd objekt [{0}] se nenal\u00e9z\u00e1 v datab\u00e1zi. Pravd\u011bpodobn\u011b byl odstran\u011bn."} + , { "error.rollback","Datab\u00e1ze se neobnovila. V\u0161echny zm\u011bny zru\u0161eny."} + , { "error.wrong_password","\u0160patn\u00e9 heslo!"} + , { "every_appointment","V\u0161echny ujedn\u00e1n\u00ed"} + , { "everytime","poka\u017ed\u00e9"} + , { "exception","Vyj\u00edmka"} + , { "exchange_allocatables","Vym\u011bn\u011bn\u00fd rozvr\u017een\u00ed"} + , { "exclamation.format","{0} !"} + , { "exit","Odej\u00edt"} + , { "exit.abort","Pokra\u010duj v pl\u00e1nov\u00e1n\u00ed"} + , { "exit.ok","Ukon\u010dit syst\u00e9m Rapla"} + , { "exit.question","Opravdu chcete ukon\u010dit pl\u00e1nov\u00e1n\u00ed ?"} + , { "exit.title","Ukon\u010dit Rapla?"} + , { "expected_rows","O\u010dek\u00e1van\u00e1 \u0159adka"} + , { "export","Export"} + , { "file","Soubor"} + , { "filter.contains","Obsahuje"} + , { "filter.earlier_than","Je d\u0159\u00edve, ne\u017e"} + , { "filter.equals","Je stejn\u00fd, jako"} + , { "filter.greater_or_equals","je v\u011bt\u0161\u00ed nebo rovno"} + , { "filter.is_greater_than","Je v\u011bt\u0161\u00ed, ne\u017e"} + , { "filter.is_smaller_than","Je men\u0161\u00ed, ne\u017e"} + , { "filter.later_than","Je pozd\u011bji, ne\u017e"} + , { "filter.not_equals","nen\u00ed rovno"} + , { "filter.smaller_or_equals","Je men\u0161\u00ed nebo rovno"} + , { "filter_allocatable","Prost\u0159edky a osoby"} + , { "filter_reservation.all","Od ka\u017ed\u00e9ho u\u017eivatele"} + , { "filter_reservation.own","Moje ud\u00e1losti"} + , { "filter_restrictions","Pohled omezen\u00fd {0}"} + , { "filtername","N\u00e1zev filtru"} + , { "fixed_date","Pevn\u00fd datum"} + , { "for","Pro"} + , { "forename","Prvn\u00ed jm\u00e9no"} + , { "format.repeat_from","Z {0}"} + , { "format.repeat_n_times","Opakuj {0} kr\u00e1t"} + , { "format.repeat_until","Dokud {0}"} + , { "goto_date","J\u00edt na datum"} + , { "group","Skupina"} + , { "groups","Skupiny"} + , { "help","Pomoc"} + , { "hierarchy","Hierarchie"} + , { "holdbackconflicts","N\u00e1vrat p\u0159ed konflikt"} + , { "import","Import"} + , { "in_period.format","V period\u011b {0}"} + , { "including_date","v\u010detn\u011b data: {0}"} + , { "info","Informace"} + , { "insert","Vlo\u017eit"} + , { "interval","Interval"} + , { "interval.format","Ka\u017ed\u00fd {0} {1}"} + , { "keep","Podr\u017eet"} + , { "key","Kl\u00ed\u010d"} + , { "language","Jazyk"} + , { "last_changed","Naposledy zm\u011bn\u011bno"} + , { "last_changed_by","Naposledy zm\u011bnil u\u017eivatel: "} + , { "legend","Legenda"} + , { "level","\u00darove\u0148"} + , { "license.text","" + + " Rapla Verze 1.4.3-beta (C) Copyright 2000-2011 Rapla Team" + + "

Na syst\u00e9m Rapla nen\u00ed poskytov\u00e1na \u017d\u00c1DN\u00c1\u00a0Z\u00c1RUKA!!" + + " Rapla Toto je voln\u00fd software a je mo\u017en\u00e9 jej roz\u0161i\u0159ovat pouze za ur\u010dit\u00fdch " + + " podm\u00ednek.

" + + " "} + , { "licensedialog.title","GNU Licence"} + , { "list.format","{0}: {1}"} + , { "load","Nahr\u00e1t"} + , { "login","Login"} + , { "logindialog.title","P\u0159ihl\u00e1\u0161en\u00ed k rezerva\u010dn\u00edmu syst\u00e9mu"} + , { "logout","Odhl\u00e1sit"} + , { "main-view","Hlavn\u00ed pohled"} + , { "month","M\u011bs\u00edc"} + , { "months","M\u011bs\u00edce"} + , { "move","P\u0159esunout"} + , { "move_appointment.format","Chcete p\u0159esunout \u0159adu nebo pouze term\u00edn {0}?"} + , { "name","Jm\u00e9no"} + , { "never","Nikdy"} + , { "new","Nov\u00fd"} + , { "new_appointment","Nov\u00e1 rezervace"} + , { "new_category","Nov\u00e1 kategorie"} + , { "new_password","Nov\u00e9 heslo"} + , { "new_reservation","Nov\u00e1 ud\u00e1lost"} + , { "new_reservation.format","Nov\u00e1 ud\u00e1lost: {0}"} + , { "new_rule","Nov\u00e9 pravidlo pro "} + , { "new_sub-category","Nov\u00e1 podkategorie"} + , { "no","Ne"} + , { "no-view","Skryt\u00fd"} + , { "no_classification","Neklasifikov\u00e1no"} + , { "no_repeating","Bez opakov\u00e1n\u00ed"} + , { "not_selected.help","Polo\u017eka nen\u00ed v t\u00e9to chv\u00edli vybr\u00e1na.
Odstra\u0148te volbu \"Moje rezervace\", nebo zm\u011b\u0148te nastaven\u00ed va\u0161eho filtru."} + , { "not_visible","neviditen\u011b"} + , { "not_visible.help","Skryt\u00e1 polo\u017eka.
P\u0159\u00edstup k rezerva\u010dn\u00edm informac\u00edm je zak\u00e1z\u00e1n."} + , { "nothing_selected","Nen\u00ed vybr\u00e1na \u017e\u00e1dn\u00e1 polo\u017eka"} + , { "occupation","Obsazenost"} + , { "ok","Budi\u017e"} + , { "old_password","Star\u00e9 heslo"} + , { "only_own_reservations","Moje rezervace"} + , { "open","Otev\u0159\u00edt"} + , { "options","Volby"} + , { "or","nebo"} + , { "password","Heslo"} + , { "password_verification","Heslo ov\u011b\u0159eno"} + , { "paste","Vlo\u017eit"} + , { "paste_as","Vlo\u017eit v\u0161e"} + , { "period","Perioda"} + , { "period.format.end","Konec {0}"} + , { "period.format.start","Za\u010d\u00e1tek {0}"} + , { "period.format.week","{0}.t\u00fdden {1}"} + , { "period.not_set","Ozna\u010d periodu"} + , { "period_view","Perioda"} + , { "periods","Periody"} + , { "permission.access","P\u0159\u00edstupn\u00fd"} + , { "permission.admin","Administr\u00e1torsk\u00e9 pr\u00e1va"} + , { "permission.allocate","m\u016f\u017ee alokovat"} + , { "permission.allocate-conflicts","m\u016f\u017ee alokovat & vytv\u00e1\u0159et konflikty"} + , { "permission.denied","Zam\u00edtnuto"} + , { "permission.read","m\u016f\u017ee \u010d\u00edst"} + , { "permissions","P\u0159\u00edstupov\u00e1 pr\u00e1va"} + , { "person","Osoba"} + , { "person_type","Druh osoby"} + , { "person_types","Druh osob"} + , { "persons","Osoby"} + , { "preferences","P\u0159edvolby"} + , { "print","Tisknout"} + , { "print_to_file","Tisk do souboru"} + , { "publish","publikovat"} + , { "question","Ot\u00e1zka"} + , { "rapla.name","Rapla"} + , { "rapla.title","Rapla, chytr\u00e9 pl\u00e1nov\u00e1n\u00ed prost\u0159edk\u016f a ud\u00e1lost\u00ed"} + , { "rapla.welcome","Dobr\u00fd den, p\u0159ejeme p\u0159\u00edjemn\u00e9 pl\u00e1nov\u00e1n\u00ed! P\u0159ihl\u00e1\u0161en u\u017eivatel: {0}"} + , { "refresh","Obnovit"} + , { "reload_data","Znovu na\u010d\u00edst data"} + , { "remove","Odstranit"} + , { "repeat","Opakuj"} + , { "repeating","Opakov\u00e1n\u00ed"} + , { "repeating.end_date","Dokud"} + , { "repeating.forever","Nem\u00e1 konec"} + , { "repeating.interval.post","."} + , { "repeating.interval.pre","Poka\u017ed\u00e9"} + , { "repeating.n_times","x kr\u00e1t"} + , { "repeating.start_date","z"} + , { "report","Report"} + , { "reservation","Ud\u00e1lost"} + , { "reservation.allocations","Rozvr\u017een\u00ed:"} + , { "reservation.appointments","Sjednan\u00fd:"} + , { "reservation.create_without_wizard","Ud\u00e1lost"} + , { "reservation.name","N\u00e1zev ud\u00e1losti"} + , { "reservation.owner","Rezervace od"} + , { "reservation_type","Druh ud\u00e1losti"} + , { "reservations","Ud\u00e1losti"} + , { "reservations_from_all_users","Rezervace v\u0161ech u\u017eivatel\u016f"} + , { "reset","Reset"} + , { "resource","Prost\u0159edek"} + , { "resource.owner","Registrovan\u00fd od"} + , { "resource_type","Typ prost\u0159edku"} + , { "resource_types","Typy prost\u0159edk\u016f"} + , { "resources","Prost\u0159edky"} + , { "resources_persons","Prost\u0159edky a osoby"} + , { "restart_client","Restart klienta"} + , { "restart_options","..."} + , { "restart_server","Restart serveru"} + , { "root","root"} + , { "rows_per_hour","Po\u010det \u0159\u00e1dk\u016f na hodinu"} + , { "save","Ulo\u017eit"} + , { "search","Hledat"} + , { "select","Vybrat"} + , { "selectable","Ozna\u010diteln\u00fd"} + , { "selectable_on","Ozna\u010diteln\u00fd v"} + , { "selected","Vybr\u00e1no"} + , { "selected_on","Ozna\u010den\u00fd v"} + , { "selection","V\u00fdb\u011br"} + , { "selection_resource","Ozna\u010den\u00e9 prost\u0159edky"} + , { "serie","S\u00e9rie"} + , { "server","Server"} + , { "show_as","Uka\u017e v\u0161e"} + , { "show_conflicts","Zobrazit vyj\u00edmky"} + , { "show_tips","Zobrazit vyj\u00edmky v mouse-over"} + , { "single_appointment","Jeden term\u00edn"} + , { "source","Zdroj"} + , { "start_date","Po\u010d\u00e1te\u010dn\u00ed datum"} + , { "start_time","Po\u010d\u00e1te\u010dn\u00ed \u010das"} + , { "surname","Jm\u00e9no"} + , { "switch_back","P\u0159epni zp\u011bt"} + , { "switch_to","P\u0159epni k"} + , { "table","tabule"} + , { "time_at","v"} + , { "time_until","do"} + , { "today","Dnes"} + , { "total_occurances","celkem z\u00e1znam\u016f"} + , { "translation","p\u0159eklad"} + , { "translation.format","P\u0159eklady pro \"{0}\":"} + , { "type","Typ"} + , { "type.boolean","Ano|Ne"} + , { "type.date","Datum"} + , { "type.int","\u010c\u00edslo"} + , { "type.rapla:category","Kategorie"} + , { "type.string","Text"} + , { "types","Typy"} + , { "user","U\u017eivatel"} + , { "username","U\u017eivatelsk\u00e9 jm\u00e9no"} + , { "users","U\u017eivatel\u00e9"} + , { "view","Pohled"} + , { "warning","Varov\u00e1n\u00ed!"} + , { "warning.conflict","POZOR: Nalezen konflikt !"} + , { "warning.duplicated_appointments","POZOR: Stejn\u00e9 ujedn\u00e1n\u00ed existuje ve v\u00edce \u010dasech [{0}]!"} + , { "warning.experienced_users_only","Tato nastaven\u00ed by m\u011bla b\u00fdt m\u011bn\u011bna zku\u0161en\u00fdmi u\u017eivateli!"} + , { "warning.max-one-eventtype","Pouze 1 typ ud\u00e1losti je povoleno. Nastavte. filtr!"} + , { "warning.no_allocatables_selected","Nejsou ozna\u010deny \u017e\u00e1dn\u00e9 prost\u0159edky/osoby!"} + , { "warning.no_conflict_permission","Nem\u00e1te pov\u011b\u0159en\u00ed vytvo\u0159it konflikt pro [{0}] !"} + , { "warning.no_reserve_permission","Nem\u00e1te pov\u011b\u0159en\u00ed rezervovat/m\u011bnit [{0}] v ujedn\u00e1n\u00ed vaci [{1}]!"} + , { "warning.period_shorter_than_week","Nem\u00e1te pov\u011b\u0159en\u00ed rezervovat/m\u011bnit [{0}] v ujedn\u00e1n\u00ed vaci [{1}]! "} + , { "warning.readonly_storage","Data jsou zm\u011bn\u011bny pouze v pam\u011bti. Zm\u011bny budou ztraceny pro dal\u0161\u00ed pou\u017eit\u00ed."} + , { "warning.reservation.delete","Editovan\u00e1 polo\u017eka bude smaz\u00e1na."} + , { "warning.reservation.update","Editovan\u00e1 polo\u017eka bude zm\u011bn\u011bna. Zobrazen\u00e9 informace budou obnoveny."} + , { "warning.update","Objekt \"{0}\" kter\u00fd je editov\u00e1n bude zm\u011bn\u011bn mimo okno. Sou\u010dasn\u00e1 editace bude p\u0159eru\u0161ena."} + , { "week","T\u00fdden"} + , { "week_compact","Cel\u00fd t\u00fdden"} + , { "weekday","Den v t\u00fddnu"} + , { "weekly","T\u00fddn\u011b"} + , { "weeks","T\u00fddny"} + , { "weekview.print.choose_export","Vyberte zp\u016fsob ukl\u00e1d\u00e1n\u00ed!"} + , { "weekview.print.dialog_title","N\u00e1hled p\u0159ed tiskem"} + , { "weekview.print.format_button","Vlastnosti tisk\u00e1rny"} + , { "weekview.print.postscript","Postscript"} + , { "weekview.print.title_textfield","Titul"} + , { "width","\u0161\u00ed\u0159ka"} + , { "with","s"} + , { "x_days_advance","x dn\u00ed p\u0159edem"} + , { "year",".."} + , { "years",".."} + , { "yes","ano"} + , { "zero_appointment","Nesjedn\u00e1no"} + }; +} diff --git a/Rapla/generated-src/org/rapla/RaplaResources_de.java b/Rapla/generated-src/org/rapla/RaplaResources_de.java new file mode 100644 index 0000000..98d2862 --- /dev/null +++ b/Rapla/generated-src/org/rapla/RaplaResources_de.java @@ -0,0 +1,435 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class RaplaResources_de extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "abort","Abbrechen"} + , { "add","Hinzuf\u00fcgen"} + , { "additional-view","zus\u00e4tzliche Informationen"} + , { "admin","Administrator"} + , { "admin.login","Sie haben Administrationsrechte!"} + , { "administration","Administration"} + , { "all-day","ganzt\u00e4gig"} + , { "all_filtered","Alle gefilterten Objekte."} + , { "all_users","alle Benutzer"} + , { "allocatable_in_timeframe","Belegbar in folgendem Zeitraum"} + , { "allocation_view","Belegungen von {0}"} + , { "alteration","\u00c4nderung"} + , { "and","und"} + , { "apply","\u00dcbernehmen"} + , { "appointment","Termin"} + , { "appointment.convert","In Einzeltermine umwandeln"} + , { "appointment.day_x","am Tag x"} + , { "appointment.days","Tag(e)"} + , { "appointment.exception.days","Ausgenommene Tage:"} + , { "appointment.exception.general","Ausnahmen:"} + , { "appointment.exceptions","Ausnahmen"} + , { "appointment.format.exceptions","{0} Exceptions:"} + , { "appointment.hours","Std."} + , { "appointment.minutes","Min."} + , { "appointment.next_day","am n\u00e4chsten Tag"} + , { "appointment.no_exceptions","Keine Ausnahmen:"} + , { "appointment.rule","Regel:"} + , { "appointment.same_day","am selben Tag"} + , { "appointment_list","Terminliste"} + , { "appointments","Termine"} + , { "attribute","Attribut"} + , { "attributes","Attribute"} + , { "available","vorhanden"} + , { "back","Zur\u00fcck"} + , { "bind_with_person","Mit Person verbinden"} + , { "calendar","Kalender"} + , { "calendar_settings","Kalendareinstellungen"} + , { "calendarname","Kalender Namen: "} + , { "calendarweek.abbreviation","KW {0,date,w}"} + , { "cancel","Abbrechen"} + , { "categories","Kategorien"} + , { "category","Kategorie"} + , { "change","\u00c4ndern"} + , { "change.format","{0} \u00e4ndern"} + , { "change_appointment","Termin \u00e4ndern"} + , { "changes","\u00c4nderungen"} + , { "choose_language","W\u00e4hlen Sie Ihre Sprache:"} + , { "choose_root_category","Wurzelkategorie ausw\u00e4hlen"} + , { "class","Klasse"} + , { "classification","Klassifizierung"} + , { "close","Schlie\u00dfen"} + , { "color","Farbe"} + , { "complete_time","gesamte Zeit"} + , { "confirm","Best\u00e4tigen"} + , { "confirm-close.ok","\u00c4nderungen verwerfen"} + , { "confirm-close.question","Sie haben die vorgenommenen \u00c4nderungen noch" + + " nicht gespeichert."} + , { "confirm-close.title","\u00c4nderungen noch nicht gespeichert!"} + , { "confirm.dialog.question","Diese Informationen speichern?"} + , { "confirm.dialog.title","{0} best\u00e4tigen"} + , { "conflict.appointment1","Wann?"} + , { "conflict.appointment2","Konflikt-Termin"} + , { "conflict.reservation1","Welche Veranstaltung?"} + , { "conflict.reservation2","Andere Veranstaltung?"} + , { "conflict.resource","Was/Wer?"} + , { "conflict.user","Mit Wem?"} + , { "conflictUC","Konflikte ({0,number,integer})"} + , { "conflicts","Konflikte"} + , { "connection","Verbindung mit dem Server"} + , { "constraints","Einschr\u00e4nkungen"} + , { "continue","Weiter"} + , { "copy","Kopieren"} + , { "copy_appointment.format","Wollen Sie die ganze Serie oder nur den Termin am {0} kopieren?"} + , { "copy_to_clipboard","In die Zwischenablage"} + , { "create","Erstellen"} + , { "created_at","erstellt am"} + , { "customized","benutzerdefiniert"} + , { "daily","t\u00e4glich"} + , { "database","Datenbasis"} + , { "date","Datum"} + , { "day","Tag"} + , { "days","Tage"} + , { "default","Standard"} + , { "defaultselection","Standardauswahl"} + , { "delete","L\u00f6schen"} + , { "delete.abort","NICHT ausf\u00fchren!!"} + , { "delete.format","{0} l\u00f6schen"} + , { "delete.ok","L\u00f6schen!!"} + , { "delete.question","Sie l\u00f6schen die folgenden Objekte: "} + , { "delete.title","L\u00f6schen?"} + , { "delete_appointment.format","Wollen Sie die ganze Serie oder nur den Termin am {0} l\u00f6schen?"} + , { "delete_selection","Auswahl l\u00f6schen"} + , { "destination","Ziel"} + , { "display_exceptions","Zeige Ausnahmen im Kalender"} + , { "duration","Zeitdauer"} + , { "dynamictype","Typ"} + , { "dynamictype.annotation.nameformat","Anzeigeformatierung"} + , { "dynamictype.annotation.nameformat.description","Umklammern sie die Schl\u00fcsselnamen mit {} um Attribute einzuf\u00fcgen, Bsp. {name}"} + , { "dynamictype.name","Name des Typs"} + , { "edit","Bearbeiten"} + , { "edit-view","Bearbeitungsansicht"} + , { "edit.format","{0} bearbeiten"} + , { "edit_advanced","zeige Profi-Einstellungen"} + , { "edit_reservation.format","Veranstaltung bearbeiten: {0}"} + , { "edit_reservations","eigene bearbeiten"} + , { "elementkey","Elementschl\u00fcssel"} + , { "email","Email"} + , { "end_date","Ende"} + , { "end_time","Endzeit"} + , { "error","Fehler"} + , { "error.cant_delete_your_account","Sie k\u00f6nnen [{0}] nicht l\u00f6schen, weil Sie diesen Account benutzen!"} + , { "error.connect","Verbindung mit [{0}] fehlgeschlagen! Vielleicht ist der Rechner nicht erreichbar. Versuchen Sie es zu einen sp\u00e4teren Zeitpunkt erneut!"} + , { "error.connection_closed","Keine oder unterbrochene Verbindung zu [{0}]. Bitte starten Sie Rapla erneut."} + , { "error.dependencies","F\u00fcr folgende Objekte bestehen noch Abh\u00e4ngigkeiten"} + , { "error.invalid_key","\"{0}\" ist kein g\u00fcltiger Schl\u00fcssel!" + + " Note: Sie k\u00f6nnen nur eine Kombination von max. 50 Buchstaben, Ziffern oder einer der" + + " Folgende Zeichen {1}. Der Schl\u00fcssel muss mit {2} oder einem Buchstaben beginnen! Beispiel: Anzahl_Pl\u00e4tze" + + " "} + , { "error.login","Fehlerhafter Login!"} + , { "error.new_version","Das Objekt {0} konnte nicht ver\u00e4ndert werden. Es wurde k\u00fcrzlich ver\u00e4ndert, denn" + + " es gibt schon eine neuere Version. "} + , { "error.no_appointment","Eine Veranstaltung muss aus mindestens einem Termin bestehen!"} + , { "error.no_entry_for","Sie m\u00fcssen einen Namen angeben f\u00fcr: {0}"} + , { "error.no_key","Sie m\u00fcssen einen Schl\u00fcssel angeben: {0}"} + , { "error.no_name","Sie m\u00fcssen einen Namen angeben!"} + , { "error.no_reservation_name","Sie m\u00fcssen einen Titel f\u00fcr Ihre Veranstaltung angeben!"} + , { "error.no_rollback","Achtung! W\u00e4hrend der Speicherung in der" + + " Datenbank ist ein Fehler aufgetreten. Da die verwendete" + + " Datenbank keine Transaktionen unterst\u00fctzt kann dies zu" + + " schwerwiegenden Fehlern in der Applikation fhren. Bitte setzten" + + " Sie sich umgehend mit dem zust\u00e4ndigen Adminstrator in Verbindung!" + + " "} + , { "error.not_unique","Der Name \"{0}\" ist schon" + + " vergeben. Bitte w\u00e4hlen Sie einen Anderen!"} + , { "error.one_type_requiered","Es muss mindestens ein Typ vorhanden sein!"} + , { "error.passwords_dont_match","Die Passw\u00f6rter stimmen nicht \u00fcberein!"} + , { "error.reference_not_stored","Achtung! Referenziertes Object [{0}] konnte nicht gefunden werden. Es wurde wahrscheinlich k\u00fcrzlich von jemand gel\u00f6scht."} + , { "error.rollback","W\u00e4hrend der Speicherung in der Datenbank" + + " ist ein Fehler aufgetreten. Alle \u00c4nderungen wurden r\u00fcckg\u00e4ngig" + + " gemacht!"} + , { "error.wrong_password","Falsches Passwort!"} + , { "every_appointment","allen Terminen"} + , { "everytime","immer"} + , { "exception","Ausnahme-Fehler"} + , { "exchange_allocatables","Belegung \u00e4ndern"} + , { "exclamation.format","{0} !"} + , { "exclude_days","Tage ausschlie\u00dfen"} + , { "exit","Beenden"} + , { "exit.abort","Weiter planen >>"} + , { "exit.ok","Rapla beenden?"} + , { "exit.question","Wollen Sie Rapla beenden?"} + , { "exit.title","Rapla beenden?"} + , { "expected_columns","Spaltenbreite"} + , { "expected_rows","Zeilenanzahl"} + , { "export","Export"} + , { "file","Datei"} + , { "filter","Filter"} + , { "filter.contains","enth\u00e4lt"} + , { "filter.earlier_than","ist fr\u00fcher als"} + , { "filter.equals","ist gleich"} + , { "filter.greater_or_equals","ist gr\u00f6\u00dfer gleich"} + , { "filter.is_greater_than","ist gr\u00f6\u00dfer als"} + , { "filter.is_smaller_than","ist kleiner als"} + , { "filter.later_than","ist sp\u00e4ter als"} + , { "filter.not_equals","ist ungleich"} + , { "filter.smaller_or_equals","ist kleiner gleich"} + , { "filter_allocatable","Ressourcen und Personen"} + , { "filter_reservation.all","Von allen Benutzern"} + , { "filter_reservation.own","Eigene Veranstaltungen"} + , { "filter_restrictions","Anzeige von {0} einschr\u00e4nken"} + , { "filtername","Filtername"} + , { "fixed_date","festes Datum"} + , { "for","f\u00fcr"} + , { "forename","Vorname"} + , { "format.repeat_from","ab dem {0}"} + , { "format.repeat_n_times","wiederhole {0} Mal"} + , { "format.repeat_until","bis zum {0}"} + , { "goto_date","Datum anzeigen"} + , { "group","Gruppe"} + , { "groups","Gruppen"} + , { "help","Hilfe"} + , { "hierarchy","Hierarchie"} + , { "holdbackconflicts","Unterdr\u00fccke Konflikte"} + , { "hour","Std."} + , { "hours","Std."} + , { "import","Import"} + , { "in_period.format","im Zeitraum {0}"} + , { "including_date","mit Datum: {0}"} + , { "info","Info"} + , { "info.text","" + + " Rapla Version 1.4.3-beta" + + "
" + + " (C) Copyright 2000-2011 Rapla Team" + + "

Rapla-Webseite: http://rapla.org" + + "

" + + "

Schicken Sie Ihre Anfordungen, Fragen oder Fehlerreports" + + " an die Entwickler- Mailingliste:
" + + " rapla-developers@lists.sourceforge.net" + + "

" + + "

Version vom: 2011-10-05 13:41

" + + "

Signatur: {0}

" + + "

Java Version {1}

" + + " "} + , { "info.title","" + + " Rapla Version 1.4.3-beta" + + " "} + , { "insert","Einf\u00fcgen"} + , { "interval","Zeitraum"} + , { "interval.format","Alle {0} {1}"} + , { "keep","Beibehalten"} + , { "key","Schl\u00fcssel"} + , { "language","Sprache"} + , { "last_changed","zuletzt ge\u00e4ndert am"} + , { "last_changed_by","zuletzt ge\u00e4ndert von"} + , { "legend","Legende"} + , { "level","Level"} + , { "license.text","" + + " Rapla Version 1.4.3-beta (C) Copyright 2000-2011 Rapla Team" + + "

F\u00fcr Rapla besteht KEINE\u00a0GARANTIE!!" + + " Rapla ist freie Software, die Sie unter bestimmten" + + " Bedingungen weitergeben d\u00fcrfen.

" + + " "} + , { "licensedialog.title","Die GNU-Lizenz"} + , { "list.format","{0}: {1}"} + , { "load","Laden"} + , { "login","Login"} + , { "logindialog.title","Rapla Login"} + , { "logout","Logout"} + , { "main-view","Hauptansicht"} + , { "minute","Minute"} + , { "minutes","Minute"} + , { "month","Monat"} + , { "monthly","monatlich"} + , { "months","Monate"} + , { "move","Verschieben"} + , { "move_appointment.format","Wollen Sie die ganze Serie oder nur den Termin am {0} verschieben?"} + , { "name","Name"} + , { "never","nie"} + , { "new","Neu"} + , { "new_appointment","neuer Termin"} + , { "new_category","Neue Kategorie"} + , { "new_password","Neues Passwort"} + , { "new_reservation","neue Veranstaltung"} + , { "new_reservation.format","Neue Veranstaltung: {0}"} + , { "new_rule","neue Regel f\u00fcr"} + , { "new_sub-category","Neue Unter-Kategorie"} + , { "no","Nein"} + , { "no-view","nicht sichtbar"} + , { "no_classification","keine Klassifizierung"} + , { "no_repeating","Einzeltermin"} + , { "not_selected.help","Nicht ausgew\u00e4hlt.
Schalten Sie \"nur eigene Reservierungen\" aus oder \u00e4ndern Sie die Filtereinstellungen. "} + , { "not_visible","nicht sichtbar"} + , { "not_visible.help","Nicht sichtbar.
Der Zugriff auf die Reservierungsinformation ist eingeschr\u00e4nkt."} + , { "nothing_selected","Nichts ausgew\u00e4hlt"} + , { "occupation","Besetzung"} + , { "ok","OK"} + , { "old_password","Altes Passwort"} + , { "only_own_reservations","Nur eigene Reservierungen"} + , { "open","offen"} + , { "options","Einstellungen"} + , { "or","oder"} + , { "password","Passwort"} + , { "password_verification","Passwort Verifikation"} + , { "paste","Einf\u00fcgen"} + , { "paste_as","Einf\u00fcgen als"} + , { "period","Zeitraum"} + , { "period.format.end","Ende {0}"} + , { "period.format.start","Start {0}"} + , { "period.format.week","{0}.Wo {1}"} + , { "period.not_set","Zeitraum ausw\u00e4hlen"} + , { "period_view","Zeitraum"} + , { "periods","Zeitr\u00e4ume"} + , { "permission.access","Zugriff"} + , { "permission.admin","Administrationsrechte"} + , { "permission.allocate","darf belegen"} + , { "permission.allocate-conflicts","darf belegen & Konflikte erzeugen"} + , { "permission.denied","gesperrt"} + , { "permission.read","darf lesen"} + , { "permissions","Zugriffsrechte"} + , { "person","Person"} + , { "person_type","Personentyp"} + , { "person_types","Personentypen"} + , { "persons","Personen"} + , { "preferences","Einstellungen"} + , { "print","Drucken"} + , { "print_to_file","In Datei drucken"} + , { "publish","Ver\u00f6ffentlichen"} + , { "question","Frage"} + , { "rapla.name","Rapla"} + , { "rapla.title","Rapla, der clevere Ressourcen- und Veranstaltungsplaner"} + , { "rapla.welcome","Hallo {0}, viel Spa\u00df beim Planen!"} + , { "refresh","Aktualisieren"} + , { "reload_data","Daten neu laden"} + , { "remove","Entfernen"} + , { "repeat","wiederhole"} + , { "repeating","Wiederholung"} + , { "repeating.end_date","bis zum"} + , { "repeating.forever","Kein Ende"} + , { "repeating.interval.post","."} + , { "repeating.interval.pre","Jeden"} + , { "repeating.n_times","x Mal"} + , { "repeating.start_date","ab dem"} + , { "report","Report"} + , { "reservation","Veranstaltung"} + , { "reservation.allocations","Belegungen:"} + , { "reservation.appointments","Termine:"} + , { "reservation.create_without_wizard","Veranstaltung"} + , { "reservation.name","Veranstaltungsname"} + , { "reservation.owner","reserviert von"} + , { "reservation_type","Veranstaltungstyp"} + , { "reservations","Veranstaltungen"} + , { "reservations_from_all_users","Veranstaltungen von allen Benutzern"} + , { "reset","Reset"} + , { "resource","Ressource"} + , { "resource.owner","Eingetragen von"} + , { "resource_type","Ressourcentyp"} + , { "resource_types","Ressourcentypen"} + , { "resources","Ressourcen"} + , { "resources_persons","Ressourcen und Personen"} + , { "restart_client","Rapla neu starten"} + , { "restart_options","..."} + , { "restart_server","Server neu starten"} + , { "root","Wurzel"} + , { "rows_per_hour","Zeilen pro Stunden"} + , { "save","Speichern"} + , { "search","Suche"} + , { "second","Sekunde"} + , { "seconds","Sekunden"} + , { "select","ausw\u00e4hlen"} + , { "selectable","ausw\u00e4hlbar"} + , { "selectable_on","ausw\u00e4hlbar an"} + , { "selected","ausgew\u00e4hlt"} + , { "selected_on","ausgew\u00e4hlt an"} + , { "selection","Auswahl"} + , { "selection_resource","Ressourcenauswahl"} + , { "serie","Serie"} + , { "server","Server"} + , { "server_status","" + + " Status des Servers" + + " "} + , { "show_as","Anzeigen als"} + , { "show_conflicts","Zeige Konflikte"} + , { "show_tips","Zeige mouse-over"} + , { "single_appointment","einzelner Termin"} + , { "source","Quelle"} + , { "start_date","Beginn"} + , { "start_rapla_with_applet","" + + " Rapla starten (mit Java Plugin)" + + " "} + , { "start_rapla_with_webstart","" + + " Rapla starten (mit Java Webstart)" + + " "} + , { "start_time","Startzeit"} + , { "surname","Nachname"} + , { "switch_back","wechsle zur\u00fcck"} + , { "switch_to","wechsle zu"} + , { "table","Tabelle"} + , { "time_at","um"} + , { "time_until","bis"} + , { "today","Heute"} + , { "total_occurances","Gesamtanzahl Termine"} + , { "translation","\u00dcbersetzung"} + , { "translation.format","\u00dcbersetzungen von \"{0}\":"} + , { "type","Typ"} + , { "type.boolean","Ja|Nein"} + , { "type.date","Datum"} + , { "type.int","Ganze Zahl"} + , { "type.rapla:category","Kategorie"} + , { "type.string","Text"} + , { "types","Typen"} + , { "user","Benutzer"} + , { "username","Benutzername"} + , { "users","Benutzer"} + , { "view","Anzeige"} + , { "warning","Warnung"} + , { "warning.conflict","WARNUNG: Konflikte gefunden!"} + , { "warning.duplicated_appointments","WARNUNG: Den gleichen Termin gibt es mehrmals [{0}]!"} + , { "warning.experienced_users_only","Diese Einstellungen sollten nur von erfahrenen Benutzern ver\u00e4ndert werden!"} + , { "warning.max-one-eventtype","Nur 1 Veranstaltungstyp ist erlaubt. Passen Sie den Filter!"} + , { "warning.no_allocatables_selected","Sie haben keine Ressourcen/Personen ausgew\u00e4hlt!"} + , { "warning.no_conflict_permission","Sie haben keine Zugriffsrechte um Konflikte f\u00fcr [{0}] zu erzeugen!"} + , { "warning.no_reserve_permission","Sie haben keine Zugriffsrechte um [{0}] an dem Termin [{1}] zu reservieren/\u00e4ndern!"} + , { "warning.period_shorter_than_week","Der Zeitraum is k\u00fcrzer als eine Woche!"} + , { "warning.readonly_storage","Ihre \u00c4nderungen wurden nicht gesichert und gehen beim Beenden der Sitzung verloren!"} + , { "warning.reservation.delete","Die Veranstaltung die gerade bearbeitet" + + " wird, wurde gel\u00f6scht."} + , { "warning.reservation.update","Die Veranstaltung, die gerade bearbeitet" + + " wird, wurde ge\u00e4ndert. Das Bearbeitungsfenster wird aktualisiert."} + , { "warning.update","Das Objekt \"{0}\", das gerade bearbeitet" + + " wird, wurde ge\u00e4ndert. Der aktuelle Editiervorgang wird abgebrochen." + + " "} + , { "webinfo.text","" + + "

Mehr Informationen:

" + + "

" + + " Java Webstart ist in allen Java Version seit 1.4.0 enthalten." + + " Um Rapla zu starten brauchen Sie Java 1.4 oder eine neuere Version. Diese k\u00f6nnen Sie herunterladen von" + + " java.sun.com." + + "

" + + "

" + + " Besuchen Sie auch rapla.sourceforge.net f\u00fcr mehr Informationen \u00fcber Rapla." + + "

" + + " "} + , { "week","Woche"} + , { "week_compact","kompakte Woche"} + , { "weekday","Wochentag"} + , { "weekly","w\u00f6chentlich"} + , { "weeks","Wochen"} + , { "weekview.print.choose_export","W\u00e4hlen Sie die Speichermethode!"} + , { "weekview.print.dialog_title","Druck Voransicht"} + , { "weekview.print.format_button","Drucker-Einstellungen"} + , { "weekview.print.postscript","Postscript"} + , { "weekview.print.title_textfield","\u00dcberschrift"} + , { "width","Breite"} + , { "with","mit"} + , { "x_days_advance","x Tage im Voraus"} + , { "year","Jaar"} + , { "yearly","j\u00e4hrlich"} + , { "years","Jare"} + , { "yes","Ja"} + , { "zero_appointment","keinem Termin"} + }; +} diff --git a/Rapla/generated-src/org/rapla/RaplaResources_es.java b/Rapla/generated-src/org/rapla/RaplaResources_es.java new file mode 100644 index 0000000..da53db0 --- /dev/null +++ b/Rapla/generated-src/org/rapla/RaplaResources_es.java @@ -0,0 +1,421 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class RaplaResources_es extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "abort","Anular"} + , { "add","A\u00f1adir"} + , { "additional-view","Informaci\u00f3n complementaria"} + , { "admin","Administrador"} + , { "admin.login","\u00a1Dispone de derechos de administrador!"} + , { "administration","Administraci\u00f3n"} + , { "all-day","24h"} + , { "all_filtered","Todos los objetos filtrados."} + , { "all_users","Todos los usuarios"} + , { "allocatable_in_timeframe","Puede ser asignado en el periodo"} + , { "allocation_view","Asignaci\u00f3n de {0}"} + , { "alteration","Modificaci\u00f3n"} + , { "and","y"} + , { "apply","Aplicar"} + , { "appointment","Reserva"} + , { "appointment.convert","Convertir a los eventos individuales"} + , { "appointment.day_x","el d\u00eda x"} + , { "appointment.days","D\u00eda(s)"} + , { "appointment.exception.days","D\u00edas de excepciones"} + , { "appointment.exception.general","Excepciones :"} + , { "appointment.exceptions","Excepciones"} + , { "appointment.format.exceptions","{0} Excepciones :"} + , { "appointment.hours","Hora(s)"} + , { "appointment.minutes","min."} + , { "appointment.next_day","d\u00eda siguiente"} + , { "appointment.no_exceptions","Sin excepciones"} + , { "appointment.rule","Regla :"} + , { "appointment.same_day","mismo d\u00eda"} + , { "appointment_list","Lista de citas"} + , { "appointments","Citas"} + , { "attribute","Atributo"} + , { "attributes","Atributos"} + , { "available","Disponible"} + , { "back","Volver"} + , { "bind_with_person","connectar con persona"} + , { "calendar","Calendario"} + , { "calendar_settings","la configuraci\u00f3n del calendario"} + , { "calendarname","Calendario nombre: "} + , { "calendarweek.abbreviation","se {0,date,w}"} + , { "cancel","Cancelar"} + , { "categories","Categor\u00edas"} + , { "category","Categor\u00eda"} + , { "change","Modificar"} + , { "change.format","Modificar {0}"} + , { "change_appointment","Modificar la reserva"} + , { "changes","Modificaciones"} + , { "choose_language","Escoja su idioma :"} + , { "choose_root_category","Seleccionar la categor\u00eda ra\u00edz"} + , { "class","Clase"} + , { "classification","Clasificaci\u00f3n"} + , { "close","Cerrar"} + , { "color","Color"} + , { "complete_time","Periodo total"} + , { "confirm","Confirmar"} + , { "confirm-close.ok","Anular las modificaciones"} + , { "confirm-close.question","\u00a1No ha guardado las modificaciones!"} + , { "confirm-close.title","\u00a1Modificaciones no guardadas!"} + , { "confirm.dialog.question","\u00bfGuardar esta informaci\u00f3n?"} + , { "confirm.dialog.title","Confirmar {0}"} + , { "conflict.appointment1","\u00bfCu\u00e1ndo?"} + , { "conflict.appointment2","Conflicto de asignaci\u00f3n"} + , { "conflict.reservation1","\u00bfQu\u00e9 evento?"} + , { "conflict.reservation2","\u00bfOtro evento?"} + , { "conflict.resource","\u00bfQu\u00e9/Qui\u00e9n?"} + , { "conflict.user","\u00bfCon qui\u00e9n?"} + , { "conflictUC","Conflictos ({0,number,integer})"} + , { "conflicts","Conflictos"} + , { "connection","conexi\u00f3n a servidor"} + , { "constraints","Restricciones"} + , { "continue","Continuar"} + , { "copy","Copiar"} + , { "copy_appointment.format","\u00bfDesea copiar toda la serie o s\u00f3lo la reserva del {0}?"} + , { "copy_to_clipboard","Copiar al portapapeles"} + , { "create","Crear"} + , { "created_at","Creado el"} + , { "customized","Espec\u00edfico"} + , { "daily","A diario"} + , { "database","Base de datos"} + , { "date","Fecha"} + , { "day","D\u00eda"} + , { "days","d\u00edas"} + , { "default","Est\u00e1ndar"} + , { "defaultselection","Selecci\u00f3n por defecto"} + , { "delete","Borrar"} + , { "delete.abort","\u00a1No, no lo hagas!"} + , { "delete.format","Eliminar {0}"} + , { "delete.ok","S\u00ed, borrar"} + , { "delete.title","\u00bfBorrar?"} + , { "delete_appointment.format","\u00bfDesea eliminar toda la serie o s\u00f3lo la reserva del {0}?"} + , { "delete_selection","Borrar la selecci\u00f3n"} + , { "destination","Destino"} + , { "display_exceptions","Mostrar las excepciones en el calendario"} + , { "duration","Duraci\u00f3n"} + , { "dynamictype","Tipo"} + , { "dynamictype.annotation.nameformat","Formato de nombre visualizado"} + , { "dynamictype.annotation.nameformat.description","Meta la clave entre {} para insertar los atributos, por ejemplo {miClave}"} + , { "dynamictype.name","Nombre de tipo"} + , { "edit","Editar"} + , { "edit-view","Vista de modificaci\u00f3n"} + , { "edit.format","Modificar {0}"} + , { "edit_advanced","Mostrar par\u00e1metros para usuarios experimentados"} + , { "edit_reservation.format","Modificar el evento : {0}"} + , { "edit_reservations","modificar las propias"} + , { "elementkey","Clave de elemento"} + , { "email","Email"} + , { "end_date","Fin"} + , { "end_time","Hora final"} + , { "error","Error"} + , { "error.cant_delete_your_account","\u00a1No puede borrar [{0}], porque est\u00e1 usando esa cuenta en estos momentos!"} + , { "error.connect","\u00a1La conexi\u00f3n a [{0}] ha fallado! El servidor puede no estar disponible. Int\u00e9ntelo m\u00e1s tarde."} + , { "error.connection_closed","Sin conexi\u00f3n a [{0}]. Por favor, reinicie Rapla."} + , { "error.dependencies","Existen dependencias para estos objetos"} + , { "error.invalid_key","\u00a1\"{0}\" no es una clave v\u00e1lida!" + + " Nota : S\u00f3lo se puede utilizar una combinaci\u00f3n de max. 50 letras, d\u00edgitos o uno de los" + + " siguientes caracteres {1}. La clave debe comenzar con {2} o una carta! Ejemplo: N\u00famero_de_plazas" + + " "} + , { "error.login","\u00a1Error al acceder al sistema!"} + , { "error.new_version","El objeto {0} no ha podido ser modificado. Ha sido modificado recientemente por otra persona." + + " Existe en memoria una versi\u00f3n m\u00e1s reciente del mismo. "} + , { "error.no_appointment","\u00a1Un evento ha de tener al menos una cita!"} + , { "error.no_entry_for","Debe dar un nombre para : {0}"} + , { "error.no_key","Debe dar una clave para : {0}"} + , { "error.no_name","\u00a1Debe dar un nombre!"} + , { "error.no_reservation_name","\u00a1Debe introducir un t\u00edtulo para su evento!"} + , { "error.no_rollback","\u00a1Atenci\u00f3n! La actualizaci\u00f3n de la base de datos ha fallado. Puesto" + + " que su base de datos no soporta transacciones, esto puedo ocasionar errores serios. Por favor, contacte" + + " con su administrador inmediatamente!"} + , { "error.not_unique","\u00a1El nombre \"{0}\" ya est\u00e1 en uso. Por favor, escoja un nombre diferente!"} + , { "error.one_type_requiered","\u00a1Al menos un tipo debe estar disponible!"} + , { "error.passwords_dont_match","\u00a1Las palabras de paso no coinciden!"} + , { "error.reference_not_stored","\u00a1Atenci\u00f3n! No se pudo encontrar el objeto referenciado [{0}]. Probablemente se elimin\u00f3 recientemente."} + , { "error.rollback","La actualizaci\u00f3n de la base de datos ha fallado." + + " Se han cancelado todas las modificaciones"} + , { "error.wrong_password","\u00a1Palabra de paso incorrecta!"} + , { "every_appointment","Todas las citas"} + , { "everytime","Siempre"} + , { "exception","Excepci\u00f3n"} + , { "exchange_allocatables","Modificar la asignaci\u00f3n"} + , { "exclamation.format","{0} !"} + , { "exit","Salir"} + , { "exit.abort","Continuar Rapla >>"} + , { "exit.ok","\u00bfAbandonar Rapla?"} + , { "exit.question","\u00bfDesea realmente abandonar Rapla?"} + , { "exit.title","\u00bfAbandonar Rapla?"} + , { "expected_columns","N\u00famero de columnas"} + , { "expected_rows","N\u00famero de l\u00edneas"} + , { "export","Exportar"} + , { "file","Fichero"} + , { "filter","Filtro"} + , { "filter.contains","contiene"} + , { "filter.earlier_than","es anterior a las"} + , { "filter.equals","es igual a"} + , { "filter.greater_or_equals","es mayor o igual que"} + , { "filter.is_greater_than","es mayor que"} + , { "filter.is_smaller_than","es menor que"} + , { "filter.later_than","es posterior a las"} + , { "filter.not_equals","es desigual"} + , { "filter.smaller_or_equals","es menor o igual que"} + , { "filter_allocatable","Recursos y personas"} + , { "filter_reservation.all","De todos los usuarios"} + , { "filter_reservation.own","Sus propios eventos"} + , { "filter_restrictions","Limitar la vista de {0}"} + , { "filtername","Nombre del filtro"} + , { "fixed_date","Fecha fija"} + , { "for","para"} + , { "forename","Nombre"} + , { "format.repeat_from","a partir de {0}"} + , { "format.repeat_n_times","repite {0} veces"} + , { "format.repeat_until","hasta {0}"} + , { "goto_date","Ir a la fecha"} + , { "group","Grupo"} + , { "groups","Grupos"} + , { "help","Ayuda"} + , { "hierarchy","Jerarqu\u00eda"} + , { "holdbackconflicts","No tener en cuenta los conflictos"} + , { "hour","Hora"} + , { "hours","Horas"} + , { "import","Importar"} + , { "in_period.format","dentro del periodo {0}"} + , { "including_date","incluyendo la fecha: {0} "} + , { "info","Info"} + , { "info.text","" + + " Rapla Version 1.4.3-beta" + + "
" + + " (C) Copyright 2000-2011 Rapla Team" + + "

P\u00e1gina de Rapla: http://rapla.org" + + "

" + + "

Env\u00eda tus propuestas de cambios, preguntas, o errores encontrados" + + " a la lista de correo de nuestros desarrolladores:
" + + " rapla-developers@lists.sourceforge.net" + + "

" + + "

Version : 2011-10-05 13:41

" + + "

Firma: {0}

" + + "

Version Java {1}

" + + " "} + , { "info.title","" + + " Rapla Version 1.4.3-beta" + + " "} + , { "insert","Insertar"} + , { "interval","Intervalo"} + , { "interval.format","Cada {0} {1}"} + , { "keep","Conservar"} + , { "key","Clave"} + , { "language","Idioma"} + , { "last_changed","\u00daltima modificaci\u00f3n el"} + , { "last_changed_by","\u00daltima modificaci\u00f3n"} + , { "legend","Leyenda"} + , { "level","Nivel"} + , { "license.text","" + + " Rapla Version 1.4.3-beta (C) Copyright 2000-2011 Rapla Team" + + "

\u00a1\u00a1Rapla viene SIN\u00a0GARANT\u00cdA!!" + + " Rapla es software libre, as\u00ed que puede usarlo y distribuirlo bajo las siguientes condiciones.

" + + " "} + , { "licensedialog.title","La licencia GNU"} + , { "list.format","{0} : {1}"} + , { "load","Cargar"} + , { "login","Acceso"} + , { "logindialog.title","Acceso a Rapla"} + , { "logout","Salir"} + , { "main-view","Vista principal"} + , { "minute","Hora"} + , { "minutes","Horas"} + , { "month","Mes"} + , { "monthly","mensual"} + , { "months","Meses"} + , { "move","Mover"} + , { "move_appointment.format","\u00bfDesea desplazar toda la seria o s\u00f3lo la reserva del {0}?"} + , { "name","Nombre"} + , { "never","Nunca"} + , { "new","Nuevo"} + , { "new_appointment","Nueva cita"} + , { "new_category","Nueva categor\u00eda"} + , { "new_password","Nueva palabra de paso"} + , { "new_reservation","nuevo evento"} + , { "new_reservation.format","Nuevo evento : {0}"} + , { "new_rule","Nueva regla para"} + , { "new_sub-category","Nueva sub-categor\u00eda"} + , { "no","No"} + , { "no-view","Invisible"} + , { "no_classification","Sin clasificaci\u00f3n"} + , { "no_repeating","\u00danica"} + , { "not_selected.help","Actualmente no seleccionado.
Desmarca \"S\u00f3lo mis reservas\" o cambia las configuraciones de los filtros."} + , { "not_visible","no visible"} + , { "not_visible.help","No visible.
El acceso a la informaci\u00f3n de la reserva est\u00e1 restringido."} + , { "nothing_selected","Nada seleccionado"} + , { "occupation","Ocupaci\u00f3n"} + , { "ok","OK"} + , { "old_password","Antig\u00fca palabra de paso"} + , { "only_own_reservations","S\u00f3lo mis reservas"} + , { "open","Abrir"} + , { "options","Opciones"} + , { "or","o"} + , { "password","Palabra de paso"} + , { "password_verification","Verificaci\u00f3n de palabra de paso"} + , { "paste","Pegar"} + , { "paste_as","Pegar como"} + , { "period","Periodo"} + , { "period.format.end","Fin de {0}"} + , { "period.format.start","Inicio de {0}"} + , { "period.format.week","{0}.Sem. {1}"} + , { "period.not_set","Seleccionar el periodo"} + , { "period_view","Periodo"} + , { "periods","Periodos"} + , { "permission.access","Acceso"} + , { "permission.admin","Derechos de administrador"} + , { "permission.allocate","Puede asignar"} + , { "permission.allocate-conflicts","Puede asignar y crear conflictos"} + , { "permission.denied","Denegado"} + , { "permission.read","Puede leer"} + , { "permissions","Permisos"} + , { "person","Persona"} + , { "person_type","Tipo de persona"} + , { "person_types","Tipos de personas"} + , { "persons","Personas"} + , { "preferences","Preferencias"} + , { "print","Imprimir"} + , { "print_to_file","Imprimir en Fichero"} + , { "publish","Publicar"} + , { "question","Pregunta"} + , { "rapla.name","Rapla"} + , { "rapla.title","Rapla, el planificador inteligente"} + , { "rapla.welcome","Hola {0}, \u00a1disfruta planificando!"} + , { "refresh","Actualizar"} + , { "reload_data","Recargar los datos"} + , { "remove","Eliminar"} + , { "repeat","repite"} + , { "repeating","Repetir"} + , { "repeating.end_date","hasta"} + , { "repeating.forever","indefinido"} + , { "repeating.interval.post","."} + , { "repeating.interval.pre","Cada"} + , { "repeating.n_times","x veces"} + , { "repeating.start_date","a partir de"} + , { "report","Informe"} + , { "reservation","Evento"} + , { "reservation.allocations","Asignaciones : "} + , { "reservation.appointments","Citas: "} + , { "reservation.create_without_wizard","Evento"} + , { "reservation.name","Nombre del evento"} + , { "reservation.owner","Reservado por"} + , { "reservation_type","Tipo de evento"} + , { "reservations","Eventos"} + , { "reservations_from_all_users","Eventos de todos los usuarios"} + , { "reset","Restablecer"} + , { "resource","Recurso"} + , { "resource.owner","Registrado por"} + , { "resource_type","Tipo de recurso"} + , { "resource_types","Tipos de recursos"} + , { "resources","Recursos"} + , { "resources_persons","Recursos y personas"} + , { "restart_client","Reiniciar el cliente Rapla"} + , { "restart_options","..."} + , { "restart_server","Reiniciar el servidor"} + , { "root","Ra\u00edz"} + , { "rows_per_hour","L\u00edneas por hora"} + , { "save","Guardar"} + , { "search","Buscar"} + , { "second","secundo"} + , { "seconds","secundos"} + , { "select","Seleccionar"} + , { "selectable","Seleccionable"} + , { "selectable_on","Seleccionable en"} + , { "selected","Seleccionado"} + , { "selected_on","Seleccionado en"} + , { "selection","Selecci\u00f3n"} + , { "selection_resource","Selecci\u00f3n de recursos"} + , { "serie","Serie"} + , { "server","Servidor"} + , { "show_as","Mostrar como"} + , { "show_conflicts","Mostrar las conflictos"} + , { "show_tips","Mostrar las tips mouse-over"} + , { "single_appointment","Reserva \u00fanica"} + , { "source","Fuente"} + , { "start_date","Inicio"} + , { "start_rapla_with_applet","" + + " iniciar Rapla (con Java-Plugin)" + + " "} + , { "start_rapla_with_webstart","" + + " iniciar Rapla (con java webstart)" + + " "} + , { "start_time","Hora de inicio"} + , { "surname","Apellidos"} + , { "switch_back","Volver a ser administrador"} + , { "switch_to","Cambiar a"} + , { "table","Tabla"} + , { "time_at","a las"} + , { "time_until","hasta"} + , { "today","Hoy"} + , { "total_occurances","N\u00famero de apariciones"} + , { "translation","Traducci\u00f3n"} + , { "translation.format","Traducci\u00f3n de \"{0}\":"} + , { "type","Tipo"} + , { "type.boolean","S\u00ed|No"} + , { "type.date","Fecha"} + , { "type.int","N\u00famero entero"} + , { "type.rapla:category","Categor\u00eda"} + , { "type.string","Texto"} + , { "types","Tipos"} + , { "user","Usuario"} + , { "username","Nombre de usuario"} + , { "users","Usuarios"} + , { "view","Ver"} + , { "warning","Atenci\u00f3n"} + , { "warning.conflict","\u00a1ATENCI\u00d3N : Se han encontrado conflictos!"} + , { "warning.duplicated_appointments","\u00a1ATENCI\u00d3N : La misma reserva existe en m\u00faltiples ocasiones [{0}]!"} + , { "warning.experienced_users_only","\u00a1Estos par\u00e1metros est\u00e1n reservados s\u00f3lo para usuarios experimentados!"} + , { "warning.max-one-eventtype","S\u00f3lo un tipo de evento se permite. Ajuste el filtro!"} + , { "warning.no_allocatables_selected","\u00a1No ha seleccionado ning\u00fan(a) recurso/persona"} + , { "warning.no_conflict_permission","\u00a1No tiene permisos suficientes para crear conflictos para [{0}]!"} + , { "warning.no_reserve_permission","\u00a1No tiene permisos suficientes para efectuar/modificar [{0}] la reserva [{1}]!"} + , { "warning.period_shorter_than_week","\u00a1El periodo es inferior a una semana!"} + , { "warning.readonly_storage","\u00a1Los datos se han modificado s\u00f3lo en memoria. Las modificaciones se perder\u00e1n para las sesiones siguientes!"} + , { "warning.reservation.delete","El evento que est\u00e1 editando ha sido eliminado"} + , { "warning.reservation.update","El evento que est\u00e1 editando ha sido modificado." + + " La informaci\u00f3n que aparece en esta ventana ser\u00e1 actualizada."} + , { "warning.update","El objeto \"{0}\" que est\u00e1 editando ha sido modificado fuera de la ventana" + + " La edici\u00f3n actual va a ser interrumpida."} + , { "webinfo.text","" + + "

M\u00e1s informaci\u00f3n:

" + + "

" + + " Java-Webstart se incluye en todas las versiones de Java desde la 1.4.0." + + " Para ejecutar Rapla, se necesita Java 1.4 o superior. Se lo puede descargar de" + + " java.sun.com." + + "

" + + "

" + + " Para m\u00e1s informaci\u00f3n, visite rapla.sourceforge.net." + + "

" + + " "} + , { "week","Semana"} + , { "week_compact","Semana corta"} + , { "weekday","D\u00eda de la semana"} + , { "weekly","Semanal"} + , { "weeks","semanas"} + , { "weekview.print.choose_export","Escojer el m\u00e9todo de almacenamiento"} + , { "weekview.print.dialog_title","Imprimir la previsualizaci\u00f3n"} + , { "weekview.print.format_button","Preferencias de impresi\u00f3n"} + , { "weekview.print.postscript","Postscript"} + , { "weekview.print.title_textfield","T\u00edtulo"} + , { "width","Ancho"} + , { "with","con"} + , { "x_days_advance","x d\u00edas de antelaci\u00f3n"} + , { "year",".."} + , { "yearly","anual"} + , { "years",".."} + , { "yes","S\u00ed"} + , { "zero_appointment","Ninguna cita"} + }; +} diff --git a/Rapla/generated-src/org/rapla/RaplaResources_fr.java b/Rapla/generated-src/org/rapla/RaplaResources_fr.java new file mode 100644 index 0000000..80cf896 --- /dev/null +++ b/Rapla/generated-src/org/rapla/RaplaResources_fr.java @@ -0,0 +1,424 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class RaplaResources_fr extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "abort","Annuler"} + , { "add","Ins\u00e9rer"} + , { "additional-view","Informations compl\u00e9mentaires"} + , { "admin","Administrateur"} + , { "admin.login","Vous disposez des droits d'administrateur !"} + , { "administration","Administration"} + , { "all-day","24h"} + , { "all_filtered","Tous les objets filtr\u00e9s."} + , { "all_users","Tous les utilisateurs"} + , { "allocatable_in_timeframe","Peut \u00eatre allou\u00e9 pendant la p\u00e9riode"} + , { "allocation_view","Allocation de {0}"} + , { "alteration","Modification"} + , { "and","et"} + , { "apply","Valider"} + , { "appointment","R\u00e9servation"} + , { "appointment.convert","Convertir des \u00e9v\u00e9nements uniques"} + , { "appointment.day_x","le jour x"} + , { "appointment.days","Jour(s)"} + , { "appointment.exception.days","Jours d'exceptions"} + , { "appointment.exception.general","Exceptions :"} + , { "appointment.exceptions","Exceptions"} + , { "appointment.format.exceptions","{0} Exceptions :"} + , { "appointment.hours","Heure(s)"} + , { "appointment.minutes","min."} + , { "appointment.next_day","jour suivant"} + , { "appointment.no_exceptions","Sans exception"} + , { "appointment.rule","R\u00e8gle :"} + , { "appointment.same_day","m\u00eame jour"} + , { "appointment_list","Liste des r\u00e9servations"} + , { "appointments","R\u00e9servations"} + , { "attribute","Attribut"} + , { "attributes","Attributs"} + , { "available","Disponible"} + , { "back","Retour"} + , { "bind_with_person","connecter personne"} + , { "calendar","Calendrier"} + , { "calendar_settings","L'Affichage"} + , { "calendarname","Nom du calendrier: "} + , { "calendarweek.abbreviation","S{0,date,w}"} + , { "cancel","Annuler"} + , { "categories","Cat\u00e9gories"} + , { "category","Cat\u00e9gorie"} + , { "change","Modifier"} + , { "change.format","Modifier {0}"} + , { "change_appointment","Modifier la r\u00e9servation"} + , { "changes","Modifications"} + , { "choose_language","Choisissez votre langue :"} + , { "choose_root_category","S\u00e9lectionner la cat\u00e9gorie racine"} + , { "class","Classe"} + , { "classification","Classification"} + , { "close","Fermer"} + , { "color","Couleur"} + , { "complete_time","P\u00e9riode totale"} + , { "confirm","Confirmer"} + , { "confirm-close.ok","Annuler les modifications"} + , { "confirm-close.question","Vous n'avez pas sauvegard\u00e9 les modifications!"} + , { "confirm-close.title","Modifications non sauvegard\u00e9es !"} + , { "confirm.dialog.question","Sauvegarder cette information ?"} + , { "confirm.dialog.title","Confirmer {0}"} + , { "conflict.appointment1","Quand ?"} + , { "conflict.appointment2","Conflit d'allocation"} + , { "conflict.reservation1","Quel \u00e9v\u00e8nement ?"} + , { "conflict.reservation2","Autre \u00e9v\u00e8nement ?"} + , { "conflict.resource","Quoi/Qui ?"} + , { "conflict.user","Avec qui ?"} + , { "conflictUC","Conflits ({0,number,integer})"} + , { "conflicts","Conflits"} + , { "connection","connexion au serveur"} + , { "constraints","Contraintes"} + , { "continue","Suivant"} + , { "copy","Copier"} + , { "copy_appointment.format","Voulez-vous copier toute la s\u00e9rie de r\u00e9servations ou seulement celle-ci {0}?"} + , { "copy_to_clipboard","Copier vers le presse papier"} + , { "create","Fermer"} + , { "created_at","Cr\u00e9\u00e9 le"} + , { "customized","Sp\u00e9cifique"} + , { "daily","Quotidien"} + , { "database","Base de donn\u00e9es"} + , { "date","Date"} + , { "day","Jour"} + , { "days","Jours"} + , { "default","Standard"} + , { "defaultselection","S\u00e9lection par d\u00e9faut"} + , { "delete","Effacer"} + , { "delete.abort","Annuler la commande"} + , { "delete.format","Effacer {0}"} + , { "delete.ok","Oui, effacer"} + , { "delete.question","Vous allez effacer les objets suivants :"} + , { "delete.title","Effacer"} + , { "delete_appointment.format","Voulez-vous effacer toute la s\u00e9rie de r\u00e9servations ou seulement celle-ci {0}?"} + , { "delete_selection","Effacer la s\u00e9lection"} + , { "destination","destination"} + , { "display_exceptions","Afficher les exceptions dans le calendrier"} + , { "duration","Dur\u00e9e"} + , { "dynamictype","Type"} + , { "dynamictype.annotation.nameformat","Mettre en forme l'affichage"} + , { "dynamictype.annotation.nameformat.description","Mettez la cl\u00e9 entre {} pour ins\u00e9rer des attributs, par ex. {macl\u00e9}"} + , { "dynamictype.name","Nom du type"} + , { "edit","Modifier"} + , { "edit-view","\u00c9cran de modification"} + , { "edit.format","Modifier {0}"} + , { "edit_advanced","Affiche param\u00e9trage pour utilisateurs exp\u00e9riment\u00e9s"} + , { "edit_reservation.format","Modifier l\"\u00e9v\u00e8nement: {0}"} + , { "edit_reservations","modifier les siennes"} + , { "elementkey","Cl\u00e9 d'\u00e9l\u00e9ment"} + , { "email","Email"} + , { "end_date","Fin"} + , { "end_time","Heure de fin"} + , { "error","Erreur"} + , { "error.cant_delete_your_account","Vous ne pouvez effacer [{0}], parce que vous utilisez actuellement ce compte !"} + , { "error.connect","La connexion vers [{0}] a \u00e9chou\u00e9 ! Le serveur n''est peut \u00eatre pas disponible. Essayez plus tard."} + , { "error.connection_closed","Pas ou plus de connexion vers [{0}]. Red\u00e9marrer Rapla."} + , { "error.dependencies","Des d\u00e9pendences existent pour ces objets"} + , { "error.invalid_key","\"{0}\" n''est pas une cl\u00e9 valide!" + + " Note: Vous ne pouvez utiliser une combinaison de max. 50 lettres, chiffres ou l'une des" + + " caract\u00e8res suivants {1}. La cl\u00e9 doit commencer par {2} ou une lettre! Exemple: Nombre_de_places" + + " "} + , { "error.login","Erreur dans le Login !"} + , { "error.new_version","L''objet {0} n''a pu \u00eatre modifi\u00e9. Il \u00e9tait en cours de modification par quelqu''un d''autre." + + " Une autre version existe. "} + , { "error.no_appointment","Un \u00e9v\u00e8nement est constitu\u00e9 d'au moins une r\u00e9servation !"} + , { "error.no_entry_for","Vous devez donner un nom pour : {0}"} + , { "error.no_key","Vous devez donner une cl\u00e9 pour : {0}"} + , { "error.no_name","Vous devez donner un nom !"} + , { "error.no_reservation_name","Vous devez donner un titre \u00e0 cet \u00e9v\u00e8nement !"} + , { "error.no_rollback","Attention ! Une erreur est survenue pendant l'actualisation de la base de donn\u00e9es. Votre base de donn\u00e9es refuse cette transaction. Pr\u00e9venez imm\u00e9diatement votre administrateur !"} + , { "error.not_unique","Le nom \"{0}\" est d\u00e9j\u00e0 existant. Choisissez en un autre !"} + , { "error.one_type_requiered","Au moins un type doit \u00eatre disponible !"} + , { "error.passwords_dont_match","Les mots de passe ne sont pas concordants !"} + , { "error.reference_not_stored","Attention! L''objet r\u00e9f\u00e9renc\u00e9 [{0}] n''a pu \u00eatre trouv\u00e9. A probablement \u00e9t\u00e9 effac\u00e9 r\u00e9cemment."} + , { "error.rollback","L'actualisation de la base de donn\u00e9es a \u00e9chou\u00e9." + + " Toutes les modifications sont perdues"} + , { "error.wrong_password","Mauvais mot de passe !"} + , { "every_appointment","Toutes les r\u00e9servations"} + , { "everytime","Toujours"} + , { "exception","Exception"} + , { "exchange_allocatables","Modifier l\"allocation"} + , { "exclamation.format","{0} !"} + , { "exclude_days","exclure les jours"} + , { "exit","Quitter"} + , { "exit.abort","Continuer Rapla >>"} + , { "exit.ok","Quitter Rapla ?"} + , { "exit.question","Souhaitez vous r\u00e9ellement quitter Rapla ?"} + , { "exit.title","Quitter Rapla ?"} + , { "expected_columns","Nombre de colonnes"} + , { "expected_rows","Nombre de lignes"} + , { "export","Export"} + , { "file","Fichier"} + , { "filter","Filtre"} + , { "filter.contains","contient"} + , { "filter.earlier_than","est avant le"} + , { "filter.equals","est \u00e9gal \u00e0"} + , { "filter.greater_or_equals","est sup\u00e9rieur ou \u00e9gal \u00e0"} + , { "filter.is_greater_than","est sup\u00e9rieur \u00e0"} + , { "filter.is_smaller_than","est plus petit que"} + , { "filter.later_than","est apr\u00e8s le"} + , { "filter.not_equals","est in\u00e9gaux"} + , { "filter.smaller_or_equals","est plus petit ou \u00e9gal que"} + , { "filter_allocatable","Ressources et personnes"} + , { "filter_reservation.all","De tous les utilisateurs"} + , { "filter_reservation.own","Vos propres \u00e9v\u00e8nements"} + , { "filter_restrictions","Limiter l''affichage de {0}"} + , { "filtername","Nom du filtre"} + , { "fixed_date","Date fixe"} + , { "for","pour"} + , { "forename","Pr\u00e9nom"} + , { "format.repeat_from","\u00e0 partir du {0}"} + , { "format.repeat_n_times","r\u00e9p\u00e8te {0} fois"} + , { "format.repeat_until","jusqu`\u00e0 {0}"} + , { "goto_date","Atteindre la date"} + , { "group","Groupe"} + , { "groups","Groupes"} + , { "help","Aide"} + , { "hierarchy","Hi\u00e9rarchie"} + , { "holdbackconflicts","Ne pas tenir compte des conflits"} + , { "hour","Heure"} + , { "hours","Heures"} + , { "import","Import"} + , { "in_period.format","dans la p\u00e9riode {0}"} + , { "including_date","y compris la date: {0}"} + , { "info","Information"} + , { "info.text","" + + " Rapla Version 1.4.3-beta" + + "
" + + " (C) Copyright 2000-2011 Rapla Team" + + "

Rapla-Webseite: http://rapla.org" + + "

" + + "

Envoyer vos propositions d\"\u00e9volutions, questions, ou erreurs trouv\u00e9es" + + " \u00e0 la Mailingliste d\u00e9veloppeurs :
" + + " rapla-developers@lists.sourceforge.net" + + "

" + + "

Version : 2011-10-05 13:41

" + + "

Signature: {0}

" + + "

Version Java {1}

" + + " "} + , { "info.title","" + + " Rapla Version 1.4.3-beta" + + " "} + , { "insert","Ins\u00e9rer"} + , { "interval","Interval"} + , { "interval.format","Tous/toutes les {0} {1}"} + , { "keep","Conserver"} + , { "key","Cl\u00e9"} + , { "language","Langue"} + , { "last_changed","Derni\u00e8re modification le"} + , { "last_changed_by","Derni\u00e8re modification"} + , { "legend","L\u00e9gende"} + , { "level","Niveau"} + , { "license.text","" + + " Rapla Version 1.4.3-beta (C) Copyright 2000-2011 Rapla Team" + + "

Rapla est SANS\u00a0GARANTIE!!" + + " Rapla est un logiciel libre, que vous pouvez utiliser et distribuer suivant ces conditions.

" + + " "} + , { "licensedialog.title","La licence GNU"} + , { "list.format","{0} : {1}"} + , { "load","Charger"} + , { "login","Login"} + , { "logindialog.title","Login Rapla"} + , { "logout","Quitter"} + , { "main-view","\u00c9cran principal"} + , { "minute","Minute"} + , { "minutes","Minutes"} + , { "month","Mois"} + , { "monthly","mensuel"} + , { "months","Mois"} + , { "move","D\u00e9placer"} + , { "move_appointment.format","Voulez-vous d\u00e9placer toute la s\u00e9rie de r\u00e9servations ou seulement celle-ci {0}?"} + , { "name","Nom"} + , { "never","Jamais"} + , { "new","Nouveau"} + , { "new_appointment","Nouvelle r\u00e9servation"} + , { "new_category","Nouvelle cat\u00e9gorie"} + , { "new_password","Nouveau mot de passe"} + , { "new_reservation","nouvel \u00e9v\u00e8nement"} + , { "new_reservation.format","Nouvel \u00e9v\u00e8nement : {0}"} + , { "new_rule","Nouvelle r\u00e8gle pour"} + , { "new_sub-category","Nouvelle sous-cat\u00e9gorie"} + , { "no","Non"} + , { "no-view","invisible"} + , { "no_classification","Pas de classification"} + , { "no_repeating","Unique"} + , { "not_selected.help","Actuellement non s\u00e9lectionn\u00e9.
D\u00e9-s\u00e9lectionnez \"Seulement mes r\u00e9servations\" ou changez vos r\u00e9glages du filtre."} + , { "not_visible","non visible"} + , { "not_visible.help","Non visible.
L'acc\u00e8s aux d\u00e9tails de la r\u00e9servation est restreint."} + , { "nothing_selected","Rien de s\u00e9lectionn\u00e9"} + , { "occupation","l''Occupation"} + , { "ok","OK"} + , { "old_password","Ancien mot de passe"} + , { "only_own_reservations","Seulement mes r\u00e9servations"} + , { "open","Ouvrir"} + , { "options","Options"} + , { "or","ou"} + , { "password","Mot de passe"} + , { "password_verification","V\u00e9rification du mot de passe"} + , { "paste","Coller"} + , { "paste_as","Coller"} + , { "period","P\u00e9riode"} + , { "period.format.end","Fin {0}"} + , { "period.format.start","D\u00e9but {0}"} + , { "period.format.week","{0}.Sem. {1}"} + , { "period.not_set","S\u00e9lectionner la p\u00e9riode"} + , { "period_view","P\u00e9riode"} + , { "periods","P\u00e9riodes"} + , { "permission.access","Acc\u00e8s"} + , { "permission.admin","Droits d'administrateur"} + , { "permission.allocate","peut allouer"} + , { "permission.allocate-conflicts","Peut allouer & Cr\u00e9er des conflits"} + , { "permission.denied","Interdit"} + , { "permission.read","peut lire"} + , { "permissions","Permissions"} + , { "person","Personne"} + , { "person_type","Type de personne"} + , { "person_types","Types de personne"} + , { "persons","Personnes"} + , { "preferences","pr\u00e9f\u00e9rence"} + , { "print","Imprimer"} + , { "print_to_file","Imprimer en Fichier"} + , { "publish","Publier"} + , { "question","Question"} + , { "rapla.name","Rapla"} + , { "rapla.title","Rapla, le planificateur intelligent"} + , { "rapla.welcome","Bonjour {0}, amusez vous en planifiant !"} + , { "refresh","Actualiser"} + , { "reload_data","Recharger les donn\u00e9es"} + , { "remove","Effacer"} + , { "repeat","r\u00e9p\u00e8te"} + , { "repeating","R\u00e9p\u00e9ter"} + , { "repeating.end_date","jusqu'\u00e0"} + , { "repeating.forever","Ind\u00e9fini"} + , { "repeating.interval.post","."} + , { "repeating.interval.pre","Chaque"} + , { "repeating.n_times","x fois"} + , { "repeating.start_date","\u00e0 partir de"} + , { "report","Rapport"} + , { "reservation","\u00c9v\u00e8nement"} + , { "reservation.allocations","Allocation :"} + , { "reservation.appointments","\u00c9v\u00e8nement :"} + , { "reservation.create_without_wizard","\u00c9v\u00e8nement"} + , { "reservation.name","Nom de l'\u00e9v\u00e8nement"} + , { "reservation.owner","r\u00e9serv\u00e9 par"} + , { "reservation_type","Type d'\u00e9v\u00e8nement"} + , { "reservations","\u00c9v\u00e8nements"} + , { "reservations_from_all_users","Ev\u00e8nement de tous les utilisateurs"} + , { "reset","R\u00e9initialisation"} + , { "resource","Ressource"} + , { "resource.owner","Enregistr\u00e9 par"} + , { "resource_type","Type de ressources"} + , { "resource_types","Types de ressources"} + , { "resources","Ressources"} + , { "resources_persons","Ressources et Personnes"} + , { "restart_client","Red\u00e9marrer Rapla"} + , { "restart_options","..."} + , { "restart_server","Red\u00e9marrer Serveur"} + , { "root","Racine"} + , { "rows_per_hour","Lignes par heure"} + , { "save","Sauvegarder"} + , { "search","Rechercher"} + , { "second","Seconde"} + , { "seconds","Secondes"} + , { "select","S\u00e9lectionner"} + , { "selectable","S\u00e9lectionnable le"} + , { "selectable_on","Possible le"} + , { "selected","S\u00e9lectionn\u00e9"} + , { "selected_on","S\u00e9lectionn\u00e9 le"} + , { "selection","S\u00e9lection"} + , { "selection_resource","S\u00e9lection des ressources"} + , { "serie","S\u00e9rie"} + , { "server","Serveur"} + , { "server_status","" + + " Etat du Serveur" + + " "} + , { "show_as","Afficher"} + , { "show_conflicts","Afficher conflits"} + , { "show_tips","Afficher mouse-over tips dans"} + , { "single_appointment","r\u00e9servation unique"} + , { "source","source"} + , { "start_date","D\u00e9but"} + , { "start_rapla_with_applet","" + + " D\u00e9marrer Rapla (avec le Plugin Java)" + + " "} + , { "start_rapla_with_webstart","" + + " D\u00e9marrer Rapla (avec java webstart)" + + " "} + , { "start_time","Heure de d\u00e9but"} + , { "surname","Nom"} + , { "switch_back","revenir vers"} + , { "switch_to","Changer pour"} + , { "table","Table"} + , { "time_at","\u00e0"} + , { "time_until","jusqu'\u00e0"} + , { "today","Aujourd'hui"} + , { "total_occurances","Nombre d'occurrences"} + , { "translation","Traduction"} + , { "translation.format","Traduction de \"{0}\":"} + , { "type","Type"} + , { "type.boolean","Oui|Non"} + , { "type.date","Date"} + , { "type.int","Nombre entier"} + , { "type.rapla:category","Cat\u00e9gorie"} + , { "type.string","Texte"} + , { "types","Types"} + , { "user","Utilisateur"} + , { "username","Profil utilisateur"} + , { "users","Utilisateurs"} + , { "view","Affichage"} + , { "warning","Attention"} + , { "warning.conflict","ATTENTION : Conflits trouv\u00e9s !"} + , { "warning.duplicated_appointments","ATTENTION : La m\u00eame r\u00e9servation existe plusieurs fois [{0}]!"} + , { "warning.experienced_users_only","Ce param\u00e9trage est r\u00e9serv\u00e9 aux utilisateurs exp\u00e9riment\u00e9s !"} + , { "warning.max-one-eventtype","Seulement 1 type d'\u00e9v\u00e9nement est autoris\u00e9. R\u00e9glez le filtre!"} + , { "warning.no_allocatables_selected","Vous n'avez pas s\u00e9lectionn\u00e9 de ressource/personne !"} + , { "warning.no_conflict_permission","Vous ne disposez pas des droits suffisants pour cr\u00e9er des conflits pour [{0}] !"} + , { "warning.no_reserve_permission","Vous ne disposez pas des droits suffisants pour effectuer/modifier [{0}] la r\u00e9servation [{1}] "} + , { "warning.period_shorter_than_week","La p\u00e9riode est inf\u00e9rieure \u00e0 une semaine !"} + , { "warning.readonly_storage","Modifi\u00e9 uniquement en m\u00e9moire. La modification sera perdue pour les sessions suivantes !"} + , { "warning.reservation.delete","L'\u00e9v\u00e8nement que vous \u00e9ditez vient d'\u00eatre effac\u00e9"} + , { "warning.reservation.update","L'\u00e9v\u00e8nement que vous \u00e9ditez a \u00e9t\u00e9 modifi\u00e9." + + " Les informations affich\u00e9es dans la fen\u00eatre vont \u00eatre rafraichies."} + , { "warning.update","L''objet \"{0}\" que vous \u00e9ditez a \u00e9t\u00e9 modifi\u00e9 hors de la fen\u00eatre." + + " Cette \u00e9dition va \u00eatre interrompue."} + , { "webinfo.text","" + + "

Plus d'informations:

" + + "

" + + " Java-Webstart est inclus dans toutes les versions de Java depuis la 1.4.0." + + " Pour lancer Rapla, vous avez besoin de Java 1.4 ou sup\u00e9rieur que vous pouvez t\u00e9l\u00e9charger depuis" + + " java.sun.com." + + "

" + + "

" + + " Consultez rapla.sourceforge.net pour de plus amples informations." + + "

" + + " "} + , { "week","Semaines"} + , { "week_compact","Semaine courte"} + , { "weekday","Jour de la semaine"} + , { "weekly","Hebdomadaire"} + , { "weeks","Semaines"} + , { "weekview.print.choose_export","Choisissez la m\u00e9thode de sauvegarde!"} + , { "weekview.print.dialog_title","Imprimer la pr\u00e9visualisation"} + , { "weekview.print.format_button","Pr\u00e9f\u00e9rence d'Impression"} + , { "weekview.print.postscript","Postscript"} + , { "weekview.print.title_textfield","Titre"} + , { "width","Largeur"} + , { "with","avec"} + , { "x_days_advance","x jours en avance"} + , { "year","Ann\u00e9e"} + , { "yearly","annuel"} + , { "years","Ann\u00e9e"} + , { "yes","Oui"} + , { "zero_appointment","Pas de r\u00e9servation"} + }; +} diff --git a/Rapla/generated-src/org/rapla/RaplaResources_nl.java b/Rapla/generated-src/org/rapla/RaplaResources_nl.java new file mode 100644 index 0000000..00304ed --- /dev/null +++ b/Rapla/generated-src/org/rapla/RaplaResources_nl.java @@ -0,0 +1,403 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class RaplaResources_nl extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "abort","Annuleren"} + , { "add","Toevoegen"} + , { "additional-view","Extra gegevens"} + , { "admin","Beheerder"} + , { "admin.login","Beheerder"} + , { "administration","Systeem beheer"} + , { "all-day","24u"} + , { "all_filtered","alle gefilterde objecten."} + , { "all_users","Alle gebruikers"} + , { "allocatable_in_timeframe","Beschikbaarheid"} + , { "allocation_view","Toewijzing van {0}"} + , { "alteration","Wijziging"} + , { "and","en"} + , { "apply","OK"} + , { "appointment","Reservatie"} + , { "appointment.convert","Converteren naar \u00e9\u00e9nmalige evenementen"} + , { "appointment.day_x","op dag x"} + , { "appointment.days","dag(en)"} + , { "appointment.exception.days","Uitgezonderd:"} + , { "appointment.exception.general","Uitgezonderd:"} + , { "appointment.exceptions","Uitgezonderd"} + , { "appointment.format.exceptions","{0} Uitgezonderd: "} + , { "appointment.hours","uur"} + , { "appointment.minutes","min."} + , { "appointment.next_day","volgende dag"} + , { "appointment.no_exceptions","Geen uitzonderingen:"} + , { "appointment.rule","Regel: "} + , { "appointment.same_day","zelfde dag"} + , { "appointment_list","Lijst reservaties"} + , { "appointments","Reservaties"} + , { "attribute","Kenmerk"} + , { "attributes","Kenmerken"} + , { "available","Beschikbaar"} + , { "back","Sluit"} + , { "bind_with_person","Verbinding met persoon"} + , { "calendar","Kalender"} + , { "calendar_settings","Overzicht opslaan"} + , { "calendarname","Kalender naam: "} + , { "calendarweek.abbreviation","WK{0,date,w}"} + , { "cancel","Annuleren"} + , { "categories","Klassement"} + , { "category","Klassement"} + , { "change","Wijzig"} + , { "change.format","Wijzig {0}"} + , { "change_appointment","Wijzig afspraak"} + , { "changes","Wijzigingen"} + , { "choose_language","Taalkeuze:"} + , { "class","Klasse"} + , { "classification","Klassificatie"} + , { "close","Sluit"} + , { "color","Kleur"} + , { "complete_time","volledige duur"} + , { "confirm","Bevestig"} + , { "confirm-close.ok","Wijzigingen negeren"} + , { "confirm-close.question","De wijzigingen werden niet geregisteerd!"} + , { "confirm-close.title","Wijzigingen"} + , { "confirm.dialog.question","Bevestig deze informatie?"} + , { "confirm.dialog.title","Bevestig {0}"} + , { "conflict.appointment1","Wanneer?"} + , { "conflict.appointment2","Toewijzings conflict"} + , { "conflict.reservation1","Welk evenement?"} + , { "conflict.reservation2","Ander evenement?"} + , { "conflict.resource","Wat/Wie?"} + , { "conflict.user","Gebruiker?"} + , { "conflictUC","Conflict ({0,number,integer})"} + , { "conflicts","Conflict"} + , { "connection","Verbinding met de Server"} + , { "constraints","Beperkingen"} + , { "continue","Verder"} + , { "copy","Kopieer"} + , { "copy_appointment.format","Wil je de reeks of enkel de boeking op {0} kopieren?"} + , { "copy_to_clipboard","Kopie\u00ebr naar plakbord"} + , { "create","Aanmaken"} + , { "created_at","Aangemaakt op"} + , { "customized","Aangepast door Gebruiker"} + , { "daily","Dagelijks"} + , { "database","Databank"} + , { "date","Datum"} + , { "day","Dag"} + , { "days","Dagen"} + , { "default","Standaard"} + , { "defaultselection","Standaard Selectie"} + , { "delete","Verwijderen"} + , { "delete.abort","Annuleren"} + , { "delete.format","Verwijder: {0}"} + , { "delete.ok","OK"} + , { "delete.question","Volgende onderdelen worden verwijderd: "} + , { "delete.title","Verwijderen?"} + , { "delete_appointment.format","De volledige reeks of \u00e9\u00e9nmalig de afspraak op {0} verwijderen?"} + , { "delete_selection","Verwijder selectie"} + , { "destination","Naar"} + , { "display_exceptions","Uitzonderingen"} + , { "duration","Tijdsduur"} + , { "dynamictype","Folder"} + , { "dynamictype.annotation.nameformat","Titel formaat"} + , { "dynamictype.annotation.nameformat.description","Sleutels tussen {...} plaatsen; bvb. {mijnSleutel}"} + , { "dynamictype.name","Naam type"} + , { "edit","Bewerk"} + , { "edit-view","Toon als"} + , { "edit.format","Bewerk: {0}"} + , { "edit_advanced","Toon speciale instellingen"} + , { "edit_reservation.format","Bewerk evenement: {0}"} + , { "edit_reservations","bewerk eigen"} + , { "elementkey","Elementsleutel"} + , { "email","Email"} + , { "end_date","Stop datum"} + , { "end_time","tot"} + , { "error","Fout"} + , { "error.cant_delete_your_account","Je kan [{0}] niet verwijderen,omdat je deze momenteel in gebruik hebt!"} + , { "error.connect","Verbinding met [{0}] is verbroken, de server is niet beschikbaar, contacteer de Rapla beheerder of probeer later!"} + , { "error.connection_closed","Geen of een gesloten connectie naar [{0}]. Rapla herstarten a.u.b."} + , { "error.dependencies","Afhankelijk van"} + , { "error.invalid_key","\"{0}\" is een ongeldige sleutel!" + + " Nota: Maximaal 50 letters, cijfers of {1} beginnend met {2} of een letter!" + + " "} + , { "error.login","Aanmelding geweigerd!"} + , { "error.new_version","{0} versie {1} is recentelijk gewijzigd, versie {2} is beschikbaar. Probeer later."} + , { "error.no_appointment","Evenement zonder reservaties!"} + , { "error.no_entry_for","Je moet de gegevens opgeven voor: {0}"} + , { "error.no_key","Een sleutel is verplicht: {0}"} + , { "error.no_reservation_name","Een titel opgeven!"} + , { "error.no_rollback","Fout! Databank wijziging. Databank ondersteund geen transacties." + + " Dit kan tot serieuze fouten leiden.Neem onmiddelijk contact met de Rapla beheerder!"} + , { "error.not_unique","De naam \"{0}\" is reeds in gebruik. Kies een andere naam!"} + , { "error.one_type_requiered","Minimaal 1 soort is nodig."} + , { "error.passwords_dont_match","De paswoorden verschillen!"} + , { "error.reference_not_stored","Fout! Gevraagd object [{0}] niet gevonden. Waarschijnlijk recentelijk verwijderd."} + , { "error.rollback","Databank wijziging. Wijzigingen werden geannuleerd."} + , { "error.wrong_password","Verkeerd paswoord!"} + , { "every_appointment","Alle afspraken in lijst"} + , { "everytime","onbeperkt"} + , { "exception","Uitzondering"} + , { "exchange_allocatables","Wijzig toewijzing"} + , { "exclamation.format","{0} !"} + , { "exclude_days","Uitgezonderd dagen:"} + , { "exit","Sluiten"} + , { "exit.abort","Terug"} + , { "exit.ok","Planning be\u00ebindigen"} + , { "exit.question","Planning be\u00ebindigen?"} + , { "exit.title","Rapla be\u00ebindigen"} + , { "expected_columns","Aantal kolommen."} + , { "expected_rows","Aantal rijen"} + , { "export","Publiceer"} + , { "file","Bestand"} + , { "filter","Filter"} + , { "filter.contains","bevat"} + , { "filter.earlier_than","vroeger dan"} + , { "filter.equals","gelijk aan"} + , { "filter.greater_or_equals","groter dan of gelijk"} + , { "filter.is_greater_than","groter dan"} + , { "filter.is_smaller_than","kleiner dan"} + , { "filter.later_than","later dan"} + , { "filter.not_equals","ongelijk"} + , { "filter.smaller_or_equals","kleiner dan of gelijk"} + , { "filter_allocatable","Personen & Materialen"} + , { "filter_reservation.all","voor alle gebruikers"} + , { "filter_restrictions","beperk beeld van {0}"} + , { "filtername","Filter naam"} + , { "fixed_date","Vaste datum"} + , { "for","voor"} + , { "forename","Voornaam"} + , { "format.repeat_from","van {0}"} + , { "format.repeat_n_times","{0}X"} + , { "format.repeat_until","tot en met {0}"} + , { "goto_date","Ga naar datum"} + , { "group","Groep"} + , { "groups","Groepen"} + , { "help","Help"} + , { "hierarchy","Hierarchie"} + , { "holdbackconflicts","Conflicten onderdrukken"} + , { "hour","Uur"} + , { "hours","Uren"} + , { "import","Importeer"} + , { "in_period.format","in periode {0}"} + , { "including_date","met vaste datum = {0}"} + , { "info","Informatie"} + , { "info.text","" + + " (C) Copyright 2000-2011 Rapla Team
" + + "

Rapla versie 1.4.3-beta
" + + " Gebouwd op: 2011-10-05 13:41
" + + " Ondertekend: {0}
" + + " Java versie: {1}

" + + "

Rapla homepage: http://rapla.org
" + + " Support: http://lists.sourceforge.net/lists/listinfo/rapla-developers" + + " "} + , { "info.title","" + + " Rapla versie 1.4.3-beta" + + " "} + , { "insert","Toevoegen"} + , { "interval","Interval"} + , { "interval.format","Elke {0} {1}"} + , { "keep","Bewaar"} + , { "key","Sleutel"} + , { "language","Taal"} + , { "last_changed","Gewijzigd op"} + , { "last_changed_by","Laatst gewijzigd door"} + , { "legend","Legende"} + , { "level","Niveau"} + , { "license.text","" + + " Rapla versie 1.4.3-beta : 2011-10-05 13:41
" + + " (C) Copyright 2000-2011 Rapla Team" + + "

Rapla wordt geleverd ZONDER\u00a0GARANTIE.

" + + " Deze software kan enkel onder bepaalde voorwaarden vrij worden verspreid." + + " "} + , { "licensedialog.title","GNU Licentie"} + , { "list.format","{0}: {1}"} + , { "load","Laad"} + , { "login","Aanmelden"} + , { "logindialog.title","Rapla Aanmelden"} + , { "logout","Afmelden"} + , { "main-view","Basis gegevens"} + , { "minute","Minuut"} + , { "minutes","Minuten"} + , { "month","Maand"} + , { "monthly","Maandelijks"} + , { "months","Maanden"} + , { "move","Verplaats"} + , { "move_appointment.format","De volledige reeks of \u00e9\u00e9nmalig de afspraak op {0} verplaatsen?"} + , { "name","Naam"} + , { "never","nooit"} + , { "new","Nieuw"} + , { "new_appointment","Nieuwe afspraak"} + , { "new_category","Nieuw klassement"} + , { "new_password","Nieuw paswoord"} + , { "new_reservation","Nieuw evenement"} + , { "new_reservation.format","Nieuw evenement {0}"} + , { "new_rule","Nieuwe regel"} + , { "new_sub-category","Nieuw onderklassement"} + , { "no","Nee"} + , { "no-view","Onzichtbaar"} + , { "no_classification","Geen Classificatie"} + , { "no_repeating","E\u00e9nmalig"} + , { "not_selected.help","\"\"Enkel eigen reservaties\" afvinken\" of wijzig de filter instellingen.\". "} + , { "not_visible","onzichtbaar"} + , { "not_visible.help","Gewijgerd.
Toegang tot de boekings informatie is beperkt."} + , { "nothing_selected","Geen selectie"} + , { "occupation","Bezetting"} + , { "ok","OK"} + , { "old_password","Oud paswoord"} + , { "only_own_reservations","Enkel eigen reservaties"} + , { "open","Onbepaald"} + , { "options","Opties"} + , { "or","of"} + , { "password","Paswoord"} + , { "password_verification","Paswoord controle"} + , { "paste","Plakken"} + , { "paste_as","Plakken als"} + , { "period","Periode"} + , { "period.format.end","Einde van {0}"} + , { "period.format.start","Start van {0}"} + , { "period.format.week","Week {0} van {1}"} + , { "period.not_set","Kies periode"} + , { "period_view","Periode"} + , { "periods","Periode"} + , { "permission.access","Toegang"} + , { "permission.admin","Beheer"} + , { "permission.allocate","Toewijzen"} + , { "permission.allocate-conflicts","Toewijzen met conflicten"} + , { "permission.denied","Geen toegang"} + , { "permission.read","Lezen"} + , { "permissions","Toegang"} + , { "person","Persoon"} + , { "person_type","Type: Persoon"} + , { "person_types","Type: Personen"} + , { "persons","Personen"} + , { "preferences","Voorkeur"} + , { "print","Afdrukken"} + , { "print_to_file","Afdrukken naar Bestand"} + , { "publish","Publiceer"} + , { "question","Vraag"} + , { "rapla.title","Rapla, Smart Planning"} + , { "rapla.welcome","Online: {0}"} + , { "refresh","Actualiseren"} + , { "reload_data","herladen gegevens"} + , { "remove","Verwijderen"} + , { "repeat","herhaal"} + , { "repeating","Herhaling:"} + , { "repeating.end_date","tot en met"} + , { "repeating.forever","tot oneindig"} + , { "repeating.interval.post","-(ste/de)"} + , { "repeating.interval.pre","Elke"} + , { "repeating.n_times","x-Maal"} + , { "repeating.start_date","van"} + , { "report","Rapport"} + , { "reservation","Evenement"} + , { "reservation.allocations","Bezetting: "} + , { "reservation.appointments","Lijst afspraken:"} + , { "reservation.create_without_wizard","Evenement"} + , { "reservation.name","Evenement naam"} + , { "reservation.owner","Aangemaakt door"} + , { "reservation_type","Type Evenement"} + , { "reservations","Evenementen"} + , { "reservations_from_all_users","Evenementen van alle gebruikers"} + , { "reset","Terugzetten"} + , { "resource","Middel"} + , { "resource.owner","Geregistreerd door"} + , { "resource_type","Type: Middel"} + , { "resource_types","Middel type"} + , { "resources","Middelen"} + , { "resources_persons","Middelen&Personen"} + , { "restart_client","Herstart gebruiker"} + , { "restart_options","Om de wijzigingen te activeren moet je Rapla herstarten!"} + , { "restart_server","Herstart Rapla server"} + , { "root","Oorsprong"} + , { "rows_per_hour","Tijdsdelen per uur:"} + , { "save","Opslaan"} + , { "search","Opzoeken"} + , { "second","Seconde"} + , { "seconds","Seconden"} + , { "select","Selecteer"} + , { "selectable","Beschikbaarheid"} + , { "selectable_on","Voor:"} + , { "selected","Actief"} + , { "selected_on","Door:"} + , { "selection","Selecteer voorkeur"} + , { "selection_resource","Keuze middel"} + , { "serie","reeks"} + , { "server","Server"} + , { "show_as","Type"} + , { "show_conflicts","Conflicten"} + , { "show_tips","Tips"} + , { "single_appointment","\u00e9\u00e9nmalig"} + , { "source","Van"} + , { "start_date","Start datum"} + , { "start_rapla_with_applet","" + + " Start Rapla planning (Applet)" + + " "} + , { "start_rapla_with_webstart","" + + " Rapla Smart Planning" + + " "} + , { "start_time","van"} + , { "surname","Naam"} + , { "switch_back","Aanmelden als vorige gebruiker"} + , { "switch_to","Aanmelden als"} + , { "table","Tabel"} + , { "time_at","van"} + , { "time_until","tot"} + , { "today","Vandaag"} + , { "total_occurances","Totaal aantal"} + , { "translation","Vertaling"} + , { "translation.format","Vertaling voor \"{0}\":"} + , { "type","Type"} + , { "type.boolean","Ja|Neen"} + , { "type.date","Datum"} + , { "type.int","Getal"} + , { "type.rapla:category","Klassement"} + , { "type.string","Tekst"} + , { "types","Types"} + , { "user","Gebruiker"} + , { "username","Gebruiker"} + , { "users","Gebruikers"} + , { "view","Overzicht"} + , { "warning","Waarschuwing"} + , { "warning.conflict","WAARSCHUWING: Conflict gevonden!"} + , { "warning.duplicated_appointments","WAARSCHUWING: Dezelfde afspraak bestaat meerdere keren: [{0}]!"} + , { "warning.experienced_users_only","Deze instelling kan enkel gewijzigd worden door een super gebruiker!"} + , { "warning.max-one-eventtype","Enkel 1 type evenement is toegestaan. Pas de filter aan!"} + , { "warning.no_allocatables_selected","Evenement zonder Personen & Materialen!"} + , { "warning.no_conflict_permission","Aanmaken van conflicten voor [{0}], is niet toegestaan!"} + , { "warning.no_reserve_permission","Plannen of wijzigen van [{0}] in reservatie [{1}] is niet toegestaan!"} + , { "warning.period_shorter_than_week","Periode minder dan 1 week!"} + , { "warning.readonly_storage","Wijzigingen enkel in het geheugen. Gegevens worden niet opgeslagen."} + , { "warning.reservation.delete","Het evenement dat je bewerkt, werd verwijderd."} + , { "warning.reservation.update","Het evenement dat je wil wijzigen is gewijzigd." + + " De gegevens worden terug opgehaald."} + , { "warning.update","Het object \"{0}\" dat je bewerkt, werd gewijzigd. Bewerken wordt onderbroken."} + , { "webinfo.text","" + + "

Meer informatie:

" + + " rapla.sourceforge.net" + + " "} + , { "week","Week"} + , { "week_compact","Week (Compact)"} + , { "weekday","Weekdag"} + , { "weekly","Wekelijks"} + , { "weeks","Weken"} + , { "weekview.print.choose_export","Kies opslag methode!"} + , { "weekview.print.dialog_title","Afdrukvoorbeeld"} + , { "weekview.print.format_button","Printer voorkeur"} + , { "weekview.print.postscript","Postscript"} + , { "weekview.print.title_textfield","Titel"} + , { "width","Breedte"} + , { "with","met"} + , { "x_days_advance","x dagen op voorhand"} + , { "year","Jaar"} + , { "yearly","Jaarlijks"} + , { "years","Jaren"} + , { "yes","Ja"} + , { "zero_appointment","Geen afspraak"} + }; +} diff --git a/Rapla/generated-src/org/rapla/RaplaResources_pl.java b/Rapla/generated-src/org/rapla/RaplaResources_pl.java new file mode 100644 index 0000000..b567c26 --- /dev/null +++ b/Rapla/generated-src/org/rapla/RaplaResources_pl.java @@ -0,0 +1,383 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class RaplaResources_pl extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "abort","Anuluj"} + , { "add","Dodaj"} + , { "additional-view","Dodatkowe informacje"} + , { "admin","Administrator"} + , { "admin.login","Masz uprawnienia administratora."} + , { "administration","Administracja"} + , { "all-day","24h"} + , { "all_filtered","Wszystkie filtrowane obiekty."} + , { "all_users","Wszyscy"} + , { "allocatable_in_timeframe","Dost\u0119pne w okresie"} + , { "allocation_view","Przyporz\u0105dkowanie {0}"} + , { "alteration","Zmiana"} + , { "and","i"} + , { "apply","Zastosuj"} + , { "appointment","Termin"} + , { "appointment.convert","Przekszta\u0142\u0107 w wydarzenia jednorazowe"} + , { "appointment.day_x","dnia x"} + , { "appointment.days","Dzie\u0144(dni)"} + , { "appointment.exception.days","Wy\u0142\u0105czone dni:"} + , { "appointment.exception.general","Wyj\u0105tki:"} + , { "appointment.exceptions","Wyj\u0105tki"} + , { "appointment.format.exceptions","{0} Wyj\u0105tki:"} + , { "appointment.hours","Godzina(-y)"} + , { "appointment.minutes","min."} + , { "appointment.next_day","Nast\u0119pny dzie\u0144"} + , { "appointment.no_exceptions","Bez wyj\u0105tk\u00f3w:"} + , { "appointment.rule","Regu\u0142a:"} + , { "appointment.same_day","Ten sam dzie\u0144"} + , { "appointment_list","Lista termin\u00f3w"} + , { "appointments","Terminy"} + , { "attribute","Atrybut"} + , { "attributes","Atrybuty"} + , { "available","Dost\u0119pne"} + , { "back","Wstecz"} + , { "calendar","Kalendarz"} + , { "calendar_settings","ustawienia Kalendarz"} + , { "calendarname","Nazwa kalendarza:"} + , { "calendarweek.abbreviation","T{0,date,w}"} + , { "cancel","Anuluj"} + , { "categories","Kategorie"} + , { "category","Kategoria"} + , { "change","Zmie\u0144"} + , { "change.format","Zmie\u0144 {0}"} + , { "change_appointment","Edytuj termin"} + , { "changes","Zmiany"} + , { "choose_language","Wybierz j\u0119zyk:"} + , { "choose_root_category","Wybierz kategori\u0119 g\u0142\u00f3wn\u0105 (root):"} + , { "class","Klasa"} + , { "classification","Klasyfikacja"} + , { "close","Zamknij"} + , { "color","Kolor"} + , { "complete_time","Ca\u0142kowity czas"} + , { "confirm","Akceptuj"} + , { "confirm-close.ok","Ignoruj zmiany"} + , { "confirm-close.question","Nie zapisano zmian."} + , { "confirm-close.title","Nie zapisano zmian!"} + , { "confirm.dialog.question","Zapisa\u0107 te informacje?"} + , { "confirm.dialog.title","Potwierd\u017a {0}"} + , { "conflict.appointment1","Kiedy?"} + , { "conflict.appointment2","Konflikt przydzia\u0142u"} + , { "conflict.reservation1","Kt\u00f3re wydarzenie?"} + , { "conflict.reservation2","Inne wydarzenie?"} + , { "conflict.resource","Kto/co?"} + , { "conflict.user","U\u017cytkownik?"} + , { "conflictUC","Konflikty ({0,number,integer})"} + , { "conflicts","Konflikty"} + , { "connection","Po\u0142\u0105czenie z serwerem"} + , { "constraints","Ograniczenia"} + , { "continue","Kontynuuj"} + , { "copy","Kopiuj"} + , { "copy_appointment.format","Skopiowa\u0107 ca\u0142\u0105 seri\u0119, czy tylko wybrany termin {0}?"} + , { "copy_to_clipboard","Kopiuj do schowka"} + , { "create","Utw\u00f3rz"} + , { "created_at","Stworzono"} + , { "customized","Dostosowany"} + , { "daily","Codziennie"} + , { "database","Baza danych"} + , { "date","Data"} + , { "day","Dzie\u0144"} + , { "days","Dni"} + , { "default","Domy\u015blne"} + , { "delete","Usu\u0144"} + , { "delete.abort","Anulowa\u0107"} + , { "delete.format","Usu\u0144 {0}"} + , { "delete.ok","Tak, usu\u0144!"} + , { "delete.question","Nast\u0119puj\u0105ce elementy zostan\u0105 usuni\u0119te:"} + , { "delete.title","Usun\u0105\u0107?"} + , { "delete_appointment.format","Usun\u0105\u0107 ca\u0142\u0105 seri\u0119, czy tylko wybrany termin {0}?"} + , { "delete_selection","Usu\u0144"} + , { "destination","Miejsce docelowe"} + , { "display_exceptions","Poka\u017c wyj\u0105tki"} + , { "duration","Czas trwania"} + , { "dynamictype","Typ"} + , { "dynamictype.annotation.nameformat","Format wy\u015bwietlania"} + , { "dynamictype.annotation.nameformat.description","Nazwy klucza powinny znajdowa\u0107 si\u0119 w {} np. {klucz}."} + , { "dynamictype.name","Nazwa typu"} + , { "edit","Edytuj"} + , { "edit-view","Widok edycji"} + , { "edit.format","Edytuj {0}"} + , { "edit_advanced","Poka\u017c zaawansowane ustawienia"} + , { "edit_reservation.format","Edytuj wydarzenie: {0}"} + , { "edit_reservations","Edytuj swoje"} + , { "elementkey","Klucz"} + , { "email","E-mail"} + , { "end_date","Data zako\u0144czenia"} + , { "end_time","Czas zako\u0144czenia"} + , { "error","B\u0142\u0105d"} + , { "error.cant_delete_your_account","Nie mo\u017cesz usun\u0105\u0107 [{0}], poniewa\u017c obecnie u\u017cywasz tego konta!"} + , { "error.connect","Nie uda\u0142o si\u0119 po\u0142\u0105czy\u0107 z {0}! By\u0107 mo\u017ce host jest wy\u0142\u0105czony. Spr\u00f3buj ponownie p\u00f3\u017aniej."} + , { "error.connection_closed","Brak lub utracone po\u0142\u0105czenie z {0}. Uruchom ponownie Rapla."} + , { "error.dependencies","Istniej\u0105 zale\u017cno\u015bci dla tego obiektu"} + , { "error.invalid_key","\"{0}\" jest nieprawid\u0142owym kluczem!" + + " Mo\u017cesz u\u017cy\u0107 tylko liter, cyfr oraz jednego z tych znak\u00f3w {0}. Klucz musi zaczyna\u0107 si\u0119 od litery." + + " "} + , { "error.login","B\u0142\u0105d logowania"} + , { "error.new_version","Nie mo\u017cna zmodyfikowa\u0107 {0}. Obiekt zosta\u0142 niedawno zmieniony przez inn\u0105 osob\u0119. Istnieje nowsza wersja obiektu w pami\u0119ci."} + , { "error.no_appointment","Wydarzenie musi mie\u0107 cho\u0107 jeden termin."} + , { "error.no_entry_for","Musisz co\u015b wpisa\u0107 w: {0}"} + , { "error.no_key","Musisz poda\u0107 klucz: {0}"} + , { "error.no_name","Musisz wpisa\u0107 nazw\u0119!"} + , { "error.no_reservation_name","Podaj nazw\u0119 dla wydarzenia!"} + , { "error.no_rollback","B\u0142\u0105d! Aktualizacja bazy nie powiod\u0142a si\u0119. Twoja baza nie obs\u0142uguje transakcji, co mo\u017ce prowadzi\u0107 do powa\u017cnych b\u0142\u0119d\u00f3w. Skontaktuj si\u0119 ze swoim administratorem."} + , { "error.not_unique","Nazwa \"{0}\" jest ju\u017c zaj\u0119ta. Wybierz inn\u0105."} + , { "error.one_type_requiered","Przynajmniej jeden typ jest wymagany."} + , { "error.passwords_dont_match","Has\u0142a nie zgadzaj\u0105 si\u0119."} + , { "error.reference_not_stored","Nie mo\u017cna znale\u017a\u0107 obiektu [{0}]. Prawdopodobnie zosta\u0142 niedawno usuni\u0119ty."} + , { "error.rollback","B\u0142\u0105d aktualizacji bazy. Wszystkie zmiany zosta\u0142y utracone."} + , { "error.wrong_password","B\u0142\u0119dne has\u0142o!"} + , { "every_appointment","Ka\u017cdy termin"} + , { "everytime","Zawsze"} + , { "exception","Wyj\u0105tek"} + , { "exchange_allocatables","Zmie\u0144 przydzia\u0142"} + , { "exclamation.format","{0} !"} + , { "exclude_days","Wy\u0142\u0105cz dni:"} + , { "exit","Wyj\u015bcie"} + , { "exit.abort","Wr\u00f3\u0107 do Rapla"} + , { "exit.ok","Wyjd\u017a"} + , { "exit.question","Czy na pewno chcesz zako\u0144czy\u0107 Rapla?"} + , { "exit.title","Zako\u0144czy\u0107 Rapla?"} + , { "expected_columns","Liczba kolumn"} + , { "expected_rows","Liczba wierszy"} + , { "export","Eksportuj"} + , { "file","Plik"} + , { "filter","Filtr"} + , { "filter.contains","zawiera"} + , { "filter.earlier_than","przed"} + , { "filter.equals","dok\u0142adnie"} + , { "filter.greater_or_equals","wi\u0119ksze ni\u017c lub r\u00f3wna"} + , { "filter.is_greater_than","wi\u0119ksze ni\u017c"} + , { "filter.is_smaller_than","mniejsze ni\u017c"} + , { "filter.later_than","po"} + , { "filter.not_equals","nier\u00f3wny"} + , { "filter.smaller_or_equals","mniejsze ni\u017c lub r\u00f3wna"} + , { "filter_allocatable","Zasoby i osoby"} + , { "filter_reservation.all","Od wszystkich"} + , { "filter_reservation.own","Moje wydarzenia"} + , { "filter_restrictions","Ogranicz widok {0}"} + , { "filtername","Nazwa filtra"} + , { "fixed_date","Ustalona data"} + , { "for","dla / na"} + , { "forename","Imi\u0119"} + , { "format.repeat_from","od {0}"} + , { "format.repeat_n_times","Powt\u00f3rz {0} razy"} + , { "format.repeat_until","do {0}"} + , { "goto_date","Id\u017a do daty"} + , { "group","Grupa"} + , { "groups","Grupy"} + , { "help","Pomoc"} + , { "hierarchy","Hierarchia"} + , { "holdbackconflicts","Ignoruj konflikty"} + , { "hour","Godzina"} + , { "hours","Godziny"} + , { "in_period.format","w okresie {0}"} + , { "including_date","w tym dniu: {0}"} + , { "info","O Rapla"} + , { "info.text","" + + " Rapla - wersja 1.4.3-beta" + + "
" + + " (C) Copyright 2000-2011 Rapla Team" + + "

strona Rapla: http://rapla.org" + + "

" + + "

Pytania, sugestie, feedback:
" + + " rapla-developers@lists.sourceforge.net" + + "

" + + "

Build-Time: 2011-10-05 13:41

" + + "

Podpis: {0}

" + + "

Java - wersja {1}

" + + " "} + , { "insert","Wstaw"} + , { "interval","Interwa\u0142"} + , { "interval.format","Co {0} {1}"} + , { "keep","Zachowaj"} + , { "key","Klucz"} + , { "language","J\u0119zyk"} + , { "last_changed","Ostatnia zmiana"} + , { "last_changed_by","Zmienione przez"} + , { "legend","Legenda"} + , { "level","Poziom"} + , { "licensedialog.title","Licencja GNU"} + , { "list.format","{0}: {1}"} + , { "load","Wczytaj"} + , { "login","Logowanie"} + , { "logout","Wyloguj"} + , { "main-view","Widok g\u0142\u00f3wny"} + , { "month","Miesi\u0105c"} + , { "monthly","Co miesi\u0105c"} + , { "months","Miesi\u0105ce"} + , { "move","Przenie\u015b"} + , { "move_appointment.format","Przenie\u015b\u0107 ca\u0142\u0105 seri\u0119, czy tylko wybrany termin {0}?"} + , { "name","Nazwa"} + , { "never","Nigdy"} + , { "new","Nowy"} + , { "new_appointment","Nowy termin"} + , { "new_category","Nowa kategoria"} + , { "new_password","Nowe has\u0142o"} + , { "new_reservation","Nowe wydarzenie"} + , { "new_reservation.format","Nowe wydarzenie: {0}"} + , { "new_rule","Nowa regu\u0142a dla"} + , { "new_sub-category","Nowa podkategoria"} + , { "no","Nie"} + , { "no-view","niewidoczny"} + , { "no_classification","Brak klasyfikacji"} + , { "no_repeating","Jednorazowe"} + , { "not_selected.help","Nie wybrano.
Odznacz opcj\u0119 \"Poka\u017c tylko moje rezerwacje\" lub zmie\u0144 ustawienia filtru."} + , { "not_visible","Ukryty"} + , { "not_visible.help","Ukryty.
Dost\u0119p do informacji o rezerwacji jest zastrze\u017cony."} + , { "nothing_selected","Nic nie wybrano"} + , { "occupation","Zaw\u00f3d"} + , { "ok","OK"} + , { "old_password","Stare has\u0142o"} + , { "only_own_reservations","Tylko moje rezerwacje"} + , { "open","Otw\u00f3rz"} + , { "options","Opcje"} + , { "or","lub"} + , { "password","Has\u0142o"} + , { "password_verification","Weryfikacja has\u0142a"} + , { "paste","Wklej"} + , { "paste_as","Wklej jako"} + , { "period","Okres"} + , { "period.format.end","Koniec {0}"} + , { "period.format.start","Pocz\u0105tek {0}"} + , { "period.format.week","{0} tyg. {1}"} + , { "period.not_set","Wybierz okres"} + , { "period_view","Okres"} + , { "periods","Okresy"} + , { "permission.access","Dost\u0119pne"} + , { "permission.admin","Uprawnienia administratora"} + , { "permission.allocate","mo\u017ce przydziela\u0107"} + , { "permission.allocate-conflicts","mo\u017ce przydziela\u0107 i tworzy\u0107 konflikty"} + , { "permission.denied","Zastrze\u017cone"} + , { "permission.read","mo\u017ce odczyta\u0107"} + , { "permissions","Uprawnienia"} + , { "person","Osoba"} + , { "person_type","Typ osoby"} + , { "person_types","Typy os\u00f3b"} + , { "persons","Osoby"} + , { "preferences","Preferencje"} + , { "print","Drukuj"} + , { "print_to_file","Drukuj do pliku"} + , { "publish","Publikuj"} + , { "question","Pytanie"} + , { "rapla.name","Rapla"} + , { "rapla.title","Rapla, inteligentne planowanie"} + , { "rapla.welcome","Witaj {0}."} + , { "refresh","Od\u015bwie\u017c"} + , { "reload_data","Od\u015bwie\u017c dane"} + , { "remove","Usu\u0144"} + , { "repeat","Powt\u00f3rz"} + , { "repeating","Powtarzaj\u0105ce si\u0119:"} + , { "repeating.end_date","do"} + , { "repeating.forever","Bez ko\u0144ca"} + , { "repeating.interval.post","."} + , { "repeating.interval.pre","Ka\u017cdy"} + , { "repeating.n_times","x razy"} + , { "repeating.start_date","od"} + , { "report","Raport"} + , { "reservation","Wydarzenie"} + , { "reservation.allocations","Przydzia\u0142 zasob\u00f3w:"} + , { "reservation.appointments","Terminy:"} + , { "reservation.create_without_wizard","Wydarzenie"} + , { "reservation.name","Nazwa wydarzenia"} + , { "reservation.owner","Rezerwacja przez"} + , { "reservation_type","Wydarzenia"} + , { "reservations","Wydarzenia"} + , { "reservations_from_all_users","Rezerwacje wszystkich"} + , { "reset","Resetuj"} + , { "resource","Zas\u00f3b"} + , { "resource.owner","Zarejestrowany przez"} + , { "resource_type","Rodzaj zasobu"} + , { "resource_types","Rodzaje zasob\u00f3w"} + , { "resources","Zasoby"} + , { "resources_persons","Zasoby i osoby"} + , { "restart_client","Restart Rapla"} + , { "restart_options","Zmiany zostan\u0105 wprowadzone po restarcie Rapla"} + , { "restart_server","Restart serwera"} + , { "rows_per_hour","Podzia\u0142ka godzin"} + , { "save","Zapisz"} + , { "search","Szukaj"} + , { "second","Sekunda"} + , { "seconds","Sekundy"} + , { "select","Zaznacz"} + , { "selectable","Dost\u0119pne"} + , { "selectable_on","Dost\u0119pne w"} + , { "selected","Wybrane"} + , { "selected_on","Wybrane na"} + , { "selection","Zaznaczenie"} + , { "selection_resource","Zaznaczenie zasobu"} + , { "serie","Serie"} + , { "server","Serwer"} + , { "show_as","Wy\u015bwietlaj jako"} + , { "show_conflicts","Poka\u017c konflikty"} + , { "show_tips","Pokazuj podpowiedzi"} + , { "single_appointment","Jeden termin"} + , { "source","\u0179r\u00f3d\u0142o"} + , { "start_date","Data rozpocz\u0119cia"} + , { "start_time","Czas rozpocz\u0119cia"} + , { "surname","Nazwisko"} + , { "switch_back","Wr\u00f3\u0107"} + , { "switch_to","Prze\u0142\u0105cz na"} + , { "table","Tabela"} + , { "time_at","o"} + , { "time_until","do"} + , { "today","Dzi\u015b"} + , { "total_occurances","Suma wyst\u0105pie\u0144"} + , { "translation","T\u0142umaczenie"} + , { "translation.format","T\u0142umaczenia na \"(0)\":"} + , { "type","Typ"} + , { "type.boolean","Tak|Nie"} + , { "type.date","Data"} + , { "type.int","Liczba"} + , { "type.rapla:category","Kategoria"} + , { "type.string","Tekst"} + , { "types","Typy"} + , { "user","U\u017cytkownik"} + , { "username","Nazwa u\u017cytkownika"} + , { "users","U\u017cytkownicy"} + , { "view","Widok"} + , { "warning","Uwaga"} + , { "warning.conflict","UWAGA: Wykryto konflikt!"} + , { "warning.duplicated_appointments","UWAGA: Ten sam termin wyst\u0119puje kilkakrotnie [{0}]!"} + , { "warning.experienced_users_only","Nie kombinuj, chyba, \u017ce wiesz, co robisz ;)"} + , { "warning.max-one-eventtype","Tylko 1 zdarzenie typu jest dozwolone. Ustaw filtr!"} + , { "warning.no_allocatables_selected","Nie wybrano \u017cadnych zasob\u00f3w/os\u00f3b!"} + , { "warning.no_conflict_permission","Nie masz uprawnie\u0144 do tworzenia konflikt\u00f3w w [{0}]!"} + , { "warning.no_reserve_permission","Nie masz uprawnie\u0144 do zmiany/ustalenia [{0}] terminu [{1}]! "} + , { "warning.period_shorter_than_week","Okres kr\u00f3tszy ni\u017c tydzie\u0144."} + , { "warning.readonly_storage","Dane zmienione tylko w pami\u0119ci. Zmiany zostan\u0105 utracone."} + , { "warning.reservation.delete","Edytowane wydarzenie zosta\u0142o usuni\u0119te."} + , { "warning.reservation.update","Edytowane wydarzenie zosta\u0142o zmienione." + + " Dane w tym oknie zostan\u0105 zaktualizowane."} + , { "week","Tydzie\u0144"} + , { "week_compact","Tydzie\u0144 (skr\u00f3cony)"} + , { "weekday","Dzie\u0144 tygodnia"} + , { "weekly","Co tydzie\u0144"} + , { "weeks","Tygodnie"} + , { "weekview.print.choose_export","Wybierz metod\u0119 zapisu!"} + , { "weekview.print.dialog_title","Podgl\u0105d wydruku"} + , { "weekview.print.format_button","Ustawienia drukarki"} + , { "weekview.print.postscript","Postscript"} + , { "weekview.print.title_textfield","Tytu\u0142"} + , { "width","Szeroko\u015b\u0107"} + , { "with","z"} + , { "x_days_advance","x dni wcze\u015bniej"} + , { "year","Rok"} + , { "yearly","Co rok"} + , { "years","Lata"} + , { "yes","Tak"} + , { "zero_appointment","Brak rezerwacji"} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/autoexport/AutoExportResources.java b/Rapla/generated-src/org/rapla/plugin/autoexport/AutoExportResources.java new file mode 100644 index 0000000..f78df18 --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/autoexport/AutoExportResources.java @@ -0,0 +1,21 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.autoexport; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class AutoExportResources extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "autoexport.description","Publish calendar to HTML"} + , { "autoexport.option.description","The following views are currently stored:"} + , { "calendar_list","Exported calendars"} + , { "file.enter_name","Name for the view"} + , { "show_navigation","Show navigation bar"} + , { "webserver","Web Server"} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/autoexport/AutoExportResources_de.java b/Rapla/generated-src/org/rapla/plugin/autoexport/AutoExportResources_de.java new file mode 100644 index 0000000..7a9fa16 --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/autoexport/AutoExportResources_de.java @@ -0,0 +1,21 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.autoexport; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class AutoExportResources_de extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "autoexport.description","Ver\u00f6ffentliche Kalender nach HTML"} + , { "autoexport.option.description","Folgende Ansichten sind vorhanden:"} + , { "calendar_list","Exportierte Kalender"} + , { "file.enter_name","Name der Ansicht"} + , { "show_navigation","Zeige den Navigationsbalken"} + , { "webserver","Web-Server"} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/autoexport/AutoExportResources_es.java b/Rapla/generated-src/org/rapla/plugin/autoexport/AutoExportResources_es.java new file mode 100644 index 0000000..b7bcf68 --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/autoexport/AutoExportResources_es.java @@ -0,0 +1,21 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.autoexport; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class AutoExportResources_es extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "autoexport.description","Publicar el calendario a HTML"} + , { "autoexport.option.description","Las vistas siguientes est\u00e1n disponibles :"} + , { "calendar_list","calendarios exportados"} + , { "file.enter_name","Nombre de la vista"} + , { "show_navigation","Indicar la navegaci\u00f3n"} + , { "webserver","Webov\u00fd Server"} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/autoexport/AutoExportResources_fr.java b/Rapla/generated-src/org/rapla/plugin/autoexport/AutoExportResources_fr.java new file mode 100644 index 0000000..9accf59 --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/autoexport/AutoExportResources_fr.java @@ -0,0 +1,21 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.autoexport; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class AutoExportResources_fr extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "autoexport.description","Publier le calendrier en HTML"} + , { "autoexport.option.description","Les vues suivantes sont disponibles :"} + , { "calendar_list","Calendriers Export\u00e9s"} + , { "file.enter_name","Nom de la vue"} + , { "show_navigation","Affiche la barre de navigation"} + , { "webserver","Serveur Web"} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/autoexport/AutoExportResources_nl.java b/Rapla/generated-src/org/rapla/plugin/autoexport/AutoExportResources_nl.java new file mode 100644 index 0000000..c67fef9 --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/autoexport/AutoExportResources_nl.java @@ -0,0 +1,19 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.autoexport; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class AutoExportResources_nl extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "calendar_list","Gepubliceerde Planningen"} + , { "file.enter_name","Naam"} + , { "show_navigation","Navigatie balk"} + , { "webserver","Web Server"} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/export2ical/Export2iCalResources.java b/Rapla/generated-src/org/rapla/plugin/export2ical/Export2iCalResources.java new file mode 100644 index 0000000..930b37a --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/export2ical/Export2iCalResources.java @@ -0,0 +1,32 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.export2ical; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class Export2iCalResources extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "available_autoexport_views_text","Available views based on html export:"} + , { "available_ical_views_text","Available iCal views:"} + , { "cancel_text","Cancel"} + , { "created_succesfully_text","Successfully created."} + , { "delete_text","Delete"} + , { "export_file_succesfull_text","The iCal file has been successfully created."} + , { "export_file_text","Export view to iCal file"} + , { "export_ical_text","Publish calendar to iCal"} + , { "export_text","Export"} + , { "ical_view_name_text","iCal view name:"} + , { "no_dates_text","No dates available in the current view."} + , { "no_name_text","No name is given."} + , { "previous_days_text","previous days:"} + , { "save_title_text","Save iCal view"} + , { "subsequent_days_text","subsequent days:"} + , { "use_user_interval_setting_text","use user interval setting"} + , { "user_interval_setting_text","User interval setting:"} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/export2ical/Export2iCalResources_de.java b/Rapla/generated-src/org/rapla/plugin/export2ical/Export2iCalResources_de.java new file mode 100644 index 0000000..f7db658 --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/export2ical/Export2iCalResources_de.java @@ -0,0 +1,32 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.export2ical; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class Export2iCalResources_de extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "available_autoexport_views_text","Verf\u00fcgbare Ansichten basierend auf HTML Export:"} + , { "available_ical_views_text","Verf\u00fcgbare iCal Ansichten:"} + , { "cancel_text","Abbrechen"} + , { "created_succesfully_text","Erfolgreich angelegt."} + , { "delete_text","L\u00f6schen"} + , { "export_file_succesfull_text","Die iCal Datei wurde erfolgreich erstellt."} + , { "export_file_text","Exportiere Ansicht in iCal File"} + , { "export_ical_text","Ver\u00f6ffentliche Kalender nach iCal"} + , { "export_text","Exportieren"} + , { "ical_view_name_text","Name der iCal Ansicht:"} + , { "no_dates_text","In der aktuellen Ansicht sind keine Termine vorhanden."} + , { "no_name_text","Kein Name gesetzt."} + , { "previous_days_text","vorhergehende Tage:"} + , { "save_title_text","Ansicht als iCal speichern"} + , { "subsequent_days_text","darauffolgende Tage:"} + , { "use_user_interval_setting_text","benutzerspezifische Intervalleinstellung verwenden"} + , { "user_interval_setting_text","Benutzerspezifische Intervalleinstellung:"} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/export2ical/Export2iCalResources_es.java b/Rapla/generated-src/org/rapla/plugin/export2ical/Export2iCalResources_es.java new file mode 100644 index 0000000..0eeda15 --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/export2ical/Export2iCalResources_es.java @@ -0,0 +1,16 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.export2ical; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class Export2iCalResources_es extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "export_ical_text","Publicar el calendario en iCal"} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/export2ical/Export2iCalResources_fr.java b/Rapla/generated-src/org/rapla/plugin/export2ical/Export2iCalResources_fr.java new file mode 100644 index 0000000..418e3b4 --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/export2ical/Export2iCalResources_fr.java @@ -0,0 +1,32 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.export2ical; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class Export2iCalResources_fr extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "available_autoexport_views_text","Les vues disponibles \u00e0 la base d`HTML Export:"} + , { "available_ical_views_text","Les vues iCal disponibles:"} + , { "cancel_text","Annuler"} + , { "created_succesfully_text","Cr\u00e9\u00e9 avec succ\u00e8s."} + , { "delete_text","Effacer"} + , { "export_file_succesfull_text","Le fichier iCal a \u00e9t\u00e9 cr\u00e9\u00e9 avec succ\u00e8s."} + , { "export_file_text","Exporte la vue en iCal File"} + , { "export_ical_text","Publier le calendrier en iCal"} + , { "export_text","Exporter"} + , { "ical_view_name_text","Nom de la vue iCal:"} + , { "no_dates_text","Pas de dates disponibles dans la vue en cours."} + , { "no_name_text","Aucun nom n'est donn\u00e9."} + , { "previous_days_text","les jours pr\u00e9c\u00e9dents:"} + , { "save_title_text","Enregistrer la vue iCal"} + , { "subsequent_days_text","les jours suivants:"} + , { "use_user_interval_setting_text","utiliser le param\u00e8tre d`intervalle utilisateur sp\u00e9cifique:"} + , { "user_interval_setting_text","le param\u00e8tre d`intervalle utilisateur sp\u00e9cifique:"} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/importusers/ImportUsersResources.java b/Rapla/generated-src/org/rapla/plugin/importusers/ImportUsersResources.java new file mode 100644 index 0000000..f4b500a --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/importusers/ImportUsersResources.java @@ -0,0 +1,16 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.importusers; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class ImportUsersResources extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "checklog","{0} of {1} errors. Consult log file"} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/importusers/ImportUsersResources_cs.java b/Rapla/generated-src/org/rapla/plugin/importusers/ImportUsersResources_cs.java new file mode 100644 index 0000000..0627b5c --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/importusers/ImportUsersResources_cs.java @@ -0,0 +1,16 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.importusers; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class ImportUsersResources_cs extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "checklog","{0} z {1} chyb. Konzultovat log souboru."} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/importusers/ImportUsersResources_de.java b/Rapla/generated-src/org/rapla/plugin/importusers/ImportUsersResources_de.java new file mode 100644 index 0000000..2d4ad3e --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/importusers/ImportUsersResources_de.java @@ -0,0 +1,16 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.importusers; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class ImportUsersResources_de extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "checklog","{0} von {1} Fehler. Consult Protokolldatei."} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/importusers/ImportUsersResources_es.java b/Rapla/generated-src/org/rapla/plugin/importusers/ImportUsersResources_es.java new file mode 100644 index 0000000..8b77e51 --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/importusers/ImportUsersResources_es.java @@ -0,0 +1,16 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.importusers; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class ImportUsersResources_es extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "checklog","{0} de {1} errores. Consulte el archivo de registro."} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/importusers/ImportUsersResources_fr.java b/Rapla/generated-src/org/rapla/plugin/importusers/ImportUsersResources_fr.java new file mode 100644 index 0000000..2d38c00 --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/importusers/ImportUsersResources_fr.java @@ -0,0 +1,16 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.importusers; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class ImportUsersResources_fr extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "checklog","{0} sur {1} erreurs. Consultez le fichier journal."} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/importusers/ImportUsersResources_nl.java b/Rapla/generated-src/org/rapla/plugin/importusers/ImportUsersResources_nl.java new file mode 100644 index 0000000..ae6d516 --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/importusers/ImportUsersResources_nl.java @@ -0,0 +1,16 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.importusers; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class ImportUsersResources_nl extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "checklog","{0} op {1} fouten. Raadpleeg logbestand."} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/notification/NotificationResources.java b/Rapla/generated-src/org/rapla/plugin/notification/NotificationResources.java new file mode 100644 index 0000000..289a58a --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/notification/NotificationResources.java @@ -0,0 +1,26 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.notification; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class NotificationResources extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "appointment.add","New appointment for [{0}]:"} + , { "appointment.change","Appointment changed for [{0}] from:"} + , { "appointment.remove","Removed appointment for [{0}]:"} + , { "complete_reservation","Complete event"} + , { "mail_body","The following allocation changes have been made:"} + , { "mail_subject","Rapla: Allocation change of [{0}]"} + , { "modified_by","Modified by {0}"} + , { "moved_to","to"} + , { "notification.option.description","Mail for every allocation change of the listed resources and persons"} + , { "notification_options","Notification options"} + , { "notify_if_owner","Also notify on modification of own events."} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/notification/NotificationResources_de.java b/Rapla/generated-src/org/rapla/plugin/notification/NotificationResources_de.java new file mode 100644 index 0000000..bef0735 --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/notification/NotificationResources_de.java @@ -0,0 +1,26 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.notification; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class NotificationResources_de extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "appointment.add","Neuer Termin f\u00fcr [{0}]:"} + , { "appointment.change","Termin ver\u00e4ndert f\u00fcr [{0}] von:"} + , { "appointment.remove","Termin f\u00fcr [{0}] gel\u00f6scht:"} + , { "complete_reservation","Komplette Veranstaltung"} + , { "mail_body","Die folgenden Belegungs\u00e4nderungen wurden vorgenommen:"} + , { "mail_subject","Rapla: Reservierungsbenachrichtigung [{0}]"} + , { "modified_by","ver\u00e4ndert durch {0}"} + , { "moved_to","nach"} + , { "notification.option.description","Mail bei Belegungs\u00e4nderung von folgenden Ressourcen und Personen"} + , { "notification_options","Benachrichtigungs Optionen"} + , { "notify_if_owner","Benachrichtige auch bei \u00c4nderungen an eigenen Veranstaltungen."} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/notification/NotificationResources_es.java b/Rapla/generated-src/org/rapla/plugin/notification/NotificationResources_es.java new file mode 100644 index 0000000..b614297 --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/notification/NotificationResources_es.java @@ -0,0 +1,25 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.notification; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class NotificationResources_es extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "appointment.add","Nueva cita para [{0}]"} + , { "appointment.change","Cambio de cita para [{0}]"} + , { "complete_reservation","Descriptci\u00f3n completa del evento"} + , { "mail_body","Se han hecho los siguientes cambios de asignaci\u00f3n:"} + , { "mail_subject","Rapla : Cambio de asignaci\u00f3n para [{0}]"} + , { "modified_by","Modificado por {0}"} + , { "moved_to","para"} + , { "notification.option.description","Env\u00edo de correo a cada cambio de asignaci\u00f3n de recursos/personas listado"} + , { "notification_options","Notificaciones"} + , { "notify_if_owner","Quiero ser notificado de los cambios hechos por m\u00ed mismo"} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/notification/NotificationResources_fr.java b/Rapla/generated-src/org/rapla/plugin/notification/NotificationResources_fr.java new file mode 100644 index 0000000..e8a37d6 --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/notification/NotificationResources_fr.java @@ -0,0 +1,26 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.notification; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class NotificationResources_fr extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "appointment.add","Nouvelle r\u00e9servation pour [{0}] :"} + , { "appointment.change","Changement d''horaire pour [{0}] c''\u00e9tait :"} + , { "appointment.remove","Se elimin\u00f3 la cita para [{0}]"} + , { "complete_reservation","Description compl\u00e8te de l'\u00e9v\u00e8nement"} + , { "mail_body","Les changement d'allocation suivants ont \u00e9t\u00e9 faits:"} + , { "mail_subject","Rapla : Changement d'allocation pour [{0}]"} + , { "modified_by","Modifi\u00e9 par {0}"} + , { "moved_to","cela devient :"} + , { "notification.option.description","Envoit de Mail \u00e0 chaque changement d'allocation des resources/personnes list\u00e9es"} + , { "notification_options","Options de notification"} + , { "notify_if_owner","Je veux \u00eatre notifi\u00e9 pour les changements faits par moi-m\u00eame"} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/occupationview/OccupationResources.java b/Rapla/generated-src/org/rapla/plugin/occupationview/OccupationResources.java new file mode 100644 index 0000000..71a4879 --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/occupationview/OccupationResources.java @@ -0,0 +1,43 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.occupationview; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class OccupationResources extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "FirstDay","Day 1 of month"} + , { "ToDay","Today"} + , { "archive","Archive {0} >= {1} days inactive"} + , { "bestFit","Best fit"} + , { "confirm","Confirm"} + , { "daysin","In"} + , { "daysout","In"} + , { "event_check","Remarks"} + , { "firstFit","First fit"} + , { "forbidden","Resource not availabe."} + , { "forcearchive","Archive {0} < {1} days inactive"} + , { "freeSlot","Free slot: "} + , { "horizon","Month +"} + , { "icon.archive","images/archive.gif"} + , { "icon.delete","images/eclipse-icons/trash.gif"} + , { "icon.edit","images/eclipse-icons/edit.gif"} + , { "icon.error","images/error.png"} + , { "icon.help","images/eclipse-icons/help.gif"} + , { "icon.new","images/eclipse-icons/new.gif"} + , { "minus1month","-1 Month"} + , { "noendoflife","_endoflive not defined."} + , { "occupation","Occupation"} + , { "options.timeshift","Options"} + , { "plus1month","+1 Month"} + , { "resourcedeleted","Deleted"} + , { "resources_persons","Resources/persons"} + , { "slotGaps","#Gaps"} + , { "startday","Start day of the Month"} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/occupationview/OccupationResources_cs.java b/Rapla/generated-src/org/rapla/plugin/occupationview/OccupationResources_cs.java new file mode 100644 index 0000000..7471cab --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/occupationview/OccupationResources_cs.java @@ -0,0 +1,36 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.occupationview; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class OccupationResources_cs extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "FirstDay","Den 1 m\u011bs\u00edc"} + , { "ToDay","Dnes"} + , { "archive","Archiv {0} >= {1} dn\u016f neaktivn\u00ed"} + , { "bestFit","...: "} + , { "confirm","Archiv nyn\u00ed"} + , { "daysin","Vstupn\u00ed"} + , { "daysout","V\u00fdstupn\u00ed"} + , { "firstFit","...: "} + , { "forbidden","...: "} + , { "forcearchive","Archiv {0} < {1} dn\u016f neaktivn\u00ed"} + , { "freeSlot","...: "} + , { "horizon","...: "} + , { "minus1month","-1"} + , { "noendoflife","Archiv nyn\u00ed"} + , { "occupation","Obsazenost"} + , { "options.timeshift","Volby"} + , { "plus1month","+1"} + , { "resourcedeleted","vypou\u0161t\u00ed"} + , { "resources_persons","Prost\u0159edky/osoby"} + , { "slotGaps","...: "} + , { "startday","Ending Reason"} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/occupationview/OccupationResources_de.java b/Rapla/generated-src/org/rapla/plugin/occupationview/OccupationResources_de.java new file mode 100644 index 0000000..efdc1eb --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/occupationview/OccupationResources_de.java @@ -0,0 +1,37 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.occupationview; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class OccupationResources_de extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "FirstDay","Tag 1 des Monats"} + , { "ToDay","Heute"} + , { "archive","Archiv {0} >= {1} Tage inaktiv"} + , { "bestFit","...: "} + , { "confirm","Archiv jetzt"} + , { "daysin","Ein"} + , { "daysout","Aus"} + , { "event_check","..."} + , { "firstFit","...: "} + , { "forbidden","...: "} + , { "forcearchive","Archiv {0} < {1} Tage inaktiv"} + , { "freeSlot","Frei: "} + , { "horizon","Monat +"} + , { "minus1month","-1 Monate"} + , { "noendoflife","Archiv jetzt"} + , { "occupation","Belegung"} + , { "options.timeshift","Einstellungen"} + , { "plus1month","+1 Monate"} + , { "resourcedeleted","Gel\u00f6scht"} + , { "resources_persons","Ressourcen/Personen"} + , { "slotGaps","...: "} + , { "startday","Ending Reason"} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/occupationview/OccupationResources_es.java b/Rapla/generated-src/org/rapla/plugin/occupationview/OccupationResources_es.java new file mode 100644 index 0000000..8b438bc --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/occupationview/OccupationResources_es.java @@ -0,0 +1,37 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.occupationview; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class OccupationResources_es extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "FirstDay","D\u00eda 1 del mes"} + , { "ToDay","Hoy"} + , { "archive","pArchivo {0} >= {1} d\u00eda inactivos"} + , { "bestFit","...: "} + , { "confirm","Archivo ahora"} + , { "daysin","Entrada"} + , { "daysout","Salida"} + , { "event_check","..."} + , { "firstFit","...: "} + , { "forbidden","...: "} + , { "forcearchive","Archivo {0} < {1} d\u00eda inactivos"} + , { "freeSlot","...: "} + , { "horizon","...: "} + , { "minus1month","-1"} + , { "noendoflife","Archivo ahora"} + , { "occupation","Ocupaci\u00f3n"} + , { "options.timeshift","Opciones"} + , { "plus1month","+1"} + , { "resourcedeleted","Eliminado"} + , { "resources_persons","Recursos/personas"} + , { "slotGaps","...: "} + , { "startday","Ending Reason"} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/occupationview/OccupationResources_fr.java b/Rapla/generated-src/org/rapla/plugin/occupationview/OccupationResources_fr.java new file mode 100644 index 0000000..991775d --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/occupationview/OccupationResources_fr.java @@ -0,0 +1,37 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.occupationview; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class OccupationResources_fr extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "FirstDay","Jour 1 du mois"} + , { "ToDay","Aujourd'hui"} + , { "archive","Archive {0} >= {1} jours inactifs"} + , { "bestFit","Mellieur libre"} + , { "confirm","Archive maintenant"} + , { "daysin","Entr\u00e9e"} + , { "daysout","Sortie"} + , { "event_check","..."} + , { "firstFit","Premier libre"} + , { "forbidden","Resource pas disponible.\""} + , { "forcearchive","Archive {0} < {1} jours inactifs"} + , { "freeSlot","Libre:"} + , { "horizon","Mois + "} + , { "minus1month","-1 Mois\""} + , { "noendoflife","Archive maintenant"} + , { "occupation","Occupation"} + , { "options.timeshift","Options"} + , { "plus1month","+1 Mois\""} + , { "resourcedeleted","Supprim\u00e9"} + , { "resources_persons","Ressources/Personnes"} + , { "slotGaps","#Gaps"} + , { "startday","Raison de fin"} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/occupationview/OccupationResources_nl.java b/Rapla/generated-src/org/rapla/plugin/occupationview/OccupationResources_nl.java new file mode 100644 index 0000000..216bb70 --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/occupationview/OccupationResources_nl.java @@ -0,0 +1,37 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.occupationview; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class OccupationResources_nl extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "FirstDay","Dag 1 van maand"} + , { "ToDay","Vandaag"} + , { "archive","Archiveer {0} >= {1} dagen inactief"} + , { "bestFit","Beste vrij"} + , { "confirm","Bevestig."} + , { "daysin","In"} + , { "daysout","Uit"} + , { "event_check","Opmerking"} + , { "firstFit","Eerst vrij"} + , { "forbidden","Middel niet beschikbaar."} + , { "forcearchive","Archiveer {0} < {1} dagen inactief"} + , { "freeSlot","Vrij: "} + , { "horizon","Maand +"} + , { "minus1month","-1 Maand"} + , { "noendoflife","_endoflife niet gedefinieerd."} + , { "occupation","Bezetting"} + , { "options.timeshift","Voorkeur"} + , { "plus1month","+1 Maand"} + , { "resourcedeleted","Geschrapt"} + , { "resources_persons","Persoon/Middel"} + , { "slotGaps","#Gaps"} + , { "startday","Start dag van de maand"} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/periodwizard/WizardResources.java b/Rapla/generated-src/org/rapla/plugin/periodwizard/WizardResources.java new file mode 100644 index 0000000..772c370 --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/periodwizard/WizardResources.java @@ -0,0 +1,65 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.periodwizard; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class WizardResources extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "enter_appointments","Enter appointments:"} + , { "error.no_period_found","" + + " No periods found. Please enter the periods which you want to use for planning first. You'll need to login as admin for this." + + " "} + , { "reservation.create_with_default_wizard","Weekly event in a period"} + , { "reservation_wizard.add_appointment","Add Appointment"} + , { "reservation_wizard.appointment_menu","Appointment Menu"} + , { "reservation_wizard.panel1","" + + " Create a regular event:
" + + "
" + + " 1. Select a period.
" + + "
" + + " 2. Choose a event-type.
" + + "
" + + " 3. Enter the event information.
" + + "
" + + " 4. Proceed to the appointments-menu.
" + + " "} + , { "reservation_wizard.panel2","" + + " Enter known appointments:
" + + " Fill in the day of the week and the time of the appointments.
" + + "
" + + " Search for free appointments:
" + + " Mark the resource or persons for which you are searching a free appointment in" + + " the left table and press \"Search free appointment\".
" + + "
" + + " Selecting resources and persons:
" + + " Mark the persons and resources you want in the left table." + + " Double-click to add them to the right table and to assign them" + + " to all entered appointments. If you want to assign a" + + " resource or person to a particular appointment, click in the" + + " right column next to the corresponding entry in the right table." + + " "} + , { "reservation_wizard.panel3","" + + " Search for appointments:
" + + " Select with your mouse a free appointment in the wee kview." + + " Click \"Add Appointment\" to create the appointment" + + " and return to the previous menu." + + "
" + + "
" + + " There you can assign the resource or person to the appointment." + + " "} + , { "reservation_wizard.search_free_appointment","Search for a free appointment"} + , { "reservation_wizard.title","Wizard for regular events"} + , { "reservation_wizard.weekview","{0}: {1}"} + , { "select_persons_and_resources","Select resources and persons:"} + , { "warning.need_resource_or_person","" + + " You have to mark at least one resource or person" + + " to search for a free appointment!" + + " "} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/periodwizard/WizardResources_de.java b/Rapla/generated-src/org/rapla/plugin/periodwizard/WizardResources_de.java new file mode 100644 index 0000000..5c7a935 --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/periodwizard/WizardResources_de.java @@ -0,0 +1,64 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.periodwizard; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class WizardResources_de extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "enter_appointments","Eingabe der Termine:"} + , { "error.no_period_found","" + + " Keine Zeitr\u00e4ume gefunden. Bitte geben Sie zuerst die Zeitr\u00e4ume ein, f\u00fcr die Sie planen m\u00f6chten. Sie m\u00fcssen sich hierf\u00fcr als admin einloggen." + + " "} + , { "reservation.create_with_default_wizard","W\u00f6chentliche Veranstaltung in einem Zeitraum"} + , { "reservation_wizard.add_appointment","Termin hinzuf\u00fcgen"} + , { "reservation_wizard.appointment_menu","Termineingabe"} + , { "reservation_wizard.panel1","" + + " Anlegen einer regelm\u00e4\u00dfigen Veranstaltung:
" + + "
" + + " 1. Zeitraum in dem die Reservierung stattfindet ausw\u00e4hlen.
" + + "
" + + " 2. Veranstaltungstyp ausw\u00e4hlen.
" + + "
" + + " 3. Eingabe der Veranstaltungsdaten.
" + + "
" + + " 4. Weiter zur Termineingabe.
" + + " "} + , { "reservation_wizard.panel2","" + + " Eingabe von bekannten Terminen:
" + + " Geben Sie den Wochentag und die Uhrzeit der Termine ein.
" + + "
" + + " Suche nach freien Terminen:
" + + " Markieren Sie die Ressourcen oder Personen f\u00fcr die Sie einen freien Termin suchen" + + " in der linken Tabelle und dr\u00fccken Sie auf \"Freien Termin suchen\".
" + + "
" + + " Ausw\u00e4hlen von Ressourcen und Personen:
" + + " Markieren Sie die gew\u00fcnschten Personen und Ressourcen in der linken Tabelle." + + " Mit einen Doppelklick werden die Eintr\u00e4ge in die rechte Tabelle \u00fcbernommen und" + + " allen eingegebenen Termine zugewiesen. Wenn Sie eine" + + " Ressource oder Person nur einem bestimmten Termin zuweisen wollen, klicken Sie" + + " in die rechte Spalte neben dem entsprechenden Eintrag in der rechten Tabelle." + + " "} + , { "reservation_wizard.panel3","" + + " Terminsuche:
" + + " W\u00e4hlen Sie mit der Maus ein freies Zeitfenster in der Wochenansicht aus." + + " Dr\u00fccken Sie \"Termin hinzuf\u00fcgen\", um den Termin anzulegen und zum" + + " vorherigen Men\u00fc zur\u00fcckzukehren.
" + + "
" + + " Sie k\u00f6nnen dort die gew\u00fcnschte Ressource oder Person dem Termin zuweisen." + + " "} + , { "reservation_wizard.search_free_appointment","Freien Termin suchen"} + , { "reservation_wizard.title","Assistent zum Anlegen einer regelm\u00e4\u00dfigen Veranstaltung"} + , { "reservation_wizard.weekview","{0}: {1}"} + , { "select_persons_and_resources","Ausw\u00e4hlen von Ressourcen und Personen:"} + , { "warning.need_resource_or_person","" + + " Sie m\u00fcssen mindestens eine Ressource oder Person" + + " f\u00fcr die Sie den freien Termin suchen markieren!" + + " "} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/periodwizard/WizardResources_es.java b/Rapla/generated-src/org/rapla/plugin/periodwizard/WizardResources_es.java new file mode 100644 index 0000000..1e4dc15 --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/periodwizard/WizardResources_es.java @@ -0,0 +1,63 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.periodwizard; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class WizardResources_es extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "enter_appointments","Introducci\u00f3n de citas :"} + , { "error.no_period_found","No existen periodos. Comience por definir los periodos del a\u00f1o que se usar\u00e1n en las planificaciones. Para ello, necesitar\u00e1 conectarse como usuario admin."} + , { "reservation.create_with_default_wizard","Evento semanal durante todo un periodo"} + , { "reservation_wizard.add_appointment","A\u00f1adir cita"} + , { "reservation_wizard.appointment_menu","Creaci\u00f3n de citas"} + , { "reservation_wizard.panel1","" + + " Creaci\u00f3n de eventos semanales:
" + + "
" + + " 1. Seleccione el periodo del a\u00f1o.
" + + "
" + + " 2. Escoja el tipo de evento.
" + + "
" + + " 3. Introduzca la informaci\u00f3n del evento.
" + + "
" + + " 4. Pase a la ventana de creaci\u00f3n de citas.
" + + " "} + , { "reservation_wizard.panel2","" + + " Introduzca las citas que conoce:
" + + " Indique el d\u00eda de la semana y la hora de inicio y de fin de la cita.
" + + "
" + + " B\u00fasqueda de citas disponibles:
" + + " Seleccione en la tabla de la izquierda" + + " los recursos y las personas para los cuales" + + " desee buscar una cita disponible" + + " y pulse sobre \"Buscar una cita disponible\".
" + + "
" + + " Seleccione los recursos y las personas:
" + + " Seleccione en la tabla de la izquierda" + + " los recursos y las personas y pulse el bot\u00f3n \"A\u00f1adir\"." + + " Se a\u00f1adir\u00e1n para todas las citas." + + " Para asignar un recurso o persona a una cita" + + " particular, pulse sobre el bot\u00f3n que aparece a continuaci\u00f3n" + + " del recurso o persona en la tabla de la derecha." + + " "} + , { "reservation_wizard.panel3","" + + " B\u00fasqueda de citas :
" + + " Seleccione con el rat\u00f3n una cita disponible durante la semana." + + " Pulse \"A\u00f1adir cita\" para a\u00f1adir la cita" + + " y volver a la ventana previa." + + "
" + + "
" + + " All\u00ed podr\u00e1 asignar recursos o personas a la cita." + + " "} + , { "reservation_wizard.search_free_appointment","Buscar una cita disponible"} + , { "reservation_wizard.title","Asistente de creaci\u00f3n de eventos semanales"} + , { "reservation_wizard.weekview","{0}: {1}"} + , { "select_persons_and_resources","Selecci\u00f3n de recursos y personas :"} + , { "warning.need_resource_or_person","\u00a1Debe seleccionar al menos un recurso o persona para el cual (o los cuales) desea buscar una cita disponible!"} + }; +} diff --git a/Rapla/generated-src/org/rapla/plugin/periodwizard/WizardResources_fr.java b/Rapla/generated-src/org/rapla/plugin/periodwizard/WizardResources_fr.java new file mode 100644 index 0000000..f4e8b1f --- /dev/null +++ b/Rapla/generated-src/org/rapla/plugin/periodwizard/WizardResources_fr.java @@ -0,0 +1,64 @@ +/******************************************* + * Autogenerated file. Please do not edit. * + * Edit the *Resources.xml file. * + *******************************************/ + +package org.rapla.plugin.periodwizard; +import java.util.ListResourceBundle; +import java.util.ResourceBundle; + +public class WizardResources_fr extends ListResourceBundle { + public Object[][] getContents() { return contents; } + public void setParent(ResourceBundle parent) { super.setParent(parent); } + static final Object[][] contents = { {"",""} + , { "enter_appointments","Ajouter des cr\u00e9naux :"} + , { "error.no_period_found","Pas de p\u00e9riode. Commencez par d\u00e9finir des p\u00e9riodes dans l'ann\u00e9e. Vous avez besoin de vous connecter comme 'admin' pour le faire"} + , { "reservation.create_with_default_wizard","\u00c9v\u00e8nement hebdomadaire pendant toute une p\u00e9riode"} + , { "reservation_wizard.add_appointment","Ajouter un cr\u00e9nau"} + , { "reservation_wizard.appointment_menu","Cr\u00e9ation des cr\u00e9naux"} + , { "reservation_wizard.panel1","" + + " Cr\u00e9ation d'\u00e9v\u00e8nement hebdomadaire
" + + "
" + + " 1. Choisissez la p\u00e9riode de l'ann\u00e9e.
" + + "
" + + " 2. Choisissez le type de l'\u00e9v\u00e8nement.
" + + "
" + + " 3. Entrez les informations concernant l'\u00e9v\u00e8nement.
" + + "
" + + " 4. Passez \u00e0 la fen\u00eatre de d\u00e9finition des cr\u00e9naux.
" + + " "} + , { "reservation_wizard.panel2","" + + " Indiquer les cr\u00e9naux que vous connaissez :
" + + " Indiquez le jour de la semaine et l'heure de d\u00e9but et de fin du cr\u00e9nau.
" + + "
" + + " Chercher des cr\u00e9naux libres :
" + + " S\u00e9lectionnez dans la table de gauche" + + " les ressources et personnes pour lesquelles vous" + + " voulez rechercher un cr\u00e9nau libre." + + " Puis cliquez sur \"Chercher un cr\u00e9nau libre\".
" + + "
" + + " Selectionner les ressources et personnes :
" + + " S\u00e9lectionnez dans la table de gauche" + + " les ressources et personnes puis cliquez sur \"Ins\u00e9rer\"" + + " Elle seront assign\u00e9es pour tous les cr\u00e9naux." + + " Pour assigner une ressource ou personne \u00e0 des cr\u00e9naux" + + " particuliers, cliquez sur le bouton \u00e0 cot\u00e9 de la ressource" + + " ou personne dans la table de droite." + + " "} + , { "reservation_wizard.panel3","" + + " Chercher des cr\u00e9naux :
" + + " S\u00e9lectionnez avec la souris un cr\u00e9nau libre" + + " dans la semaine." + + " Cliquez sur \"Ajouter cr\u00e9naux\" pour" + + " ajouter le cr\u00e9naux et revenir sur la fen\u00eatre pr\u00e9c\u00e9dente." + + "
" + + " Ainsi vous pouvez assigner la ressource ou la personne" + + " aux cr\u00e9naux en cliquant sur 'ins\u00e9rer'" + + " "} + , { "reservation_wizard.search_free_appointment","Chercher un cr\u00e9nau libre"} + , { "reservation_wizard.title","Assistant de cr\u00e9ation d'\u00e9v\u00e8nement hebdomadaire"} + , { "reservation_wizard.weekview","{0}: {1}"} + , { "select_persons_and_resources","S\u00e9lection des ressources et personnes :"} + , { "warning.need_resource_or_person","Vous devez au moins s\u00e9lectionner une resource ou personne pour laquelle (ou lesquelles) vous voulez rechercher un cr\u00e9nau possible"} + }; +} diff --git a/Rapla/jetty/jetty.xml b/Rapla/jetty/jetty.xml new file mode 100644 index 0000000..045a0f1 --- /dev/null +++ b/Rapla/jetty/jetty.xml @@ -0,0 +1,104 @@ + + + + + + + + + + org.mortbay.jetty.Request.maxFormContentSize + 64000000 + + + + + + + + 5 + 6 + 50 + + + + + + + + + + + + + + + 30000 + 2 + 8443 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /webapp + / + /jetty/webdefault.xml + /webapp + + + + + + + true + + true + + diff --git a/Rapla/jetty/webdefault.xml b/Rapla/jetty/webdefault.xml new file mode 100644 index 0000000..b276a0a --- /dev/null +++ b/Rapla/jetty/webdefault.xml @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + Default web.xml file. + This file is applied to a Web application before it's own WEB_INF/web.xml file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + default + org.mortbay.jetty.servlet.DefaultServlet + + acceptRanges + true + + + dirAllowed + true + + + redirectWelcome + false + + + maxCacheSize + 2000000 + + + maxCachedFileSize + 254000 + + + maxCachedFiles + 1000 + + + gzip + false + + + useFileMappedBuffer + false + + 0 + + + default / + + + + + + + + 30 + + + + + + + + + + + + + index.html + index.htm + index.jsp + + + + + arISO-8859-6 + beISO-8859-5 + bgISO-8859-5 + caISO-8859-1 + csISO-8859-2 + daISO-8859-1 + deISO-8859-1 + elISO-8859-7 + enISO-8859-1 + esISO-8859-1 + etISO-8859-1 + fiISO-8859-1 + frISO-8859-1 + hrISO-8859-2 + huISO-8859-2 + isISO-8859-1 + itISO-8859-1 + iwISO-8859-8 + jaShift_JIS + koEUC-KR + ltISO-8859-2 + lvISO-8859-2 + mkISO-8859-5 + nlISO-8859-1 + noISO-8859-1 + plISO-8859-2 + ptISO-8859-1 + roISO-8859-2 + ruISO-8859-5 + shISO-8859-5 + skISO-8859-2 + slISO-8859-2 + sqISO-8859-2 + srISO-8859-5 + svISO-8859-1 + trISO-8859-9 + ukISO-8859-5 + zhGB2312 + zh_TWBig5 + + + + + + diff --git a/Rapla/legal/COPYING b/Rapla/legal/COPYING new file mode 100644 index 0000000..74035d4 --- /dev/null +++ b/Rapla/legal/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Rapla/legal/LIBRARIES-FAQ b/Rapla/legal/LIBRARIES-FAQ new file mode 100644 index 0000000..82f75b0 --- /dev/null +++ b/Rapla/legal/LIBRARIES-FAQ @@ -0,0 +1,21 @@ +The Rapla project uses the following Libraries from other free-software-projects: + +Library:------------------from:-------------------Get the source from:----------- + +ant.jar Apache Software Found. ant.apache.org +optional.jar + +avalon-framework-4.2.0.jar Apache Software Found. avalon.apache.org +avalon-loggin.jar Apache Software Found. avalon.apache.org + +aelfred-saxon.jar Michal Kay saxon.sf.net + +junit.jar - www.junit.org + +org.mortbay.jetty.jar Mort Bay Consulting. http://jetty.mortbay.org/jetty/index.html +javax.servlet.jar Sun API + +Most icons were taken from the eclipse project (www.eclipse.org) +Some icons were taken from +- ximian evolution www.ximian.org +- http://wm-icons.sourceforge.net/ diff --git a/Rapla/legal/LICENCE.iCal4j b/Rapla/legal/LICENCE.iCal4j new file mode 100644 index 0000000..d550e53 --- /dev/null +++ b/Rapla/legal/LICENCE.iCal4j @@ -0,0 +1,33 @@ +================== + iCal4j - License +================== + +Copyright (c) 2011, Ben Fortuna +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + o Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + + o Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + + o Neither the name of Ben Fortuna nor the names of any other contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/Rapla/legal/LICENSE.apache b/Rapla/legal/LICENSE.apache new file mode 100644 index 0000000..b23e41b --- /dev/null +++ b/Rapla/legal/LICENSE.apache @@ -0,0 +1,48 @@ +/* + * ============================================================================ + * The Apache Software License, Version 1.1 + * ============================================================================ + * + * Copyright (C) 1999 The Apache Software Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modifica- + * tion, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: "This product includes software + * developed by the Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Ant" and "Apache Software Foundation" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", nor may + * "Apache" appear in their name, without prior written permission of the + * Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- + * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This software consists of voluntary contributions made by many individuals + * on behalf of the Apache Software Foundation. For more information on the + * Apache Software Foundation, please see . + * + */ diff --git a/Rapla/legal/LICENSE.eclipse-icons b/Rapla/legal/LICENSE.eclipse-icons new file mode 100644 index 0000000..c9990a7 --- /dev/null +++ b/Rapla/legal/LICENSE.eclipse-icons @@ -0,0 +1,213 @@ +Common Public License Version 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC +LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM +CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + + a) in the case of the initial Contributor, the initial code and +documentation distributed under this Agreement, and + + b) in the case of each subsequent Contributor: + + i) changes to the Program, and + + ii) additions to the Program; + + where such changes and/or additions to the Program originate from and are +distributed by that particular Contributor. A Contribution 'originates' from a +Contributor if it was added to the Program by such Contributor itself or anyone +acting on such Contributor's behalf. Contributions do not include additions to +the Program which: (i) are separate modules of software distributed in +conjunction with the Program under their own license agreement, and (ii) are not +derivative works of the Program. + +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents " mean patent claims licensable by a Contributor which are +necessarily infringed by the use or sale of its Contribution alone or when +combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, +including all Contributors. + +2. GRANT OF RIGHTS + + a) Subject to the terms of this Agreement, each Contributor hereby grants +Recipient a non-exclusive, worldwide, royalty-free copyright license to +reproduce, prepare derivative works of, publicly display, publicly perform, +distribute and sublicense the Contribution of such Contributor, if any, and such +derivative works, in source code and object code form. + + b) Subject to the terms of this Agreement, each Contributor hereby grants +Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed +Patents to make, use, sell, offer to sell, import and otherwise transfer the +Contribution of such Contributor, if any, in source code and object code form. +This patent license shall apply to the combination of the Contribution and the +Program if, at the time the Contribution is added by the Contributor, such +addition of the Contribution causes such combination to be covered by the +Licensed Patents. The patent license shall not apply to any other combinations +which include the Contribution. No hardware per se is licensed hereunder. + + c) Recipient understands that although each Contributor grants the licenses +to its Contributions set forth herein, no assurances are provided by any +Contributor that the Program does not infringe the patent or other intellectual +property rights of any other entity. Each Contributor disclaims any liability to +Recipient for claims brought by any other entity based on infringement of +intellectual property rights or otherwise. As a condition to exercising the +rights and licenses granted hereunder, each Recipient hereby assumes sole +responsibility to secure any other intellectual property rights needed, if any. +For example, if a third party patent license is required to allow Recipient to +distribute the Program, it is Recipient's responsibility to acquire that license +before distributing the Program. + + d) Each Contributor represents that to its knowledge it has sufficient +copyright rights in its Contribution, if any, to grant the copyright license set +forth in this Agreement. + +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under its +own license agreement, provided that: + + a) it complies with the terms and conditions of this Agreement; and + + b) its license agreement: + + i) effectively disclaims on behalf of all Contributors all warranties and +conditions, express and implied, including warranties or conditions of title and +non-infringement, and implied warranties or conditions of merchantability and +fitness for a particular purpose; + + ii) effectively excludes on behalf of all Contributors all liability for +damages, including direct, indirect, special, incidental and consequential +damages, such as lost profits; + + iii) states that any provisions which differ from this Agreement are offered +by that Contributor alone and not by any other party; and + + iv) states that source code for the Program is available from such +Contributor, and informs licensees how to obtain it in a reasonable manner on or +through a medium customarily used for software exchange. + +When the Program is made available in source code form: + + a) it must be made available under this Agreement; and + + b) a copy of this Agreement must be included with each copy of the Program. + +Contributors may not remove or alter any copyright notices contained within the +Program. + +Each Contributor must identify itself as the originator of its Contribution, if +any, in a manner that reasonably allows subsequent Recipients to identify the +originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with +respect to end users, business partners and the like. While this license is +intended to facilitate the commercial use of the Program, the Contributor who +includes the Program in a commercial product offering should do so in a manner +which does not create potential liability for other Contributors. Therefore, if +a Contributor includes the Program in a commercial product offering, such +Contributor ("Commercial Contributor") hereby agrees to defend and indemnify +every other Contributor ("Indemnified Contributor") against any losses, damages +and costs (collectively "Losses") arising from claims, lawsuits and other legal +actions brought by a third party against the Indemnified Contributor to the +extent caused by the acts or omissions of such Commercial Contributor in +connection with its distribution of the Program in a commercial product +offering. The obligations in this section do not apply to any claims or Losses +relating to any actual or alleged intellectual property infringement. In order +to qualify, an Indemnified Contributor must: a) promptly notify the Commercial +Contributor in writing of such claim, and b) allow the Commercial Contributor to +control, and cooperate with the Commercial Contributor in, the defense and any +related settlement negotiations. The Indemnified Contributor may participate in +any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product +offering, Product X. That Contributor is then a Commercial Contributor. If that +Commercial Contributor then makes performance claims, or offers warranties +related to Product X, those performance claims and warranties are such +Commercial Contributor's responsibility alone. Under this section, the +Commercial Contributor would have to defend claims against the other +Contributors related to those performance claims and warranties, and if a court +requires any other Contributor to pay any damages as a result, the Commercial +Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR +IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each +Recipient is solely responsible for determining the appropriateness of using and +distributing the Program and assumes all risks associated with its exercise of +rights under this Agreement, including but not limited to the risks and costs of +program errors, compliance with applicable laws, damage to or loss of data, +programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY +CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST +PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS +GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable +law, it shall not affect the validity or enforceability of the remainder of the +terms of this Agreement, and without further action by the parties hereto, such +provision shall be reformed to the minimum extent necessary to make such +provision valid and enforceable. + +If Recipient institutes patent litigation against a Contributor with respect to +a patent applicable to software (including a cross-claim or counterclaim in a +lawsuit), then any patent licenses granted by that Contributor to such Recipient +under this Agreement shall terminate as of the date such litigation is filed. In +addition, if Recipient institutes patent litigation against any entity +(including a cross-claim or counterclaim in a lawsuit) alleging that the Program +itself (excluding combinations of the Program with other software or hardware) +infringes such Recipient's patent(s), then such Recipient's rights granted under +Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to +comply with any of the material terms or conditions of this Agreement and does +not cure such failure in a reasonable period of time after becoming aware of +such noncompliance. If all Recipient's rights under this Agreement terminate, +Recipient agrees to cease use and distribution of the Program as soon as +reasonably practicable. However, Recipient's obligations under this Agreement +and any licenses granted by Recipient relating to the Program shall continue and +survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in +order to avoid inconsistency the Agreement is copyrighted and may only be +modified in the following manner. The Agreement Steward reserves the right to +publish new versions (including revisions) of this Agreement from time to time. +No one other than the Agreement Steward has the right to modify this Agreement. +IBM is the initial Agreement Steward. IBM may assign the responsibility to serve +as the Agreement Steward to a suitable separate entity. Each new version of the +Agreement will be given a distinguishing version number. The Program (including +Contributions) may always be distributed subject to the version of the Agreement +under which it was received. In addition, after a new version of the Agreement +is published, Contributor may elect to distribute the Program (including its +Contributions) under the new version. Except as expressly stated in Sections +2(a) and 2(b) above, Recipient receives no rights or licenses to the +intellectual property of any Contributor under this Agreement, whether +expressly, by implication, estoppel or otherwise. All rights in the Program not +expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the +intellectual property laws of the United States of America. No party to this +Agreement will bring a legal action under this Agreement more than one year +after the cause of action arose. Each party waives its rights to a jury trial in +any resulting litigation. diff --git a/Rapla/legal/LICENSE.jetty b/Rapla/legal/LICENSE.jetty new file mode 100644 index 0000000..6a0d97c --- /dev/null +++ b/Rapla/legal/LICENSE.jetty @@ -0,0 +1,133 @@ +Jetty License +$Revision: 584 $ +Preamble: + +The intent of this document is to state the conditions under which the Jetty +Package may be copied, such that the Copyright Holder maintains some semblance +of control over the development of the package, while giving the users of the +package the right to use, distribute and make reasonable modifications to the +Package in accordance with the goals and ideals of the Open Source concept as +described at http://www.opensource.org. + +It is the intent of this license to allow commercial usage of the Jetty package, +so long as the source code is distributed or suitable visible credit given or +other arrangements made with the copyright holders. + +Definitions: + + * "Jetty" refers to the collection of Java classes that are distributed as a +HTTP server with servlet capabilities and associated utilities. + + * "Package" refers to the collection of files distributed by the Copyright +Holder, and derivatives of that collection of files created through textual +modification. + + * "Standard Version" refers to such a Package if it has not been modified, +or has been modified in accordance with the wishes of the Copyright Holder. + + * "Copyright Holder" is whoever is named in the copyright or copyrights for +the package. + Mort Bay Consulting Pty. Ltd. (Australia) is the "Copyright Holder" for +the Jetty package. + + * "You" is you, if you're thinking about copying or distributing this +Package. + + * "Reasonable copying fee" is whatever you can justify on the basis of media +cost, duplication charges, time of people involved, and so on. (You will not be +required to justify it to the Copyright Holder, but only to the computing +community at large as a market that must bear the fee.) + + * "Freely Available" means that no fee is charged for the item itself, +though there may be fees involved in handling the item. It also means that +recipients of the item may redistribute it under the same conditions they +received it. + +0. The Jetty Package is Copyright (c) Mort Bay Consulting Pty. Ltd. (Australia) +and others. Individual files in this package may contain additional copyright +notices. The javax.servlet packages are copyright Sun Microsystems Inc. + +1. The Standard Version of the Jetty package is available from +http://jetty.mortbay.org. + +2. You may make and distribute verbatim copies of the source form of the +Standard Version of this Package without restriction, provided that you include +this license and all of the original copyright notices and associated +disclaimers. + +3. You may make and distribute verbatim copies of the compiled form of the +Standard Version of this Package without restriction, provided that you include +this license. + +4. You may apply bug fixes, portability fixes and other modifications derived +from the Public Domain or from the Copyright Holder. A Package modified in such +a way shall still be considered the Standard Version. + +5. You may otherwise modify your copy of this Package in any way, provided that +you insert a prominent notice in each changed file stating how and when you +changed that file, and provided that you do at least ONE of the following: + + a) Place your modifications in the Public Domain or otherwise make them +Freely Available, such as by posting said modifications to Usenet or an +equivalent medium, or placing the modifications on a major archive site such as +ftp.uu.net, or by allowing the Copyright Holder to include your modifications in +the Standard Version of the Package. + + b) Use the modified Package only within your corporation or organization. + + c) Rename any non-standard classes so the names do not conflict with +standard classes, which must also be provided, and provide a separate manual +page for each non-standard class that clearly documents how it differs from the +Standard Version. + + d) Make other arrangements with the Copyright Holder. + +6. You may distribute modifications or subsets of this Package in source code or +compiled form, provided that you do at least ONE of the following: + + a) Distribute this license and all original copyright messages, together +with instructions (in the about dialog, manual page or equivalent) on where to +get the complete Standard Version. + + b) Accompany the distribution with the machine-readable source of the +Package with your modifications. The modified package must include this license +and all of the original copyright notices and associated disclaimers, together +with instructions on where to get the complete Standard Version. + + c) Make other arrangements with the Copyright Holder. + +7. You may charge a reasonable copying fee for any distribution of this Package. +You may charge any fee you choose for support of this Package. You may not +charge a fee for this Package itself. However, you may distribute this Package +in aggregate with other (possibly commercial) programs as part of a larger +(possibly commercial) software distribution provided that you meet the other +distribution requirements of this license. + +8. Input to or the output produced from the programs of this Package do not +automatically fall under the copyright of this Package, but belong to whomever +generated them, and may be sold commercially, and may be aggregated with this +Package. + +9. Any program subroutines supplied by you and linked into this Package shall +not be considered part of this Package. + +10. The name of the Copyright Holder may not be used to endorse or promote +products derived from this software without specific prior written permission. + +11. This license may change with each release of a Standard Version of the +Package. You may choose to use the license associated with version you are using +or the license of the latest Standard Version. + +12. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +13. If any superior law implies a warranty, the sole remedy under such shall be +, at the Copyright Holders option either a) return of any price paid or b) use +or reasonable endeavours to repair or replace the software. + +14. This license shall be read under the laws of Australia. + +The End +This license was derived from the Artistic license published on +http://www.opensource.com \ No newline at end of file diff --git a/Rapla/lib/ant-junit.jar b/Rapla/lib/ant-junit.jar new file mode 100644 index 0000000..1a2d293 Binary files /dev/null and b/Rapla/lib/ant-junit.jar differ diff --git a/Rapla/lib/ant-launcher.jar b/Rapla/lib/ant-launcher.jar new file mode 100644 index 0000000..1e35f68 Binary files /dev/null and b/Rapla/lib/ant-launcher.jar differ diff --git a/Rapla/lib/ant.jar b/Rapla/lib/ant.jar new file mode 100644 index 0000000..3a67607 Binary files /dev/null and b/Rapla/lib/ant.jar differ diff --git a/Rapla/lib/client/.cvsignore b/Rapla/lib/client/.cvsignore new file mode 100644 index 0000000..e69de29 diff --git a/Rapla/lib/client/CLIENT-LIBRARIES.txt b/Rapla/lib/client/CLIENT-LIBRARIES.txt new file mode 100644 index 0000000..eb5244c --- /dev/null +++ b/Rapla/lib/client/CLIENT-LIBRARIES.txt @@ -0,0 +1 @@ +Place in this folder all jars that should only be used by the client application. \ No newline at end of file diff --git a/Rapla/lib/common/.cvsignore b/Rapla/lib/common/.cvsignore new file mode 100644 index 0000000..e69de29 diff --git a/Rapla/lib/common/COMMON-LIBRARIES.txt b/Rapla/lib/common/COMMON-LIBRARIES.txt new file mode 100644 index 0000000..a3e1670 --- /dev/null +++ b/Rapla/lib/common/COMMON-LIBRARIES.txt @@ -0,0 +1 @@ +Place in this folder all jars that should be used by the client and the server application. \ No newline at end of file diff --git a/Rapla/lib/common/avalon-framework-4.2.0.jar b/Rapla/lib/common/avalon-framework-4.2.0.jar new file mode 100644 index 0000000..fb1b5cd Binary files /dev/null and b/Rapla/lib/common/avalon-framework-4.2.0.jar differ diff --git a/Rapla/lib/common/avalon-logging.jar b/Rapla/lib/common/avalon-logging.jar new file mode 100644 index 0000000..472042e Binary files /dev/null and b/Rapla/lib/common/avalon-logging.jar differ diff --git a/Rapla/lib/common/picollo.jar b/Rapla/lib/common/picollo.jar new file mode 100644 index 0000000..3b3d3b7 Binary files /dev/null and b/Rapla/lib/common/picollo.jar differ diff --git a/Rapla/lib/jetty-6.1.11.jar b/Rapla/lib/jetty-6.1.11.jar new file mode 100644 index 0000000..7dc9e09 Binary files /dev/null and b/Rapla/lib/jetty-6.1.11.jar differ diff --git a/Rapla/lib/jetty-util-6.1.11.jar b/Rapla/lib/jetty-util-6.1.11.jar new file mode 100644 index 0000000..dccbbe6 Binary files /dev/null and b/Rapla/lib/jetty-util-6.1.11.jar differ diff --git a/Rapla/lib/junit-3.8.jar b/Rapla/lib/junit-3.8.jar new file mode 100644 index 0000000..674d71e Binary files /dev/null and b/Rapla/lib/junit-3.8.jar differ diff --git a/Rapla/lib/server/.cvsignore b/Rapla/lib/server/.cvsignore new file mode 100644 index 0000000..e69de29 diff --git a/Rapla/lib/server/SERVER-LIBRARIES.txt b/Rapla/lib/server/SERVER-LIBRARIES.txt new file mode 100644 index 0000000..f4c6b4e --- /dev/null +++ b/Rapla/lib/server/SERVER-LIBRARIES.txt @@ -0,0 +1 @@ +Place in this folder all jars that should only be used by the server application. \ No newline at end of file diff --git a/Rapla/lib/server/avalon-logging.jar b/Rapla/lib/server/avalon-logging.jar new file mode 100644 index 0000000..472042e Binary files /dev/null and b/Rapla/lib/server/avalon-logging.jar differ diff --git a/Rapla/lib/server/backport-util-concurrent-3.1.jar b/Rapla/lib/server/backport-util-concurrent-3.1.jar new file mode 100644 index 0000000..3a4c279 Binary files /dev/null and b/Rapla/lib/server/backport-util-concurrent-3.1.jar differ diff --git a/Rapla/lib/server/commons-codec-1.5.jar b/Rapla/lib/server/commons-codec-1.5.jar new file mode 100644 index 0000000..e9013fe Binary files /dev/null and b/Rapla/lib/server/commons-codec-1.5.jar differ diff --git a/Rapla/lib/server/commons-lang-2.6.jar b/Rapla/lib/server/commons-lang-2.6.jar new file mode 100644 index 0000000..98467d3 Binary files /dev/null and b/Rapla/lib/server/commons-lang-2.6.jar differ diff --git a/Rapla/lib/server/commons-logging-1.1.1.jar b/Rapla/lib/server/commons-logging-1.1.1.jar new file mode 100644 index 0000000..1deef14 Binary files /dev/null and b/Rapla/lib/server/commons-logging-1.1.1.jar differ diff --git a/Rapla/lib/server/ical4j-1.0.1.jar b/Rapla/lib/server/ical4j-1.0.1.jar new file mode 100644 index 0000000..ed565e1 Binary files /dev/null and b/Rapla/lib/server/ical4j-1.0.1.jar differ diff --git a/Rapla/lib/servlet-api-2.5-6.1.11.jar b/Rapla/lib/servlet-api-2.5-6.1.11.jar new file mode 100644 index 0000000..221d34a Binary files /dev/null and b/Rapla/lib/servlet-api-2.5-6.1.11.jar differ diff --git a/Rapla/lib/test-only/hsqldb.jar b/Rapla/lib/test-only/hsqldb.jar new file mode 100644 index 0000000..dc3055e Binary files /dev/null and b/Rapla/lib/test-only/hsqldb.jar differ diff --git a/Rapla/lib/test-only/mysql-connector-java-3.1.6-bin.jar b/Rapla/lib/test-only/mysql-connector-java-3.1.6-bin.jar new file mode 100644 index 0000000..6ca54ff Binary files /dev/null and b/Rapla/lib/test-only/mysql-connector-java-3.1.6-bin.jar differ diff --git a/Rapla/lib/test-only/postgresjdbc.jar b/Rapla/lib/test-only/postgresjdbc.jar new file mode 100644 index 0000000..2dc45ad Binary files /dev/null and b/Rapla/lib/test-only/postgresjdbc.jar differ diff --git a/Rapla/lib/unused/.cvsignore b/Rapla/lib/unused/.cvsignore new file mode 100644 index 0000000..e69de29 diff --git a/Rapla/maintainj/META-INF/aop.xml b/Rapla/maintainj/META-INF/aop.xml new file mode 100644 index 0000000..e3d8240 --- /dev/null +++ b/Rapla/maintainj/META-INF/aop.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/Rapla/service/bin/InstallRaplaWrapper-NT.bat b/Rapla/service/bin/InstallRaplaWrapper-NT.bat new file mode 100644 index 0000000..c95a582 --- /dev/null +++ b/Rapla/service/bin/InstallRaplaWrapper-NT.bat @@ -0,0 +1,103 @@ +@echo off +setlocal + +rem Copyright (c) 1999, 2010 Tanuki Software, Ltd. +rem http://www.tanukisoftware.com +rem All rights reserved. +rem +rem This software is the proprietary information of Tanuki Software. +rem You shall use it only in accordance with the terms of the +rem license agreement you entered into with Tanuki Software. +rem http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html +rem +rem Java Service Wrapper general startup script. + +rem ----------------------------------------------------------------------------- +rem These settings can be modified to fit the needs of your application +rem Optimized for use with version 3.5.6 of the Wrapper. + +rem The base name for the Wrapper binary. +set _WRAPPER_BASE=wrapper + +rem The name and location of the Wrapper configuration file. This will be used +rem if the user does not specify a configuration file as the first argument to +rem this script. +set _WRAPPER_CONF_DEFAULT=../conf/wrapper.conf + +rem _PASS_THROUGH tells the script to pass all arguments through to the JVM +rem as is. +set _PASS_THROUGH=true + +rem Do not modify anything beyond this point +rem ----------------------------------------------------------------------------- + +rem +rem Resolve the real path of the wrapper.exe +rem For non NT systems, the _REALPATH and _WRAPPER_CONF values +rem can be hard-coded below and the following test removed. +rem +if "%OS%"=="Windows_NT" goto nt +echo This script only works with NT-based versions of Windows. +goto :eof + +:nt +rem +rem Find the application home. +rem +rem %~dp0 is location of current script under NT +set _REALPATH=%~dp0 + +rem +rem Decide on the specific Wrapper binary to use (See delta-pack) +rem +if "%PROCESSOR_ARCHITECTURE%"=="AMD64" goto amd64 +if "%PROCESSOR_ARCHITECTURE%"=="IA64" goto ia64 +set _WRAPPER_L_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe +goto search +:amd64 +set _WRAPPER_L_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-64.exe +goto search +:ia64 +set _WRAPPER_L_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-ia-64.exe +goto search +:search +set _WRAPPER_EXE=%_WRAPPER_L_EXE% +if exist "%_WRAPPER_EXE%" goto conf +set _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%.exe +if exist "%_WRAPPER_EXE%" goto conf +echo Unable to locate a Wrapper executable using any of the following names: +echo %_WRAPPER_L_EXE% +echo %_WRAPPER_EXE% +pause +goto :eof + +rem +rem Find the wrapper.conf +rem +:conf +set _WRAPPER_CONF="%~f1" +if not [%_WRAPPER_CONF%]==[""] ( + shift + goto :startup +) +set _WRAPPER_CONF="%_WRAPPER_CONF_DEFAULT%" + +rem +rem Start the Wrapper +rem +:startup + +rem Collect an parameters +:parameters +set _PARAMETERS=%_PARAMETERS% %1 +shift +if not [%1]==[] goto :parameters + +if [%_PASS_THROUGH%]==[] ( + "%_WRAPPER_EXE%" -i %_WRAPPER_CONF% +) else ( + "%_WRAPPER_EXE%" -i %_WRAPPER_CONF% -- %_PARAMETERS% +) +if not errorlevel 1 goto :eof +pause +:eof diff --git a/Rapla/service/bin/PauseRaplaWrapper-NT.bat b/Rapla/service/bin/PauseRaplaWrapper-NT.bat new file mode 100644 index 0000000..7140742 --- /dev/null +++ b/Rapla/service/bin/PauseRaplaWrapper-NT.bat @@ -0,0 +1,91 @@ +@echo off +setlocal + +rem Copyright (c) 1999, 2010 Tanuki Software, Ltd. +rem http://www.tanukisoftware.com +rem All rights reserved. +rem +rem This software is the proprietary information of Tanuki Software. +rem You shall use it only in accordance with the terms of the +rem license agreement you entered into with Tanuki Software. +rem http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html +rem +rem Java Service Wrapper general startup script. + +rem ----------------------------------------------------------------------------- +rem These settings can be modified to fit the needs of your application +rem Optimized for use with version 3.5.6 of the Wrapper. + +rem The base name for the Wrapper binary. +set _WRAPPER_BASE=wrapper + +rem The name and location of the Wrapper configuration file. This will be used +rem if the user does not specify a configuration file as the first argument to +rem this script. +set _WRAPPER_CONF_DEFAULT=../conf/wrapper.conf + +rem Note that it is only possible to pass parameters through to the JVM when +rem installing the service, or when running in a console. + +rem Do not modify anything beyond this point +rem ----------------------------------------------------------------------------- + +rem +rem Resolve the real path of the wrapper.exe +rem For non NT systems, the _REALPATH and _WRAPPER_CONF values +rem can be hard-coded below and the following test removed. +rem +if "%OS%"=="Windows_NT" goto nt +echo This script only works with NT-based versions of Windows. +goto :eof + +:nt +rem +rem Find the application home. +rem +rem %~dp0 is location of current script under NT +set _REALPATH=%~dp0 + +rem +rem Decide on the specific Wrapper binary to use (See delta-pack) +rem +if "%PROCESSOR_ARCHITECTURE%"=="AMD64" goto amd64 +if "%PROCESSOR_ARCHITECTURE%"=="IA64" goto ia64 +set _WRAPPER_L_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe +goto search +:amd64 +set _WRAPPER_L_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-64.exe +goto search +:ia64 +set _WRAPPER_L_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-ia-64.exe +goto search +:search +set _WRAPPER_EXE=%_WRAPPER_L_EXE% +if exist "%_WRAPPER_EXE%" goto conf +set _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%.exe +if exist "%_WRAPPER_EXE%" goto conf +echo Unable to locate a Wrapper executable using any of the following names: +echo %_WRAPPER_L_EXE% +echo %_WRAPPER_EXE% +pause +goto :eof + +rem +rem Find the wrapper.conf +rem +:conf +set _WRAPPER_CONF="%~f1" +if not [%_WRAPPER_CONF%]==[""] ( + shift + goto :startup +) +set _WRAPPER_CONF="%_WRAPPER_CONF_DEFAULT%" + +rem +rem Start the Wrapper +rem +:startup +"%_WRAPPER_EXE%" -a %_WRAPPER_CONF% +if not errorlevel 1 goto :eof +pause + diff --git a/Rapla/service/bin/QueryRaplaWrapper-NT.bat b/Rapla/service/bin/QueryRaplaWrapper-NT.bat new file mode 100644 index 0000000..a8f4dc9 --- /dev/null +++ b/Rapla/service/bin/QueryRaplaWrapper-NT.bat @@ -0,0 +1,91 @@ +@echo off +setlocal + +rem Copyright (c) 1999, 2010 Tanuki Software, Ltd. +rem http://www.tanukisoftware.com +rem All rights reserved. +rem +rem This software is the proprietary information of Tanuki Software. +rem You shall use it only in accordance with the terms of the +rem license agreement you entered into with Tanuki Software. +rem http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html +rem +rem Java Service Wrapper general startup script. + +rem ----------------------------------------------------------------------------- +rem These settings can be modified to fit the needs of your application +rem Optimized for use with version 3.5.6 of the Wrapper. + +rem The base name for the Wrapper binary. +set _WRAPPER_BASE=wrapper + +rem The name and location of the Wrapper configuration file. This will be used +rem if the user does not specify a configuration file as the first argument to +rem this script. +set _WRAPPER_CONF_DEFAULT=../conf/wrapper.conf + +rem Note that it is only possible to pass parameters through to the JVM when +rem installing the service, or when running in a console. + +rem Do not modify anything beyond this point +rem ----------------------------------------------------------------------------- + +rem +rem Resolve the real path of the wrapper.exe +rem For non NT systems, the _REALPATH and _WRAPPER_CONF values +rem can be hard-coded below and the following test removed. +rem +if "%OS%"=="Windows_NT" goto nt +echo This script only works with NT-based versions of Windows. +goto :eof + +:nt +rem +rem Find the application home. +rem +rem %~dp0 is location of current script under NT +set _REALPATH=%~dp0 + +rem +rem Decide on the specific Wrapper binary to use (See delta-pack) +rem +if "%PROCESSOR_ARCHITECTURE%"=="AMD64" goto amd64 +if "%PROCESSOR_ARCHITECTURE%"=="IA64" goto ia64 +set _WRAPPER_L_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe +goto search +:amd64 +set _WRAPPER_L_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-64.exe +goto search +:ia64 +set _WRAPPER_L_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-ia-64.exe +goto search +:search +set _WRAPPER_EXE=%_WRAPPER_L_EXE% +if exist "%_WRAPPER_EXE%" goto conf +set _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%.exe +if exist "%_WRAPPER_EXE%" goto conf +echo Unable to locate a Wrapper executable using any of the following names: +echo %_WRAPPER_L_EXE% +echo %_WRAPPER_EXE% +pause +goto :eof + +rem +rem Find the wrapper.conf +rem +:conf +set _WRAPPER_CONF="%~f1" +if not [%_WRAPPER_CONF%]==[""] ( + shift + goto :startup +) +set _WRAPPER_CONF="%_WRAPPER_CONF_DEFAULT%" + +rem +rem Start the Wrapper +rem +:startup +"%_WRAPPER_EXE%" -q %_WRAPPER_CONF% +if not errorlevel 1 goto :eof +pause + diff --git a/Rapla/service/bin/RaplaWrapperCommand.bat b/Rapla/service/bin/RaplaWrapperCommand.bat new file mode 100644 index 0000000..95d849f --- /dev/null +++ b/Rapla/service/bin/RaplaWrapperCommand.bat @@ -0,0 +1,179 @@ +@echo off +setlocal + +rem Copyright (c) 1999, 2010 Tanuki Software, Ltd. +rem http://www.tanukisoftware.com +rem All rights reserved. +rem +rem This software is the proprietary information of Tanuki Software. +rem You shall use it only in accordance with the terms of the +rem license agreement you entered into with Tanuki Software. +rem http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html +rem +rem Java Service Wrapper command based script. + +rem ----------------------------------------------------------------------------- +rem These settings can be modified to fit the needs of your application +rem Optimized for use with version 3.5.6 of the Wrapper. + +rem The base name for the Wrapper binary. +set _WRAPPER_BASE=wrapper + +rem The name and location of the Wrapper configuration file. +rem (Do not remove quotes.) +set _WRAPPER_CONF="../conf/wrapper.conf" + +rem _FIXED_COMMAND tells the script to use a hard coded action rather than +rem expecting the first parameter of the command line to be the command. +rem By default the command will will be expected to be the first parameter. +rem set _FIXED_COMMAND=console + +rem _PASS_THROUGH tells the script to pass all arguments through to the JVM +rem as is. If _FIXED_COMMAND is specified then all arguments will be passed. +rem If not set then all arguments starting with the second will be passed. +set _PASS_THROUGH=true + +rem Do not modify anything beyond this point +rem ----------------------------------------------------------------------------- + +if "%OS%"=="Windows_NT" goto nt +echo This script only works with NT-based versions of Windows. +goto :eof + +:nt +rem Find the application home. +rem %~dp0 is location of current script under NT +set _REALPATH=%~dp0 + +rem +rem Decide on the specific Wrapper binary to use (See delta-pack) +rem +if "%PROCESSOR_ARCHITECTURE%"=="AMD64" goto amd64 +if "%PROCESSOR_ARCHITECTURE%"=="IA64" goto ia64 +set _WRAPPER_L_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe +goto search +:amd64 +set _WRAPPER_L_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-64.exe +goto search +:ia64 +set _WRAPPER_L_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-ia-64.exe +goto search +:search +set _WRAPPER_EXE=%_WRAPPER_L_EXE% +if exist "%_WRAPPER_EXE%" goto validate +set _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%.exe +if exist "%_WRAPPER_EXE%" goto validate +echo Unable to locate a Wrapper executable using any of the following names: +echo %_WRAPPER_L_EXE% +echo %_WRAPPER_EXE% +pause +goto :eof + +:validate +if not [%_FIXED_COMMAND%]==[] goto defaultaction +rem There should be a command on the command line. Look for it. +set _COMMAND= +for /F %%v in ('echo %1^|findstr "^console$ ^start$ ^pause$ ^resume$ ^stop$ ^restart$ ^install$ ^remove$"') do call :exec set _COMMAND=%%v + +if [%_COMMAND%]==[] ( + set _COMMAND=%1 + goto showusage +) else ( + rem Got a command + shift +) +goto havecommand + +:defaultaction +rem Specified a default action. +set _COMMAND=%_FIXED_COMMAND% +goto havecommand + +:havecommand +if [%_PASS_THROUGH%]==[] goto callcommand +rem Collect an parameters +:parameters +set _PARAMETERS=%_PARAMETERS% %1 +shift +if not [%1]==[] goto parameters + +:callcommand +rem +rem Run the application. +rem At runtime, the current directory will be that of wrapper.exe +rem +call :%_COMMAND% +if errorlevel 1 goto handleerror +goto :eof + +:handleerror +if [%_MATCHED]==[] goto showusage +pause +goto :eof + +:showusage +rem A command was not specified, or it was now known. +if not [%_COMMAND%]==[] ( + echo Unknown command: %_COMMAND% + echo. +) +if [%_PASS_THROUGH%]==[] ( + echo Usage: %0 [ console : start : pause : resume : stop : restart : install : update : remove ] +) else ( + echo Usage: %0 [ console {JavaAppArgs} : start : pause : resume : stop : restart : install {JavaAppArgs} : update {JavaAppArgs} : remove ] +) +pause +goto :eof + +:console +set _MATCHED=true +if [%_PASS_THROUGH%]==[] ( + "%_WRAPPER_EXE%" -c %_WRAPPER_CONF% +) else ( + "%_WRAPPER_EXE%" -c %_WRAPPER_CONF% -- %_PARAMETERS% +) +goto :eof + +:start +set _MATCHED=true +"%_WRAPPER_EXE%" -t %_WRAPPER_CONF% +goto :eof + +:pause +set _MATCHED=true +"%_WRAPPER_EXE%" -a %_WRAPPER_CONF% +goto :eof + +:resume +set _MATCHED=true +"%_WRAPPER_EXE%" -e %_WRAPPER_CONF% +goto :eof + +:stop +set _MATCHED=true +"%_WRAPPER_EXE%" -p %_WRAPPER_CONF% +goto :eof + +:install +set _MATCHED=true +if [%_PASS_THROUGH%]==[] ( + "%_WRAPPER_EXE%" -i %_WRAPPER_CONF% +) else ( + "%_WRAPPER_EXE%" -i %_WRAPPER_CONF% -- %_PARAMETERS% +) +goto :eof + +:remove +set _MATCHED=true +"%_WRAPPER_EXE%" -r %_WRAPPER_CONF% +goto :eof + +:restart +set _MATCHED=true +call :stop +call :start +goto :eof + +:exec +%* +goto :eof diff --git a/Rapla/service/bin/RestartRaplaWrapper-NT.bat b/Rapla/service/bin/RestartRaplaWrapper-NT.bat new file mode 100644 index 0000000..bcee8f7 --- /dev/null +++ b/Rapla/service/bin/RestartRaplaWrapper-NT.bat @@ -0,0 +1,6 @@ +@echo off +set _REALPATH=%~dp0 +echo %_REALPATH% +call %_REALPATH%StopRaplaWrapper-NT.bat +call %_REALPATH%StartRaplaWrapper-NT.bat + diff --git a/Rapla/service/bin/ResumeRaplaWrapper-NT.bat b/Rapla/service/bin/ResumeRaplaWrapper-NT.bat new file mode 100644 index 0000000..4d27d71 --- /dev/null +++ b/Rapla/service/bin/ResumeRaplaWrapper-NT.bat @@ -0,0 +1,91 @@ +@echo off +setlocal + +rem Copyright (c) 1999, 2010 Tanuki Software, Ltd. +rem http://www.tanukisoftware.com +rem All rights reserved. +rem +rem This software is the proprietary information of Tanuki Software. +rem You shall use it only in accordance with the terms of the +rem license agreement you entered into with Tanuki Software. +rem http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html +rem +rem Java Service Wrapper general startup script. + +rem ----------------------------------------------------------------------------- +rem These settings can be modified to fit the needs of your application +rem Optimized for use with version 3.5.6 of the Wrapper. + +rem The base name for the Wrapper binary. +set _WRAPPER_BASE=wrapper + +rem The name and location of the Wrapper configuration file. This will be used +rem if the user does not specify a configuration file as the first argument to +rem this script. +set _WRAPPER_CONF_DEFAULT=../conf/wrapper.conf + +rem Note that it is only possible to pass parameters through to the JVM when +rem installing the service, or when running in a console. + +rem Do not modify anything beyond this point +rem ----------------------------------------------------------------------------- + +rem +rem Resolve the real path of the wrapper.exe +rem For non NT systems, the _REALPATH and _WRAPPER_CONF values +rem can be hard-coded below and the following test removed. +rem +if "%OS%"=="Windows_NT" goto nt +echo This script only works with NT-based versions of Windows. +goto :eof + +:nt +rem +rem Find the application home. +rem +rem %~dp0 is location of current script under NT +set _REALPATH=%~dp0 + +rem +rem Decide on the specific Wrapper binary to use (See delta-pack) +rem +if "%PROCESSOR_ARCHITECTURE%"=="AMD64" goto amd64 +if "%PROCESSOR_ARCHITECTURE%"=="IA64" goto ia64 +set _WRAPPER_L_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe +goto search +:amd64 +set _WRAPPER_L_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-64.exe +goto search +:ia64 +set _WRAPPER_L_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-ia-64.exe +goto search +:search +set _WRAPPER_EXE=%_WRAPPER_L_EXE% +if exist "%_WRAPPER_EXE%" goto conf +set _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%.exe +if exist "%_WRAPPER_EXE%" goto conf +echo Unable to locate a Wrapper executable using any of the following names: +echo %_WRAPPER_L_EXE% +echo %_WRAPPER_EXE% +pause +goto :eof + +rem +rem Find the wrapper.conf +rem +:conf +set _WRAPPER_CONF="%~f1" +if not [%_WRAPPER_CONF%]==[""] ( + shift + goto :startup +) +set _WRAPPER_CONF="%_WRAPPER_CONF_DEFAULT%" + +rem +rem Start the Wrapper +rem +:startup +"%_WRAPPER_EXE%" -e %_WRAPPER_CONF% +if not errorlevel 1 goto :eof +pause + diff --git a/Rapla/service/bin/StartRaplaWrapper-NT.bat b/Rapla/service/bin/StartRaplaWrapper-NT.bat new file mode 100644 index 0000000..cdf09fa --- /dev/null +++ b/Rapla/service/bin/StartRaplaWrapper-NT.bat @@ -0,0 +1,91 @@ +@echo off +setlocal + +rem Copyright (c) 1999, 2010 Tanuki Software, Ltd. +rem http://www.tanukisoftware.com +rem All rights reserved. +rem +rem This software is the proprietary information of Tanuki Software. +rem You shall use it only in accordance with the terms of the +rem license agreement you entered into with Tanuki Software. +rem http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html +rem +rem Java Service Wrapper general startup script. + +rem ----------------------------------------------------------------------------- +rem These settings can be modified to fit the needs of your application +rem Optimized for use with version 3.5.6 of the Wrapper. + +rem The base name for the Wrapper binary. +set _WRAPPER_BASE=wrapper + +rem The name and location of the Wrapper configuration file. This will be used +rem if the user does not specify a configuration file as the first argument to +rem this script. +set _WRAPPER_CONF_DEFAULT=../conf/wrapper.conf + +rem Note that it is only possible to pass parameters through to the JVM when +rem installing the service, or when running in a console. + +rem Do not modify anything beyond this point +rem ----------------------------------------------------------------------------- + +rem +rem Resolve the real path of the wrapper.exe +rem For non NT systems, the _REALPATH and _WRAPPER_CONF values +rem can be hard-coded below and the following test removed. +rem +if "%OS%"=="Windows_NT" goto nt +echo This script only works with NT-based versions of Windows. +goto :eof + +:nt +rem +rem Find the application home. +rem +rem %~dp0 is location of current script under NT +set _REALPATH=%~dp0 + +rem +rem Decide on the specific Wrapper binary to use (See delta-pack) +rem +if "%PROCESSOR_ARCHITECTURE%"=="AMD64" goto amd64 +if "%PROCESSOR_ARCHITECTURE%"=="IA64" goto ia64 +set _WRAPPER_L_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe +goto search +:amd64 +set _WRAPPER_L_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-64.exe +goto search +:ia64 +set _WRAPPER_L_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-ia-64.exe +goto search +:search +set _WRAPPER_EXE=%_WRAPPER_L_EXE% +if exist "%_WRAPPER_EXE%" goto conf +set _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%.exe +if exist "%_WRAPPER_EXE%" goto conf +echo Unable to locate a Wrapper executable using any of the following names: +echo %_WRAPPER_L_EXE% +echo %_WRAPPER_EXE% +pause +goto :eof + +rem +rem Find the wrapper.conf +rem +:conf +set _WRAPPER_CONF="%~f1" +if not [%_WRAPPER_CONF%]==[""] ( + shift + goto :startup +) +set _WRAPPER_CONF="%_WRAPPER_CONF_DEFAULT%" + +rem +rem Start the Wrapper +rem +:startup +"%_WRAPPER_EXE%" -t %_WRAPPER_CONF% +if not errorlevel 1 goto :eof +pause + diff --git a/Rapla/service/bin/StopRaplaWrapper-NT.bat b/Rapla/service/bin/StopRaplaWrapper-NT.bat new file mode 100644 index 0000000..93135b9 --- /dev/null +++ b/Rapla/service/bin/StopRaplaWrapper-NT.bat @@ -0,0 +1,91 @@ +@echo off +setlocal + +rem Copyright (c) 1999, 2010 Tanuki Software, Ltd. +rem http://www.tanukisoftware.com +rem All rights reserved. +rem +rem This software is the proprietary information of Tanuki Software. +rem You shall use it only in accordance with the terms of the +rem license agreement you entered into with Tanuki Software. +rem http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html +rem +rem Java Service Wrapper general startup script. + +rem ----------------------------------------------------------------------------- +rem These settings can be modified to fit the needs of your application +rem Optimized for use with version 3.5.6 of the Wrapper. + +rem The base name for the Wrapper binary. +set _WRAPPER_BASE=wrapper + +rem The name and location of the Wrapper configuration file. This will be used +rem if the user does not specify a configuration file as the first argument to +rem this script. +set _WRAPPER_CONF_DEFAULT=../conf/wrapper.conf + +rem Note that it is only possible to pass parameters through to the JVM when +rem installing the service, or when running in a console. + +rem Do not modify anything beyond this point +rem ----------------------------------------------------------------------------- + +rem +rem Resolve the real path of the wrapper.exe +rem For non NT systems, the _REALPATH and _WRAPPER_CONF values +rem can be hard-coded below and the following test removed. +rem +if "%OS%"=="Windows_NT" goto nt +echo This script only works with NT-based versions of Windows. +goto :eof + +:nt +rem +rem Find the application home. +rem +rem %~dp0 is location of current script under NT +set _REALPATH=%~dp0 + +rem +rem Decide on the specific Wrapper binary to use (See delta-pack) +rem +if "%PROCESSOR_ARCHITECTURE%"=="AMD64" goto amd64 +if "%PROCESSOR_ARCHITECTURE%"=="IA64" goto ia64 +set _WRAPPER_L_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe +goto search +:amd64 +set _WRAPPER_L_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-64.exe +goto search +:ia64 +set _WRAPPER_L_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-ia-64.exe +goto search +:search +set _WRAPPER_EXE=%_WRAPPER_L_EXE% +if exist "%_WRAPPER_EXE%" goto conf +set _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%.exe +if exist "%_WRAPPER_EXE%" goto conf +echo Unable to locate a Wrapper executable using any of the following names: +echo %_WRAPPER_L_EXE% +echo %_WRAPPER_EXE% +pause +goto :eof + +rem +rem Find the wrapper.conf +rem +:conf +set _WRAPPER_CONF="%~f1" +if not [%_WRAPPER_CONF%]==[""] ( + shift + goto :startup +) +set _WRAPPER_CONF="%_WRAPPER_CONF_DEFAULT%" + +rem +rem Start the Wrapper +rem +:startup +"%_WRAPPER_EXE%" -p %_WRAPPER_CONF% +if not errorlevel 1 goto :eof +pause + diff --git a/Rapla/service/bin/UninstallRaplaWrapper-NT.bat b/Rapla/service/bin/UninstallRaplaWrapper-NT.bat new file mode 100644 index 0000000..78fd122 --- /dev/null +++ b/Rapla/service/bin/UninstallRaplaWrapper-NT.bat @@ -0,0 +1,91 @@ +@echo off +setlocal + +rem Copyright (c) 1999, 2010 Tanuki Software, Ltd. +rem http://www.tanukisoftware.com +rem All rights reserved. +rem +rem This software is the proprietary information of Tanuki Software. +rem You shall use it only in accordance with the terms of the +rem license agreement you entered into with Tanuki Software. +rem http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html +rem +rem Java Service Wrapper general startup script. + +rem ----------------------------------------------------------------------------- +rem These settings can be modified to fit the needs of your application +rem Optimized for use with version 3.5.6 of the Wrapper. + +rem The base name for the Wrapper binary. +set _WRAPPER_BASE=wrapper + +rem The name and location of the Wrapper configuration file. This will be used +rem if the user does not specify a configuration file as the first argument to +rem this script. +set _WRAPPER_CONF_DEFAULT=../conf/wrapper.conf + +rem Note that it is only possible to pass parameters through to the JVM when +rem installing the service, or when running in a console. + +rem Do not modify anything beyond this point +rem ----------------------------------------------------------------------------- + +rem +rem Resolve the real path of the wrapper.exe +rem For non NT systems, the _REALPATH and _WRAPPER_CONF values +rem can be hard-coded below and the following test removed. +rem +if "%OS%"=="Windows_NT" goto nt +echo This script only works with NT-based versions of Windows. +goto :eof + +:nt +rem +rem Find the application home. +rem +rem %~dp0 is location of current script under NT +set _REALPATH=%~dp0 + +rem +rem Decide on the specific Wrapper binary to use (See delta-pack) +rem +if "%PROCESSOR_ARCHITECTURE%"=="AMD64" goto amd64 +if "%PROCESSOR_ARCHITECTURE%"=="IA64" goto ia64 +set _WRAPPER_L_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe +goto search +:amd64 +set _WRAPPER_L_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-64.exe +goto search +:ia64 +set _WRAPPER_L_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-ia-64.exe +goto search +:search +set _WRAPPER_EXE=%_WRAPPER_L_EXE% +if exist "%_WRAPPER_EXE%" goto conf +set _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%.exe +if exist "%_WRAPPER_EXE%" goto conf +echo Unable to locate a Wrapper executable using any of the following names: +echo %_WRAPPER_L_EXE% +echo %_WRAPPER_EXE% +pause +goto :eof + +rem +rem Find the wrapper.conf +rem +:conf +set _WRAPPER_CONF="%~f1" +if not [%_WRAPPER_CONF%]==[""] ( + shift + goto :startup +) +set _WRAPPER_CONF="%_WRAPPER_CONF_DEFAULT%" + +rem +rem Start the Wrapper +rem +:startup +"%_WRAPPER_EXE%" -r %_WRAPPER_CONF% +if not errorlevel 1 goto :eof +pause + diff --git a/Rapla/service/bin/wrapper.exe b/Rapla/service/bin/wrapper.exe new file mode 100644 index 0000000..ceb111c Binary files /dev/null and b/Rapla/service/bin/wrapper.exe differ diff --git a/Rapla/service/conf/wrapper.conf b/Rapla/service/conf/wrapper.conf new file mode 100644 index 0000000..a581521 --- /dev/null +++ b/Rapla/service/conf/wrapper.conf @@ -0,0 +1,208 @@ +#encoding=UTF-8 +# Configuration files must begin with a line specifying the encoding +# of the the file. + +#******************************************************************** +# Wrapper License Properties (Ignored by Community Edition) +#******************************************************************** +# Professional and Standard Editions of the Wrapper require a valid +# License Key to start. Licenses can be purchased or a trial license +# requested on the following pages: +# http://wrapper.tanukisoftware.com/purchase +# http://wrapper.tanukisoftware.com/trial + +# Include file problems can be debugged by removing the first '#' +# from the following line: +##include.debug + +# The Wrapper will look for either of the following optional files for a +# valid License Key. License Key properties can optionally be included +# directly in this configuration file. +#include ../conf/wrapper-license.conf +#include ../conf/wrapper-license-%WRAPPER_HOST_NAME%.conf + +# The following property will output information about which License Key(s) +# are being found, and can aid in resolving any licensing problems. +#wrapper.license.debug=TRUE + +#******************************************************************** +# Wrapper Localization +#******************************************************************** +# Specify the locale which the Wrapper should use. By default the system +# locale is used. +#wrapper.lang=en_US # en_US or ja_JP + +# Specify the location of the Wrapper's language resources. If these are +# missing, the Wrapper will default to the en_US locale. +wrapper.lang.folder=../lang + +#******************************************************************** +# Wrapper Java Properties +#******************************************************************** +# Java Application +# Locate the java binary on the system PATH: +wrapper.java.command=java +# Specify a specific java binary: +#set.JAVA_HOME=/java/path +#wrapper.java.command=%JAVA_HOME%/bin/java + +# Tell the Wrapper to log the full generated Java command line. +#wrapper.java.command.loglevel=INFO + +# Java Main class. This class must implement the WrapperListener interface +# or guarantee that the WrapperManager class is initialized. Helper +# classes are provided to do this for you. See the Integration section +# of the documentation for details. +wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp + +# Java Classpath (include wrapper.jar) Add class path elements as +# needed starting from 1 +wrapper.java.classpath.1=../../raplabootstrap.jar +wrapper.java.classpath.2=../lib/wrapper.jar + +# Java Library Path (location of Wrapper.DLL or libwrapper.so) +wrapper.java.library.path.1=../lib + +# Java Bits. On applicable platforms, tells the JVM to run in 32 or 64-bit mode. +wrapper.java.additional.auto_bits=TRUE + +# Java Additional Parameters +wrapper.java.additional.1=-Djetty.home=../../ +wrapper.java.additional.2=-Djava.awt.headless=true + +# Initial Java Heap Size (in MB) +#wrapper.java.initmemory=3 + +# Maximum Java Heap Size (in MB) +#wrapper.java.maxmemory=64 + +# Application parameters. Add parameters as needed starting from 1 +wrapper.app.parameter.1=org.rapla.bootstrap.RaplaServerAsServiceLoader + +#******************************************************************** +# Wrapper Logging Properties +#******************************************************************** +# Enables Debug output from the Wrapper. +# wrapper.debug=TRUE + +# Format of output for the console. (See docs for formats) +wrapper.console.format=PM + +# Log Level for console output. (See docs for log levels) +wrapper.console.loglevel=INFO + +# Log file to use for wrapper output logging. +wrapper.logfile=../logs/wrapper.log + +# Format of output for the log file. (See docs for formats) +wrapper.logfile.format=LPTM + +# Log Level for log file output. (See docs for log levels) +wrapper.logfile.loglevel=INFO + +# Maximum size that the log file will be allowed to grow to before +# the log is rolled. Size is specified in bytes. The default value +# of 0, disables log rolling. May abbreviate with the 'k' (kb) or +# 'm' (mb) suffix. For example: 10m = 10 megabytes. +wrapper.logfile.maxsize=10m + +# Maximum number of rolled log files which will be allowed before old +# files are deleted. The default value of 0 implies no limit. +wrapper.logfile.maxfiles=10 + +# Log Level for sys/event log output. (See docs for log levels) +wrapper.syslog.loglevel=WARN + +#******************************************************************** +# Wrapper General Properties +#******************************************************************** +# Allow for the use of non-contiguous numbered properties +wrapper.ignore_sequence_gaps=TRUE + +# Title to use when running as a console +wrapper.console.title=Test Wrapper Sample Application + +#******************************************************************** +# Wrapper JVM Checks +#******************************************************************** +# Detect DeadLocked Threads in the JVM. (Requires Standard Edition) +wrapper.check.deadlock=TRUE +wrapper.check.deadlock.interval=10 +wrapper.check.deadlock.action=RESTART +wrapper.check.deadlock.output=FULL + +# Out Of Memory detection. +# (Ignore output from dumping the configuration to the console. This is only needed by the TestWrapper sample application.) +wrapper.filter.trigger.999=wrapper.filter.trigger.*java.lang.OutOfMemoryError +wrapper.filter.allow_wildcards.999=TRUE +wrapper.filter.action.999=NONE +# (Simple match) +wrapper.filter.trigger.1000=java.lang.OutOfMemoryError +# (Only match text in stack traces if -XX:+PrintClassHistogram is being used.) +#wrapper.filter.trigger.1000=Exception in thread "*" java.lang.OutOfMemoryError +#wrapper.filter.allow_wildcards.1000=TRUE +wrapper.filter.action.1000=RESTART +wrapper.filter.message.1000=The JVM has run out of memory. + +#******************************************************************** +# Wrapper Email Notifications. (Requires Professional Edition) +#******************************************************************** +# Common Event Email settings. +#wrapper.event.default.email.debug=TRUE +#wrapper.event.default.email.smtp.host= +#wrapper.event.default.email.smtp.port=25 +#wrapper.event.default.email.subject=[%WRAPPER_HOSTNAME%:%WRAPPER_NAME%:%WRAPPER_EVENT_NAME%] Event Notification +#wrapper.event.default.email.sender= +#wrapper.event.default.email.recipient= + +# Configure the log attached to event emails. +#wrapper.event.default.email.attach_log=TRUE +#wrapper.event.default.email.maillog.lines=50 +#wrapper.event.default.email.maillog.format=LPTM +#wrapper.event.default.email.maillog.loglevel=INFO + +# Enable specific event emails. +#wrapper.event.wrapper_start.email=TRUE +#wrapper.event.jvm_prelaunch.email=TRUE +#wrapper.event.jvm_start.email=TRUE +#wrapper.event.jvm_started.email=TRUE +#wrapper.event.jvm_deadlock.email=TRUE +#wrapper.event.jvm_stop.email=TRUE +#wrapper.event.jvm_stopped.email=TRUE +#wrapper.event.jvm_restart.email=TRUE +#wrapper.event.jvm_failed_invocation.email=TRUE +#wrapper.event.jvm_max_failed_invocations.email=TRUE +#wrapper.event.jvm_kill.email=TRUE +#wrapper.event.jvm_killed.email=TRUE +#wrapper.event.jvm_unexpected_exit.email=TRUE +#wrapper.event.wrapper_stop.email=TRUE + +# Specify custom mail content +wrapper.event.jvm_restart.email.body=The JVM was restarted.\n\nPlease check on its status.\n + +#******************************************************************** +# Wrapper Windows NT/2000/XP Service Properties +#******************************************************************** +# WARNING - Do not modify any of these properties when an application +# using this configuration file has been installed as a service. +# Please uninstall the service before modifying this section. The +# service can then be reinstalled. + +# Name of the service +wrapper.name=Raplaxxxxxxxx + +# Display name of the service +wrapper.displayname=Rapla Server xxxxxxxx + +# Description of the service +wrapper.description=Rapla resource-management server xxxxxxxx + +# Service dependencies. Add dependencies as needed starting from 1 +wrapper.ntservice.dependency.1= + +# Mode in which the service is installed. AUTO_START, DELAY_START or DEMAND_START +wrapper.ntservice.starttype=AUTO_START + +# Allow the service to interact with the desktop. +wrapper.ntservice.interactive=false + diff --git a/Rapla/service/doc/index.html b/Rapla/service/doc/index.html new file mode 100644 index 0000000..35078dc --- /dev/null +++ b/Rapla/service/doc/index.html @@ -0,0 +1,12 @@ + + + Java Service Wrapper Documentation + + + Complete documentation can be found online:
+ http://wrapper.tanukisoftware.org +

+ Java docs are available online as well:
+ http://wrapper.tanukisoftware.org/jdoc/index.html + + diff --git a/Rapla/service/doc/readme.txt b/Rapla/service/doc/readme.txt new file mode 100644 index 0000000..6332f32 --- /dev/null +++ b/Rapla/service/doc/readme.txt @@ -0,0 +1,7 @@ +Java Service Wrapper + +Complete documentation can be found online: +http://wrapper.tanukisoftware.org + +Java docs are available online as well: +http://wrapper.tanukisoftware.org/jdoc/index.html diff --git a/Rapla/service/doc/revisions.txt b/Rapla/service/doc/revisions.txt new file mode 100644 index 0000000..3b8dcea --- /dev/null +++ b/Rapla/service/doc/revisions.txt @@ -0,0 +1,2511 @@ +Java Service Wrapper Revision History. +-------------------------------------- + +3.5.6 +* Fix a problem on UNIX platforms where the log file path was being calculated + incorrectly if an absolute path was specified for wrapper.logfile, and the + file did not already exist. A warning message was being displayed but the + Wrapper would continue using the default log file. There was a problem in + the message displayed which caused a crash on some platforms include Mac OSX. + Introduced in version 3.5.5. +* Fix a problem on Windows platforms where the Wrapper would crash if it could + not access the directory of the configured wrapper.logfile. + Introduced in version 3.5.5. Bug #3087424. +* Improve the way warnings are logged when there are problems writing to the + configured wrapper.logfile so that the message will now be logged into the + log file that the Wrapper ends up using in case it is successful in falling + back to a default log file. +* Fix a problem on Windows platforms where wrapper.java.additional. + properties that were specified on the command line, and contained spaces, + were not being requoted correctly when building up the Java command line. + Introduced in version 3.3.6. +* Fix a problem where the warning message logged for invalid values of the + wrapper.java.additional. property, contained corrupted text. Introduced + in version 3.3.6. +* Fix a problem on UNIX platforms where an invalid value for the + wrapper.java.additional. property was correctly being reported and then + skipped, but the resulting command line to launch the JVM had a blank value + that was causing the JVM to fail to launch. An invalid value is any value + that does not begin with a "-" character. +* Add a new WRAPPER_INIT_DIR environment variable which can be used to + reference the working directory from which the Wrapper was launched. This is + needed for certain applications because the Wrapper always changes its + working directory to the location of the Wrapper binary. +* Modify the UNIX shell script so it no longer changes the current working dir + to the location of the script. This is no longer needed because the Wrapper + has been changing the working directory to its own location since version + 3.2.0. +* Add a new wrapper.request_thread_dump_on_failed_jvm_exit.delay property to + control how long the Wrapper will wait after doing a thread dump before + killing the Java process. This delay has always been hardcoded to 5 seconds. +* Clean up the text of several warning messages about invalid configuration + values to make them more consistent. +* (Professional) Add a new wrapper.jvm_kill.delay property which makes it + possible to control the amount of time to allow between the jvm_kill event + being fired and the JVM actually being killed. Useful if an external event + command is fired that needs to do something with the JVM process first. +* (Professional) Fix a problem where the output of the + wrapper.event..message and wrapper.event..loglevel + properties were not displaying correctly on UNIX platforms. +* (Professional) Fix a problem on UNIX platforms where the Java side of the + Wrapper was not being correctly notified if a large number of child + processes that had been launched with WrapperManager.exec, exited at the + same instant. Some of them were going into a defunct state until the next + child exited, at which point they would be cleaned up. This was mainly an + issue on JVM shutdown if the user code was calling WrapperProcess.waitFor() + as part of its shutdown process. WaitFor calls at any point were getting + stuck and would remain so until another child exited. As part of this fix, + there were also several changes to the Windows implementation to bring the + two code bases into sync. +* Fix a problem on Windows when multiple threads were creating Childobjects, + Handles could have been unintendedly get inherited by another Child Process, + causing problems on reading/writing to the Input/Output/Errorstream. +* Fix a problem on solaris and AIX when errno calls were not thread safe due + to a compiler switch. +* Fix a problem where debug level warning output while loading the Wrapper + configuration was being displayed on startup. This could be fixed because + the Wrapper actually loads the configuration twice, and such output is now + only logged on the second call. +* (Standard, Professional) Remove the undocumented ability to define a single + file share mapping without the index. This would cause confusion if used, + and complicated the code. +* (Standard, Professional) Fix a byte alignment problem caused by a bad + compiler directive on Windows platforms. It was known to cause a crash when + defining mapped drives on 64-bit Windows versions. The problem was in the + source since version 3.3.7, but is not known to cause any other issues. +* (Standard, Professional) Modify the messages displayed when network shares + are mapped or fail for some reason. Also add messages about them being + unmapped on shutdown. +* On some Windows platforms, a failure to delete a rolled log file was not + being reported correctly. The system function to delete a file was + returning success even if it had failed. We now double check. +* Fix a deadlock in the code that is used to send data to the Java process. + It was only possible if debug level output was enabled and log file rolling + was enabled. Introduced in 3.3.7. +* Fix a problem where the WrapperManager.getWrapperLogFile() was not returning + the accurate log file name until the first time the log file was rolled after + each JVM invocation. This was only noticeable if the wrapper.logfile + contained either the "ROLLNUM" or "YYYYMMDD" tokens. +* Correct an error message that was displayed on UNIX platforms when the + configured java binary could not be accessed. The message referenced a + buffer whose contents were undefined on some platforms. + +3.5.5 +* Add new wrapper.filter.trigger. action, "SUCCESS". If this gets triggered + then the Wrapper will treat the current JVM invocation as a success, and + reset its internal failed invocation counter. This is useful for + applications that need to be restarted frequently. +* (Standard, Professional) Ignore Juniper Network Connect hostIds as they + change on each reboot and are thus unreliable as hostIds. +* Added a PASS_THROUGH setting to the UNIX shell script and Windows + AppCommand.bat.in files which tells them to pass any extra arguments directly + on to the JVM when it is launched. +* Added a FIXED_COMMAND setting to the UNIX shell script and Windows + AppCommand.bat.in files to make it possible to run either without specifying + a command. Mainly useful in conjunction with PASS_THROUGH. +* (Standard, Professional) Added a --passthrough option to the exe + customization, in order to tell the wrapper to precede the whole command line + through to the application in the JVM. +* (Standard, Professional) Added a --conf option to change the default conf + file, the wrapper tries opening when no conf file was explicitly specified. +* Added wrapper.ntservice.account.prompt. If set to TRUE the wrapper will + prompt for all account details (domain, account name, password). +* Fix a minor issue in #include file declarations where a leading space was not + required. +* Add a new #include.required directive which works the same as the #include + directive except that it will output an error and prevent the loading of the + configuration if the included file does not exist. Normally include files + are optional by design. +* Modify the error messages displayed when configuration files fail to load so + they now provide more information about where specifically the problem was. +* Disabled the forced reloading of the SYSTEM (and if set to a specific + account, the user) registry before launching the Wrapper as a service on + Windows. This was done originally in Windows NT because changes to the + configured environment were not being reflected when running a service unless + the system was first rebooted. Microsoft appears to have solved this problem + in Windows XP and 2003. In Windows 7 and 2008, this was actually causing a + problem because the SYSTEM registry contains a setting "USERNAME=SYSTEM" by + default that was overwriting the USERNAME when run as specific user. It was + decided to disable this registry loading for Windows versions starting with + XP and 2003. Of the supported versions, only 2000 is now reloading its + environment. The only difference from version 3.5.4 and earlier that could + be found is that when running as the SYSTEM user on Windows 7 or 2008, the + USERNAME environment variable will now be set to the host name followed by a + dollar sign rather than SYSTEM. This is actually how all other services + work. But just in case this is a problem, it can we resolved by adding a + "set.USERNAME=SYSTEM" property into the Wrapper configuration file. + Bug #3061490. +* (Standard, Professional) Fix a problem for Solaris and HP-UX where the socket + timeout properties for the email notifications were ignored. +* (Standard, Professional) Added wrapper.ntservice.recovery. properties to + define system level actions in the event that the Wrapper process itself + has a failure. +* (Standard, Professional) Fixed a problem in the WrapperProcess.waitFor() and + WrapperProcess.exitValue() call, where it would fail to return when called + after the Wrapper had initiated the shutdown of the JVM. +* (Standard, Professional) Add WrapperProcessConfig.setSoftShutdownTimeout(int) + method to tell the Wrapper how long to wait after nicely asking the child + process to shutdown cleanly when calling WrapperProcess.destroy(). Once the + timeout has ellapsed, the child process will be forcibly terminated. This + timeout had been hard coded to 5 seconds in earlier versions. +* Add more detailed usage output to the UNIX shell script. +* Make it possible to 'pause' and 'resume' the JVM from the UNIX shell and + Windows batch scripts. +* (Professional) Fix a minor memory memory leak while initializing timers. +* Fix a memory leak which could happen if there were any invalid strings in + localization resources. +* (Professional) Fix a bug where the wrapper.event..command.argv. + properties were not correctly parsed on Windows. This issue was introduced + in version 3.5.0. +* (Professional) Add the ability to define wrapper.event.default.command.argv. + properties that will be used if the event specific specific commands are not + defined. Mainly useful for testing. +* Fix a problem occuring when the wrapper failed to roll the log file causing + to write to the wrapper's default log (wrapper.log) file rather than + continuing to write to the current logfile. +* (Standard, Professional) Fix a put problem in the internal hash map + implemenation used for localization where values could be lost. This was + not a visible issue because of the data used. +* Add new wrapper.filter.allow_wildcards. property and make it possible to + specify wrapper.filter.trigger. patterns which contain '*' and '?' + wildcards. +* Add a commented alternative in the default OutOfMemoryError filter + configuration to make it more specific to only trigger on uncaught exception + stack traces. This is to avoid output like that from the + -XX:+PrintClassHistogram JVM argument from causing the JVM to restart with a + false out of memory warning. See the wrapper.filter.trigger. + OutOfMemoryError example for more details. +* Localize the default filter message. +* Added ISO-8859-* encoding support and a few other encodings. +* (Standard, Professional) Fix a problem on UNIX versions, parsing dates in + license keys that was introduced in version 3.5.0. Windows verisons did not + have this problem. All license upgrade terms and lease terms which contained + "08" or "09" as either a month or day were being parsed incorrectly. This + was leading the Wrapper to interpret those date components as "00". If the + number was the date, then this would cause the date to be set to the last day + of the previous month. If it was the month however, it would cause the date + to be set to December of the previous year. For example "2010-08-20" was + being interpreted as "2009-12-20", and "2010-10-08" was being interpreted as + "2009-09-30". This would have prevented some licenses from being able to + start because the upgrade terms were in effect prior to the Wrapper's release + date. Some trial licenses could also have stopped early because their lease + term end was earlier that it should may have been. For normal licenses, his + will have no effect on installations once they are up and running because + they do not use the lease term. +* Fix a problem on Windows when a service received several service control + codes in rapid succession. Since version 3.5.1, the Wrapper was only to + process a single control code in each cycle of its main loop. This was + resulting in messages like "Previous control code (4) was still in queue, + overwriting with (4)." in the logs. The Wrapper can now handle up to 25 + control codes per 10ms cycle. +* Fix a problem where it was not possible to send passthrough arguments to the + JVM when installing or updating a Windows Service. Passthrough using the + "--" argument was added in 3.5.2. +* Add a new wrapper.pause_on_startup property which makes it possible to tell + the Wrapper to go directly into a paused state without ever launching a JVM. +* Fix a problem where the STOP command set in a command file was being ignored + if the Wrapper was currently in a paused state. +* Make it possible to specify DEFAULT for the configuration file encoding. + This will cause the file to be loaded using the default system encoding. We + added this by request, but recommend using a portable encoding like UTF-8 to + ensure that the configuration file will load correctly on all systems. +* Added a WRAPPER_LANG environment variable which makes it possible to reference + the current locale language code in the configuration file. One common use + is to do localization using inclues. + (e.g. #include ../conf/wrapper-%WRAPPER_LANG%.conf) + +3.5.4 +* Add optional support for custom public static methods in the + WrapperSimpleApp and WrapperStartStopApp helper classes. Feature Request + #2812276. +* Add a new special configuration file directive "#properties.debug" which + enables debug output about the properties as they are loaded by the + configuration file. This can be useful to tell if and why certain properties + are being overwritten. Feature Request #3042959. +* Fix a minor problem where the "#include.debug" configuration file directive + was sticky so it would be enabled when the configuration file was reloaded + even if the reloaded configuration file no longer had the directive set. + This was only an issue if the wrapper.restart.reload_configuration property + was set. +* Messages about missing included configuration files that were output when the + #include.debug" configuration file directive was active were being logged at + the ERROR level even though they were not problems. +* Fix a minor problem where the WRAPPER_JAVA_HOME environment variable was not + correctly being set to final when it was set internally by Wrapper. This + could lead to unexected results if the user overwrote it later in their + configuration file. +* Fix a problem on AIX and z/OS, when running the Wrapper without any + arguments. The Wrapper was attempting to use the default wrapper.conf file + but the check for the file was failing causing the Wrapper to continue even + though the file did not exist. This caused a confusing error message to be + displayed, but was otherwise harmless. +* Clean up some debug code associated with sleeping where log output was being + queued when it did not need to be. +* (Standard, Professional) Consolidate some redundant code associated with + waiting for interfaces on startup. +* (Professional) Fix a problem in the email feature of the Wrapper where a + subject of more than 27 bytes in length when encoded as UTF-8. This was + caused by a miscalculation in the Base64 conversion of the subject. +* (Professional) Fix a problem when the WrapperManager.exec method which takes + an array of command elements was called on Windows. The command elements + need to be combined into a single command line, but if any of the elements + contained spaces, the resulting command line was not being correctly quoted. +* Add a new wrapper.java.command.resolve property to control whether or not the + Wrapper tries to resolve any symbolic links in the Java command, specified + with the wrapper.java.command property. Historically, it has always done so, + but some jvm started applications like run-java-tool on Gentoo will fail if + it is run directly as they have a check to make sure it is launched via a + symbolic link. +* Fix a problem on Windows versions where a path to the Wrapper binary, + including the Wrapper binary itself, which was more than 100 characters would + cause a buffer overflow when installing the service. A second similar + problem would happen if the same path was more than 128 characters, whenever + the Wrapper was launched. These were both very old issues and only happened + on 32-bit versions of Windows XP and 2000. Microsoft documentation says that + the issue should also exist on the 64-bit versions, but we were unable to + reproduce it there. Newer versions of Windows are all fine. + +3.5.3 +* Fix a typo in the UNIX shell scripts that was causing "command not found" + errors to be shown when running the Community Edition. +* Add new wrapper.console.fatal_to_stderr, wrapper.console.error_to_stderr, + and wrapper.console.warn_to_stderr properties to control whether the output + at the FATAL, ERROR, and WARN log levels go to stdout or stderr. In the past + they all went to stdout. With this change, FATAL and ERROR log levels now + default to stderr output. +* Fix a problem where the shell script would produce unexpected results if the + Standard or Professional Edition shell scripts were used with the Community + Edition Wrapper. Fix was in Wrapper binary by changing the default ERROR and + FATAL log level console output to stderr rather than stdout. +* (Standard, Professional) Fix a problem where script error message output was + not being shown if the wrapper.conf file specified in the script did not + exist. +* Fix a problem where errors from failed forks on Windows were always being + flushed immediately rather than honoring the value of the + wrapper.console.flush property. +* Fix a problem on Windows 2000 systems where a new feature added in 3.5.2 was + preventing the Wrapper from running because the API used was too new. +* Change the font of the wrapperw dialog in order to have prettier output of + multibyte characters. +* Add a line feed after the first message when starting the Wrapper from the + UNIX script. +* Add a note in the debug output so the configured java temporary directory is + always logged to help with debugging. +* Add a workaround for a bug in both Sun and IBM JVMs which cause an invalid + exception to be thrown when a socket is already bound. It had been causing + the Wrapper to report: "Unexpected exception opening backend socket: + java.net.SocketException: Unrecognized Windows Sockets error: 0: JVM_Bind": + http://bugs.sun.com/view_bug.do?bug_id=6965962 +* Add the encoding of the subjects in the event mails to be always UTF-8 + Base-64 encoded. +* Add new wrapper.event..email.smtp.auth.type, + wrapper.event..email.smtp.auth.userid, and + wrapper.event..email.smtp.auth.password properties which make it possible + to do LOGIN and PLAIN connection authorizations. Currently SSL (STARTTLS) + connections to the mail are server are not yet supported. +* Fix a buffer overflow while loading the configuration file on Mac OSX + versions. Introduced in 3.5.0. +* Fix a several memory leaks on UNIX versions that were added in 3.5.0, as well + as a few others on all platforms, some from quite early versions. +* Fix some places where a resolved path of exactly MAX_PATH characters in + length could have resulted in a buffer overflow. +* (Standard, Professional) Fix a memory leak disposing language packs. +* Go through and increase the consistency of text strings. +* Fix a problem on HP-UX where the Wrapper was logging a warning that the + configured JVM was invalid if it was a PA-RISC 2.0 binary. Bug #3037317. +* Fix a problem where the WrapperManager was failing to trap and hide errors + initializing the MBean server on some JVMs that did not support it. + +3.5.2 +* Added new command line argument "--" . All arguments following will be + preceded and passed to the java application. The arguments are attached after + the arguments used in wrapper.app.parameter. +* Fixed a problem in the shell script which could lead to report failed starts + of a daemon incorrectly on the command line. +* Implemented some small logic in the wrapper script which tries to change the + permissions of the wrapper binary to executable if it wasn't set. +* The Demo Application had problems locating the right conf file on Unix + platforms and failed to launch the internal demonstration wrapper process. +* Improved the error message logged if the Windows version of the Wrapper exits + with an internal error. It now logs more information about the internal + state of the Wrapper as well as saving a mini dump file which can be sent to + support to make it easier to diagnose the cause of the problem. +* Fix a problem where the names and displayNames in WrapperWin32Service + instances were corrupted. List affected the WrapperManager.listServices() + and WrapperManager.sendServiceControlCode() methods. There was a similar + problem with the WrapperManger.getUser(), WrapperManager.getInteractiveUser() + and WrapperManager.setConsoleTitle() methods. Introduced in 3.5.0. +* Fix a problem on Windows where wildcards would sometimes fail to be resolved + or cause the Wrapper to crash. This affected the generation of classpaths + and logfile rolling. Introduced in 3.5.0. +* Fix a problem on UNIX platforms where some error messages related to a failed + fork to launch the JVM were not being logged correctly. Introduced in 3.5.0. +* Fix a problem where invalid characters in configuration files that did not + declare an encoding could cause the Wrapper to crash on startup. This could + be issue for users upgrading from versions prior to 3.5.0 as older versions + did not do any character set translations and would not have had a problem. +* Fix a problem in code to check whether a drive was a mapped network drive or + not was failing. This would sometimes lead to a false warning that the drive + could not be identified. Introduced in 3.5.0. + +3.5.1 +* Start using a system function to determine whether or not a character in the + configuration file is a control character or not. This way it works on + non-ASCII platforms. +* (Standard, Professional) Fix a crash on Windows platforms caused by freeing + up unallocated memory while loading the Wrapper's configuration. +* Add debug output describing the system and current code pages on Windows + versions to help understand encoding and mojibake issues. +* Add a Japanese localized src/conf/wrapper.conf.in_ja template configuration + file to assist Japanese users. +* Fix an potential deadlock on UNIX platforms if the JVM exits while logging + is taking place and the wrapper.debug or undocumented wrapper.state_output + properties were set to TRUE. +* Fix a problem where a failed JVM invocation that happened immediately after + being launched would cause the Wrapper to immediately give up and shutdown. + This should not have been possible with normal JVM configurations, but was + turned up in testing. +* Fix a problem where some startup output of the tick timer debug output was + corrupted. This was only an issue if the wrapper.tick_output property was + set. +* (Standard, Professional) Rework the way text is localized so that mallocs + are all handled on initialization. 3.5.0 had problems with occassional + freezes and crashes caused by mallocs being made within signal handlers. + The problems were rare unless debug output was enabled. +* Greatly simplify signal handlers on all platforms so they now do as little + as possible, actions are now queued and then processed by the main thread. +* (Standard, Professional) Fix a problem where the hostname in the + wrapper.syslog.remote.host couldn't resolve a hostname (IP Address was + working). +* (Standard, Professional) Add debug output on Windows versions to help debug + localization and code page issues. +* (Standard, Professional) Fix a localization problem on Windows where the + Wrapper was using the system-wide language rather than that of the current + process. This was resulting in mojibake if the Wrapper was launched in a + new console if that new console did not support the required code page. + This was only an issue if the user had changed the active code page from the + default for their Windows desktop. +* Fix a problem on Windows platforms where the JVM child output was being + logged with an extra line feed in some cases. +* Fix a problem running the DemoApp from some Network shares on Windows. +* Add a new WrapperManager.isWindows() and WrapperManager.isMacOSX() methods to + make it easy to write such conditional code as both platforms can require non + standard code. +* (Standard, Professional) Make the GetHostId.bat file more robust so it can be + executed using a relative reference from the command line. It also now + supports Delta-pack style Wrapper binary naming and falling back to use which + binaries are available. +* Change the timing of when the wrapper.working.dir is set so that any error + messages generated while loading the configuration will be logged to the + correct log file. It is also needed for Standard and Professional Editions + to ensure that the language pack is loaded from the correct location so that + all such messages will be in the correct locale. +* Fix a problem on UNIX platforms where the status command was failing with an + error due to the localization feature added in 3.5.0. + +3.5.0 +* (Standard) Added the ability to detect and react to thread dead locks in + the JVM. Requires at Java 1.5. Added wrapper.check.deadlock, + wrapper.check.deadlock.interval, wrapper.check.deadlock.action, and + wrapper.check.deadlock.output properties to configure the detection. +* (Professional) Add a new jvm_deadlock event which is fired in response to + a thread deadlock being detected within the JVM. +* Fix a problem where the intervals like wrapper.ping.interval were all + rounding down causing them to function at a rate one second shorter than + configured. +* Fix a minor memory leak calling WrapperManager.exec. +* Add a new wrapper.filter.message. property which can be used to control + the message which is logged when a wrapper.filter.trigger. is matched. +* Rework the way actions are fired for the wrapper.filter..action and + wrapper.timer..action properties so there is no longer any duplicate + code. +* Modify the way the wrapper.filter..action and wrapper.timer..action + properties work so it is now possible to specify a list of actions which will + happen in the order specified. This makes it possible to do a thread dump + and then restart in response to an error. +* Add the DEBUG action to the wrapper.filter..action property for + consistency. +* (Professional) Implement the ability to fire user defined events as actions + of the wrapper.filter..action, wrapper.timer..action, and + wrapper.check.deadlock.action properties. +* Fix a problem in the WrapperProcess.destroy() function, when a child process + couldn't have been forcibly killed. +* Add GetHosdID.bat file for Windows platforms. This file will open a dialog + displaying the HostId of the machine. +* Fix a problem in the shell script which, on a rc based OS, could lead to + problems starting the Wrapper when the APP_NAME in the script contained + capital letters. +* (Standard, Professional) Added a set of wrapper.share..* properties which + makes it possible to configure the Wrapper to map specific network resources + before launching the JVM on Windows. +* Corrected the way the Wrapper is installing itself as a Windows service when + the binary and/or conf file are located on a Mapped Drive. +* Add new wrapper.pausable and wrapper.pausable.stop_jvm properties to control + whether the JVM can be paused or not, and deprecate the use of the Windows + specific wrapper.ntservice.pausable and wrapper.ntservice.pausable.stop_jvm + properties. +* Modified the wrapper.commandfile property so PAUSE and RESUME commands are + now supported on all platforms at any time. +* Modified the wrapper.filter.action. property so PAUSE, RESUME, and DEBUG + actions are now supported. +* (Professional) Modified the wrapper.timer..action property so PAUSE and + RESUME actions are now supported. +* (Professional) Modified the wrapper.event..command.block.action + property so PAUSE and RESUME actions are now supported. +* Added new WrapperServicePauseEvent and WrapperServiceResumeEvent classes to + make it possible to notify an application that it has been paused or resumed. + To date, this was only possible when the Windows service was paused or + resumed +* (Professional) Add new "wrapper_pause" and "wrapper_resume" event types that + are fired whenever the Wrapper is paused or resumed. +* Enhance the command line parsing for the WrapperManager.exec() command. The + single commandLine version of the exec call will handle quotes more + sophisticated. +* Modified the wrapper.syslog.ident property so its value is now silently + trimmed to 32 characters in length. This is needed to avoid problems with + syslog servers. (See RFC 3164 section 4.1.3) +* (Standard, Professional) Add new wrapper.syslog.remote.host, + wrapper.syslog.remote.port, and wrapper.syslog.local.host properties which + can be used to redirect all syslog (UNIX) or Event Log (Windows) output to + a remote syslog server. +* Add support for the wrapper.syslog.facility property on Windows so remote + syslog entries can be sent from Windows versions of the Wrapper. +* Add debug output from the WrapperManager class which shows information about + the current wrapper.jar, including its size, location, and MD5 hash. +* Add a check for wrapper.java.classpath. entries containing quotes. The + quotes are now stripped and a debug warning logged. +* (Standard, Professional) Increase the size of the wrapperw.exe error dialog + so it is a little easier to read. Also changed the font for the same reason. +* (Standard, Professional) Modify the title of the wrapperw.exe error dialog + so it now includes the value of the wrapper.displayname property. +* Add a new check for the writability of the configured Java temporary + directory. When running on Vista, it will not be writable and can lead to + errors. +* Added new wrapper.java.tmpdir.required and wrapper.java.tmpdir.warn_silently + properties to control whether the temporary directory is required. +* Add a note in Wrapper conf template showing users how to configure a specific + Java version to use. +* Add a new CLOSE_SOCKET command to the wrapper.commandfile property and added + a new wrapper.commandfile.enable_tests property to control whether it can be + used. +* Fix a debug warning while shutting down caused by one thread closing the + backend socket when another expected it to be open. +* If the backend socket between the JVM and Wrapper is somehow closed, make the + JVM respond promptly by restarting the JVM immediately to resync with the + Wrapper. Added log information to make it clear when this happens. +* Add a new wrapper.use_tick_mutex property to control whether or not the + internal tick timer is protected by a mutex. (Experimental) +* (Standard, Professional) Add support for zLinux 32 and 64-bit on s390 and + s390x systems. +* Change the internal encoding of the wrapper from native characters to + UNICODE. This will allow the wrapper to handle correctly any characters in + its configuration file and internally. +* (Standard, Professional) Implement a new WrapperResources class which makes + it possible for user applications to localize their applications using the + same methods common to native applications. The Wrapper uses this internally + to provide localized versions of the Wrapper. +* Remove the old org.tanukisoftware.wrapper.resources package as it is no + longer being used. +* (Standard, Professional) Add Japanese language pack to localize the Wrapper + to Japanese. +* Fix a problem in the WrapperManager class where unexpected exceptions thrown + in the main socket loop were being thrown away without being logged. +* Make the Wrapper more robust against failures removing the Wrapper's shutdown + hook on shutdown. It had been causing the clean shutdown of the JVM to fail + as an unexpected exit. +* Fixed a problem in the wrapper customize code where customized multi layer + icons may get scaled incorrectly by Windows. Bug #3015848 +* Modify the wrapper.on_exit. property so it is now possible to PAUSE the + Wrapper when a JVM exits. This delays the restart of the JVM until the + Wrapper is resumed. +* Add a new log format, 'U', which logs the approximate time in seconds since + the Wrapper was launched. It is based on the internal tick timer and will + be a few percent lower than the actual number of seconds that have elapsed. + Added to the wrapper.console.format, wrapper.logfile.format, and + wrapper.logdialog.format properties. +* Fix a problem where deleting the configured anchor file was not recognized + if the JVM was not running at the time. The Wrapper was not noticing that + it was missing until the next JVM was launched. +* (Standard, Professional) Add a new NOTICE log level which is used to log + license related message. These will only show up trial license log output. +* (Standard, Professional) Add a new default 15 minute trial license which + can be used out of the box on any machine without the need to register and + obtain a trial license or purchase a license. +* Add a new wrapper.environment.dump property which will dump all of the + environment variables to the log, along with where the variables originated. +* Force the Wrapper to flush all logfile output for each line until the + configuration file has been loaded. There is normally no output up to this + point anyway, but this ensures that any errors are logged in a consistent + location. +* Move the registration of the main thread with the logging system up a bit + to make it possible to perform low level debug logging earlier. +* Add a set of checks to display an error and prevent the Wrapper from starting + if the batch or shell scripts being used are based on the TestWrapper demo + application. It is important that the user start with the default template + files in the distribution src/bin and src/conf directories to make sure the + integration goes smoothly. + +3.4.1 +* Fix a problem where the wrapper.app.parameter.1 property was always being + set to an empty string when launched from launchd on Mac OSX. It worked + correctly with the testwrapper application, but would fail for most user + applications. +* (Professional) Fix a potential synchronization problem in the log output + of the tick timer, event command and event email code. This had been in + the code since 3.3.0 without any reports of problems. +* Improve the error message displayed when the configured wrapper.java.command + can not be found on the path. +* Log the name of the signal in the logs when a SIGSEGV is detected from the + JVM. It had been logged as Unknown. +* Add some comments to the wrapper.conf.in template to help users get started + with a new license key file. +* Add some default properties to the wrapper.conf.in template file to make + it easier to get up and running with out of memory checks, as well as + sending out alert emails. +* Add a small debug message when running wrapperw.exe so it is clear which + wrapper binary is being used. +* Fix a problem where the wrapper.logfile.maxfiles was not being applied on + startup when the current log file did not yet exist. This was true for any + wrapper.logfile value which contained a unique value like a PID. +* (Professional) Fix a problem in the WrapperProcess.destroy() function, where + the function was failing to forcibly kill the child process. +* (Standard, Professional) Add a new GetHostID.bat file for Windows platforms. + This file will open a dialog displaying the HostId of the machine to help + new users get up and running. +* Add a new WrapperManager.appearOrphan() method which can be used to test + how the WrapperManager shuts down the JVM in the event that the Wrapper + process ever crashes or freezes. This was not due to any particular problem + and was added to fill out our test coverage. +* (Professional) Fix a problem where event commands were being left in a + defunct state after execution on UNIX platforms. +* Fix a potential problem on 64-bit versions where the internal tick timer was + not rolling over in the same way as the 32-bit versions. This has been in all + previous versions but is not known to have caused any problems. It was fixed + for consistency. +* Modify the WrapperManager class so it will now request a JVM hard exit only + after 4-times the ping timeout, in seconds, since the last contact with the + JVM. This had been 90 seconds more than the ping timeout, which was not + always long enough when the wrapper.ping.timeout was very large. +* Shift the initial start point of the tick counter so it will roll over 50 + seconds after startup. This shouldn't be noticeable to the user, but makes + sure that this roll over is much better tested as it always happens the first + time on startup. +* Add a new wrapper.java.classpath.use_environment property which makes it + possible to set the classpath using the CLASSPATH environment variable rather + than including it on the Java command line. +* Fix a problem where requests to shutdown the Wrapper when in the state where + it was just about to launch a JVM would being ignored. This was fixed in all + editions, but was only an issue in the Professional Edition when the + wrapper.event.jvm_prelaunch.command.on_exit.=SHUTDOWN property was being + used with the wrapper.event.jvm_prelaunch.command.block=TRUE property and the + command exited with a code that triggered the SHUTDOWN. +* (Professional) Add a new wrapper.event..command.block.action + property to control how the Wrapper behaves when a command does not complete + execution within the configured timeout. Accepted values are CONTINUE, KILL, + KILL_SHUTDOWN and KILL_RESTART. Default value is CONTINUE. +* (Professional) Made it possible to use the "default" event type with the + wrapper.event..command.loglevel, + wrapper.event..command.block, + wrapper.event..command.block.timeout, + wrapper.event..command.on_exit., and + wrapper.event..command.on_exit.default properties. +* Fixed a single byte buffer overflow which takes place just before the JVM is + launched on Windows platforms. This was introduced in 3.4.0. +* Add a message pointing the user to the Wrapper's log file when the service + fails to start after it has been launched. +* Update the debug message displayed when attempting to open the backend socket + if the port is unavailable so it doesn't seem like a problem. It will retry + with another port. +* Work around an issue on some Solaris problems where the shell script would fail + if /usr/ucb/ps wasn't installed. +* Fix a problem on UNIX versions where the Wrapper would fail to resolve the + correct location of java if it was located using the system PATH, and that PATH + element pointed to a binary via a relative symbolic link. This was introduced + in 3.4.0. + +3.4.0 +* Increased the minimum supported Java version from 1.2 to 1.4. +* Removed the reflection code used to manage the Wrapper's shutdown hook. + The Runtime methods are now called normally. +* Add a note in the TestWrapper shell script to make it clear that the user + should always use the scripts in the WRAPPER_HOME/src/bin/ directory when + generating scripts for their own applications. This became more of an + issue because of changes to the TestWrapper script starting with 3.3.8. + Bug #2902843. +* (Professional) Added new WrapperManager.exec methods and a new + WrapperProcess class to allow the launching and management of child + processes through the Wrapper rather than using the standard Runtime + class. This makes it possible to clean up child processes when the + JVM crashes or is restarted, as well as solves severe memory issues with + the way processes are forked on some UNIX platforms. +* (Professional) Added a new wrapper.child.status.loglevel property to make + it easier to debug the status of child processes. +* (Professional) Added a new wrapper.child.count.interval property to control + the interval at which the number of waiting child processes is logged after + a JVM is shutdown. +* (Professional) Added a new wrapper.jvm_cleanup.timeout property to control + the amount of time that managed child processes still running after the JVM + has exited are given to shutdown before being forcibly killed. +* Fix a problem with WrapperManager.getUser() and getInteractiveUser() methods + which was preventing us from using the latest compiler on 32-bit Windows. + (The compiler used to build 32-bit Windows versions was rolled back to the + version used through the 3.3.5 release in 3.3.9 to work around the problem + that was causing these methods to always return null.) The change in this + version should not affect how the Wrapper functions. +* The old compiler used in 3.3.9 for Windows 32-bit builds was causing a false + hit on Symantec security software. The new compiler causes the Wrapper to + once again pass without any warnings. +* Added a warning while starting up the JVM which will be displayed if the + Java command in wrapper.java.command does not point to a valid java binary + file. +* [Changed the scripts and batch files to try to load the wrapper executable + according to the system, i.e. on 64-bit systems the 64bit binary of the + wrapper is attempted to be executed first and after this fails the 32 bit + gets attempted to be loaded. This behaviour is primaly useful with the + delta-pack of the wrapper.] +* Add some debug output logging the current timezone to assist supporting + time related problems. +* (Standard, Professional) Added a new wrapper.license.debug property which + will log information about the license key(s) being tested on startup. +* (Standard, Professional) Fix a problem where license validation was + failing at certain times of the day for servers with timezones east of + Japan. Development license keys which have their update times obfuscated + were not affected. This was a problem introduced in 3.3.7. +* Fixed a bug in the wrapper shell script which occured when running the script + on a Solaris within a non singlebyte locale. Bug #2910327 +* Fixed a potential bug in the wrapper script where requesting a Thread Dump, + the shell is sending the wrapper console to the background and returning + with a prompt. +* Fix a problem where Java log output would sometimes get extra line feeds + when under heavy load. +* Fix a problem which was leading to a resource not found error when trying + to start a service, if it was installed on a Mapped Network Drive under + Windows. +* Added some advice messages recommending the use of UNC paths if a resource + located on a Mapped Network Drive is used in the configuration file on + Windows versions. +* (Standard, Professional) Fix a bug in the wrapper binary customization, + which occured when the source binary file was set to read only. +* Fixed a null string problem in the error message if a classpath value + wasn't found. +* (Professional) Added the option to include a logfile into the wrapper's + EmailNotification mails via wrapper.event.default.attach_log=TRUE +* Improve the message displayed when a user calls + WrapperManager.requestThreadDump() in JVM which does not have console + window on Windows. This should never be an issue when run with the Wrapper, + but can happen if running standalone without the wrapper binary. +* Update the Windows batch scripts so they now take AMD64 and IA64 + architectures into account when deciding which version of the Wrapper to + run when using the delta pack. +* Update the UNIX shell script so it now does a much better job of resolving + the ideal platform bits to run the correct version of the Wrapper when using + the delta pack. Thanks to Leo Leung for the patch. +* Add install and remove commands to the UNIX shell script so it is now much + easier to install and remove the Wrapper on many UNIX plattforms to start + and stop on system startup and shutdown. Thanks to Leo Leung for the patch. +* Update the Windows batch scripts so it is now easier to change the location + of the Wrapper configuration file or rename the Wrapper binary when using + the scripts. +* Added a new QueryApp-NT.bat.in template script which can be used to query + the current running status of the service on Windows. +* (Standard, Professional) Add a new "-u" or "--update" command to the Wrapper + which allows you to effectively reinstall the Wrapper as a service without + having to provide the account credentials if the service is running as a + specific user. This is very useful for installers upgrading an application + to a new version. +* Add a new "update" command to the Windows AppCommand.bat.in batch script. +* Go through and clean up the messages displayed when controlling the Wrapper + as a service so the messages are consistent and more meaningful to the user. +* Add wrapper.wait_for_hostid and wrapper.max_hostid_timeout property. + This properties set if and how long the wrapper shall wait when starting up + until the hostid is available. This is important to make sure that server + licenses are validated correctly on UNIX platforms as the OS is booting up. +* (Professional) Fix a problem where environment variables referenced in + property values were not being expanded correctly the first time they were + referenced if the property's value was a default value. The underlying fix + was in all editions, but this was only causing a problem in the Professional + Edition where the WRAPPER_HOSTNAME variable was not being expanded in the + subject and body of emails sent out for the "wrapper_start" event if the + defaults were used. +* (Professional) Fix a problem where backslashes in the body of emails, + configured with the wrapper.event..email.body property, were + not being handled correctly when displaying things like paths from + environment variable references. +* Fix a problem on UNIX platforms where the Wrapper was displaying an error + about not being able to locate the configuration file when the Wrapper was + run without any arguments. +* (Standard, Professional) Improve the message shown when a License Key is not + found. +* Add a new message to the Community Edition when the user requests a HostId. +* Add WAIT_FOR_STARTED_STATUS and WAIT_FOR_STARTED_TIMEOUT to the script. This + lets the script wait (up to timeout sec.) when starting a daemonized process + until the process has been started. + Thanks to Dobes V. (Feature Requests 2917391) +* Improve the error message displayed when the user tries to run the Wrapper + with the internal -s or --service commands. +* Fix a problem where the WrapperSystemPropertyUtil.getBooleanValue() method + was not correctly returning the specified default value when the looked up + property was missing. Also added a new getStringValue() method. +* Improve the error message displayed when the user tries to install and remove + the wrapper as Service on Windows Versions after Windows Vista. +* Add an advice message when MacOSX applications launched with launchd + encounter a "Returning nil _server" error when displaying a GUI. + +3.3.9 +* Modify the way JNI functions are called from within the native library so + they work correctly on platforms which are not ASCII based. +* (Professional) Add support for IBM z/OS 390 servers. This is still an alpha + release and will be made available on request. +* Improve the message displayed when a server license key is used on a + different server. +* Add a minimum max file size of 1024 bytes to the wrapper.logfile.maxsize + property to avoid the log file rotating too often if the user enters a + very small value. +* Add a message that shows up in the console when the Wrapper fails to write + to the configured log file. As with previous versions, it will then fall + back to writing to wrapper.log in the current working directory. +* On UNIX platforms, automatically set the wrapper.disable_console_input + property when wrapper.daemonize is set. +* Fix a problem introduced in 3.3.8 where relative configuration file includes + were failing to resolve correctly when the wrapper.working.dir and + wrapper.daemonize properties were used together. The wrapper.daemonize + property causes the configuration to be loaded twice on startup and the + working directly was not being reset correctly before it was loaded the + second time. +* Fix a problem introduced in 3.3.8 where wildcard selection of files on + Windows failed in some cases. +* Fix a problem introduced in 3.3.8 where setting the wrapper.logfile.maxfiles + property to 0 was causing all log files to be deleted rather than none when + the ROLLNUM file pattern was used. +* Revert the way rolled log files are deleted when using the ROLLNUM file + pattern to the functionality used in versions 3.3.7 and earlier. Files such + as wrapper.log.bak or other files manually created with the same log file + base name were also being deleted with 3.3.8. +* (Standard, Professional) Fix a problem where the close window button in the + title of the WrapperW log dialog was not correctly cancelling the dialog. +* (Standard, Professional) Fix a problem where the WrapperW log dialog would + sometimes fail to show itself on top of other windows if the splashscreen + had been displayed on startup. +* Fix a problem on 32-bit Windows versions where starting with he Wrapper, + the WrapperManager.getUser() and getInteractiveUser() methods were always + returning null. This problem was introduced in version 3.3.6. +* (Professional) Fix a buffer overflow when sending alert emails to multiple + addresses. This would sometimes cause crashes in versions 3.3.7 and 3.3.8 + when sending alert emails to even a single recipient. Because of the nature + of the overflow, in most cases did not cause any problems. + +3.3.8 +* Add the new start type DELAY_START for NT Services, which startes the service + after other auto-start services are started plus a short delay. +* Fix a problem where the Wrapper's PID file was not being set to the correct + PID when the wrapper was launched daemonized. With the shell scripts that + ship with the Wrapper, this means that it would not be possible to stop the + Wrapper using the script because the expected PID did not exist. This was + a problem introduced in 3.3.7. +* Changed the timing for the wrapper's splashscreen when the splash screen + mode was set to JVM_STARTING. Now the splashscreen will disappear when the + JVM has been initialized. +* Fix a problem where the splashscreen was being shown when starting a service + or performing other command line operations. +* Remove some extra debug output on startup for Mac versions. +* Fix a crash in the Community Edition on PPC platforms of the Mac OSX version. + This crash has been reproduced in all versions starting with 3.3.1. The OSX + distribution is a universal binary but does not appear to result in a crash + on x86 servers. Standard and Professional Editions were not affected. +* Fix a problem on Windows versions where problems accessing the registry were + not reporting the correct error message. This did not cause any problems in + and of itself, but it made it difficult to track the down the cause. +* When wildcards are used in the classpath elements, the list of matching jar + files included in the classpath are now sorted to ensure that their order + is consistent across installations. Normally it would not matter, but if + certain classes or resources are redefined in multiple jars this will ensure + that the application now always works the same. +* Fix a problem where wrapper.logfile.maxfiles was being ignored when + wrapper.logfile.rollmode=JVM was set. +* Changed the way the wrapper.logfile.maxfiles property works with the ROLLNUM + token. Now when the log files are rolled, all files greater than maxfiles + count will be deleted. Previously, the Wrapper would roll within the maxfiles + count and ignore extra files if they existed. This would cause extra files to + be left around if the maxfiles property value was decreased. +* Add new wrapper.logfile.purge.pattern and wrapper.logfile.purge.sort + properties which make it possible to limit the number of log files in some + advanced cases. Be sure to read the documentation before playing with them. +* Fix a potential crash when referencing non-existent environment variables + in the value of wrapper.logfile. +* Modify the way all properties used to define file names behave so that + undefined environment variable references will now have their '%' characters + replaced by '_'. This is to avoid problems caused by file names containing + '%' characters on some platforms. +* Fix a problem introduced in 3.3.6 where the windows shown by the JVM or its + child processes could not be displayed when running in iteractive mode. +* Rework the TestWrapper application a bit so it can now be run in headless + mode for testing. +* Fix a problem on some UNIX platforms where the shell script was showing an + extra '-n' when run with the "start" command. +* Fix a problem for FreeBSD which could cause the wrapper being unable to stop + the wrapper daemon if the ps command buffer size (kern.ps_arg_cache_limit) + was too small to contain the wrapper command line. + +3.3.7 +* (Professional and Standard) Added the ability to customize the wrapper.exe + and wrapperw.exe binaries on Windows with a user specified icon and splash + screen. +* (Professional and Standard) Added a new wrapper.splashscreen.mode property + to control how long the splashscreen is visible. +* Fix a problem on SELinux where a strict security policy could lead the + Wrapper fail to load the libwrapper library. Thanks to Jean for the hint. +* Fixed a problem in the obfuscated license date, which caused a license to + be reported as invalid if run in a timezone west of Japan. This feature + was implemented in 3.3.6, but disabled on the site until the release of + version 3.3.7. Thanks to Leo for the hint. +* Added a new WRAPPER_PID environment variable. Feature Request #2870136. +* Added a new WrapperManager.getWrapperLogFile() method and + WrapperLogFileChangedEvent class that can be used to receive notifications + of changes to the Wrapper log file currently in use. + Feature Request #2870133. +* (Profesional) Improved the wrapper.event..email.to property + so it now supports the ability to specify multiple recipients. + +3.3.6 +* Introduce the wrapper.timezone property. This property allows to set the + timezone in which the wrapper will be executed. This property is available + in the standard and the professional Edition of the Java Service Wrapper. +* Fix a potential problem on Windows platforms where a failure to register the + control handler was not being logged. If this happened, user logoffs would + not be trapped correctly but no other problems would have occurred. +* Fixed a problem in the shell script on Solaris platforms where a deep + directory structure would cause the script to incorrectly report that the + Wrapper was not running. That could lead to multiple zombie copies of the + Wrapper and its application running in memory. + Bug #1664303. +* Fixed a problem in the shell script on HP-UX platforms where a deep + directory structure would cause the script to incorrectly report that the + Wrapper was not running. That could lead to multiple zombie copies of the + Wrapper and its application running in memory. + Bug #2812207. +* Improve the error message displayed when there are problems initializing the + main class specified for the WrapperSimpleApp, WrapperStartStopApp, or + WrapperJarApp helper classes. +* (Professional) Add new wrapper.event..loglevel and + wrapper.event..message properties which can be used to output + a customizable message like "Event: " to the logs at an arbitrary + log level. +* Add debug output in the Windows version to log the full path of the native + library. +* Add a new wrapper.java.detect_debug_jvm property which will control whether + or not the Wrapper disabled timeouts when java's debugger is in use. The + Wrapper has automatically disabled timeouts in this case since version 3.2.1. + Thanks to Ruslan Gainutdinov for the patch. +* Fix a buffer overflow problem for values of wrapper.ntservice.name. +* Fix a problem with where the wrapper.syslog.ident property was not working + as documented. It had been necessary to use wrapper.ntservice.name in its + place. +* Add a new wrapper.ignore_sequence_gaps property which makes it possible to + have non sequential numbered property values. This affects the + wrapper.app.parameter., wrapper.event..command.argv., + wrapper.filter.trigger., wrapper.java.additional., + wrapper.java.classpath., wrapper.java.library.path., + wrapper.ntservice.dependency., and wrapper.timer..interval, + properties. The default value maintains past functionality. + Feature Request #1930298. +* (Professional) Fix a problem where the Event Handler Variables + were not being set correctly in the values of the + wrapper.event..command.argv. properties. +* (Professional) Fix a potential access violation if memory allocations fail + while sending event mails or executing event commands. +* Add a new WRAPPER_HOSTNAME default environment variable as an alias of the + existing WRAPPER_HOST_NAME variable. +* (Professional) Deprecate the use of the wrapper.event..set_environment + property. It will remain in the product for compatibility but its use is + discouraged as it does not always work correctly when the configuration + file is reloaded. See the property's documentation for more details. +* Add a new DUMP action to the wrapper.filter.action. property to make it + possible to invoke a thread dump in response to a specific string in the + JVM's console output. +* Fix a problem where the WrapperManager.stopAndReturn method was dead locking + if called when the JVM was being run in standalone mode without the Wrapper. + Bug #2711872. +* (Standard, Professional) Modify the way the licensing times work so the + Wrapper compares the License Upgrade Term to an official release date + rather than the build date. This makes it possible to release additional + platforms at a later date while keeping the use of a license consistent. +* (Standard, Professional) Make it possible to obfuscate the upgrade term in + License Key files for development licenses so it is not visible to an end + user what the development license holder's upgrade term is. This feature + is accessed from the License Management Page when downloading a new or + existing License Key file. + + +3.3.5 +* Fix a problem on some UNIX platforms introduced in 3.3.4 where the Wrapper + would crash on startup if the configured JVM could not be found. +* Fix a problem introduced in 3.3.2 where the Wrapper could crash if the + system host name was longer than 27 characters in length. +* Fix a potential problem with the way thread ids were being compared on UNIX + systems. +* Add a new wrapper.java.additional.auto_bits property which will automatically + add the -d32 or -d64 arguments to the JVM for platforms whose JVMs typically + expect the argument. +* (Professional) Fix a problem with the Date field of outgoing event emails. + +3.3.4 +* (Standard, Professional) Update the development license to version 1.1 + so that a new copyright notice file can be shipped with user applications + rather than the full license text. +* The Community Edition may be licensed under either the GPL2 based Community + License, or the Development License. The source for the Standard and + Professional Editions of the Wrapper are proprietary. + http://wrapper.tanukisoftware.org/doc/english/licenseOverview.html +* Fix a problem introduced in 3.3.2 where querying a the status of a Windows + service with the -q or -qs commands resulted in an access violation. The + running status of the service was reported correctly but additional + configuration information was failing. Bug #2644515. +* Add a new wrapper.disable_restarts.automatic property to disable only + restarts caused by JVM timeouts, crashes, etc. Manual or configured + restarts will still be allowed. +* Switch to using make for HPUX IA 32/64 builds. +* Add Advice comments when the Wrapper fails to launch the JVM process. +* Fix a problem on UNIX platforms where log entries made by the forked Wrapper + process would result in two blocks of log entries in the log file because + the parent Wrapper process would think they were console output from the JVM + process. +* Add a set of new wrapper environment variables that can be referenced in the + wrapper.conf file to generate random numbers, or timestamps for use in + generating unique file names, etc. See the Default Environment Variable + definitions section for more details. +* (Standard, Professional) Fix a problem with Development licenses being + able to authorize applications using Integration Method #4 which was + added in version 3.3.3. +* Add the number of bits of the current Wrapper to the startup banner to aid + in support requests. +* (Standard, Professional) Fix a crash problem on HPUX versions which would + reliably happen on some machines at startup. + +3.3.3 +* Modify the wrapper.ignore_signals property so it now takes the values + WRAPPER, JAVA, and BOTH, in addition to TRUE and FALSE. +* Modify the WrapperManager so it is now careful never to poll the native + library once the JVM is flagged as being ready to shutdown. This is to + make sure that the JVM never exits in the middle of a call as that could + lead to the JVM crashing. +* Add a pair of methods to allow threads to request and release locks which + will prevent the Wrapper from letting the JVM exit while certain operations + are in progress. See WrapperManager.requestShutdownLock() and + WrapperManager.releaseShutdownLock(). +* Fix a problem where interactive services would sometimes leave a console + window visible when after starting up. The Wrapper is now more resilient + about closing the window even if it fails to do so initially. +* Add a new integration method (#4), which makes it easy to configure the + Wrapper to execute an executable jar file. +* Fix a problem where the random generator was not being seeded correctly + before naming the Wrapper's console window when running as a Windows + service. This was leading to problems identifying the wrapper's console + when more than one service was running on the same machine. + +3.3.2 +* Add a file information record to the wrapper.exe and wrapperw.exe binaries + so the Version tab will be displayed correctly in the Properties dialog of + the file. +* (Standard, Professional) Fix a problem with the wrapperw.exe binary where + the log dialog was not being displayed correctly when the Wrapper was + launched with the "-?", "-v", or "-h" arguments. +* (Standard, Professional) Fix a problem with the wrapperw.exe binary where + a message was being displayed in the dialog with the location of the full + log file even if there had not been any entries written to the log. +* (Professional) Improve the debug output produced when sending event emails + when the wrapper.event..email.debug property enabled. +* (Professional) Add wrapper.event..email.send.timeout and + wrapper.event..email.receive.timeout properties to avoid the + wrapper hanging waiting for a response from a remote mail server. + Bug #2064885. +* (Professional) Fix a problem where the Wrapper would get stuck and fail to + send emails with some mail servers. Problems was being caused by incorrect + linefeeds in the body of the email. +* Add a warning if the leading '#' is missing from an '#include' in the + configuration file to assist users with include file problems. +* Added a new wrapper.ntservice.generate_console property which will cause + the Wrapper to always generate a console when running as a service and + then hide it immediately. This will cause a slight flicker but is needed + to support thread dumps. Bug #2060181. +* Fix a problem in the Windows version where the console window would + sometimes be left visible when running as an interactive service even when + it was configured to be hidden. +* Add support for PowerEvents so Windows services can respond to suspend + and resume events. +* Fix a problem where the wrapper.key system property passed to the JVM was + being generated incorrectly randomly, 1 in 65536 times the JVM was launched. + This would result in the JVM failing to start and the Wrapper shutting down. +* Add a new wrapper.disable_console_input to disable to feature which allows + the Wrapper to pass console input on to the Java process. +* Fix a buffer overflow problem in the logging code which would happen once on + startup. This was benign on most platforms but was causing intermittent + crashes in the 32-bit AIX version. +* Modify the way configuration properties are parsed so that their names are + no longer case sensitive. +* Modify the WrapperServiceException so that a new getErrorCode method can be + used to obtain the integer error code which caused the exception. + Feature Request #2188280. +* (Standard, Professional) Fix a problem where on some Windows machines the + Wrapper would return a random hostId that changed each time the system was + rebooted. +* (Standard, Professional) Make it possible to define License Keys so that + their property names are encoded using either the host name or hostId. This + makes it possible to define multiple keys within the same configuration file, + visible on the same host. This was necessary to support some load balanced + network adapters where the visible hostId changes depending on the active + physical network adapter. +* Rework the Java side state engine so it is now possible for the Java side of + the Wrapper to respond to stop events while the WrapperListener.start method + is still in progress. +* Add a new wrapper.listener.force_stop property which allows control over + whether the WrapperListener.stop method is called on shutdown even if the + WrapperListener.start method has not yet completed. +* Fix a problem on Windows where the ability to start and stop the Wrapper as + a service using the Wrapper itself was requiring the Administrator permission + when a lower permission should have been possible. The Wrapper should now + allow service control to do whatever is possible from the Services control + panel. +* Fix a memory corruption error if the value of wrapper.java.maxmemory was more + than 4 digits in length. wrapper.java.initmemory did not have any problems. +* (Professional) Add a new wrapper.event..email.client.host + property which makes it possible to configure the host name sent in EHLO and + HELO commands to the SMTP server. +* Add a new default environment variable, WRAPPER_HOST_NAME, which stores the + name of the machine where the Wrapper is running. + +3.3.1 +* Add debug output showing the current os name and architecture to aid in + debugging problems. +* (Standard, Professional) Improve the message displayed when a license key + is found but is deemed to be invalid. +* Modify the template wrapper.conf file to help users debug include file + problems. +* Disable the console title feature on all UNIX platforms other than LINUX + because the console title does not get reset correctly when the Wrapper + process terminates. +* Add support for HP-UX IA64 CPUs. +* Update the license banner in source files so it is clearer that the user + is restricted by the license they agreed to. +* Modify the Community edition so it will now display a Licensed to banner + on startup if shipped with a Development License. This is required to + enable the distribution of the Community Edition under the Development + License. +* (Professional) Fix a problem where the UNIX versions of the Professional + Edition would sometimes deadlock on startup when run as a daemon process. + Bug #1989355. +* (Professional) Added two new events; jvm_failed_invocation and + jvm_max_failed_invocations. Feature Request #1994718. +* Fix a problem where the Windows service exit code was not being set + correctly when the JVM exited with a non-zero exit code. The problem + could be seen by running "sc query {service}" from the command line. + Bug #1859061. +* Added support for the Windows Itanium 64-bit platform. +* Added support for the HP-UX Itanium 32 and 64-bit platforms. +* Added support for the MAC OSX 64-bit platform. +* (Standard, Professional) Fix a problem on Windows versions where servers + which reported a large number of possible host ids could cause a buffer + overflow on startup. This crash was possible when using either + Development or Server licenses. Removed duplicate host ids from the list + of possible ids. +* Add a new "condrestart" command to the shell script which will restart + the Wrapper only if it is already running. Feature Request #1928045. +* Fix a problem where the 64-bit Solaris x86 version was unable to load its + JNI library. Bug #1992039. +* Fix a problem on Windows versions where a frozen JVM process was not always + being killed. This could result in the zombie JVM processes being left + around that consumed memory and other resources. +* Add a new wrapper.ignore_console_logouts property which allows the Wrapper + and JVM to survive logouts when launched as a console application from + another service. +* (Standard, Professional) Add a wrapperw.exe binary in Windows implementations + which makes it possible to run the Wrapper without a console. A console still + flickers for an instant when the Wrapper starts. This is the same issue that + has existed when running as an interactive service and is required to make + thread dumping possible. +* (Standard, Professional) Add new wrapper.logdialog.enable, + wrapper.logdialog.format, wrapper.logdialog.lines, and + wrapper.logdialog.loglevel properties used to configure the display of a Log + dialog when the wrapperw.exe binary exits in an error state. +* Fix a problem where the Wrapper was attempting to reopen its backend port + even when the JVM was down. This was only a problem when the defined port + range was limited to a single port with the wrapper.port.min and + wrapper.port.max properties. In such a case one or more warning messages + were being displayed because the port is locked for a few moments after + being closed. +* (Standard, Professional) The wrapper.initmemory.percent and + wrapper.maxmemory.percent properties were not correctly being calculated + relative to a maximum of 2048MB for 32-bit versions of the Wrapper. + Bug #2053167. + +3.3.0 +* Add a new wrapper.ping.interval.logged property which makes it possible to + reduce the debug output caused by ping transactions between the Wrapper and + JVM. +* Fix a problem on Windows where the Windows Service Manager was not waiting + the full configured time of the wrapper.jvm_exit.timeout and + wrapper.shutdown.timeout properties. This was leading to the net stop + command timing out and the system shutting down without the java application + having fully stopped. Bug #1582568. +* If internal firewalls were preventing the backend socket from being created, + it was not being made clear what the cause was. It was also possible that + the JVM would deadlock on shutdown. This second problem was recovered from + when the Wrapper killed the JVM. +* Rework the console output from all Wrapper classes to make it much more + obvious that that is their source. +* Submit a patch to the UNIX sh script by Chris Dance which makes it possible + to tell the shell to wait a few seconds for the wrapper to start up. Also + includes some modifications to work correctly on older Debian and RedHat + systems. +* Fix a problem where the local copy of ant was not always being used + correctly on UNIX systems which have a default copy of any installed. + Thanks to Robey Pointer for the patch. +* Add the -lm to the command line when building Linux 32 and 64 bit versions + of the wrapper on Linux. This is to support building on recent Debian + and Ubuntu versions. Thanks to Robey Pointer for the patch. +* Add support for the SIGUSR1 and SIGUSR2 signals so they can now trigger a + shutdown, restart or be forwarded to the JVM for custom functionality. + See the wrapper.signal.mode.usr1 and wrapper.signal.mode.usr2 properties. + Based on a patch by Robey Pointer. Note that the JVM process does not + trap SIGUSR1 or SIGUSR2 signals as they are used internally by the JVM + as part of the garbage collection process. +* Fix a problem where the WRAPPER_OS, WRAPPER_ARCH, and WRAPPER_BITS + environment variables were not being initialized correctly for the various + unix platforms. +* Removed the 4096Mb upper limit set on the wrapper.java.initmemory and + wrapper.java.maxmemory properties. 64bit users need to go way beyond that. +* Fix a problem where relative include file references in the configuration + file were not working correctly if the wrapper.working.dir was used to + change the working directory. The working directory is now always reset + to its original value as the configuration file is being loaded. +* Added a new wrapper.registry.java_home property which makes it + possible to specify the location of java within the registry. +* Set a new WRAPPER_JAVA_HOME environment variable if the JAVA_HOME is + located in the Windows registry. +* Modify the way properties are looked up so that any unreplaced environment + variable references will be reevaluated in case they were set after the + property was originally set. This is possible with some WRAPPER_* + environment variables or depending on the placement of set.* properties + in the configuration file. +* Set any unset properties to their default values internally. This is + necessary so the WrapperManager.getProperties() method returns the + correct set of active properties. +* Fix an occasional crash with 64-bit UNIX caused by a native synchronization + problem when system signals are trapped. Bug #1614010. +* Fix a problem on Solaris versions, where the Wrapper was not correctly + recovering and attempting another back end port when the first was already + in use. Bug #1594073. +* Fix a problem on Solaris and AIX where the RUN_AS_USER feature of the + shell script was not working due to lack of support for the "-m" option of + su. The shell script now uses "su -". Bug #1590168. +* Add HP-UX Makefiles for building with make. Fix some problems in the + shell script to make the script work better on HP-UX. Thanks to David + Brown and Nicolas Varney for the patches. +* Fix a problem where any signals received by the JVM and triggering a + SIGCHLD signal in the Wrapper were being interpretted as the JVM having + stopped. This was not always true. Bug #1643666. +* The Wrapper is now able to detect when the JVM process is stopped and + continued. It will still timeout if stopped, but a descriptive warning + is now logged. +* Increase the maximum number of log entries which can be queued to avoid + losing them. These are only used for log entries outside of the primary + thread. +* Fix a problem in the shell script which was making it impossible to stop + the Wrapper or query its status on OSX. +* Add support for 64 bit AIX. Thanks to Nicolas Varney for supplying a + Makefile. +* Correct the AIX library extension to be ".a". +* Rename the UNIX Makefiles so it is more obvious which tool must be used + to build with them. +* Fix a problem where the HP-UX native library would not be located + correctly for some processor types if the file had a platform specific + name. +* Internally rename the WRAPPER_JSTATE_LAUNCH state to + WRAPPER_JSTATE_LAUNCH_DELAY for clarity. +* Fix a problem where the UNIX versions of the Wrapper would shutdown + rather than restarting a frozen JVM if the arrival of the SIGCHLD signal + from the old JVM process was delayed by more than a second or two. +* Rework the Windows build so it now uses nmake and a Makefile rather than + vcbuild. This is probably not as clean, but it was needed to get the + 64-bit build working. +* Fix a problem on Windows versions where quoted values specified from the + command line were not always being requoted correctly when generating the + java command line. +* Add validation checks for Windows versions to make sure that all additional + parameters, application arguments, the classpath, and library path all + contain values which are quoted correctly. Incorrectly quoted values will + now result in a warning message that will help resolve the problem. +* Fix a memory leak when calling WrapperManager.listServices() on Windows. + Bug #1665947. +* Fix a buffer overflow problem if the Wrapper was launched without explicitly + specifying a configuration file. +* Add tests of the return values of all malloc calls to catch out of memory + errors and recover as gracefully as possible. Bug #1649880. +* Modify the WrapperManager.signalStarting and signalStopping methods so that + they will never have the effect of shortening the existing timeout. + Updated the javadocs of both methods so they more accurately reflect what + the methods do. +* Move the Wrapper Copyright banner into the Wrapper process so it will be + output more consistently. +* Branch the code to support Community, Standard, and Professional Editions + of the Java Service Wrapper. +* (Standard, Professional) Add support for Server (Fixed) as well as + Development (OEM based) licenses. +* (Standard, Professional) Add 64-bit versions of the Windows version. The + 64-bit Community Edition will not be distributed initially to support + ongoing development costs. +* (Professional) Add event handling callbacks for Wrapper start/stop, JVM + start/stop, JVM started/stopped, JVM restart, JVM killed, and JVM unexpected + exit events. +* (Professional) Add the ability to send emails in response to any event + callback. +* (Professional) Add the ability to execute a a user configured command in + response to any event callback. +* Add WRAPPER_BIN_DIR and WRAPPER_WORKING_DIR environment variables which are + now available for use within the wrapper.conf file as well as by any child + processes. +* Add documentation to the integration pages about existing system properties + that be used to control the way the WrapperSimpleApp and WrapperStartStopApp + handle application startup. +* Remove support for native PPC and x86 distributions of the Wrapper for + MAC OSX in favor of the universal X-Code distribution. This appears to be + the standard for the market and saves lots of time on testing. +* Add a new '-it' command to the Windows version which makes it possible to + install and start a service as a single command. +* Fix a problem where the PATH environment variable was not being set correctly + on Windows versions when run as a service if the wrapper.ntservice.account + was set and a PATH was set for both the SYSTEM and user accounts. + Bug #1702274. +* Modify the shell script to set JAVA_HOME to the default JVM location on + OSX systems if it is not already set. OSX always places the JVM in a known + location so this is possible. Thanks to Andrew Williams for the patch. +* Fix a problem where the UNIX shell script would fail if the APP_NAME was set + to a value containing spaces. Thanks to Andrew Williams for the patch. + Bug #1695678. +* Fix a problem where the DIST_ARCH was not being resolved correctly on HP-UX + systems. Thanks to Matej Kraus for the patch. Patch #1697421. +* Log output from the timer thread was not being queued correctly, this could + have lead to timing problems if there were any delays writing to disk. +* Add partial support for OS/400 into the build file. Still needs a Makefile. +* Fix a problem where the WrapperActionServer would deadlock in its stop method + if the JVM shutdown was initiated by a call to the shutdown or restart + actions. +* Add support for wrapper.console.title on UNIX platforms. Add a set of + wrapper.console.title. properties which make it possible to set the + title based on the platform. +* Fix a problem where the wrapper.ntservice.account and + wrapper.ntservice.password properties were being stored in the system + registry if the they were specified on the command line when the Wrapper + was installed as a service. This was broken in version 3.2.2. + Bug #1538725. +* Fix a problem where the DUMP command was not working with the + wrapper.commandfile property when run as a service on Windows. + Bug #1644421. +* Fix a problem where wildcards like "*.*" or "*" in a classpath property were + including the "." and ".." files on Windows versions. Bug #1517928. +* Modify the debug log output when the Wrapper is attempting to load its native + library in an attempt to make the expected failures less threatening. +* Commit a patch by Rob Oxspring which adds the start_msg and stop_msg commands + to the shell script. These are expected by init scripts on HP-UX. + Patch #1750027. +* Add a DETAIL_STATUS flag to the UNIX shell script which will cause the + status, start_msg, and stop_msg commands to display the current internal + status of both the Wrapper and Java processes. +* Commit a patch by Rob Oxspring which adds an init block to the UNIX shell + script to make it work with install_initd and remove_initd scripts used by + SUSE linux. Patch #1750028. +* Commit a patch by Travis Carlson, ia64 systems were being grouped as "x86" + systems. They now are assigned the name "ia" which makes it possible to + create a distribution for them. Patch #1663887. +* (Standard, Professional) Add new wrapper.java.initmemory.percent and + wrapper.java.maxmemory.percent properties which make it possible to set + the initial and maximum memory values relative to the amount of physical + memory on the system. Feature Request #1741051. +* Add a new #include.debug declaration in the wrapper configuration file which + makes it much easier to debug problems with cascading include files. +* Add -l and --controlcode commands to the Windows version which make it easy + to send user defined control codes to the Wrapper running as a service. +* Fix a synchronization problem in the logging code which could result in + data corruption or access violations. +* Add version numbers to the bat and sh scripts to make them easier to + support. +* Make the wrapper.ntservice.name, wrapper.ntservice.displayname, and + wrapper.ntservice.description properties aliases of new wrapper.name, + wrapper.displayname, and wrapper.description properties as they are now + used on UNIX platforms as well as Windows. +* Fix a problem where the wrapper would sometimes fail to send a packet to + the JVM because the sending of the packet would block. Thanks to Peter + Gorgon for the patch. +* Fix a problem where CPUs identifying themselves as 'ia64n' or 'ia64w' were + not correctly being categorized as 'ia'. Thanks to Stirling Chow for the + patch. Patch #1859412. +* Add -d and --dump commands to the Windows version which make it possible to + send thread dump requests to the Wrapper when running as a service. Works + in association with the new wrapper.thread_dump_control_code property. + Feature Request #1118110. +* (Professional) Add wrapper.timer..interval and wrapper.timer..action + properties which make it possible to schedule Wrapper shutdowns, JVM + restarts and thread dumps at arbitrary times. +* Fix a problem where the default configuration file name was being corrupted + by a buffer overrun. Thanks to Rob Joyce for the patch. Patch #1879049. +* Fix a problem where the WrapperManager would sometimes attempt to unregister + its shutdown hook after the shutdown hook had been initiated. Bug #1799489. +* Fix a problem where log files were limited to 2GB on Linux systems. + Bug #1881038. + +3.2.3 +* Add support for x86 Mac OS X distributions. +* The 3.2.2 Windows version was accidentally released with its MFC libraries + dynamically linked. This meant that anyone who did not have VS8 installed + were not able to run the Wrapper due to missing DLLs. This version fixes + that snafu by correctly using statically linked libraries as was done in + previous versions built with VS6. Bug #1578554. + +3.2.2 +* Correct a typo in the usage output of the WrapperStartStopApp. Thanks to + Michael Giroux for pointing it out. +* Fix a problem on OSF1 systems where the backend socket was not listening + correctly due to a backlog of 0. This was broken in 3.2.0. Thanks to + Phillip Gussow for supplying a patch. +* Remove the com.silveregg.wrapper package classes that were deprecated in + version 3.0.0. +* Fix a potential problem in the UNIX script where the lock file permissions + were not being set correctly if the LOCKFILE and RUN_AS_USER variables are + specified but the group of the specified user could not be resolved. +* Fix a problem where the exit code returned by WrapperListener.stop was being + ignored in some cases. +* Fix a problem where the shell script would not work correctly when the + wrapper or its configuration files were located in a directory path + containing spaces. +* Apply a series of patches by Michael Saya to get the Windows 64 bit build + working. +* Fix a problem in UNIX versions where the SIGTERM handler was being disabled + when a SIGCHLD was received. +* Added support in UNIX versions for the SIGHUP signal. +* Migrated the source to Subversion from CVS. Did a bunch of cleanup in the + source, removing CVS specific tags. +* Fix a problem in UNIX versions were the pid file specified by the + wrapper.java.pidfile property contained the wrapper pid rather than the + jvm pid. Bug #1565011. +* Fix a problem in UNIX versions where the file specified by the + wrapper.java.pidfile property was not always being deleted when the JVM + process went away. +* A user encountered a JVM bug where calls to System.exit were resulting in + an IllegalThreadStateException being thrown. Added some code to trap this + and shut down the JVM using other means to avoid a hang during shutdown. +* Fix a NullPointerException caused by users incorrectly implementing + an Integration Method #3 class and then calling WrapperManager.start with + a null value for the args parameter. +* Update the banner displayed by the Wrapper on startup to include a + copyright notice. Please see the license page of the documentation for + details. +* Add a new 'Z' log format which will log the time to millisecond accuracy. +* Fix a problem where the JVM exit code was not being set correctly when + the JVM was shutdown using WrapperManager.stopImmediate(). The exit code + of the Wrapper was being set correctly through other means however. +* Fix a potential synchronization problem in the logging code if a JVM exits + with debug output enabled. +* Updated the WrapperListener.stop method javadocs to better explain the + exitCode value under certain exit modes. +* On UNIX versions, add a log message which records the signal that caused + the JVM process to exit when it terminates unexpectedly. +* Fix a problem where the wrapper.on_exit. property was not working + correctly in some cases on UNIX. With help from Andreas Schafer. +* Add support for building the Wrapper with Visual Studio 8 for Windows. + Releases will now be done using this compiler. +* Fix a CRITICAL bug in the 3.2.0 and 3.2.1 Windows versions of the Wrapper + where the Wrapper would crash under rare circumstances when running as a + service. If the service manager interrogated the service at the same + instant as the wrapper was pinging the JVM, the wrapper was sometimes + crashing due to a synchronization problem. The problem did not exist + prior to 3.2.0. Bug #1574537. +* Fix a minor logging problem where the 'D' format was not displaying the + correct thread name for queued log messages. + +3.2.1 +* Fix a problem with the solaris-sparc-64 makefile. +* Add a solaris-x86-64 makefile. +* Merge in a patch by Hugo Weber to make it possible to configure the Wrapper + to pull the JRE from the system registry on windows. +* Fix a batch file bug added in 3.2.0 where the scripts would not function + correctly if the full path to the batch file contained spaces. + Bug #1450601. +* Modify the message shown when a native library fails to load so the + exception message text is now shown in the log without having to enable + debug log output. +* Modify the UNIX shell script to be more informative if the script is unable + to locate a wrapper binary due to a executable bit permission problem. +* Fix a minor permission problem with the build for the delta-pack. +* Commit a patch by Juergen Hermann to make the error shown when realpath + fails clearer. +* Add the ability to use a default wrapper.conf file that is in the same + directory as the wrapper binary. The file will be named based on the + name of the wrapper binary. +* Synchronize the command line so that both the Windows and UNIX versions + are now the same. The old command line syntaxes are now supported + everywhere so there will be no compatibility problems. +* It is no longer possible to specify arguments using the '/c' syntax. + This was undocumented so hopefully it is not being used. The documented + '-c' syntax must now be used. The change was necessary to synchronize + the command line between UNIX and windows platforms. +* The 32-bit HP-UX 3.2.0 build was generating a libwrapper.so file rather + than libwrapper.sl. +* Make the WrapperManager.setConsoleTitle, getWrapperPID, and getJavaPID + methods available through JMX. +* Fix a state engine problem introduced in 3.2.0 which was causing the + wrapper.on_exit. properties to be ignored in most cases. +* Fix a potential problem that could have caused crashes when debug logging + was enabled. +* Fix a problem where signals were not being handled correctly on some UNIX + platforms, including AIX. This was making it impossible to shutdown the + wrapper cleanly with the TERM signal. Bug #1477619. +* Add new default environment variables which can be referenced in a + configuration file to configure platform specific directories and file + names. WRAPPER_OS, WRAPPER_ARCH, and WRAPPER_BITS. +* Add a -v argument to make it possible to request the version from a wrapper + binary. +* Add support for registering the WrapperManager MBean with the + PlatformMBeanServer when run on a 1.5+ JVM. See the JMX section in the + documentation for details. +* Rework the way timeout properties are handled. Values of 0 now actually + disable the timeouts rather than setting them to a large value. To avoid + overflow problems when converting to internal timer ticks, timeouts are now + restricted to a maximum of 20 days, or 1728000 seconds. Change affects the + wrapper.cpu.timeout, wrapper.startup.timeout, wrapper.ping.timeout, + wrapper.shutdown.timeout, and wrapper.jvm_exit.timeout properties. For + values less than 20 days, there should be no change in functionality. +* Add support for debuggers. The Wrapper will now show a warning on startup + and then again the first time a timeout occurs. But all timeouts will be + ignored. This is to avoid problems with the Wrapper restarting a suspended + JVM in the middle of a debugging session. The wrapper enters this mode if + the wrapper.java.command ends with the string "jdb" or "jdb.exe", or the + "-Xdebug" parameter is passed to the JVM. +* Add 'athlon' to the list of supported architectures. +* Fix a problem where the environment variables loaded when a service was + started were always the system environment even if the service was running + as a specific account. The environment of a specific account will now be + loaded on top of the system environment if the USERNAME environment + variable is set by the system. Bug #1491138. +* Added new wrapper.ntservice.pausable and wrapper.ntservice.pausable.stop_jvm + properties to make it possible to pause and resume the Wrapper when installed + as a Windows service. +* Added new Pause and Resume batch files as well as modified the command batch + file to support pause and resume. +* Added PAUSE and RESUME commands for use by the wrapper.commandfile property. +* Fix a problem with the wrapper.pidfile, wrapper.java.pidfile, + wrapper.anchorfile, wrapper.commandfile, wrapper.statusfile, + wrapper.java.statusfile, wrapper.java.idfile, and wrapper.lockfile + properties where forward slashes in paths were not being changed to back + slashes on Windows versions. +* Simplify the code used to load a native library by using the + System.mapLibraryName method rather than doing the same thing manually. +* Add a new wrapper.syslog.facility property which makes it possible to + specify the syslog facility on UNIX systems. Thanks for the patch from + Bruce Pennypacker. +* Removed the custom thread counting used to keep track of when the wrapped + Java application has completed. It is now done in a different way that + will work on all Java implementations without requiring any special + consideration of the current JVM. Deprecated the + wrapper.monitor_thread_count and wrapper.thread_count_delay properties. + Bug #1470265. +* The WrapperStartStopApp helper class still requires thread counting if the + stopWait parameter is set to true. Previous versions all hardcoded the + system thread count to 1 which worked for most JVMs. A new system property, + org.tanukisoftware.wrapper.WrapperStartStopApp.systemThreadCount, was added + to make it possible to customize. It currently defaults to 1. +* Make it possible to extend the WrapperSimpleApp and WrapperStartStopApp + helper classes. Feature Request #1510274. +* Add warning messages if the old org.silveregg.wrapper package classes are + still being used. They will be removed in the next release. + +3.2.0 +* Rework the release cycle so that the wrapper.jar file released for all + platforms is now built on the same machine. This resolves a few + incompatibility problems caused by jars built on very new JVMs but run + on old JVMs. +* Add additional output when the JVM can not be launched due to security + restrictions on Windows. +* Greatly improved the performance of file logging. On a windows test machine + 3.1.2 could log 67210 lines of output in 20 seconds with a 80-15% split + between the Wrapper and JVM process CPU usage. It now outputs 215214 lines + with a 64-34% split, also showing less load on the system process. This is + a 220% increase in performance. In both cases, the JVM was completely idle + other than the console output which makes the Wrapper appear to be a bit of + a CPU hog. In fact it is the only process doing any work in this case. + This improvement was accomplished by keeping the log file open unless idle. + The idle time can be controlled using the new + wrapper.logfile.inactivity.timeout property. The speed increase on UNIX + platforms was much smaller at around 10%. +* Add a new property, wrapper.disable_restarts, which will completely disable + the Wrapper's ability to restart JVMs. +* Add a pair of new properties, wrapper.port.min and wrapper.port.max, which + make it possible to define the port range used when a specific wrapper.port + is not specified. +* Fix a problem where certain characters like umlauts were being stripped from + property values. Bug #1049528. +* Make the PIDs of the Wrapper and Java process easier to access by providing + a new pair os system properties; wrapper.pid and wrapper.java.pid, as well + as a new pair of methods; WrapperManager.getWrapperPID() and + WrapperManager.getJavaPID(). +* Add a new WrapperEventListener class which can be implemented to receive + a wide variety of events from the Wrapper. +* Add a WrapperServiceControlEvent class which will report any service control + codes received by the Wrapper as it is running as an NT service. This was + added to make it possible for other applications to sent custom codes to the + Wrapper using the Window Service Manager. +* Add a WrapperManager.listServices() method which can be used to obtain the + status of all services on a Windows system. +* Add a WrapperManager.sendServiceControlCode() method which makes it possible + to start, stop, pause, continue, any service on Windows systems. It is also + possible to send custom user codes via the service manager. +* Add comments in the sh script to support the chkconfig command. +* Implement the ability to read from standard input via System.in. Feature + Request #1024693. +* Made the tick based timer the default by changing the default value of the + wrapper.use_system_time property to false. Most users should see an + improvement in reliability under heavy loads with this new setting. If you + have extended any timeouts in the past, you may wish to try going back to + defaults as they may no longer need to be extended. +* Add a new wrapper.restart.reload_configuration property which causes the + Wrapper to reload its configuration file immediately before a JVM restart. + Properties which can not be reloaded have comments stating that fact in + their documentation. Feature Request #981060. +* Fix a problem in the UNIX shell script which was preventing the script from + locating the PID and anchor files when the wrapper.working.dir property was + used. +* Modify UNIX versions so that the wrapper binary will now force its working + directory to the location of the wrapper binary. This change was made to + make the UNIX version work the same way as the Windows version and thus make + configuration files that modify their working directory work correctly on + a cross platform basis. Users which have been using the scripts supplied + with the Wrapper should not encounter any problems. Other users may require + slight changes to their configuration file to deal with the new way that the + Wrapper deals with its initial working directory. +* Add a new method WrapperManager.getProperties() which makes it possible to + access any property in the Wrapper configuration file. +* Fix a problem where TERM signals were not being correctly ignored by the + JVM process on UNIX platforms even if the wrapper.ignore_signals property + was set to true. Earlier versions of the Wrapper would generate a + WRAPPER_CTRL_SHUTDOWN_EVENT when a TERM signal was received. On Windows + that signal should never be ignored. To resolve this a new + WRAPPER_CTRL_TERM_EVENT was added making it possible to selectively ignore + the TERM signals. This change may affect user implementations of the + WrapperListener.controlEvent() method. Bug #1086344. +* The Windows version has a feature which allows the user to immediately kill + the Wrapper and its Java application without waiting for a clean shutdown + by pressing CTRL-C twice. Windows sends the CTRL-C signal to both the + Wrapper and Java processes. Due to a rare timing problem, it was possible + for the Java process to get the signal first and initialize a shutdown + before the Wrapper could respond to the signal. In this case the Wrapper + was interpreting this as a second CTRL-C signal even though the user only + pressed it once. +* If the wrapper.anchorfile or wrapper.pidfile properties are used on Windows + they were being unintentionally deleted if the -t, -p, -i, or -r commands + were used while another Wrapper instance was running. In the case of the + anchor file, this would result in the Wrapper being shutdown unintentionally. + This was not an issue on non-Windows versions. Bug #1108517. +* Fix a security problem where the value of the wrapper.ntservice.account + and wrapper.ntservice.password properties were being stored in plain text + within the registry if they were specified on the command line when + installing the Wrapper as a Windows service. Bug #1110183. +* Add a pair of properties wrapper.ntservice.password.prompt and + wrapper.ntservice.password.prompt.mask which which will cause the Wrapper + to prompt the user for an account password when it is being installed as + an NT service. +* Added system properties to make it possible to configure whether or not + the WrapperSimpleApp and WrapperStartStopApp helper classes will wait + for the configured main methods to complete before reporting that the + application has started. See the javadocs for these classes for more + details. +* Modify the HP-UX build so that it now dynamically links with the pthread + library. This was to make the binaries work with HP-UX 11.00. Thanks to + Sun Kun Choi for the patch. +* Add new wrapper.statusfile and wrapper.java.statusfile properties which can + be used by external applications to monitor the internal state of the Wrapper + or JVM at any given time. These will not be useful to most users. +* Add a new wrapper.commandfile property which can be used by external + applications to control the Wrapper and its JVM. +* Add a new wrapper.java.idfile property which can be used by external + applications to monitor the internal state of the JVM at any given time. +* Add a warning on startup if the JVM has a SecurityManager set but the + wrapper.jar has not been granted the AllPermissions permission. Failure + to do so will almost certainly lead to the Wrapper throwing a number of + errors and this helps to point out the cause. +* Add a security model which protects most Wrapper method calls when a + SecurityManager has been registered. See the Security Model section + for more details. +* Add a new pair of batch files which can be used to start and stop the + Wrapper when installed as a service. +* Add new -q and -qs commands to the Windows version of the Wrapper which + make it possible to query the currently installed status of the service. +* Fix a problem where the wrapper.java.library.path.append_system_path + property was not working correctly on Windows when the system PATH + contained quotes. Bug #1238726. +* Modify the usage output of the Wrapper on all platforms so the Wrapper's + version is now included. It was not previously possible to get the version + of the Wrapper being used without launching a JVM. +* Add a pair of new methods WrapperManager.stopAndReturn() and + WrapperManager.restartAndReturn() which make it possible for code to + stop or restart the JVM and then continue until the JVM is shutdown. + This can be useful for shutdowns initiated within places like servlets, + whose operation is expected to complete. +* Fix a problem on UNIX where the child JVM was sometimes leaving around + zombie processes after a restart. The SIGCHLD signal was not being handled + correctly. Thanks to Traun Leyden for the patch. Bug #1291201. +* Implement the ability to catch control events using the WrapperEventLisener. + Feature Request #836975. +* Add new wrapper.jvm.port, wrapper.jvm.port.min, and wrapper.jvm.port.max + properties which make it possible to control the port the JVM uses to open + a connection back to the JVM. The Wrapper uses to leave this up to the + OS, but some users were having problems with the default conflicting with + other ports. +* Switch from using ftime() to gettimeofday() on UNIX platforms to work around + a problem where the Wrapper would not run on new versions of OSX because + they deprecated the ftime() function call. Thanks for the patch by + Michael Macaluso. Bug #1313162. +* Remove the shutdown timeout from the UNIX shell script. It is not needed + and can cause a zombie JVM if the wrapper's internal shutdown timeout was + longer than that of the shell script. +* Add the ability to specify integer property values in base 8 or 16 in + addition to base 10. Base 8 values start with a '0' and base 16 values + start with a '0x'. +* Make it possible to set the umask on all files created by the Wrapper + as well as the default umask of files created by the JVM. Added new + wrapper.umask, wrapper.java.umask, wrapper.pidfile.umask, + wrapper.lockfile.umask, wrapper.java.pidfile.umask, + wrapper.java.idfile.umask, wrapper.statusfile.umask, + wrapper.java.statusfile.umask, wrapper.anchorfile.umask, and + wrapper.logfile.umask properties. +* Improve the message when the native library can not be loaded to make mention + of the possibility of a 32/64 bit mismatch. +* Add a new wrapper.monitor_thread_count property which makes it possible to + disable the Wrapper's counting of non-daemon threads and thus the shutting + down of the JVM when they have all completed. +* Add support for BELOW_NORMAL and ABOVE_NORMAL options to the + wrapper.ntservice.process_priority property. Feature Request #1373922. +* Ignore '#' characters which are included within double quotes in the value + of a property in the configuration file. Unquoted values must be escaped + with a second '#' characters or it will be interpreted as a comment. +* Display the Wrapper banner in the JVM earlier so that it is displayed + even where there are startup errors. +* Modify the WrapperSimpleApp and WrapperStartStopApp classes so that the + WrapperManager is always initialized immediately. This makes the output + clearer in the event of startup errors. +* Fix a problem where the Windows ServiceManager was not correctly reporting + a startup error if a service failed on startup. The service was being + reported as having started even though it failed to start. +* Fix a problem on UNIX versions where the Wrapper would go into a recursive + state of attempting to launch the JVM from failed child processes if there + was any problems executing the configured java process. +* Rework the way the RUN_AS_USER setting in the UNIX shell script works so + the specified user is now set regardless of the command being executed. + To make sure the user never has to enter the password twice when running + the script, it now recurses after changing the user. The script then + runs entirely as the configured user. +* Improve the message that is displayed when attempting to start, stop, or + remove a windows service which is not installed. +* Add new wrapper.lockfile property which makes it possible to specify a + lock file containing a pid. +* Modified the sh script so it now creates a lock file on startup in the + /var/lock/subsys directory if it exists. This is needed by fedora systems + on shutdown. +* Store javadocs in tar distibutions in a nested tar file to avoid problems + with long filenames in some tar distributions. +* Fix a problem with the WrapperSimpleApp and WrapperStartStopApp helper + classes where on heavily loaded systems it was possible for the Wrapper + to get a running thread count of 0 and shutdown before the main thread + had a chance to be started. +* Add a new wrapper.thread_count_delay property which will force the + WrapperManager to wait the specified number of seconds before it begins + to check the number of running threads. +* Fix a problem where the wrapper.java.library.path.append_system_path + property was appending the PATH rather than the LD_LIBRARY_PATH environment + variable on Unix systems. PATH is correct for Windows systems. +* Add a new wrapper.logfile.rollmode property which makes it possible to + control how and when the logfile is rolled. Feature Requests #864463, + #1085097, and #1085850. +* Fix a problem on Linux where the test for the status of the Java child + process would sometimes fail causing the Wrapper to shutdown with the + error "Critical error: wait for JVM process failed (No child processes)" + rather than restart the child JVM. Users who encountered this problem + found it easy to reproduce, but it only happened on some systems. +* Modify the way the UNIX shell script tests for the existence of a process + matching the pid in an existing pid file. It now verifies the process + command as well as the pid to fix a system reboot problem where a stale + pid has been reused by another application, making the script think the + wrapper was already running. +* Add support for the GNU libjcj JVM. Like JRocket, it requires slightly + different thread counting. +* Add support for Linux 64-bit PPC and Solaris 32-bit x86 versions. +* Add a new set.default.ENV syntax to the configuration file making it + possible to environment variable values which do not overwrite existing + values, ie. to specify a default value. +* Added a new wrapper.console.flush property which forces the wrapper to + explicitly flush stdout after each line of log output. +* Change the error shown when the JVM shuts down prematurely during a + shutdown to a warning message. +* Fix a problem where the Wrapper would show the following error message + if user code called System.exit from within the WrapperListener.stop + callback method. This would happen if the stop class's main method + registered with the WrapperStartStopApp called System.exit. + "JVM exited unexpectedly while stopping the application." + Bug #945976. +* Add a new wrapper.syslog.ident property which makes it possible to + specify the identity used in syslog entries on UNIX. This was possible + in older versions but was set using the wrapper.ntservice.name property. + Bug #1432855. +* Add support for MacOSX Universal Binary distributions. +* Add support for Delta Pack distributions. This is a distribution that + contains the binaries of multiple platforms. + +3.1.2 +* Modify the way boolean system properties are resolved by the WrapperManager + so it is now possible to set them to true or false rather than assuming they + are true if set. +* Fix a problem where some localized error messages were not having their + tokens replaced correctly. +* Fix a problem when using the WrapperStartStopApp helper class. The usage + text was incorrectly being displayed in the console if an exception was + thrown while executing the main method of the configured stop class. This + did not change the functionality of the application, but it did cause some + confusion. +* Fix a problem on Windows where a library path or class path which ended in + a backslash was preventing the Wrapper from launching the JVM. The Windows + OS was using the backslash to escape the quote used to close the path. The + fix was to add a second backslash where needed. +* Added a new wrapper.java.command.loglevel property which makes it possible + to control the log level of the generated java command. +* Add support for escaped quotes when stripping quotes on UNIX for the + wrapper.java.additional. and wrapper.app.parameter. properties. +* Change the default value of wrapper.jvm_exit.timeout from 5 to 15 seconds. + The old default was too fast for some applications which take a while to + exit. Applications which were exiting promptly will not see any difference. +* Fix a problem where the JVM would restart at certain times when using the + system time based timer due to an overflow error. This problem was + introduced in 3.1.0. Due to a separate bug in 3.1.0, the Wrapper would + shutdown rather than simply restarting the JVM as was happening in 3.1.1. + The last restart happened on Aug 21, 2004. It will next occur Oct 10, 2004 + and repeat at regular intervals. There are no problems when using the new + Tick based timer. Bug #1014405. +* Correct the wrapper.logfile.maxsize property so that a a kilobyte is now 1024 + rather than 1000, and a megabyte is a megabyte. We aren't a hard drive + manufacturer after all. +* Add try-catch blocks around all thread entry points in the Windows version. + This has always been done in the main function, but these blocks will help + to narrow down the cause of problems should they ever be encountered in + control or service handlers. +* Centralize shutdown code on UNIX version in an appExit method as was already + being done for Windows versions. +* Fix a problem where the build.sh was not correctly using the included ant + if an ANT_HOME environment variable was defined. +* Add a new wrapper.single_invocation property which will prevent multiple + invocations of an application from being started on Windows platforms. + The shell script handles this on UNIX platforms. Feature Request #889123. +* Fix a crash problem introduced in 3.1.1, caused by a pair of uninitialized + pointers. The crash was possible on all platforms but would only happen + if the Wrapper was started without any arguments. It would not affect + users running the Wrapper normally. Bug #1018481. +* Fix a problem with the run as user feature of the shell script on Solaris. + Needed to be using /usr/xpg4/bin/id rather than /usr/bin/in if available. + Bug #1024008. +* Replace calls to usleep with nanosleep on platforms where it is available. + This was to fix an occasional hang on a specific Solaris machine. It would + occasionally hang on calls to usleep. From research, it appears that usleep + has problems when signals are encountered while sleeping. Still testing + whether or not this change solved the problem. +* Upgrade the version of Ant included with source releases to 1.6.2 to fix + some problems generating jni headers when building with Java 1.4.2. +* Upgrade the version of Cocoon included with source releases to 2.0.4 to + fix some problems generating documentation using Java 1.4.2. +* Display a warning if the exit status of a JVM process ever returns the + STILL_ACTIVE status on Windows. There was no known problem here, just + noticed it while looking over the code. +* Display a descriptive error message on Windows if the the JVM process crashes + due to an uncaught exception in native JVM code. +* Add a test for invalid jvm arguments set using the wrapper.java.additional. + properties. Invalid arguments could cause the Wrapper startup to fail in + non obvious ways if they are mistaken by the JVM as the main class. + +3.1.1 +* Modified the way libwrapper.so is built on Solaris and Linux so that it + no longer statically links its required libraries. +* Fix a file handle leak when calling WrapperManager.getUser or + WrapperManager.getInteractiveUser on Windows platforms. +* Fix a problem introduced in 3.1.0 where the JVM would not be restarted + correctly if it quit after a ping timeout to let the Wrapper resynch and + restart it. +* Fix a problem where CTRL-C was not being handled correctly if the console + was configured to be shown when running as an NT service. +* Fix a problem where signals fired at UNIX versions of the wrapper were + not being handled correctly when the tick timer was being used. +* Fix a synchronization problem in the logging code which would + occassionally cause the Wrapper to crash with an Access Violation. + The problem was only encountered when the tick timer was enabled, + and was only seen on multi-CPU systems. Bug #949877. +* Fix a problem when using the tick timer where the Wrapper would sometimes + exit on startup due to an uncaught SIGALRM. Only reported on multi-CPU + Solaris systems. +* Fix a problem where the Wrapper would sometimes hang on shutdown if + another thread called System.exit while the Wrapper was shutting down. + Bug #955248. +* Fix a problem introduced in 3.1.0 where a very very large CPU timeout + warning message was being displayed if the system time was set back + while using the default system timer. +* Added a new property, wrapper.anchorfile, which makes it possible to + cause the Wrapper to shutdown by deleting an anchor file. The UNIX sh + script has been modified to optionally make use of this feature. +* Add a debug message at startup which makes it clear which timer is being + used. +* A Windows user reported that using forward slashes in the path the log + file was failing. Avoid this problem by always converting '/' to '\' + in the wrapper.logfile property on Windows. +* Fix a problem where it was not possible disable the wrapper log file as + documented in the wrapper.logfile property. Most likely broken way back + in version 2.2.5. +* Add some additional error checks after calls to control the pipe between + the JVM and Wrapper as well as improving the messages around other socket + related error messages. +* Fix a problem on some HP-UX systems were not working correctly because + the EAGAIN and EWOULDBLOCK constants are not equal with some compilers. +* Change some of the defaults in the src/conf/wrapper.conf.in file which + ships with the Wrapper to avoid confusion with new users. +* Rewrote the routine which reads and logs console output from the JVM + for Windows versions. Internal buffers are now scaled dynamically, + fixing a problem where long lines were being wrapped at 1024 characters. + This rewrite also resulted in a 4 fold increase in speed when the JVM is + sending large quantities of output to the console. +* Increase debug output on UNIX platforms when a signal is caught. When + possible, information about where the signal came from is now logged. +* Modified the way log output from within signal handlers is handled so it + is now queued and then logged by the main event loop. +* Back out a 3.1.0 change where a JVM that had failed to exit cleanly was + sent a SIGTERM prior to a SIGKILL. The SIGTERM made no difference and + slowed down the forced shutdown. A modification to the event loop made + the functionality more difficult to implement. +* Add the ability to set the user that the Wrapper and its JVM will run as + from within the sh script on UNIX platforms. +* Add an icon resource to the Wrapper binary on Windows versions. +* Fix a typo in the UNIX sh script which caused an extra slash to be included + in the path of the pid file. Was not causing any known problems. +* Added support for 64-bit HP-UX. Big thanks to Venkatesh Sellappa for + supplying the patch. +* Fix a deadlock problem introduced in 3.1.0 with some FreeBSD systems. Not + all users were experiencing it, but those who did were able to reliably + reproduce the problem. The problem appears to have been caused by + FreeBSD bug #kern/64313. +* Make the signal handling variables in the wrapper native library volatile. + Directly this was to fix a compiler warning on HP-UX64 systems but it + should also make the calls more efficient. + +3.1.0 +* The license was revised for this version to include a copyright omission. + This change is to be retroactively applied to all versions of the Java + Service Wrapper starting with version 3.0.0. The changes should have + no effect on users. +* The Online documentation and web site were both reworked. The logo has + been updated so that Duke is no longer used. The new online site now + has the ability for users to logon and append comments to any page. +* Added a new batch file which accepts commands like the UNIX shell script. + The new file is offered as an alternative to the default batch files, and + can be found at src/bin/AppCommand.bat.in. Thanks to Mike Castle for + donating the new script. +* The Windows version of the Wrapper was not correctly registering that it + would accept SHUTDOWN messages when running as a service. The Wrapper + was getting the message anyway so this should not change functionality. + Thanks to Jason Tishler for noticing this and sending in a patch. +* Add a new property, wrapper.native_library, which can be used to specify + the base name of the native library which is loaded by the WrapperManager + class. +* Modify the WrapperManager class so it now stores references to System.out + and System.err on initialization and always writes to those stored streams. + This makes sure that all Wrapper console output always goes to the + wrapper.log file even if user code overrides those streams with calls to + System.setOut and System.setErr. This was necessary to prevent deadlocks + in such user code from affecting the functionality of the Wrapper. +* Fixed a problem where some environment variables where not being correctly + loaded from the system registry when running as an NT service. Big thanks + to Eric Smith for tracking this down and submitting a patch. It turns out + that the putenv function was not being used correctly. +* Modified the way the wrapper.conf file is loaded so it will now read the + contents correctly even if the line feeds in the file are incorrect for + the current platform. Windows line feeds had been causing problems when + used on UNIX platforms. Feature Request #829896. +* Added a new property, wrapper.ntservice.console, which allows a console to + be displayed when running as an NT service. +* Fixed a problem where the request thread dump on failed JVM exit feature + had never worked when running as an NT service. Bug #831775. +* Add a new property, wrapper.console.title, which makes it possible to set + the title of the console in which the Wrapper is currently running. This + currently only works on Windows platforms. +* Added a new method, setConsoleTitle, to the WrapperManager class which + enables the application to dynamically set the console title. Like the + wrapper.console.title property, this only works on Windows platforms. +* Improved the algorithm of the request thread dump on failed JVM exit feature + so that extremely large thread dumps will not be truncated when the JVM + is killed. +* Fix a problem where CTRL-C was being ignored by the WrapperManager if a + WrapperListener is never registered. This is not possible if the Wrapper + is being used correctly but never the less a user did come across it. +* Add some additional debug output to help identify the cause of problems + loading the native library. +* The WrapperManager class now checks to make sure that its current version + matches the version of the native library and Wrapper. If there are any + discrepancies found then appropriate warnings will be displayed, but the + Application will still be allowed to start. This was added to make obvious + the cause of problems resulting from mismatched versions. +* Added a new property wrapper.use_system_time system time. By setting this + property to false, the Wrapper will start using a new experimental timer + which uses a background thread to manage time rather than the system time. + This has a number of advantages over using the system time and should give + most users even more reliable behavior when the system is under high load + or there are changes being made to the system time. The timer is very + critical to the operation of the Wrapper so the old behavior is left as + the default for the time being until this feature has had the chance to be + "time" tested. If all goes well then this will be enabled as the default + in a future version of the Wrapper. + A pair of related properties, wrapper.timer_fast_threshold and + wrapper.timer_slow_threshold were also added to aid in debugging. +* Rework the logging code so it is now thread safe. The addition of the + timer thread means that there is now more than a single thread accessing + that code. This was causing problems as the two threads tried to use the + same buffers. As part of this change, a new format variable 'D' was added + to display the thread which is doing the logging. +* Fix a problem where a thread dump would be invoked if the request thread + dump on failed JVM exit was enabled and the user forced an immediate + shutdown by pressing CTRL-C more than once. +* Add getUser and getInteractiveUser methods to the WrapperManager class to + make it possible for user code to query information about the user running + Wrapper or the user who is interacting with the Wrapper and its JVM. + Feature Request #812175. +* The Wrapper will now always exit with the exit code used to terminate the JVM + whether System.exit is used or WrapperManager.stop. When running as an NT + service the Wrapper now correctly returns the correct exit code to the + service manager so failure recovery tools should now work correctly. + Feature Request #852491. +* Add a status command to the UNIX shell script which can be used to find out + whether or not the wrapper is currently running. Patch submitted by + Joseph Benavidez. +* Modify the WrapperSimpleApp and WrapperStartStopApp so that the main method + of a class is located even if it exists in a parent class rather than the + class specified. +* To make debugging classpath problems easier, the Wrapper now verifies all + classpath entries before launching a JVM and logs debug level warnings for + any entries that do not exist. +* Fix a problem where it was possible to define a zero length filter that would + trigger on any output. +* Add some additional debug output to make it easier to debug startup, + shutdown and restart problems. +* Modify the way the Wrapper forcibly kills a frozen JVM on UNIX platforms so + that it now sends a SIGTERM, waits up to 5 seconds, then sends a SIGKILL. +* Add a new wrapper.java.library.path.append_system_path property which will + cause the Wrapper to append the system path to the generated library path. + Feature Request #917902. +* Fix a problem where spaces around the '=' character of a property definition + were rendering the property invisible to the Wrapper. Bug #916001. +* Fix a problem where the first ping timeout after the JVM was started was + still hard coded at 30 seconds. This was causing a combination of large + values of wrapper.ping.interval and wrapper.ping.timeout to fail. +* Fix a problem where the JVM would fail to shutdown cleanly if the Wrapper + was asked to stop too soon after launching a JVM. This was leading to the + JVM being killed after the shutdown timeout expired. Bug #917281. +* Added an adviser which will print out explanatory messages to the console + and wrapper log file when the Wrapper encounters a commonly made + configuration mistake. This is designed to cut down on support requests + by new users. Can be disabled using the wrapper.adviser property. +* The bash script and the realpath utility have been deprecated since version + 3.0.3. They have been removed in this release. The sh script is recommended + on all UNIX platforms, and the realpath utility which was used by pre-3.0.3 + bash and sh scripts has not been used since. +* Add the wrapper.startup.delay property along with console and service + specific variants which make it possible to configure a delay between the + Wrapper being launched and the first JVM being launched. +* Promote the wrapper.debug property back from being "deprecated". It has + continued to be useful and deserved documentation and official status. +* Add wrapper.on_exit. properties to control what happens when a exits + based on the exit code. +* Modify the way calls to System.in.read() are handled so that they now block + rather than throwing an exception. Currently, System.in can not be used with + the Wrapper because of the way I/O is passed between the Wrapper and JVM. +* Modified the Windows batch files to fix a problem where the path to the + Wrapper.exe contained more than one "/bin". The new batch files are much + simpler and should be easier to customize if needed. Bug #925308. +* Modified the wrapper.java.initmemory and wrapper.java.maxmemory properties + so that they now default to a value of 0 which causes the -Xms and -Xmx + parameters to be omitted from the command used to launch Java. This + will cause the JVM to use its own default values and also makes it possible + to specify the memory parameters using the wrapper.java.additional. + properties. +* Added a pair of environment variables, WRAPPER_FILE_SEPARATOR and + WRAPPER_PATH_SEPARATOR, whose values are set to either '/' and ':' or + '\' and ';' on startup. They can be used in the wrapper.conf file + to construct platform independent property values. +* Add a new wrapper.working.dir property which makes if possible to change + the Wrapper and JVM's working directory to a location other than the + location of the Wrapper binary. Feature Request #738160. + +3.0.5 +* Added support for SGI Irix. Big thanks to Andreas Wendt for supplying the + patch. +* Due to a bug in the build, the native library was not included in the 3.0.3 + or 3.0.4 binary releases for OSX, building from source was working correctly. + This has been fixed and the build greatly simplified to avoid such problems + in the future. Bug #791755. +* Changed the default location of the pid file generated by the sh script to + exist in the same directory as the sh script rather than in the /var/run. + This can be changed by setting the PIDDIR variable in the sh script used to + launch the Wrapper. +* Added support for the wrapper.pidfile property on the Windows platform. +* Added the wrapper.java.pidfile property which will cause the pid of the + java process to be written to a specified file. + (WINDOWS USERS) If you are using a wrapper.conf file that was created prior + to version 3.0.0 of the Wrapper, then you may have this property defined in + your configuration file. You will get an error on startup if the specified + path does not exist. +* Stop clearing the file creation mask when the Unix version of the Wrapper is + run as a daemon process. The file creation mask will not be inherited from + the process which launches the Wrapper. Bug #788849. +* Modify the sh script so it works on Linux, then deprecate the bash script. + This means that all Unix platforms can now use the same script to control + the Wrapper. Thanks to Mike Castle for the patch. The bash script can still + be found in the release, but it is deprecated and will be removed in a + future version. +* Modified the sh script so it is now possible to set the nice priority in the + script configuration block. +* Remove output to System.out in the WrapperManager.requestThreadDump() method. + If some JVM threads were hung while accessing the System.out object, + attempting to do a thread a dump would cause the calling thread to hang as + well. Thanks to Thomas Hart for the patch. +* Make it obvious in the log whether or not the Wrapper was started as a + daemon process on UNIX systems. +* Modify the way restarts requested from the JVM, or caused by a filter are + handled. The Wrapper will no longer reset the restart count in either of + these cases. If an application runs for longer than the + wrapper.successful_invocation_time timeout then the count will still be + reset back to 0. +* Added a new wrapper.ignore_signals property which makes it possible to + configure the Wrapper so it will ignore CTRL-C, HALT and INT signals. +* Modify the WrapperManager.isLaunchedAsService() method on UNIX systems so it + now returns true if the Wrapper was launched with the wrapper.daemonize flag + set. +* Added a pair of MBean interfaces which allow the Wrapper to be controlled + using JMX. See the new JMX section in the documentation for details. + Thanks to Sal Ingrilli for help with testing. +* Modify the Windows build so the Wrapper.exe and Wrapper.dll files can now + be built from Ant if MSVC is installed. +* Added a new wrapper.ping.interval property which lets users control the + frequency that the Wrapper pings the JVM. Feature Request #607768. +* When a JVM refuses to shutdown, the Wrapper can be configured to request a + thread dump using the wrapper.request_thread_dump_on_failed_jvm_exit + property. The Wrapper was then waiting 1 second before the process was + killed. This was not always long enough, resulting in a truncated thread + dump. Increased the pause to 3 seconds. Feature Request #633761. +* Fix a bug where wrapper.app.parameter. and wrapper.java.additional. + properties declared from the Windows command line were not correctly + handling spaces in their values. Support Request #802139. + +3.0.4 +* Fix a problem on UNIX systems where requesting a second thread dump any time + during the life of a single Wrapper process would cause the Wrapper and JVM + to shutdown rather than perform the thread dump. +* Fix a problem where a, user without permission, attempting to stop an + application was able to delete the pid file even though they were unable + to stop the application itself. This would make the scripts think that + the application was stopped when was actually still running. +* Fix a problem where an application was being killed prematurely if it took + longer than 6 seconds to exit on its own. The scripts now make sure that + an application always has enough time to shutdown cleanly. +* Improve the debug output so that packet codes are now shown using a name + rather than a raw number. +* Reduce the frequency of "Waiting to stop..." messages displayed when removing + an NT service that is currently running. Decreased frequency from once per + second to once every five seconds. +* Fix a minor problem where the hour in the date returned by + WrapperInfo.getBuildTime() was not base 24. +* Added -t and -p command line options to the Windows version of the Wrapper + to sTart and stoP the Wrapper as an NT service. This can be used in place + of "net start" and "net stop", which do not always work correctly when a + service takes a long time to start up or shutdown. See the Launching Your + Application (Win32) section for more details. +* Add a new method WrapperManager.stopImmediate which will cause the JVM to + exit immediately without calling any stop methods or shutdown hooks. +* Add a new class, WrapperActionServer, which makes it easy to remotely control + the Wrapper remotely by opening a socket and sending commands. See the + javadocs of the class for more details. +* Fix bug #744801. A Java GUI was not being displayed when the application was + run in either console mode or as a service with wrapper.ntservice.interactive + enabled. This problem was introduced in Version 3.0.0 when using 1.2.x or + 1.3.x versions of Java. To use interactive services with 1.2.x or 1.3.x + versions of java, please review the documentation for the + wrapper.ntservice.interactive property. +* Fix a problem where the JVM was not receiving CTRL-C and CTRL-CLOSE events + when running under the Wrapper on Windows. This was not a problem in most + cases as the Wrapper was taking care of the processing of the events. But + the WrapperListener.controlEvent() method was not being called as documented. +* Changed the way the WrapperSimpleApp and WrapperStartStopApp respond to + control events so that the JVM will respond and call WrapperManager.stop() + even when being controlled by the Wrapper. +* Modified the suggested behavior of the WrapperListener.controlEvent() method. + Users who have implemented the WrapperListener interface themselves should + review the Javadocs. The changes are not required and applications will + continue to function as they did before. +* Added support for DEC OSF1 (Alpha). Big thanks to Andreas Wendt for + supplying the patch. +* Fix a problem where the sh and bash scripts were failing if the path to the + script contained spaces. +* Fix a problem where the JVM would sometimes hang when trying to shutdown if + the wrapper.key parameter was passed to the JVM while not being controlled + by the Wrapper. This would happen if a user copied the command from the + Wrapper's debug output and attempted to run it as is without first removing + the wrapper.key parameter. +* Implement the ability to specify an NT service's load order group in response + to feature request #764143. See the javadocs for the new + wrapper.ntservice.load_order_group property for details. +* Improve the error message displayed when the NT EventLog is full in response + to feature request #643617. The EventLog output will now be disabled if any + errors are encountered while logging events. This prevents the error from + repeating. +* Improve the error message displayed on Windows when the configured Java + command can not be executed or does not exist. +* Fix a problem where the Wrapper was leaving a pipe unclosed each time the JVM + was restarted on all UNIX platforms. This was causing the Wrapper to run out + of file handles. Bug #767267, discovered and patched by David Wong. +* Fix a problem where the '#' character, which signifies a comment, could not + be included in property values. A double hash, '##' will now resolve into a + '#' within the property value. Bug #777303. +* Added support for FreeBSD. Big thanks to Alphonse Bendt for supplying the + patch. +* Make the wrapper.port property optional. +* Changed the way environment variables are loaded from the registry on Windows + platforms so users will no longer get warning messages about not being able + to handle very large environment variables. Prior versions could only handle + environment variables whose expanded value was less than 2048 characters in + length. +* Fix a problem on UNIX platforms where a shell used to start the Wrapper + running as a detached process would hang when the user attempted to exit + the shell. Thanks to Mike Castle for this patch. + +3.0.3 +* Added support for Mac OS X. Big thanks to Andy Barnett for supplying the + patch. +* Fix a segmentation fault on UNIX systems when the first console output + from the JVM was an empty line. Thanks to Mike Castle for finding this. +* Fix a problem where a 0 length malloc was being called if there were no + configured filters. This was fine on most platforms but caused a crash + on MAC OS X. +* Rework the initialization of the bash and sh scripts so that they will + work correctly when referenced as symbolic links. Thanks go out to Richard + Emberson for the code to resolve symbolic links. +* Deprecated the realpath binary in the *NIX distributions as it is no longer + used by the bash or sh scripts. It is being left in for now so as not to + break the build scripts of other projects, but it will be removed after a + couple more releases. +* Added a test to make sure that wrapper.ntservice.interactive is not set to + TRUE when an account is specified using wrapper.ntservice.account. + +3.0.2 +* Modified the sh and bash scripts so that console log output is disabled by + default when the scripts are launched with the 'start' action. Running with + the 'console' action will still send output to the console. Logging to the + file is still enabled. +* Modified the wrapper.ping.timeout property so it also controls the ping + timeout within the JVM. Before the timeout on responses to the Wrapper + could be controlled, but the ping timeout within the JVM was hardcoded to + 30 seconds. +* In the last release, some work was done to avoid false timeouts caused by + large quantities of output. On some heavily loaded systems, timeouts were + still being encountered. Rather than reading up to 50 lines of input, the + code will now read for a maximum of 250ms before returning to give the main + event loop more cycles. +* Fix a problem where the values of environment variables set in the + configuration file were not correct when those values included references + to other environment variables. +* Fix a potential buffer overflow problem if configuration properties + referenced extremely large environment variables. +* Fix a potential problem where the inability to expand very large environment + variables would have led to an access violation when run as an NT service. +* Add some extra checks in the event where the native library can not be loaded + so that the WrapperManager can differentiate between the library missing and + not being readable due to permission problems. +* Remove the wrapper.ntservice.process_priority from the default wrapper.conf + because its use can produce unexpected results if used improperly. Please + see the property documentation for details. +* Fix a problem where environment variables in the registry which had no value + were causing the Wrapper to crash with an access violation. This was + introduced in version 3.0.0 with the feature to load environment variables + from the registry. The offending registry entry was WV_GATEWAY_CFG which + appears to be related to Oracle. + +3.0.1 +* Fix a problem with the wrapper.disable_shutdown_hook. Due to a typo in the + source, the property was being ignored. This was broken in the 3.0.0 + release. +* Fix a problem with the HP-UX release build reported by Ashish Gawarikar. +* Add the ability to set environment variables from within the configuration + file or from the command line. +* Fix a problem on HP-UX and AIX machines where the stop() function in the + shell scripts was causing a syntax error due to a conflict with a like named + shell command on those platforms. This appears to be an issue with the + Korn shell on all platforms. +* Fix a problem where very heavy output from the JVM can cause the Wrapper to + give a false timeout. The Wrapper now only reads 50 lines of input at a time + to guarantee that the Wrapper's event loop always gets cycles. +* Fix a problem on UNIX versions where extra line breaks would sometimes be + added to the logged output when there was large amounts of output being + sent from the JVM. +* Fix a problem where a large number of calls to WrapperManager.log() + immediately before the JVM exits could lead to the Wrapper incorrectly + reporting that the JVM exited unexpectedly. + +3.0.0 +* Deprecated the com.silveregg.wrapper package in favor of + org.tanukisoftware.wrapper. The classes and interfaces in the silveregg + package will continue to function, but migration to the new package should + be done when possible. See the project history for details. +* On Windows systems change any forward slashes in the wrapper.java.command + property to back slashes. Some users had reported having problems on + Windows XP. +* Implemented feature request #633178. Added WrapperManager.requestThreadDump() + to force the current JVM to immediately perform a thread dump. +* Fixed bug where wrapper.logfile.maxsize was being set to 0 if the 'k' or 'm' + unit was omitted. +* Add the ability to specify an account name and password when installing an + NT service. +* Add a property, wrapper.ntservice.interactive, which makes it possible to + control whether or not the Java process can gain access to the desktop while + it is running as an NT service. +* Add limited support for 1.2.x versions of Java. Shutdown hooks are + supported until Java 1.3 so those functions will be disabled. If the + application displays a GUI then Java 1.3 should be used as the GUI can not + currently be displayed when using Java 1.2.x. +* Made it possible to use the wrapper.pidfile property on all *nix platforms. + Please notice that the property has been removed from the default + wrapper.conf file. The property is not needed when the wrapper is launched + with the bash shell script. The sh shell script will set the wrapper.pidfile + when the wrapper is launched. If either of the scripts provided with the + Wrapper distribution are used then the wrapper.pidfile should always be + removed from your wrapper.conf file. +* Added a new wrapper.daemonize property which, when set, will form the wrapper + process to be a detached non-session group leader. This makes it possible to + launch the wrapper in such a way that it will not be terminated when the user + launching the process logs out. This had been a problem on Solaris systems + when using the sh shell. The default sh and bash scripts both make use of + this in the default. Please update your scripts for use with this version. + Thanks to Rajiv Subrahmanyam for the patch. +* Fix a problem where the Wrapper was incorrectly counting the number of + non-daemon threads in BEA's JRockit Virtual Machine. This was causing the + application to shutdown when the non-daemon thread count dropped to 1. +* Added support for building the wrapper on AIX and HP-UX systems. Thanks for + the patches involved go out to Ashish Gawarikar and William Lee. +* Implement feature request #653131 to force the JVM to immediately exit when + the user presses CTRL-C multiple times. +* Added a 'console' action to the bash and sh scripts to make it possible to + launch the Wrapper in the current shell process. The 'start' task will launch + the Wrapper as a spawned daemon process. +* Fixed a problem where missing environment variables specified in classpath + or library path properties were not being handled correctly. +* Implemented feature request #676599 to enable the filtering of JVM output to + trigger JVM restarts or Wrapper shutdowns. See the new + wrapper.filter.trigger.n and wrapper.filter.action.n properties. +* Modify the Win32 version of the Wrapper so that Environment Variables are + always read from the system registry when the Wrapper is run as a service. + By doing this, it makes it possible to change or add the system environment + variables and have them take effect without having to first reboot the + machine. +* Implemented cascading configuration files. +* Changed the default value for the wrapper.java.initmemory property to be 3Mb. + The default on Windows and Linux JVMs is 2Mb, but the Solaris JVM requires + a minimum of 3Mb. The minimum value accepted by the Wrapper was changed + from 8Mb to 1Mb to make it possible to reduce the footprint of applications + to what is possible without using the wrapper. +* Improve the parsing of configuration files so that leading and trailing white + space is now correctly trimmed. It is also now possible to have comments at + the end of a line containing a property. +* Modify the way exceptions thrown by an application's main method are + presented to the user by the WrapperSimpleApp and WrapperStartStopApp so + they no longer look like a problem with Wrapper configuration. + +2.2.9 +* Added a new property, wrapperper.restart.delay, which allows the user to control + the amount of time to pause between a JVM exiting and a new JVM being + launched. +* Fixed bug #611024. The Wrapper would sometimes fail to start if + wrapper.max_failed_invocations is set to 1. +* Fix a problem where the number of non-daemon threads was not being calculated + in some cases. +* Implemented feature request #491443. Environment variables referenced in the + wrapper.conf file will now be evaluated as the file is loaded. The windows + syntax for environment variables is used on all platforms to make them + platform independent. +* Fixed a problem where the wrapper.conf was being open with both read and + write locks when a read lock is all that is needed. Made the wrapper fail + on startup if another application held a read lock on the conf file. +* Modified the message displayed when the native library could not be found, + so that it is much more descriptive. Hopefully it will cut down on questions + caused by configuration problems. +* Implemented feature request #613539. Modified the wrapper.java.library.path + to function like the wrapper.java.classpath.n properties so that multiple + directories can be specified in the library path in a platform independent + way. The old property is still supported, but deprecated. +* Fix Bug #632215. The WrapperManager.isLaunchedAsService() method was always + returning false, even when run as a service under Windows. On linux, the + Wrapper is always run as a console app, so this method will always return + false. +* Improve the message thrown when user code attempts to access System.in from + within a JVM being controlled by the Wrapper. System.in will not work + because the JVM is a spawned process. + +2.2.8 +* Fixed a compiler problem on Solaris some systems. +* Added a new property, wrapper.cpu.timeout, which allows the user to control + how much time without receiving any CPU the Wrapper will tolerate before + displaying a warning message. The CPU timeout feature was added in 2.2.7 + but the default timeout of 10 seconds was not configurable. +* The Wrapper was only allowing 5 seconds between the JVM informing the + Wrapper that it was going to exit and the JVM process actually exiting. + This would cause the Wrapper to terminate the process prematurely in + cases where an application shutdown thread took longer than 5 seconds to + complete. The Wrapper now allows wrapper.jvm_exit.timeout seconds for + the JVM process to exit on its own before being forcibly terminated. +* When there is a configuration problem or a resource is unavailable, a JVM + will sometimes exit abnormally very shortly after being launched. This + can lead the the JVM being infinitely restarted due to a simple class + path misconfiguration. To work around this, the Wrapper has always had + a hard limit of 5 restarts within a short period of time. If the JVM + has been running for more than a few minutes, then the count was reset. + In this version, a new property. wrapper.max_failed_invocations was added + to allow the max value to be set. The time period which the JVM must + now be running for the JVM launch to have been considered a success for + restart purposes is set using the new wrapper.successful_invocation_time + property. +* The number of advanced properties which most users do not need has been + increasing as the Wrapper has been made more and more flexible. This + has been causing confusion in their usage by people who play with them + without first reading the documentation. To solve this, the advanced + properties were removed from the default configuration file. They still + function. But users must now read to the advanced configuration + documentation to learn about their existence. Added quite about to the + descriptions of these properties to hopefully clear up any confusion + about their usage. +* When the JVM exits abnormally, the Wrapper will pause for a few seconds + before starting another JVM. If the user pressed CTRL-C during this + pause, a new JVM would still be launched. The new JVM was exiting + immediately but it was a waste of time. The Wrapper now recognizes the + event and aborts launching the new JVM. +* Added a page to the documentation which shows inline javadocs. This + will hopefully make it easier to navigate them as part of the full + documentation set. +* Added a new method to the WrapperManager which enables user code to + log at any log level. +* Added a new Helper class WrapperStartStopApp which allows users to easily + integrate applications like Tomcat which use a separate class to stop + the application. +* Added a samples section to the documentation. Just includes Tomcat 4 + for now. + +2.2.7 +* Fix a problem where the JVM was trying to reconnect the Wrapper as it was + being shutdown. This was causing problems if the JVM was being restarted. +* Added support for the system being suspended to RAM or disk. Also improved + wrapper performance when a system is under 100% load. See the new example + output in the example section. +* Fix a problem where the log output was not being directed to a file called + wrapper.log in the same directory as the Wrapper binary in the event that the + configured wrapper log file could not be accessed. +* Fix a problem where the Wrapper was not shutting down the JVM correctly when + all non daemon threads completed. Normally a JVM will exit when all of its + non daemon threads have completed so this was causing some problems. + (Thanks to Jung Tamas) +* Added the ability to set the priority of the Wrapper and its JVM when run + as an NT service or console application. The same thing can be better + achieved on Unix systems by using "nice" in the shell script used to launch + the Wrapper. See the documentation for for details. +* JVM information was not being displayed correctly when the Wrapper native + library could not be loaded. +* Added a new property to cause the wrapper to attempt to request a thread dump + when the JVM does not exit on request. +* Improved the documentation of the WrapperSimpleApp and WrapperListener + classes. +* Adding a new property wrapper.shutdown.timeout to allow the user to extend + the length of time that an application is allowed to take shutting down. +* Rework the way the shutdown process works so that System.exit will never be + called before the stop method in WrapperListener has had a chance to complete. +* Add a Restart button to the TestWrapper application. +* Fix a problem on Unix versions where '%' characters in Java output would + sometimes cause the wrapper to crash. Somehow missed getting this into the + last release. +* Added a test to make sure that WrapperManager.stop is not called recursively. +* Added support for building under Windows XP. Prebuilt installations had + already been working. + +2.2.6 +* Fix a problem where '%' characters in Java output would sometimes cause the + wrapper to crash. (Thanks to Frode Moe) +* Added support for requesting a Java thread dump without shutting down the + Java process. +* Fixed a problem on windows where the java command was looking in the windows + system and system32 directories for the java executable before checking the + path when the full path to the java executable was not specified in the + configuration file. This could lead to different JVM being run from the + Wrapper than was run if java -version was run from the command line. The + Wrapper will now attempt to resolve the full java path using the PATH + environment variable. +* Added debug output showing Java version information when the JVM first + starts. +* Modified c source to use /* */ style comments rather than // style comments. + Some people were having problems with some compilers. + +2.2.5 +* Added support for service descriptions for Win2k and XP. +* Fixed bug issue when reading configuration files from Windows on Unix. +* Deprecated the wrapper.debug property in favor of loglevels. +* Added new logger functionality includes the following features: + Loglevels like Log4j, NT Eventlog support, UNIX syslog support and rolling + log files. +* Added wildcard support for classpath entries in wrapper.conf. +* Added the ability to specify configuration properties from the command line. +* Changed the way NT services are installed so that a patched version of the + Wrapper.exe file no longer needs to be created to reference the + wrapper.conf file. + +2.2.4 +* The value of APP_NAME in the bash or sh scripts no longer needs to be the + same as the script. +* Added the ability to format and/or disable file logging and output to the + console. +* Set mode of executables in binary release tar files so that they can be run + without modification after being extracted. +* Fixed line feeds in release so that bat files are always CRLF, unix scripts + are always LF. Other source files are always CRLF in ZIP archives and LF + in tar.gz archives. +* Make the build fail if Wrapper.exe or Wrapper.dll do not exist for Windows + builds. +* Added an empty wrapper.log to the releases so that the TestWrapper example + program runs out of the box. + +2.2.3 +* Added template scripts and conf files for ease of integration with other + applications. +* Cleaned up the build. +* The WrapperSimpleApp method of launchine applications was not working + correctly with applications whose main method did not return. +* Add sample scripts and wrapper.conf files in the bin and lib directories. + These scripts are used to start a sample application which runs out of the + box. See the new example.html page in the documentation for more details. +* Get rid of the platform specific directories in the bin and lib directories. +* Enable relative paths for Windows version. In previous versions of Wrapper, + it was necessary to always use absolute paths because the working directory + of the wrapper would be then NT System32 directory when run as a service. +* On the windows version, the wrapper always sets the current working + directory to the location of the wrapper executable immediately after + startup. +* Improvements to the documentation / web page. + +2.2.2 +* Added HTML based documentation. + +2.2.1 +* Added Linux and Solaris build files. +* Added Linux and Solaris documentation. + +2.2.0 +* Initial Public Release. + diff --git a/Rapla/service/doc/wrapper-community-license-1.1.txt b/Rapla/service/doc/wrapper-community-license-1.1.txt new file mode 100644 index 0000000..dc02512 --- /dev/null +++ b/Rapla/service/doc/wrapper-community-license-1.1.txt @@ -0,0 +1,396 @@ +---------------------------------------------------------------------- +----------------- ----------------- + Tanuki Software, Ltd. + Community Software License Agreement + Version 1.1 + +IMPORTANT-READ CAREFULLY: This license agreement is a legal agreement +between you ("Licensee") and Tanuki Software, Ltd. ("TSI"), which +includes computer software, associated media, printed materials, and +may include online or electronic documentation ( Software ). PLEASE +READ THIS AGREEMENT CAREFULLY BEFORE YOU INSTALL, COPY, DOWNLOAD OR +USE THE SOFTWARE ACCOMPANYING THIS PACKAGE. + +Section 1 - Grant of License + +Community editions of the Software are made available on the GNU +General Public License, Version 2 ("GPLv2"), included in Section 4 of +this license document. All sections of the Community Software License +Agreement must be complied with in addition to those of the GPLv2. + + +Section 2 - Definitions + +2.1. "Community Edition" shall mean versions of the Software Program +distributed in source form under this license agreement, and all new +releases, corrections, enhancements and updates to the Software +Program, which TSI makes generally available under this agreement. + +2.2. "Documentation" shall mean the contents of the website +describing the functionality and use of the Software Program, located +at http://wrapper.tanukisoftware.org + +2.3. "Product" shall mean the computer programs, that are provided by +Licensee to Licensee customers or potential customers, and that +contain both the Software Program as a component of the Product, and a +component or components (other than the Software Program) that provide +the material functionality of the Product. If the Product is released +in source form, the Software Program or any of its components may only +be included in executable form. + +2.4. "Software Program" shall mean the computer software and license +file provided by TSI under this Agreement, including all new releases, +corrections, enhancements and updates to such computer software, which +TSI makes generally available and which Licensee receive pursuant to +Licensee subscription to TSIMS. Some specific features or platforms +may not be enabled if they do not fall under the feature set(s) +covered by the specific license fees paid. + +2.5 "End User" shall mean the customers of the Licensee or any +recipient of the Product whether or not any payment is made to use +the Product. + + +Section 3 - Licensee Obligations + +A copy of this license must be distributed in full with the Product +in a location that is obvious to any End User. + +In accordance with Section 4, the full source code of all components +of the Product must be made available to any and all End Users. + +Licensee may extend and/or modify the Software Program and distribute +under the terms of this agreement provided that the copyright notice +and license information displayed in the console and log files are +not obfuscated or obstructed in any way. + + +Section 4 - GPLv2 License Agreement + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + Everyone is permitted to copy and distribute verbatim copies of + this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your + freedom to share and change it. By contrast, the GNU General + Public License is intended to guarantee your freedom to share and + change free software--to make sure the software is free for all + its users. This General Public License applies to most of the Free + Software Foundation's software and to any other program whose + authors commit to using it. (Some other Free Software Foundation + software is covered by the GNU Library General Public License + instead.) You can apply it to your programs, too. + + When we speak of free software, we are referring to freedom, not + price. Our General Public Licenses are designed to make sure that + you have the freedom to distribute copies of free software (and + charge for this service if you wish), that you receive source code + or can get it if you want it, that you can change the software or + use pieces of it in new free programs; and that you know you can + do these things. + + To protect your rights, we need to make restrictions that forbid + anyone to deny you these rights or to ask you to surrender the + rights. These restrictions translate to certain responsibilities + for you if you distribute copies of the software, or if you modify + it. + + For example, if you distribute copies of such a program, whether + gratis or for a fee, you must give the recipients all the rights + that you have. You must make sure that they, too, receive or can + get the source code. And you must show them these terms so they + know their rights. + + We protect your rights with two steps: + + (1) copyright the software, and + (2) offer you this license which gives you legal permission to + copy, distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make + certain that everyone understands that there is no warranty for + this free software. If the software is modified by someone else + and passed on, we want its recipients to know that what they have + is not the original, so that any problems introduced by others + will not reflect on the original authors' reputations. + + Finally, any free program is threatened constantly by software + patents. We wish to avoid the danger that redistributors of a free + program will individually obtain patent licenses, in effect making + the program proprietary. To prevent this, we have made it clear + that any patent must be licensed for everyone's free use or not + licensed at all. + + The precise terms and conditions for copying, distribution and + modification follow. + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which + contains a notice placed by the copyright holder saying it may be + distributed under the terms of this General Public License. The + "Program", below, refers to any such program or work, and a "work + based on the Program" means either the Program or any derivative + work under copyright law: that is to say, a work containing the + Program or a portion of it, either verbatim or with modifications + and/or translated into another language. (Hereinafter, translation + is included without limitation in the term "modification".) Each + licensee is addressed as "you". + + Activities other than copying, distribution and modification are + not covered by this License; they are outside its scope. The act + of running the Program is not restricted, and the output from the + Program is covered only if its contents constitute a work based on + the Program (independent of having been made by running the + Program). Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's + source code as you receive it, in any medium, provided that you + conspicuously and appropriately publish on each copy an + appropriate copyright notice and disclaimer of warranty; keep + intact all the notices that refer to this License and to the + absence of any warranty; and give any other recipients of the + Program a copy of this License along with the Program. + + You may charge a fee for the physical act of transferring a copy, + and you may at your option offer warranty protection in exchange + for a fee. + + 2. You may modify your copy or copies of the Program or any + portion of it, thus forming a work based on the Program, and copy + and distribute such modifications or work under the terms of + Section 1 above, provided that you also meet all of these + conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but does + not normally print such an announcement, your work based on the + Program is not required to print an announcement.) + + These requirements apply to the modified work as a whole. If + identifiable sections of that work are not derived from the + Program, and can be reasonably considered independent and separate + works in themselves, then this License, and its terms, do not + apply to those sections when you distribute them as separate works. + But when you distribute the same sections as part of a whole which + is a work based on the Program, the distribution of the whole must + be on the terms of this License, whose permissions for other + licensees extend to the entire whole, and thus to each and every + part regardless of who wrote it. + + Thus, it is not the intent of this section to claim rights or + contest your rights to work written entirely by you; rather, the + intent is to exercise the right to control the distribution of + derivative or collective works based on the Program. + + In addition, mere aggregation of another work not based on the + Program with the Program (or with a work based on the Program) on + a volume of a storage or distribution medium does not bring the + other work under the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, + under Section 2) in object code or executable form under the terms + of Sections 1 and 2 above provided that you also do one of the + following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software + interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + + The source code for a work means the preferred form of the work + for making modifications to it. For an executable work, complete + source code means all the source code for all modules it contains, + plus any associated interface definition files, plus the scripts + used to control compilation and installation of the executable. + However, as a special exception, the source code distributed need + not include anything that is normally distributed (in either + source or binary form) with the major components (compiler, + kernel, and so on) of the operating system on which the executable + runs, unless that component itself accompanies the executable. + + If distribution of executable or object code is made by offering + access to copy from a designated place, then offering equivalent + access to copy the source code from the same place counts as + distribution of the source code, even though third parties are not + compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program + except as expressly provided under this License. Any attempt + otherwise to copy, modify, sublicense or distribute the Program is + void, and will automatically terminate your rights under this + License. However, parties who have received copies, or rights, + from you under this License will not have their licenses + terminated so long as such parties remain in full compliance. + + 5. You are not required to accept this License, since you have not + signed it. However, nothing else grants you permission to modify + or distribute the Program or its derivative works. These actions + are prohibited by law if you do not accept this License. + Therefore, by modifying or distributing the Program (or any work + based on the Program), you indicate your acceptance of this + License to do so, and all its terms and conditions for copying, + distributing or modifying the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on + the Program), the recipient automatically receives a license from + the original licensor to copy, distribute or modify the Program + subject to these terms and conditions. You may not impose any + further restrictions on the recipients' exercise of the rights + granted herein. You are not responsible for enforcing compliance + by third parties to this License. + + 7. If, as a consequence of a court judgment or allegation of + patent infringement or for any other reason (not limited to + patent issues), conditions are imposed on you (whether by court + order, agreement or otherwise) that contradict the conditions of + this License, they do not excuse you from the conditions of this + License. If you cannot distribute so as to satisfy simultaneously + your obligations under this License and any other pertinent + obligations, then as a consequence you may not distribute the + Program at all. For example, if a patent license would not permit + royalty-free redistribution of the Program by all those who + receive copies directly or indirectly through you, then the only + way you could satisfy both it and this License would be to refrain + entirely from distribution of the Program. + + If any portion of this section is held invalid or unenforceable + under any particular circumstance, the balance of the section is + intended to apply and the section as a whole is intended to apply + in other circumstances. + + It is not the purpose of this section to induce you to infringe + any patents or other property right claims or to contest validity + of any such claims; this section has the sole purpose of + protecting the integrity of the free software distribution system, + which is implemented by public license practices. Many people have + made generous contributions to the wide range of software + distributed through that system in reliance on consistent + application of that system; it is up to the author/donor to decide + if he or she is willing to distribute software through any other + system and a licensee cannot impose that choice. + + This section is intended to make thoroughly clear what is believed + to be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in + certain countries either by patents or by copyrighted interfaces, + the original copyright holder who places the Program under this + License may add an explicit geographical distribution limitation + excluding those countries, so that distribution is permitted only + in or among countries not thus excluded. In such case, this + License incorporates the limitation as if written in the body of + this License. + + 9. The Free Software Foundation may publish revised and/or new + versions of the General Public License from time to time. Such new + versions will be similar in spirit to the present version, but may + differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the + Program specifies a version number of this License which applies + to it and "any later version", you have the option of following + the terms and conditions either of that version or of any later + version published by the Free Software Foundation. If the Program + does not specify a version number of this License, you may choose + any version ever published by the Free Software Foundation. + + 10. If you wish to incorporate parts of the Program into other + free programs whose distribution conditions are different, write + to the author to ask for permission. For software which is + copyrighted by the Free Software Foundation, write to the Free + Software Foundation; we sometimes make exceptions for this. Our + decision will be guided by the two goals of preserving the free + status of all derivatives of our free software and of promoting + the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO + WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE + LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS + AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY + OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND + PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE + DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR + OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN + WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY + MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE + LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, + INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR + INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF + DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU + OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY + OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + +Section 4 - 3rd Party Components + +(1) The Software Program includes software and documentation components +developed in part by Silver Egg Technology, Inc.("SET") prior to 2001 +and released under the following license. + + Copyright (c) 2001 Silver Egg Technology + + 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, sub-license, 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 + NON-INFRINGEMENT. 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. + diff --git a/Rapla/service/lib/wrapper.dll b/Rapla/service/lib/wrapper.dll new file mode 100644 index 0000000..2041570 Binary files /dev/null and b/Rapla/service/lib/wrapper.dll differ diff --git a/Rapla/service/lib/wrapper.jar b/Rapla/service/lib/wrapper.jar new file mode 100644 index 0000000..9356e00 Binary files /dev/null and b/Rapla/service/lib/wrapper.jar differ diff --git a/Rapla/src/org/rapla/AppointmentFormaterImpl.java b/Rapla/src/org/rapla/AppointmentFormaterImpl.java new file mode 100644 index 0000000..a715852 --- /dev/null +++ b/Rapla/src/org/rapla/AppointmentFormaterImpl.java @@ -0,0 +1,359 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla; +import java.util.Calendar; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.rapla.components.util.DateTools; +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.AppointmentFormater; +import org.rapla.entities.domain.Period; +import org.rapla.entities.domain.Repeating; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.framework.RaplaLocale; + +/** default implementation of appointment formater */ +class AppointmentFormaterImpl + implements + AppointmentFormater +{ + I18nBundle i18n; + RaplaLocale loc; + + public AppointmentFormaterImpl(RaplaContext sm) throws RaplaException + { + i18n = (I18nBundle) sm.lookup(I18nBundle.ROLE + "/org.rapla.RaplaResources"); + loc = (RaplaLocale) sm.lookup(RaplaLocale.ROLE); + } + + protected RaplaLocale getRaplaLocale() { + return loc; + } + + protected I18nBundle getI18n() { + return i18n; + } + + protected String getString(String key) { + return i18n.getString(key); + } + + + public String getShortSummary(Appointment appointment) { + String time = loc.formatTime(appointment.getStart()); + Repeating repeating = appointment.getRepeating(); + final boolean wholeDaysSet = appointment.isWholeDaysSet(); + final String timeString = wholeDaysSet ? "" :" " + time; + if (repeating != null) { + if (repeating.isWeekly()) { + return loc.getWeekday(appointment.getStart()) + timeString; + } + if (repeating.isDaily()) + return getString("daily") + " " + time; + if (repeating.isMonthly()) + return getWeekdayOfMonth( appointment.getStart() ) + loc.getWeekday(appointment.getStart()) + timeString; + if (repeating.isYearly()) + return getDayOfMonth( appointment.getStart() ) + loc.getMonth(appointment.getStart()) +" " + timeString; + } + String date = loc.formatDate(appointment.getStart()); + return date + " " + timeString; + } + + public String getVeryShortSummary(Appointment appointment) { + Repeating repeating = appointment.getRepeating(); + if (repeating != null) { + if (repeating.isWeekly()) + return getRaplaLocale().getWeekday(appointment.getStart()); + if (repeating.isDaily()) { + String time = getRaplaLocale().formatTime(appointment.getStart()); + return time; + } + if (repeating.isMonthly()) + { + return getRaplaLocale().getWeekday(appointment.getStart()); + } + } + String date = getRaplaLocale().formatDateShort(appointment.getStart()); + return date; + } + + public String getSummary( Appointment a ) { + StringBuffer buf = new StringBuffer(); + Repeating repeating = a.getRepeating(); + final boolean wholeDaysSet = a.isWholeDaysSet(); + if ( repeating == null ) + { + buf.append( loc.getWeekday( a.getStart() ) ); + buf.append( ' ' ); + buf.append( loc.formatDate( a.getStart() ) ); + if (!wholeDaysSet) + { + buf.append( ' ' ); + buf.append( loc.formatTime( a.getStart() ) ); + + if ( isSameDay( a.getStart(), a.getEnd() ) ) + { + buf.append( '-' ); + } + else + { + buf.append( " - " ); + buf.append( loc.getWeekday( a.getEnd() ) ); + buf.append( ' ' ); + buf.append( loc.formatDate( a.getEnd() ) ); + buf.append( ' ' ); + } + buf.append( loc.formatTime( a.getEnd() ) ); + } + else if ( a.getEnd().getTime() - a.getStart().getTime() > DateTools.MILLISECONDS_PER_DAY) + { + buf.append( " - " ); + buf.append( loc.getWeekday( a.getEnd() ) ); + buf.append( ' ' ); + buf.append( loc.formatDate( a.getEnd() ) ); + } + } + else if ( repeating.isWeekly() || repeating.isMonthly() || repeating.isYearly()) + { + if( repeating.isMonthly()) + { + buf.append( getWeekdayOfMonth( a.getStart() )); + } + if (repeating.isYearly()) + { + buf.append( getDayOfMonth( a.getStart() ) ); + buf.append( loc.getMonth( a.getStart() ) ); + } + else + { + buf.append( loc.getWeekday( a.getStart() ) ); + } + if (wholeDaysSet) + { + if ( a.getEnd().getTime() - a.getStart().getTime() > DateTools.MILLISECONDS_PER_DAY) + { + if ( a.getEnd().getTime() - a.getStart().getTime() <= DateTools.MILLISECONDS_PER_DAY * 6 ) + { + buf.append( " - " ); + buf.append( loc.getWeekday( a.getEnd() ) ); + } + else + { + buf.append( ' ' ); + buf.append( loc.formatDate( a.getStart() ) ); + buf.append( " - " ); + buf.append( loc.getWeekday( a.getEnd() ) ); + buf.append( ' ' ); + buf.append( loc.formatDate( a.getEnd() ) ); + } + } + } + else + { + buf.append( ' ' ); + if ( isSameDay( a.getStart(), a.getEnd() ) ) + { + buf.append( loc.formatTime( a.getStart() ) ); + buf.append( '-' ); + buf.append( loc.formatTime( a.getEnd() ) ); + } + else if ( a.getEnd().getTime() - a.getStart().getTime() <= DateTools.MILLISECONDS_PER_DAY * 6 ) + { + buf.append( loc.formatTime( a.getStart() ) ); + buf.append( " - " ); + buf.append( loc.getWeekday( a.getEnd() ) ); + buf.append( ' ' ); + buf.append( loc.formatTime( a.getEnd() ) ); + } + else + { + buf.append( loc.formatDate( a.getStart() ) ); + buf.append( ' ' ); + buf.append( loc.formatTime( a.getStart() ) ); + buf.append( " - " ); + buf.append( loc.getWeekday( a.getEnd() ) ); + buf.append( ' ' ); + buf.append( loc.formatDate( a.getEnd() ) ); + buf.append( ' ' ); + buf.append( loc.formatTime( a.getEnd() ) ); + } + } + + if ( repeating.isWeekly()) + { + buf.append( ' ' ); + buf.append( getInterval( repeating ) ); + } + if ( repeating.isMonthly()) + { + buf.append(" " + getString("monthly")); + } + if ( repeating.isYearly()) + { + buf.append(" " + getString("yearly")); + } + } + + else if ( repeating.isDaily() ) + { + + long days =(a.getEnd().getTime() - a.getStart().getTime()) / (DateTools.MILLISECONDS_PER_HOUR * 24 ); + if ( !a.isWholeDaysSet()) + { + buf.append( loc.formatTime( a.getStart() ) ); + if ( days <1) + { + buf.append( '-' ); + buf.append( loc.formatTime( a.getEnd() ) ); + } + buf.append( ' ' ); + } + buf.append( getInterval( repeating ) ); + } + return buf.toString(); + } + + private String getWeekdayOfMonth( Date date ) + { + StringBuffer b = new StringBuffer(); + Calendar cal = getRaplaLocale().createCalendar(); + cal.setTime( date); + int numb = cal.get( Calendar.DAY_OF_WEEK_IN_MONTH); + b.append( String.valueOf(numb)); + b.append( '.'); + b.append( ' '); + return b.toString(); + } + + private String getDayOfMonth( Date date ) + { + StringBuffer b = new StringBuffer(); + Calendar cal = getRaplaLocale().createCalendar(); + cal.setTime( date); + int numb = cal.get( Calendar.DAY_OF_MONTH); + b.append( String.valueOf(numb)); + b.append( '.'); + b.append( ' '); + return b.toString(); + } + + /** uses the internal calendar object for date comparison. + @see DateTools#isSameDay( java.util.Calendar, Date, Date) + */ + private boolean isSameDay( Date d1, Date d2 ) { + return DateTools.isSameDay( loc.createCalendar(), d1, d2 ); + } + + + public String getExceptionSummary( Repeating r ) { + StringBuffer buf = new StringBuffer(); + buf.append(getString("appointment.exceptions")); + buf.append(": "); + Date[] exc = r.getExceptions(); + for ( int i=0;i0) + buf.append(", "); + buf.append( getRaplaLocale().formatDate( exc[i] ) ); + } + return buf.toString(); + } + + private String getInterval( Repeating r ) { + StringBuffer buf = new StringBuffer(); + if ( r.getInterval() == 1 ) { + buf.append( getString( r.getType().toString() ) ); + } else { + String fString ="weekly"; + if ( r.isWeekly() ) { + fString = getString( "weeks" ); + } + if ( r.isDaily() ) { + fString = getString( "days" ); + } + buf.append( getI18n().format( "interval.format", "" + r.getInterval(), fString ) ); + } + return buf.toString(); + } + + private boolean isPeriodicaly(Period period, Repeating r) { + Appointment a = r.getAppointment(); + if (r.getEnd().after( period.getEnd() ) ) + return false; + if ( r.isWeekly() ) + { + return + ( DateTools.cutDate(a.getStart().getTime()) - period.getStart().getTime() ) + <= DateTools.MILLISECONDS_PER_DAY * 6 + && + ( DateTools.cutDate(period.getEnd().getTime()) - r.getEnd().getTime() ) + <= DateTools.MILLISECONDS_PER_DAY * 6 + ; + } + else if ( r.isDaily() ) + { + return + isSameDay( a.getStart(), period.getStart() ) + && + isSameDay( r.getEnd(), period.getEnd() ) + ; + } + return false; + } + + public String getSummary( Repeating r , List periods) { + if ( r.getEnd() != null && !r.isFixedNumber() ) + { + Iterator it = periods.iterator(); + while ( it.hasNext() ) { + Period period = (Period) it.next(); + if ( isPeriodicaly(period, r)) + return getI18n().format("in_period.format" + ,period.getName(loc.getLocale()) + ); + } + } + return getSummary(r); + } + + public String getSummary( Repeating r ) { + Appointment a = r.getAppointment(); + StringBuffer buf = new StringBuffer(); + String startDate = loc.formatDate( a.getStart() ); + buf.append( getI18n().format("format.repeat_from", startDate) ); + buf.append( ' ' ); + // print end date, when end is given + if ( r.getEnd() != null) { + String endDate = loc.formatDate( DateTools.subDay(r.getEnd()) ); + buf.append( getI18n().format("format.repeat_until", endDate) ); + buf.append( ' ' ); + } + + // print number of repeating if number is gt 0 and fixed times + if ( r.getNumber()>=0 && r.isFixedNumber() ) { + buf.append( getI18n().format("format.repeat_n_times", String.valueOf(r.getNumber())) ); + buf.append( ' ' ); + } + // print never ending if end is null + if (r.getEnd() == null ){ + buf.append( getString("repeating.forever") ); + buf.append( ' ' ); + } + + return buf.toString(); + } + +} diff --git a/Rapla/src/org/rapla/COPYING b/Rapla/src/org/rapla/COPYING new file mode 100644 index 0000000..addf491 --- /dev/null +++ b/Rapla/src/org/rapla/COPYING @@ -0,0 +1,280 @@ + GNU GENERAL PUBLIC LICENSE Version 2, June 1991 + + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/Rapla/src/org/rapla/Main.java b/Rapla/src/org/rapla/Main.java new file mode 100644 index 0000000..979b81a --- /dev/null +++ b/Rapla/src/org/rapla/Main.java @@ -0,0 +1,229 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla; +import java.awt.Font; +import java.net.URL; +import java.util.Map; + +import javax.swing.UIDefaults; +import javax.swing.UIManager; + +import org.apache.avalon.framework.container.ContainerUtil; +import org.apache.avalon.framework.logger.ConsoleLogger; +import org.apache.avalon.framework.logger.Logger; +import org.rapla.client.ClientService; +import org.rapla.client.RaplaClientListenerAdapter; +import org.rapla.framework.Container; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.StartupEnvironment; +import org.rapla.framework.internal.ConfigTools; +import org.rapla.gui.images.Images; +import org.rapla.storage.ImportExportManager; + +/** This class is used to start rapla from the command-line. +

+ Usage :
+ [-?|-c PATH_TO_CONFIG_FILE] [ACTION]
+ Possible actions:
+ rapla   : Starts the rapla-gui (this is the default)
+ client  : Starts the rapla-gui in client/server mode
+ 
+ You can also display the list with the -? argument. +*/ + +final public class Main { + + /** The default config filename for standalone-mode is rapla.xconf*/ + public final static String DEFAULT_CONFIG_NAME = "rapla.xconf"; + /** The default config filename for client-mode raplaclient.xconf*/ + public final static String DEFAULT_CLIENT_CONFIG_NAME = "raplaclient.xconf"; + + public final static String DEFAULT_SERVER_CONFIG_NAME = "raplaserver.xconf"; + + + public static final String USAGE = new String ( + "Usage : \n" + + "[-?|-c PATH_TO_CONFIG_FILE] [ACTION]\n" + + "Possible actions:\n" + + " rapla : Starts the rapla-gui (this is the default)\n" + + " client : Starts the rapla-client (this is the default)\n" + + " import : Import from file into the database\n" + + " See importexport entry in " + DEFAULT_SERVER_CONFIG_NAME + "\n" + + " export : Export from database into file\n" + + " See importexport entry in " + DEFAULT_SERVER_CONFIG_NAME + "\n" + ); + + + private Logger logger = new ConsoleLogger(ConsoleLogger.LEVEL_WARN).getChildLogger("init"); + RaplaStartupEnvironment env = new RaplaStartupEnvironment(); + Container raplaContainer; + + Main() { + + } + + void init(URL configURL,int mode) throws Exception { + env.setStartupMode( mode ); + env.setConfigURL( configURL ); + env.setBootstrapLogger( getLogger() ); + } + + /** starts Rapla with the default controller-class: org.rapla.gui.usecase.defaults.MainController + You can replace this with your custom controller (you need to add a plugin. See plugins for details).*/ + void startRapla() throws Exception { + setLookandFeel(); + raplaContainer = new RaplaMainContainer( env); + RaplaContext sm = raplaContainer.getContext(); + ClientService client = (ClientService) sm.lookup(ClientService.ROLE); + client.addRaplaClientListener(new RaplaClientListenerAdapter() { + public void clientClosed(boolean restart) { + if ( restart) { + ContainerUtil.dispose( raplaContainer); + try { + startRapla(); + } catch (Exception ex) { + getLogger().error("Error restarting client",ex); + exit(); + } + } else { + exit(); + } + } + public void clientAborted() + { + exit(); + } + }); + } + + private void setLookandFeel() { + UIDefaults defaults = UIManager.getDefaults(); + Font textFont = defaults.getFont("Label.font"); + if ( textFont == null) + { + textFont = new Font("SansSerif", Font.PLAIN, 12); + } + else + { + textFont = textFont.deriveFont( Font.PLAIN ); + } + defaults.put("Label.font", textFont); + defaults.put("Button.font", textFont); + defaults.put("Menu.font", textFont); + defaults.put("MenuItem.font", textFont); + defaults.put("RadioButton.font", textFont); + defaults.put("CheckBoxMenuItem.font", textFont); + defaults.put("CheckBox.font", textFont); + defaults.put("ComboBox.font", textFont); + defaults.put("Tree.expandedIcon",Images.getIcon("/org/rapla/gui/images/eclipse-icons/tree_minus.gif")); + defaults.put("Tree.collapsedIcon",Images.getIcon("/org/rapla/gui/images/eclipse-icons/tree_plus.gif")); + defaults.put("TitledBorder.font", textFont.deriveFont(Font.PLAIN,(float)10.)); + + } + + private void exit() { + ContainerUtil.dispose( raplaContainer); + if (env.getStartupMode() != StartupEnvironment.APPLET) + System.exit(0); + } + + Logger getLogger() { + return logger; + } + + /** Read the data out of the dest-operator and write it into the source operator. The necessary + properties should be specified in the config-file. + */ + public void startExport() throws Exception { + startExportImport(true); + } + + /** Read the data out of the source-operator and write it into the dest operator. The necessary + properties should be specified in the config-file. + */ + public void startImport() throws Exception { + startExportImport(false); + } + + private void startExportImport(boolean export) throws Exception { + raplaContainer = new RaplaMainContainer( env); + RaplaContext sm = raplaContainer.getContext(); + ImportExportManager conv = (ImportExportManager) sm.lookup(ImportExportManager.ROLE); + if (export) + conv.doExport(); + else + conv.doImport(); + } + + + /** This is the entry point for the client-application. + * For a complete list of the arguments see the description + * of this class. */ + public static void main(String[] args) { + Main main = new Main(); + Map paramMap = ConfigTools.parseParams(args); + if (paramMap == null) { + System.out.println(USAGE); + return; + } + String action = (String) paramMap.get("action"); + String config = (String) paramMap.get("config"); + if (action == null) + action = "rapla"; + try { + if ( action.equals("build-script")) { + main.init( ConfigTools.configFileToURL(config, DEFAULT_CONFIG_NAME) + ,StartupEnvironment.CONSOLE); + main.startRapla(); + } else if ( action.equals("webstart")) { + main.init( ConfigTools.webstartConfigToURL(config, "webclient/" + DEFAULT_CLIENT_CONFIG_NAME) + ,StartupEnvironment.WEBSTART); + main.startRapla(); + } else if ( action.equals("rapla")) { + main.init( ConfigTools.configFileToURL(config, DEFAULT_CONFIG_NAME) + , StartupEnvironment.CONSOLE); + main.startRapla(); + main.getLogger().info("Exit"); + } else if ( action.equals("client")) { + main.init( ConfigTools.configFileToURL(config, "webapp/webclient/" + DEFAULT_CLIENT_CONFIG_NAME) + , StartupEnvironment.CONSOLE); + main.startRapla(); + main.getLogger().info("Exit"); + } else if ( action.equals("import")) { + main.init( ConfigTools.configFileToURL(config, DEFAULT_SERVER_CONFIG_NAME) + ,StartupEnvironment.CONSOLE); + main.startImport(); + } else if ( action.equals("export")) { + main.init( ConfigTools.configFileToURL(config, DEFAULT_SERVER_CONFIG_NAME) + ,StartupEnvironment.CONSOLE); + main.startExport(); + } else { + System.out.println(USAGE); + } + } catch (Throwable ex) { + main.getLogger().error("Couldn't start Rapla",ex); + if (main != null) + ContainerUtil.dispose(main.raplaContainer); + System.out.flush(); + try + { + Thread.sleep( 2000 ); + } + catch ( InterruptedException e ) + { + } + System.exit(1); + + } + } +} diff --git a/Rapla/src/org/rapla/MainApplet.java b/Rapla/src/org/rapla/MainApplet.java new file mode 100644 index 0000000..a99126c --- /dev/null +++ b/Rapla/src/org/rapla/MainApplet.java @@ -0,0 +1,135 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla; + +import java.awt.Color; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.net.URL; + +import javax.swing.BorderFactory; +import javax.swing.JApplet; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; + +import org.rapla.framework.StartupEnvironment; + +/** The applet-encapsulation of the Main.class reads the configuration + * from the document-base of the applet and displays + * an applet with a start button. + * @author Christopher Kohlhaas + * @see Main + */ + +final public class MainApplet extends JApplet +{ + private static final long serialVersionUID = 1L; + + JPanel dlg = new JPanel(); + JButton button; + JLabel label; + + boolean startable = false; + + public MainApplet() + { + JPanel panel1 = new JPanel(); + panel1.setBackground( new Color( 255, 255, 204 ) ); + + GridLayout gridLayout1 = new GridLayout(); + gridLayout1.setColumns( 1 ); + gridLayout1.setRows( 3 ); + gridLayout1.setHgap( 10 ); + gridLayout1.setVgap( 10 ); + panel1.setLayout( gridLayout1 ); + + label = new JLabel( "Rapla-Applet loading" ); + panel1.add( label ); + + button = new JButton( "StartRapla" ); + button.addActionListener( new ActionListener() + { + + public void actionPerformed( ActionEvent e ) + { + startThread(); + } + + } ); + panel1.add( button ); + + dlg.setBackground( new Color( 255, 255, 204 ) ); + dlg.setBorder( BorderFactory.createMatteBorder( 1, 1, 2, 2, Color.black ) ); + dlg.add( panel1 ); + } + + public void start() + { + getRootPane().putClientProperty( "defeatSystemEventQueueCheck", Boolean.TRUE ); + try + { + setContentPane( dlg ); + button.setEnabled( startable ); + startable = true; + button.setEnabled( startable ); + } + catch ( Exception ex ) + { + ex.printStackTrace(); + } + } + + private void updateStartable() + { + javax.swing.SwingUtilities.invokeLater( new Runnable() + { + public void run() + { + button.setEnabled( startable ); + } + } ); + } + + private void startThread() + { + ( new Thread() + { + public void run() + { + try + { + startable = false; + updateStartable(); + Main main = new Main(); + URL configURL = new URL( getDocumentBase(), "webclient/" + Main.DEFAULT_CLIENT_CONFIG_NAME ); + main.init( configURL, StartupEnvironment.APPLET ); + main.env.setDownloadURL( getDocumentBase() ); + System.out.println( "Docbase " + getDocumentBase() ); + main.startRapla(); + } + catch ( Exception ex ) + { + ex.printStackTrace(); + } + finally + { + startable = true; + updateStartable(); + } + } + } ).start(); + } + +} diff --git a/Rapla/src/org/rapla/MainServlet.java b/Rapla/src/org/rapla/MainServlet.java new file mode 100644 index 0000000..0ccd438 --- /dev/null +++ b/Rapla/src/org/rapla/MainServlet.java @@ -0,0 +1,358 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla; + +import java.io.File; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.net.URL; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.apache.avalon.framework.container.ContainerUtil; +import org.apache.avalon.framework.logger.Logger; +import org.rapla.components.util.IOUtil; +import org.rapla.framework.Container; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaContextException; +import org.rapla.framework.StartupEnvironment; +import org.rapla.plugin.RaplaExtensionPoints; +import org.rapla.server.RemoteSession; +import org.rapla.server.ServerService; +import org.rapla.server.ShutdownListener; +import org.rapla.server.ShutdownService; +import org.rapla.server.internal.RemoteSessionImpl; +import org.rapla.server.internal.ServerServiceImpl; +import org.rapla.servletpages.RaplaPageGenerator; + + +final public class MainServlet extends HttpServlet { + private static final long serialVersionUID = 1L; + + /** The default config filename is raplaserver.xconf*/ + Container raplaMainContainer; + public final static String DEFAULT_CONFIG_NAME = "raplaserver.xconf"; + Collection pageList; + ServerService serverService; + + long serverStartTime; + String serverId = null; + + private File getConfigFile(String entryName, String defaultName) throws ServletException,IOException { + String configName = getServletConfig().getInitParameter(entryName); + if (configName == null) + configName = defaultName; + if (configName == null) + throw new ServletException("Must specify " + entryName + " entry in web.xml !"); + + File configFile = new File(getServletConfig().getServletContext().getRealPath("/WEB-INF/" + configName)); + if (!configFile.exists()) { + String message = "ERROR: Config file not found " + configName; + throw new ServletException(message); + } + return configFile.getCanonicalFile(); + } + + public String getServerId() + { + return serverId; + } + + public void setServerId( String serverId ) + { + this.serverId = serverId; + } + + /** + * Initializes Servlet and creates a RaplaMainContainer instance + * + * @exception ServletException if an error occurs + */ + public void init() + throws ServletException + { + String realPath = getServletContext().getRealPath("webclient"); + // if (realPath != null) + { + File webclientFolder= new File(realPath ); + webclientFolder.mkdir(); + copy( "WEB-INF/lib/rapla.jar", "webclient/rapla.jar" ); + } + startServer(); + } + + private void copy( String sourceLib, String destLib ) throws ServletException + { + if (!new File(getServletContext().getRealPath(sourceLib)).exists()) + { + return; + } + try + { + log("Copy " + sourceLib + " to " + destLib); + IOUtil.copy( getServletContext().getRealPath(sourceLib), getServletContext().getRealPath(destLib), true); + } + catch (IOException e) + { + throw new ServletException("Can't copy " + sourceLib + " Cause " + e.getMessage()); + } + } + + ShutdownListener shutdownListener = new ShutdownListener() { + public void shutdownInitiated() { + } + + public void shutdownComplete( boolean restart) { + if ( restart ) { + Thread restartThread = new Thread() { + public void run() { + try { + log( "Stopping Server"); + stopServer(); + //getServletContext() + log( "Restarting Server"); + startServer(); + } catch (Exception e) { + log( "Error while restarting Server", e ); + } + } + }; + restartThread.setDaemon( false ); + restartThread.start(); + } + } + }; + + void startServer()throws ServletException { + log("Starting Rapla Servlet"); + Logger logger = null; + serverStartTime = System.currentTimeMillis(); + + try + { + File configFile = getConfigFile("config-file",DEFAULT_CONFIG_NAME); + URL configURL = configFile.toURI().toURL(); + URL logConfigURL = getConfigFile("log-config-file","raplaserver.xlog").toURI().toURL(); + + RaplaStartupEnvironment env = new RaplaStartupEnvironment(); + env.setStartupMode( StartupEnvironment.SERVLET); + env.setConfigURL( configURL ); + env.setLogConfigURL( logConfigURL ); + + raplaMainContainer = new RaplaMainContainer( env ); + logger = (Logger)raplaMainContainer.getContext().lookup(Logger.class.getName()); + try { + //lookup shutdownService + ShutdownService shutdownService = (ShutdownService) raplaMainContainer.getContext().lookup(ShutdownService.ROLE); + shutdownService.addShutdownListener( shutdownListener ); + } catch (RaplaContextException ex) { + log("No shutdown service found. You must stop the server with ctrl-c!"); + } + // Start the storage service + RaplaContext sm = raplaMainContainer.getContext(); + + + + String lookupName = ServerService.ROLE ; + if (serverId != null) + { + lookupName += "/" + serverId; + } + serverService = (ServerService)sm.lookup( lookupName ); + + pageList = serverService.getAllServicesFor( RaplaExtensionPoints.SERVLET_PAGE_EXTENSION); + + getServletConfig().getServletContext().setAttribute("context", sm); + } + catch( Exception e ) + { + if ( logger != null) { + logger.fatalError("Could not start server", e); + } + ContainerUtil.dispose( raplaMainContainer); + log( "Problem starting Rapla ", e ); + throw new ServletException( "Error during initialization", e ); + } + log("Rapla Servlet started"); + + } + + /** + * Pass all servlet requests through to container to be handled. + ^s */ + public void service( HttpServletRequest request, HttpServletResponse response ) + throws IOException, ServletException + { + String page = request.getParameter("page"); + String contextPath =request.getRequestURI(); + + int rpcIndex=contextPath.indexOf("/rapla/rpc/") ; + if ( rpcIndex>= 0) { + handleRPCCall( request, response, contextPath ); + return; + } + if ( page == null || page.trim().length() == 0) { + page = "index"; + } + + if (pageList.contains( page) ) { + RaplaPageGenerator servletPage; + try { + servletPage = (RaplaPageGenerator) serverService.getContext().lookup( RaplaExtensionPoints.SERVLET_PAGE_EXTENSION + "/" + page ); + } catch (RaplaContextException e) { + java.io.PrintWriter out = response.getWriter(); + out.println(IOUtil.getStackTraceAsString( e)); + throw new ServletException( e); + } + servletPage.generatePage( getServletContext(), request, response); + } else { + throw new ServletException( "Page " + page + " not found in Rapla context"); + } + } + + private void handleRPCCall( HttpServletRequest request, HttpServletResponse response, String contextPath ) throws IOException + { + int rpcIndex=contextPath.indexOf("/rapla/rpc/") ; + String methodName = contextPath.substring(rpcIndex + "/rapla/rpc/".length()); + HttpSession session = request.getSession( true); + + if ( methodName.equals("getException")) + { + Exception ex = (Exception)session.getAttribute("lastException"); + if ( ex == null) + { + response.sendError( 500 , "No exception found"); + } else { + ObjectOutputStream out = new ObjectOutputStream( response.getOutputStream()); + out.writeObject( ex); + out.flush(); + } + return; + } + + String username = (String)session.getAttribute("username"); + try + { + Map paramterMap = makeSingles(request.getParameterMap()); + ServerServiceImpl server = (ServerServiceImpl)raplaMainContainer.getContext().lookup( ServerService.ROLE); + if ( methodName.equals("login")) + { + username = server.login( paramterMap); + session.setAttribute("username", username); + response.getWriter().println("Login successfull" ); + } + else if ( methodName.equals("logout")) + { + session.removeAttribute("username"); + response.getWriter().println("User logout" ); + } + else + { + RemoteSession remoteSession = (RemoteSession)session.getAttribute(RemoteSession.class.getName()); + if ( remoteSession != null) + { + // If session was created by another server, than invalidate + if (((RemoteSessionImpl)remoteSession).getServerStartTime() != serverStartTime) + { + remoteSession = null; + } + } + if ( remoteSession == null) + { + remoteSession = new RemoteSessionImpl(server.getContext(), session.getId(), serverStartTime); + session.setAttribute( RemoteSession.class.getName(), remoteSession); + } + ((RemoteSessionImpl)remoteSession).setUsername( username); + byte[] out = server.dispatch(remoteSession, methodName, paramterMap); + response.setContentType( "text/html; charset=utf-8"); + //response.setCharacterEncoding( "utf-8" ); + response.getOutputStream().write( out ); + } + // response.getWriter().println("There are currently " + reservations + " reservations"); + } + catch (Exception e) + { + String message = e.getMessage(); + if ( message == null ) + { + message = e.getClass().getName(); + } + session.setAttribute( "lastException", e); + response.addHeader("X-Error-Stacktrace", message); + response.getWriter().println("Error: " + IOUtil.getStackTraceAsString( e)); + + //response.sendError( 500, e.getMessage()); + + response.setStatus( 500); + //throw new ServletException( e); + } + } + + private Map makeSingles( Map parameterMap ) + { + Map singlesMap = new HashMap(); + for (Iterator it = parameterMap.keySet().iterator();it.hasNext();) + { + String key = (String)it.next(); + String[] values = (String[]) parameterMap.get( key); + if ( values != null && values.length > 0 ) + { + singlesMap.put( key,values[0]); + } + else + { + singlesMap.put( key,null); + } + } + + return singlesMap; + + } + + private void stopServer() { + try { + ShutdownService shutdownService = (ShutdownService) raplaMainContainer.getContext().lookup(ShutdownService.ROLE); + shutdownService.removeShutdownListener( shutdownListener ); + ContainerUtil.dispose( raplaMainContainer ); + } catch (Exception ex) { + log("Error while stopping server"); + } + } + + /** + * Disposes of container manager and container instance. + */ + public void destroy() + { + log("Destroying rapla"); + stopServer(); + } + + public RaplaContext getContext() + { + return raplaMainContainer.getContext(); + } + + + + +} + diff --git a/Rapla/src/org/rapla/MyResources.xml b/Rapla/src/org/rapla/MyResources.xml new file mode 100644 index 0000000..c6de8b9 --- /dev/null +++ b/Rapla/src/org/rapla/MyResources.xml @@ -0,0 +1,8 @@ + + + + + + Hello {0}, enjoy yourself! + + diff --git a/Rapla/src/org/rapla/RaplaLocaleImpl.java b/Rapla/src/org/rapla/RaplaLocaleImpl.java new file mode 100644 index 0000000..9a96457 --- /dev/null +++ b/Rapla/src/org/rapla/RaplaLocaleImpl.java @@ -0,0 +1,250 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla; +import java.text.DateFormat; +import java.text.FieldPosition; +import java.text.NumberFormat; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.logger.Logger; +import org.rapla.components.util.DateTools; +import org.rapla.components.xmlbundle.LocaleSelector; +import org.rapla.components.xmlbundle.impl.LocaleSelectorImpl; +import org.rapla.framework.RaplaException; +import org.rapla.framework.RaplaLocale; +class RaplaLocaleImpl implements RaplaLocale { + + TimeZone zone; + LocaleSelector localeSelector = new LocaleSelectorImpl(); + + String[] availableLanguages; + + String COUNTRY = "country"; + String LANGUAGES = "languages"; + String LANGUAGE = "language"; + String CHARSET = "charset"; + String charsetForHtml; + + public RaplaLocaleImpl(Configuration config ) throws RaplaException + { + this(config, null); + } + + public RaplaLocaleImpl(Configuration config,Logger logger ) throws RaplaException + { + String selectedCountry = config.getChild( COUNTRY).getValue(Locale.getDefault().getCountry() ); + Configuration languageConfig = config.getChild( LANGUAGES ); + Configuration[] languages = languageConfig.getChildren( LANGUAGE ); + charsetForHtml = config.getChild(CHARSET).getValue("iso-8859-15"); + availableLanguages = new String[languages.length]; + for ( int i=0;i=0) ) { + buf.delete( fieldPosition.getBeginIndex(), fieldPosition.getEndIndex() ); + } + return buf.toString(); + } + + /* (non-Javadoc) + * @see org.rapla.common.IRaplaLocale#formatDate(java.util.Date) + */ + public String formatDate( Date date ) { + DateFormat format = DateFormat.getDateInstance( DateFormat.SHORT, getLocale() ); + format.setTimeZone( zone ); + return format.format( date ); + } + + + /* (non-Javadoc) + * @see org.rapla.common.IRaplaLocale#formatDateLong(java.util.Date) + */ + public String formatDateLong( Date date ) { + DateFormat format = DateFormat.getDateInstance( DateFormat.MEDIUM, getLocale() ); + format.setTimeZone( zone ); + return format.format( date) + " (" + getWeekday(date) + ")"; + } + + /* (non-Javadoc) + * @see org.rapla.common.IRaplaLocale#getWeekday(java.util.Date) + */ + public String getWeekday( Date date ) { + SimpleDateFormat format = new SimpleDateFormat( "EE", getLocale() ); + format.setTimeZone( getTimeZone() ); + return format.format( date ); + } + + /* (non-Javadoc) + * @see org.rapla.common.IRaplaLocale#getMonth(java.util.Date) + */ + public String getMonth( Date date ) { + SimpleDateFormat format = new SimpleDateFormat( "MMMMM", getLocale() ); + format.setTimeZone( getTimeZone() ); + return format.format( date ); + } + + + /* (non-Javadoc) + * @see org.rapla.common.IRaplaLocale#getTimeZone() + */ + public TimeZone getTimeZone() { + return zone; + } + + /* (non-Javadoc) + * @see org.rapla.common.IRaplaLocale#getLocale() + */ + public Locale getLocale() { + return localeSelector.getLocale(); + } + + public String getCharsetNonUtf() + { + return charsetForHtml; + } + + +} + + diff --git a/Rapla/src/org/rapla/RaplaLogKitAdapater.java b/Rapla/src/org/rapla/RaplaLogKitAdapater.java new file mode 100644 index 0000000..7e617df --- /dev/null +++ b/Rapla/src/org/rapla/RaplaLogKitAdapater.java @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------* +| Copyright (C) 2006 Christopher Kohlhaas | +| | +| This program is free software; you can redistribute it and/or modify | +| it under the terms of the GNU General Public License as published by the | +| Free Software Foundation. A copy of the license has been included with | +| these distribution in the COPYING file, if not go to www.fsf.org | +| | +| As a special exception, you are granted the permissions to link this | +| program with every library, which license fulfills the Open Source | +| Definition as published by the Open Source Initiative (OSI). | +*--------------------------------------------------------------------------*/ +package org.rapla; + + +import org.apache.avalon.excalibur.logger.LogKitLoggerManager; +import org.apache.avalon.excalibur.logger.LoggerManager; +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.container.ContainerUtil; +import org.apache.avalon.framework.context.DefaultContext; +import org.apache.avalon.framework.logger.Logger; +import org.rapla.components.util.IOUtil; +import org.rapla.framework.RaplaException; +import org.rapla.framework.StartupEnvironment; +import org.rapla.framework.internal.LogManagerAdapter; + + +class RaplaLogKitAdapater implements LogManagerAdapter { + + LoggerManager m_loggerManager; + + public RaplaLogKitAdapater(StartupEnvironment env, Configuration loggerConfig) throws RaplaException { + try { + final String lmLoggerName = loggerConfig.getAttribute( "logger","system.logkit" ); + m_loggerManager= new LogKitLoggerManager( "rapla", lmLoggerName ); + DefaultContext context = new DefaultContext(); + context.put( "context-root",IOUtil.getFileFrom(env.getContextRootURL())); + ContainerUtil.enableLogging( m_loggerManager, env.getBootstrapLogger()); + ContainerUtil.contextualize( m_loggerManager, context); + ContainerUtil.configure( m_loggerManager, loggerConfig); + } catch (Exception ex) { + throw new RaplaException(ex); + } + + } + public Logger getLoggerForCategory(String categoryName) { + return m_loggerManager.getLoggerForCategory( categoryName ); + } + + public Logger getDefaultLogger() { + return m_loggerManager.getDefaultLogger(); + } + + +} diff --git a/Rapla/src/org/rapla/RaplaMainContainer.java b/Rapla/src/org/rapla/RaplaMainContainer.java new file mode 100644 index 0000000..4250861 --- /dev/null +++ b/Rapla/src/org/rapla/RaplaMainContainer.java @@ -0,0 +1,219 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.logger.Logger; +import org.rapla.components.util.IOUtil; +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.components.xmlbundle.LocaleSelector; +import org.rapla.components.xmlbundle.impl.I18nBundleImpl; +import org.rapla.entities.domain.AppointmentFormater; +import org.rapla.framework.PluginDescriptor; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaDefaultContext; +import org.rapla.framework.RaplaException; +import org.rapla.framework.RaplaLocale; +import org.rapla.framework.StartupEnvironment; +import org.rapla.framework.internal.ConfigTools; +import org.rapla.framework.internal.ContainerImpl; +import org.rapla.framework.internal.LogManagerAdapter; +import org.rapla.gui.CalendarOptions; +import org.rapla.gui.CalendarOptionsImpl; +import org.rapla.server.ShutdownService; +import org.rapla.server.internal.ShutdownServiceImpl; +/** +The Rapla Main Container class for the basic container for Rapla specific services and the rapla plugin architecture. +The rapla container has only one instance at runtime. Configuration of the RaplaMainContainer is done in the rapla*.xconf +files. Typical configurations of the MainContainer are + +
    +
  1. Standalone (rapla.xconf): A ClientService, one facade (with preconfigured auto admin login) and a storage (file).
  2. +
  3. Client (raplaclient.xconf ): A ClientService, one facade (without auto login) and a remote storage ( automaticaly pointing to the download server in webstart mode)
  4. +
  5. Server (raplaserver.xconf ): A ServerService (providin a facade) a messaging server for handling the connections with the clients, a storage (file or db) and an extra service for importing and exporting in the db
  6. +
  7. Embedded: Configuration example follows.
  8. +
+

+The Main Container provides the following Services to all RaplaComponents +

    +
  • I18nBundle
  • +
  • AppointmentFormater
  • +
  • RaplaLocale
  • +
  • LocaleSelector
  • +
  • RaplaMainContainer.PLUGIN_LIST (A list of all available plugins)
  • +
+

+ + @see I18nBundle + @see RaplaLocale + @see AppointmentFormater + @see LocaleSelector + */ +final public class RaplaMainContainer extends ContainerImpl +{ + public RaplaMainContainer() throws Exception { + this(new RaplaStartupEnvironment()); + } + + public RaplaMainContainer(RaplaStartupEnvironment env) throws Exception { + this( createRaplaContext(env,env.getLoggerConfig()),env ); + } + + public RaplaMainContainer(StartupEnvironment env) throws Exception { + this( createRaplaContext(env, null),env ); + } + + RaplaMainContainer( RaplaContext context, StartupEnvironment env) throws Exception{ + super( context, env.getStartupConfiguration() ); + addContainerProvidedComponentInstance( StartupEnvironment.ROLE, env); + addContainerProvidedComponentInstance( "download-server", env.getDownloadURL().getHost()); + addContainerProvidedComponentInstance( "download-url", env.getDownloadURL()); + if (env.getContextRootURL() != null) + { + addContainerProvidedComponentInstance( "context-root", IOUtil.getFileFrom( env.getContextRootURL())); + } + initialize(); + + } + + + private static RaplaContext createRaplaContext( StartupEnvironment env, Configuration loggerConfig ) throws RaplaException + { + Logger logger = env.getBootstrapLogger(); + LogManagerAdapter logManager = null; + if ( loggerConfig != null ) { + if ( checkForAvalonLogManager()) { + logManager = new RaplaLogKitAdapater( env, loggerConfig ); + // Replace bootstrap logger with new logkit logger + logger = logManager.getDefaultLogger(); + } else { + logger.warn("avalon logging jar not found. Using bootstrap logger."); + } + } + + RaplaDefaultContext context = new RaplaDefaultContext(); + if ( logManager != null) + { + context.put("logger-manager", logManager); + } + context.put( Logger.class.getName(), logger); + + return context; + } + + private static boolean checkForAvalonLogManager() + { + try { + Class.forName("org.apache.avalon.excalibur.logger.LogKitLoggerManager"); + return true; + } catch (Throwable ex ) { + return false; + } + } + + protected Map getComponentInfos() { + return new RaplaMetaConfigInfo(); + + } + + private void initialize() throws Exception { + addContainerProvidedComponent( ShutdownService.ROLE, ShutdownServiceImpl.class.getName()); + + CalendarOptions calendarOptions = new CalendarOptionsImpl(m_config.getChild("locale")); + addContainerProvidedComponentInstance( CalendarOptions.ROLE, calendarOptions ); + + RaplaLocaleImpl raplaLocale = new RaplaLocaleImpl(m_config.getChild("locale"), getLogger()); + // Startup mode= EMBEDDED = 0, CONSOLE = 1, WEBSTART = 2, APPLET = 3, SERVLET = 4 + getLogger().info("----------- Rapla startup mode = " + getStartupEnvironment().getStartupMode()); //BJO + getLogger().info("Default Locale= " + Locale.getDefault().toString()); //BJO + getLogger().info("Configured Locale= " + raplaLocale.getLocaleSelector().getLocale().toString()); //BJO + addContainerProvidedComponentInstance( RaplaLocale.ROLE,raplaLocale); + addContainerProvidedComponentInstance( LocaleSelector.ROLE,raplaLocale.getLocaleSelector()); + + Configuration[] defaultBundles = m_config.getChildren("default-bundle"); + for ( int i=0;i< defaultBundles.length;i++) { + String defaultBundleName = defaultBundles[i].getValue(); + addContainerProvidedComponent + ( + I18nBundle.ROLE + ,I18nBundleImpl.class.getName() + ,defaultBundleName + ,I18nBundleImpl.createConfig( defaultBundleName ) + ); + } + + addContainerProvidedComponent + ( + I18nBundle.ROLE + ,I18nBundleImpl.class.getName() + ,"org.rapla.RaplaResources" + ,I18nBundleImpl.createConfig( "org.rapla.RaplaResources" ) + ); + + addContainerProvidedComponentInstance( AppointmentFormater.ROLE, new AppointmentFormaterImpl( getContext())); + + // Override the intern Resource Bundle with user provided + for ( int i=0;i< defaultBundles.length;i++) { + String defaultBundleName = defaultBundles[i].getValue(); + I18nBundleImpl i18n = (I18nBundleImpl)getContext().lookup( I18nBundle.ROLE + "/" + defaultBundleName); + String parentId = i18n.getParentId(); + if ( parentId != null) { + addContainerProvidedComponentInstance(I18nBundle.ROLE,parentId, i18n); + } + } + + // Discover and register the plugins for Rapla + Enumeration pluginEnum = ConfigTools.class.getClassLoader().getResources("META-INF/rapla-plugin.list"); + List pluginList = new ArrayList( ); + while ( pluginEnum.hasMoreElements() ) { + BufferedReader reader = new BufferedReader(new InputStreamReader(((URL)pluginEnum.nextElement()).openStream())); + while ( true ) { + String plugin = reader.readLine(); + if ( plugin == null) + break; + pluginList.add((PluginDescriptor) instanciate(plugin, null, getLogger())); + } + } + addContainerProvidedComponentInstance( PluginDescriptor.PLUGIN_LIST, pluginList); + getLogger().info("Config=" + getStartupEnvironment().getConfigURL()); + getLogger().info("Config=" + getStartupEnvironment().getLoggerConfigURL()); + + I18nBundleImpl m_i18n = (I18nBundleImpl)getContext().lookup(I18nBundle.ROLE + "/org.rapla.RaplaResources"); + String version = m_i18n.getString( "rapla.version" ); + getLogger().info("Rapla.Version=" + version); + version = m_i18n.getString( "rapla.build" ); + getLogger().info("Rapla.Build=" + version); + try { + version = System.getProperty("java.version"); + getLogger().info("Java.Version=" + version); + } catch (SecurityException ex) { + version = "-"; + getLogger().warn("Permission to system property java.version is denied!"); + } + } + + public void dispose() { + getLogger().info("Shutting down rapla-container"); //BJO + super.dispose(); + } + } + diff --git a/Rapla/src/org/rapla/RaplaMetaConfigInfo.java b/Rapla/src/org/rapla/RaplaMetaConfigInfo.java new file mode 100644 index 0000000..77173dd --- /dev/null +++ b/Rapla/src/org/rapla/RaplaMetaConfigInfo.java @@ -0,0 +1,61 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla; + +import java.util.HashMap; + +import org.rapla.client.ClientService; +import org.rapla.client.internal.RaplaClientServiceImpl; +import org.rapla.components.mail.MailInterface; +import org.rapla.components.mail.SmtpClient; +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.components.xmlbundle.impl.I18nBundleImpl; +import org.rapla.facade.ClientFacade; +import org.rapla.facade.internal.FacadeImpl; +import org.rapla.framework.internal.ComponentInfo; +import org.rapla.plugin.jndi.JNDIAuthenticationStore; +import org.rapla.server.ServerService; +import org.rapla.server.ShutdownService; +import org.rapla.server.internal.ServerServiceImpl; +import org.rapla.server.internal.ShutdownServiceImpl; +import org.rapla.storage.CachableStorageOperator; +import org.rapla.storage.ImportExportManager; +import org.rapla.storage.StorageOperator; +import org.rapla.storage.dbfile.FileOperator; +import org.rapla.storage.dbrm.RemoteOperator; +import org.rapla.storage.dbsql.DBOperator; +import org.rapla.storage.impl.ImportExportManagerImpl; + + +class RaplaMetaConfigInfo extends HashMap { + private static final long serialVersionUID = 1L; + + public RaplaMetaConfigInfo() { + put( "rapla-client", new ComponentInfo(RaplaClientServiceImpl.class.getName(),ClientService.class.getName())); + put( "resource-bundle",new ComponentInfo(I18nBundleImpl.class.getName(),I18nBundle.class.getName())); + put( "facade",new ComponentInfo(FacadeImpl.class.getName(),ClientFacade.class.getName())); + put( "file-storage",new ComponentInfo(FileOperator.class.getName(),new String[] {StorageOperator.class.getName(), CachableStorageOperator.class.getName()})); + put( "remote-storage",new ComponentInfo(RemoteOperator.class.getName(),new String[] {StorageOperator.class.getName(), CachableStorageOperator.class.getName()})); + put( "db-storage",new ComponentInfo(DBOperator.class.getName(),new String[] {StorageOperator.class.getName(), CachableStorageOperator.class.getName()})); + put( "sql-storage",new ComponentInfo(DBOperator.class.getName(),new String[] {StorageOperator.class.getName(), CachableStorageOperator.class.getName()})); + + put( "importexport", new ComponentInfo(ImportExportManagerImpl.class.getName(),ImportExportManager.class.getName())); + put( "shutdown-service", new ComponentInfo(ShutdownServiceImpl.class.getName(),ShutdownService.class.getName())); + put( "jndi-authentication", new ComponentInfo(JNDIAuthenticationStore.class.getName())); + + put( "rapla-server", new ComponentInfo(ServerServiceImpl.class.getName(),ServerService.class.getName())); + put("smtp-service", new ComponentInfo( SmtpClient.class.getName(), MailInterface.ROLE )); + } +} + + diff --git a/Rapla/src/org/rapla/RaplaResources.xml b/Rapla/src/org/rapla/RaplaResources.xml new file mode 100644 index 0000000..43bea67 --- /dev/null +++ b/Rapla/src/org/rapla/RaplaResources.xml @@ -0,0 +1,3837 @@ + + + + + + + @doc.version@ + + + + @doc.buildtime@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Apply + Übernehmen + Valider + Aplicar + Použít + OK + Zastosuj + + + Go back + Zurück + Retour + Volver + Zpět + Sluit + Wstecz + + + Cancel + Abbrechen + Annuler + Cancelar + Zrušit + Annuleren + Anuluj + + + Continue + Weiter + Suivant + Continuar + Pokračovat + Verder + Kontynuuj + + + Error + Fehler + Erreur + Error + Chyba + Fout + Błąd + + + Exception + Ausnahme-Fehler + Exception + Excepción + Vyjímka + Uitzondering + Wyjątek + + + OK + OK + OK + OK + Budiž + OK + OK + + + Warning + Warnung + Attention + Atención + Varování! + Waarschuwing + Uwaga + + + + Abort + Abbrechen + Annuler + Anular + Ukončit + Annuleren + Anuluj + + + Add + Hinzufügen + Insérer + Añadir + Přidat + Toevoegen + Dodaj + + + Change + Ändern + Modifier + Modificar + Změnit + Wijzig + Zmień + + + Confirm + Bestätigen + Confirmer + Confirmar + Potvrdit + Bevestig + Akceptuj + + + Color + Farbe + Couleur + Color + Barva + Kleur + Kolor + + + Copy + Kopieren + Copier + Copiar + Kopie + Kopieer + Kopiuj + + + Copy to Clipboard + In die Zwischenablage + Copier vers le presse papier + Copiar al portapapeles + Kopírovat do schránky + Kopieër naar plakbord + Kopiuj do schowka + + + Close + Schließen + Fermer + Cerrar + Zavřít + Sluit + Zamknij + + + Create + Erstellen + Fermer + Crear + Vytvořit + Aanmaken + Utwórz + + + Delete + Löschen + Effacer + Borrar + Smazat + Verwijderen + Usuń + + + Delete Selection + Auswahl löschen + Effacer la sélection + Borrar la selección + Smazat výběr + Verwijder selectie + Usuń + + + Edit + Bearbeiten + Modifier + Editar + Upravit + Bewerk + Edytuj + + + Exit + Beenden + Quitter + Salir + Odejít + Sluiten + Wyjście + + + Publish + Veröffentlichen + Publier + Publicar + Publikuj + publikovat + Publiceer + + + Export + Export + Export + Exportar + Export + Publiceer + Eksportuj + + + Insert + Einfügen + Insérer + Insertar + Vložit + Toevoegen + Wstaw + + + Import + Import + Import + Importar + Import + Importeer + + + Keep + Beibehalten + Conserver + Conservar + Podržet + Bewaar + Zachowaj + + + Load + Laden + Charger + Cargar + Nahrát + Laad + Wczytaj + + + Login + Login + Login + Acceso + Login + Aanmelden + Logowanie + + + Logout + Logout + Quitter + Salir + Odhlásit + Afmelden + Wyloguj + + + Move + Verschieben + Déplacer + Mover + Přesunout + Verplaats + Przenieś + + + New + Neu + Nouveau + Nuevo + Nový + Nieuw + Nowy + + + Paste + Einfügen + Coller + Pegar + Vložit + Plakken + Wklej + + + Paste As + Einfügen als + Coller + Pegar como + Vložit vše + Plakken als + Wklej jako + + + Print + Drucken + Imprimer + Imprimir + Tisknout + Afdrukken + Drukuj + + + Print + Drucke + Imprimer + Imprimir + Tisknout + Afdrukken + Drukuj + + + Print to File + In Datei drucken + Imprimer en Fichier + Imprimir en Fichero + Tisk do souboru + Afdrukken naar Bestand + Drukuj do pliku + + + Refresh + Aktualisieren + Actualiser + Actualizar + Obnovit + Actualiseren + Odśwież + + + Remove + Entfernen + Effacer + Eliminar + Odstranit + Verwijderen + Usuń + + + Root + Wurzel + Racine + Raíz + root + Oorsprong + + + Save + Speichern + Sauvegarder + Guardar + Uložit + Opslaan + Zapisz + + + Show As + Anzeigen als + Afficher + Mostrar como + Ukaž vše + Type + Wyświetlaj jako + + + View + Anzeige + Affichage + Ver + Pohled + Overzicht + Widok + + + + All users + alle Benutzer + Tous les utilisateurs + Todos los usuarios + Všichni uživatelé + Alle gebruikers + Wszyscy + + + Alteration + Änderung + Modification + Modificación + Změna + Wijziging + Zmiana + + + and + und + et + y + a + en + i + + + Attribute + Attribut + Attribut + Atributo + Vlastnost + Kenmerk + Atrybut + + + Attributes + Attribute + Attributs + Atributos + Potvrdit + Kenmerken + Atrybuty + + + Available + vorhanden + Disponible + Disponible + Dostupný + Beschikbaar + Dostępne + + + Calendar + Kalender + Calendrier + Calendario + Kalendář + Kalender + Kalendarz + + + Categories + Kategorien + Catégories + Categorías + Seznam kategorií + Klassement + Kategorie + + + Category + Kategorie + Catégorie + Categoría + Kategorie + Klassement + Kategoria + + + Changes + Änderungen + Modifications + Modificaciones + Změny + Wijzigingen + Zmiany + + + Class + Klasse + Classe + Clase + Třída + Klasse + Klasa + + + Classification + Klassifizierung + Classification + Clasificación + Hodnocení + Klassificatie + Klasyfikacja + + + Connection with server + Verbindung mit dem Server + connexion au serveur + conexión a servidor + Verbinding met de Server + Połączenie z serwerem + + + Constraints + Einschränkungen + Contraintes + Restricciones + Omezení + Beperkingen + Ograniczenia + + + Created at + erstellt am + Créé le + Creado el + Vytvořeno + Aangemaakt op + Stworzono + + + Customized + benutzerdefiniert + Spécifique + Específico + Přizpůsobený + Aangepast door Gebruiker + Dostosowany + + + Date + Datum + Date + Fecha + Datum + Datum + Data + + + Default + Standard + Standard + Estándar + Výchozí + Standaard + Domyślne + + + Destination + Ziel + destination + Destino + Cíl + Naar + Miejsce docelowe + + + Show advanced settings + zeige Profi-Einstellungen + Affiche paramétrage pour utilisateurs expérimentés + Mostrar parámetros para usuarios experimentados + Zobraz pokročilé nastavení + Toon speciale instellingen + Pokaż zaawansowane ustawienia + + + Element key + Elementschlüssel + Clé d'élément + Clave de elemento + Klíč elementu + Elementsleutel + Klucz + + + Email + Email + Email + Email + e-mail + Email + E-mail + + + End time + Endzeit + Heure de fin + Hora final + Čas ukončení + tot + Czas zakończenia + + + End date + Ende + Fin + Fin + Konec + Stop datum + Data zakończenia + + + Filter + Filter + Filtre + Filtro + Filter + Filtr + + + Fixed Date + festes Datum + Date fixe + Fecha fija + Pevný datum + Vaste datum + Ustalona data + + + for + für + pour + para + Pro + voor + dla / na + + + Go to Date + Datum anzeigen + Atteindre la date + Ir a la fecha + Jít na datum + Ga naar datum + Idź do daty + + + Group + Gruppe + Groupe + Grupo + Skupina + Groep + Grupa + + + Groups + Gruppen + Groupes + Grupos + Skupiny + Groepen + Grupy + + + Help + Hilfe + Aide + Ayuda + Pomoc + Help + Pomoc + + + Hierarchy + Hierarchie + Hiérarchie + Jerarquía + Hierarchie + Hierarchie + Hierarchia + + + About Rapla... + Info + Information + Info + Informace + Informatie + O Rapla + + + Not visible + nicht sichtbar + non visible + no visible + neviditeně + onzichtbaar + Ukryty + + + Not visible.
Access to the reservation info is restricted.
+ Nicht sichtbar.
Der Zugriff auf die Reservierungsinformation ist eingeschränkt.
+ Non visible.
L'accès aux détails de la réservation est restreint.
+ No visible.
El acceso a la información de la reserva está restringido.
+ Skrytá položka.
Přístup k rezervačním informacím je zakázán.
+ Gewijgerd.
Toegang tot de boekings informatie is beperkt.
+ Ukryty.
Dostęp do informacji o rezerwacji jest zastrzeżony.
+
+ + Not currently selected.
Unselect "Only Own Reservations" or change your filter settings.
+ Nicht ausgewählt.
Schalten Sie "nur eigene Reservierungen" aus oder ändern Sie die Filtereinstellungen.
+ Actuellement non sélectionné.
Dé-sélectionnez "Seulement mes réservations" ou changez vos réglages du filtre.
+ Actualmente no seleccionado.
Desmarca "Sólo mis reservas" o cambia las configuraciones de los filtros.
+ Položka není v této chvíli vybrána.
Odstraňte volbu "Moje rezervace", nebo změňte nastavení vašeho filtru.
+ ""Enkel eigen reservaties" afvinken" of wijzig de filter instellingen.". + Nie wybrano.
Odznacz opcję "Pokaż tylko moje rezerwacje" lub zmień ustawienia filtru.
+
+ + Key + Schlüssel + Clé + Clave + Klíč + Sleutel + Klucz + + + Language + Sprache + Langue + Idioma + Jazyk + Taal + Język + + + Last changed + zuletzt geändert am + Dernière modification le + Última modificación el + Naposledy změněno + Gewijzigd op + Ostatnia zmiana + + + Last changed by + zuletzt geändert von + Dernière modification + Última modificación + Naposledy změnil uživatel: + Laatst gewijzigd door + Zmienione przez + + + Legend + Legende + Légende + Leyenda + Legenda + Legende + Legenda + + + Level + Level + Niveau + Nivel + Úroveň + Niveau + Poziom + + + Nothing selected + Nichts ausgewählt + Rien de sélectionné + Nada seleccionado + Není vybrána žádná položka + Geen selectie + Nic nie wybrano + + + Name + Name + Nom + Nombre + Jméno + Naam + Nazwa + + + Never + nie + Jamais + Nunca + Nikdy + nooit + Nigdy + + + Every time + immer + Toujours + Siempre + pokaždé + onbeperkt + Zawsze + + + No + Nein + Non + No + Ne + Nee + Nie + + + No classification + keine Klassifizierung + Pas de classification + Sin clasificación + Neklasifikováno + Geen Classificatie + Brak klasyfikacji + + + Options + Einstellungen + Options + Opciones + Volby + Opties + Opcje + + + or + oder + ou + o + nebo + of + lub + + + Open + offen + Ouvrir + Abrir + Otevřít + Onbepaald + Otwórz + + + Permissions + Zugriffsrechte + Permissions + Permisos + Přístupová práva + Toegang + Uprawnienia + + + Preferences + Einstellungen + préférence + Preferencias + Předvolby + Voorkeur + Preferencje + + + Question + Frage + Question + Pregunta + Otázka + Vraag + Pytanie + + + Report + Report + Rapport + Informe + Report + Rapport + Raport + + + Calendar settings + Kalendareinstellungen + L'Affichage + la configuración del calendario + nastavení pohledu + Overzicht opslaan + ustawienia Kalendarz + + + Search + Suche + Rechercher + Buscar + Hledat + Opzoeken + Szukaj + + + Select + auswählen + Sélectionner + Seleccionar + Vybrat + Selecteer + Zaznacz + + + Selected + ausgewählt + Sélectionné + Seleccionado + Vybráno + Actief + Wybrane + + + Selection + Auswahl + Sélection + Selección + Výběr + Selecteer voorkeur + Zaznaczenie + + + Source + Quelle + source + Fuente + Zdroj + Van + Źródło + + + Start time + Startzeit + Heure de début + Hora de inicio + Počáteční čas + van + Czas rozpoczęcia + + + Start + Beginn + Début + Inicio + Počáteční datum + Start datum + Data rozpoczęcia + + + Table + Tabelle + Table + Tabla + tabule + Tabel + Tabela + + + Today + Heute + Aujourd'hui + Hoy + Dnes + Vandaag + Dziś + + + + at + um + à + a las + v + van + o + + + until + bis + jusqu'à + hasta + do + tot + do + + + Total occurances + Gesamtanzahl Termine + Nombre d'occurrences + Número de apariciones + celkem záznamů + Totaal aantal + Suma wystąpień + + + Translation + Übersetzung + Traduction + Traducción + překlad + Vertaling + Tłumaczenie + + + Width + Breite + Largeur + Ancho + šířka + Breedte + Szerokość + + + Yes + Ja + Oui + + ano + Ja + Tak + + + x Days in advance + x Tage im Voraus + x jours en avance + x días de antelación + x dní předem + x dagen op voorhand + x dni wcześniej + + + Server + Server + Serveur + Servidor + Server + Server + Serwer + + + File + Datei + Fichier + Fichero + Soubor + Bestand + Plik + + + with + mit + avec + con + s + met + z + + + + + WARNING: Conflicts found! + WARNUNG: Konflikte gefunden! + ATTENTION : Conflits trouvés ! + ¡ATENCIÓN : Se han encontrado conflictos! + POZOR: Nalezen konflikt ! + WAARSCHUWING: Conflict gevonden! + UWAGA: Wykryto konflikt! + + + WARNING: The same appointment exists multiple times [{0}]! + WARNUNG: Den gleichen Termin gibt es mehrmals [{0}]! + ATTENTION : La même réservation existe plusieurs fois [{0}]! + ¡ATENCIÓN : La misma reserva existe en múltiples ocasiones [{0}]! + POZOR: Stejné ujednání existuje ve více časech [{0}]! + WAARSCHUWING: Dezelfde afspraak bestaat meerdere keren: [{0}]! + UWAGA: Ten sam termin występuje kilkakrotnie [{0}]! + + + These settings should only be changed by experienced users! + Diese Einstellungen sollten nur von erfahrenen Benutzern verändert werden! + Ce paramétrage est réservé aux utilisateurs expérimentés ! + ¡Estos parámetros están reservados sólo para usuarios experimentados! + Tato nastavení by měla být měněna zkušenými uživateli! + Deze instelling kan enkel gewijzigd worden door een super gebruiker! + Nie kombinuj, chyba, że wiesz, co robisz ;) + + + You have no permissions to reserve/change [{0}] an appointment [{1}]! + Sie haben keine Zugriffsrechte um [{0}] an dem Termin [{1}] zu reservieren/ändern! + Vous ne disposez pas des droits suffisants pour effectuer/modifier [{0}] la réservation [{1}] + ¡No tiene permisos suficientes para efectuar/modificar [{0}] la reserva [{1}]! + Nemáte pověření rezervovat/měnit [{0}] v ujednání vaci [{1}]! + Plannen of wijzigen van [{0}] in reservatie [{1}] is niet toegestaan! + Nie masz uprawnień do zmiany/ustalenia [{0}] terminu [{1}]! + + + You have no permissions to create conflicts for [{0}]! + Sie haben keine Zugriffsrechte um Konflikte für [{0}] zu erzeugen! + Vous ne disposez pas des droits suffisants pour créer des conflits pour [{0}] ! + ¡No tiene permisos suficientes para crear conflictos para [{0}]! + Nemáte pověření vytvořit konflikt pro [{0}] ! + Aanmaken van conflicten voor [{0}], is niet toegestaan! + Nie masz uprawnień do tworzenia konfliktów w [{0}]! + + + + Period is shorter than 1 week! + Der Zeitraum is kürzer als eine Woche! + La période est inférieure à une semaine ! + ¡El periodo es inferior a una semana! + Nemáte pověření rezervovat/měnit [{0}] v ujednání vaci [{1}]! + Periode minder dan 1 week! + Okres krótszy niż tydzień. + + + + Data is only modified in memory. Changes will be lost for future sessions. + Ihre Änderungen wurden nicht gesichert und gehen beim Beenden der Sitzung verloren! + Modifié uniquement en mémoire. La modification sera perdue pour les sessions suivantes ! + ¡Los datos se han modificado sólo en memoria. Las modificaciones se perderán para las sesiones siguientes! + Data jsou změněny pouze v paměti. Změny budou ztraceny pro další použití. + Wijzigingen enkel in het geheugen. Gegevens worden niet opgeslagen. + Dane zmienione tylko w pamięci. Zmiany zostaną utracone. + + + + The event you are editing has been changed. + The data displayed in this window will be refreshed. + Die Veranstaltung, die gerade bearbeitet + wird, wurde geändert. Das Bearbeitungsfenster wird aktualisiert. + L'évènement que vous éditez a été modifié. + Les informations affichées dans la fenêtre vont être rafraichies. + El evento que está editando ha sido modificado. + La información que aparece en esta ventana será actualizada. + Editovaná položka bude změněna. Zobrazené informace budou obnoveny. + Het evenement dat je wil wijzigen is gewijzigd. + De gegevens worden terug opgehaald. + Edytowane wydarzenie zostało zmienione. + Dane w tym oknie zostaną zaktualizowane. + + + + The event you are editing has been deleted. + Die Veranstaltung die gerade bearbeitet + wird, wurde gelöscht. + L'évènement que vous éditez vient d'être effacé + El evento que está editando ha sido eliminado + Editovaná položka bude smazána. + Het evenement dat je bewerkt, werd verwijderd. + Edytowane wydarzenie zostało usunięte. + + + + You have not selected any resources/persons! + Sie haben keine Ressourcen/Personen ausgewählt! + Vous n'avez pas sélectionné de ressource/personne ! + ¡No ha seleccionado ningún(a) recurso/persona + Nejsou označeny žádné prostředky/osoby! + Evenement zonder Personen & Materialen! + Nie wybrano żadnych zasobów/osób! + + + + The object "{0}" you are editing has been changed outside the window. + The current editing will be aborted. + Das Objekt "{0}", das gerade bearbeitet + wird, wurde geändert. Der aktuelle Editiervorgang wird abgebrochen. + + L''objet "{0}" que vous éditez a été modifié hors de la fenêtre. + Cette édition va être interrompue. + El objeto "{0}" que está editando ha sido modificado fuera de la ventana + La edición actual va a ser interrumpida. + Objekt "{0}" který je editován bude změněn mimo okno. Současná editace bude přerušena. + Het object "{0}" dat je bewerkt, werd gewijzigd. Bewerken wordt onderbroken. + + + + + Login failed! + Fehlerhafter Login! + Erreur dans le Login ! + ¡Error al acceder al sistema! + Špatný login! + Aanmelding geweigerd! + Błąd logowania + + + Connection to [{0}] failed! Maybe the host is down and you could try later! + Verbindung mit [{0}] fehlgeschlagen! Vielleicht ist der Rechner nicht erreichbar. Versuchen Sie es zu einen späteren Zeitpunkt erneut! + La connexion vers [{0}] a échoué ! Le serveur n''est peut être pas disponible. Essayez plus tard. + ¡La conexión a [{0}] ha fallado! El servidor puede no estar disponible. Inténtelo más tarde. + Připojení k [{0}] je špatné! Možná je vypnutý server. Zkuste to později. + Verbinding met [{0}] is verbroken, de server is niet beschikbaar, contacteer de Rapla beheerder of probeer later! + Nie udało się połączyć z {0}! Być może host jest wyłączony. Spróbuj ponownie później. + + + No or closed connection to [{0}]. Please restart Rapla. + Keine oder unterbrochene Verbindung zu [{0}]. Bitte starten Sie Rapla erneut. + Pas ou plus de connexion vers [{0}]. Redémarrer Rapla. + Sin conexión a [{0}]. Por favor, reinicie Rapla. + Neexistuje nebo nefunguje spojení [{0}]. Prosím restartujte rezervační systém + Geen of een gesloten connectie naar [{0}]. Rapla herstarten a.u.b. + Brak lub utracone połączenie z {0}. Uruchom ponownie Rapla. + + + + At least one type is requiered. + Es muss mindestens ein Typ vorhanden sein! + Au moins un type doit être disponible ! + ¡Al menos un tipo debe estar disponible! + Je požadován alespoň jeden typ. + Minimaal 1 soort is nodig. + Przynajmniej jeden typ jest wymagany. + + + + You have to enter a title for your event! + Sie müssen einen Titel für Ihre Veranstaltung angeben! + Vous devez donner un titre à cet évènement ! + ¡Debe introducir un título para su evento! + Vložit nadpis události! + Een titel opgeven! + Podaj nazwę dla wydarzenia! + + + + You need to provide a name! + Sie müssen einen Namen angeben! + Vous devez donner un nom ! + ¡Debe dar un nombre! + Nutné poskytnout jméno + Je moet een naam opgeven! + Musisz wpisać nazwę! + + + You need to provide an entry for: {0} + Sie müssen einen Namen angeben für: {0} + Vous devez donner un nom pour : {0} + Debe dar un nombre para : {0} + Nutné poskytnout přístup pro: + Je moet de gegevens opgeven voor: {0} + Musisz coś wpisać w: {0} + + + + You need to provide a key: {0} + Sie müssen einen Schlüssel angeben: {0} + Vous devez donner une clé pour : {0} + Debe dar una clave para : {0} + Nutné poskytnout klíč: + Een sleutel is verplicht: {0} + Musisz podać klucz: {0} + + + "{0}" is not a valid key! + Note: You can only use a combination of max. 50 letters, digits or one of the + following characters {1}. The key must start with {2} or a letter! Example: room_size + + "{0}" ist kein gültiger Schlüssel! + Note: Sie können nur eine Kombination von max. 50 Buchstaben, Ziffern oder einer der + Folgende Zeichen {1}. Der Schlüssel muss mit {2} oder einem Buchstaben beginnen! Beispiel: Anzahl_Plätze + + "{0}" n''est pas une clé valide! + Note: Vous ne pouvez utiliser une combinaison de max. 50 lettres, chiffres ou l'une des + caractères suivants {1}. La clé doit commencer par {2} ou une lettre! Exemple: Nombre_de_places + + ¡"{0}" no es una clave válida! + Nota : Sólo se puede utilizar una combinación de max. 50 letras, dígitos o uno de los + siguientes caracteres {1}. La clave debe comenzar con {2} o una carta! Ejemplo: Número_de_plazas + + "{0}" je neplatný klíč!
+ Note: Můžete použít pouze v kombinaci s Max. 50 písmen, číslice nebo jeden z + tyto znaky {1}. Klíč musí začínat {2} nebo dopis! Např.: mistnost_velikost +
+ "{0}" is een ongeldige sleutel! + Nota: Maximaal 50 letters, cijfers of {1} beginnend met {2} of een letter! + + "{0}" jest nieprawidłowym kluczem! + Możesz użyć tylko liter, cyfr oraz jednego z tych znaków {0}. Klucz musi zaczynać się od litery. + +
+ + An event has to consist of at least one appointment! + Eine Veranstaltung muss aus mindestens einem Termin bestehen! + Un évènement est constitué d'au moins une réservation ! + ¡Un evento ha de tener al menos una cita! + Evenement zonder reservaties! + Wydarzenie musi mieć choć jeden termin. + + + Dependencies exist for these objects + Für folgende Objekte bestehen noch Abhängigkeiten + Des dépendences existent pour ces objets + Existen dependencias para estos objetos + Závislosti existují pro jiný objekt + Afhankelijk van + Istnieją zależności dla tego obiektu + + + The name "{0}" is already + taken. Please choose a different name! + Der Name "{0}" ist schon + vergeben. Bitte wählen Sie einen Anderen! + Le nom "{0}" est déjà existant. Choisissez en un autre ! + ¡El nombre "{0}" ya está en uso. Por favor, escoja un nombre diferente! + Jméno "{0}" je využíváno. Vyber jiné! + De naam "{0}" is reeds in gebruik. Kies een andere naam! + Nazwa "{0}" jest już zajęta. Wybierz inną. + + + The object {0} couldn''t be modified. It was recently modified by someone else. + There was a newer version in the storage. + Das Objekt {0} konnte nicht verändert werden. Es wurde kürzlich verändert, denn + es gibt schon eine neuere Version. + L''objet {0} n''a pu être modifié. Il était en cours de modification par quelqu''un d''autre. + Une autre version existe. + El objeto {0} no ha podido ser modificado. Ha sido modificado recientemente por otra persona. + Existe en memoria una versión más reciente del mismo. + Objekt {0} nemohl být upraven. Byl upraven nedávno někým jiým. + {0} versie {1} is recentelijk gewijzigd, versie {2} is beschikbaar. Probeer later. + Nie można zmodyfikować {0}. Obiekt został niedawno zmieniony przez inną osobę. Istnieje nowsza wersja obiektu w pamięci. + + + The passwords don't match! + Die Passwörter stimmen nicht überein! + Les mots de passe ne sont pas concordants ! + ¡Las palabras de paso no coinciden! + Heslo neodpovídá + De paswoorden verschillen! + Hasła nie zgadzają się. + + + Wrong password! + Falsches Passwort! + Mauvais mot de passe ! + ¡Palabra de paso incorrecta! + Špatné heslo! + Verkeerd paswoord! + Błędne hasło! + + + You can''t delete [{0}], because you are currently using this account! + Sie können [{0}] nicht löschen, weil Sie diesen Account benutzen! + Vous ne pouvez effacer [{0}], parce que vous utilisez actuellement ce compte ! + ¡No puede borrar [{0}], porque está usando esa cuenta en estos momentos! + Nemůžete smazat [{0}], protože výběr využívá tento účet! + Je kan [{0}] niet verwijderen,omdat je deze momenteel in gebruik hebt! + Nie możesz usunąć [{0}], ponieważ obecnie używasz tego konta! + + + Database update failed. All changes were canceled. + Während der Speicherung in der Datenbank + ist ein Fehler aufgetreten. Alle Änderungen wurden rückgängig + gemacht! + L'actualisation de la base de données a échoué. + Toutes les modifications sont perdues + La actualización de la base de datos ha fallado. + Se han cancelado todas las modificaciones + Databáze se neobnovila. Všechny změny zrušeny. + Databank wijziging. Wijzigingen werden geannuleerd. + Błąd aktualizacji bazy. Wszystkie zmiany zostały utracone. + + + Error! Database update failed. Because + your database doesn't support transactions, that could lead to + serious failures. Please contact your administrator + immediately! + Achtung! Während der Speicherung in der + Datenbank ist ein Fehler aufgetreten. Da die verwendete + Datenbank keine Transaktionen unterstützt kann dies zu + schwerwiegenden Fehlern in der Applikation fhren. Bitte setzten + Sie sich umgehend mit dem zuständigen Adminstrator in Verbindung! + + Attention ! Une erreur est survenue pendant l'actualisation de la base de données. Votre base de données refuse cette transaction. Prévenez immédiatement votre administrateur ! + ¡Atención! La actualización de la base de datos ha fallado. Puesto + que su base de datos no soporta transacciones, esto puedo ocasionar errores serios. Por favor, contacte + con su administrador inmediatamente! + Chyba! Databáze se neobnovila. Protože databáze nepodporuje transakci, chyba by se mohla opakovat, informujte administrátora + Fout! Databank wijziging. Databank ondersteund geen transacties. + Dit kan tot serieuze fouten leiden.Neem onmiddelijk contact met de Rapla beheerder! + Błąd! Aktualizacja bazy nie powiodła się. Twoja baza nie obsługuje transakcji, co może prowadzić do poważnych błędów. Skontaktuj się ze swoim administratorem. + + + Error! Referenced object [{0}] not found in store. It was probably recently removed. + Achtung! Referenziertes Object [{0}] konnte nicht gefunden werden. Es wurde wahrscheinlich kürzlich von jemand gelöscht. + Attention! L''objet référencé [{0}] n''a pu être trouvé. A probablement été effacé récemment. + ¡Atención! No se pudo encontrar el objeto referenciado [{0}]. Probablemente se eliminó recientemente. + Chyba! Zmíněný objekt [{0}] se nenalézá v databázi. Pravděpodobně byl odstraněn. + Fout! Gevraagd object [{0}] niet gevonden. Waarschijnlijk recentelijk verwijderd. + Nie można znaleźć obiektu [{0}]. Prawdopodobnie został niedawno usunięty. + + + + Choose root category: + Wurzelkategorie auswählen + Sélectionner la catégorie racine + Seleccionar la categoría raíz + Vyber hlavní kategorii: + Wybierz kategorię główną (root): + + + Type + Typ + Type + Tipo + Typ + Folder + Typ + + + Type name + Name des Typs + Nom du type + Nombre de tipo + Jméno typu + Naam type + Nazwa typu + + + Enclose the key names in {} to insert attributes, e.g. {myKey} + Umklammern sie die Schlüsselnamen mit {} um Attribute einzufügen, Bsp. {name} + Mettez la clé entre {} pour insérer des attributs, par ex. {maclé} + Meta la clave entre {} para insertar los atributos, por ejemplo {miClave} + Uzavřené klíčové jméno v {} k vložení vlastnosti, např. {jméno} + Sleutels tussen {...} plaatsen; bvb. {mijnSleutel} + Nazwy klucza powinny znajdować się w {} np. {klucz}. + + + Displayed name format + Anzeigeformatierung + Mettre en forme l'affichage + Formato de nombre visualizado + Zobrazovaný formát jména + Titel formaat + Format wyświetlania + + + Type + Typ + Type + Tipo + Typ + Type + Typ + + + Types + Typen + Types + Tipos + Typy + Types + Typy + + + + Yes|No + Ja|Nein + Oui|Non + Sí|No + Ano|Ne + Ja|Neen + Tak|Nie + + + Category + Kategorie + Catégorie + Categoría + Kategorie + Klassement + Kategoria + + + Date + Datum + Date + Fecha + Datum + Datum + Data + + + Integer + Ganze Zahl + Nombre entier + Número entero + Číslo + Getal + Liczba + + + Text + Text + Texte + Texto + Text + Tekst + Tekst + + + Edit View + Bearbeitungsansicht + Écran de modification + Vista de modificación + Upravit pohled + Toon als + Widok edycji + + + Main View + Hauptansicht + Écran principal + Vista principal + Hlavní pohled + Basis gegevens + Widok główny + + + Additional Information + zusätzliche Informationen + Informations complémentaires + Información complementaria + Doplňující informace + Extra gegevens + Dodatkowe informacje + + + invisible + nicht sichtbar + invisible + Invisible + Skrytý + Onzichtbaar + niewidoczny + + + + Expected rows + Zeilenanzahl + Nombre de lignes + Número de líneas + Očekávaná řadka + Aantal rijen + Liczba wierszy + + + + Expected columns + Spaltenbreite + Nombre de colonnes + Número de columnas + Aantal kolommen. + Liczba kolumn + + + + + User + Benutzer + Utilisateur + Usuario + Uživatel + Gebruiker + Użytkownik + + + Users + Benutzer + Utilisateurs + Usuarios + Uživatelé + Gebruikers + Użytkownicy + + + Switch to + wechsle zu + Changer pour + Cambiar a + Přepni k + Aanmelden als + Przełącz na + + + switch back + wechsle zurück + revenir vers + Volver a ser administrador + Přepni zpět + Aanmelden als vorige gebruiker + Wróć + + + Username + Benutzername + Profil utilisateur + Nombre de usuario + Uživatelské jméno + Gebruiker + Nazwa użytkownika + + + bind with person + Mit Person verbinden + connecter personne + connectar con persona + Verbinding met persoon + + + Administrator + Administrator + Administrateur + Administrador + Administrátor + Beheerder + Administrator + + + You have adminstrator privileges! + Sie haben Administrationsrechte! + Vous disposez des droits d'administrateur ! + ¡Dispone de derechos de administrador! + Máte administrátorská práva! + Beheerder + Masz uprawnienia administratora. + + + Administration + Administration + Administration + Administración + Administrace + Systeem beheer + Administracja + + + Password + Passwort + Mot de passe + Palabra de paso + Heslo + Paswoord + Hasło + + + Old password + Altes Passwort + Ancien mot de passe + Antigüa palabra de paso + Staré heslo + Oud paswoord + Stare hasło + + + New password + Neues Passwort + Nouveau mot de passe + Nueva palabra de paso + Nové heslo + Nieuw paswoord + Nowe hasło + + + Password verification + Passwort Verifikation + Vérification du mot de passe + Verificación de palabra de paso + Heslo ověřeno + Paswoord controle + Weryfikacja hasła + + + + Person + Person + Personne + Persona + Osoba + Persoon + Osoba + + + Persons + Personen + Personnes + Personas + Osoby + Personen + Osoby + + + Person type + Personentyp + Type de personne + Tipo de persona + Druh osoby + Type: Persoon + Typ osoby + + + Person Types + Personentypen + Types de personne + Tipos de personas + Druh osob + Type: Personen + Typy osób + + + Name + Nachname + Nom + Apellidos + Jméno + Naam + Nazwisko + + + + First name + Vorname + Prénom + Nombre + První jméno + Voornaam + Imię + + + + + Event + Veranstaltung + Évènement + Evento + Událost + Evenement + Wydarzenie + + + New Event... + neue Veranstaltung + nouvel évènement + nuevo evento + Nová událost + Nieuw evenement + Nowe wydarzenie + + + + Event name + Veranstaltungsname + Nom de l'évènement + Nombre del evento + Název události + Evenement naam + Nazwa wydarzenia + + + + Events + Veranstaltungen + Évènements + Eventos + Události + Evenementen + Wydarzenia + + + Event Type: + Veranstaltungstyp + Type d'évènement + Tipo de evento + Druh události + Type Evenement + Wydarzenia + + + Event + Veranstaltung + Évènement + Evento + Událost + Evenement + Wydarzenie + + + Edit own + eigene bearbeiten + modifier les siennes + modificar las propias + Změň majitele + bewerk eigen + Edytuj swoje + + + Appointments: + Termine: + Évènement : + Citas: + Sjednaný: + Lijst afspraken: + Terminy: + + + Allocations: + Belegungen: + Allocation : + Asignaciones : + Rozvržení: + Bezetting: + Przydział zasobów: + + + Reserved by + reserviert von + réservé par + Reservado por + Rezervace od + Geregistreerd door + Rezerwacja przez + + + on + am + à la + en el + na + aan de + na + + + Registered by + Eingetragen von + Enregistré par + Registrado por + Registrovaný od + Geregistreerd door + Zarejestrowany przez + + + + Day of week + Wochentag + Jour de la semaine + Día de la semana + Den v týdnu + Weekdag + Dzień tygodnia + + + Duration + Zeitdauer + Durée + Duración + Trvání + Tijdsduur + Czas trwania + + + Interval + Zeitraum + Interval + Intervalo + Interval + Interval + Interwał + + + Same day + am selben Tag + même jour + mismo día + Stejný den + zelfde dag + Ten sam dzień + + + Next day + am nächsten Tag + jour suivant + día siguiente + Další den + volgende dag + Następny dzień + + + on day x + am Tag x + le jour x + el día x + Za den x + op dag x + dnia x + + + Day(s) + Tag(e) + Jour(s) + Día(s) + Den + dag(en) + Dzień(dni) + + + Hour(s) + Std. + Heure(s) + Hora(s) + Hodina + uur + Godzina(-y) + + + Min. + Min. + min. + min. + Min. + min. + min. + + + New appointment + neuer Termin + Nouvelle réservation + Nueva cita + Nová rezervace + Nieuwe afspraak + Nowy termin + + + Appointment + Termin + Réservation + Reserva + Rezervace + Reservatie + Termin + + + Appointment List + Terminliste + Liste des réservations + Lista de citas + Seznam rezervací + Lijst reservaties + Lista terminów + + + Appointments + Termine + Réservations + Citas + Rezervace + Reservaties + Terminy + + + Change appointment + Termin ändern + Modifier la réservation + Modificar la reserva + Změna rezervace + Wijzig afspraak + Edytuj termin + + + No repeating + Einzeltermin + Unique + Única + Bez opakování + Eénmalig + Jednorazowe + + + Repeating + Wiederholung + Répéter + Repetir + Opakování + Herhaling: + Powtarzające się: + + + Weekly + wöchentlich + Hebdomadaire + Semanal + Týdně + Wekelijks + Co tydzień + + + Daily + täglich + Quotidien + A diario + Po dnech + Dagelijks + Codziennie + + + Monthly + monatlich + mensuel + mensual + Maandelijks + Co miesiąc + + + Yearly + jährlich + annuel + anual + Jaarlijks + Co rok + + + Every {0} {1} + Alle {0} {1} + Tous/toutes les {0} {1} + Cada {0} {1} + Každý {0} {1} + Elke {0} {1} + Co {0} {1} + + + Week + Woche + Semaines + Semana + Týden + Week + Tydzień + + + cw {0,date,w} + KW {0,date,w} + S{0,date,w} + se {0,date,w} + T{0,date,w} + WK{0,date,w} + T{0,date,w} + + + + Short week + kompakte Woche + Semaine courte + Semana corta + Celý týden + Week (Compact) + Tydzień (skrócony) + + + Hour + Std. + Heure + Hora + Uur + Godzina + + + Hours + Std. + Heures + Horas + Uren + Godziny + + + + Minutes + Minute + Minutes + Horas + Minuten + + + + Minute + Minute + Minute + Hora + Minuut + + + Second + Sekunde + Seconde + secundo + Seconde + Sekunda + + + Seconds + Sekunden + Secondes + secundos + Seconden + Sekundy + + + Day + Tag + Jour + Día + Den + Dag + Dzień + + + Days + Tage + Jours + días + Dny + Dagen + Dni + + + Weeks + Wochen + Semaines + semanas + Týdny + Weken + Tygodnie + + + Month + Monat + Mois + Mes + Měsíc + Maand + Miesiąc + + + Years + Jare + Année + .. + .. + Jaren + Lata + + + Year + Jaar + Année + .. + .. + Jaar + Rok + + + Months + Monate + Mois + Meses + Měsíce + Maanden + Miesiące + + + Repeats forever + Kein Ende + Indéfini + indefinido + Nemá konec + tot oneindig + Bez końca + + + from the + ab dem + à partir de + a partir de + z + van + od + + + until + bis zum + jusqu'à + hasta + Dokud + tot en met + do + + + x times + x Mal + x fois + x veces + x krát + x-Maal + x razy + + + Repeat + wiederhole + répète + repite + Opakuj + herhaal + Powtórz + + + every + Jeden + Chaque + Cada + Pokaždé + Elke + Każdy + + + . + . + . + . + . + -(ste/de) + . + + + Repeat {0} times + wiederhole {0} Mal + répète {0} fois + repite {0} veces + Opakuj {0} krát + {0}X + Powtórz {0} razy + + + from {0} + ab dem {0} + à partir du {0} + a partir de {0} + Z {0} + van {0} + od {0} + + + until {0} + bis zum {0} + jusqu`à {0} + hasta {0} + Dokud {0} + tot en met {0} + do {0} + + + {0} Exceptions: + {0} Exceptions: + {0} Exceptions : + {0} Excepciones : + {0} Vyjímka: + {0} Uitgezonderd: + {0} Wyjątki: + + + Rule: + Regel: + Règle : + Regla : + Pravidlo: + Regel: + Reguła: + + + Exceptions + Ausnahmen + Exceptions + Excepciones + Vyjímka + Uitgezonderd + Wyjątki + + + No exceptions: + Keine Ausnahmen: + Sans exception + Sin excepciones + Bez vyjímky + Geen uitzonderingen: + Bez wyjątków: + + + Exceptions: + Ausnahmen: + Exceptions : + Excepciones : + Vyjímka: + Uitgezonderd: + Wyjątki: + + + Exception days: + Ausgenommene Tage: + Jours d'exceptions + Días de excepciones + Vyjímečné dny: + Uitgezonderd: + Wyłączone dni: + + + Convert to single events + In Einzeltermine umwandeln + Convertir des événements uniques + Convertir a los eventos individuales + Prevést na jednorázové akce + Converteren naar éénmalige evenementen + Przekształć w wydarzenia jednorazowe + + + Do you want to delete the series or just the appointment on {0}? + Wollen Sie die ganze Serie oder nur den Termin am {0} löschen? + Voulez-vous effacer toute la série de réservations ou seulement celle-ci {0}? + ¿Desea eliminar toda la serie o sólo la reserva del {0}? + Chcete smazat celou sérii nebo pouze vybraný termín {0}? + De volledige reeks of éénmalig de afspraak op {0} verwijderen? + Usunąć całą serię, czy tylko wybrany termin {0}? + + + Do you want to copy the series or just the appointment on {0}? + Wollen Sie die ganze Serie oder nur den Termin am {0} kopieren? + Voulez-vous copier toute la série de réservations ou seulement celle-ci {0}? + ¿Desea copiar toda la serie o sólo la reserva del {0}? + Chcete kopírovat celou sérii nebo pouze jeden termín {0}? + Wil je de reeks of enkel de boeking op {0} kopieren? + Skopiować całą serię, czy tylko wybrany termin {0}? + + + Do you want to move the series or just the appointment on {0}? + Wollen Sie die ganze Serie oder nur den Termin am {0} verschieben? + Voulez-vous déplacer toute la série de réservations ou seulement celle-ci {0}? + ¿Desea desplazar toda la seria o sólo la reserva del {0}? + Chcete přesunout řadu nebo pouze termín {0}? + De volledige reeks of éénmalig de afspraak op {0} verplaatsen? + Przenieść całą serię, czy tylko wybrany termin {0}? + + + Series + Serie + Série + Serie + Série + reeks + Serie + + + Single appointment + einzelner Termin + réservation unique + Reserva única + Jeden termín + éénmalig + Jeden termin + + + Resource + Ressource + Ressource + Recurso + Prostředek + Middel + Zasób + + + Resources + Ressourcen + Ressources + Recursos + Prostředky + Middelen + Zasoby + + + Resource type + Ressourcentyp + Type de ressources + Tipo de recurso + Typ prostředku + Type: Middel + Rodzaj zasobu + + + Resource types + Ressourcentypen + Types de ressources + Tipos de recursos + Typy prostředků + Middel type + Rodzaje zasobów + + + Resource selection + Ressourcenauswahl + Sélection des ressources + Selección de recursos + Označené prostředky + Keuze middel + Zaznaczenie zasobu + + + + Change allocation + Belegung ändern + Modifier l"allocation + Modificar la asignación + Vyměněný rozvržení + Wijzig toewijzing + Zmień przydział + + + Resources and persons + Ressourcen und Personen + Ressources et Personnes + Recursos y personas + Prostředky a osoby + Middelen&Personen + Zasoby i osoby + + + Hold back conflicts + Unterdrücke Konflikte + Ne pas tenir compte des conflits + No tener en cuenta los conflictos + Návrat před konflikt + Conflicten onderdrukken + Ignoruj konflikty + + + Selectable on + auswählbar an + Possible le + Seleccionable en + Označitelný v + Voor: + Dostępne w + + + Selectable + auswählbar + Sélectionnable le + Seleccionable + Označitelný + Beschikbaarheid + Dostępne + + + Selected on + ausgewählt an + Sélectionné le + Seleccionado en + Označený v + Door: + Wybrane na + + + Every appointment + allen Terminen + Toutes les réservations + Todas las citas + Všechny ujednání + Alle afspraken in lijst + Każdy termin + + + No appointment + keinem Termin + Pas de réservation + Ninguna cita + Nesjednáno + Geen afspraak + Brak rezerwacji + + + Allocatable in the given timeframe + Belegbar in folgendem Zeitraum + Peut être alloué pendant la période + Puede ser asignado en el periodo + Výběr daný do časového rámce + Beschikbaarheid + Dostępne w okresie + + + Access + Zugriff + Accès + Acceso + Přístupný + Toegang + Dostępne + + + Denied + gesperrt + Interdit + Denegado + Zamítnuto + Geen toegang + Zastrzeżone + + + can read + darf lesen + peut lire + Puede leer + může číst + Lezen + może odczytać + + + can allocate + darf belegen + peut allouer + Puede asignar + může alokovat + Toewijzen + może przydzielać + + + can allocate & create conflicts + darf belegen & Konflikte erzeugen + Peut allouer & Créer des conflits + Puede asignar y crear conflictos + může alokovat & vytvářet konflikty + Toewijzen met conflicten + może przydzielać i tworzyć konflikty + + + administrator rights + Administrationsrechte + Droits d'administrateur + Derechos de administrador + Administrátorské práva + Beheer + Uprawnienia administratora + + + + Start of {0} + Start {0} + Début {0} + Inicio de {0} + Začátek {0} + Start van {0} + Początek {0} + + + End of {0} + Ende {0} + Fin {0} + Fin de {0} + Konec {0} + Einde van {0} + Koniec {0} + + + {0}.week {1} + {0}.Wo {1} + {0}.Sem. {1} + {0}.Sem. {1} + {0}.týden {1} + Week {0} van {1} + {0} tyg. {1} + + + Select period + Zeitraum auswählen + Sélectionner la période + Seleccionar el periodo + Označ periodu + Kies periode + Wybierz okres + + + Period + Zeitraum + Période + Periodo + Perioda + Periode + Okres + + + Period + Zeitraum + Période + Periodo + Perioda + Periode + Okres + + + in period {0} + im Zeitraum {0} + dans la période {0} + dentro del periodo {0} + V periodě {0} + in periode {0} + w okresie {0} + + + Periods + Zeiträume + Périodes + Periodos + Periody + Periode + Okresy + + + + Conflicts + Konflikte + Conflits + Conflictos + Konflikt + Conflict + Konflikty + + + Conflicts ({0,number,integer}) + Konflikte ({0,number,integer}) + Conflits ({0,number,integer}) + Conflictos ({0,number,integer}) + Konflikt ({0,number,integer}) + Conflict ({0,number,integer}) + Konflikty ({0,number,integer}) + + + Which event? + Welche Veranstaltung? + Quel évènement ? + ¿Qué evento? + Čí je to událost? + Welk evenement? + Które wydarzenie? + + + Other event? + Andere Veranstaltung? + Autre évènement ? + ¿Otro evento? + Další událost? + Ander evenement? + Inne wydarzenie? + + + When? + Wann? + Quand ? + ¿Cuándo? + Když + Wanneer? + Kiedy? + + + What/who? + Was/Wer? + Quoi/Qui ? + ¿Qué/Quién? + Uživatel? + Wat/Wie? + Kto/co? + + + User? + Mit Wem? + Avec qui ? + ¿Con quién? + Uživatel? + Gebruiker? + Użytkownik? + + + conflicting allocation + Konflikt-Termin + Conflit d'allocation + Conflicto de asignación + Konfiktní rozvrh + Toewijzings conflict + Konflikt przydziału + + + + + Confirm {0} + {0} bestätigen + Confirmer {0} + Confirmar {0} + Potvrdit + Bevestig {0} + Potwierdź {0} + + + Save this Information? + Diese Informationen speichern? + Sauvegarder cette information ? + ¿Guardar esta información? + Uložit tyto informace? + Bevestig deze informatie? + Zapisać te informacje? + + + + The GNU-license + Die GNU-Lizenz + La licence GNU + La licencia GNU + GNU Licence + GNU Licentie + Licencja GNU + + + + New category + Neue Kategorie + Nouvelle catégorie + Nueva categoría + Nová kategorie + Nieuw klassement + Nowa kategoria + + + New sub-category + Neue Unter-Kategorie + Nouvelle sous-catégorie + Nueva sub-categoría + Nová podkategorie + Nieuw onderklassement + Nowa podkategoria + + + + Delete? + Löschen? + Effacer + ¿Borrar? + Smazat? + Verwijderen? + Usunąć? + + + You will delete the following objects: + Sie löschen die folgenden Objekte: + Vous allez effacer les objets suivants : + Budeš mazat následující objekt + Volgende onderdelen worden verwijderd: + Następujące elementy zostaną usunięte: + + + Yes, delete it! + Löschen!! + Oui, effacer + Sí, borrar + Ano, smaž to!! + OK + Tak, usuń! + + + No, don't delete it! + NICHT ausführen!! + Annuler la commande + ¡No, no lo hagas! + Ne, nedělej to!! + Annuleren + Anulować + + + + Quit Rapla? + Rapla beenden? + Quitter Rapla ? + ¿Abandonar Rapla? + Ukončit Rapla? + Rapla beëindigen + Zakończyć Rapla? + + + Do you really want to quit Rapla? + Wollen Sie Rapla beenden? + Souhaitez vous réellement quitter Rapla ? + ¿Desea realmente abandonar Rapla? + Opravdu chcete ukončit plánování ? + Planning beëindigen? + Czy na pewno chcesz zakończyć Rapla? + + + Quit Rapla + Rapla beenden? + Quitter Rapla ? + ¿Abandonar Rapla? + Ukončit systém Rapla + Planning beëindigen + Wyjdź + + + Continue Rapla >> + Weiter planen >> + Continuer Rapla >> + Continuar Rapla >> + Pokračuj v plánování + Terug + Wróć do Rapla + + + + Changes not saved! + Änderungen noch nicht gespeichert! + Modifications non sauvegardées ! + ¡Modificaciones no guardadas! + Změny neuloženy! + Wijzigingen + Nie zapisano zmian! + + + You have not saved your changes. + Sie haben die vorgenommenen Änderungen noch + nicht gespeichert. + Vous n'avez pas sauvegardé les modifications! + ¡No ha guardado las modificaciones! + Neuložil(a) jste změny! + De wijzigingen werden niet geregisteerd! + Nie zapisano zmian. + + + Discard changes + Änderungen verwerfen + Annuler les modifications + Anular las modificaciones + Ignoruj změny + Wijzigingen negeren + Ignoruj zmiany + + + + + Rapla version @doc.version@ + + + Rapla Version @doc.version@ + + + Rapla Version @doc.version@ + + + Rapla Version @doc.version@ + + + Rapla versie @doc.version@ + + + + + + Rapla version @doc.version@ +
+ (C) Copyright @doc.year@ @doc.copyright@ +

Rapla homepage: @doc.homepage@ +

+

Post your feature requests, questions or bug reports + to our developers mailing list:
+ @doc.developer-list@ +

+

Build-Time: @doc.buildtime@

+

Signature: {0}

+

Java version {1}

+
+ + Rapla Version @doc.version@ +
+ (C) Copyright @doc.year@ @doc.copyright@ +

Rapla-Webseite: @doc.homepage@ +

+

Schicken Sie Ihre Anfordungen, Fragen oder Fehlerreports + an die Entwickler- Mailingliste:
+ @doc.developer-list@ +

+

Version vom: @doc.buildtime@

+

Signatur: {0}

+

Java Version {1}

+
+ + Rapla Version @doc.version@ +
+ (C) Copyright @doc.year@ @doc.copyright@ +

Rapla-Webseite: @doc.homepage@ +

+

Envoyer vos propositions d"évolutions, questions, ou erreurs trouvées + à la Mailingliste développeurs :
+ @doc.developer-list@ +

+

Version : @doc.buildtime@

+

Signature: {0}

+

Version Java {1}

+
+ + Rapla Version @doc.version@ +
+ (C) Copyright @doc.year@ @doc.copyright@ +

Página de Rapla: @doc.homepage@ +

+

Envía tus propuestas de cambios, preguntas, o errores encontrados + a la lista de correo de nuestros desarrolladores:
+ @doc.developer-list@ +

+

Version : @doc.buildtime@

+

Firma: {0}

+

Version Java {1}

+
+ + (C) Copyright @doc.year@ @doc.copyright@
+

Rapla versie @doc.version@
+ Gebouwd op: @doc.buildtime@
+ Ondertekend: {0}
+ Java versie: {1}

+

Rapla homepage: @doc.homepage@
+ Support: @doc.developer-list-link@

+
+ + Rapla - wersja @doc.version@ +
+ (C) Copyright @doc.year@ @doc.copyright@ +

strona Rapla: @doc.homepage@ +

+

Pytania, sugestie, feedback:
+ @doc.developer-list@ +

+

Build-Time: @doc.buildtime@

+

Podpis: {0}

+

Java - wersja {1}

+
+
+ + + + +

More information:

+

+ Java-Webstart is included in all Java versions since 1.4.0. + To run Rapla, you need Java 1.4 or above, which you can download from + java.sun.com. +

+

+ Check out rapla.sourceforge.net for more information. +

+
+ +

Mehr Informationen:

+

+ Java Webstart ist in allen Java Version seit 1.4.0 enthalten. + Um Rapla zu starten brauchen Sie Java 1.4 oder eine neuere Version. Diese können Sie herunterladen von + java.sun.com. +

+

+ Besuchen Sie auch rapla.sourceforge.net für mehr Informationen über Rapla. +

+
+ +

Plus d'informations:

+

+ Java-Webstart est inclus dans toutes les versions de Java depuis la 1.4.0. + Pour lancer Rapla, vous avez besoin de Java 1.4 ou supérieur que vous pouvez télécharger depuis + java.sun.com. +

+

+ Consultez rapla.sourceforge.net pour de plus amples informations. +

+
+ +

Más información:

+

+ Java-Webstart se incluye en todas las versiones de Java desde la 1.4.0. + Para ejecutar Rapla, se necesita Java 1.4 o superior. Se lo puede descargar de + java.sun.com. +

+

+ Para más información, visite rapla.sourceforge.net. +

+
+ +

Meer informatie:

+ rapla.sourceforge.net +
+
+ + + + Start Rapla (with java webstart) + + + Rapla starten (mit Java Webstart) + + + Démarrer Rapla (avec java webstart) + + + iniciar Rapla (con java webstart) + + + Rapla Smart Planning + + + + + + Start Rapla (with Java Plugin) + + + Rapla starten (mit Java Plugin) + + + Démarrer Rapla (avec le Plugin Java) + + + iniciar Rapla (con Java-Plugin) + + + Start Rapla planning (Applet) + + + + + + Server Status + + + Status des Servers + + + Etat du Serveur + + + + + + Rapla Login + Rapla Login + Login Rapla + Acceso a Rapla + Přihlášení k rezervačnímu systému + Rapla Aanmelden + + + Choose your language: + Wählen Sie Ihre Sprache: + Choisissez votre langue : + Escoja su idioma : + Zvolte váš jazyk: + Taalkeuze: + Wybierz język: + + + + Rapla version @doc.version@ (C) Copyright @doc.year@ @doc.copyright@ +

+ Rapla comes with NO WARRANTY!! + This is free software, you are welcome to redistribute it under certain + conditions.

+
+ + Rapla Version @doc.version@ (C) Copyright @doc.year@ @doc.copyright@ +

Für Rapla besteht KEINE GARANTIE!! + Rapla ist freie Software, die Sie unter bestimmten + Bedingungen weitergeben dürfen.

+
+ + Rapla Version @doc.version@ (C) Copyright @doc.year@ @doc.copyright@ +

Rapla est SANS GARANTIE!! + Rapla est un logiciel libre, que vous pouvez utiliser et distribuer suivant ces conditions.

+
+ + Rapla Version @doc.version@ (C) Copyright @doc.year@ @doc.copyright@ +

¡¡Rapla viene SIN GARANTÍA!! + Rapla es software libre, así que puede usarlo y distribuirlo bajo las siguientes condiciones.

+
+ + Rapla Verze @doc.version@ (C) Copyright @doc.year@ @doc.copyright@ +

Na systém Rapla není poskytována ŽÁDNÁ ZÁRUKA!! + Rapla Toto je volný software a je možné jej rozšiřovat pouze za určitých + podmínek.

+
+ + Rapla versie @doc.version@ : @doc.buildtime@
+ (C) Copyright @doc.year@ @doc.copyright@ +

Rapla wordt geleverd ZONDER GARANTIE.

+ Deze software kan enkel onder bepaalde voorwaarden vrij worden verspreid. +
+
+ + + From all users + Von allen Benutzern + De tous les utilisateurs + De todos los usuarios + Od každého uživatele + voor alle gebruikers + Od wszystkich + + + Own events + Eigene Veranstaltungen + Vos propres évènements + Sus propios eventos + Moje události + Moje wydarzenia + + + Filter name + Filtername + Nom du filtre + Nombre del filtro + Název filtru + Filter naam + Nazwa filtra + + + is smaller than + ist kleiner als + est plus petit que + es menor que + Je menší, než + kleiner dan + mniejsze niż + + + is greater than + ist größer als + est supérieur à + es mayor que + Je větší, než + groter dan + większe niż + + + smaller than or equal + ist kleiner gleich + est plus petit ou égal que + es menor o igual que + Je menší nebo rovno + kleiner dan of gelijk + mniejsze niż lub równa + + + + greater than or equal + ist größer gleich + est supérieur ou égal à + es mayor o igual que + je větší nebo rovno + groter dan of gelijk + większe niż lub równa + + + + is earlier than + ist früher als + est avant le + es anterior a las + Je dříve, než + vroeger dan + przed + + + is later than + ist später als + est après le + es posterior a las + Je později, než + later dan + po + + + equals + ist gleich + est égal à + es igual a + Je stejný, jako + gelijk aan + dokładnie + + + is not equal to + ist ungleich + est inégaux + es desigual + není rovno + ongelijk + nierówny + + + contains + enthält + contient + contiene + Obsahuje + bevat + zawiera + + + Restrict view of {0} + Anzeige von {0} einschränken + Limiter l''affichage de {0} + Limitar la vista de {0} + Pohled omezený {0} + beperk beeld van {0} + Ogranicz widok {0} + + + Resources and persons + Ressourcen und Personen + Ressources et personnes + Recursos y personas + Prostředky a osoby + Personen & Materialen + Zasoby i osoby + + + Only Own Reservations + Nur eigene Reservierungen + Seulement mes réservations + Sólo mis reservas + Moje rezervace + Enkel eigen reservaties + Tylko moje rezerwacje + + + Events from all users + Veranstaltungen von allen Benutzern + Evènement de tous les utilisateurs + Eventos de todos los usuarios + Rezervace všech uživatelů + Evenementen van alle gebruikers + Rezerwacje wszystkich + + + Complete time + gesamte Zeit + Période totale + Periodo total + Celkový čas + volledige duur + Całkowity czas + + + All filtered objects. + Alle gefilterten Objekte. + Tous les objets filtrés. + Todos los objetos filtrados. + Všechny filtrované objekty. + alle gefilterde objecten. + Wszystkie filtrowane obiekty. + + + New rule for + neue Regel für + Nouvelle règle pour + Nueva regla para + Nové pravidlo pro + Nieuwe regel + Nowa reguła dla + + + + + Allocation of {0} + Belegungen von {0} + Allocation de {0} + Asignación de {0} + Umístění {0} + Toewijzing van {0} + Przyporządkowanie {0} + + + + + Print Preview + Druck Voransicht + Imprimer la prévisualisation + Imprimir la previsualización + Náhled před tiskem + Afdrukvoorbeeld + Podgląd wydruku + + + Postscript + Postscript + Postscript + Postscript + Postscript + Postscript + Postscript + + + Choose your save method! + Wählen Sie die Speichermethode! + Choisissez la méthode de sauvegarde! + Escojer el método de almacenamiento + Vyberte způsob ukládání! + Kies opslag methode! + Wybierz metodę zapisu! + + + Printer Preferences + Drucker-Einstellungen + Préférence d'Impression + Preferencias de impresión + Vlastnosti tiskárny + Printer voorkeur + Ustawienia drukarki + + + Title + Überschrift + Titre + Título + Titul + Titel + Tytuł + + + + Rapla, smart resource and event planning + Rapla, der clevere Ressourcen- und Veranstaltungsplaner + Rapla, le planificateur intelligent + Rapla, el planificador inteligente + Rapla, chytré plánování prostředků a událostí + Rapla, Smart Planning + Rapla, inteligentne planowanie + + + Rapla + Rapla + Rapla + Rapla + Rapla + Rapla + + + Hello {0}, enjoy planning! + Hallo {0}, viel Spaß beim Planen! + Bonjour {0}, amusez vous en planifiant ! + Hola {0}, ¡disfruta planificando! + Dobrý den, přejeme příjemné plánování! Přihlášen uživatel: {0} + Online: {0} + Witaj {0}. + + + + Database + Datenbasis + Base de données + Base de datos + Databáze + Databank + Baza danych + + + Display exceptions in calendar + Zeige Ausnahmen im Kalender + Afficher les exceptions dans le calendrier + Mostrar las excepciones en el calendario + Zobrazit vyjímky v kalendáři + Uitzonderingen + Pokaż wyjątki + + + Display conflicts + Zeige Konflikte + Afficher conflits + Mostrar las conflictos + Zobrazit vyjímky + Conflicten + Pokaż konflikty + + + Display mouse-over tips + Zeige mouse-over + Afficher mouse-over tips dans + Mostrar las tips mouse-over + Zobrazit vyjímky v mouse-over + Tips + Pokazuj podpowiedzi + + + Exclude days + Tage ausschließen + exclure les jours + Uitgezonderd dagen: + Wyłącz dni: + + + Rows per hour + Zeilen pro Stunden + Lignes par heure + Líneas por hora + Počet řádků na hodinu + Tijdsdelen per uur: + Podziałka godzin + + + Restart Rapla client + Rapla neu starten + Redémarrer Rapla + Reiniciar el cliente Rapla + Restart klienta + Herstart gebruiker + Restart Rapla + + + Restart Server + Server neu starten + Redémarrer Serveur + Reiniciar el servidor + Restart serveru + Herstart Rapla server + Restart serwera + + + Load data + Daten neu laden + Recharger les données + Recargar los datos + Znovu načíst data + herladen gegevens + Odśwież dane + + + + + New Event: {0} + Neue Veranstaltung: {0} + Nouvel évènement : {0} + Nuevo evento : {0} + Nová událost: {0} + Nieuw evenement {0} + Nowe wydarzenie: {0} + + + Edit event: {0} + Veranstaltung bearbeiten: {0} + Modifier l"évènement: {0} + Modificar el evento : {0} + Upravit událost: {0} + Bewerk evenement: {0} + Edytuj wydarzenie: {0} + + + Edit {0} + {0} bearbeiten + Modifier {0} + Modificar {0} + Upravit {0} + Bewerk: {0} + Edytuj {0} + + + Delete {0} + {0} löschen + Effacer {0} + Eliminar {0} + Odstranit {0} + Verwijder: {0} + Usuń {0} + + + Translations for "{0}": + Übersetzungen von "{0}": + Traduction de "{0}": + Traducción de "{0}": + Překlady pro "{0}": + Vertaling voor "{0}": + Tłumaczenia na "(0)": + + + Change {0} + {0} ändern + Modifier {0} + Modificar {0} + Změnit {0} + Wijzig {0} + Zmień {0} + + + {0} ! + {0} ! + {0} ! + {0} ! + {0} ! + {0} ! + {0} ! + + + {0}: {1} + {0}: {1} + {0} : {1} + {0} : {1} + {0}: {1} + {0}: {1} + {0}: {1} + + + all-day + ganztägig + 24h + 24h + 24h + 24u + 24h + + + You need to restart Rapla/Rapla-server when you change the options! + ... + ... + ... + ... + Om de wijzigingen te activeren moet je Rapla herstarten! + Zmiany zostaną wprowadzone po restarcie Rapla + + + + Calendar name: + Kalender Namen: + Nom du calendrier: + Calendario nombre: + Název kalendáře: + Kalender naam: + Nazwa kalendarza: + + + Reset + Reset + Réinitialisation + Restablecer + Reset + Terugzetten + Resetuj + + + Default Selection + Standardauswahl + Sélection par défaut + Selección por defecto + Výběr výchozí + Standaard Selectie + + + Only 1 event type is allowed. Adjust the filter! + Nur 1 Veranstaltungstyp ist erlaubt. Passen Sie den Filter! + Seulement 1 type d'événement est autorisé. Réglez le filtre! + Sólo un tipo de evento se permite. Ajuste el filtro! + Pouze 1 typ události je povoleno. Nastavte. filtr! + Enkel 1 type evenement is toegestaan. Pas de filter aan! + Tylko 1 zdarzenie typu jest dozwolone. Ustaw filtr! + + + Include date: {0} + mit Datum: {0} + y compris la date: {0} + incluyendo la fecha: {0} + včetně data: {0} + met vaste datum = {0} + w tym dniu: {0} + + + Occupation + Besetzung + l''Occupation + Ocupación + Obsazenost + Bezetting + Zawód + +
\ No newline at end of file diff --git a/Rapla/src/org/rapla/RaplaStartupEnvironment.java b/Rapla/src/org/rapla/RaplaStartupEnvironment.java new file mode 100644 index 0000000..340ea9a --- /dev/null +++ b/Rapla/src/org/rapla/RaplaStartupEnvironment.java @@ -0,0 +1,159 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla; + +import java.net.MalformedURLException; +import java.net.URL; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.logger.ConsoleLogger; +import org.apache.avalon.framework.logger.Logger; +import org.rapla.components.util.IOUtil; +import org.rapla.components.util.JNLPUtil; +import org.rapla.framework.RaplaException; +import org.rapla.framework.StartupEnvironment; +import org.rapla.framework.internal.ConfigTools; + +final public class RaplaStartupEnvironment implements StartupEnvironment +{ + private int startupMode = EMBEDDED; + //private LoadingProgress progressbar; + private Logger bootstrapLogger = new ConsoleLogger( ConsoleLogger.LEVEL_WARN ); + private URL configURL; + private URL loggerConfigURL; + private URL contextURL; + private URL downloadURL; + + public Configuration getStartupConfiguration() throws RaplaException + { + return ConfigTools.createConfig( getConfigURL().toExternalForm() ); + } + + public URL getConfigURL() throws RaplaException + { + if ( configURL != null ) + { + return configURL; + } + else + { + return ConfigTools.configFileToURL( null, "rapla.xconf" ); + } + } + + public URL getLoggerConfigURL() throws RaplaException + { + if ( loggerConfigURL != null ) + { + return loggerConfigURL; + } + else + { + try + { + URL loggerConfig = new URL( getConfigURL(), "raplaclient.xlog" ); + return loggerConfig; + } + catch ( MalformedURLException ex ) + { + return null; + } + } + } + + public Configuration getLoggerConfig() throws RaplaException + { + return ConfigTools.createConfig( getLoggerConfigURL().toExternalForm() ); + } + + public Logger getBootstrapLogger() + { + return bootstrapLogger; + } + + public void setStartupMode( int startupMode ) + { + this.startupMode = startupMode; + } + + /* (non-Javadoc) + * @see org.rapla.framework.IStartupEnvironment#getStartupMode() + */ + public int getStartupMode() + { + return startupMode; + } + + public void setBootstrapLogger( Logger logger ) + { + bootstrapLogger = logger; + } + + public void setLogConfigURL( URL logConfigURL ) + { + this.loggerConfigURL = logConfigURL; + } + + public void setConfigURL( URL configURL ) + { + this.configURL = configURL; + } + + public URL getContextRootURL() throws RaplaException + { + if ( contextURL != null ) + return contextURL; + return IOUtil.getBase( getConfigURL() ); + } + + public URL getDownloadURL() throws RaplaException + { + if ( downloadURL != null ) + { + return downloadURL; + } + if ( startupMode == APPLET ) + { + return IOUtil.getBase( getConfigURL() ); + } + if ( startupMode == WEBSTART ) + { + try + { + return JNLPUtil.getCodeBase(); + } + catch ( Exception e ) + { + throw new RaplaException( e ); + } + } + else + { + try + { + return new URL( "http://localhost:8051" ); + } + catch ( MalformedURLException e ) + { + throw new RaplaException( "Invalid URL" ); + } + } + } + + public void setDownloadURL( URL downloadURL ) + { + this.downloadURL = downloadURL; + } + +} diff --git a/Rapla/src/org/rapla/bootstrap/RaplaLoader.java b/Rapla/src/org/rapla/bootstrap/RaplaLoader.java new file mode 100644 index 0000000..d07c77e --- /dev/null +++ b/Rapla/src/org/rapla/bootstrap/RaplaLoader.java @@ -0,0 +1,124 @@ +package org.rapla.bootstrap; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.StringTokenizer; + +/** + Puts all jar-files from the libdirs into the classpath and start the mainClass. + Usage: + + Syntax: baseDir libdir1,libdir2,... mainClass arg1 arg2 ... + + Will put all jar-files from the libdirs into the classpath and start the mainClass + + baseDir: replace with the path, from wich you want the lib-dirs to resolve. + libdir[1-n]: Lib-Directories relativ to the base jars."); + mainClass: The Java-Class you want to start after loading the jars. + arg[1-n]: + + Example: ./lib common,client org.rapla.Main rapla + + loads the jars in lib/common and lib/client and + starts org.rapla.Main with the argument rapla + + */ +public class RaplaLoader { + /** returns all *.jar files in the directories passed in dirList relative to the baseDir */ + public static File[] getJarFiles(String baseDir,String dirList) throws IOException { + ArrayList completeList = new ArrayList(); + StringTokenizer tokenizer = new StringTokenizer(dirList,","); + while (tokenizer.hasMoreTokens()) + { + File jarDir = new File(baseDir,tokenizer.nextToken()); + if (jarDir.exists() && jarDir.isDirectory()) + { + File[] jarFiles = jarDir.listFiles(); + for (int i = 0; i < jarFiles.length; i++) { + if ( + jarFiles[i].getAbsolutePath().endsWith(".jar") + ) + { + completeList.add(jarFiles[i].getCanonicalFile()); + } + } + completeList.add( jarDir.getCanonicalFile() ); + } + + } + return (File[]) completeList.toArray(new File[] {}); + } + + + private static void printUsage() { + System.out.println("Syntax: baseDir libdir1,libdir2,... mainClass arg1 arg2 ..."); + System.out.println(); + System.out.println("Will put all jar-files from the libdirs into the classpath and start the mainClass"); + System.out.println(); + System.out.println(" baseDir: replace with the path, from wich you want the lib-dirs to resolve."); + System.out.println(" libdir[1-n]: Lib-Directories relativ to the base jars."); + System.out.println(" mainClass: The Java-Class you want to start after loading the jars."); + System.out.println(" arg[1-n]: "); + System.out.println(); + System.out.println(" Example: ./lib common,client org.rapla.Main rapla "); + System.out.println("loads the jars in lib/common and lib/client and "); + System.out.println(" starts org.rapla.Main with the argument rapla"); + } + + public static void main(String[] args) { + String baseDir; + String dirList; + String classname; + String[] applicationArgs; + + if (args.length <3) { + printUsage(); + System.exit(1); + } + baseDir=args[0]; + dirList=args[1]; + classname=args[2]; + + applicationArgs = new String[args.length-3]; + for (int i=0;i +

+The client package is responsible for initialize the gui +and the client-plugins and providing the services for the +client application. +

+ + + + diff --git a/Rapla/src/org/rapla/components/calendar/AbstractBlockField.java b/Rapla/src/org/rapla/components/calendar/AbstractBlockField.java new file mode 100644 index 0000000..e18b603 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendar/AbstractBlockField.java @@ -0,0 +1,398 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.calendar; + +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.text.*; + +import java.util.ArrayList; +import java.awt.*; +import java.awt.event.*; +import java.util.Observable; +import java.util.Observer; + +import org.rapla.components.calendar.jdk14adapter.AWTAdapterFactory; + +/** The base class for TextFields that supports entering values in + blocks. Most notifiably the DateField and the TimeField.
You + can use the left/right arrow keys to switch the blocks. Use of the + top/down arrow keys will increase/decrease the values in the + selected block (page_up/page_down for major changes). You can + specify maximum length for each block. If a block reaches this + maximum value the focus will switch to the next block. + @see DateField + @see TimeField + */ +public abstract class AbstractBlockField extends JTextField { + private static final long serialVersionUID = 1L; + int m_markedBlock = 0; + char m_lastChar = 0; + boolean m_keyPressed = false; + protected String m_oldText; + + ArrayList m_listenerList = new ArrayList(); + + public AbstractBlockField() { + Listener listener = new Listener(); + addMouseListener(listener); + addActionListener(listener); + addFocusListener(listener); + addKeyListener(listener); + AWTAdapterFactory fact = AWTAdapterFactory.getFactory(); + if (fact != null) + fact.createMouseWheelObservable(this).addObserver(listener); + } + + class Listener implements MouseListener,KeyListener,FocusListener,Observer,ActionListener { + public void mouseReleased(MouseEvent evt) { + } + public void mousePressed(MouseEvent evt) { + // We have to mark the block on mouse pressed and mouse clicked. + // Windows needs mouse clicked while Linux needs mouse pressed. + markCurrentBlock(); + } + public void mouseEntered(MouseEvent me) { + } + public void mouseExited(MouseEvent me) { + } + public void mouseClicked(MouseEvent evt) { + // We have to mark the block on mouse pressed and mouse clicked. + markCurrentBlock(); + if (evt.getClickCount()>1) { + selectAll(); + return; + } + if (blocksValid()) { + fireValueChanged(); + } + } + + // Implementation of ActionListener + public void actionPerformed(ActionEvent e) { + blocksValid(); + fireValueChanged(); + } + + public void focusGained(FocusEvent evt) { + if (blocksValid()) { + setBlock(0); + } + } + public void focusLost(FocusEvent evt) { + select(-1,-1); + blocksValid(); + fireValueChanged(); + } + + public void keyPressed(KeyEvent evt) { + m_keyPressed = true; + m_lastChar=evt.getKeyChar(); + if (!blocksValid()) + return; + if (isSeparator(evt.getKeyChar())) { + evt.consume(); + return; + } + switch (evt.getKeyCode()) { + case KeyEvent.VK_LEFT: + case KeyEvent.VK_KP_LEFT: + if (blockCount() >1) { + setBlock(-1); + evt.consume(); + } + return; + case KeyEvent.VK_KP_RIGHT: + case KeyEvent.VK_RIGHT: + if (blockCount() >1) { + setBlock(1); + evt.consume(); + } + return; + case KeyEvent.VK_HOME: + setBlock(-1000); + evt.consume(); + return; + case KeyEvent.VK_END: + setBlock(1000); + evt.consume(); + return; + case KeyEvent.VK_KP_UP: + case KeyEvent.VK_UP: + changeSelectedBlock(1); + evt.consume(); + return; + case KeyEvent.VK_KP_DOWN: + case KeyEvent.VK_DOWN: + changeSelectedBlock(-1); + evt.consume(); + return; + case KeyEvent.VK_PAGE_UP: + changeSelectedBlock(10); + evt.consume(); + return; + case KeyEvent.VK_PAGE_DOWN: + changeSelectedBlock(-10); + evt.consume(); + return; + } + } + + public void keyReleased(KeyEvent evt) { + m_lastChar=evt.getKeyChar(); + // Only change the block if the keyReleased + // event follows a keyPressed. + // If you type very quickly you could + // get two strunged keyReleased events + if (m_keyPressed == true) + m_keyPressed = false; + else + return; + if (!blocksValid()) + return; + if (isSeparator(m_lastChar) ) { + if ( isSeparator( getText().charAt(getCaretPosition()))) + nextBlock(); + evt.consume(); + } else if (isValidChar(m_lastChar)) { + advance(); + evt.consume(); + } + } + public void keyTyped(KeyEvent evt) { + } + /** MouseWheelObserver */ + public void update(Observable o, Object arg) { + if (!blocksValid() || arg == null) + return; + int count = ((Long) arg).intValue(); + changeSelectedBlock(-1 * count/(Math.abs(count))); + } + } + + private void markCurrentBlock() { + if (!blocksValid()) { + return; + } + int blockstart[] = new int[blockCount()]; + int block = calcBlocks(blockstart); + markBlock(blockstart,block); + } + + public void addChangeListener(ChangeListener listener) { + m_listenerList.add(listener); + } + + /** removes a listener from this component.*/ + public void removeChangeListener(ChangeListener listener) { + m_listenerList.remove(listener); + } + + public ChangeListener[] getChangeListeners() { + return (ChangeListener[])m_listenerList.toArray(new ChangeListener[]{}); + } + + /** A ChangeEvent will be fired to every registered ActionListener + * when the value has changed. + */ + protected void fireValueChanged() { + if (m_listenerList.size() == 0) + return; + + // Only fire, when text has changed. + if (m_oldText != null && m_oldText.equals(getText())) + return; + m_oldText = getText(); + + ChangeEvent evt = new ChangeEvent(this); + ChangeListener[] listeners = getChangeListeners(); + for (int i = 0; i= blockCount() -1) + return; + int selectedLen = (block ==0) ? blockstart[1] : (blockstart[block + 1] - blockstart[block] - 1); + if (selectedLen == maxBlockLength(block)) { + markBlock(blockstart,Math.min(block + 1,blockCount() -1)); + } + } + + /** changes the value of the selected Block. Adds the count value. */ + private void changeSelectedBlock(int count) { + int blockstart[] = new int[blockCount()]; + int block = calcBlocks(blockstart); + int start = (block == 0) ? 0 : blockstart[block] + 1; + int end = (block == blockCount() -1) ? getText().length() : blockstart[block+1]; + String selected = getText().substring(start,end); + markBlock(blockstart,block); + changeSelectedBlock(blockstart,block,selected,count); + } + + private void setBlock(int count) { + if (blockCount()<1) + return; + int blockstart[] = new int[blockCount()]; + int block = calcBlocks(blockstart); + if (count !=0) + markBlock(blockstart,Math.min(Math.max(0,block + count),blockCount()-1)); + else + markBlock(blockstart,m_markedBlock); + } + + + /** Select the specified block. */ + final protected void markBlock(int[] blockstart,int block) { + m_markedBlock = block; + if (m_markedBlock == 0) + mark(blockstart[0], (blockCount()>1) ? blockstart[1] : getText().length()); + else if (m_markedBlock==blockCount()-1) + mark(blockstart[m_markedBlock] + 1,getText().length()); + else + mark(blockstart[m_markedBlock] + 1,blockstart[m_markedBlock+1]); + } + + private boolean isPrevSeparator(int i,char[] source,String currentText,int offs) { + return (i>0 && isSeparator(source[i-1]) + || (offs > 0 && isSeparator(currentText.charAt(offs-1)))); + } + private boolean isNextSeparator(int i,char[] source,String currentText,int offs) { + return (offs < currentText.length()-1 && isSeparator(currentText.charAt(offs))); + } + + class DateDocument extends PlainDocument { + private static final long serialVersionUID = 1L; + + public void insertString(int offs, String str, AttributeSet a) + throws BadLocationException { + String currentText = getText(0, getLength()); + char[] source = str.toCharArray(); + char[] result = new char[source.length]; + int j = 0; + boolean bShouldBeep = false; + for (int i = 0; i < source.length; i++) { + boolean bValid = true; + if (!isValidChar(source[i])) { + bValid = false; + } else { + //if (offs < currentText.length()-1) + //System.out.println("offset-char: " + currentText.charAt(offs)); +// + if (isSeparator(source[i])) + if (isNextSeparator(i,source,currentText,offs) + || isPrevSeparator(i,source,currentText,offs) + || (i==0 && offs==0) + || (i==source.length && offs==currentText.length()) + ) + { + // beep(); + continue; + } + } + if (bValid) + result[j++] = source[i]; + else + bShouldBeep = true; + } + if (bShouldBeep) + beep(); + super.insertString(offs, new String(result, 0, j), a); + } + public void remove(int offs, int len) + throws BadLocationException { + if (m_lastChar == 0 + || (!isSeparator(m_lastChar) + && (isValidChar(m_lastChar) + || !Character.isLetter(m_lastChar)) + ) + ) + super.remove(offs, len); + } + } + + final protected Document createDefaultModel() { + return new DateDocument(); + } + + /** Calculate the blocks. The new blockstarts will be stored in + the int array. + @return the block that contains the caret. + */ + protected int calcBlocks(int[] blockstart) { + String text = getText(); + int dot = getCaretPosition(); + int block = 0; + blockstart[0] = 0; + char[] separators = getSeparators(); + for (int i=1;i=0 && pos < min) + min = pos; + } + blockstart[i] = min; + if (dot>blockstart[i]) + block = i; + } + return block; + } + + /** Select the text from dot to mark. */ + protected void mark(int dot,int mark) { + setCaretPosition(mark); + moveCaretPosition(dot); + } + + /** this method will be called when an non-valid character is entered. */ + protected void beep() { + Toolkit.getDefaultToolkit().beep(); + } + + /** returns true if the text can be split into blocks. */ + abstract public boolean blocksValid(); + /** The number of blocks for this Component. */ + abstract protected int blockCount(); + /** This method will be called, when the user has pressed the up/down + * arrows on a selected block. + * @param count Posible values are 1,-1,10,-10. + */ + abstract protected void changeSelectedBlock(int[] blocks,int block,String selected,int count); + /** returns the maximum length of the specified block. */ + abstract protected int maxBlockLength(int block); + /** returns true if the character should be accepted by the component. */ + abstract protected boolean isValidChar(char c); + /** returns true if the character is a block-separator. All block-separators must + be valid characters. + @see #isValidChar + */ + abstract protected boolean isSeparator(char c); + /** @return all seperators.*/ + abstract protected char[] getSeparators(); + +} diff --git a/Rapla/src/org/rapla/components/calendar/ArrowPolygon.java b/Rapla/src/org/rapla/components/calendar/ArrowPolygon.java new file mode 100644 index 0000000..5704fee --- /dev/null +++ b/Rapla/src/org/rapla/components/calendar/ArrowPolygon.java @@ -0,0 +1,78 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.components.calendar; +import java.awt.Polygon; + +/* + The following classes are only responsible to paint the Arrows for the NavButton. +*/ + +class ArrowPolygon extends Polygon { + private static final long serialVersionUID = 1L; + + public ArrowPolygon(char type,int width) { + this(type,width,true); + } + + public ArrowPolygon(char type,int width,boolean border) { + int polyWidth = ((width - 7) / 8); + polyWidth = (polyWidth + 1) * 8; + int dif = border ? (width - polyWidth) /2 : 0; + int half = polyWidth / 2 + dif; + int insets = (polyWidth == 8) ? 0 : polyWidth / 4; + int start = insets + dif; + int full = polyWidth - insets + dif; + int size = (polyWidth == 8) ? polyWidth / 4 : polyWidth / 8; + + if ( type == '>') { + addPoint(half - size,start); + addPoint(half + size,half); + addPoint(half - size,full); + } // end of if () + + if ( type == '<') { + addPoint(half + size,start); + addPoint(half - size,half); + addPoint(half + size,full); + } // end of if () + + if ( type == '^') { + addPoint(start,half + size); + addPoint(half,half - size); + addPoint(full,half + size); + } // end of if () + + if ( type == 'v') { + addPoint(start,half - size); + addPoint(half,half + size); + addPoint(full,half - size); + } // end of if () + + if ( type == '-') { + addPoint(start + 1,half - 1); + addPoint(full - 3,half - 1); + } // end of if () + + if ( type == '+') { + addPoint(start + 1,half - 1); + addPoint(full - 3,half - 1); + addPoint(half -1,half - 1); + addPoint(half- 1, half - (size + 1)); + addPoint(half -1,half + ( size -1)); + addPoint(half -1,half - 1); + } // end of if () + + } +} + diff --git a/Rapla/src/org/rapla/components/calendar/CalendarMenu.java b/Rapla/src/org/rapla/components/calendar/CalendarMenu.java new file mode 100644 index 0000000..cbdd7f0 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendar/CalendarMenu.java @@ -0,0 +1,385 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.components.calendar; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.MenuElement; +import javax.swing.MenuSelectionManager; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; +import javax.swing.border.LineBorder; + +import org.rapla.components.calendar.jdk14adapter.AWTAdapterFactory; +/** The graphical date-selection field with month and year incerement/decrement buttons. + * @author Christopher Kohlhaas + */ + + +public class CalendarMenu extends JPanel implements MenuElement { + private static final long serialVersionUID = 1L; + + static Color BACKGROUND_COLOR = Color.white; + static Border FOCUSBORDER = new LineBorder(DaySelection.FOCUSCOLOR,1); + static Border EMPTYBORDER = new EmptyBorder(1,1,1,1); + BorderLayout borderLayout1 = new BorderLayout(); + + JPanel topSelection = new JPanel(); + Border border1 = BorderFactory.createEtchedBorder(); + BorderLayout borderLayout2 = new BorderLayout(); + JPanel monthSelection = new JPanel(); + NavButton jPrevMonth = new NavButton('<'); + JLabel labelMonth = new JLabel(); + NavButton jNextMonth = new NavButton('>'); + JPanel yearSelection = new JPanel(); + NavButton jPrevYear = new NavButton('<'); + JLabel labelYear = new JLabel(); + NavButton jNextYear = new NavButton('>'); + + protected DaySelection daySelection; + JLabel labelCurrentDay = new JLabel(); + JButton focusButton = new JButton() { + private static final long serialVersionUID = 1L; + + public void paint(Graphics g) { + Dimension size = getSize(); + g.setColor(getBackground()); + g.fillRect(0,0,size.width,size.height); + } + }; + + + + DateModel m_model; + DateModel m_focusModel; + + String[] m_monthNames; + boolean m_focusable = true; + + public CalendarMenu(DateModel newModel) { + m_model = newModel; + + m_monthNames = createMonthNames(newModel.getLocale()); + + m_focusModel = new DateModel(newModel.getLocale(),newModel.getTimeZone()); + daySelection = new DaySelection(newModel,m_focusModel); + + initGUI(); + Listener listener = new Listener(); + + jPrevMonth.addActionListener(listener); + jPrevMonth.setBorder( null ); + jNextMonth.addActionListener(listener); + jNextMonth.setBorder( null ); + jPrevYear.addActionListener(listener); + jPrevYear.setBorder( null ); + jNextYear.addActionListener(listener); + jNextYear.setBorder( null ); + + AWTAdapterFactory factory = AWTAdapterFactory.getFactory(); + if ( factory != null ) + { + factory.createFocusAdapter(jPrevMonth).setFocusable( false ); + factory.createFocusAdapter(jNextMonth).setFocusable( false ); + factory.createFocusAdapter(jPrevYear).setFocusable( false ); + factory.createFocusAdapter(jNextYear).setFocusable( false ); + } + + this.addMouseListener(listener); + jPrevMonth.addMouseListener(listener); + jNextMonth.addMouseListener(listener); + jPrevYear.addMouseListener(listener); + jNextYear.addMouseListener(listener); + labelCurrentDay.addMouseListener(listener); + daySelection.addMouseListener(listener); + labelCurrentDay.addMouseListener(listener); + m_model.addDateChangeListener(listener); + m_focusModel.addDateChangeListener(listener); + focusButton.addKeyListener(listener); + + focusButton.addFocusListener(listener); + calculateSizes(); + m_focusModel.setDate(m_model.getDate()); + } + + class Listener implements ActionListener,MouseListener,FocusListener,DateChangeListener,KeyListener { + public void actionPerformed(ActionEvent evt) { + if ( evt.getSource() == jNextMonth) { + m_focusModel.addMonth(1); + } // end of if () + + if ( evt.getSource() == jPrevMonth) { + m_focusModel.addMonth(-1); + } // end of if () + + if ( evt.getSource() == jNextYear) { + m_focusModel.addYear(1); + } // end of if () + + if ( evt.getSource() == jPrevYear) { + m_focusModel.addYear(-1); + } // end of if () + } + + // Implementation of DateChangeListener + public void dateChanged(DateChangeEvent evt) { + if (evt.getSource() == m_model) { + m_focusModel.setDate(evt.getDate()); + } else { + updateFields(); + } + } + // Implementation of MouseListener + public void mousePressed(MouseEvent me) { + if (me.getSource() == labelCurrentDay) { + // Set the current day as sellected day + m_model.setDate(new Date()); + } else { + if (m_focusable && !focusButton.hasFocus()) + focusButton.requestFocus(); + } + } + public void mouseClicked(MouseEvent me) { + } + public void mouseReleased(MouseEvent me) { + } + public void mouseEntered(MouseEvent me) { + } + public void mouseExited(MouseEvent me) { + } + + // Implementation of KeyListener + public void keyPressed(KeyEvent e) { + processCalendarKey(e); + } + public void keyReleased(KeyEvent e) { + } + public void keyTyped(KeyEvent e) { + } + + // Implementation of FocusListener + public void focusGained(FocusEvent e) { + if (e.getSource() == focusButton) + setBorder(FOCUSBORDER); + else + transferFocus(); + } + public void focusLost(FocusEvent e) { + if (e.getSource() == focusButton) + setBorder(EMPTYBORDER); + } + } + + private void updateFields() { + labelCurrentDay.setText(m_focusModel.getCurrentDateString()); + labelMonth.setText(m_monthNames[m_focusModel.getMonth()]); + labelYear.setText(m_focusModel.getYearString()); + } + + public DateModel getModel() { + return m_model; + } + + public boolean hasFocus() { + return focusButton.hasFocus(); + } + + public boolean isFocusable() { + return m_focusable; + } + + public void setFocusable(boolean m_focusable) { + this.m_focusable = m_focusable; + } + + // #TODO Property change listener for TimeZone + public void setTimeZone(TimeZone timeZone) { + m_focusModel.setTimeZone(timeZone); + } + + public void setFont(Font font) { + super.setFont(font); + // Method called during constructor? + if (labelMonth == null || font == null) + return; + labelMonth.setFont(font); + labelYear.setFont(font); + daySelection.setFont(font); + labelCurrentDay.setFont(font); + calculateSizes(); + invalidate(); + } + + public void requestFocus() { + if (m_focusable) + focusButton.requestFocus(); + } + + public DaySelection getDaySelection() { + return daySelection; + } + + private void calculateSizes() { + // calculate max size of month names + int maxWidth =0; + FontMetrics fm = getFontMetrics(labelMonth.getFont()); + for (int i=0;i< m_monthNames.length;i++) { + int len = fm.stringWidth(m_monthNames[i]); + if (len>maxWidth) + maxWidth = len; + } + labelMonth.setPreferredSize(new Dimension(maxWidth,fm.getHeight())); + int h = fm.getHeight(); + jPrevMonth.setSize(h,h); + jNextMonth.setSize(h,h); + jPrevYear.setSize(h,h); + jNextYear.setSize(h,h); + // Workaraund for focus-bug in JDK 1.3.1 + Border dummyBorder = new EmptyBorder(fm.getHeight(),0,0,0); + focusButton.setBorder(dummyBorder); + } + + private void initGUI() { + setBorder(EMPTYBORDER); + topSelection.setLayout(borderLayout2); + topSelection.setBorder(border1); + daySelection.setBackground(BACKGROUND_COLOR); + + FlowLayout flowLayout = new FlowLayout(FlowLayout.LEFT,0,0); + monthSelection.setLayout(flowLayout); + monthSelection.add(focusButton); + monthSelection.add(jPrevMonth); + monthSelection.add(labelMonth); + monthSelection.add(jNextMonth); + + yearSelection.setLayout(flowLayout); + yearSelection.add(jPrevYear); + yearSelection.add(labelYear); + yearSelection.add(jNextYear); + + topSelection.add(monthSelection,BorderLayout.WEST); + topSelection.add(yearSelection,BorderLayout.EAST); + + setLayout(borderLayout1); + add(topSelection,BorderLayout.NORTH); + add(daySelection,BorderLayout.CENTER); + add(labelCurrentDay,BorderLayout.SOUTH); + } + + private String[] createMonthNames(Locale locale ) { + Calendar calendar = Calendar.getInstance(m_model.getLocale()); + calendar.setLenient(true); + Collection monthNames = new ArrayList(); + SimpleDateFormat format = new SimpleDateFormat("MMM",m_model.getLocale()); + int firstMonth = 0; + int month = 0; + while (true) { + calendar.set(Calendar.DATE,1); + calendar.set(Calendar.MONTH,month); + if (month == 0) + firstMonth = calendar.get(Calendar.MONTH); + else + if (calendar.get(Calendar.MONTH) == firstMonth) + break; + monthNames.add(format.format(calendar.getTime())); + month ++; + } + return (String[]) monthNames.toArray(new String[0]); + } + + private void processCalendarKey(KeyEvent e) { + switch (e.getKeyCode()) { + case (KeyEvent.VK_KP_UP): + case (KeyEvent.VK_UP): + m_focusModel.addDay(-7); + break; + case (KeyEvent.VK_KP_DOWN): + case (KeyEvent.VK_DOWN): + m_focusModel.addDay(7); + break; + case (KeyEvent.VK_KP_LEFT): + case (KeyEvent.VK_LEFT): + m_focusModel.addDay(-1); + break; + case (KeyEvent.VK_KP_RIGHT): + case (KeyEvent.VK_RIGHT): + m_focusModel.addDay(1); + break; + case (KeyEvent.VK_PAGE_DOWN): + m_focusModel.addMonth(1); + break; + case (KeyEvent.VK_PAGE_UP): + m_focusModel.addMonth(-1); + break; + case (KeyEvent.VK_ENTER): + m_model.setDate(m_focusModel.getDate()); + break; + case (KeyEvent.VK_SPACE): + m_model.setDate(m_focusModel.getDate()); + break; + } + } + + // Start of MenuElement implementation + public Component getComponent() { + return this; + } + public MenuElement[] getSubElements() { + return new MenuElement[0]; + } + + public void menuSelectionChanged(boolean isIncluded) { + } + + public void processKeyEvent(KeyEvent event, MenuElement[] path, MenuSelectionManager manager) { + if (event.getID() == KeyEvent.KEY_PRESSED) + processCalendarKey(event); + switch (event.getKeyCode()) { + case (KeyEvent.VK_ENTER): + case (KeyEvent.VK_ESCAPE): + manager.clearSelectedPath(); + } + event.consume(); + } + + public void processMouseEvent(MouseEvent event, MenuElement[] path, MenuSelectionManager manager) { + } + // End of MenuElement implementation +} + diff --git a/Rapla/src/org/rapla/components/calendar/DEPENDENCIES b/Rapla/src/org/rapla/components/calendar/DEPENDENCIES new file mode 100644 index 0000000..1c9f7f6 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendar/DEPENDENCIES @@ -0,0 +1,7 @@ +This component depends on the following packages (including subpackages): +java.* +javax.swing.* + +If you want to compile with JDK 1.3 or lower please exclude +the class jdk14adapter.AWTAdapterFacotryImpl. +Requires at least jdk1.2 \ No newline at end of file diff --git a/Rapla/src/org/rapla/components/calendar/DateChangeEvent.java b/Rapla/src/org/rapla/components/calendar/DateChangeEvent.java new file mode 100644 index 0000000..a7c2906 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendar/DateChangeEvent.java @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.components.calendar; +import java.util.EventObject; +import java.util.Date; +public class DateChangeEvent extends EventObject { + private static final long serialVersionUID = 1L; + + Date m_date; + public DateChangeEvent(Object source,Date date) { + super(source); + m_date = date; + } + public Date getDate() { + return m_date; + } +} diff --git a/Rapla/src/org/rapla/components/calendar/DateChangeListener.java b/Rapla/src/org/rapla/components/calendar/DateChangeListener.java new file mode 100644 index 0000000..e91cace --- /dev/null +++ b/Rapla/src/org/rapla/components/calendar/DateChangeListener.java @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.components.calendar; +/* The components that implement DateChangeListener + * get notified by the DateModel if the date has been changed + */ +public interface DateChangeListener extends java.util.EventListener { + public void dateChanged(DateChangeEvent evt); +} diff --git a/Rapla/src/org/rapla/components/calendar/DateField.java b/Rapla/src/org/rapla/components/calendar/DateField.java new file mode 100644 index 0000000..e29d85e --- /dev/null +++ b/Rapla/src/org/rapla/components/calendar/DateField.java @@ -0,0 +1,356 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.calendar; + +import java.awt.*; +import java.awt.event.*; +import java.text.*; +import java.util.Locale; +import java.util.Date; +import java.util.Calendar; +import java.util.TimeZone; +/** The DateField only accepts characters that are part of + * DateFormat.getDateInstance(DateFormat.SHORT,locale). The + * inputblocks are [date,month,year]. The order of the input-blocks is + * determined by the locale. You can use the keyboard to navigate + * between the blocks or to increment/decrement the blocks. + * @see AbstractBlockField + */ + +final public class DateField extends AbstractBlockField { + private static final long serialVersionUID = 1L; + + private DateFormat m_outputFormat; + private DateFormat m_parsingFormat; + private Calendar m_calendar; + private int m_rank[] = null; + private char[] m_separators; + private SimpleDateFormat m_weekdayFormat; + private boolean m_weekdaysVisible = true; + private DateRenderer m_dateRenderer; + /** stores the y-coordinate of the weekdays display field*/ + private int m_weekdaysX; + private boolean nullValuePossible; + private boolean nullValue; + + public boolean isNullValue() { + return nullValue; + } + + public void setNullValue(boolean nullValue) { + this.nullValue = nullValue; + } + + public boolean isNullValuePossible() + { + return nullValuePossible; + } + + public void setNullValuePossible(boolean nullValuePossible) + { + this.nullValuePossible = nullValuePossible; + } + + public DateField() { + this(Locale.getDefault()); + } + + public DateField(Locale locale) { + this(locale,TimeZone.getDefault()); + } + + public DateField(Locale locale,TimeZone timeZone) { + super(); + m_calendar = Calendar.getInstance(timeZone, locale); + super.setLocale(locale); + setFormat(); + setDate(new Date()); + } + + + public void setLocale(Locale locale) { + super.setLocale(locale); + if (locale != null && getTimeZone() != null) + update(getTimeZone(), locale); + } + + public void setTimeZone(TimeZone timeZone) { + if (timeZone != null) + update(timeZone, getLocale()); + } + + private void update(TimeZone timeZone, Locale locale) { + Date date = getDate(); + if (locale != null) + m_calendar = Calendar.getInstance(timeZone, locale); + else + m_calendar = Calendar.getInstance(timeZone); + setFormat(); + setText(m_outputFormat.format(date)); + } + + + /** you can choose, if weekdays should be displayed in the right corner of the DateField. + Default is true. + */ + public void setWeekdaysVisible(boolean m_weekdaysVisible) { + this.m_weekdaysVisible = m_weekdaysVisible; + } + + /** sets the DateRenderer for the calendar */ + public void setDateRenderer(DateRenderer dateRenderer) { + m_dateRenderer = dateRenderer; + } + + private void setFormat() { + m_parsingFormat = DateFormat.getDateInstance(DateFormat.SHORT, getLocale()); + m_weekdayFormat = new SimpleDateFormat("EE", getLocale()); + m_parsingFormat.setTimeZone(getTimeZone()); + m_weekdayFormat.setTimeZone(getTimeZone()); + + String formatStr = m_parsingFormat.format(m_calendar.getTime()); + FieldPosition datePos = new FieldPosition(DateFormat.DATE_FIELD); + FieldPosition monthPos = new FieldPosition(DateFormat.MONTH_FIELD); + FieldPosition yearPos = new FieldPosition(DateFormat.YEAR_FIELD); + m_parsingFormat.format(m_calendar.getTime(), new StringBuffer(),datePos); + m_parsingFormat.format(m_calendar.getTime(), new StringBuffer(),monthPos); + m_parsingFormat.format(m_calendar.getTime(), new StringBuffer(),yearPos); + + int mp = monthPos.getBeginIndex(); + int dp = datePos.getBeginIndex(); + int yp = yearPos.getBeginIndex(); + int pos[] = null; + // System.out.println(formatStr + " day:"+dp+" month:"+mp+" year:"+yp); + if (mp<0 || dp<0 || yp<0) { + throw new IllegalArgumentException("Can't parse the date-format for this locale"); + } + // quick and diry sorting + if (dp= m_weekdaysX) + return super.getToolTipText(event); + return null; + } + + public boolean blocksValid() { + String dateTxt = null; + try { + dateTxt = getText(); + if ( isNullValuePossible() && dateTxt.length() == 0) + { + nullValue = true; + return true; + } + m_calendar.setTime(m_parsingFormat.parse(dateTxt)); + nullValue = false; + return true; + } catch (ParseException e) { + return false; + } + } + + static int[] BLOCKLENGTH = new int[] {2,2,5}; + + protected int blockCount() { + return BLOCKLENGTH.length; + } + + protected void mark(int dot,int mark) { + super.mark(dot,mark); + if (!m_weekdaysVisible) + repaint(); + } + + protected int maxBlockLength(int block) { + return BLOCKLENGTH[block]; + } + + protected boolean isValidChar(char c) { + return (Character.isDigit(c) || isSeparator(c) ); + } + + /** This method is necessary to shorten the names down to 2 characters. + Some date-formats ignore the setting. + */ + private String small(String string) { + return string.substring(0,Math.min(string.length(),2)); + } + + public void paint(Graphics g) { + super.paint(g); + if (!m_weekdaysVisible) + return; + Insets insets = getInsets(); + final String format = nullValue ? "" :m_weekdayFormat.format(m_calendar.getTime()); + String s = small(format); + FontMetrics fm = g.getFontMetrics(); + int width = fm.stringWidth(s); + int x = getWidth()-width-insets.right-3; + m_weekdaysX = x; + int y = insets.top + (getHeight() - (insets.bottom + insets.top))/2 + fm.getAscent()/3; + if (m_dateRenderer != null) { + Color color =m_dateRenderer.getBackgroundColor( + m_calendar.get(Calendar.DAY_OF_WEEK) + ,m_calendar.get(Calendar.DATE) + ,m_calendar.get(Calendar.MONTH) + ,m_calendar.get(Calendar.YEAR) + ); + if (color != null) { + g.setColor(color); + g.fillRect(x-1, insets.top, width + 3 , getHeight() - insets.bottom - insets.top - 1); + } + } + g.setColor(Color.gray); + g.drawString(s,x,y); + } +} diff --git a/Rapla/src/org/rapla/components/calendar/DateModel.java b/Rapla/src/org/rapla/components/calendar/DateModel.java new file mode 100644 index 0000000..c9312f8 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendar/DateModel.java @@ -0,0 +1,205 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.components.calendar; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.ArrayList; +import java.util.Locale; +import java.util.Date; +import java.util.TimeZone; + +/** + * The model of the obligatory MVC approach is a wrapper arround an + * Calendar object. + */ +final class DateModel { + private Calendar m_calendar; + private int m_daysMonth; + private int m_daysLastMonth; + private int m_firstWeekday; + private Locale m_locale; + private DateFormat m_yearFormat; + private DateFormat m_currentDayFormat; + + ArrayList listenerList = new ArrayList(); + + public DateModel(Locale locale,TimeZone timeZone) { + m_locale = locale; + m_calendar = Calendar.getInstance(timeZone,m_locale); + trim(m_calendar); + m_yearFormat = new SimpleDateFormat("yyyy", m_locale); + m_yearFormat.setTimeZone(timeZone); + m_currentDayFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, m_locale); + m_currentDayFormat.setTimeZone(timeZone); + m_calendar.setLenient(true); + recalculate(); + } + + public boolean sameDate(Date date) { + Calendar calendar2 = Calendar.getInstance(m_locale); + calendar2.setTimeZone(getTimeZone()); + calendar2.setTime(date); + trim(calendar2); + return calendar2.getTime().equals(getDate()); + } + + public void addDateChangeListener(DateChangeListener listener) { + listenerList.add(listener); + } + + public void removeDateChangeListener(DateChangeListener listener) { + listenerList.remove(listener); + } + + public Locale getLocale() {return m_locale; } + public int getDay() { return m_calendar.get(Calendar.DATE); } + public int getMonth() { return m_calendar.get(Calendar.MONTH); } + public int getYear() { return m_calendar.get(Calendar.YEAR); } + + /** return the number of days of the selected month */ + public int daysMonth() { return m_daysMonth; } + /** return the number of days of the month before the selected month. */ + public int daysLastMonth() { return m_daysLastMonth; } + /** return the first weekday of the selected month (1 - 7). */ + public int firstWeekday() { return m_firstWeekday; } + + /** calculates the weekday from the passed day. */ + public int getWeekday(int day) { + // calculate the weekday, consider the index shift + return (((firstWeekday() - 1) + (day - 1)) % 7 ) + 1; + } + + public Date getDate() { + return m_calendar.getTime(); + } + + // #TODO Property change listener for TimeZone + public void setTimeZone(TimeZone timeZone) { + m_calendar.setTimeZone(timeZone); + m_yearFormat.setTimeZone(timeZone); + m_currentDayFormat.setTimeZone(timeZone); + recalculate(); + } + + public TimeZone getTimeZone() { + return m_calendar.getTimeZone(); + } + + public String getDateString() { + return m_currentDayFormat.format(getDate()); + } + + public String getCurrentDateString() { + return m_currentDayFormat.format(new Date()); + } + + public void addMonth(int count) { + m_calendar.add(Calendar.MONTH,count); + recalculate(); + } + + public void addYear(int count) { + m_calendar.add(Calendar.YEAR,count); + recalculate(); + } + + public void addDay(int count) { + m_calendar.add(Calendar.DATE,count); + recalculate(); + } + + public void setDay(int day) { + m_calendar.set(Calendar.DATE,day); + recalculate(); + } + + public void setMonth(int month) { + m_calendar.set(Calendar.MONTH,month); + recalculate(); + } + public String getYearString() { + DateFormat format; + if (m_calendar.get(Calendar.ERA)!=GregorianCalendar.AD) + format = new SimpleDateFormat("yyyy GG", getLocale()); + else + format = m_yearFormat; + return format.format(getDate()); + } + + public void setYear(int year) { + m_calendar.set(Calendar.YEAR,year); + recalculate(); + } + + public void setDate(int day,int month,int year) { + m_calendar.set(Calendar.DATE,day); + m_calendar.set(Calendar.MONTH,month); + m_calendar.set(Calendar.YEAR,year); + trim(m_calendar); + recalculate(); + } + + public void setDate(Date date) { + m_calendar.setTime(date); + trim(m_calendar); + recalculate(); + } + + private void trim(Calendar calender) { + m_calendar.set(Calendar.HOUR_OF_DAY,0); + m_calendar.set(Calendar.MINUTE,0); + m_calendar.set(Calendar.SECOND,0); + m_calendar.set(Calendar.MILLISECOND,0); + } + + // 18.02.2004 CK: Workaround for bug in JDK 1.5.0 .Replace add with roll + private void recalculate() { + Calendar calendar = Calendar.getInstance(getTimeZone(), getLocale()); + calendar.setTime(getDate()); + // calculate the number of days of the selected month + calendar.add(Calendar.MONTH,1); + calendar.set(Calendar.DATE,1); + calendar.add(Calendar.DAY_OF_YEAR,-1); + calendar.getTime(); + m_daysMonth = calendar.get(Calendar.DAY_OF_MONTH); + + // calculate the number of days of the month before the selected month + calendar.set(Calendar.DATE,1); + m_firstWeekday = calendar.get(Calendar.DAY_OF_WEEK); + calendar.add(Calendar.DAY_OF_YEAR,-1); + m_daysLastMonth = calendar.get(Calendar.DAY_OF_MONTH); + + // System.out.println("Calendar Recalculate: " + getDay() + "." + getMonth() + "." + getYear()); + fireDateChanged(); + } + + public DateChangeListener[] getDateChangeListeners() { + return (DateChangeListener[])listenerList.toArray(new DateChangeListener[]{}); + } + + protected void fireDateChanged() { + DateChangeListener[] listeners = getDateChangeListeners(); + DateChangeEvent evt = new DateChangeEvent(this,getDate()); + for (int i = 0;imaximum days per month 40 + */ + private String[] days = new String[40]; + + + + private int selectedField = 10; + private int focusedField = 10; + private DateModel model; + private DateModel focusModel; + + private int lastFocusedMonth; // performance improvement + private int lastFocusedYear; // performance improvement + + + private PaintEnvironment e; + private DateRenderer dateRenderer = null; + + public DaySelection(DateModel model,DateModel focusModel) { + this.model = model; + this.focusModel = focusModel; + focusModel.addDateChangeListener(this); + addMouseListener(this); + createWeekdays(model.getLocale()); + createDays(model.getLocale()); + // setFont(DEFAULT_FONT); + } + + /** Implementation-specific. Should be private.*/ + public void mousePressed(MouseEvent me) { + me.consume(); + selectField(me.getX(),me.getY()); + } + /** Implementation-specific. Should be private.*/ + public void mouseReleased(MouseEvent me) { + me.consume(); + } + + /** Implementation-specific. Should be private.*/ + public void mouseClicked(MouseEvent me) { + me.consume(); + } + + /** Implementation-specific. Should be private.*/ + public void mouseEntered(MouseEvent me) { + } + + /** You must call javax.swing.ToolTipManager.sharedInstance().registerComponent(thisComponent) + to enable ToolTip text. + */ + public String getToolTipText(MouseEvent me) { + int day = calcDay(me.getX(),me.getY()); + if (day >=0 && dateRenderer != null) + return dateRenderer.getToolTipText(focusModel.getWeekday(day), day, focusModel.getMonth(), focusModel.getYear()); + else + return ""; + } + + /** Implementation-specific. Should be private.*/ + public void mouseExited(MouseEvent me) { + } + + + public void setDateRenderer(DateRenderer dateRenderer) { + this.dateRenderer = dateRenderer; + } + + // Recalculate the Components minimum and maximum sizes + private void calculateSizes() { + if (e == null) + e = new PaintEnvironment(); + int maxWidth =0; + FontMetrics fm = getFontMetrics(getFont()); + e.fontHeight = fm.getHeight(); + e.cellHeight = fm.getHeight() + VGAP *2; + + for (int i=0;imaxWidth) + maxWidth = len; + } + if (fm.stringWidth("33")> maxWidth) + maxWidth = fm.stringWidth("33"); + + e.fontWidth = maxWidth; + e.cellWidth = maxWidth + HGAP * 2 ; + int width = COLUMNS * e.cellWidth + HBORDER *2; + setPreferredSize(new Dimension( width,(ROWS) * e.cellHeight + 2*HBORDER)); + setMinimumSize(new Dimension( width,(ROWS) * e.cellHeight + 2*VBORDER)); + invalidate(); + } + + public void setFont(Font font) { + super.setFont(font); + if (font == null) + return; + else + // We need the font-size to calculate the component size + calculateSizes(); + } + + public Dimension getPreferredSize() { + if (getFont() != null && e==null) + // We need the font-size to calculate the component size + calculateSizes(); + return super.getPreferredSize(); + } + + public void dateChanged(DateChangeEvent evt) { + if (e==null) + // We need the font-size to calculate the component size + if (getFont() != null ) + calculateSizes(); + else + return; + + int lastSelectedField = selectedField; + if (model.getMonth() == focusModel.getMonth() && + model.getYear() == focusModel.getYear()) + // #BUGFIX consider index shift. + // weekdays 1..7 and days 1..31 but field 0..40 + selectedField = weekday2slot[model.firstWeekday()]+ (model.getDay() - 1) ; + else + selectedField = -1; + + int lastFocusedField = focusedField; + // #BUGFIX consider index shift. + // weekdays 1..7 and days 1..31 but field 0..40 + int newFocusedField = weekday2slot[focusModel.firstWeekday()] + (focusModel.getDay() - 1) ; + + if (lastSelectedField == selectedField + && lastFocusedMonth == focusModel.getMonth() + && lastFocusedYear == focusModel.getYear() + ) { + // Only repaint the focusedField (Performance improvement) + focusedField = -1; + calculateRectangle(lastFocusedField); + repaint(e.r); + focusedField = newFocusedField; + calculateRectangle(focusedField); + repaint(e.r); + } else { + // repaint everything + focusedField = newFocusedField; + repaint(); + } + lastFocusedMonth = focusModel.getMonth(); + lastFocusedYear = focusModel.getYear(); + } + + /** calculate the day of month from the specified field. 1..31 + */ + private int calcDay(int field) { + return (field + 1) - weekday2slot[focusModel.firstWeekday()] ; + } + + /** calculate the day of month from the x and y coordinates. + @return -1 if coordinates don't point to a day in the selected month + */ + private int calcDay(int x,int y) { + int col = (x - HBORDER) / e.cellWidth; + int row = (y - VBORDER) / e.cellHeight; + int field = 0; + // System.out.println(row + ":" + col); + if ( row > 0 && col < COLUMNS && row < ROWS) { + field =(row-1) * COLUMNS + col; + } + if (belongsToMonth(field)) { + return calcDay(field); + } else { + return -1; + } + } + + + private void createDays(Locale locale) { + NumberFormat format = NumberFormat.getInstance(locale); + // Try to simluate a right align of the numbers with a space + for ( int i=0; i <10; i++) + days[i] = " " + format.format(i); + for ( int i=10; i < 40; i++) + days[i] = format.format(i); + } + + /** This method is necessary to shorten the names down to 2 characters. + Some date-formats ignore the setting. + */ + private String small(String string) { + return string.substring(0,Math.min(string.length(),2)); + } + + private void createWeekdays(Locale locale ) { + Calendar calendar = Calendar.getInstance(locale); + SimpleDateFormat format = new SimpleDateFormat("EE",locale); + calendar.set(Calendar.DAY_OF_WEEK,calendar.getFirstDayOfWeek()); + for (int i=0;i<7;i++) { + weekday2slot[calendar.get(Calendar.DAY_OF_WEEK)] = i; + weekdayNames[i] = small(format.format(calendar.getTime())); + calendar.add(Calendar.DAY_OF_WEEK,1); + } + } + + // calculates the rectangle for a given field + private void calculateRectangle(int field) { + int row = (field / COLUMNS) + 1; + int col = (field % COLUMNS); + calculateRectangle(row,col); + } + + + // calculates the rectangle for a given row and col + private void calculateRectangle(int row,int col) { + Rectangle r = e.r; + r.x = e.cellWidth * col + HBORDER; + r.y = e.cellHeight * row + VBORDER; + r.width = e.cellWidth; + r.height = e.cellHeight; + } + + private void calculateTextPos(int field) { + int row = (field / COLUMNS) + 1; + int col = (field % COLUMNS); + calculateTextPos(row,col); + } + + // calculates the text-anchor for a giver field + private void calculateTextPos(int row,int col) { + Point p = e.p; + p.x = e.cellWidth * col + HBORDER + (e.cellWidth - e.fontWidth)/2; + p.y = e.cellHeight * row + VBORDER + e.fontHeight - 1; + } + + private boolean belongsToMonth(int field) { + return (calcDay(field) > 0 && calcDay(field) <= focusModel.daysMonth()); + } + + private void selectField(int x,int y) { + int day = calcDay(x,y); + + if ( day >=0 ) { + model.setDate(day, focusModel.getMonth(), focusModel.getYear()); + } // end of if () + } + + + private Color getBackgroundColor(int field) { + int day = calcDay(field); + if (belongsToMonth(field) && dateRenderer != null) { + Color color = dateRenderer.getBackgroundColor(focusModel.getWeekday(day), day, focusModel.getMonth(), focusModel.getYear()); + if (color != null) + return color; + } + return DEFAULT_CELL_BACKGROUND; + } + + // return the Daytext, that should be displayed in the specified field + private String getText(int field) { + int index = 0; + + if ( calcDay(field) < 1) + index = focusModel.daysLastMonth() + calcDay(field) ; + + if (belongsToMonth(field)) + index = calcDay(field); + + if ( calcDay(field) > focusModel.daysMonth() ) + index = calcDay(field) - focusModel.daysMonth(); + + return days[index]; + } + + static char[] test = {'1'}; + private void drawField(int field,boolean highlight) { + if (field <0) + return; + Graphics g = e.g; + Rectangle r = e.r; + Point p = e.p; + + calculateRectangle(field); + if ( field == selectedField ) { + g.setColor(SELECTION_BACKGROUND); + g.fillRect(r.x +2 ,r.y +2 ,r.width-4,r.height-4); + g.setColor(SELECTION_BORDER); + //g.drawRect(r.x,r.y,r.width-1,r.height-1); + g.drawRect(r.x+1,r.y+1,r.width-3,r.height-3); + if ( field == focusedField ) + g.setColor(FOCUSCOLOR); + else + g.setColor(getBackgroundColor(field)); + g.drawRect(r.x,r.y,r.width-1,r.height-1); + g.setColor(SELECTION); + } else if ( field == focusedField ) { + g.setColor(getBackgroundColor(field)); + g.fillRect(r.x,r.y,r.width -1,r.height -1); + g.setColor(FOCUSCOLOR); + g.drawRect(r.x,r.y,r.width-1,r.height-1); + if ( highlight) { + g.setColor(SELECTABLE); + } else { + g.setColor(UNSELECTABLE); + } // end of else + } else { + g.setColor(getBackgroundColor(field)); + g.fillRect(r.x ,r.y ,r.width ,r.height); + if ( highlight) { + g.setColor(SELECTABLE); + } else { + g.setColor(UNSELECTABLE); + } // end of else + } + calculateTextPos(field); + g.drawString(getText(field) + , p.x , p.y); + } + + private void drawHeader() { + Graphics g = e.g; + Point p = e.p; + + g.setColor(HEADER_BACKGROUND); + g.fillRect(HBORDER,VBORDER, getSize().width - 2 * HBORDER, e.cellHeight); + g.setColor(HEADER); + for ( int i=0; i < COLUMNS; i++) { + calculateTextPos(0,i); + g.drawString(weekdayNames[i], p.x,p.y); + } + } + + private void paintComplete() { + drawHeader(); + int field = 0; + int start = weekday2slot[focusModel.firstWeekday()]; + int end= start + focusModel.daysMonth() -1 ; + for ( int i=0; i< start; i++) { + drawField(field ++,false); + } + + for ( int i= start; i <= end ; i++) { + drawField(field ++,true); + } + + for ( int i= end + 1; i< FIELDS ; i++) { + drawField(field ++,false); + } + } + + private void updateEnvironment(Graphics g) { + e.cellWidth = (getSize().width - HBORDER * 2) / COLUMNS; + e.g = g; + } + + public void paint(Graphics g) { + super.paint(g); + if (e==null) + if (getFont() != null ) + // We need the font-size to calculate the component size + calculateSizes(); + else + return; + updateEnvironment(g); + paintComplete(); + drawField(selectedField,true); + drawField(focusedField,true); + } + +} + +// Environment stores the values that would normaly be stored on the stack. +// This is to speedup performance, e.g. new Point and new Rectangle are only called once. +class PaintEnvironment { + Graphics g; + int cellWidth; + int cellHeight; + int fontWidth; + int fontHeight; + Point p = new Point(); + Rectangle r = new Rectangle(); +} + + + + diff --git a/Rapla/src/org/rapla/components/calendar/NavButton.java b/Rapla/src/org/rapla/components/calendar/NavButton.java new file mode 100644 index 0000000..9896480 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendar/NavButton.java @@ -0,0 +1,180 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.components.calendar; +import java.awt.Polygon; +import java.awt.Graphics; +import java.awt.Dimension; +import java.awt.Color; +import java.awt.event.MouseListener; +import java.awt.event.MouseEvent; +import java.awt.event.ActionEvent; +import javax.swing.AbstractButton; +import javax.swing.UIManager; +import javax.swing.SwingUtilities; +import javax.swing.plaf.ButtonUI; + +public class NavButton extends AbstractButton implements MouseListener { + private static final long serialVersionUID = 1L; + + Polygon m_poly; + char m_type; + boolean m_buttonDown = false; + Color m_disabledColor; + boolean m_enabled=true; + boolean m_border=true; + int m_delay = 0; + int leftPosition; + + ButtonStateChecker m_checker = new ButtonStateChecker(); + /** You can set the alignment of the arrow with one of these four characters: + '<', '^', '>', 'v' + */ + public NavButton(char type) { + this(type,18); + } + + public NavButton(char type,int size) { + this(type,size,true); + } + + public NavButton(char type,int size,boolean border) { + super(); + m_border = border; + m_type = type; + setSize(size,size); + addMouseListener(this); + m_disabledColor = UIManager.getColor("Button.disabledText"); + } + + /** Here you can set if the button should fire repeated clicks. If + set to 0 the button will fire only once when pressed. + */ + public void setClickRepeatDelay(int millis) { + m_delay = millis; + } + + public int getClickRepeatDelay() { + return m_delay; + } + + public void setUI(ButtonUI ui) { + super.setUI(ui); + m_disabledColor = UIManager.getColor("Button.disabledText"); + } + + void setLeftPosition(int position) { + leftPosition = position; + } + + /** Implementation-specific. Should be private.*/ + public void mouseEntered(MouseEvent me) { + } + /** Implementation-specific. Should be private.*/ + public void mouseExited(MouseEvent me) { + } + + /** Implementation-specific. Should be private.*/ + public void mousePressed(MouseEvent me) { + if (!isEnabled()) + return; + m_buttonDown = true; + repaint(); + m_checker.start(); + } + /** Implementation-specific. Should be private.*/ + public void mouseReleased(MouseEvent me) { + m_buttonDown = false; + repaint(); + } + + /** Implementation-specific. Should be private.*/ + public void mouseClicked(MouseEvent me) { + m_buttonDown = false; + repaint(); + } + + /** Set the size of the nav-button. A nav button is square. + The maximum of width and height will be used as new size. + */ + public void setSize(int width,int height) { + int size = Math.max(width,height); + m_poly = new ArrowPolygon(m_type,size,m_border); + super.setSize(size,size); + Dimension dim = new Dimension(size,size); + setPreferredSize(dim); + setMaximumSize(dim); + setMinimumSize(dim); + } + + public void setEnabled(boolean enabled) { + m_enabled = enabled; + repaint(); + } + public boolean isEnabled() { + return m_enabled; + } + public void paint(Graphics g) { + g.translate( leftPosition, 0); + if (m_buttonDown) { + //g.setColor( UIManager.getColor("Button.pressed")); + g.setColor( UIManager.getColor("Button.focus")); + g.fillPolygon(m_poly); + g.drawPolygon(m_poly); + } else { + if (isEnabled()) { + g.setColor( UIManager.getColor("Button.font") ); + g.fillPolygon(m_poly); + } else { + g.setColor(m_disabledColor); + } + g.drawPolygon(m_poly); + } + } + + + class ButtonStateChecker implements Runnable{ + long startMillis; + long startDelay; + public void start() { + startDelay = m_delay * 8; + fireAndReset(); + if (m_delay > 0) + SwingUtilities.invokeLater(this); + } + + private void fireAndReset() { + fireActionPerformed(new ActionEvent(this + ,ActionEvent.ACTION_PERFORMED + ,"")); + startMillis = System.currentTimeMillis(); + } + public void run() { + if (!m_buttonDown) + return; + if ((Math.abs(System.currentTimeMillis() - startMillis) > startDelay)) { + if (startDelay > m_delay) + startDelay = startDelay/2; + fireAndReset(); + } + try { + Thread.sleep(10); + } catch (Exception ex) { + return; + } + SwingUtilities.invokeLater(this); + }; + } +} + + diff --git a/Rapla/src/org/rapla/components/calendar/NumberField.java b/Rapla/src/org/rapla/components/calendar/NumberField.java new file mode 100644 index 0000000..bd075a1 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendar/NumberField.java @@ -0,0 +1,215 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.calendar; + +import java.awt.ComponentOrientation; + +/** + * The NumberField only accepts integer values. + * Warning! Currently only Longs are supported. + */ + +public class NumberField extends AbstractBlockField { + private static final long serialVersionUID = 1L; + + static char[] m_separators = new char[0]; + Number m_number; + Number m_minimum; + Number m_maximum; + /** + * @return Returns the m_blockStepSize. + */ + public int getBlockStepSize() { + return m_blockStepSize; + } + /** + * @param stepSize The m_blockStepSize to set. + */ + public void setBlockStepSize(int stepSize) { + m_blockStepSize = stepSize; + } + /** + * @return Returns the m_stepSize. + */ + public int getStepSize() { + return m_stepSize; + } + /** + * @param size The m_stepSize to set. + */ + public void setStepSize(int size) { + m_stepSize = size; + } + int m_stepSize; + int m_blockStepSize; + int m_maxLength; + boolean m_isNullPermitted = true; + public NumberField() { + setComponentOrientation( ComponentOrientation.RIGHT_TO_LEFT); + updateColumns(); + } + + public NumberField(Number minimum,Number maximum,int stepSize,int blockStepSize) { + this(); + setStepSize(stepSize); + setBlockStepSize(blockStepSize); + setMinimum( minimum ); + setMaximum( maximum ); + } + + + public void setMinimum(Number minimum){ + m_minimum = minimum; + updateColumns(); + } + + public void setMaximum(Number maximum){ + m_maximum = maximum; + updateColumns(); + } + + public Number getMinimum() { + return m_minimum; + } + + public Number getMaximum() { + return m_maximum; + } + + private void updateColumns() { + if (m_maximum!= null && m_minimum != null) { + if ((Math.abs(m_maximum.longValue())) + > (Math.abs(m_minimum.longValue())) * 10) + m_maxLength = m_maximum.toString().length(); + else + m_maxLength = m_minimum.toString().length(); + setColumns(m_maxLength); + } else { + m_maxLength = 100; + setColumns(4); + } + } + + public boolean isNullPermitted() { + return m_isNullPermitted; + } + public void setNullPermitted(boolean isNullPermitted) { + m_isNullPermitted = isNullPermitted; + if (m_number == null && !isNullPermitted) + m_number = new Double(defaultValue()); + } + + private long defaultValue() { + if (m_minimum != null && m_minimum.longValue()>0) + return m_minimum.longValue(); + else if (m_maximum != null && m_maximum.longValue()<0) + return m_maximum.longValue(); + return 0; + } + + public void setNumber(Number number) { + updateNumber( number ); + m_oldText = getText(); + fireValueChanged(); + } + + private void updateNumber(Number number) { + m_number = number; + String text; + if (number != null) { + text = String.valueOf(number.longValue()); + } else { + text = ""; + } + setText( text ); + } + + public void increase() { + changeSelectedBlock(new int[1],0,"",1); + } + + public void decrease() { + changeSelectedBlock(new int[1],0,"",-1); + } + + public Number getNumber() { + return m_number; + } + + public boolean allowsNegative() { + return (m_minimum == null || m_minimum.longValue()<0); + } + + protected char[] getSeparators() { + return m_separators; + } + + protected boolean isSeparator(char c) { + return false; + } + + protected void changeSelectedBlock(int[] blocks,int block,String selected,int count) { + long longValue = ((m_number != null) ? m_number.longValue() : defaultValue()); + if (count == 1) + longValue = longValue + m_stepSize; + if (count == -1) + longValue = longValue - m_stepSize; + if (count == 10) + longValue = longValue + m_blockStepSize; + if (count == -10) + longValue = longValue - m_blockStepSize; + + if (m_minimum != null && longValuem_maximum.longValue()) + longValue = m_maximum.longValue(); + updateNumber(new Long(longValue)); + calcBlocks(blocks); + markBlock(blocks,block); + } + + public boolean blocksValid() { + try { + String text = getText(); + if (text.length() ==0) { + if (isNullPermitted()) + m_number = null; + return true; + } + + long newLong = Long.parseLong(text); + if ((m_minimum == null || newLong>=m_minimum.longValue()) + && (m_maximum == null || newLong<=m_maximum.longValue())) { + m_number = new Long(newLong); + return true; + } + } catch (NumberFormatException e) { + if (isNullPermitted()) + m_number = null; + } + return false; + } + + protected int blockCount() { + return 1; + } + + protected int maxBlockLength(int block) { + return m_maxLength; + } + + protected boolean isValidChar(char c) { + return (Character.isDigit(c) || (allowsNegative() && c=='-')); + } + +} diff --git a/Rapla/src/org/rapla/components/calendar/RaplaArrowButton.java b/Rapla/src/org/rapla/components/calendar/RaplaArrowButton.java new file mode 100644 index 0000000..8ea1063 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendar/RaplaArrowButton.java @@ -0,0 +1,151 @@ +/*--------------------------------------------------------------------------* +| Copyright (C) 2006 Christopher Kohlhaas | +| | +| This program is free software; you can redistribute it and/or modify | +| it under the terms of the GNU General Public License as published by the | +| Free Software Foundation. A copy of the license has been included with | +| these distribution in the COPYING file, if not go to www.fsf.org | +| | +| As a special exception, you are granted the permissions to link this | +| program with every library, which license fulfills the Open Source | +| Definition as published by the Open Source Initiative (OSI). | +*--------------------------------------------------------------------------*/ +package org.rapla.components.calendar; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.image.BufferedImage; + +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.SwingUtilities; + +public class RaplaArrowButton extends JButton { + private static final long serialVersionUID = 1L; + + ButtonStateChecker m_checker = new ButtonStateChecker(); + + int m_delay = 0; + boolean m_buttonDown = false; + + ArrowPolygon poly; + char c; + + public RaplaArrowButton(char c) { + this(c,18); + + } + + public RaplaArrowButton(char c,int size) { + super(); + this.c = c; + setMargin(new Insets(0,0,0,0)); + setSize(size,size); + addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent me) { + if (!isEnabled()) + return; + m_buttonDown = true; + // repaint(); + m_checker.start(); + } + /** Implementation-specific. Should be private.*/ + public void mouseReleased(MouseEvent me) { + m_buttonDown = false; + // repaint(); + } + + /** Implementation-specific. Should be private.*/ + public void mouseClicked(MouseEvent me) { + m_buttonDown = false; + //repaint(); + } + }); + } + + /** Here you can set if the button should fire repeated clicks. If + set to 0 the button will fire only once when pressed. + */ + public void setClickRepeatDelay(int millis) { + m_delay = millis; + } + + public int getClickRepeatDelay() { + return m_delay; + } + + public void setChar( char c) + { + this.c = c; + final Dimension size = getSize(); + final int width2 = (int)size.getWidth(); + final int height2 = (int)size.getHeight(); + setSize( width2,height2); + } + /** Set the size of the drop-down button. + The minimum of width and height will be used as new size of the arrow. + */ + public void setSize(int width,int height) { + int size = Math.min(width,height); + int imageSize = size - 8; + if (imageSize > 0) { + poly = new ArrowPolygon(c,imageSize); + BufferedImage image = new BufferedImage(imageSize,imageSize,BufferedImage.TYPE_INT_ARGB); + Graphics g = image.createGraphics(); + g.setColor(new Color(0, 0, 0, 0)); + g.fillRect( 0, 0, imageSize, imageSize ); + + g.setColor( Color.darkGray ); + g.fillPolygon( poly ); + g.setColor( Color.black ); + g.drawPolygon( poly ); + setIcon(new ImageIcon(image)); + } else { + setIcon(null); + } + super.setSize(width ,height); + Dimension dim = new Dimension(width ,height); + setPreferredSize(dim); + setMaximumSize(dim); + setMinimumSize(dim); + } + + class ButtonStateChecker implements Runnable{ + long startMillis; + long startDelay; + public void start() { + startDelay = m_delay * 10; + startMillis = System.currentTimeMillis(); + if (m_delay > 0) + SwingUtilities.invokeLater(this); + } + + private void fireAndReset() { + fireActionPerformed(new ActionEvent(this + ,ActionEvent.ACTION_PERFORMED + ,"")); + startMillis = System.currentTimeMillis(); + } + public void run() { + if (!m_buttonDown) + return; + if ((Math.abs(System.currentTimeMillis() - startMillis) > startDelay)) { + if (startDelay > m_delay) + startDelay = startDelay/2; + fireAndReset(); + } + try { + Thread.sleep(10); + } catch (Exception ex) { + return; + } + SwingUtilities.invokeLater(this); + }; + } +} + diff --git a/Rapla/src/org/rapla/components/calendar/RaplaCalendar.java b/Rapla/src/org/rapla/components/calendar/RaplaCalendar.java new file mode 100644 index 0000000..bdf21ac --- /dev/null +++ b/Rapla/src/org/rapla/components/calendar/RaplaCalendar.java @@ -0,0 +1,280 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.components.calendar; +import javax.swing.*; +import javax.swing.event.*; +import java.awt.*; +import java.util.*; +/** This is another ComboBox-like calendar component. + * It is localizable and it uses swing-components. + *

The combobox editor is a {@link DateField}. If the ComboBox-Button + * is pressed, a CalendarMenu will drop down.

+ * @see CalendarMenu + * @see DateField + * @author Christopher Kohlhaas + */ +public final class RaplaCalendar extends RaplaComboBox { + private static final long serialVersionUID = 1L; + + protected DateField m_dateField; + protected CalendarMenu m_calendarMenu; + Collection m_listenerList = new ArrayList(); + protected DateModel m_model; + private Date m_lastDate; + DateRenderer m_dateRenderer; + + /** Create a new Calendar with the default locale. The calendarmenu + will be accessible via a drop-down-box */ + public RaplaCalendar() { + this(Locale.getDefault()); + } + + /** Create a new Calendar with the specified locale and the + default timeZone. The calendarmenu will be accessible via a + drop-down-box */ + public RaplaCalendar(Locale locale) { + this(locale,TimeZone.getDefault(),true); + } + + /** @deprecated replaced with #RaplaCalendar(Locale,TimeZone,boolean)*/ + public RaplaCalendar(Locale locale,boolean isDropDown) { + this(locale,TimeZone.getDefault(),isDropDown); + } + + /** Create a new Calendar with the specified locale and timezone. The calendarmenu + will be accessible via a drop-down-box */ + public RaplaCalendar(Locale locale,TimeZone timeZone) { + this(locale,timeZone,true); + } + + /** Create a new Calendar with the specified locale and timezone. The + isDropDown flag specifies if the calendarmenu should be + accessible via a drop-down-box. Alternatively you can get the + calendarmenu with getPopupComponent(). + */ + public RaplaCalendar(Locale locale,TimeZone timeZone,boolean isDropDown) { + super(isDropDown,new DateField(locale,timeZone)); + m_model = new DateModel(locale,timeZone); + m_dateField = (DateField) m_editorComponent; + Listener listener = new Listener(); + m_dateField.addChangeListener(listener); + m_model.addDateChangeListener(listener); + m_lastDate = m_model.getDate(); + setDateRenderer(new WeekendHighlightRenderer()); + } + + class Listener implements ChangeListener,DateChangeListener { + // Implementation of ChangeListener + public void stateChanged(ChangeEvent evt) { + validateEditor(); + } + + // Implementation of DateChangeListener + public void dateChanged(DateChangeEvent evt) { + closePopup(); + if (needSync()) + m_dateField.setDate(evt.getDate()); + if (m_lastDate == null || !m_lastDate.equals(evt.getDate())) + fireDateChange(evt.getDate()); + m_lastDate = evt.getDate(); + } + } + + public void setTimeZone(TimeZone timeZone) { + m_model.setTimeZone(timeZone); + if (m_calendarMenu != null) + m_calendarMenu.setTimeZone(timeZone); + m_dateField.setTimeZone(timeZone); + } + + public TimeZone getTimeZone() { + return m_model.getTimeZone(); + } + + + /* Use this to get the CalendarMenu Component. The calendar menu will be created lazily.*/ + public JComponent getPopupComponent() { + if (m_calendarMenu == null) { + m_calendarMenu = new CalendarMenu(m_model); + m_calendarMenu.setFont( getFont() ); + // #TODO Property change listener for TimeZone + m_calendarMenu.getDaySelection().setDateRenderer(m_dateRenderer); + javax.swing.ToolTipManager.sharedInstance().registerComponent(m_calendarMenu.getDaySelection()); + } + return m_calendarMenu; + } + + public void setFont(Font font) { + super.setFont(font); + // Method called during super-constructor? + if (m_calendarMenu == null || font == null) + return; + m_calendarMenu.setFont(font); + } + + /** Selects the date relative to the given timezone. + * The hour,minute,second and millisecond values will be ignored. + */ + public void setDate(Date date) + { + if ( date != null) + { + m_model.setDate(date); + } + else + { + m_dateField.setDate( null); + } + } + + /** Parse the returned date with a calendar-object set to the + * correct time-zone to get the date,month and year. The + * hour,minute,second and millisecond values should be ignored. + * @return the selected date + * @see #getYear + * @see #getMonth + * @see #getDay + */ + public Date getDate() { + if ( m_dateField.isNullValue()) + { + return null; + } + return m_model.getDate(); + } + + /** selects the specified day, month and year. + @see #setDate(Date date)*/ + public void select(int day,int month,int year) { + m_model.setDate(day,month,year); + } + + /** you can choose, if you want to highlight the weekends. Default is true. + If flag is set to true a new WeekendHighlightRenderer will be installed. + If flag is set to false the DateRenderer will be uninstalled. + @deprecated use setDateRenderer(new WeekendHighlightRenderer()) instead. + */ + public void setHighlightWeekend(boolean bHighlight) { + if (bHighlight) + setDateRenderer(new WeekendHighlightRenderer()); + else + setDateRenderer(null); + } + + /** sets the DateRenderer for the calendar */ + public void setDateRenderer(DateRenderer dateRenderer) { + m_dateRenderer = dateRenderer; + if (m_calendarMenu != null) { + m_calendarMenu.getDaySelection().setDateRenderer(m_dateRenderer); + } + m_dateField.setDateRenderer(m_dateRenderer); + } + + /** you can choose, if weekdays should be displayed in the right corner of the DateField. + Default is true. This method simply calls setWeekdaysVisble on the DateField Component. + If a DateRender is installed the weekday will be rendered with the DateRenderer. + This includes a tooltip that shows up on the DateRenderer. + @see DateField + */ + public void setWeekdaysVisibleInDateField(boolean bVisible) { + m_dateField.setWeekdaysVisible(bVisible); + } + + /** @return the selected year (relative to the given TimeZone) + @see #getDate + @see #getMonth + @see #getDay + */ + public int getYear() {return m_model.getYear(); } + + /** @return the selected month (relative to the given TimeZone) + @see #getDate + @see #getYear + @see #getDay + */ + public int getMonth() {return m_model.getMonth(); } + + /** @return the selected day (relative to the given TimeZone) + @see #getDate + @see #getYear + @see #getMonth + */ + public int getDay() {return m_model.getDay(); } + + /** registers new DateChangeListener for this component. + * A DateChangeEvent will be fired to every registered DateChangeListener + * when the a different date is selected. + * @see DateChangeListener + * @see DateChangeEvent + */ + public void addDateChangeListener( DateChangeListener listener ) { + m_listenerList.add(listener); + } + + /** removes a listener from this component.*/ + public void removeDateChangeListener( DateChangeListener listener ) { + m_listenerList.remove(listener); + } + + public DateChangeListener[] getDateChangeListeners() { + return (DateChangeListener[])m_listenerList.toArray(new DateChangeListener[]{}); + } + + /** A DateChangeEvent will be fired to every registered DateChangeListener + * when the a different date is selected. + */ + protected void fireDateChange( Date date ) { + if (m_listenerList.size() == 0) + return; + DateChangeListener[] listeners = getDateChangeListeners(); + DateChangeEvent evt = new DateChangeEvent(this,date); + for (int i = 0;i +
  • No pluggable look and feel
  • +
  • drop-down button and editor in a separate component (each can get focus)
  • +
  • the popup-component can be an arbitrary JComponent.
  • + +*/ +public abstract class RaplaComboBox extends JPanel { + + private static final long serialVersionUID = 1L; + private JPopupMenu m_popup; + private boolean m_popupVisible = false; + private boolean m_isDropDown = true; + protected RaplaArrowButton m_popupButton; + protected JLabel jLabel; + protected JComponent m_editorComponent; + + // If you click on the popupButton while the PopupWindow is shown, + // the Window will be closed, because you clicked outside the Popup + // (not because you clicked that special button). + // The flag popupVisible is unset. + // After that the same click will trigger the actionPerfomed method + // on our button. And the window would popup again. + // Solution: We track down that one mouseclick that closes the popup + // with the following flags: + private boolean m_closing = false; + private boolean m_pressed = false; + private Listener m_listener = new Listener(); + + public RaplaComboBox(JComponent editorComponent) { + this(true,editorComponent); + } + + RaplaComboBox(boolean isDropDown,JComponent editorComponent) { + m_editorComponent = editorComponent; + m_isDropDown = isDropDown; + jLabel = new JLabel(); + setLayout(new BorderLayout()); + add(jLabel,BorderLayout.WEST); + add(m_editorComponent,BorderLayout.CENTER); + + if (m_isDropDown) { + installPopupButton(); + add(m_popupButton,BorderLayout.EAST); + m_popupButton.addActionListener(m_listener); + m_popupButton.addMouseListener(m_listener); + m_popupVisible = false; + } + } + + public JLabel getLabel() { + return jLabel; + } + + class Listener implements ActionListener,MouseListener,PopupMenuListener { + // Implementation of ActionListener + public void actionPerformed(ActionEvent e) { + // Ignore action, when Popup is closing + log("Action Performed"); + if (m_pressed && m_closing) { + m_closing = false; + return; + } + if ( !m_popupVisible && !m_closing) { + log("Open"); + showPopup(); + } else { + m_popupVisible = false; + closePopup(); + } // end of else + } + + private void log(String string) { + // System.out.println(System.currentTimeMillis() / 1000 + "m_visible:" + m_popupVisible + " closing: " + m_closing + " [" + string + "]"); + } + // Implementation of MouseListener + public void mousePressed(MouseEvent evt) { + m_pressed = true; + m_closing = false; + log("Pressed"); + } + public void mouseClicked(MouseEvent evt) { + m_closing = false; + } + public void mouseReleased(MouseEvent evt) { + m_pressed = false; + m_closing = false; + log("Released"); + } + public void mouseEntered(MouseEvent me) { + } + public void mouseExited(MouseEvent me) { + } + + + // Implementation of PopupListener + public void popupMenuWillBecomeVisible(PopupMenuEvent e) { + m_popupVisible = true; + } + + public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { + m_popupVisible = false; + m_closing = true; + m_popupButton.requestFocus(); + m_popupButton.setChar('v'); + log("Invisible"); + final boolean oldState = m_popupButton.isEnabled(); + m_popupButton.setEnabled( false); + final Timer timer = new Timer(true); + TimerTask task = new TimerTask() + { + public void run() + { + m_popupButton.setEnabled( oldState); + } + }; + timer.schedule(task, 100); + } + + public void popupMenuCanceled(PopupMenuEvent e) { + m_popupVisible = false; + m_closing = true; + m_popupButton.requestFocus(); + log("Cancel"); + } + + }; + + private void installPopupButton() { + // Maybe we could use the combobox drop-down button here. + m_popupButton = new RaplaArrowButton('v'); + } + + public void setFont(Font font) { + super.setFont(font); + // Method called during constructor? + if (font == null) + return; + if (m_editorComponent != null) + m_editorComponent.setFont(font); + if (m_popupButton != null && m_isDropDown) { + int size = (int) getPreferredSize().getHeight(); + m_popupButton.setSize(size,size); + } + } + + public void setEnabled( boolean enabled ) { + super.setEnabled( enabled ); + if ( m_editorComponent != null ) { + m_editorComponent.setEnabled( enabled ); + } + if ( m_popupButton != null ) { + m_popupButton.setEnabled ( enabled ); + } + } + + protected void showPopup() { + if (m_popup == null) + createPopup(); + + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + Dimension menuSize = m_popup.getPreferredSize(); + Dimension buttonSize = m_popupButton.getSize(); + Point location = m_popupButton.getLocationOnScreen(); + int diffx= buttonSize.width - menuSize.width; + if (location.x + diffx<0) + diffx = - location.x; + int diffy= buttonSize.height; + if (location.y + diffy + menuSize.height > screenSize.height) + diffy = screenSize.height - menuSize.height - location.y; + m_popup.show(m_popupButton,diffx,diffy); + m_popup.requestFocus(); + m_popupButton.setChar('^'); + } + + protected void closePopup() { + if (m_popup != null && m_popup.isVisible()) { + // #Workaround for JMenuPopup-Bug in JDK 1.4 + // intended behaviour: m_popup.setVisible(false); + + javax.swing.SwingUtilities.invokeLater(new Runnable() { + public void run() { + // Show JMenuItem and fire a mouse-click + cardLayout.last(m_popup); + menuItem.menuSelectionChanged(true); + menuItem.dispatchEvent(new MouseEvent(m_popup + ,MouseEvent.MOUSE_RELEASED + ,System.currentTimeMillis() + ,0 + ,0 + ,0 + ,1 + ,false)); + // show original popup again + cardLayout.first(m_popup); + m_popupButton.requestFocus(); + } + }); + } + m_popupVisible = false; + } + + private JMenuItem menuItem; + private CardLayout cardLayout; + + class MyPopup extends JPopupMenu { + private static final long serialVersionUID = 1L; + + MyPopup() { + super(); + } + public void menuSelectionChanged(boolean isIncluded) { + closePopup(); + } + } + + private void createPopup() { + m_popup = new JPopupMenu(); + /* try { + PopupMenu.class.getMethod("isPopupTrigger",new Object[]{}); + } catch (Exception ex) { + m_popup.setLightWeightPopupEnabled(true); + }*/ + m_popup.setBorder(null); + cardLayout = new CardLayout(); + m_popup.setLayout(cardLayout); + m_popup.setInvoker(this); + m_popup.add(getPopupComponent(),"0"); + menuItem = new JMenuItem(""); + m_popup.add(menuItem,"1"); + m_popup.setBorderPainted(true); + m_popup.addPopupMenuListener(m_listener); + } + + /** the component that should apear in the popup menu */ + protected abstract JComponent getPopupComponent(); +} + + diff --git a/Rapla/src/org/rapla/components/calendar/RaplaNumber.java b/Rapla/src/org/rapla/components/calendar/RaplaNumber.java new file mode 100644 index 0000000..afff1fc --- /dev/null +++ b/Rapla/src/org/rapla/components/calendar/RaplaNumber.java @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2003 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.calendar; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.EventListenerList; +/** The RaplaNumber is an adapter for NumberField. + * Warning! Currently only Longs are supported. + * @see NumberField + */ +public final class RaplaNumber extends JPanel{ + private static final long serialVersionUID = 1L; + + NumberField m_numberField = null; + public static Number ZERO = new Long(0); + public static Number ONE = new Long(1); + public static Number DEFAULT_STEP_SIZE = new Long(1); + EventListenerList m_listenerList; + Listener m_listener = new Listener(); + Number m_emptyValue = ZERO; + JPanel m_buttonPanel = new JPanel(); + RaplaArrowButton m_upButton = new RaplaArrowButton('+', 15);//'^',10,false); + RaplaArrowButton m_downButton = new RaplaArrowButton('-', 15);//new NavButton('v',10,false); + + /** currently only Longs are supported */ + public RaplaNumber() { + this( null, null, null, false); + } + + public RaplaNumber(Number value,Number minimum,Number maximum,boolean isNullPermitted) { + m_numberField = new NumberField(minimum,maximum,DEFAULT_STEP_SIZE.intValue(),10); + m_numberField.setNumber(value); + m_numberField.setNullPermitted(isNullPermitted); + if (minimum != null && minimum.longValue()>0) + m_emptyValue = minimum; + else if (maximum != null && maximum.longValue()<0) + m_emptyValue = maximum; + m_buttonPanel.setLayout(new GridLayout(2,1)); + m_buttonPanel.add(m_upButton); + m_upButton.setBorder( null); + m_buttonPanel.add(m_downButton); + m_downButton.setBorder(null); + m_buttonPanel.setMinimumSize(new Dimension(18,20)); + m_buttonPanel.setPreferredSize(new Dimension(18,20)); + m_buttonPanel.setBorder(BorderFactory.createEtchedBorder()); + m_upButton.setClickRepeatDelay(50); + m_downButton.setClickRepeatDelay(50); + m_upButton.addActionListener(m_listener); + m_downButton.addActionListener(m_listener); + setLayout(new BorderLayout()); + add(m_numberField,BorderLayout.CENTER); + add(m_buttonPanel,BorderLayout.EAST); + + m_upButton.setFocusable( false); + m_downButton.setFocusable( false); + } + + public void setFont(Font font) { + super.setFont(font); + // Method called during constructor? + if (m_numberField == null || font == null) + return; + m_numberField.setFont(font); + } + + public void setColumns(int columns) { + m_numberField.setColumns(columns); + } + + public NumberField getNumberField() { + return m_numberField; + } + + public void setEnabled( boolean enabled ) { + super.setEnabled( enabled ); + if ( m_numberField != null ) { + m_numberField.setEnabled( enabled ); + } + if ( m_upButton != null ) { + m_upButton.setEnabled ( enabled ); + m_downButton.setEnabled ( enabled ); + } + } + + /** currently only Longs are supported */ + public void setNumber(Number newValue) { + m_numberField.setNumber(newValue); + } + + public Number getNumber() { + return m_numberField.getNumber(); + } + + public void addChangeListener(ChangeListener changeListener) { + if (m_listenerList == null) { + m_listenerList = new EventListenerList(); + m_numberField.addChangeListener(m_listener); + } + m_listenerList.add(ChangeListener.class,changeListener); + } + + public void removeChangeListener(ChangeListener changeListener) { + if (m_listenerList == null) { + return; + } + m_listenerList.remove(ChangeListener.class,changeListener); + } + + + class Listener implements ChangeListener,ActionListener { + public void actionPerformed(ActionEvent evt) { + m_numberField.requestFocus(); + if (evt.getSource() == m_upButton) { + m_numberField.increase(); + } + if (evt.getSource() == m_downButton) { + m_numberField.decrease(); + } + SwingUtilities.invokeLater( new Runnable() { + + public void run() { + stateChanged(null); + m_numberField.selectAll(); + } + } + + ); + } + + public void stateChanged(ChangeEvent originalEvent) { + if (m_listenerList == null) + return; + + + ChangeEvent evt = new ChangeEvent(RaplaNumber.this); + Object[] listeners = m_listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==ChangeListener.class) { + ((ChangeListener)listeners[i+1]).stateChanged(evt); + } + } + } + + } + + + +} diff --git a/Rapla/src/org/rapla/components/calendar/RaplaTime.java b/Rapla/src/org/rapla/components/calendar/RaplaTime.java new file mode 100644 index 0000000..e6d8b66 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendar/RaplaTime.java @@ -0,0 +1,555 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.components.calendar; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.MediaTracker; +import java.awt.Point; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.image.BufferedImage; +import java.net.URL; +import java.text.DateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +import javax.swing.BorderFactory; +import javax.swing.DefaultListCellRenderer; +import javax.swing.DefaultListModel; +import javax.swing.ImageIcon; +import javax.swing.JComponent; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollBar; +import javax.swing.JScrollPane; +import javax.swing.ListSelectionModel; +import javax.swing.MenuElement; +import javax.swing.MenuSelectionManager; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +/** A ComboBox like time chooser. + * It is localizable and it uses swing-components. + *

    The combobox editor is a {@link TimeField}. If the ComboBox-Button + * is pressed a TimeSelectionList will drop down.

    + * @author Christopher Kohlhaas + */ +public final class RaplaTime extends RaplaComboBox { + private static final long serialVersionUID = 1L; + + protected TimeField m_timeField; + protected TimeList m_timeList; + protected TimeModel m_timeModel; + protected Collection m_listenerList = new ArrayList(); + private Date m_lastTime; + private int m_visibleRowCount = -1; + private int m_rowsPerHour = 4; + private TimeRenderer m_renderer; + private static Image clock; + boolean m_showClock; + /** Create a new TimeBox with the default locale. */ + public RaplaTime() { + this(Locale.getDefault()); + } + + /** Create a new TimeBox with the specified locale. */ + public RaplaTime(Locale locale) { + this(locale,TimeZone.getDefault(),true, true); + } + + /** Create a new TimeBox with the specified locale and timeZone. */ + public RaplaTime(Locale locale,TimeZone timeZone) { + this(locale,timeZone,true, true); + } + + /** Create a new TimeBox with the specified locale and timeZone. The + isDropDown flag specifies if times could be selected + via a drop-down-box. + */ + public RaplaTime(Locale locale,TimeZone timeZone,boolean isDropDown, boolean showClock) { + super(isDropDown,new TimeField(locale,timeZone)); + m_showClock = showClock; + m_timeModel = new TimeModel(locale, timeZone); + m_timeField = (TimeField) m_editorComponent; + Listener listener = new Listener(); + m_timeField.addChangeListener(listener); + m_timeModel.addDateChangeListener(listener); + m_lastTime = m_timeModel.getTime(); + + if ( showClock ) + { + if ( clock == null ) + { + URL url = RaplaTime.class.getResource("clock.png"); + if ( url != null ) + { + clock =Toolkit.getDefaultToolkit().createImage(url ); + MediaTracker m = new MediaTracker(this); + m.addImage(clock, 0); + try { m.waitForID(0); } catch (InterruptedException ex) {} + } + } + + getLabel().setIcon( new ImageIcon( createClockImage())); + getLabel().setBorder( BorderFactory.createEmptyBorder(0,0,0,1)); + } + + } + + static Color HOUR_POINTER = new Color( 40,40,100); + static Color MINUTE_POINTER = new Color( 100,100,180); + + protected Image createClockImage() { + BufferedImage image = new BufferedImage( 17, 17, BufferedImage.TYPE_INT_ARGB); + Calendar calendar = Calendar.getInstance(getTimeZone(),m_timeModel.getLocale()); + calendar.setTime( m_timeModel.getTime()); + int hourOfDay = calendar.get( Calendar.HOUR_OF_DAY) % 12; + int minute = calendar.get( Calendar.MINUTE); + + Graphics g = image.getGraphics(); + + double hourPos = (hourOfDay * 60 + minute - 180) / (60.0 * 12) * 2 * Math.PI ; + double minutePos = (minute -15) / 60.0 * 2 * Math.PI; + int xhour = (int) (Math.cos( hourPos) * 4.5); + int yhour = (int) (Math.sin( hourPos ) * 4.5 ); + int xminute = (int) (Math.cos( minutePos ) * 6.5 ); + int yminute = (int) (Math.sin( minutePos) * 6.5); + g.drawImage( clock,0,0,17,17, null); + g.setColor( HOUR_POINTER); + int centerx = 8; + int centery = 8; + g.drawLine( centerx, centery, centerx + xhour,centery + yhour); + g.setColor( MINUTE_POINTER); + g.drawLine( centerx, centery, centerx + xminute,centery +yminute); + return image; + } + /** The granularity of the selection rows: + *
      + *
    • 1: 1 rows per hour = 1 Hour
    • + *
    • 2: 2 rows per hour = 1/2 Hour
    • + *
    • 2: 3 rows per hour = 20 Minutes
    • + *
    • 4: 4 rows per hour = 15 Minutes
    • + *
    • 6: 6 rows per hour = 10 Minutes
    • + *
    • 12: 12 rows per hour = 5 Minutes
    • + *
    + */ + public void setRowsPerHour(int rowsPerHour) { + m_rowsPerHour = rowsPerHour; + if (m_timeList != null) { + throw new IllegalStateException("Property can only be set during initialization."); + } + } + + /** @see #setRowsPerHour */ + public int getRowsPerHour() { + return m_rowsPerHour; + } + + class Listener implements ChangeListener,DateChangeListener { + // Implementation of ChangeListener + public void stateChanged(ChangeEvent evt) { + validateEditor(); + } + + public void dateChanged(DateChangeEvent evt) { + closePopup(); + if (needSync()) + m_timeField.setTime(evt.getDate()); + if (m_lastTime == null || !m_lastTime.equals(evt.getDate())) + fireTimeChanged(evt.getDate()); + m_lastTime = evt.getDate(); + if ( clock != null && m_showClock) + { + getLabel().setIcon( new ImageIcon( createClockImage())); + } + } + } + + /** test if we need to synchronize the dateModel and the m_timeField*/ + private boolean needSync() { + return (m_timeField.getTime() != null && !m_timeModel.sameTime(m_timeField.getTime())); + } + + protected void validateEditor() { + if (needSync()) + m_timeModel.setTime(m_timeField.getTime()); + } + + /** the number of visble rows in the drop-down menu.*/ + public void setVisibleRowCount(int count) { + m_visibleRowCount = count; + if (m_timeList != null) + m_timeList.getList().setVisibleRowCount(count); + } + + public void setFont(Font font) { + super.setFont(font); + // Method called during constructor? + if (m_timeList == null || font == null) + return; + m_timeList.setFont(font); + } + + public void setLocale(Locale locale) { + super.setLocale(locale); + if (m_timeField != null) + m_timeField.setLocale(locale); + } + + public void setTimeZone(TimeZone zone) { + m_timeModel.setTimeZone(zone); + m_timeField.setTimeZone(zone); + } + + public TimeZone getTimeZone() { + return m_timeField.getTimeZone(); + } + + /** Set the time relative to the given timezone. + * The date,month and year values will be ignored. + */ + public void setTime(Date time) { + m_timeModel.setTime(time); + } + + /** Set the time relative to the given timezone. + */ + public void setTime(int hour, int minute) { + m_timeModel.setTime(hour,minute); + } + + /** Parse this date with a calendar-object set to the correct + time-zone to get the hour,minute and second. The + date,month and year values should be ignored. + */ + public Date getTime() { + return m_timeModel.getTime(); + } + + protected void showPopup() { + validateEditor(); + super.showPopup(); + m_timeList.selectTime(m_timeField.getTime()); + } + + /** registers new DateChangedListener for this component. + * An DateChangedEvent will be fired to every registered DateChangedListener + * when the a different time is selected. + * @see DateChangeListener + * @see DateChangeEvent + */ + public void addDateChangeListener(DateChangeListener listener) { + m_listenerList.add(listener); + } + + /** removes a listener from this component.*/ + public void removeDateChangeListener(DateChangeListener listener) { + m_listenerList.remove(listener); + } + + public DateChangeListener[] getDateChangeListeners() { + return (DateChangeListener[])m_listenerList.toArray(new DateChangeListener[]{}); + } + + protected void fireTimeChanged(Date date) { + DateChangeListener[] listeners = getDateChangeListeners(); + if (listeners.length == 0) + return; + DateChangeEvent evt = new DateChangeEvent(this,date); + for (int i = 0; i=0) + m_timeList.getList().setVisibleRowCount(m_visibleRowCount); + } + m_timeList.setTimeRenderer( m_renderer ); + return m_timeList; + } + + public void setTimeRenderer(TimeRenderer renderer) { + m_renderer = renderer; + } + +} + + +class TimeList extends JPanel implements MenuElement,MouseListener,MouseMotionListener { + private static final long serialVersionUID = 1L; + + JScrollPane scrollPane = new JScrollPane(); + NavButton upButton = new NavButton('^',10); + NavButton downButton = new NavButton('v',10); + JList m_list; + DateFormat m_format; + TimeModel m_timeModel; + int m_rowsPerHour = 4; + int m_minutesPerRow = 60 / m_rowsPerHour;; + TimeRenderer m_renderer; + + private double getRowHeight() { + return m_list.getVisibleRect().getHeight()/m_list.getVisibleRowCount(); + } + + public TimeList(int rowsPerHour) { + super(); + this.setLayout( new BorderLayout()); + JPanel upPane = new JPanel(); + upPane.setLayout( new BorderLayout()); + upPane.add( upButton, BorderLayout.CENTER ); + JPanel downPane = new JPanel(); + downPane.setLayout( new BorderLayout()); + downPane.add( downButton, BorderLayout.CENTER ); + upButton.addActionListener( new ActionListener() { + public void actionPerformed( ActionEvent e ) + { + int direction = (int)- getRowHeight() * (m_list.getVisibleRowCount() -1); + JScrollBar bar = scrollPane.getVerticalScrollBar(); + int value = Math.min( Math.max( 0, bar.getValue() + direction ), bar.getMaximum()); + scrollPane.getVerticalScrollBar().setValue( value ); + } + + }); + downButton.addActionListener( new ActionListener() { + public void actionPerformed( ActionEvent e ) + { + int direction = (int) getRowHeight() * (m_list.getVisibleRowCount() -1) ; + JScrollBar bar = scrollPane.getVerticalScrollBar(); + int value = Math.min( Math.max( 0, bar.getValue() + direction ), bar.getMaximum()); + scrollPane.getVerticalScrollBar().setValue( value ); + } + + }); + + /* + upPane.addMouseListener( new Mover( -1)); + upButton.addMouseListener( new Mover( -1)); + downPane.addMouseListener( new Mover( 1)); + downButton.addMouseListener( new Mover( 1)); + */ + //upPane.setPreferredSize( new Dimension(0,0)); + //downPane.setPreferredSize( new Dimension(0,0)); + this.add(upPane, BorderLayout.NORTH); + this.add( scrollPane, BorderLayout.CENTER); + this.add( downPane, BorderLayout.SOUTH); + scrollPane.setVerticalScrollBarPolicy( JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); + scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER ); + scrollPane.getVerticalScrollBar().setEnabled( false ); + scrollPane.getVerticalScrollBar().setSize( new Dimension(0,0)); + scrollPane.getVerticalScrollBar().setPreferredSize( new Dimension(0,0)); + scrollPane.getVerticalScrollBar().setMaximumSize( new Dimension(0,0)); + scrollPane.getVerticalScrollBar().setMinimumSize( new Dimension(0,0)); + m_rowsPerHour = rowsPerHour; + m_minutesPerRow = 60 / m_rowsPerHour; + //this.setLayout(new BorderLayout()); + m_list = new JList(); + scrollPane.setViewportView( m_list); + m_list.setBackground(this.getBackground()); + //JScrollPane scrollPane = new JScrollPane(m_list, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + m_list.setVisibleRowCount(8); + m_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + m_list.addMouseListener(this); + m_list.addMouseMotionListener(this); + m_list.setCellRenderer( new DefaultListCellRenderer() { + private static final long serialVersionUID = 1L; + + public Component getListCellRendererComponent( JList list, Object value, int index, boolean isSelected, boolean cellHasFocus ) + { + Component component = super.getListCellRendererComponent( list, value, index, isSelected,cellHasFocus ); + if ( m_renderer!= null && !isSelected && !cellHasFocus ) { + int hour = getHourForIndex( index ); + int minute = getMinuteForIndex( index ); + Color color = m_renderer.getBackgroundColor( hour, minute ); + if ( color != null ) { + component.setBackground( color ); + } + } + return component; + } + }); + } + + public void setModel(TimeModel model,DateFormat format) { + m_timeModel = model; + m_format = (DateFormat) format.clone(); + Calendar calendar = Calendar.getInstance(m_format.getTimeZone(),model.getLocale()); + DefaultListModel listModel = new DefaultListModel(); + for (int i=0;i<24 * m_rowsPerHour;i++) { + calendar.set(Calendar.HOUR_OF_DAY,i/m_rowsPerHour ); + calendar.set(Calendar.MINUTE,(i%m_rowsPerHour) * m_minutesPerRow); + listModel.addElement(" " + m_format.format(calendar.getTime()) + " "); + } + m_list.setModel(listModel); + int pos = (int)getPreferredSize().getWidth()/2 - 5; + upButton.setLeftPosition( pos); + downButton.setLeftPosition( pos ); + } + + public void setTimeRenderer(TimeRenderer renderer) { + m_renderer = renderer; + } + + public JList getList() { + return m_list; + } + + public void setFont(Font font) { + super.setFont(font); + if (m_list == null || font == null) + return; + m_list.setFont(font); + int pos = (int)getPreferredSize().getWidth()/2 - 5; + upButton.setLeftPosition( pos); + downButton.setLeftPosition( pos ); + } + + /** Implementation-specific. Should be private.*/ + public void mousePressed(MouseEvent e) { + ok(); + } + /** Implementation-specific. Should be private.*/ + public void mouseClicked(MouseEvent e) { + } + /** Implementation-specific. Should be private.*/ + public void mouseReleased(MouseEvent e) { + } + /** Implementation-specific. Should be private.*/ + public void mouseEntered(MouseEvent me) { + } + /** Implementation-specific. Should be private.*/ + public void mouseExited(MouseEvent me) { + } + + + private int lastIndex = -1; + private int lastY= -1; + public void mouseDragged(MouseEvent e) { + } + public void mouseMoved(MouseEvent e) { + if (e.getY() == lastY) + return; + lastY = e.getY(); + Point p = new Point(e.getX(),e.getY()); + int index = m_list.locationToIndex(p); + if (index == lastIndex) + return; + lastIndex = index; + m_list.setSelectedIndex(index); + } + + public void selectTime(Date time) { + Calendar calendar = Calendar.getInstance(m_timeModel.getTimeZone(),m_timeModel.getLocale()); + calendar.setTime(time); + int index = (calendar.get(Calendar.HOUR_OF_DAY)) * m_rowsPerHour + + (calendar.get(Calendar.MINUTE) / m_minutesPerRow); + select(index); + } + + + private void select(int index) { + m_list.setSelectedIndex(index); + m_list.ensureIndexIsVisible(Math.max(index -3,0)); + m_list.ensureIndexIsVisible(Math.min(index + 3,m_list.getModel().getSize() -1)); + } + + + // Start of MenuElement implementation + public Component getComponent() { + return this; + } + public MenuElement[] getSubElements() { + return new MenuElement[0]; + } + + public void menuSelectionChanged(boolean isIncluded) { + } + + public void processKeyEvent(KeyEvent event, MenuElement[] path, MenuSelectionManager manager) { + int index; + if (event.getID() == KeyEvent.KEY_PRESSED) { + switch (event.getKeyCode()) { + case (KeyEvent.VK_KP_UP): + case (KeyEvent.VK_UP): + index = m_list.getSelectedIndex(); + if (index > 0) + select(index - 1); + break; + case (KeyEvent.VK_KP_DOWN): + case (KeyEvent.VK_DOWN): + index = m_list.getSelectedIndex(); + if (index = 0) { + calendar.set(Calendar.HOUR_OF_DAY,hour ); + calendar.set(Calendar.MINUTE,minute); + calendar.set(Calendar.SECOND,0); + calendar.set(Calendar.MILLISECOND,0); + m_timeModel.setTime(calendar.getTime()); + } + } + + public void processMouseEvent(MouseEvent event, MenuElement[] path, MenuSelectionManager manager) { + } + // End of MenuElement implementation +} + + diff --git a/Rapla/src/org/rapla/components/calendar/TimeField.java b/Rapla/src/org/rapla/components/calendar/TimeField.java new file mode 100644 index 0000000..32fcadf --- /dev/null +++ b/Rapla/src/org/rapla/components/calendar/TimeField.java @@ -0,0 +1,302 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.calendar; + +import java.text.DateFormat; +import java.text.FieldPosition; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; +/** The TimeField only accepts characters that are part of DateFormat.getTimeInstance(DateFormat.SHORT,locale). + * The input blocks are [hour,minute,am_pm] or [hour_of_day,minute] + * depending on the selected locale. You can use the keyboard to + * navigate between the blocks or to increment/decrement the blocks. + * @see AbstractBlockField + */ + +final public class TimeField extends AbstractBlockField { + private static final long serialVersionUID = 1L; + + private DateFormat m_outputFormat; + private DateFormat m_parsingFormat; + private Calendar m_calendar; + private int m_rank[] = null; + private char[] m_separators; + private boolean m_useAM_PM = false; + private boolean americanAM_PM_character = false; + + public TimeField() { + this(Locale.getDefault()); + } + + public TimeField(Locale locale) { + this(locale, TimeZone.getDefault()); + } + + public TimeField(Locale locale,TimeZone timeZone) { + super(); + m_calendar = Calendar.getInstance(timeZone, locale); + super.setLocale(locale); + setFormat(); + setTime(new Date()); + } + + + public void setLocale(Locale locale) { + super.setLocale(locale); + if (locale != null && getTimeZone() != null) + setFormat(); + } + + private void setFormat() { + m_parsingFormat = DateFormat.getTimeInstance(DateFormat.SHORT, getLocale()); + m_parsingFormat.setTimeZone(getTimeZone()); + Date oldDate = m_calendar.getTime(); + m_calendar.set(Calendar.HOUR_OF_DAY,0); + m_calendar.set(Calendar.MINUTE,0); + String formatStr = m_parsingFormat.format(m_calendar.getTime()); + + FieldPosition minutePos = new FieldPosition(DateFormat.MINUTE_FIELD); + m_parsingFormat.format(m_calendar.getTime(), new StringBuffer(),minutePos); + + FieldPosition hourPos = new FieldPosition(DateFormat.HOUR0_FIELD); + StringBuffer hourBuf = new StringBuffer(); + m_parsingFormat.format(m_calendar.getTime(), hourBuf,hourPos); + + FieldPosition hourPos1 = new FieldPosition(DateFormat.HOUR1_FIELD); + StringBuffer hourBuf1 = new StringBuffer(); + m_parsingFormat.format(m_calendar.getTime(), hourBuf1,hourPos1); + + FieldPosition amPmPos = new FieldPosition(DateFormat.AM_PM_FIELD); + m_parsingFormat.format(m_calendar.getTime(), new StringBuffer(),amPmPos); + + String zeroDigit = m_parsingFormat.getNumberFormat().format(0); + int zeroPos = hourBuf.toString().indexOf(zeroDigit,hourPos.getBeginIndex()); + + // 0:30 or 12:30 + boolean zeroBased = (zeroPos == 0); + String testFormat = m_parsingFormat.format( m_calendar.getTime() ).toLowerCase(); + int mp = minutePos.getBeginIndex(); + int ap = amPmPos.getBeginIndex(); + int hp = Math.max( hourPos.getBeginIndex(), hourPos1.getBeginIndex() ); + + int pos[] = null; + + // Use am/pm + if (amPmPos.getEndIndex()>0) { + m_useAM_PM = true; + americanAM_PM_character = m_useAM_PM && (testFormat.indexOf( "am" )>=0 || testFormat.indexOf( "pm" )>=0); + // System.out.println(formatStr + " hour:"+hp+" minute:"+mp+" ampm:"+ap); + if (hp<0 || mp<0 || ap<0 || formatStr == null) { + throw new IllegalArgumentException("Can't parse the time-format for this locale: " + formatStr); + } + // quick and diry sorting + if (mp=1.4 + You can safely exclude it if you build with JDK <1.4 + */ +class AWTAdapterFactoryImpl extends AWTAdapterFactory { + public Observable createMouseWheelObservable(Component component) { + return new MyWheelListener(component); + } + public FocusAdapter createFocusAdapter(Container container) { + return new MyFocusAdapter(container); + } +} + +class MyWheelListener extends Observable implements MouseWheelListener { + MyWheelListener(Component component) { + component.addMouseWheelListener(this); + } + + public void mouseWheelMoved(MouseWheelEvent evt) { + setChanged(); + notifyObservers(new Long(evt.getWheelRotation())); + } + +} + +class MyFocusAdapter implements FocusAdapter { + Container c; + MyFocusAdapter(Container container) { + this.c = container; + } + public boolean isFocusOwner() { + return c.isFocusOwner(); + } + public boolean isFocusable() { + return c.isFocusable(); + } + public void setFocusCycleRoot(boolean bEnable) { + c.setFocusCycleRoot(bEnable); + } + public void setFocusable(boolean bFocusable) { + c.setFocusable(bFocusable); + } + public void transferFocusUpCycle() { + c.transferFocusUpCycle(); + } + public void transferFocusBackward() { + c.transferFocusBackward(); + } + public boolean requestFocusInWindow() { + return c.requestFocusInWindow(); + } + + public void ignoreFocusComponents(FocusTester focusTester) { + c.setFocusTraversalPolicy(new MyFocusTraversalPolicy(focusTester)); + } +} + +class MyFocusTraversalPolicy extends LayoutFocusTraversalPolicy { + private static final long serialVersionUID = 1L; + + FocusTester focusTester; + MyFocusTraversalPolicy( FocusTester focusTester) { + this.focusTester = focusTester; + } + + protected boolean accept(Component component) { + if (!focusTester.accept(component)) + return false; + return super.accept(component); + } +} + diff --git a/Rapla/src/org/rapla/components/calendar/jdk14adapter/FocusAdapter.java b/Rapla/src/org/rapla/components/calendar/jdk14adapter/FocusAdapter.java new file mode 100644 index 0000000..27850d9 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendar/jdk14adapter/FocusAdapter.java @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.calendar.jdk14adapter; + +public interface FocusAdapter { + boolean isFocusOwner(); + boolean isFocusable(); + void setFocusable(boolean bFocusable); + void transferFocusUpCycle(); + void transferFocusBackward(); + boolean requestFocusInWindow(); + void ignoreFocusComponents(FocusTester focusTester); + void setFocusCycleRoot(boolean bEnable); +} diff --git a/Rapla/src/org/rapla/components/calendar/jdk14adapter/FocusTester.java b/Rapla/src/org/rapla/components/calendar/jdk14adapter/FocusTester.java new file mode 100644 index 0000000..e6dd3d1 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendar/jdk14adapter/FocusTester.java @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.calendar.jdk14adapter; + +import java.awt.Component; + +public interface FocusTester { + boolean accept(Component component); +} diff --git a/Rapla/src/org/rapla/components/calendar/jdk14adapter/package.html b/Rapla/src/org/rapla/components/calendar/jdk14adapter/package.html new file mode 100644 index 0000000..7f6c90b --- /dev/null +++ b/Rapla/src/org/rapla/components/calendar/jdk14adapter/package.html @@ -0,0 +1,7 @@ + +Adapter for using the new MouseWheel and Focus features in JDK 1.4.0 without +breaking backwards compatibility with 1.2 or 1.3. + + + + diff --git a/Rapla/src/org/rapla/components/calendar/package.html b/Rapla/src/org/rapla/components/calendar/package.html new file mode 100644 index 0000000..a6ebded --- /dev/null +++ b/Rapla/src/org/rapla/components/calendar/package.html @@ -0,0 +1,7 @@ + +Contains widgets for date- and time- selection. +This package is independant from the rest of rapla. + + + + diff --git a/Rapla/src/org/rapla/components/calendarview/AbstractGroupStrategy.java b/Rapla/src/org/rapla/components/calendarview/AbstractGroupStrategy.java new file mode 100644 index 0000000..f461380 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendarview/AbstractGroupStrategy.java @@ -0,0 +1,256 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.calendarview; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +import org.rapla.components.util.DateTools; +import org.rapla.components.util.SmallIntMap; + +/** Arranges blocks into groups, and tries to place one group into one slot. + The subclass must overide the group method to perform the grouping on a given + list of blocks. +*/ +public abstract class AbstractGroupStrategy implements BuildStrategy { + boolean m_sortSlotsBySize; + public static long MILLISECONDS_PER_DAY = 24 * 3600 * 1000; + private boolean m_fixedSlots; + private boolean m_conflictResolving; + + + Comparator blockComparator = new Comparator() { + public int compare(Object o1,Object o2) { + Block b1 = (Block) o1; + Block b2 = (Block) o2; + int result = b1.getStart().compareTo(b2.getStart()); + if (result != 0) + return result; + else + return -1 * b1.getEnd().compareTo(b2.getEnd()); + + } + }; + + Comparator slotComparator = new Comparator() { + public int compare(Object o1,Object o2) { + List s1 = (List) o1; + List s2 = (List) o2; + if (s1.size() == 0 || s2.size() ==0) { + if (s1.size() == s2.size()) + return 0; + else + return s1.size() < s2.size() ? -1 : 1; + } + + Block b1 = (Block) s1.get(0); + Block b2 = (Block) s2.get(0); + return b1.getStart().compareTo(b2.getStart()); + } + }; + + public void build(CalendarView wv, List blocks) + { + SmallIntMap days = new SmallIntMap(); + Calendar calendar = Calendar.getInstance(wv.getTimeZone()); + Iterator it = blocks.iterator(); + long timespan = wv.getEndDate().getTime() - wv.getStartDate().getTime(); + // guess maxDays + int maxDays = (int) (timespan / MILLISECONDS_PER_DAY + 2); + while (it.hasNext()) { + Block block = (Block) it.next(); + calendar.setTime(block.getStart()); + int day = calendar.get(Calendar.DAY_OF_YEAR)%maxDays; + List list = (List) days.get(day); + if (list == null) { + list = new ArrayList(); + days.put(day,list); + } + list.add(block); + } + for (int day=0;day + WeekdayMapper mapper = new WeekdayMapper(); + // print name of Sunday + System.out.println(mapper.getName(Calendar.SUNDAY)); + // Create a weekday ComboBox + JComboBox comboBox = new JComboBox(); + comboBox.setModel(new DefaultComboBoxModel(mapper.getNames())); + // select sunday + comboBox.setSelectedIndex(mapper.getIndexForDay(Calendar.SUNDAY)); + // weekday == Calendar.SUNDAY + int weekday = mapper.getDayForIndex(comboBox.getSelectedIndex()); + + +*/ +public class MonthMapper { + String[] monthNames; + + public MonthMapper() { + this(Locale.getDefault()); + } + + public MonthMapper(Locale locale) { + monthNames = new String[12]; + SimpleDateFormat format = new SimpleDateFormat("MMMMMM",locale); + Calendar calendar = Calendar.getInstance(locale); + for (int i=0;i<12;i++) { + calendar.set(Calendar.MONTH,i); + monthNames[i] = format.format(calendar.getTime()); + } + } + + public String[] getNames() { + return monthNames; + } + + /** month are 0 based */ + public String getName(int month) { + return getNames()[month]; + } + + +} + diff --git a/Rapla/src/org/rapla/components/calendarview/WeekdayMapper.java b/Rapla/src/org/rapla/components/calendarview/WeekdayMapper.java new file mode 100644 index 0000000..5b54e84 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendarview/WeekdayMapper.java @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas, Bettina Lademann | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.calendarview; + +import java.util.Calendar; +import java.util.Locale; +import java.text.SimpleDateFormat; + + +/** maps weekday names to Calendar.DAY_OF_WEEK. + Example: +
    +       WeekdayMapper mapper = new WeekdayMapper();
    +       // print name of Sunday
    +       System.out.println(mapper.getName(Calendar.SUNDAY));
    +       // Create a weekday ComboBox
    +       JComboBox comboBox = new JComboBox();
    +       comboBox.setModel(new DefaultComboBoxModel(mapper.getNames()));
    +       // select sunday
    +       comboBox.setSelectedIndex(mapper.getIndexForDay(Calendar.SUNDAY));
    +       // weekday == Calendar.SUNDAY
    +       int weekday = mapper.getDayForIndex(comboBox.getSelectedIndex());
    +   
    + +*/ +public class WeekdayMapper { + String[] weekdayNames; + int[] weekday2index; + int[] index2weekday; + + public WeekdayMapper() { + this(Locale.getDefault()); + } + + public WeekdayMapper(Locale locale) { + weekdayNames = new String[7]; + weekday2index = new int[8]; + index2weekday = new int[8]; + SimpleDateFormat format = new SimpleDateFormat("EEEEEE",locale); + Calendar calendar = Calendar.getInstance(locale); + calendar.set(Calendar.DAY_OF_WEEK, calendar.getFirstDayOfWeek()); + for (int i=0;i<7;i++) { + weekday2index[calendar.get(Calendar.DAY_OF_WEEK)] = i; + index2weekday[i] = calendar.get(Calendar.DAY_OF_WEEK); + weekdayNames[i] = format.format(calendar.getTime()); + calendar.add(Calendar.DATE,1); + } + } + + public String[] getNames() { + return weekdayNames; + } + + public String getName(int weekday) { + return getNames()[indexForDay(weekday)]; + } + + public int dayForIndex(int index) { + return index2weekday[index]; + } + + public int indexForDay(int weekday) { + return weekday2index[weekday]; + } + +} + diff --git a/Rapla/src/org/rapla/components/calendarview/html/AbstractHTMLView.java b/Rapla/src/org/rapla/components/calendarview/html/AbstractHTMLView.java new file mode 100644 index 0000000..ae0deb8 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendarview/html/AbstractHTMLView.java @@ -0,0 +1,145 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.components.calendarview.html; + +import java.util.*; + +import org.rapla.components.calendarview.Block; +import org.rapla.components.calendarview.Builder; +import org.rapla.components.calendarview.CalendarView; +import org.rapla.components.calendarview.WeekdayMapper; +import org.rapla.components.calendarview.BlockComparator; + +public abstract class AbstractHTMLView implements CalendarView { + public static String COLOR_NO_RESOURCE = "#BBEEBB"; + protected Collection builders = new ArrayList(); + /** shared calendar instance. Only used for temporary stored values. */ + Calendar blockCalendar; + WeekdayMapper weekdayMapper; + Date startDate; + Date endDate; + String m_html; + protected Collection excludeDays = Collections.EMPTY_SET; + + Locale locale; + TimeZone timeZone; + Builder builder; + + public void setTimeZone(TimeZone timeZone) { + this.timeZone = timeZone; + if ( locale != null) { + blockCalendar = createCalendar(); + } + } + + public void setLocale(Locale locale) { + this.locale = locale; + if ( timeZone != null) { + blockCalendar = createCalendar(); + } + weekdayMapper = new WeekdayMapper( locale ); + } + + public TimeZone getTimeZone() { + return timeZone; + } + + public void addBuilder(Builder b) { + builders.add(b); + } + + public void removeBuilder(Builder b) { + builders.remove(b); + } + + public void rebuild(Builder builder) { + try { + addBuilder( builder); + rebuild(); + } finally { + removeBuilder( builder ); + } + } + + public void setToDate(Date weekDate) { + calcMinMaxDates( weekDate ); + } + + public void setExcludeDays(Collection excludeDays) { + this.excludeDays = excludeDays; + if (startDate != null) + calcMinMaxDates( startDate ); + } + + abstract public Collection getBlocks(); + abstract void calcMinMaxDates( Date date ); + + Calendar createCalendar() { + return Calendar.getInstance(getTimeZone(),locale); + } + + void checkBlock( Block bl ) { + if ( !bl.getStart().before(this.endDate)) { + throw new IllegalStateException("Start-date " +bl.getStart() + " must be before calendar end at " +this.endDate); + } + } + public Date getStartDate() { + return startDate; + } + + public Date getEndDate() { + return endDate; + } + + public String getHtml() { + return m_html; + } + + + protected class HTMLSmallDaySlot extends ArrayList { + private static final long serialVersionUID = 1L; + + private String date; + public HTMLSmallDaySlot(String date) { + super(2); + this.date = date; + } + public void putBlock(Block block) { + add( block ); + } + public void sort() { + Collections.sort( this, BlockComparator.COMPARATOR); + } + + public void paint(StringBuffer out) { + out.append("
    "); + out.append( date ); + out.append("
    \n"); + for ( int i=0;i"); + out.append(block.toString()); + out.append("\n"); + } + } + } + + + + +} diff --git a/Rapla/src/org/rapla/components/calendarview/html/HTMLBlock.java b/Rapla/src/org/rapla/components/calendarview/html/HTMLBlock.java new file mode 100644 index 0000000..b16d673 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendarview/html/HTMLBlock.java @@ -0,0 +1,8 @@ +package org.rapla.components.calendarview.html; + +import org.rapla.components.calendarview.Block; + +public interface HTMLBlock extends Block { + String getBackgroundColor(); + String toString(); +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/components/calendarview/html/HTMLCompactWeekView.java b/Rapla/src/org/rapla/components/calendarview/html/HTMLCompactWeekView.java new file mode 100644 index 0000000..732ff28 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendarview/html/HTMLCompactWeekView.java @@ -0,0 +1,189 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.components.calendarview.html; + +import java.util.*; + +import org.rapla.components.calendarview.Block; +import org.rapla.components.calendarview.Builder; +import org.rapla.components.calendarview.swing.SwingWeekView; + +public class HTMLCompactWeekView extends AbstractHTMLView { + public final static int ROWS = 6; //without the header row + public final static int COLUMNS = 7; + /** shared calendar instance. Only used for temporary stored values. */ + HTMLSmallDaySlot[] slots = {}; + String[] slotNames = {}; + private ArrayList rows = new ArrayList(); + private boolean[] excluded = new boolean[COLUMNS]; + + void calcMinMaxDates(Date date) { + Calendar calendar = createCalendar(); + calendar.setTime(date); + calendar.set(Calendar.MINUTE,0); + calendar.set(Calendar.HOUR_OF_DAY,0); + calendar.set(Calendar.SECOND,0); + calendar.set(Calendar.MILLISECOND,0); + calendar.set(Calendar.DAY_OF_WEEK, calendar.getFirstDayOfWeek()); + startDate = null; + endDate = null; + for (int i=0;i\n"); + result.append("\n"); + result.append(""); + result.append(""); + + for (int i=0;i"); + result.append(""); + result.append(headerNames[i]); + result.append(""); + result.append(""); + } + result.append("\n"); + + int rowsize = rows.size(); + + slots = new HTMLSmallDaySlot[rowsize * COLUMNS]; + for (int row=0;row\n"); + result.append("\n"); + if ( slotNames.length > row ) { + result.append( slotNames[ row ] ); + } + result.append("\n"); + for (int column=0;column < COLUMNS; column++) { + List blocks = (List) rows.get( row ); + int fieldNumber = row * COLUMNS + column; + slots[fieldNumber] = createField( blocks, column ); + if ( !isExcluded( column ) ) { + result.append("\n"); + slots[fieldNumber].paint( result ); + result.append("\n"); + } + } + result.append("\n"); + } + result.append(""); + m_html = result.toString(); + } + + private HTMLSmallDaySlot createField(List blocks, int column) { + HTMLSmallDaySlot c = new HTMLSmallDaySlot(""); + if ( blocks != null) { + Iterator it = blocks.iterator(); + while (it.hasNext()){ + HTMLBlock block = (HTMLBlock)it.next(); + blockCalendar.setTime(block.getStart()); + int day = blockCalendar.get(Calendar.DAY_OF_WEEK); + if ( weekdayMapper.indexForDay(day) == column ) { + c.putBlock( block ); + } + } + } + c.sort(); + return c; + }; + + + public void addBlock(Block block, int slot) { + checkBlock( block ); + while ( rows.size() <= slot ) { + addRow(); + } + ArrayList blocks = (ArrayList) rows.get( slot ); + blocks.add( block ); + + blockCalendar.setTime(block.getStart()); + int weekday = blockCalendar.get(Calendar.DAY_OF_WEEK); + excluded[weekdayMapper.indexForDay( weekday )] = false; + } + + private void addRow() { + rows.add( rows.size(), new ArrayList()); + } + +} diff --git a/Rapla/src/org/rapla/components/calendarview/html/HTMLMonthView.java b/Rapla/src/org/rapla/components/calendarview/html/HTMLMonthView.java new file mode 100644 index 0000000..c06a471 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendarview/html/HTMLMonthView.java @@ -0,0 +1,162 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.components.calendarview.html; + +import java.text.SimpleDateFormat; +import java.util.*; + +import org.rapla.components.calendarview.Block; +import org.rapla.components.calendarview.Builder; + +public class HTMLMonthView extends AbstractHTMLView { + public final static int ROWS = 6; //without the header row + public final static int COLUMNS = 7; + /** shared calendar instance. Only used for temporary stored values. */ + int daysInMonth; + HTMLSmallDaySlot[] slots; + + void calcMinMaxDates(Date date) { + blockCalendar.setTime( date ); + blockCalendar.set(Calendar.DAY_OF_MONTH, 1); + blockCalendar.set(Calendar.HOUR_OF_DAY,0); + blockCalendar.set(Calendar.MINUTE,0); + blockCalendar.set(Calendar.SECOND,0); + blockCalendar.set(Calendar.MILLISECOND,0); + this.startDate = blockCalendar.getTime(); + blockCalendar.set(Calendar.MILLISECOND,1); + this.daysInMonth = blockCalendar.getActualMaximum( Calendar.DAY_OF_MONTH ) ; + blockCalendar.set(Calendar.MILLISECOND,0); + blockCalendar.add(Calendar.DATE, this.daysInMonth); + this.endDate = blockCalendar.getTime(); + } + + public Collection getBlocks() { + ArrayList list = new ArrayList(); + for (int i=0;i 15) { + blockCalendar.add(Calendar.DATE, 7); + } + int startField = blockCalendar.get(Calendar.DATE) -1; + for ( int i=startField;i < slots.length;i+=7 ) { + if (!slots[i].isEmpty() ) { + return false; + } + } + return true; + } + + public void rebuild() { + String[] headerNames = weekdayMapper.getNames(); + // we need to clone the calendar, because we modify the calendar object int the getExclude() method + Calendar counter = (Calendar) blockCalendar.clone(); + + // calculate the blocks + Iterator it= builders.iterator(); + while (it.hasNext()) { + Builder b= (Builder)it.next(); + b.prepareBuild(getStartDate(),getEndDate()); + } + slots = new HTMLSmallDaySlot[ daysInMonth ]; + for (int i=0;i" + monthYearFormat.format(startDate) + "\n"); + + result.append("\n"); + result.append("\n"); + for (int i=0;i"); + result.append(""); + result.append(headerNames[i]); + result.append(""); + result.append(""); + } + result.append("\n"); + + for (int row=0; row<=lastRow; row++) { + boolean excludeRow = true; + // calculate if we can exclude the row + for (int column = 0; column\n"); + for (int column = 0; column\n"); + } else { + result.append("\n"); + } + } + result.append("\n"); + } + result.append("
    \n"); + slot.paint( result ); + result.append("
    "); + m_html = result.toString(); + } + + public void addBlock(Block block,int slot) { + checkBlock( block ); + blockCalendar.setTime(block.getStart()); + int day = blockCalendar.get(Calendar.DATE); + slots[day-1].putBlock( block ); + } +} diff --git a/Rapla/src/org/rapla/components/calendarview/html/HTMLPeriodView.java b/Rapla/src/org/rapla/components/calendarview/html/HTMLPeriodView.java new file mode 100644 index 0000000..9278022 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendarview/html/HTMLPeriodView.java @@ -0,0 +1,319 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | Copyright (C) 2005 Thierry Excoffier, Universite Claude Bernard Lyon | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.components.calendarview.html; + +import java.text.DateFormat; +import java.text.FieldPosition; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Iterator; +import java.util.Locale; +import java.util.TimeZone; + +import org.rapla.components.calendarview.Block; +import org.rapla.components.calendarview.Builder; +import org.rapla.components.util.Tools; + +public class HTMLPeriodView extends AbstractHTMLView { + /** shared calendar instance. Only used for temporary stored values. */ + protected HTMLSmallDaySlot[] slots; + int weeksNr; + int startHour, endHour; + double cellHeight = 1.8, cellWidth = 10; + + /** formats the date and month in the selected locale and timeZone*/ + public static String formatDateMonth(Date date, Locale locale, TimeZone timeZone) { + FieldPosition fieldPosition = new FieldPosition( DateFormat.YEAR_FIELD ); + StringBuffer buf = new StringBuffer(); + DateFormat format = DateFormat.getDateInstance( DateFormat.SHORT, locale); + format.setTimeZone( timeZone ); + buf = format.format(date, + buf, + fieldPosition + ); + if ( fieldPosition.getEndIndex()=0) ) { + buf.delete( fieldPosition.getBeginIndex(), fieldPosition.getEndIndex() ); + } + if (buf.charAt(buf.length()-1) == '/') + return buf.substring(0,buf.length()-1); + else + return buf.toString(); + } + + /** formats the day of week, date and month in the selected locale and timeZone*/ + public static String formatDayOfWeekDateMonth(Date date, Locale locale, TimeZone timeZone) { + SimpleDateFormat format = new SimpleDateFormat("EEE", locale); + format.setTimeZone(timeZone); + return format.format(date) + " " + formatDateMonth( date,locale,timeZone ) ; + } + + /** returns the name of the weekday */ + String formatDayOfWeek(Date date) { + SimpleDateFormat format = new SimpleDateFormat("EEEEE", locale); + format.setTimeZone(getTimeZone()); + return format.format(date); + } + + public void findWorkHour() { + Calendar calendar = createCalendar(); + startHour = 24; + endHour = 0; + for(int i=0; i = endHour ) { + endHour = calendar.get(Calendar.HOUR_OF_DAY); + if ( calendar.get(Calendar.MINUTE) > 0 ) + endHour++; + } + } + } + } + + + public void setEndDate(Date endDate) { + if ( endDate == null ) { + this.endDate = null; + return; + } + Calendar calendar = createCalendar(); + calendar.setTime( endDate ); + // calendar.set(Calendar.DAY_OF_WEEK, 6); + calendar.set(Calendar.HOUR_OF_DAY,23); + calendar.set(Calendar.MINUTE,59); + calendar.set(Calendar.SECOND,59); + calendar.set(Calendar.MILLISECOND,999); + this.endDate = calendar.getTime(); + } + + void calcMinMaxDates(Date date) { + Calendar calendar = createCalendar(); + calendar.setTime( date ); + // calendar.set(Calendar.DAY_OF_WEEK, 0); + calendar.set(Calendar.HOUR_OF_DAY,0); + calendar.set(Calendar.MINUTE,0); + calendar.set(Calendar.SECOND,0); + calendar.set(Calendar.MILLISECOND,0); + this.startDate = calendar.getTime(); + + if ( this.endDate == null ) { + calendar.add(Calendar.DATE, 7*7); // 7 weeks + this.endDate = calendar.getTime(); + } + } + + public Collection getBlocks() { + ArrayList list = new ArrayList(); + for (int i=0;i"); + result.append(formatDayOfWeekDateMonth(calendar.getTime(),locale,getTimeZone())); + result.append("\n"); + calendar.add(Calendar.DATE, 7); + } + calendar.add(Calendar.DATE, -7*weeksNr); + y += label_height; + + for(int h=startHour; h", ", "); + content = Tools.replaceAll(content, "\n", ""); + result.append("" + + "" + hour + ":" + minutes + + "-" + + hour_end + ":" + minutes_end + " " + + content + + "\n" + ); + it.remove(); + } + } + } + y++ ; + } + } + m_html = result.toString(); + } + + public void addBlock(Block bl, int slot) { + //if ( ((AbstractRaplaBlock)bl).isException() ) + // return; + + checkBlock( bl ); + + int day = (int)((bl.getStart().getTime() - startDate.getTime())/(1000*3600*24)); + if ( day >= slots.length ) + return; + + slots[day].putBlock(bl); + } + +} diff --git a/Rapla/src/org/rapla/components/calendarview/html/HTMLWeekView.java b/Rapla/src/org/rapla/components/calendarview/html/HTMLWeekView.java new file mode 100644 index 0000000..b10346c --- /dev/null +++ b/Rapla/src/org/rapla/components/calendarview/html/HTMLWeekView.java @@ -0,0 +1,375 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.components.calendarview.html; + +import java.util.*; +import java.text.*; + +import org.rapla.components.calendarview.Block; +import org.rapla.components.calendarview.Builder; +import org.rapla.components.calendarview.swing.SwingWeekView; + +public class HTMLWeekView extends AbstractHTMLView { + private int endHour; + private int minRow; + private int maxRow; + private int startHour; + private boolean showNonEmptyExcluded; + int m_rowsPerHour = 2; + int COLUMNS = 7; + HTMLDaySlot[] multSlots = new HTMLDaySlot[COLUMNS]; + ArrayList blocks = new ArrayList(); + ArrayList blockStart = new ArrayList(); + ArrayList blockSize = new ArrayList(); + + /** The granularity of the selection rows. + *
      + *
    • 1: 1 rows per hour = 1 Hour
    • + *
    • 2: 2 rows per hour = 1/2 Hour
    • + *
    • 3: 3 rows per hour = 20 Minutes
    • + *
    • 4: 4 rows per hour = 15 Minutes
    • + *
    • 6: 6 rows per hour = 10 Minutes
    • + *
    • 12: 12 rows per hour = 5 Minutes
    • + *
    + * Default is 2. + */ + public void setRowsPerHour(int rows) { + m_rowsPerHour = rows; + } + + public int getRowsPerHour() { + return m_rowsPerHour; + } + + void calcMinMaxDates(Date date) { + blockCalendar.setTime(date); + blockCalendar.set(Calendar.MINUTE,0); + blockCalendar.set(Calendar.HOUR_OF_DAY,0); + blockCalendar.set(Calendar.SECOND,0); + blockCalendar.set(Calendar.MILLISECOND,0); + blockCalendar.set(Calendar.DAY_OF_WEEK,blockCalendar.getFirstDayOfWeek()); + startDate = null; + endDate = null; + for (int i=0;i24) + throw new IllegalStateException("builder.getMax() is greater than 24"); + } + minRow = start * m_rowsPerHour; + maxRow = end * m_rowsPerHour; + for (int i=0;i\n"); + result.append("\n"); + result.append(""+getWeekNumberRow()+""); + for (int i=0;i<7;i++) { + if ( isExcluded ( i ) ) + continue; + result.append(""); + result.append(""); + result.append(headerNames[i]); + result.append(""); + result.append(""); + } + result.append("\n"); + result.append(""); + boolean useAM_PM = org.rapla.components.calendarview.swing.TimeScale.isAmPmFormat( locale ); + //System.out.println("USING AM_PM" + useAM_PM); + for (int row = minRow;row\n"); + if ( row % m_rowsPerHour == 0) { + result.append(""); + result.append(timeString); + result.append(""); + result.append("  \n"); + } + + + + for (int day=0;day<7;day++) { + if (isExcluded(day)) + continue; + if (multSlots[day].size() == 0) + { + // Rapla 1.4: Make line for full hours darker than others + if ((row - minRow) % m_rowsPerHour == 0) + { + result.append(" "); + result.append(" \n"); + } + else + { + result.append(" "); + result.append(" \n"); + } + } + for (int slotnr = 0; slotnr < multSlots[day].size(); slotnr++) + { + // Rapla 1.4: Make line for full hours darker than others + if ((row - minRow) % m_rowsPerHour == 0) + { + result.append(" "); + } + else + { + result.append(" "); + } + + Slot slot = multSlots[day].getSlotAt(slotnr); + if (slot.isSkip(row)) + { + // Do nothing + } + else if (slot.isEmpty(row)) + { + // Rapla 1.4: Make line for full hours darker than others + if ((row - minRow) % m_rowsPerHour == 0 || (!slot.isEmpty(row-1) && (row-minRow) > 0)) + { + result.append(" \n"); + } + else + { + result.append(" \n"); + } + } + else + { + Block block = slot.getBlock(row); + int rowspan = slot.getBlockSize(row) - Math.max(minRow - slot.getBlockStart(row), 0); + result.append(""); + result.append(block.toString()); + result.append("\n"); + } + } + + // Rapla 1.4: Make line for full hours darker than others + if ((row - minRow) % m_rowsPerHour == 0) + { + result.append(" "); + } + else + { + result.append(" "); + } + } + + result.append("\n\n"); + } + result.append("\n"); + m_html = result.toString(); + } + + protected String getWeekNumberRow() { + return ""; //To change body of created methods use File | Settings | File Templates. + } + + public void addBlock(Block block,int slot) { + checkBlock ( block ); + blockCalendar.setTime(block.getStart()); + int day = blockCalendar.get(Calendar.DAY_OF_WEEK); + HTMLDaySlot multiSlot = multSlots[weekdayMapper.indexForDay(day)]; + blockCalendar.setTime( block.getStart()); + int row = (int) ( + blockCalendar.get(Calendar.HOUR_OF_DAY)* m_rowsPerHour + + Math.round((blockCalendar.get(Calendar.MINUTE) * m_rowsPerHour)/60.0) + ); + blockCalendar.setTime(block.getEnd()); + row = Math.max( minRow, row ); + int endRow = (int) ( + blockCalendar.get(Calendar.HOUR_OF_DAY)* m_rowsPerHour + + Math.round((blockCalendar.get(Calendar.MINUTE) * m_rowsPerHour)/60.0) + ); + endRow = Math.min( maxRow, endRow ); + int rowCount = endRow -row; + blocks.add(block); + blockStart.add(new Integer(row)); + blockSize.add(new Integer( rowCount)); + multiSlot.putBlock( blocks.size() - 1, slot, row, rowCount); + } + + private String formatTime(int minuteOfDay,boolean useAM_PM) { + blockCalendar.set(Calendar.MINUTE, minuteOfDay%60); + int hour = minuteOfDay/60; + blockCalendar.set(Calendar.HOUR_OF_DAY, hour); + SimpleDateFormat format = new SimpleDateFormat(useAM_PM ? "h:mm" : "H:mm", locale); + format.setTimeZone(blockCalendar.getTimeZone()); + if (useAM_PM && hour == 12 && minuteOfDay%60 == 0) { + return format.format(blockCalendar.getTime()) + " PM"; + } else { + return format.format(blockCalendar.getTime()); + } + } + + protected class HTMLDaySlot extends ArrayList { + private static final long serialVersionUID = 1L; + + int minSlotRow = 0; + private boolean empty = true; + + public HTMLDaySlot(int minRow,int size) { + super(size); + this.minSlotRow = minRow; + } + + public void putBlock(int blockNr,int slotnr, int row,int size) { + while (slotnr >= size()) { + addSlot(); + } + getSlotAt(slotnr).putBlock( blockNr, row, size); + empty = false; + } + + public int addSlot() { + Slot slot = new Slot(minSlotRow); + add(slot); + return size(); + } + public Slot getSlotAt(int index) { + return (Slot) get(index); + } + + public boolean isEmpty() { + return empty; + } + } + + protected class Slot { + int EMPTY = -2; + int SKIP = -1; + int[] rows = new int[24 * m_rowsPerHour]; + int minSlotRow = 0; + + public Slot(int minRow) { + this.minSlotRow = minRow; + for (int i = 0;i < rows.length;i++) { + rows[i] = EMPTY; + } + } + + public void putBlock(int blockNr, int row, int size) { + int start = Math.max(minSlotRow , row ); + rows[start] = blockNr; + for (int i = start + 1;i < row + size;i++) { + rows[i] = SKIP; + } + } + + public boolean isSkip(int row) { + return rows[row]==SKIP; + } + + public boolean isEmpty(int row) { + return rows[row]==EMPTY; + } + + public Block getBlock(int row) { + if (rows[row] == SKIP || rows[row] == EMPTY ) + return null; + else + return (Block) blocks.get(rows[row]); + } + + public int getBlockStart(int row) { + return ((Integer)blockStart.get(rows[row])).intValue(); + } + public int getBlockSize(int row) { + return ((Integer)blockSize.get(rows[row])).intValue(); + } + + } +} diff --git a/Rapla/src/org/rapla/components/calendarview/package.html b/Rapla/src/org/rapla/components/calendarview/package.html new file mode 100644 index 0000000..4c28220 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendarview/package.html @@ -0,0 +1,7 @@ + +Provides basic functionality for displaying and editing appointment blocks, +in table-like components. + + + + diff --git a/Rapla/src/org/rapla/components/calendarview/swing/AbstractDaySlot.java b/Rapla/src/org/rapla/components/calendarview/swing/AbstractDaySlot.java new file mode 100644 index 0000000..1157f41 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendarview/swing/AbstractDaySlot.java @@ -0,0 +1,227 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.calendarview.swing; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionListener; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.JPanel; +import javax.swing.UIManager; + +abstract class AbstractDaySlot extends JPanel implements DaySlot +{ + private static final long serialVersionUID = 1L; + private boolean bEditable = true; + boolean paintDraggingGrid; + int draggingSlot; + int draggingY; + SwingBlock draggingView; + int draggingHeight; + protected Map blockViewMapper = new HashMap(); + protected Method getButtonMethod = null; + + AbstractDaySlot() { + try { + //is only available sind 1.4 + getButtonMethod = MouseEvent.class.getMethod("getButton", new Class[] {}); + } catch (Exception ex) { + } + } + + public void setEditable(boolean b) { + bEditable = b; + } + + public boolean isEditable() { + return bEditable; + } + + SwingBlock getBlockFor(Object component) { + return (SwingBlock) blockViewMapper.get(component); + } + + protected void showPopup(MouseEvent evt) { + Point p = new Point(evt.getX(),evt.getY()); + SwingBlock block = getBlockFor(evt.getSource()); + if (block != null) + draggingHandler.blockPopup(block,p); + } + + protected Color getSelectionColor() { + return UIManager.getColor("Table.selectionBackground"); + } + + public void paintDraggingGrid(int slot,int y, int height,SwingBlock draggingView,int oldY,int oldHeight,boolean bPaint) { + this.paintDraggingGrid = bPaint; + this.draggingSlot = slot; + this.draggingY = y; + this.draggingHeight = height; + this.draggingView = draggingView; + this.invalidateDragging(oldY, oldHeight); + } + + public int getX(Component component) { + return component.getParent().getLocation().x; + } + + void invalidateDragging(int oldY, int oldHeight) { + repaint(); + } + + void setDraggingHandler(DraggingHandler draggingHandler) { + this.draggingHandler = draggingHandler; + } + + private DraggingHandler draggingHandler; + /** BlockListener handles the dragging events and the Block + * Context Menu (right click). + */ + class BlockListener extends MouseAdapter implements MouseMotionListener{ + boolean preventDragging = false; + + public void mouseClicked(MouseEvent evt) { + if (evt.isPopupTrigger()) { + showPopup(evt); + } else { + if (evt.getClickCount()>1) + blockEdit(evt); + } + //draggingPointOffset = evt.getY(); + } + + private int calcResizeDirection( MouseEvent evt ) { + if ( !draggingHandler.supportsResizing()) + return 0; + int height = ((Component)evt.getSource()).getHeight(); + int diff = height- evt.getY() ; + if ( diff <= 5 && diff >=0 ) + return 1; + if (evt.getY() >=0 && evt.getY() < 5) + return -1; + return 0; + } + + public void mousePressed(MouseEvent evt) { + if (evt.isPopupTrigger()) { + showPopup(evt); + } + //System.out.println ("Button:" +evt.getButton() ); + preventDragging = false; + if ( getButtonMethod != null) { + try { + Integer button = (Integer) getButtonMethod.invoke( evt, new Object [] {}); + preventDragging = button.intValue() != 1; + } catch (Exception ex) { + } + } + if ( preventDragging ) + return; + SwingBlock block = getBlockFor(evt.getSource()); + int resizeDirection = calcResizeDirection( evt ); + if ( resizeDirection != 0) { + draggingHandler.blockBorderPressed( AbstractDaySlot.this,block, evt, resizeDirection ); + } + } + + public void mouseReleased(MouseEvent evt) { + if (evt.isPopupTrigger()) { + showPopup(evt); + } + preventDragging = false; + SwingBlock block = getBlockFor(evt.getSource()); + draggingHandler.mouseReleased( AbstractDaySlot.this, block, evt); + } + + public void mouseEntered(MouseEvent evt) { + } + + public void mouseExited(MouseEvent evt) { + if ( draggingHandler.isDragging()) + return; + setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + + public void mouseMoved(MouseEvent evt) { + if ( draggingHandler.isDragging()) { + return; + } + SwingBlock block = getBlockFor(evt.getSource()); + if ( calcResizeDirection( evt ) == 1 && block.isEndResizable()) { + setCursor(Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR)); + } else if (calcResizeDirection( evt ) == -1 && block.isStartResizable()){ + setCursor(Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR)); + } else { + setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + } + + public void mouseDragged(MouseEvent evt) { + if ( preventDragging || !isEditable()) + return; + + SwingBlock block = getBlockFor(evt.getSource()); + + draggingHandler.mouseDragged( AbstractDaySlot.this, block, evt); + } + + + private void blockEdit(MouseEvent evt) { + SwingBlock block = getBlockFor(evt.getSource()); + draggingHandler.blockEdit(block,new Point(evt.getX(),evt.getY())); + } + } + + protected void paintDraggingGrid(Graphics g, int x, int y, int width, int height) { + /* + Rectangle rect = g.getClipBounds(); + int startx = draggingView.getView().getX(); + int starty = draggingView.getView().getY(); + g.setColor(Color.gray); + for (int y1=10;y1= rect.x + && x2 <= rect.x + rect.width + && y2 >= rect.y + && y2 <= rect.y + rect.height + ) + g.drawRect(x2,y2,2,2); + } + */ + g.translate( x-1, y-1); + + if ( draggingView != null) { + draggingView.paintDragging( g, width , height +1 ); + } + g.translate( -(x-1), -(y-1)); + + + } + + static int count = 0; +} + + + + diff --git a/Rapla/src/org/rapla/components/calendarview/swing/AbstractSwingCalendar.java b/Rapla/src/org/rapla/components/calendarview/swing/AbstractSwingCalendar.java new file mode 100644 index 0000000..c57c777 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendarview/swing/AbstractSwingCalendar.java @@ -0,0 +1,367 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.calendarview.swing; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Point; +import java.awt.Rectangle; +import java.text.DateFormat; +import java.text.FieldPosition; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Iterator; +import java.util.Locale; +import java.util.TimeZone; + +import javax.swing.BoxLayout; +import javax.swing.JPanel; +import javax.swing.JScrollBar; +import javax.swing.JScrollPane; +import javax.swing.JViewport; +import javax.swing.ScrollPaneConstants; +import javax.swing.border.Border; +import javax.swing.border.EtchedBorder; + +import org.rapla.components.calendarview.Block; +import org.rapla.components.calendarview.Builder; +import org.rapla.components.calendarview.CalendarView; +import org.rapla.components.calendarview.WeekdayMapper; +import org.rapla.components.layout.TableLayout; + + +public abstract class AbstractSwingCalendar extends JPanel implements CalendarView { + + private static final long serialVersionUID = 1L; + static Border SLOTHEADER_BORDER = new EtchedBorder(); + int slotSize = 100; + boolean bEditable = true; + protected Collection excludeDays = Collections.EMPTY_SET; + + WeekdayMapper weekdayMapper; + TimeZone timeZone; + private Date startDate; + private Date endDate; + + ArrayList listenerList = new ArrayList(); + + JScrollPane scrollPane = new JScrollPane(); + JPanel jHeader = new JPanel(); + BoxLayout boxLayout1 = new BoxLayout(jHeader, BoxLayout.X_AXIS); + JPanel jCenter = new JPanel(); + Locale locale; + + + Collection builders = new ArrayList(); + + /** Calendar used by the put block method */ + protected Calendar blockCalendar; + /** Differing slot size for resource column */ + private int resourceSlotSize; + + AbstractSwingCalendar(boolean showScrollPane) { + jHeader.setLayout(boxLayout1); + jHeader.setOpaque( false ); + jCenter.setOpaque( false ); + if (showScrollPane) { + setLayout(new BorderLayout()); + add(scrollPane,BorderLayout.CENTER); + scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); + scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); + scrollPane.setViewportView(jCenter); + scrollPane.setColumnHeaderView(jHeader); + scrollPane.getVerticalScrollBar().setUnitIncrement(10); + scrollPane.getHorizontalScrollBar().setUnitIncrement(10); + scrollPane.setBorder(null); + } else { + setLayout(new TableLayout(new double[][] { + {TableLayout.PREFERRED,TableLayout.FILL} + ,{TableLayout.PREFERRED,TableLayout.FILL} + })); + add(jHeader,"1,0"); + add(jCenter,"1,1"); + } + this.timeZone = TimeZone.getDefault(); + setLocale( Locale.getDefault() ); + } + + public void setTimeZone(TimeZone timeZone) { + this.timeZone = timeZone; + blockCalendar = createCalendar(); + } + + Calendar createCalendar() { + return Calendar.getInstance(getTimeZone(),locale); + } + + public TimeZone getTimeZone() { + return timeZone; + } + + public void setLocale(Locale locale) { + this.locale = locale; + weekdayMapper = new WeekdayMapper( locale ); + super.setLocale( locale); + // Constructor called? + if (timeZone != null) { + setTimeZone( timeZone ); + blockCalendar = createCalendar(); + } + } + + public void setToDate(Date date) { + calcMinMaxDates( date ); + } + + protected void setStartDate( Date startDate ) + { + this.startDate = startDate; + } + + public Date getStartDate() + { + return startDate; + } + + protected void setEndDate( Date endDate ) + { + this.endDate = endDate; + } + + public Date getEndDate() + { + return endDate; + } + + public void setExcludeDays(Collection excludeDays) { + this.excludeDays = excludeDays; + if (getStartDate() != null) + calcMinMaxDates( getStartDate() ); + } + + abstract void calcMinMaxDates(Date date); + + void checkBlock( Block bl ) { + if ( !bl.getStart().before(this.getEndDate())) { + throw new IllegalStateException("Start-date " +bl.getStart() + " must be before calendar end at " +this.getEndDate()); + } + } + + public boolean isEditable() { + return bEditable; + } + + public void setEditable( boolean editable ) { + bEditable = editable; + } + + /** + Width of a single slot in pixel. + */ + public void setSlotSize(int slotSize) { + this.slotSize = Math.max(40, Math.min(300,slotSize)); + } + + public int getSlotSize() { + return slotSize; + } + + public void setBackground(Color color) { + super.setBackground(color); + if (scrollPane != null) + scrollPane.setBackground(color); + if (jCenter != null) + jCenter.setBackground(color); + if (jHeader != null) + jHeader.setBackground(color); + } + + public Iterator getBuilders() { + return builders.iterator(); + } + + public void addBuilder(Builder b) { + builders.add(b); + } + + public void removeBuilder(Builder b) { + builders.remove(b); + } + + public void addCalendarViewListener(ViewListener listener) { + listenerList.add(listener); + } + + public void removeCalendarViewListener(ViewListener listener) { + listenerList.remove(listener); + } + + JScrollPane getScrollPane() { + return scrollPane; + } + + void scrollTo(int x, int y) { + JViewport viewport = scrollPane.getViewport(); + Rectangle rect = viewport.getViewRect(); + + int leftBound = rect.x; + int upperBound = rect.y; + int lowerBound = rect.y + rect.height; + int rightBound = rect.x + rect.width; + int maxX = viewport.getView().getWidth(); + int maxY = viewport.getView().getHeight(); + + + JScrollBar scrollBar = scrollPane.getVerticalScrollBar(); + if ( y > lowerBound && lowerBound < maxY) { + scrollBar.setValue(scrollBar.getValue() + 20); + } + if ( y < upperBound && upperBound >0) { + scrollBar.setValue(scrollBar.getValue() - 20); + } + + scrollBar = scrollPane.getHorizontalScrollBar(); + if ( x > rightBound && rightBound < maxX) { + scrollBar.setValue(scrollBar.getValue() + 20); + } + if ( x < leftBound && leftBound >0) { + scrollBar.setValue(scrollBar.getValue() - 20); + } + } + + public void rebuild(Builder builder) { + try { + addBuilder( builder); + rebuild(); + } finally { + removeBuilder( builder ); + } + } + + public ViewListener[] getWeekViewListeners() { + return (ViewListener[])listenerList.toArray(new ViewListener[]{}); + } + + final void fireSelectionChanged(Date start, Date end) { + // Fire the popup event + ViewListener[] listeners = getWeekViewListeners(); + for (int i=0;i=0) ) { + buf.delete( fieldPosition.getBeginIndex(), fieldPosition.getEndIndex() ); + } + if (buf.charAt(buf.length()-1) == '/') + return buf.substring(0,buf.length()-1); + else + return buf.toString(); + } + + /** formats the day of week, date and month in the selected locale and timeZone*/ + public static String formatDayOfWeekDateMonth(Date date, Locale locale, TimeZone timeZone) { + SimpleDateFormat format = new SimpleDateFormat("EEE", locale); + format.setTimeZone(timeZone); + return format.format(date) + " " + formatDateMonth( date,locale,timeZone ) ; + } + + /** returns the name of the weekday */ + String formatDayOfWeek(Date date) { + SimpleDateFormat format = new SimpleDateFormat("EEEEE", locale); + format.setTimeZone(getTimeZone()); + return format.format(date); + } + + abstract int getDayCount(); + abstract DaySlot getDay(int num); + abstract int calcSlotNr( int x, int y); + abstract int getSlotNr( DaySlot slot); + abstract int getRowsPerDay(); + abstract Date createDate( DaySlot slot, int row, boolean startOfRow); + + public int getResourceSlotSize() { + return resourceSlotSize; + } + + public void setResourceSlotSize(int resourceSlotSize) { + this.resourceSlotSize = resourceSlotSize; + } + + +} diff --git a/Rapla/src/org/rapla/components/calendarview/swing/DaySlot.java b/Rapla/src/org/rapla/components/calendarview/swing/DaySlot.java new file mode 100644 index 0000000..262f876 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendarview/swing/DaySlot.java @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.calendarview.swing; + +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Point; + +interface DaySlot { + void paintDraggingGrid(int x,int y, int height,SwingBlock block, int oldY,int oldHeight,boolean bPaint); + Point getLocation(); + void unselectAll(); + int calcRow(int y); + int calcSlot(int x); + int getX(Component component); + void select(int startRow,int endRow); + void setCursor(Cursor cursor); +} diff --git a/Rapla/src/org/rapla/components/calendarview/swing/DraggingHandler.java b/Rapla/src/org/rapla/components/calendarview/swing/DraggingHandler.java new file mode 100644 index 0000000..6ddc7ac --- /dev/null +++ b/Rapla/src/org/rapla/components/calendarview/swing/DraggingHandler.java @@ -0,0 +1,245 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.calendarview.swing; + +import java.awt.Cursor; +import java.awt.Point; +import java.awt.event.MouseEvent; +import java.util.Date; + +import org.rapla.components.calendarview.swing.scaling.IRowScaleSmall; +import org.rapla.components.calendarview.swing.scaling.OneRowScale; + + +/** DraggingHandler coordinates the drag events from the Block-Components + * between the different MultiSlots of a weekview. + */ +class DraggingHandler { + int draggingPointOffset = 0; + DaySlot oldSlot; + int oldY = 0; + int oldHeight = 0; + AbstractSwingCalendar m_cv; + Date start = null; + Date newStart = null; + Date end = null; + Date newEnd = null; + int resizeDirection; + boolean bMoving; + boolean bResizing; + boolean supportsResizing; + IRowScaleSmall rowScale; + + public DraggingHandler(AbstractSwingCalendar wv,IRowScaleSmall rowScale, boolean supportsResizing) { + this.supportsResizing = supportsResizing; + this.rowScale = rowScale; + m_cv = wv; + } + + public DraggingHandler(AbstractSwingCalendar wv, boolean supportsResizing) { + this ( wv, new OneRowScale(), supportsResizing ); + } + + public boolean supportsResizing() { + return supportsResizing; + } + + public void blockPopup(SwingBlock block,Point p) { + m_cv.fireBlockPopup(block,p); + } + + public void blockEdit(SwingBlock block,Point p) { + m_cv.fireBlockEdit(block,p); + } + + public void mouseReleased(DaySlot slot, SwingBlock block, MouseEvent evt) { + if ( isDragging() ) + stopDragging(slot, block, evt); + } + + public void blockBorderPressed(DaySlot slot,SwingBlock block,MouseEvent evt, int direction) { + if (!bResizing && supportsResizing ) { + this.resizeDirection = direction; + startResize( slot, block, evt); + } + } + + public boolean isDragging() { + return bResizing || bMoving; + } + + public void mouseDragged(DaySlot slot,SwingBlock block,MouseEvent evt) { + if ( bResizing ) + startResize( slot, block, evt ); + else + startMoving( slot, block, evt ); + } + + private void dragging(DaySlot slot,SwingBlock block,int _x,int _y,boolean bDragging) { + // 1. Calculate slot + DaySlot newSlot = null; + if ( bResizing ) { + newSlot = slot; + } else { + int slotNr = m_cv.calcSlotNr( + slot.getLocation().x + _x + , slot.getLocation().y + _y); + newSlot = m_cv.getDay( slotNr ); + if (newSlot == null) + return; + } + + // 2. Calculate new x relative to slot + + int y = _y; + int xslot = 0; + int height = block.getView().getHeight(); + xslot = newSlot.calcSlot( slot.getLocation().x + _x - newSlot.getLocation().x ); + if ( bResizing ) { + if ( resizeDirection == 1) { + y = block.getView().getLocation().y; + // we must trim the endRow + int endrow = newSlot.calcRow(_y ) + 1; + endrow = Math.max( newSlot.calcRow(y) + 2, endrow); + height = rowScale.getYCoordForRow(endrow) - y; + if ( bDragging ) { + start = block.getStart(); + end = m_cv.createDate( newSlot, endrow, true); + //System.out.println ( "Resizeing@end: start=" + start + ", end=" + end) ; + } + } else if (resizeDirection == -1){ + // we must trim y + y = rowScale.trim( y ); + int row = newSlot.calcRow( y ) ; + int rowSize = rowScale.getRowSizeForRow( row ); + y = Math.min ( block.getView().getLocation().y + block.getView().getHeight() - rowSize, y ); + height = block.getView().getLocation().y + block.getView().getHeight() - y; + if ( bDragging ) { + row = newSlot.calcRow( y ); + if(y==0) + start = m_cv.createDate( newSlot, row, true); + else + start = m_cv.createDate( newSlot, row, false); + + end = block.getEnd(); + //System.out.println ( "Resizeing@start: start=" + start + ", end=" + end) ; + } + } + } else if (bMoving){ + // we must trim y + //y = rowScale.trim( y); + if ( bDragging ) { + start = m_cv.createDate( newSlot, newSlot.calcRow( y ), true); + //System.out.println ( "Moving: start=" + start + ", end=" + end +" row: " + row) ; + } + } + if (oldSlot != null && oldSlot != newSlot) + oldSlot.paintDraggingGrid(xslot, y, height, block, oldY, oldHeight, false); + + newSlot.paintDraggingGrid(xslot, y, height, block, oldY, oldHeight, bDragging); + oldSlot = newSlot; + oldY = y; + oldHeight = height; + } + + private void startMoving(DaySlot slot,SwingBlock block,MouseEvent evt) { + if (!bMoving) { + draggingPointOffset = evt.getY(); + if (block.isMovable()) { + bMoving = true; + slot.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); + } else { + bMoving = false; + slot.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + return; + } + } + if ( block == null) + return; + int x = evt.getX() + slot.getX(block.getView()); + int y = evt.getY() + block.getView().getLocation().y; + + scrollTo( slot, x, y); + // Correct y with the draggingPointOffset + y = evt.getY() - draggingPointOffset + block.getView().getLocation().y ; + + y += rowScale.getDraggingCorrection(y ) ; + dragging( slot, block, x, y, bMoving); + } + + private void startResize(DaySlot slot,SwingBlock block, MouseEvent evt) { + if ( block == null) + return; + int x = evt.getX() + slot.getX(block.getView()); + int y = evt.getY() + block.getView().getLocation().y; + if (!bResizing) { + if (block.isMovable() && ( ( resizeDirection == -1 && block.isStartResizable() ) + || ( resizeDirection == 1 && block.isEndResizable()))) { + bResizing = true; + } else { + bResizing = false; + return; + } + } + + scrollTo( slot, x, y); + dragging( slot, block, x, y, bResizing); + } + + private void stopDragging(DaySlot slot, SwingBlock block,MouseEvent evt) { + slot.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + if ( block == null) { + return ; + } + + if (!m_cv.isEditable()) + return; + try { + int x = evt.getX() + slot.getX( block.getView() ); + int y = evt.getY() - draggingPointOffset + block.getView().getLocation().y ; + y += rowScale.getDraggingCorrection(y ); + + dragging(slot,block,x,y,false); + Point upperLeft = m_cv.getScrollPane().getViewport().getViewPosition(); + Point newPoint = new Point(slot.getLocation().x + x -upperLeft.x + ,y-upperLeft.y); + if ( bMoving ) { + // Has the block moved + //System.out.println("Moved to " + newStart + " - " + newEnd); + if ( !start.equals( block.getStart() ) ) { + m_cv.fireMoved(block, newPoint, start); + } + } + if ( bResizing ) { + // System.out.println("Resized to " + start + " - " + end); + if ( !( start.equals( block.getStart() ) && end.equals( block.getEnd()) )) { + m_cv.fireResized(block, newPoint, start, end); + } + } + } finally { + bResizing = false; + bMoving = false; + start = null; + end = null; + } + } + + // Begin scrolling when hitting the upper or lower border while + // dragging or selecting. + private void scrollTo(DaySlot slot,int x,int y) { + // 1. Transfer p.x relative to jCenter + m_cv.scrollTo(slot.getLocation().x + x, slot.getLocation().y + y); + } + +} + diff --git a/Rapla/src/org/rapla/components/calendarview/swing/LargeDaySlot.java b/Rapla/src/org/rapla/components/calendarview/swing/LargeDaySlot.java new file mode 100644 index 0000000..9b01476 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendarview/swing/LargeDaySlot.java @@ -0,0 +1,374 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.calendarview.swing; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Vector; + +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JComponent; +import javax.swing.JPanel; + +import org.rapla.components.calendarview.swing.scaling.IRowScale; + + +/** Komponente, welche eine beliebige anzahl von Slot-komponenten zusammenfasst. +* die slots koennen zur laufzeit hinzugefuegt oder auch dynamisch bei bedarf +*erzeugt werden. sie werden horizontal nebeneinander angeordnet. +*/ +class LargeDaySlot extends AbstractDaySlot implements DaySlot +{ + private static final long serialVersionUID = 1L; + + public static Color THICK_LINE_COLOR = Color.black; + public static Color LINE_COLOR = new Color(0xaa, 0xaa, 0xaa); + public static Color WORKTIME_BACKGROUND = Color.white; + public static Color NON_WORKTIME_BACKGROUND = new Color(0xcc, 0xcc, 0xcc); + + private Vector slots= new Vector(); + private int slotxsize; + + private int selectionStart = -1; + private int selectionEnd = -1; + private int oldSelectionStart = -1; + private int oldSelectionEnd = -1; + + BoxLayout boxLayout1 = new BoxLayout(this, BoxLayout.X_AXIS); + int right_gap = 8; + int left_gap = 5; + int slot_space = 3; + JComponent header; + IRowScale rowScale; + + private BlockListener blockListener = new BlockListener(); + /** + es muss auch noch setTimeIntervall() aufgerufen werden, um die initialisierung + fertig zu stellen (wie beim konstruktor von Slot). + slotxsize ist die breite der einzelnen slots. + "date" legt das Datum fest, fuer welches das Slot anzeigt (Uhrzeit bleibt unberuecksichtigt) + */ + public LargeDaySlot( + int slotxsize + ,IRowScale rowScale + ,JComponent header) { + this.slotxsize= slotxsize; + this.rowScale = rowScale; + this.header = header; + setLayout(boxLayout1); + this.add(Box.createHorizontalStrut(left_gap)); + addSlot(); + setBackground(getBackground()); + setAlignmentX(0); + setAlignmentY(TOP_ALIGNMENT); + this.setBackground(Color.white); + } + + public boolean isBorderVisible() { + return true; +// return getBackground() != Color.white; + } + + public void setVisible(boolean b) { + super.setVisible(b); + header.setVisible(b); + } + + public JComponent getHeader() { + return header; + } + + public int calcSlot(int x) { + int slot = ((x - left_gap) / (slotxsize + slot_space)); + //System.out.println ( x + " slot " + slot); + if (slot<0) + slot = 0; + if (slot >= slots.size()) + slot = slots.size() -1; + return slot; + } + + public Collection getBlocks() { + ArrayList list = new ArrayList(); + for (int i=0;i= slots.size()) { + addSlot(); + } + ((Slot)slots.elementAt(slotnr)).putBlock(bl); + // The blockListener can be shared among all blocks, + // as long es we can only click on one block simultanously + bl.getView().addMouseListener(blockListener); + bl.getView().addMouseMotionListener(blockListener); + blockViewMapper.put(bl.getView(),bl); + } + + public Dimension getMinimumSize() { return getPreferredSize(); } + public Dimension getMaximumSize() { return getPreferredSize(); } + Insets insets = new Insets(0,0,0, right_gap); + Insets slotInsets = new Insets(0,0,0,0); + + public Insets getInsets() { + return insets; + } + + SwingBlock getBlockFor(Object component) { + return (SwingBlock) blockViewMapper.get(component); + } + + int getBlockCount() { + int count = 0; + Iterator it = slots.iterator(); + while (it.hasNext()) + count += ((Slot)it.next()).getBlockCount(); + return count; + } + + boolean isEmpty() { + return getBlockCount() == 0; + } + + protected void invalidateDragging(Point oldPoint) { + int width = (int) getSize().width; + int startRow = Math.min(calcRow(draggingY),calcRow(oldPoint.y )); + int endRow = Math.max(calcRow(draggingY),calcRow(oldPoint.y )) + 1; + repaint(0 + , rowScale.getYCoordForRow(startRow) -10 + , width + , rowScale.getSizeInPixelBetween(startRow, endRow) + draggingHeight + 20 + ); + } + + private void invalidateSelection() { + int width = (int) getSize().width; + int startRow = Math.min(selectionStart,oldSelectionStart); + int endRow = Math.max(selectionEnd,oldSelectionEnd) + 1; + repaint(0,rowScale.getYCoordForRow(startRow ), width, rowScale.getSizeInPixelBetween(startRow, endRow )); + // Update the values after calling repaint, because paint needs the old values. + oldSelectionStart = selectionStart; + oldSelectionEnd = selectionEnd; + } + + public void paint(Graphics g) { + Dimension dim = getSize(); + Rectangle rect = g.getClipBounds(); + if (!isEditable()) { + g.setColor(Color.white); + g.fillRect(rect.x + ,rect.y + ,rect.width + ,rect.height); + } else { + int starty = rowScale.getStartWorktimePixel(); + int endy = rowScale.getEndWorktimePixel(); + int height = rowScale.getSizeInPixel(); + Color firstColor = NON_WORKTIME_BACKGROUND; + Color secondColor = WORKTIME_BACKGROUND; + + if ( starty >= endy) + { + int c = starty; + starty = endy; + endy = c; + secondColor = NON_WORKTIME_BACKGROUND; + firstColor = WORKTIME_BACKGROUND; + } + + if (rect.y - rect.height <= starty) { + g.setColor( firstColor ); + g.fillRect(rect.x + ,Math.max(rect.y,0) + ,rect.width + ,Math.min(rect.height,starty)); + } + if (rect.y + rect.height >= starty && rect.y <= endy ) { + g.setColor( secondColor ); + g.fillRect(rect.x + ,Math.max(rect.y,starty) + ,rect.width + ,Math.min(rect.height,endy - starty)); + } + if (rect.y + rect.height >= endy) { + g.setColor( firstColor ); + g.fillRect(rect.x + ,Math.max(rect.y,endy) + ,rect.width + ,Math.min(rect.height, height - endy)); + } + } + + if (isBorderVisible()) { + g.setColor(LINE_COLOR); + g.drawLine(0,rect.y,0,rect.y + rect.height); + g.drawLine(dim.width - 1,rect.y,dim.width - 1 ,rect.y + rect.height); + } + + int max = rowScale.getMaxRows() ; + // Paint selection + for (int i=0; i < max ; i++) { + int rowSize = rowScale.getRowSizeForRow( i); + int y = rowScale.getYCoordForRow( i); + if ((y + rowSize) >= rect.y && y < (rect.y + rect.height)) { + if (i>= selectionStart && i<=selectionEnd) { + g.setColor(getSelectionColor()); + g.fillRect(Math.max (rect.x,1) + , y + , Math.min (rect.width,dim.width - Math.max (rect.x,1) - 1) + , rowSize); + } + boolean bPaintRowThick = (rowScale.isPaintRowThick(i)); + g.setColor((bPaintRowThick) ? THICK_LINE_COLOR : LINE_COLOR); + if (isEditable() || (bPaintRowThick && (i getMaximumSize().height) + y2 = getMaximumSize().height; + b.getView().setBounds(0, y1, slotxsize, y2 - y1 + 1); + add(b.getView()); + } + + public int getBlockCount() { + return blocks.size(); + } + + public Dimension getMinimumSize() { return getPreferredSize(); } + public Dimension getMaximumSize() { return getPreferredSize(); } + + public Insets getInsets() { + return slotInsets; + } + + public Collection getBlocks() { + return blocks; + } + } + + + + public int calcRow( int y ) + { + return rowScale.calcRow( y); + } +} + + + diff --git a/Rapla/src/org/rapla/components/calendarview/swing/PeriodDaySlot.java b/Rapla/src/org/rapla/components/calendarview/swing/PeriodDaySlot.java new file mode 100644 index 0000000..e8fbab5 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendarview/swing/PeriodDaySlot.java @@ -0,0 +1,401 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.calendarview.swing; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.TimeZone; +import java.util.Vector; + +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JPanel; + + +class PeriodDaySlot extends AbstractDaySlot implements DaySlot +{ + private static final long serialVersionUID = 1L; + + private int rowsPerHour = 4; + + public static Color THICK_LINE_COLOR = Color.black; + public static Color LINE_COLOR = new Color(0xaa, 0xaa, 0xaa); + public static Color WORKTIME_BACKGROUND = Color.white; + public static Color NON_WORKTIME_BACKGROUND = new Color(0xcc, 0xcc, 0xcc); + + private Vector slots= new Vector(); + private int mintime, maxtime; + private int slotxsize; + private TimeZone timeZone; + + private int selectionStart = -1; + private int selectionEnd = -1; + private int oldSelectionStart = -1; + private int oldSelectionEnd = -1; + + BoxLayout boxLayout1 = new BoxLayout(this, BoxLayout.X_AXIS); + int right_gap = 5; + int left_gap = 3; + int slot_space = 3; + int rowSize; + + private BlockListener blockListener = new BlockListener(); + /** + es muss auch noch setTimeIntervall() aufgerufen werden, um die initialisierung + fertig zu stellen (wie beim konstruktor von Slot). + slotxsize ist die breite der einzelnen slots. + "date" legt das Datum fest, fuer welches das Slot anzeigt (Uhrzeit bleibt unberuecksichtigt) + */ + public PeriodDaySlot( + TimeZone timeZone + ,int slotxsize + ,int rowsPerHour + ,int rowSize + ) { + this.timeZone = timeZone; + this.slotxsize= slotxsize; + this.rowsPerHour = rowsPerHour; + this.rowSize = rowSize; + setLayout(boxLayout1); + this.add(Box.createHorizontalStrut(left_gap)); + addSlot(); + setBackground(getBackground()); + setAlignmentX(0); + setAlignmentY(TOP_ALIGNMENT); + this.setBackground(Color.white); + } + + private Calendar calendar = null; + private Calendar getCalendar() { + // Lazy creation of the calendar + if (calendar == null) + calendar = Calendar.getInstance(timeZone); + return calendar; + } + + public boolean isBorderVisible() { + return true; +// return getBackground() != Color.white; + } + + // override + public int calcRow(int y) { + y -= 3 ; // Excoffier: Why ? + if ( y < 0 ) + y -= rowSize - 1; + int row = y / rowSize; + return row; + } + + // override + public int calcHour(int index) { + index += 7 * this.rowsPerHour * (maxtime - mintime) ; // Goto positives + index = index % (this.rowsPerHour * (maxtime - mintime)) ; + return index / rowsPerHour; + } + + // override + public int calcMinute(int index) { + int minutesPerRow = 60 / rowsPerHour; + index += rowsPerHour * 24 * 7 ; // So it is positive + return (index % rowsPerHour) * (minutesPerRow); + } + + // override + private int addSlot() { + Slot slot= new Slot(); + slot.setTimeIntervall(mintime,maxtime); + slots.addElement(slot); + this.add(slot); + this.add(Box.createHorizontalStrut(slot_space)); + return slots.size()-1; + } + + // XXX Thierry Excoffier, this draggingY correction should be + // implemented in AbstractDaySlot? + // It is badly implemented here. + public int dragY() { + int h = this.rowsPerHour * ((maxtime - mintime) * rowSize) ; + return (draggingY + 7*h) % h; + } + + public int calcSlot(int x) { + int slot = ((x - left_gap) / (slotxsize + slot_space)); + //System.out.println ( x + " slot " + slot); + if (slot<0) + slot = 0; + if (slot >= slots.size()) + slot = slots.size() -1; + return slot; + } + + public Collection getBlocks() { + ArrayList list = new ArrayList(); + for (int i=0;i= slots.size()) { + addSlot(); + } + ((Slot)slots.elementAt(slotnr)).putBlock(bl); + // The blockListener can be shared among all blocks, + // as long es we can only click on one block simultanously + bl.getView().addMouseListener(blockListener); + bl.getView().addMouseMotionListener(blockListener); + blockViewMapper.put(bl.getView(),bl); + } + + public Dimension getMinimumSize() { return getPreferredSize(); } + public Dimension getMaximumSize() { return getPreferredSize(); } + Insets insets = new Insets(0,0,0, right_gap); + Insets slotInsets = new Insets(0,0,0,0); + + public Insets getInsets() { + return insets; + } + + SwingBlock getBlockFor(Object component) { + return (SwingBlock) blockViewMapper.get(component); + } + + int getBlockCount() { + int count = 0; + Iterator it = slots.iterator(); + while (it.hasNext()) + count += ((Slot)it.next()).getBlockCount(); + return count; + } + + boolean isEmpty() { + return getBlockCount() == 0; + } + + protected void invalidateDragging(Point oldPoint) { + int width = (int) getSize().width; + int start = Math.min(calcRow(dragY()),calcRow(oldPoint.y )); + int end = Math.max(calcRow(dragY()),calcRow(oldPoint.y )) + 1; + repaint(0 + , start * rowSize -10 + , width + , (end - start) * rowSize + draggingHeight + 20 + ); + } + + private void invalidateSelection() { + int width = (int) getSize().width; + int start = Math.min(selectionStart,oldSelectionStart); + int end = Math.max(selectionEnd,oldSelectionEnd) + 1; + repaint(0,start * rowSize, width, (end - start) * rowSize); + // Update the values after calling repaint, because paint needs the old values. + oldSelectionStart = selectionStart; + oldSelectionEnd = selectionEnd; + } + + int max; + public void paint(Graphics g) { + Dimension dim = getSize(); + Rectangle rect = g.getClipBounds(); + + g.setColor(Color.white); + g.fillRect(rect.x + ,rect.y + ,rect.width + ,rect.height); + + if (isBorderVisible()) { + g.setColor(LINE_COLOR); + g.drawLine(0,rect.y,0,rect.y + rect.height); + g.drawLine(dim.width - 1,rect.y,dim.width - 1 ,rect.y + rect.height); + } + + max = rowsPerHour * (maxtime - mintime); + + // System.out.println (g + " start=" + selectionStart + " end=" + selectionEnd); + + + for (int i=0; i <= max ; i++) { + int y = rowSize * i; + if ((y + rowSize) >= rect.y && y < (rect.y + rect.height)) { + if (i>= selectionStart && i<=selectionEnd) { + g.setColor(getSelectionColor()); + g.fillRect(Math.max (rect.x,1) + , y + , Math.min (rect.width,dim.width - Math.max (rect.x,1) - 1) + , rowSize); + } + boolean bFullHour = (i % rowsPerHour == 0); + g.setColor((bFullHour) ? THICK_LINE_COLOR : LINE_COLOR); + if (isEditable() || (bFullHour && (i= y2) + { + x += 4; + y1 -= 4; + y2 -= 4; + } + } + + blocks.add(b); + if ( y1 < 0) + y1 = 0; + if ( y2 > getMaximumSize().height) + y2 = getMaximumSize().height; + b.getView().setBounds(x, y1, slotxsize - right_gap - left_gap, y2 - y1 + 1); + add(b.getView()); + } + + public int getBlockCount() { + return blocks.size(); + } + + public Dimension getMinimumSize() { return getPreferredSize(); } + public Dimension getMaximumSize() { return getPreferredSize(); } + + public Insets getInsets() { + return slotInsets; + } + + public ArrayList getBlocks() { + return blocks; + } + } +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/components/calendarview/swing/SelectionHandler.java b/Rapla/src/org/rapla/components/calendarview/swing/SelectionHandler.java new file mode 100644 index 0000000..43027fb --- /dev/null +++ b/Rapla/src/org/rapla/components/calendarview/swing/SelectionHandler.java @@ -0,0 +1,248 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.calendarview.swing; + +import java.util.Date; +import java.awt.Point; +import java.awt.Component; +import javax.swing.SwingUtilities; +import java.awt.event.*; + +/** SelectionHandler handles the selection events and the Slot + * Context Menu (right click). + * This is internally used by the weekview to communicate with its slots. + */ +class SelectionHandler extends MouseAdapter implements MouseMotionListener { + Date start; + Date end; + boolean bPopupClicked = false; + boolean bSelecting = false; + public int selectionStart = -1; + public int selectionEnd = -1; + private int oldIndex = -1; + private int oldSlotNr = -1; + private int startSlot = -1; + private int endSlot = -1; + private int draggingSlot = -1; + private int draggingIndex = -1; + AbstractSwingCalendar m_wv; + boolean periodSelection; + + public SelectionHandler(AbstractSwingCalendar wv) { + m_wv = wv; + } + + public void mouseClicked(MouseEvent evt) { + if (evt.isPopupTrigger()) { + bPopupClicked = true; + slotPopup(evt); + } else { + /* We don't check click here + if (SwingUtilities.isLeftMouseButton(evt)) + move(evt); + */ + } + } + public void mousePressed(MouseEvent evt) { + if (evt.isPopupTrigger()) { + bPopupClicked = true; + slotPopup(evt); + } else { + if (SwingUtilities.isLeftMouseButton(evt)) + move(evt); + } + } + + public void mouseReleased(MouseEvent evt) { + if (evt.isPopupTrigger()) { + bPopupClicked = true; + slotPopup(evt); + } + if (SwingUtilities.isLeftMouseButton(evt) && !bPopupClicked) + move(evt); + bPopupClicked = false; + bSelecting = false; + } + + public void mouseDragged(MouseEvent evt) { + if (SwingUtilities.isLeftMouseButton(evt) && !bPopupClicked) + move(evt); + } + public void mouseMoved(MouseEvent evt) { + } + + public void setPeriodSelection(boolean period) { + periodSelection = period; + } + + private void clearSelection() { + for (int i=0;i draggingIndex ) { + selectionStart = draggingIndex; + selectionEnd = selectedIndex; + } else if (selectedIndex < draggingIndex ){ + selectionStart = selectedIndex; + selectionEnd = draggingIndex; + } + } else if (slotNr > draggingSlot) { + startSlot = draggingSlot; + selectionStart = draggingIndex; + endSlot = slotNr; + selectionEnd = selectedIndex; + } else if (slotNr < draggingSlot) { + startSlot = slotNr; + selectionStart = selectedIndex; + endSlot = draggingSlot; + selectionEnd = draggingIndex; + } + if (selectedIndex == oldIndex && slotNr == oldSlotNr) + return; + int rowsPerDay = m_wv.getRowsPerDay(); + if (selectedIndex >= rowsPerDay-1) + selectedIndex = rowsPerDay-1; + } + oldSlotNr = slotNr; + oldIndex = selectedIndex; + setSelection(); + m_wv.scrollTo( + m_wv.getDay(slotNr).getLocation().x + evt.getX() + ,m_wv.getDay(slotNr).getLocation().y + evt.getY() + ); + } + + private void setSelection() { + + int startRow = selectionStart; + int endRow = m_wv.getRowsPerDay() -1; + + + for (int i=0;i selectionEnd ) { + startRow = selectionEnd; + endRow = selectionStart; + } else { + startRow = selectionStart; + endRow = selectionEnd; + } + int startSlotDay = startSlot % 7; + int endSlotDay = endSlot % 7; + int startDayOfWeek = Math.min(startSlotDay, endSlotDay); + int endDayOfWeek = Math.max(startSlotDay, endSlotDay); + int maxRow = m_wv.getRowsPerDay(); + int realEndSlot = (endSlot/7)*7 + endDayOfWeek + 1; + // System.out.println("selectionStart=" + selectionStart + " selectionEnd=" + selectionEnd + " startslot=" + startSlot + " endSlot=" + endSlot + " maxrow=" + maxRow) ; + for (int i=(startSlot/7)*7;i<=realEndSlot;i++) { + if (m_wv.getDay(i) == null) + continue; + int dayOfWeek = i % 7; + if ( dayOfWeek < startDayOfWeek + || dayOfWeek > endDayOfWeek + ) + m_wv.getDay(i).unselectAll(); + else if ( dayOfWeek > startDayOfWeek + && dayOfWeek < endDayOfWeek) + m_wv.getDay(i).select(0, 10000); // XXX Excoffier + else if ( dayOfWeek == startDayOfWeek + && dayOfWeek == endDayOfWeek) + m_wv.getDay(i).select(startRow,endRow); + else if ( dayOfWeek == startDayOfWeek + && dayOfWeek < endDayOfWeek) + m_wv.getDay(i).select((startRow+7*maxRow) % maxRow,10000); // XXX + else if ( dayOfWeek > startDayOfWeek + && dayOfWeek == endDayOfWeek) + m_wv.getDay(i).select(0, endRow % maxRow); + else + System.out.println("Should raise an error"); // XXX + } + for (int i=realEndSlot;i startSlot) + startRow = 0; + if (i == endSlot) + endRow = selectionEnd; + if (m_wv.getDay(i) != null) + m_wv.getDay(i).select(startRow,endRow); + } + startRow = selectionStart ; + endRow = selectionEnd ; + for (int i=endSlot+1;i=0 || endRow>=0); + if (this.selected == selected) + return; + this.selected = selected; + invalidateSelection(); + } + + public void unselectAll() { + if (!selected ) + return; + selected = false; + invalidateSelection(); + } + + private void invalidateSelection() { + repaint(); + // Update the values after calling repaint, because paint needs the old values. + } + + + /** + fuegt einen block im gewuenschten slot ein + (konflikte werden ignoriert). + */ + public void putBlock(SwingBlock bl) { + add( bl.getView() ); + blocks.add( bl ); + // The blockListener can be shared among all blocks, + // as long es we can only click on one block simultanously + bl.getView().addMouseListener( blockListener ); + bl.getView().addMouseMotionListener( blockListener ); + blockViewMapper.put( bl.getView(), bl ); + int height = Math.max( 50, blocks.size() * BLOCK_HEIGHT + START_GAP ); + Dimension newSize = new Dimension( slotxsize, height ); + setPreferredSize( newSize ); + } + + void sort() { + Collections.sort( blocks, BlockComparator.COMPARATOR); + updateBounds(); + } + + private void updateBounds() { + for (int i=0;i< blocks.size();i++) { + int y = i * BLOCK_HEIGHT + START_GAP; + SwingBlock bl = (SwingBlock) blocks.get(i); + bl.getView().setBounds( 1 ,y, slotxsize -1, BLOCK_HEIGHT -1); + } + } + + public Dimension getMinimumSize() { return getPreferredSize(); } + public Dimension getMaximumSize() { return getPreferredSize(); } + Insets insets = new Insets(0,0,0, 0); + + public Insets getInsets() { + return insets; + } + + int getBlockCount() { + return blocks.size(); + } + + public boolean isEmpty() { + return getBlockCount() == 0; + } + + public int calcRow(int y) { + return 0; + } + + public int calcSlot(int x) { + return 0; + } + + public int getX(Component component) { + return component.getLocation().x; + } + + int max; + public void paint(Graphics g) { + Dimension dim = getSize(); + Rectangle rect = g.getClipBounds(); + g.setColor(Color.white); + + if ( selected ) { + g.setColor(getSelectionColor()); + } else { + g.setColor(getBackground()); + } + + g.fillRect(rect.x + , rect.y + , rect.x + rect.width + , rect.y + rect.height); + + if (isBorderVisible()) { + g.setColor(LINE_COLOR); + g.drawLine(0,rect.y,0,rect.y + rect.height); + g.drawLine(dim.width - 1,rect.y,dim.width - 1 ,rect.y + rect.height); + g.drawLine(rect.x,0, rect.x + rect.width,0); + g.drawLine(rect.x, dim.height -1, rect.x + rect.width, dim.height -1); + } + + g.setColor( dateNumberColor ); + int numberWidth = g.getFontMetrics().stringWidth( number ); + g.drawString( number , dim.width - numberWidth - 5, Math.max( START_GAP, 10 ) ); + + super.paintChildren(g); + if ( paintDraggingGrid ) { + int height = draggingView.getView().getHeight() - 1; + int x = 0; + int y = draggingView.getView().getBounds().y; + if ( y + height + 2> getHeight()) { + y = getHeight() - height -2; + } + paintDraggingGrid(g, x, y, slotxsize -1, height ); + } + } + + public int getRowSize() + { + return rowSize; + } + +} + + + diff --git a/Rapla/src/org/rapla/components/calendarview/swing/SwingBlock.java b/Rapla/src/org/rapla/components/calendarview/swing/SwingBlock.java new file mode 100644 index 0000000..d9faeee --- /dev/null +++ b/Rapla/src/org/rapla/components/calendarview/swing/SwingBlock.java @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.calendarview.swing; + +import java.awt.Component; +import java.awt.Graphics; + +import org.rapla.components.calendarview.Block; + +public interface SwingBlock extends Block { + Component getView(); + public void paintDragging(Graphics g, int width, int height); + boolean isMovable(); + boolean isStartResizable(); + boolean isEndResizable(); +} diff --git a/Rapla/src/org/rapla/components/calendarview/swing/SwingCompactWeekView.java b/Rapla/src/org/rapla/components/calendarview/swing/SwingCompactWeekView.java new file mode 100644 index 0000000..3c33828 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendarview/swing/SwingCompactWeekView.java @@ -0,0 +1,328 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.calendarview.swing; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Point; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import javax.swing.JComponent; +import javax.swing.JLabel; + +import org.rapla.components.calendarview.Block; +import org.rapla.components.calendarview.Builder; +import org.rapla.components.layout.TableLayout; + +/** Graphical component for displaying a calendar like weekview. + * This view doesn't show the times and arragenes the different slots + * verticaly. +*/ +public class SwingCompactWeekView extends AbstractSwingCalendar +{ + private static final long serialVersionUID = 1L; + + public final static int COLUMNS = 7; + private SmallDaySlot[] slots = new SmallDaySlot[0]; + private ArrayList rows = new ArrayList(); + DraggingHandler draggingHandler = new DraggingHandler(this, false); + SelectionHandler selectionHandler = new SelectionHandler(this); + String[] slotNames = new String[] {}; + private boolean[] excluded = new boolean[COLUMNS]; + + public SwingCompactWeekView() { + this(true); + } + + public SwingCompactWeekView(boolean showScrollPane) { + super( showScrollPane ); + } + + void calcMinMaxDates(Date date) { + Calendar calendar = createCalendar(); + calendar.setTime(date); + calendar.set(Calendar.MINUTE,0); + calendar.set(Calendar.HOUR_OF_DAY,0); + calendar.set(Calendar.SECOND,0); + calendar.set(Calendar.MILLISECOND,0); + calendar.set(Calendar.DAY_OF_WEEK,calendar.getFirstDayOfWeek()); + this.setStartDate( null ); + this.setEndDate( null ); + for (int i=0;i 0) { + tableLayout.insertColumn(0, getResourceSlotSize()); + jHeader.add( createSlotHeader( null ), "0,0,l,t" ); + } else { + tableLayout.insertColumn(0, 0); + } + + // add headers + for (int column=0;column=0 && nr< slots.length) + return slots[nr]; + else + return null; + } + + int getDayCount() { + return slots.length; + } + + int calcSlotNr(int x, int y) { + for (int i=0;i 15) { + calendar.add(Calendar.DATE, 7); + } + int startField = calendar.get(Calendar.DATE) -1; + for ( int i=startField;i < slots.length;i+=7 ) { + if (!slots[i].isEmpty() ) { + return false; + } + } + return true; + } + + public Collection getBlocks(int dayOfMonth) { + int index = dayOfMonth-1; + return Collections.unmodifiableCollection(slots[ index ].getBlocks()); + } + + public Collection getBlocks() { + ArrayList list = new ArrayList(); + for (int i=0;i=0 && nr< slots.length) + return slots[nr]; + else + return null; + } + + int getDayCount() { + return slots.length; + } + + int calcSlotNr(int x, int y) { + for (int i=0;i + *
  • 1: 1 rows per hour = 1 Hour
  • + *
  • 2: 2 rows per hour = 1/2 Hour
  • + *
  • 3: 3 rows per hour = 20 Minutes
  • + *
  • 4: 4 rows per hour = 15 Minutes
  • + *
  • 6: 6 rows per hour = 10 Minutes
  • + *
  • 12: 12 rows per hour = 5 Minutes
  • + * + * Default is 4. + */ + public void setRowsPerHour(int rowsPerHour) { + this.rowsPerHour = rowsPerHour; + } + + /** @see #setRowsPerHour */ + public int getRowsPerHour() { + return rowsPerHour; + } + + /** The size of each row (in pixel). Default is 15.*/ + public void setRowSize(int rowSize) { + this.rowSize = rowSize; + } + + public int getRowSize() { + return rowSize; + } + + public void setWorktime(int startHour, int endHour) { + this.startHour = startHour; + this.endHour = endHour; + if (getStartDate() != null) + calcMinMaxDates( getStartDate() ); + } + + + void calcMinMaxDates(Date date) { + Calendar calendar = createCalendar(); + calendar.setTime( date ); + // calendar.set(Calendar.DAY_OF_WEEK, 0); + calendar.set(Calendar.HOUR_OF_DAY,0); + calendar.set(Calendar.MINUTE,0); + calendar.set(Calendar.SECOND,0); + calendar.set(Calendar.MILLISECOND,0); + this.setStartDate( calendar.getTime() ); + + if ( this.getEndDate() == null + || (this.getEndDate().getTime() - this.getStartDate().getTime())<7L*7*24*3600*1000 + ) { + calendar.add(Calendar.DATE, 7*7); // 7 weeks (no scroll bar) + this.setEndDate( calendar.getTime() ); + } + } + + + public Collection getBlocks() { + ArrayList list = new ArrayList(); + for (int i=0;i=0 && nr< slots.length) { + return slots[nr]; + } else { + return null; + } + } + + int getDayCount() { + return weeksNr * 7; + } + + + int calcSlotNr(int x, int y) { + for (int i=0;i=viewRect.x && + (slotRect.x + slotRect.width)< + (viewRect.x + viewRect.width ) + ) + { + return; + } + + scrollSlot.scrollRectToVisible(new Rectangle(0 + ,viewRect.y + ,scrollSlot.getWidth() + ,10)); + } + + public void scrollToStart() { + int y = rowScale.getStartWorktimePixel(); + int x = 0; + scrollPane.getViewport().setViewPosition(new Point(x,y)); + } + + public Collection getBlocks() { + ArrayList list = new ArrayList(); + for (int i=0;i + *
  • 1: 1 rows per hour = 1 Hour
  • + *
  • 2: 2 rows per hour = 1/2 Hour
  • + *
  • 3: 3 rows per hour = 20 Minutes
  • + *
  • 4: 4 rows per hour = 15 Minutes
  • + *
  • 6: 6 rows per hour = 10 Minutes
  • + *
  • 12: 12 rows per hour = 5 Minutes
  • + * + * Default is 4. + */ + public void setRowsPerHour(int rowsPerHour) { + if ( rowScale instanceof LinearRowScale) + ((LinearRowScale)rowScale).setRowsPerHour(rowsPerHour); + } + + /** @see #setRowsPerHour */ + public int getRowsPerHour() { + if ( rowScale instanceof LinearRowScale) + return ((LinearRowScale)rowScale).getRowsPerHour(); + return 0; + } + + /** The size of each row (in pixel). Default is 15.*/ + public void setRowSize(int rowSize) { + if ( rowScale instanceof LinearRowScale) + ((LinearRowScale)rowScale).setRowSize(rowSize); + } + + public int getRowSize() { + if ( rowScale instanceof LinearRowScale) + return ((LinearRowScale)rowScale).getRowSize(); + return 0; + } + + public void setBackground(Color color) { + super.setBackground(color); + if (timeScale != null) + timeScale.setBackground(color); + } + + public void setEditable(boolean b) { + super.setEditable( b ); + // Hide the rest + for (int i= 0;i24) + throw new IllegalStateException("builder.getMax() is greater than 24"); + } + } + + //rowScale = new VariableRowScale(); + if ( rowScale instanceof LinearRowScale) + { + LinearRowScale linearScale = (LinearRowScale) rowScale; + int pixelPerHour = linearScale.getRowsPerHour() * linearScale.getRowSize(); + + timeScale.setBackground(getBackground()); + + linearScale.setTimeZone( timeZone ); + if ( isEditable()) + { + ((TimeScale)timeScale).setTimeIntervall(0, 24, pixelPerHour); + linearScale.setTimeIntervall( 0, 24); + } + else + { + ((TimeScale)timeScale).setTimeIntervall(start, end, pixelPerHour); + linearScale.setTimeIntervall( start, end); + } + linearScale.setWorktime( this.startHour, this.endHour); + } + else + { + timeScale.setBackground(getBackground()); + + ((TimeScale)timeScale).setTimeIntervall(0, 24, 60); + VariableRowScale periodScale = (VariableRowScale) rowScale; + periodScale.setTimeZone( timeZone ); + } + + // create Slots + Calendar calendar = createCalendar(); + calendar.setTime(getStartDate()); + calendar.set( Calendar.DAY_OF_WEEK, calendar.getFirstDayOfWeek()); + DraggingHandler draggingHandler = new DraggingHandler(this, rowScale,true); + SelectionHandler selectionHandler = new SelectionHandler(this); + + for (int i=0; i=0 && nr< daySlots.length) + return daySlots[nr]; + else + return null; + } + + int getDayCount() { + return daySlots.length; + } + + int calcSlotNr(int x, int y) { + for (int i=0;i0); + } + + + + /** + mintime und maxtime definieren das zeitintevall in vollen stunden. + die skalen-einteilung wird um vgap pixel nach unten verschoben + (um ggf. zu justieren). + */ + public void setTimeIntervall(int mintime, int maxtime, int pixelPerHour) { + removeAll(); + this.mintime = mintime; + this.maxtime = maxtime; + this.pixelPerHour = pixelPerHour; + //setBackground(Color.yellow); + //super(JSeparator.VERTICAL); + setLayout(null); + setPreferredSize(new Dimension( SCALE_WIDTH, (maxtime-mintime + 1) * pixelPerHour * repeat)); + } + + private void createHours(Locale locale) { + hours = new String[24]; + Calendar cal = Calendar.getInstance(locale); + SimpleDateFormat format = new SimpleDateFormat(useAM_PM ? "h" : "H",locale); + for (int i=0;i<24;i++) { + cal.set(Calendar.HOUR_OF_DAY,i); + hours[i] = format.format(cal.getTime()); + } + } + + + public void setSmallSize(boolean smallSize) { + this.smallSize = smallSize; + } + + public void setRepeat(int repeat, String[] days) { + this.repeat = repeat; + this.days = days; + } + + public void paint(Graphics g) { + super.paint(g); + int indent[]; + int heightHour = (int) fm1.getLineMetrics("12",g).getHeight() ; + int heightEnding = (int) fm2.getLineMetrics("12",g).getHeight() ; + int current_y ; + + // Compute indentations + FontMetrics fm; + String[] indent_string = new String[3] ; + if ( days != null ) { + indent_string[0] = "M"; + indent_string[1] = "M2"; + indent_string[2] = "M22"; + } else { + indent_string[0] = ""; + indent_string[1] = "2"; + indent_string[2] = "22"; + } + if ( smallSize ) { + fm = fm2; + } else { + fm = fm1; + } + + indent = new int[3]; + for(int i=0; i<3; i++) { + indent[i] = (int) fm.stringWidth(indent_string[i]) ; + } + + Rectangle rect = g.getClipBounds(); + //System.out.println(mintime + " - " + maxtime); + int height = (maxtime - mintime) * pixelPerHour + 1 ; + + if ( days != null ) { + g.drawLine(indent[0]+1,0,indent[0]+1,repeat*height); + } + + for (int r=0; r= pad ) { + prefix = days[r].substring(i-mintime-pad,i-mintime+1-pad); + } else { + prefix = null; + } + + if (y >= rect.y && y <= (rect.y + rect.height)) { + g.drawLine(i == mintime ? 0:indent[0]+1,y,SCALE_WIDTH ,y); + } + if (y >= rect.y -heightHour && y <= (rect.y + rect.height) + heightHour ) { + if ( smallSize ) { + g.setFont(fontSmall); + } else { + g.setFont(fontLarge); + } + if ( prefix != null ) { + g.drawString(prefix, (indent[0]-fm.stringWidth(prefix)+1)/2,y + heightEnding); + } + g.drawString(hours[i],(hour < 10) ? indent[1]+2:indent[0]+2,y + ( smallSize ? heightEnding : heightHour)); + if ( !smallSize ) { + g.setFont(fontSmall); + } + g.drawString(ending, indent[2]+2,y + heightEnding); + } + } + } + } +} + diff --git a/Rapla/src/org/rapla/components/calendarview/swing/ViewListener.java b/Rapla/src/org/rapla/components/calendarview/swing/ViewListener.java new file mode 100644 index 0000000..7f72bb9 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendarview/swing/ViewListener.java @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.calendarview.swing; + +import java.awt.Point; +import java.awt.Component; +import java.util.Date; + +import org.rapla.components.calendarview.Block; + +/** Listeners for user-changes in the weekview.*/ +public interface ViewListener { + /** Invoked when the user invokes the slot-contex (right-clicks on slot). + The selected area and suggested + coordinates at which the popup menu can be shown are passed.*/ + void selectionPopup(Component slotComponent,Point p,Date start,Date end, int slotNr); + /** Invoked when the selection has changed.*/ + void selectionChanged(Date start,Date end); + /** Invoked when the user invokes a block-context (right-clicks on a block). + The suggested coordinates at which the popup menu can be shown are passed.*/ + void blockPopup(Block block,Point p); + /** Invoked when the user invokes a block-edit (double-clicks on a block). + The suggested coordinates at which the popup menu can be shown are passed.*/ + void blockEdit(Block block,Point p); + /** Invoked when the user has dragged/moved a block */ + void moved(Block block,Point p,Date newStart); + /** Invoked when the user has resized a block */ + void resized(Block block,Point p,Date newStart, Date newEnd); +} + + + + + + + diff --git a/Rapla/src/org/rapla/components/calendarview/swing/scaling/IRowScale.java b/Rapla/src/org/rapla/components/calendarview/swing/scaling/IRowScale.java new file mode 100644 index 0000000..753e331 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendarview/swing/scaling/IRowScale.java @@ -0,0 +1,20 @@ +package org.rapla.components.calendarview.swing.scaling; + +import java.util.Date; + + + +public interface IRowScale extends IRowScaleSmall +{ + int calcHour( int index ); + int calcMinute( int index ); + int getSizeInPixel(); + int getMaxRows(); + int getRowsPerDay(); + int getYCoord( Date time ); + int getStartWorktimePixel(); + int getEndWorktimePixel(); + int getSizeInPixelBetween( int startRow, int endRow ); + + boolean isPaintRowThick( int row ); +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/components/calendarview/swing/scaling/IRowScaleSmall.java b/Rapla/src/org/rapla/components/calendarview/swing/scaling/IRowScaleSmall.java new file mode 100644 index 0000000..3e66f10 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendarview/swing/scaling/IRowScaleSmall.java @@ -0,0 +1,11 @@ +package org.rapla.components.calendarview.swing.scaling; + + +public interface IRowScaleSmall +{ + int getYCoordForRow( int row ); + int getRowSizeForRow( int row ); + int calcRow( int y ); + int trim( int y ); + int getDraggingCorrection(int y); +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/components/calendarview/swing/scaling/LinearRowScale.java b/Rapla/src/org/rapla/components/calendarview/swing/scaling/LinearRowScale.java new file mode 100644 index 0000000..0626bd2 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendarview/swing/scaling/LinearRowScale.java @@ -0,0 +1,161 @@ +package org.rapla.components.calendarview.swing.scaling; + +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + + + +public class LinearRowScale implements IRowScale +{ + private int rowSize = 15; + private int rowsPerHour = 4; + final private static int MINUTES_PER_HOUR= 60; + private TimeZone timeZone = TimeZone.getDefault(); + + private int mintime; + private int maxtime; + private int workstart; + private int workend; + + public LinearRowScale() + { + } + + public void setTimeZone( TimeZone timeZone) + { + this.timeZone = timeZone; + } + + public int getRowsPerDay() + { + return rowsPerHour * 24; + } + + public void setRowSize( int rowSize ) + { + this.rowSize = rowSize; + } + + public int getRowSize() + { + return rowSize; + } + + public int getRowsPerHour() + { + return rowsPerHour; + } + + public void setRowsPerHour( int rowsPerHour ) + { + this.rowsPerHour = rowsPerHour; + } + + public int calcHour(int index) { + return index / rowsPerHour; + } + + public int calcMinute(int index) { + int minutesPerRow = 60 / rowsPerHour; + return (index % rowsPerHour) * (minutesPerRow); + } + + public int getSizeInPixel() + { + return rowSize * getMaxRows(); + } + + public int getMaxRows() + { + int max; + max = rowsPerHour * (maxtime - mintime) ; + return max; + } + + private int getMinuteOfDay(Date time) { + Calendar cal = getCalendar(); + cal.setTime(time); + return (cal.get(Calendar.HOUR_OF_DAY )) * MINUTES_PER_HOUR + cal.get(Calendar.MINUTE); + } + + public int getYCoord(Date time) { + int diff = getMinuteOfDay(time) - mintime * MINUTES_PER_HOUR ; + int pixelPerHour= rowSize * rowsPerHour; + return (diff * pixelPerHour) / MINUTES_PER_HOUR; + } + + public int getStartWorktimePixel() + { + int pixelPerHour= rowSize * rowsPerHour; + int starty = pixelPerHour * workstart; + return starty; + } + + public int getEndWorktimePixel() + { + int pixelPerHour= rowSize * rowsPerHour; + int endy = pixelPerHour * workend; + return endy; + } + + + private Calendar calendar = null; + private Calendar getCalendar() { + // Lazy creation of the calendar + if (calendar == null) + calendar = Calendar.getInstance(timeZone); + return calendar; + } + + public boolean isPaintRowThick( int row ) + { + return row % rowsPerHour == 0; + } + + public void setTimeIntervall( int startHour, int endHour ) + { + mintime = startHour; + maxtime = endHour; + } + + public void setWorktime( int startHour, int endHour ) + { + workstart = startHour; + workend = endHour; + } + + public int getYCoordForRow( int row ) + { + return row * rowSize; + } + + public int getSizeInPixelBetween( int startRow, int endRow ) + { + return (endRow - startRow) * rowSize; + } + + public int getRowSizeForRow( int row ) + { + return rowSize; + } + + public int calcRow(int y) { + int rowsPerDay = getRowsPerDay(); + int row = (y-3) / rowSize; + return Math.min(Math.max(0, row), rowsPerDay -1); + } + + public int trim(int y ) + { + return (y / rowSize) * rowSize; + } + + public int getDraggingCorrection( int y) + { + return rowSize / 2; + } + + + +} diff --git a/Rapla/src/org/rapla/components/calendarview/swing/scaling/OneRowScale.java b/Rapla/src/org/rapla/components/calendarview/swing/scaling/OneRowScale.java new file mode 100644 index 0000000..516e6f7 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendarview/swing/scaling/OneRowScale.java @@ -0,0 +1,38 @@ +package org.rapla.components.calendarview.swing.scaling; + + + +public class OneRowScale implements IRowScaleSmall +{ + + public int getYCoordForRow( int row ) + { + return 0; + } + + public int getSizeInPixelBetween( int startRow, int endRow ) + { + return 0; + } + + public int getRowSizeForRow( int row ) + { + return 15; + } + + public int calcRow( int y ) + { + return 0; + } + + public int trim( int y ) + { + return 0; + } + + public int getDraggingCorrection( int y) + { + return 15 / 2; + } + +} diff --git a/Rapla/src/org/rapla/components/calendarview/swing/scaling/VariableRowScale.java b/Rapla/src/org/rapla/components/calendarview/swing/scaling/VariableRowScale.java new file mode 100644 index 0000000..d6ba4a2 --- /dev/null +++ b/Rapla/src/org/rapla/components/calendarview/swing/scaling/VariableRowScale.java @@ -0,0 +1,197 @@ +package org.rapla.components.calendarview.swing.scaling; + +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + + + +public class VariableRowScale implements IRowScale +{ + PeriodRow[] periodRows; + private int hourSize = 60; + final private static int MINUTES_PER_HOUR= 60; + private TimeZone timeZone = TimeZone.getDefault(); + + private int mintime =0 ; + /* + private int maxtime = 24; + private int workstart = 0; + private int workend = 24; + */ + class PeriodRow + { + int minutes; + int ypos; + int startMinute; + PeriodRow( int minutes ) + { + this.minutes = minutes; + } + + public int getRowSize() + { + return (minutes * hourSize) / 60; + } + + } + + public VariableRowScale() + { + periodRows = new PeriodRow[] { + new PeriodRow( 60 * 8 ), + new PeriodRow( 45 ), + new PeriodRow( 60 ), + new PeriodRow( 10 ), + new PeriodRow( 110 ), + new PeriodRow( 60 + 15), + new PeriodRow( 60 * 3 ), + new PeriodRow( 60 * 8 ) + }; + + for ( int i=0;i< periodRows.length-1;i++) + { + periodRows[i+1].ypos = periodRows[i].ypos + periodRows[i].getRowSize(); + periodRows[i+1].startMinute = periodRows[i].startMinute + periodRows[i].minutes; + } + } + + public void setTimeZone( TimeZone timeZone) + { + this.timeZone = timeZone; + } + + public int getRowsPerDay() + { + return periodRows.length; + } + + + + public int getSizeInPixel() + { + PeriodRow lastRow = getLastRow(); + return lastRow.ypos + lastRow.getRowSize(); + } + + public int getMaxRows() + { + return periodRows.length; + } + + private int getMinuteOfDay(Date time) { + Calendar cal = getCalendar(); + cal.setTime(time); + return (cal.get(Calendar.HOUR_OF_DAY )) * MINUTES_PER_HOUR + cal.get(Calendar.MINUTE); + } + + public int calcHour(int index) { + return periodRows[index].startMinute / MINUTES_PER_HOUR; + } + + public int calcMinute(int index) { + return periodRows[index].startMinute % MINUTES_PER_HOUR; + } + + public int getYCoord(Date time) { + int diff = getMinuteOfDay(time) - mintime * MINUTES_PER_HOUR ; + return (diff * hourSize) / MINUTES_PER_HOUR; + } + + public int getStartWorktimePixel() + { + return periodRows[0].getRowSize(); + } + + public int getEndWorktimePixel() + { + PeriodRow lastRow = getLastRow(); + return lastRow.ypos; + } + + private PeriodRow getLastRow() + { + PeriodRow lastRow = periodRows[periodRows.length-1]; + return lastRow; + } + + + private Calendar calendar = null; + private Calendar getCalendar() { + // Lazy creation of the calendar + if (calendar == null) + calendar = Calendar.getInstance(timeZone); + return calendar; + } + + public boolean isPaintRowThick( int row ) + { + return true; + } +/* + public void setTimeIntervall( int startHour, int endHour ) + { + mintime = startHour; + maxtime = endHour; + } + + public void setWorktime( int startHour, int endHour ) + { + workstart = startHour; + workend = endHour; + } +*/ + public int getYCoordForRow( int row ) + { + if ( row < 0 || row >= periodRows.length) + { + return row* 400; + } + return periodRows[row].ypos; + } + + public int getSizeInPixelBetween( int startRow, int endRow ) + { + if ( startRow < 0 || endRow < 0 || startRow >= periodRows.length || endRow >=periodRows.length) + { + return (endRow - startRow) * 400; + } + + return periodRows[endRow].ypos - periodRows[startRow].ypos; + } + + public int getRowSizeForRow( int row ) + { + /* if ( row < 0 || row >= periodRows.length) + { + return 60; + } + */ + return periodRows[row].getRowSize(); + } + + public int calcRow(int y) { + for ( int i=0;i< periodRows.length;i++) + { + PeriodRow row =periodRows[i]; + if (row.ypos + row.getRowSize() >=y) + return i; + } + return 0; + } + + public int trim(int y ) + { + int row = calcRow( y ); + int rowSize = getRowSizeForRow( row); + return (y / rowSize) * rowSize; + } + + public int getDraggingCorrection(int y) + { + return 0; + } + + + +} diff --git a/Rapla/src/org/rapla/components/iolayer/ComponentPrinter.java b/Rapla/src/org/rapla/components/iolayer/ComponentPrinter.java new file mode 100644 index 0000000..0395e7a --- /dev/null +++ b/Rapla/src/org/rapla/components/iolayer/ComponentPrinter.java @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.iolayer; + +import java.awt.*; +import java.awt.print.*; +import javax.swing.*; + +/** Use this to print an awt-Component on one page. + */ +public class ComponentPrinter implements Printable +{ + private Component component; + private boolean scaleToFit; + + public ComponentPrinter(Component c, boolean scaleToFit) + { + component= c; + this.scaleToFit = scaleToFit; + } + + + public int print(Graphics g, PageFormat format, int pagenumber) throws PrinterException + { + if (pagenumber>0) { return Printable.NO_SUCH_PAGE; } + Graphics2D g2 = (Graphics2D) g; + if ( scaleToFit) { + g2.translate(format.getImageableX(), format.getImageableY()); + scaleToFit(g2, format, component); + } + component.printAll(g2); + RepaintManager rm = RepaintManager.currentManager(component); + boolean db= rm.isDoubleBufferingEnabled(); + try { + rm.setDoubleBufferingEnabled(false); + component.printAll(g2); + } finally { + rm.setDoubleBufferingEnabled(db); + } + return Printable.PAGE_EXISTS; + } + + private static void scaleToFit(Graphics2D g, PageFormat format, Component c) + { + Dimension dim = c.getPreferredSize(); + double sx = format.getImageableWidth() / dim.width; + double sy = format.getImageableHeight() / dim.height; + if (sx < sy) { sy = sx; } else { sx = sy; } + g.scale(sx, sy); + } +} + + + + + + diff --git a/Rapla/src/org/rapla/components/iolayer/DEPENDECIES b/Rapla/src/org/rapla/components/iolayer/DEPENDECIES new file mode 100644 index 0000000..e4ab5c8 --- /dev/null +++ b/Rapla/src/org/rapla/components/iolayer/DEPENDECIES @@ -0,0 +1,5 @@ +This component depends on the following packages (including subpackages): +java.* +javax.jnlp.* (For the WebstartPrinter) +javax.print.* (For the saving in postscript-format) +org.apache.avalon.framework.* diff --git a/Rapla/src/org/rapla/components/iolayer/DefaultIO.java b/Rapla/src/org/rapla/components/iolayer/DefaultIO.java new file mode 100644 index 0000000..0f2e125 --- /dev/null +++ b/Rapla/src/org/rapla/components/iolayer/DefaultIO.java @@ -0,0 +1,329 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.iolayer; + +import java.awt.Component; +import java.awt.FileDialog; +import java.awt.Frame; +import java.awt.Toolkit; +import java.awt.datatransfer.ClipboardOwner; +import java.awt.datatransfer.Transferable; +import java.awt.print.PageFormat; +import java.awt.print.Paper; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.awt.print.PrinterJob; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import javax.swing.JFileChooser; +import javax.swing.filechooser.FileFilter; + +import org.apache.avalon.framework.logger.AbstractLogEnabled; +import org.apache.avalon.framework.logger.Logger; + +public class DefaultIO extends AbstractLogEnabled implements IOInterface{ + /** + * Name of all Rapla printjobs (used in dialogs, printerqueue, etc). + */ + public final static String RAPLA_JOB= "Rapla Printjob"; + public final static String PRINT_EXPORT_CLASS = DefaultIO.class.getPackage().getName() + ".PrintExport"; + public PrinterJob job; + + public DefaultIO(Logger logger) { + enableLogging( logger); + } + + private PrinterJob getJob() { + if (job == null) + job = PrinterJob.getPrinterJob(); + return job; + } + + public PageFormat defaultPage() throws UnsupportedOperationException { + try { + PageFormat format = getJob().defaultPage(); + return format; + } catch (SecurityException ex) { + return new PageFormat(); + } + } + + public PageFormat showFormatDialog(PageFormat format) throws UnsupportedOperationException { + logPaperSize (format.getPaper()); + format = getJob().pageDialog(format); + logPaperSize (format.getPaper()); + return format; + } + + public void setContents(Transferable transferable, ClipboardOwner owner) { + Toolkit.getDefaultToolkit().getSystemClipboard().setContents( transferable, owner ); + } + + public Transferable getContents( ClipboardOwner owner) { + return Toolkit.getDefaultToolkit().getSystemClipboard().getContents( owner); + } + + public boolean supportsPostscriptExport() { + if (!supportsPrintService()) + return false; + + Object exporter; + try { + exporter = getClass().getClassLoader().loadClass( PRINT_EXPORT_CLASS ).newInstance(); + } catch (ClassNotFoundException ex) { + getLogger().warn("No support for " + PRINT_EXPORT_CLASS + ". Compile with jdk >=1.4."); + return false; + } catch (Exception ex) { + getLogger().warn("No support for " + PRINT_EXPORT_CLASS + ". Run with jdk >= 1.4.1 : " + ex.getMessage()); + return false; + } + try { + Method method = exporter.getClass().getMethod("supportsPostscriptExport",new Class[] {}); + boolean bExport = ((Boolean) method.invoke(exporter, new Object[] {})).booleanValue(); + if (!bExport) + getLogger().warn("No support for PostscriptExport"); + return bExport; + } catch (NoSuchMethodException ex) { + getLogger().error(ex.getMessage()); + } catch (IllegalAccessException ex) { + getLogger().error(ex.getMessage()); + } catch (InvocationTargetException excont) { + Throwable ex = excont.getTargetException(); + getLogger().error(ex.getMessage(),ex); + } + return false; + } + + private boolean supportsPrintService() { + try { + getClass().getClassLoader().loadClass("javax.print.StreamPrintServiceFactory"); + return true; + } catch (ClassNotFoundException ex) { + getLogger().warn("No support for javax.print.StreamPrintServiceFactory"); + return false; + } + } + + protected void callExport(Printable printable, PageFormat format,OutputStream out) throws UnsupportedOperationException,IOException { + Object exporter; + try + { + exporter = getClass().getClassLoader().loadClass( PRINT_EXPORT_CLASS ).newInstance(); + } + catch (Exception ex) + { + throw new UnsupportedOperationException("JDK < 1.4 PrintService unavailable"); + } + try + { + Method method = exporter.getClass().getMethod("save",new Class[] {Printable.class,PageFormat.class,OutputStream.class}); + method.invoke(exporter, new Object[] {printable,format,out}); + } + catch (InvocationTargetException excont) + { + Throwable ex = excont.getTargetException(); + if (ex instanceof IOException) + { + throw (IOException)ex; + } + if (ex instanceof UnsupportedOperationException) + { + throw (UnsupportedOperationException)ex; + } + getLogger().error(ex.getMessage(),ex); + throw new IOException(ex.getMessage()); + } + catch (Exception ex) + { + throw new UnsupportedOperationException(ex.getMessage()); + } + } + + public String saveAsPostscriptShowDialog(String dir,Printable printable,PageFormat format,boolean askFormat,Component owner) throws UnsupportedOperationException,IOException { + if (askFormat) { format= showFormatDialog(format); } + JFileChooser chooser = new JFileChooser(); + + if (dir != null) + chooser.setCurrentDirectory(new File(dir)); + + // Note: source for ExampleFileFilter can be found in FileChooserDemo, + // under the demo/jfc directory in the Java 2 SDK, Standard Edition. + chooser.setFileFilter( new PSFileFilter()); + int returnVal = chooser.showOpenDialog(owner); + if(returnVal != JFileChooser.APPROVE_OPTION) + return null; + + OutputStream out = new FileOutputStream(chooser.getSelectedFile()); + callExport(printable,format,out); + return chooser.getSelectedFile().getPath(); + } + + private class PSFileFilter extends FileFilter { + public boolean accept(File file) { + return (file.isDirectory() || file.getName().toLowerCase().endsWith(".ps")); + } + + public String getDescription() { + return "Postscript Files"; + } + } + public void saveAsPostscript(Printable printable,PageFormat format,OutputStream out) throws UnsupportedOperationException,IOException { + callExport(printable,format,out); + } + + /** + Prints an awt or swing component. + @param askFormat If true a dialog will show up to allow the user to edit printformat. + */ + public boolean print(Printable printable, PageFormat format, boolean askFormat) throws PrinterException { + getJob().setPrintable(printable, format); + getJob().setJobName(RAPLA_JOB); + + if (askFormat) { + if (getJob().printDialog()) { + logPaperSize (format.getPaper()); + getJob().print(); + return true; + } + } else { + getJob().print(); + return true; + } + getJob().cancel(); + return false; + } + + void logPaperSize(Paper paper) { + if (getLogger().isDebugEnabled()) + getLogger().debug( + (paper.getImageableX()/72) * INCH_TO_MM + +", " +(paper.getImageableY()/72) * INCH_TO_MM + +", " +(paper.getImageableWidth() /72) * INCH_TO_MM + +", " +(paper.getImageableHeight() /72) * INCH_TO_MM + ); + } + + public String saveFile(Frame frame,String dir,final String[] fileExtensions, String filename, byte[] content) throws IOException { + final FileDialog fd = new FileDialog(frame, "Save File", FileDialog.SAVE); + + if ( dir == null) + { + try + { + dir = getDirectory(); + } + catch (Exception ex) + { + + } + } + if ( dir != null) + { + fd.setDirectory(dir); + } + fd.setFile(filename); + if ( fileExtensions.length > 0) + { + fd.setFilenameFilter( new FilenameFilter() { + + public boolean accept(File dir, String name) { + final String[] split = name.split("."); + if ( split.length > 1) + { + String extension = split[split.length -1].toLowerCase(); + for ( String ext: fileExtensions) + { + if ( ext.toLowerCase().equals(extension )) + { + return true; + } + } + } + return false; + } + }); + } + fd.setLocation(50, 50); + fd.setVisible( true); + final String savedFileName = fd.getFile(); + + if (savedFileName == null) { + return null; + } + + String path = createFullPath(fd); + final File savedFile = new File( path); + writeFile(savedFile, content); + return path; + } + + public FileContent openFile(Frame frame,String dir, String[] fileExtensions) throws IOException { + final FileDialog fd = new FileDialog(frame, "Open File", FileDialog.LOAD); + + if ( dir == null) + { + dir = getDirectory(); + } + fd.setDirectory(dir); + fd.setLocation(50, 50); + fd.setVisible( true); + final String openFileName = fd.getFile(); + + if (openFileName == null) { + return null; + } + String path = createFullPath(fd); + final FileInputStream openFile = new FileInputStream( path); + FileContent content = new FileContent(); + content.setName( openFileName); + content.setInputStream( openFile ); + return content; + } + + + private String getDirectory() { + final String userHome = System.getProperty("user.home"); + + if (userHome == null) { + final File execDir = new File(""); + return execDir.getAbsolutePath(); + } + + return userHome; + } + + private String createFullPath(final FileDialog fd) { + return fd.getDirectory() + + System.getProperty("file.separator").charAt(0) + fd.getFile(); + } + + private void writeFile(final File savedFile, byte[] content) throws IOException { + final FileOutputStream out; + out = new FileOutputStream(savedFile); + out.write( content); + out.flush(); + out.close(); + } + +} + + + + diff --git a/Rapla/src/org/rapla/components/iolayer/FileContent.java b/Rapla/src/org/rapla/components/iolayer/FileContent.java new file mode 100644 index 0000000..3fab3f0 --- /dev/null +++ b/Rapla/src/org/rapla/components/iolayer/FileContent.java @@ -0,0 +1,23 @@ +package org.rapla.components.iolayer; + +import java.io.InputStream; + +public class FileContent { + String name; + InputStream inputStream; + public InputStream getInputStream() { + return inputStream; + } + + void setInputStream(InputStream inputStream) { + this.inputStream = inputStream; + } + + public String getName() { + return name; + } + + void setName(String name) { + this.name = name; + } +} diff --git a/Rapla/src/org/rapla/components/iolayer/IOInterface.java b/Rapla/src/org/rapla/components/iolayer/IOInterface.java new file mode 100644 index 0000000..4bb7992 --- /dev/null +++ b/Rapla/src/org/rapla/components/iolayer/IOInterface.java @@ -0,0 +1,72 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.components.iolayer; + +import java.awt.Frame; +import java.awt.datatransfer.ClipboardOwner; +import java.awt.datatransfer.Transferable; +import java.awt.print.PageFormat; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.io.IOException; +import java.io.OutputStream; + +/** The IO layer is an abstraction from the io differences in webstart or desktop mode */ +public interface IOInterface { + String ROLE = IOInterface.class.getName(); + + String saveAsPostscriptShowDialog( + String toDir + ,Printable printable + ,PageFormat format + ,boolean askFormat + ,java.awt.Component owner + ) + throws IOException,UnsupportedOperationException; + + void saveAsPostscript( + Printable printable + ,PageFormat format + ,OutputStream out + ) + throws IOException,UnsupportedOperationException; + + boolean print( + Printable printable + ,PageFormat format + ,boolean askFormat + ) + throws PrinterException,UnsupportedOperationException; + + PageFormat defaultPage() + throws UnsupportedOperationException; + + PageFormat showFormatDialog(PageFormat format) + throws UnsupportedOperationException; + + void setContents(Transferable transferable, ClipboardOwner owner); + Transferable getContents( ClipboardOwner owner); + public String saveFile(Frame frame,String dir, String[] fileExtensions,String path, byte[] content) throws IOException; + public FileContent openFile(Frame frame,String dir, String[] fileExtensions) throws IOException; + + boolean supportsPostscriptExport(); + + public double INCH_TO_MM = 25.40006; + public double MM_TO_INCH = 1.0 / INCH_TO_MM; +} + + + + + diff --git a/Rapla/src/org/rapla/components/iolayer/PrintExport.java b/Rapla/src/org/rapla/components/iolayer/PrintExport.java new file mode 100644 index 0000000..d1b47df --- /dev/null +++ b/Rapla/src/org/rapla/components/iolayer/PrintExport.java @@ -0,0 +1,102 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.components.iolayer; + +import java.awt.print.*; +import javax.print.*; +import javax.print.attribute.*; +import javax.print.attribute.standard.*; +import java.io.*; + +/** This class will only work with JDK 1.4 and above, it + uses javax.print.PrintService for exporting to postscript format + */ + +public class PrintExport { + static DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE; + + private static StreamPrintServiceFactory getPSExportServiceFactory() { + StreamPrintServiceFactory []factories = + StreamPrintServiceFactory.lookupStreamPrintServiceFactories(flavor,"application/postscript"); + + if (factories.length == 0) { + return null; + } + /* + for (int i=0;i +The IO layer is an abstraction from the io differences in webstart or desktop mode. +With JDK 1.4 or higher PS export is supported. + + + + diff --git a/Rapla/src/org/rapla/components/layout/DEPENDENCIES b/Rapla/src/org/rapla/components/layout/DEPENDENCIES new file mode 100644 index 0000000..02d1cf7 --- /dev/null +++ b/Rapla/src/org/rapla/components/layout/DEPENDENCIES @@ -0,0 +1,3 @@ +This component depends on the following packages (including subpackages): +java.* + diff --git a/Rapla/src/org/rapla/components/layout/TableLayout.java b/Rapla/src/org/rapla/components/layout/TableLayout.java new file mode 100644 index 0000000..61e0f10 --- /dev/null +++ b/Rapla/src/org/rapla/components/layout/TableLayout.java @@ -0,0 +1,2133 @@ +package org.rapla.components.layout; + + + +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Insets; +import java.util.LinkedList; +import java.util.ListIterator; + + +/** + * TableLayout is a layout manager that arranges components in rows and columns + * like a spreadsheet. TableLayout allows each row or column to be a different + * size. A row or column can be given an absolute size in pixels, a percentage + * of the available space, or it can grow and shrink to fill the remaining space + * after other rows and columns have been resized. + * + *

    Using spreadsheet terminology, a cell is the intersection of a row and + * column. Cells have finite, non-negative sizes measured in pixels. The + * dimensions of a cell depend solely upon the dimensions of its row and column. + *

    + * + *

    A component occupies a rectangular group of one or more cells. If the + * component occupies more than one cell, the component is resized to fit + * perfectly in the rectangular region of cells. If the component occupies a + * single cell, it can be aligned in four ways within that cell.

    + * + *

    A single cell component can be stretched horizontally to fit the cell + * (full justification), or it can be placed in the center of the cell. The + * component could also be left justified or right justified. Similarly, the + * component can be full, center, top, or bottom justified in the vertical.

    + * + *
    + * public static void main (String args[])
    + * {
    + *     // Create a frame
    + *     Frame frame = new Frame("Example of TableLayout");
    + *     frame.setBounds (100, 100, 300, 300);
    + * 
    + *     // Create a TableLayout for the frame
    + *     double border = 10;
    + *     double size[][] =
    + *         {{border, 0.10, 20, TableLayout.FILL, 20, 0.20, border},  // Columns
    + *          {border, 0.20, 20, TableLayout.FILL, 20, 0.20, border}}; // Rows
    + * 
    + *     frame.setLayout (new TableLayout(size));
    + * 
    + *     // Create some buttons
    + *     String label[] = {"Top", "Bottom", "Left", "Right", "Center", "Overlap"};
    + *     Button button[] = new Button[label.length];
    + * 
    + *     for (int i = 0; i < label.length; i++)
    + *         button[i] = new Button(label[i]);
    + * 
    + *     // Add buttons
    + *     frame.add (button[0], "1, 1, 5, 1"); // Top
    + *     frame.add (button[1], "1, 5, 5, 5"); // Bottom
    + *     frame.add (button[2], "1, 3      "); // Left
    + *     frame.add (button[3], "5, 3      "); // Right
    + *     frame.add (button[4], "3, 3, c, c"); // Center
    + *     frame.add (button[5], "3, 3, 3, 5"); // Overlap
    + * 
    + *     // Allow user to close the window to terminate the program
    + *     frame.addWindowListener
    + *         (new WindowListener()
    + *             {
    + *                 public void windowClosing (WindowEvent e)
    + *                 {
    + *                     System.exit (0);
    + *                 }
    + * 
    + *                 public void windowOpened (WindowEvent e) {}
    + *                 public void windowClosed (WindowEvent e) {}
    + *                 public void windowIconified (WindowEvent e) {}
    + *                 public void windowDeiconified (WindowEvent e) {}
    + *                 public void windowActivated (WindowEvent e) {}
    + *                 public void windowDeactivated (WindowEvent e) {}
    + *             }
    + *         );
    + * 
    + *     // Show frame
    + *     frame.show();
    + * }
    + * 
    + * + * @author Daniel E. Barbalace + */ + +public class TableLayout implements + java.awt.LayoutManager2, + TableLayoutConstants +{ + + + +/** Default row/column size */ +protected static final double defaultSize[][] = {{}, {}}; + + + +/** Widths of columns expressed in absolute and relative terms */ +protected double columnSpec[]; + +/** Heights of rows expressed in absolute and relative terms */ +protected double rowSpec[]; + +/** Widths of columns in pixels */ +protected int columnSize[]; + +/** Heights of rows in pixels */ +protected int rowSize[]; + +/** Offsets of columns in pixels. The left boarder of column n is at + columnOffset[n] and the right boarder is at columnOffset[n + 1] for all + columns including the last one. columnOffset.length = columnSize.length + 1 */ +protected int columnOffset[]; + +/** Offsets of rows in pixels. The left boarder of row n is at + rowOffset[n] and the right boarder is at rowOffset[n + 1] for all + rows including the last one. rowOffset.length = rowSize.length + 1 */ +protected int rowOffset[]; + +/** List of components and their sizes */ +protected LinkedList list; + +/** Indicates whether or not the size of the cells are known for the last known + size of the container. If dirty is true or the container has been resized, + the cell sizes must be recalculated using calculateSize. */ +protected boolean dirty; + +/** Previous known width of the container */ +protected int oldWidth; + +/** Previous known height of the container */ +protected int oldHeight; + + + +//****************************************************************************** +//** Constructors *** +//****************************************************************************** + + + +/** + * Constructs an instance of TableLayout. This TableLayout will have one row + * and one column. + */ + +public TableLayout () +{ + this (defaultSize); +} + + + +/** + * Constructs an instance of TableLayout. + * + * @param size widths of columns and heights of rows in the format, + * {{col0, col1, col2, ..., colN}, {row0, row1, row2, ..., rowM}} + * If this parameter is invalid, the TableLayout will have + * exactly one row and one column. + */ + +public TableLayout (double size[][]) +{ + // Make sure rows and columns and nothing else is specified + if ((size != null) && (size.length == 2)) + { + // Get the rows and columns + double tempCol[] = size[0]; + double tempRow[] = size[1]; + + // Create new rows and columns + columnSpec = new double[tempCol.length]; + rowSpec = new double[tempRow.length]; + + // Copy rows and columns + System.arraycopy (tempCol, 0, columnSpec, 0, columnSpec.length); + System.arraycopy (tempRow, 0, rowSpec, 0, rowSpec.length); + + // Make sure rows and columns are valid + for (int counter = 0; counter < columnSpec.length; counter++) + if ((columnSpec[counter] < 0.0) && + (columnSpec[counter] != FILL) && + (columnSpec[counter] != PREFERRED) && + (columnSpec[counter] != MINIMUM)) + { + columnSpec[counter] = 0.0; + } + + for (int counter = 0; counter < rowSpec.length; counter++) + if ((rowSpec[counter] < 0.0) && + (rowSpec[counter] != FILL) && + (rowSpec[counter] != PREFERRED) && + (rowSpec[counter] != MINIMUM)) + { + rowSpec[counter] = 0.0; + } + } + else + { + double tempCol[] = {FILL}; + double tempRow[] = {FILL}; + + setColumn (tempCol); + setRow (tempRow); + } + + // Create an empty list of components + list = new LinkedList(); + + // Indicate that the cell sizes are not known + dirty = true; +} + + + +//****************************************************************************** +//** Get/Set methods *** +//****************************************************************************** + + + +/** + * Gets the constraints of a given component. + * + * @param component desired component + * + * @return If the given component is found, the constraints associated with + * that component. If the given component is null or is not found, + * null is returned. + */ + +public TableLayoutConstraints getConstraints (Component component) +{ + ListIterator iterator = list.listIterator(0); + + while (iterator.hasNext()) + { + Entry entry = (Entry) iterator.next(); + + if (entry.component == component) + return new TableLayoutConstraints + (entry.col1, entry.row1, entry.col2, entry.row2, + entry.hAlign, entry.vAlign); + } + + return null; +} + + + +/** + * Sets the constraints of a given component. + * + * @param component desired component. This parameter cannot be null. + * @param constraint new set of constraints. This parameter cannot be null. + * + */ + +public void setConstraints + (Component component, TableLayoutConstraints constraint) +{ + // Check parameters + if (component == null) + throw new IllegalArgumentException + ("Parameter component cannot be null."); + else if (constraint == null) + throw new IllegalArgumentException + ("Parameter constraint cannot be null."); + + // Find and update constraints for the given component + ListIterator iterator = list.listIterator(0); + + while (iterator.hasNext()) + { + Entry entry = (Entry) iterator.next(); + + if (entry.component == component) + iterator.set (new Entry(component, constraint)); + } +} + + + +/** + * Adjusts the number and sizes of rows in this layout. After calling this + * method, the caller should request this layout manager to perform the + * layout. This can be done with the following code: + * + *
    + *     layout.layoutContainer(container);
    + *     container.repaint();
    + * 
    + * + * or + * + *
    + *     window.pack()
    + * 
    + * + * If this is not done, the changes in the layout will not be seen until the + * container is resized. + * + * @param column heights of each of the columns + * + * @see #getColumn + */ + +public void setColumn (double column[]) +{ + // Copy columns + columnSpec = new double[column.length]; + System.arraycopy (column, 0, columnSpec, 0, columnSpec.length); + + // Make sure columns are valid + for (int counter = 0; counter < columnSpec.length; counter++) + if ((columnSpec[counter] < 0.0) && + (columnSpec[counter] != FILL) && + (columnSpec[counter] != PREFERRED) && + (columnSpec[counter] != MINIMUM)) + { + columnSpec[counter] = 0.0; + } + + // Indicate that the cell sizes are not known + dirty = true; +} + + + +/** + * Adjusts the number and sizes of rows in this layout. After calling this + * method, the caller should request this layout manager to perform the + * layout. This can be done with the following code: + * + * + * layout.layoutContainer(container); + * container.repaint(); + * + * + * or + * + *
    + *     window.pack()
    + * 
    + * + * If this is not done, the changes in the layout will not be seen until the + * container is resized. + * + * @param row widths of each of the rows. This parameter cannot be null. + * + * @see #getRow + */ + +public void setRow (double row[]) +{ + // Copy rows + rowSpec = new double[row.length]; + System.arraycopy (row, 0, rowSpec, 0, rowSpec.length); + + // Make sure rows are valid + for (int counter = 0; counter < rowSpec.length; counter++) + if ((rowSpec[counter] < 0.0) && + (rowSpec[counter] != FILL) && + (rowSpec[counter] != PREFERRED) && + (rowSpec[counter] != MINIMUM)) + { + rowSpec[counter] = 0.0; + } + + // Indicate that the cell sizes are not known + dirty = true; +} + + + +/** + * Adjusts the width of a single column in this layout. After calling this + * method, the caller should request this layout manager to perform the + * layout. This can be done with the following code: + * + * + * layout.layoutContainer(container); + * container.repaint(); + * + * + * or + * + *
    + *     window.pack()
    + * 
    + * + * If this is not done, the changes in the layout will not be seen until the + * container is resized. + * + * @param i zero-based index of column to set. If this parameter is not + * valid, an ArrayOutOfBoundsException will be thrown. + * @param size width of the column. This parameter cannot be null. + * + * @see #getColumn + */ + +public void setColumn (int i, double size) +{ + // Make sure size is valid + if ((size < 0.0) && + (size != FILL) && + (size != PREFERRED) && + (size != MINIMUM)) + { + size = 0.0; + } + + // Copy new size + columnSpec[i] = size; + + // Indicate that the cell sizes are not known + dirty = true; +} + + + +/** + * Adjusts the height of a single row in this layout. After calling this + * method, the caller should request this layout manager to perform the + * layout. This can be done with the following code: + * + * + * layout.layoutContainer(container); + * container.repaint(); + * + * + * or + * + *
    + *     window.pack()
    + * 
    + * + * If this is not done, the changes in the layout will not be seen until the + * container is resized. + * + * @param i zero-based index of row to set. If this parameter is not + * valid, an ArrayOutOfBoundsException will be thrown. + * @param size height of the row. This parameter cannot be null. + * + * @see #getRow + */ + +public void setRow (int i, double size) +{ + // Make sure size is valid + if ((size < 0.0) && + (size != FILL) && + (size != PREFERRED) && + (size != MINIMUM)) + { + size = 0.0; + } + + // Copy new size + rowSpec[i] = size; + + // Indicate that the cell sizes are not known + dirty = true; +} + + + +/** + * Gets the sizes of columns in this layout. + * + * @return widths of each of the columns + * + * @see #setColumn + */ + +public double [] getColumn () +{ + // Copy columns + double column[] = new double[columnSpec.length]; + System.arraycopy (columnSpec, 0, column, 0, column.length); + + return column; +} + + + +/** + * Gets the height of a single row in this layout. + * + * @return height of the requested row + * + * @see #setRow + */ + +public double [] getRow () +{ + // Copy rows + double row[] = new double[rowSpec.length]; + System.arraycopy (rowSpec, 0, row, 0, row.length); + + return row; +} + + + +/** + * Gets the width of a single column in this layout. + * + * @param i zero-based index of row to get. If this parameter is not valid, + * an ArrayOutOfBoundsException will be thrown. + * + * @return width of the requested column + * + * @see #setRow + */ + +public double getColumn (int i) +{ + return columnSpec[i]; +} + + + +/** + * Gets the sizes of a row in this layout. + * + * @param i zero-based index of row to get. If this parameter is not valid, + * an ArrayOutOfBoundsException will be thrown. + * + * @return height of each of the requested row + * + * @see #setRow + */ + +public double getRow (int i) +{ + return rowSpec[i]; +} + + + +/** + * Gets the number of columns in this layout. + * + * @return the number of columns + */ + +public int getNumColumn () +{ + return columnSpec.length; +} + + + +/** + * Gets the number of rows in this layout. + * + * @return the number of rows + */ + +public int getNumRow () +{ + return rowSpec.length; +} + + + +//****************************************************************************** +//** Insertion/Deletion methods *** +//****************************************************************************** + + + +/** + * Inserts a column in this layout. All components to the right of the + * insertion point are moved right one column. The container will need to + * be laid out after this method returns. See setColumn. + * + * @param i zero-based index at which to insert the column. + * @param size size of the column to be inserted + * + * @see #setColumn + * @see #deleteColumn + */ + +public void insertColumn (int i, double size) +{ + // Make sure position is valid + if ((i < 0) || (i > columnSpec.length)) + throw new IllegalArgumentException + ("Parameter i is invalid. i = " + i + ". Valid range is [0, " + + columnSpec.length + "]."); + + // Make sure column size is valid + if ((size < 0.0) && + (size != FILL) && + (size != PREFERRED) && + (size != MINIMUM)) + { + size = 0.0; + } + + // Copy columns + double column[] = new double[columnSpec.length + 1]; + System.arraycopy (columnSpec, 0, column, 0, i); + System.arraycopy (columnSpec, i, column, i + 1, columnSpec.length - i); + + // Insert column + column[i] = size; + columnSpec = column; + + // Move all components that are to the right of new row + ListIterator iterator = list.listIterator(0); + + while (iterator.hasNext()) + { + // Get next entry + Entry entry = (Entry) iterator.next(); + + // Is the first column to the right of the new column + if (entry.col1 >= i) + // Move first column + entry.col1++; + + // Is the second column to the right of the new column + if (entry.col2 >= i) + // Move second column + entry.col2++; + } + + // Indicate that the cell sizes are not known + dirty = true; +} + + + +/** + * Inserts a row in this layout. All components below the insertion point + * are moved down one row. The container will need to be laid out after this + * method returns. See setRow. + * + * @param i zero-based index at which to insert the column. + * @param size size of the row to be inserted + * + * @see #setRow + * @see #deleteRow + */ + +public void insertRow (int i, double size) +{ + // Make sure position is valid + if ((i < 0) || (i > rowSpec.length)) + throw new IllegalArgumentException + ("Parameter i is invalid. i = " + i + ". Valid range is [0, " + + rowSpec.length + "]."); + + // Make sure row size is valid + if ((size < 0.0) && + (size != FILL) && + (size != PREFERRED) && + (size != MINIMUM)) + { + size = 0.0; + } + + // Copy rows + double row[] = new double[rowSpec.length + 1]; + System.arraycopy (rowSpec, 0, row, 0, i); + System.arraycopy (rowSpec, i, row, i + 1, rowSpec.length - i); + + // Insert row + row[i] = size; + rowSpec = row; + + // Move all components that are below the new row + ListIterator iterator = list.listIterator(0); + + while (iterator.hasNext()) + { + // Get next entry + Entry entry = (Entry) iterator.next(); + + // Is the first row to the right of the new row + if (entry.row1 >= i) + // Move first row + entry.row1++; + + // Is the second row to the right of the new row + if (entry.row2 >= i) + // Move second row + entry.row2++; + } + + // Indicate that the cell sizes are not known + dirty = true; +} + + + +/** + * Deletes a column in this layout. All components to the right of the + * deletion point are moved left one column. The container will need to + * be laid out after this method returns. See setColumn. + * + * @param i zero-based index of column to delete + * + * @see #setColumn + * @see #deleteColumn + */ + +public void deleteColumn (int i) +{ + // Make sure position is valid + if ((i < 0) || (i >= columnSpec.length)) + throw new IllegalArgumentException + ("Parameter i is invalid. i = " + i + ". Valid range is [0, " + + (columnSpec.length - 1) + "]."); + + // Copy columns + double column[] = new double[columnSpec.length - 1]; + System.arraycopy (columnSpec, 0, column, 0, i); + System.arraycopy (columnSpec, i + 1, column, i, columnSpec.length - i - 1); + + // Delete column + columnSpec = column; + + // Move all components that are to the right of row deleted + ListIterator iterator = list.listIterator(0); + + while (iterator.hasNext()) + { + // Get next entry + Entry entry = (Entry) iterator.next(); + + // Is the first column to the right of the new column + if (entry.col1 >= i) + // Move first column + entry.col1--; + + // Is the second column to the right of the new column + if (entry.col2 >= i) + // Move second column + entry.col2--; + } + + // Indicate that the cell sizes are not known + dirty = true; +} + + + +/** + * Deletes a row in this layout. All components below the deletion point are + * moved up one row. The container will need to be laid out after this method + * returns. See setRow. There must be at least two rows in order + * to delete a row. + * + * @param i zero-based index of column to delete + * + * @see #setRow + * @see #deleteRow + */ + +public void deleteRow (int i) +{ + // Make sure position is valid + if ((i < 0) || (i >= rowSpec.length)) + throw new IllegalArgumentException + ("Parameter i is invalid. i = " + i + ". Valid range is [0, " + + (rowSpec.length - 1) + "]."); + + // Copy rows + double row[] = new double[rowSpec.length - 1]; + System.arraycopy (rowSpec, 0, row, 0, i); + System.arraycopy (rowSpec, i + 1, row, i, rowSpec.length - i - 1); + + // Delete row + rowSpec = row; + + // Move all components that are to below the row deleted + ListIterator iterator = list.listIterator(0); + + while (iterator.hasNext()) + { + // Get next entry + Entry entry = (Entry) iterator.next(); + + // Is the first row below the new row + if (entry.row1 >= i) + // Move first row + entry.row1--; + + // Is the second row below the new row + if (entry.row2 >= i) + // Move second row + entry.row2--; + } + + // Indicate that the cell sizes are not known + dirty = true; +} + + + +//****************************************************************************** +//** Misc methods *** +//****************************************************************************** + + + +/** + * Converts this TableLayout to a string. + * + * @return a string representing the columns and row sizes in the form + * "{{col0, col1, col2, ..., colN}, {row0, row1, row2, ..., rowM}}" + */ + +public String toString () +{ + int counter; + + String value = "TableLayout {{"; + + if (columnSpec.length > 0) + { + for (counter = 0; counter < columnSpec.length - 1; counter++) + value += columnSpec[counter] + ", "; + + value += columnSpec[columnSpec.length - 1] + "}, {"; + } + else + value += "}, {"; + + if (rowSpec.length > 0) + { + for (counter = 0; counter < rowSpec.length - 1; counter++) + value += rowSpec[counter] + ", "; + + value += rowSpec[rowSpec.length - 1] + "}}"; + } + else + value += "}}"; + + return value; +} + + + +/** + * Draws a grid on the given container. This is useful for seeing where the + * rows and columns go. In the container's paint method, call this method. + * + * @param container container using this TableLayout + * @param g graphics content of container (can be offscreen) + */ + +public void drawGrid (Container container, Graphics g) +{ + + // Calculate the sizes of the rows and columns + Dimension d = container.getSize(); + + if (dirty || (d.width != oldWidth) || (d.height != oldHeight)) + calculateSize (container); + + // Initialize y + int y = 0; + + for (int row = 0; row < rowSize.length; row++) + { + // Initialize x + int x = 0; + + for (int column = 0; column < columnSize.length; column++) + { + // Use a random color to make things easy to see + Color color = new Color((int) (Math.random() * 0xFFFFFFL)); + g.setColor (color); + + // Draw the cell as a solid rectangle + g.fillRect (x, y, columnSize[column], rowSize[row]); + + // Increment x + x += columnSize[column]; + } + + // Increment y + y += rowSize[row]; + } +} + + + +/** + * Determines whether or not there are any hidden components. A hidden + * component is one that will not be shown with this layout's current + * configuration. Such a component is, at least partly, in an invalid row + * or column. For example, on a table with five rows, row -1 and row 5 are both + * invalid. Valid rows are 0 through 4, inclusively. + * + * @return True, if there are any hidden components. False, otherwise. + * + * @see #overlapping + */ + +public boolean hidden () +{ + // Assume no components are hidden + boolean hidden = false; + + // Check all components + ListIterator iterator = list.listIterator(0); + + while (iterator.hasNext()) + { + // Get next entry + Entry entry = (Entry) iterator.next(); + + // Is this component valid + if ((entry.row1 < 0) || (entry.col1 < 0) || + (entry.row2 > rowSpec.length) || + (entry.col2 > columnSpec.length)) + { + hidden = true; + break; + } + } + + return hidden; +} + + + +/** + * Determines whether or not there are any overlapping components. Two + * components overlap if they cover at least one common cell. + * + * @return True, if there are any overlapping components. False, otherwise. + * + * @see #hidden + */ + +public boolean overlapping () +{ + // Count contraints + int numEntry = list.size(); + + // If there are no components, they can't be overlapping + if (numEntry == 0) + return false; + + // Assume no components are overlapping + boolean overlapping = false; + + // Put entries in an array + Entry entry[] = (Entry[]) list.toArray(new Entry[numEntry]); + + // Check all components + for (int knowUnique = 1; knowUnique < numEntry; knowUnique++) + for (int checking = knowUnique - 1; checking >= 0; checking--) + if + ( + ( + (entry[checking].col1 >= entry[knowUnique].col1) && + (entry[checking].col1 <= entry[knowUnique].col2) && + (entry[checking].row1 >= entry[knowUnique].row1) && + (entry[checking].row1 <= entry[knowUnique].row2) + ) + || + ( + (entry[checking].col2 >= entry[knowUnique].col1) && + (entry[checking].col2 <= entry[knowUnique].col2) && + (entry[checking].row2 >= entry[knowUnique].row1) && + (entry[checking].row2 <= entry[knowUnique].row2) + ) + ) + { + overlapping = true; + break; + } + + return overlapping; +} + + + +/** + * Calculates the sizes of the rows and columns based on the absolute and + * relative sizes specified in rowSpec and columnSpec + * and the size of the container. The result is stored in rowSize + * and columnSize. + * + * @param container container using this TableLayout + */ + +protected void calculateSize (Container container) +{ + int counter; // Counting variable; + + // Get number of rows and columns + int numColumn = columnSpec.length; + int numRow = rowSpec.length; + + // Create array to hold actual sizes in pixels + columnSize = new int[numColumn]; + rowSize = new int[numRow]; + + // Get the container's insets + Insets inset = container.getInsets(); + + // Get the size of the container's available space + Dimension d = container.getSize(); + int totalWidth = d.width - inset.left - inset.right; + int totalHeight = d.height - inset.top - inset.bottom; + + // Initially, the available space is the total space + int availableWidth = totalWidth; + int availableHeight = totalHeight; + + // Assign absolute widths; this reduces available width + for (counter = 0; counter < numColumn; counter++) + // Is the current column an absolue size + if ((columnSpec[counter] >= 1.0) || (columnSpec[counter] == 0.0)) + { + // Assign absolute width + columnSize[counter] = (int) (columnSpec[counter] + 0.5); + + // Reduce available width + availableWidth -= columnSize[counter]; + } + + // Assign absolute heights; this reduces available height + for (counter = 0; counter < numRow; counter++) + // Is the current column an absolue size + if ((rowSpec[counter] >= 1.0) || (rowSpec[counter] == 0.0)) + { + // Assign absolute width + rowSize[counter] = (int) (rowSpec[counter] + 0.5); + + // Reduce available width + availableHeight -= rowSize[counter]; + } + + // Assign preferred and minimum widths; this reduces available width. + // Assignment of preferred/minimum with is like assignment of absolute + // widths except that each column must determine the maximum + // preferred/minimum width of the components that are completely contained + // within the column. + for (counter = 0; counter < numColumn; counter++) + // Is the current column a preferred size + if ((columnSpec[counter] == PREFERRED) || + (columnSpec[counter] == MINIMUM)) + { + // Assume a maximum width of zero + int maxWidth = 0; + + // Find maximum preferred width of all components completely + // contained within this column + ListIterator iterator = list.listIterator(0); + + while (iterator.hasNext()) + { + Entry entry = (Entry) iterator.next(); + + if ((entry.col1 == counter) && (entry.col2 == counter)) + { + Dimension p = (columnSpec[counter] == PREFERRED) ? + entry.component.getPreferredSize() : + entry.component.getMinimumSize(); + + int width = (p == null) ? 0 : p.width; + + if (maxWidth < width) + maxWidth = width; + } + } + + // Assign preferred width + columnSize[counter] = maxWidth; + + // Reduce available width + availableWidth -= maxWidth; + } + + // Assign preferred and minimum heights; this reduces available height. + // Assignment of preferred/minimum with is like assignment of absolute + // heights except that each row must determine the maximum + // preferred/minimum height of the components that are completely contained + // within the row. + for (counter = 0; counter < numRow; counter++) + // Is the current row a preferred size + if ((rowSpec[counter] == PREFERRED) || + (rowSpec[counter] == MINIMUM)) + { + // Assume a maximum height of zero + int maxHeight = 0; + + // Find maximum preferred height of all components completely + // contained within this row + ListIterator iterator = list.listIterator(0); + + while (iterator.hasNext()) + { + Entry entry = (Entry) iterator.next(); + + if ((entry.row1 == counter) && (entry.row2 == counter)) + { + Dimension p = (rowSpec[counter] == PREFERRED) ? + entry.component.getPreferredSize() : + entry.component.getMinimumSize(); + + int height = (p == null) ? 0 : p.height; + + if (maxHeight < height) + maxHeight = height; + } + } + + // Assign preferred height + rowSize[counter] = maxHeight; + + // Reduce available height + availableHeight -= maxHeight; + } + + // Remember how much space is available for relatively sized cells + int relativeWidth = availableWidth; + int relativeHeight = availableHeight; + + // Make sure relativeWidth and relativeHeight are non-negative + if (relativeWidth < 0) + relativeWidth = 0; + + if (relativeHeight < 0) + relativeHeight = 0; + + // Assign relative widths + for (counter = 0; counter < numColumn; counter++) + // Is the current column an relative size + if ((columnSpec[counter] > 0.0) && (columnSpec[counter] < 1.0)) + { + // Assign relative width + columnSize[counter] = + (int) (columnSpec[counter] * relativeWidth + 0.5); + + // Reduce available width + availableWidth -= columnSize[counter]; + } + + // Assign relative widths + for (counter = 0; counter < numRow; counter++) + // Is the current column an relative size + if ((rowSpec[counter] > 0.0) && (rowSpec[counter] < 1.0)) + { + // Assign relative width + rowSize[counter] = (int) (rowSpec[counter] * relativeHeight + 0.5); + + // Reduce available width + availableHeight -= rowSize[counter]; + } + + // Make sure availableWidth and availableHeight are non-negative + if (availableWidth < 0) + availableWidth = 0; + + if (availableHeight < 0) + availableHeight = 0; + + // Count the number of "fill" cells + int numFillWidth = 0; + int numFillHeight = 0; + + for (counter = 0; counter < numColumn; counter++) + if (columnSpec[counter] == FILL) + numFillWidth++; + + for (counter = 0; counter < numRow; counter++) + if (rowSpec[counter] == FILL) + numFillHeight++; + + // If numFillWidth (numFillHeight) is zero, the cooresponding if statements + // will always evaluate to false and the division will not occur. + + // If there are more than one "fill" cell, slack may occur due to rounding + // errors + int slackWidth = availableWidth; + int slackHeight = availableHeight; + + // Assign "fill" cells equal amounts of the remaining space + for (counter = 0; counter < numColumn; counter++) + if (columnSpec[counter] == FILL) + { + columnSize[counter] = availableWidth / numFillWidth; + slackWidth -= columnSize[counter]; + } + + for (counter = 0; counter < numRow; counter++) + if (rowSpec[counter] == FILL) + { + rowSize[counter] = availableHeight / numFillHeight; + slackHeight -= rowSize[counter]; + } + + // Add slack to the last "fill" cell + for (counter = numColumn - 1; counter >= 0; counter--) + { + if (columnSpec[counter] == FILL) + { + columnSize[counter] += slackWidth; + break; + } + } + + for (counter = numRow - 1; counter >= 0; counter--) + { + if (rowSpec[counter] == FILL) + { + rowSize[counter] += slackHeight; + break; + } + } + + // Calculate offsets of each column (done for effeciency) + columnOffset = new int[numColumn + 1]; + columnOffset[0] = inset.left; + + for (counter = 0; counter < numColumn; counter++) + columnOffset[counter + 1] = + columnOffset[counter] + columnSize[counter]; + + // Calculate offsets of each row (done for effeciency) + rowOffset = new int[numRow + 1]; + rowOffset[0] = inset.top; + + for (counter = 0; counter < numRow; counter++) + rowOffset[counter + 1] = + rowOffset[counter] + rowSize[counter]; + + // Indicate that the size of the cells are known for the container's + // current size + dirty = false; + oldWidth = totalWidth; + oldHeight = totalHeight; +} + + + +//****************************************************************************** +//** java.awt.event.LayoutManager methods *** +//****************************************************************************** + + + +/** + * To lay out the specified container using this layout. This method reshapes + * the components in the specified target container in order to satisfy the + * constraints of all components. + * + *

    User code should not have to call this method directly.

    + * + * @param container container being served by this layout manager + */ + +public void layoutContainer (Container container) +{ + int x, y; // Coordinates of the currnet component in pixels + int w, h; // Width and height of the current component in pixels + + // Calculate sizes if container has changed size or components were added + Dimension d = container.getSize(); + + if (dirty || (d.width != oldWidth) || (d.height != oldHeight)) + calculateSize (container); + + // Get components + Component component[] = container.getComponents(); + + // Layout components + for (int counter = 0; counter < component.length; counter++) + { + try + { + // Get the entry entry for the next component + ListIterator iterator = list.listIterator(0); + Entry entry = null; + + while (iterator.hasNext()) + { + entry = (Entry) iterator.next(); + + if (entry.component == component[counter]) + break; + else + entry = null; + } + + // Skip any components that have not been place in a specific cell + if (entry == null) + break; + + // Does the entry occupy a single cell + if (entry.singleCell) + { + // The following block of code has been optimized so that the + // preferred size of the component is only obtained if it is + // needed. There are components in which the getPreferredSize + // method is extremely expensive, such as data driven controls + // with a large amount of data. + + // Get the preferred size of the component + int preferredWidth = 0; + int preferredHeight = 0; + + if ((entry.hAlign != FULL) || (entry.vAlign != FULL)) + { + Dimension preferredSize = + component[counter].getPreferredSize(); + + preferredWidth = preferredSize.width; + preferredHeight = preferredSize.height; + } + + // Determine cell width and height + int cellWidth = columnSize[entry.col1]; + int cellHeight = rowSize[entry.row1]; + + // Determine the width of the component + if ((entry.hAlign == FULL) || + (cellWidth < preferredWidth)) + // Use the width of the cell + w = cellWidth; + else + // Use the prefered width of the component + w = preferredWidth; + + // Determine left and right boarders + switch (entry.hAlign) + { + case LEFT : + // Align left side along left edge of cell + x = columnOffset[entry.col1]; + break; + + case RIGHT : + // Align right side along right edge of cell + x = columnOffset[entry.col1 + 1] - w; + break; + + case CENTER : + // Center justify component + x = columnOffset[entry.col1] + ((cellWidth - w) >> 1); + break; + + case FULL : + // Align left side along left edge of cell + x = columnOffset[entry.col1]; + break; + + default : + // This is a never should happen case, but just in case + x = 0; + } + + // Determine the height of the component + if ((entry.vAlign == FULL) || + (cellHeight < preferredHeight)) + // Use the height of the cell + h = cellHeight; + else + // Use the prefered height of the component + h = preferredHeight; + + // Determine top and bottom boarders + switch (entry.vAlign) + { + case TOP : + // Align top side along top edge of cell + y = rowOffset[entry.row1]; + break; + + case BOTTOM : + // Align right side along right edge of cell + y = rowOffset[entry.row1 + 1] - h; + break; + + case CENTER : + // Center justify component + y = rowOffset[entry.row1] + ((cellHeight - h) >> 1); + break; + + case FULL : + // Align right side along right edge of cell + y = rowOffset[entry.row1]; + break; + + default : + // This is a never should happen case, but just in case + y = 0; + } + } + else + { + // Align left side with left boarder of first column + x = columnOffset[entry.col1]; + + // Align top side along top edge of first row + y = rowOffset[entry.row1]; + + // Align right side with right boarder of second column + w = columnOffset[entry.col2 + 1] - + columnOffset[entry.col1]; + + // Align bottom side with bottom boarder of second row + h = rowOffset[entry.row2 + 1] - rowOffset[entry.row1]; + } + + // Move and resize component + component[counter].setBounds (x, y, w, h); + } + catch (Exception error) + { + // If any error occurs, skip this component + continue; + } + } +} + + + +/** + * Determines the preferred size of the container argument using this layout. + * The preferred size is the smallest size that, if used for the container's + * size, will ensure that all components are at least as large as their + * preferred size. This method cannot guarantee that all components will be + * their preferred size. For example, if component A and component B are each + * allocate half of the container's width and component A wants to be 10 pixels + * wide while component B wants to be 100 pixels wide, they cannot both be + * accommodated. Since in general components rather be larger than their + * preferred size instead of smaller, component B's request will be fulfilled. + * The preferred size of the container would be 200 pixels. + * + * @param container container being served by this layout manager + * + * @return a dimension indicating the container's preferred size + */ + +public Dimension preferredLayoutSize (Container container) +{ + Dimension size; // Preferred size of current component + int scaledWidth = 0; // Preferred width of scalled components + int scaledHeight = 0; // Preferred height of scalled components + int temp; // Temporary variable used to compare sizes + int counter; // Counting variable + + // Determine percentage of space allocated to fill components. This is + // one minus the sum of all scalable components. + double fillWidthRatio = 1.0; + double fillHeightRatio = 1.0; + int numFillWidth = 0; + int numFillHeight = 0; + + for (counter = 0; counter < columnSpec.length; counter++) + if ((columnSpec[counter] > 0.0) && (columnSpec[counter] < 1.0)) + fillWidthRatio -= columnSpec[counter]; + else if (columnSpec[counter] == FILL) + numFillWidth++; + + for (counter = 0; counter < rowSpec.length; counter++) + if ((rowSpec[counter] > 0.0) && (rowSpec[counter] < 1.0)) + fillHeightRatio -= rowSpec[counter]; + else if (rowSpec[counter] == FILL) + numFillHeight++; + + // Adjust fill ratios to reflect number of fill rows/columns + if (numFillWidth > 1) + fillWidthRatio /= numFillWidth; + + if (numFillHeight > 1) + fillHeightRatio /= numFillHeight; + + // Cap fill ratio bottoms to 0.0 + if (fillWidthRatio < 0.0) + fillWidthRatio = 0.0; + + if (fillHeightRatio < 0.0) + fillHeightRatio = 0.0; + + // Calculate preferred/minimum column widths + int columnPrefMin[] = new int[columnSpec.length]; + + for (counter = 0; counter < columnSpec.length; counter++) + // Is the current column a preferred/minimum size + if ((columnSpec[counter] == PREFERRED) || + (columnSpec[counter] == MINIMUM)) + { + // Assume a maximum width of zero + int maxWidth = 0; + + // Find maximum preferred/minimum width of all components completely + // contained within this column + ListIterator iterator = list.listIterator(0); + + while (iterator.hasNext()) + { + Entry entry = (Entry) iterator.next(); + + if ((entry.col1 == counter) && (entry.col2 == counter)) + { + Dimension p = (columnSpec[counter] == PREFERRED) ? + entry.component.getPreferredSize() : + entry.component.getMinimumSize(); + + int width = (p == null) ? 0 : p.width; + + if (maxWidth < width) + maxWidth = width; + } + } + + // Set column's preferred/minimum width + columnPrefMin[counter] = maxWidth; + } + + + // Calculate preferred/minimum row heights + int rowPrefMin[] = new int[rowSpec.length]; + + for (counter = 0; counter < rowSpec.length; counter++) + // Is the current row a preferred/minimum size + if ((rowSpec[counter] == PREFERRED) || + (rowSpec[counter] == MINIMUM)) + { + // Assume a maximum height of zero + int maxHeight = 0; + + // Find maximum preferred height of all components completely + // contained within this row + ListIterator iterator = list.listIterator(0); + + while (iterator.hasNext()) + { + Entry entry = (Entry) iterator.next(); + + if ((entry.row1 == counter) && (entry.row1 == counter)) + { + Dimension p = (rowSpec[counter] == PREFERRED) ? + entry.component.getPreferredSize() : + entry.component.getMinimumSize(); + + int height = (p == null) ? 0 : p.height; + + if (maxHeight < height) + maxHeight = height; + } + } + + // Add preferred height + rowPrefMin[counter] += maxHeight; + } + + // Find maximum preferred size of all scaled components + ListIterator iterator = list.listIterator(0); + + while (iterator.hasNext()) + { + // Get next entry + Entry entry = (Entry) iterator.next(); + + // Make sure entry is in valid rows and columns + if ((entry.col1 < 0) || (entry.col1 >= columnSpec.length) || + (entry.col2 >= columnSpec.length) || + (entry.row1 < 0) || (entry.row1 >= rowSpec.length) || + (entry.row2 >= rowSpec.length)) + { + // Skip the bad component + continue; + } + + // Get preferred size of current component + size = entry.component.getPreferredSize(); + + // Calculate portion of component that is not absolutely sized + int scalableWidth = size.width; + int scalableHeight = size.height; + + for (counter = entry.col1; counter <= entry.col2; counter++) + if (columnSpec[counter] >= 1.0) + scalableWidth -= columnSpec[counter]; + else if ((columnSpec[counter] == PREFERRED) || + (columnSpec[counter] == MINIMUM)) + { + scalableWidth -= columnPrefMin[counter]; + } + + for (counter = entry.row1; counter <= entry.row2; counter++) + if (rowSpec[counter] >= 1.0) + scalableHeight -= rowSpec[counter]; + else if ((rowSpec[counter] == PREFERRED) || + (rowSpec[counter] == MINIMUM)) + { + scalableHeight -= rowPrefMin[counter]; + } + + //---------------------------------------------------------------------- + + // Determine total percentage of scalable space that the component + // occupies by adding the relative columns and the fill columns + double relativeWidth = 0.0; + + for (counter = entry.col1; counter <= entry.col2; counter++) + { + // Column is scaled + if ((columnSpec[counter] > 0.0) && (columnSpec[counter] < 1.0)) + // Add scaled size to relativeWidth + relativeWidth += columnSpec[counter]; + // Column is fill + else if ((columnSpec[counter] == FILL) && (fillWidthRatio != 0.0)) + // Add fill size to relativeWidth + relativeWidth += fillWidthRatio; + } + + // Determine the total scaled width as estimated by this component + if (relativeWidth == 0) + temp = 0; + else + temp = (int) (scalableWidth / relativeWidth + 0.5); + + // If the container needs to be bigger, make it so + if (scaledWidth < temp) + scaledWidth = temp; + + //---------------------------------------------------------------------- + + // Determine total percentage of scalable space that the component + // occupies by adding the relative columns and the fill columns + double relativeHeight = 0.0; + + for (counter = entry.row1; counter <= entry.row2; counter++) + { + // Row is scaled + if ((rowSpec[counter] > 0.0) && (rowSpec[counter] < 1.0)) + // Add scaled size to relativeHeight + relativeHeight += rowSpec[counter]; + // Row is fill + else if ((rowSpec[counter] == FILL) && (fillHeightRatio != 0.0)) + // Add fill size to relativeHeight + relativeHeight += fillHeightRatio; + } + + // Determine the total scaled width as estimated by this component + if (relativeHeight == 0) + temp = 0; + else + temp = (int) (scalableHeight / relativeHeight + 0.5); + + // If the container needs to be bigger, make it so + if (scaledHeight < temp) + scaledHeight = temp; + } + + // totalWidth is the scaledWidth plus the sum of all absolute widths and all + // preferred widths + int totalWidth = scaledWidth; + + for (counter = 0; counter < columnSpec.length; counter++) + // Is the current column an absolute size + if (columnSpec[counter] >= 1.0) + totalWidth += (int) (columnSpec[counter] + 0.5); + // Is the current column a preferred/minimum size + else if ((columnSpec[counter] == PREFERRED) || + (columnSpec[counter] == MINIMUM)) + { + // Add preferred/minimum width + totalWidth += columnPrefMin[counter]; + } + + // totalHeight is the scaledHeight plus the sum of all absolute heights and + // all preferred widths + int totalHeight = scaledHeight; + + for (counter = 0; counter < rowSpec.length; counter++) + // Is the current row an absolute size + if (rowSpec[counter] >= 1.0) + totalHeight += (int) (rowSpec[counter] + 0.5); + // Is the current row a preferred size + else if ((rowSpec[counter] == PREFERRED) || + (rowSpec[counter] == MINIMUM)) + { + // Add preferred/minimum width + totalHeight += rowPrefMin[counter]; + } + + // Compensate for container's insets + Insets inset = container.getInsets(); + totalWidth += inset.left + inset.right; + totalHeight += inset.top + inset.bottom; + + return new Dimension(totalWidth, totalHeight); +} + + + +/** + * Determines the minimum size of the container argument using this layout. + * The minimum size is the smallest size that, if used for the container's + * size, will ensure that all components are at least as large as their + * minimum size. This method cannot guarantee that all components will be + * their minimum size. For example, if component A and component B are each + * allocate half of the container's width and component A wants to be 10 pixels + * wide while component B wants to be 100 pixels wide, they cannot both be + * accommodated. Since in general components rather be larger than their + * minimum size instead of smaller, component B's request will be fulfilled. + * The minimum size of the container would be 200 pixels. + * + * @param container container being served by this layout manager + * + * @return a dimension indicating the container's minimum size + */ + +public Dimension minimumLayoutSize (Container container) +{ + Dimension size; // Minimum size of current component + int scaledWidth = 0; // Minimum width of scalled components + int scaledHeight = 0; // Minimum height of scalled components + int temp; // Temporary variable used to compare sizes + int counter; // Counting variable + + // Determine percentage of space allocated to fill components. This is + // one minus the sum of all scalable components. + double fillWidthRatio = 1.0; + double fillHeightRatio = 1.0; + int numFillWidth = 0; + int numFillHeight = 0; + + for (counter = 0; counter < columnSpec.length; counter++) + if ((columnSpec[counter] > 0.0) && (columnSpec[counter] < 1.0)) + fillWidthRatio -= columnSpec[counter]; + else if (columnSpec[counter] == FILL) + numFillWidth++; + + for (counter = 0; counter < rowSpec.length; counter++) + if ((rowSpec[counter] > 0.0) && (rowSpec[counter] < 1.0)) + fillHeightRatio -= rowSpec[counter]; + else if (rowSpec[counter] == FILL) + numFillHeight++; + + // Adjust fill ratios to reflect number of fill rows/columns + if (numFillWidth > 1) + fillWidthRatio /= numFillWidth; + + if (numFillHeight > 1) + fillHeightRatio /= numFillHeight; + + // Cap fill ratio bottoms to 0.0 + if (fillWidthRatio < 0.0) + fillWidthRatio = 0.0; + + if (fillHeightRatio < 0.0) + fillHeightRatio = 0.0; + + // Find maximum minimum size of all scaled components + ListIterator iterator = list.listIterator(0); + + while (iterator.hasNext()) + { + // Get next entry + Entry entry = (Entry) iterator.next(); + + // Make sure entry is in valid rows and columns + if ((entry.col1 < 0) || (entry.col1 >= columnSpec.length) || + (entry.col2 >= columnSpec.length) || + (entry.row1 < 0) || (entry.row1 >= rowSpec.length) || + (entry.row2 >= rowSpec.length)) + { + // Skip the bad component + continue; + } + + // Get minimum size of current component + size = entry.component.getMinimumSize(); + + // Calculate portion of component that is not absolutely sized + int scalableWidth = size.width; + int scalableHeight = size.height; + + for (counter = entry.col1; counter <= entry.col2; counter++) + if (columnSpec[counter] >= 1.0) + scalableWidth -= columnSpec[counter]; + + for (counter = entry.row1; counter <= entry.row2; counter++) + if (rowSpec[counter] >= 1.0) + scalableHeight -= rowSpec[counter]; + + //---------------------------------------------------------------------- + + // Determine total percentage of scalable space that the component + // occupies by adding the relative columns and the fill columns + double relativeWidth = 0.0; + + for (counter = entry.col1; counter <= entry.col2; counter++) + { + // Column is scaled + if ((columnSpec[counter] > 0.0) && (columnSpec[counter] < 1.0)) + // Add scaled size to relativeWidth + relativeWidth += columnSpec[counter]; + // Column is fill + else if ((columnSpec[counter] == FILL) && (fillWidthRatio != 0.0)) + // Add fill size to relativeWidth + relativeWidth += fillWidthRatio; + } + + // Determine the total scaled width as estimated by this component + if (relativeWidth == 0) + temp = 0; + else + temp = (int) (scalableWidth / relativeWidth + 0.5); + + // If the container needs to be bigger, make it so + if (scaledWidth < temp) + scaledWidth = temp; + + //---------------------------------------------------------------------- + + // Determine total percentage of scalable space that the component + // occupies by adding the relative columns and the fill columns + double relativeHeight = 0.0; + + for (counter = entry.row1; counter <= entry.row2; counter++) + { + // Row is scaled + if ((rowSpec[counter] > 0.0) && (rowSpec[counter] < 1.0)) + // Add scaled size to relativeHeight + relativeHeight += rowSpec[counter]; + // Row is fill + else if ((rowSpec[counter] == FILL) && (fillHeightRatio != 0.0)) + // Add fill size to relativeHeight + relativeHeight += fillHeightRatio; + } + + // Determine the total scaled width as estimated by this component + if (relativeHeight == 0) + temp = 0; + else + temp = (int) (scalableHeight / relativeHeight + 0.5); + + // If the container needs to be bigger, make it so + if (scaledHeight < temp) + scaledHeight = temp; + } + + // totalWidth is the scaledWidth plus the sum of all absolute widths and all + // preferred widths + int totalWidth = scaledWidth; + + for (counter = 0; counter < columnSpec.length; counter++) + // Is the current column an absolute size + if (columnSpec[counter] >= 1.0) + totalWidth += (int) (columnSpec[counter] + 0.5); + // Is the current column a preferred size + else if ((columnSpec[counter] == PREFERRED) || + (columnSpec[counter] == MINIMUM)) + { + // Assume a maximum width of zero + int maxWidth = 0; + + // Find maximum preferred width of all components completely + // contained within this column + iterator = list.listIterator(0); + + while (iterator.hasNext()) + { + Entry entry = (Entry) iterator.next(); + + if ((entry.col1 == counter) && (entry.col2 == counter)) + { + Dimension p = (columnSpec[counter] == PREFERRED) ? + entry.component.getPreferredSize() : + entry.component.getMinimumSize(); + + int width = (p == null) ? 0 : p.width; + + if (maxWidth < width) + maxWidth = width; + } + } + + // Add preferred width + totalWidth += maxWidth; + } + + // totalHeight is the scaledHeight plus the sum of all absolute heights and + // all preferred widths + int totalHeight = scaledHeight; + + for (counter = 0; counter < rowSpec.length; counter++) + // Is the current row an absolute size + if (rowSpec[counter] >= 1.0) + totalHeight += (int) (rowSpec[counter] + 0.5); + // Is the current row a preferred size + else if ((rowSpec[counter] == PREFERRED) || + (rowSpec[counter] == MINIMUM)) + { + // Assume a maximum height of zero + int maxHeight = 0; + + // Find maximum preferred height of all components completely + // contained within this row + iterator = list.listIterator(0); + + while (iterator.hasNext()) + { + Entry entry = (Entry) iterator.next(); + + if ((entry.row1 == counter) && (entry.row1 == counter)) + { + Dimension p = (rowSpec[counter] == PREFERRED) ? + entry.component.getPreferredSize() : + entry.component.getMinimumSize(); + + int height = (p == null) ? 0 : p.height; + + if (maxHeight < height) + maxHeight = height; + } + } + + // Add preferred height + totalHeight += maxHeight; + } + + // Compensate for container's insets + Insets inset = container.getInsets(); + totalWidth += inset.left + inset.right; + totalHeight += inset.top + inset.bottom; + + return new Dimension(totalWidth, totalHeight); +} + + + +/** + * Adds the specified component with the specified name to the layout. + * + * @param name indicates entry's position and anchor + * @param component component to add + */ + +public void addLayoutComponent (String name, Component component) +{ + addLayoutComponent (component, name); +} + + + +//****************************************************************************** +//** java.awt.event.LayoutManager2 methods *** +//****************************************************************************** + + + +/** + * Adds the specified component with the specified name to the layout. + * + * @param component component to add + * @param constraint indicates entry's position and alignment + */ + +public void addLayoutComponent (Component component, Object constraint) +{ + if (constraint instanceof String) + { + // Create an entry to associate component with its constraints + constraint = new TableLayoutConstraints((String) constraint); + + // Add component and constraints to the list + list.add (new Entry(component, (TableLayoutConstraints) constraint)); + } + else if (constraint instanceof TableLayoutConstraints) + { + // Add component and constraints to the list + list.add (new Entry(component, (TableLayoutConstraints) constraint)); + } + else if (constraint == null) + throw new IllegalArgumentException("No constraint for the component"); + else + throw new IllegalArgumentException + ("Cannot accept a constraint of class " + constraint.getClass()); +} + + + +/** + * Removes the specified component from the layout. + * + * @param component component being removed + */ + +public void removeLayoutComponent (Component component) +{ + list.remove (component); +} + + + +/** + * Returns the maximum dimensions for this layout given the components in the + * specified target container. + * + * @param target the component which needs to be laid out + * + * @return unconditionally, a Dimension of Integer.MAX_VALUE by + * Integer.MAX_VALUE since TableLayout does not limit the + * maximum size of a container + */ + +public Dimension maximumLayoutSize (Container target) +{ + return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); +} + + + +/** + * Returns the alignment along the x axis. This specifies how the component + * would like to be aligned relative to other components. The value should be + * a number between 0 and 1 where 0 represents alignment along the origin, 1 is + * aligned the furthest away from the origin, 0.5 is centered, etc. + * + * @return unconditionally, 0.5 + */ + +public float getLayoutAlignmentX (Container parent) +{ + return 0.5f; +} + + + +/** + * Returns the alignment along the y axis. This specifies how the component + * would like to be aligned relative to other components. The value should be + * a number between 0 and 1 where 0 represents alignment along the origin, 1 is + * aligned the furthest away from the origin, 0.5 is centered, etc. + * + * @return unconditionally, 0.5 + */ + +public float getLayoutAlignmentY (Container parent) +{ + return 0.5f; +} + + + +/** + * Invalidates the layout, indicating that if the layout manager has cached + * information it should be discarded. + */ + +public void invalidateLayout (Container target) +{ + dirty = true; +} + + + +//****************************************************************************** +//*** Inner Class *** +//****************************************************************************** + + + + // The following inner class is used to bind components to their constraints + protected class Entry extends TableLayoutConstraints + { + /** Component bound by the constraints */ + protected Component component; + + /** Does the component occupy a single cell */ + protected boolean singleCell; + + /** + * Constructs an Entry that binds a component to a set of constraints. + * + * @param component component being bound + * @param constraint constraints being applied + */ + + public Entry (Component component, TableLayoutConstraints constraint) + { + super (constraint.col1, constraint.row1, + constraint.col2, constraint.row2, + constraint.hAlign, constraint.vAlign); + + singleCell = ((row1 == row2) && (col1 == col2)); + this.component = component; + } + + /** + * Determines whether or not two entries are equal. + * + * @param object object being compared to; must be a Component if it + * is equal to this TableLayoutConstraints. + * + * @return True, if the entries refer to the same component object. + * False, otherwise. + */ + + public boolean equals (Object object) + { + boolean equal = false; + + if (object instanceof Component) + { + Component component = (Component) object; + equal = (this.component == component); + } + + return equal; + } + } + + + +} diff --git a/Rapla/src/org/rapla/components/layout/TableLayoutConstants.java b/Rapla/src/org/rapla/components/layout/TableLayoutConstants.java new file mode 100644 index 0000000..f55c009 --- /dev/null +++ b/Rapla/src/org/rapla/components/layout/TableLayoutConstants.java @@ -0,0 +1,55 @@ +package org.rapla.components.layout; + + + +/** + * Constants used by TableLayout. + * + * @author Daniel E. Barbalace + */ + +public interface TableLayoutConstants +{ + + + +/** Indicates that the component is left justified in its cell */ +public static final int LEFT = 0; + +/** Indicates that the component is top justified in its cell */ +public static final int TOP = 0; + +/** Indicates that the component is centered in its cell */ +public static final int CENTER = 1; + +/** Indicates that the component is full justified in its cell */ +public static final int FULL = 2; + +/** Indicates that the component is bottom justified in its cell */ +public static final int BOTTOM = 3; + +/** Indicates that the component is right justified in its cell */ +public static final int RIGHT = 3; + +/** Indicates that the row/column should fill the available space */ +public static final double FILL = -1.0; + +/** Indicates that the row/column should be allocated just enough space to + accomidate the preferred size of all components contained completely within + this row/column. */ +public static final double PREFERRED = -2.0; + +/** Indicates that the row/column should be allocated just enough space to + accomidate the minimum size of all components contained completely within + this row/column. */ +public static final double MINIMUM = -3.0; + +/** Minimum value for an alignment */ +public static final int MIN_ALIGN = 0; + +/** Maximum value for an alignment */ +public static final int MAX_ALIGN = 3; + + + +} diff --git a/Rapla/src/org/rapla/components/layout/TableLayoutConstraints.java b/Rapla/src/org/rapla/components/layout/TableLayoutConstraints.java new file mode 100644 index 0000000..3cb3123 --- /dev/null +++ b/Rapla/src/org/rapla/components/layout/TableLayoutConstraints.java @@ -0,0 +1,206 @@ +package org.rapla.components.layout; + + +import java.util.*; + + +/** + * The following inner class is used to bind components to their + * constraints. + * + * @author Daniel E. Barbalace + */ + +public class TableLayoutConstraints implements TableLayoutConstants +{ + + + +/** Cell in which the upper left corner of the component lays */ +public int col1, row1; + +/** Cell in which the lower right corner of the component lays */ +public int col2, row2; + +/** Horizontal justification if component occupies just one cell */ +public int hAlign; + +/** Verical justification if component occupies just one cell */ +public int vAlign; + + + +/** + * Constructs an TableLayoutConstraints with the default settings. This + * constructor is equivalent to TableLayoutConstraints(0, 0, 0, 0, FULL, FULL). + */ + +public TableLayoutConstraints () +{ + col1 = row1 = col2 = row2 = 0; + hAlign = vAlign = FULL; +} + + + +/** + * Constructs an TableLayoutConstraints from a string. + * + * @param constraints indicates TableLayoutConstraints's position and justification + * as a string in the form "row, column" or + * "row, column, horizontal justification, vertical + * justification" or "row 1, column 1, row 2, column 2". + * It is also acceptable to delimit the paramters with + * spaces instead of commas. + */ + +public TableLayoutConstraints (String constraints) +{ + // Parse constraints using spaces or commas + StringTokenizer st = new StringTokenizer(constraints, ", "); + + // Use default values for any parameter not specified or specified + // incorrectly. The default parameters place the component in a single + // cell at column 0, row 0. The component is fully justified. + col1 = 0; + row1 = 0; + col2 = 0; + row2 = 0; + hAlign = FULL; + vAlign = FULL; + + String token = null; + + try + { + // Get the first column (assume component is in only one column) + token = st.nextToken(); + col1 = new Integer(token).intValue(); + col2 = col1; + + // Get the first row (assume component is in only one row) + token = st.nextToken(); + row1 = new Integer(token).intValue(); + row2 = row1; + + // Get the second column + token = st.nextToken(); + col2 = new Integer(token).intValue(); + + // Get the second row + token = st.nextToken(); + row2 = new Integer(token).intValue(); + } + catch (NoSuchElementException error) {} + catch (NumberFormatException error) + { + try + { + // Check if token means horizontally justification the component + if (token.equalsIgnoreCase("L")) + hAlign = LEFT; + else if (token.equalsIgnoreCase("C")) + hAlign = CENTER; + else if (token.equalsIgnoreCase("F")) + hAlign = FULL; + else if (token.equalsIgnoreCase("R")) + hAlign = RIGHT; + + // There can be one more token for the vertical justification even + // if the horizontal justification is invalid + token = st.nextToken(); + + // Check if token means horizontally justification the component + if (token.equalsIgnoreCase("T")) + vAlign = TOP; + else if (token.equalsIgnoreCase("C")) + vAlign = CENTER; + else if (token.equalsIgnoreCase("F")) + vAlign = FULL; + else if (token.equalsIgnoreCase("B")) + vAlign = BOTTOM; + } + catch (NoSuchElementException error2) {} + } + + // Make sure row2 >= row1 + if (row2 < row1) + row2 = row1; + + // Make sure col2 >= col1 + if (col2 < col1) + col2 = col1; +} + + + +/** + * Constructs an TableLayoutConstraints a set of constraints. + * + * @param col1 column where upper-left cornor of the component is placed + * @param row1 row where upper-left cornor of the component is placed + * @param col2 column where lower-right cornor of the component is placed + * @param row2 row where lower-right cornor of the component is placed + * @param hAlign horizontal justification of a component in a single cell + * @param vAlign vertical justification of a component in a single cell + */ + +public TableLayoutConstraints + (int col1, int row1, int col2, int row2, int hAlign, int vAlign) +{ + this.col1 = col1; + this.row1 = row1; + this.col2 = col2; + this.row2 = row2; + + if ((hAlign < MIN_ALIGN) || (hAlign > MAX_ALIGN)) + this.hAlign = FULL; + else + this.hAlign = hAlign; + + if ((vAlign < MIN_ALIGN) || (vAlign > MAX_ALIGN)) + this.vAlign = FULL; + else + this.vAlign = vAlign; +} + + + +/** + * Gets a string representation of this TableLayoutConstraints. + * + * @return a string in the form "row 1, column 1, row 2, column 2" or + * "row, column, horizontal justification, vertical justification" + */ + +public String toString () +{ + StringBuffer buffer = new StringBuffer(); + + buffer.append (row1); + buffer.append (", "); + buffer.append (col1); + buffer.append (", "); + + if ((row1 == row2) && (col1 == col2)) + { + final char h[] = {'L', 'C', 'F', 'R'}; + final char v[] = {'T', 'C', 'F', 'B'}; + + buffer.append (h[hAlign]); + buffer.append (", "); + buffer.append (v[vAlign]); + } + else + { + buffer.append (row2); + buffer.append (", "); + buffer.append (col2); + } + + return buffer.toString(); +} + + + +} diff --git a/Rapla/src/org/rapla/components/layout/package.html b/Rapla/src/org/rapla/components/layout/package.html new file mode 100644 index 0000000..0fc5f41 --- /dev/null +++ b/Rapla/src/org/rapla/components/layout/package.html @@ -0,0 +1,6 @@ + +This package contains additional LayoutManagers. + + + + diff --git a/Rapla/src/org/rapla/components/mail/Convert.java b/Rapla/src/org/rapla/components/mail/Convert.java new file mode 100644 index 0000000..0837397 --- /dev/null +++ b/Rapla/src/org/rapla/components/mail/Convert.java @@ -0,0 +1,96 @@ +package org.rapla.components.mail; +/* + * Permission to use, copy, modify, and distribute this software + * and its documentation for commercial or non-commercial purposes + * is hereby granted provided that this copyright notice appears + * in all copies. + * + * LIMITATION OF LIABILITY. UNDER NO CIRCUMSTANCES AND UNDER NO + * LEGAL THEORY, SHALL THE AUTHOR OF THIS CLASS BE LIABLE TO YOU + * OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES OF ANY KIND. + * + */ + +/** Class with a few static methods used by pop3 and apop classes in this package. + *

    Get the latest version of this and other classes on + * + * Stefano Locati's Java page. + * + * + * @author Stefano Locati + * slocati@geocities.com or + * stefano.locati@usa.net + * @version $Revision: 713 $ $Date: 2004-11-16 09:37:01 -0500 (Tue, 16 Nov 2004) $ + */ +public class Convert +{ + +//--------------------------------------------------------------------------- + /** This class is a library of static methods so can't be + * istantiated. + */ + private Convert() + { + } + +//--------------------------------------------------------------------------- + /** + * Convert a String into an int, giving 0 as default value. + * @param s a String to be converted + * @return int value represented by the argument, or 0 if the argument + * isn't a valid number. + */ + public static final int toInt(String s) + { + // or try: return Integer.parseInt(s); + try { + // return (Integer.valueOf(s)).intValue(); + return Integer.parseInt(s); + } catch (NumberFormatException e) { + return 0; + } + } + +//--------------------------------------------------------------------------- + /** + * Convert a byte into an hexadecimal number. + * The output is always lowercase, two digit long in the range + * 00-ff. + *

    Example: 12 gives 0c, + * 18 gives 12, + * 255 gives ff, ... + * @param n a byte value to be converted. + * @return a two digit hexadecimal number. + */ + public static final String toHexString(byte n) + { + // note: & 0xff is used to reset high bits + if ( (n >= 0) && (n <= 15) ) + return "0" + Integer.toHexString( n & 0xff ); + else return Integer.toHexString( n & 0xff ); + } + +//--------------------------------------------------------------------------- + /** + * Convert a number represented by the given byte array into a + * hexadecimal number String. + *

    Example: { 12, 22, 16 } gives + * 0c1610. That means 12 is 0c, + * 22 is 16, + * 16 is 10 or, if you prefer all together,
    + * 12 * 256^2 + 22 * 256^1 + 16 * 256 = 792080 + * gives c1610. + * @param n the byte array to be converted. + * @return a hexadecimal String with an even number of digits. + */ + public static final String toHexString(byte[] n) + { + StringBuffer hex = new StringBuffer(2*n.length); + for (int i = 0; i < n.length; i++) { + hex.append(toHexString(n[i])); + } + return hex.toString(); + } + +} diff --git a/Rapla/src/org/rapla/components/mail/MailException.java b/Rapla/src/org/rapla/components/mail/MailException.java new file mode 100644 index 0000000..82511bb --- /dev/null +++ b/Rapla/src/org/rapla/components/mail/MailException.java @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.mail; + +import org.apache.avalon.framework.CascadingException; + +public class MailException extends CascadingException { + private static final long serialVersionUID = 1L; + + public MailException(String text,Exception ex) { + super(text,ex); + } + + public MailException(String text) { + super(text); + } + +} + + + + + + + diff --git a/Rapla/src/org/rapla/components/mail/MailInterface.java b/Rapla/src/org/rapla/components/mail/MailInterface.java new file mode 100644 index 0000000..b9d711f --- /dev/null +++ b/Rapla/src/org/rapla/components/mail/MailInterface.java @@ -0,0 +1,18 @@ +package org.rapla.components.mail; + +public interface MailInterface { + String ROLE = MailInterface.class.getName(); + /* Sends the mail. + Callers should check if the parameters are all valid + according to the SMTP RFC at http://www.ietf.org/rfc/rfc821.txt + because the implementing classes may not check for validity + */ + public void sendMail + ( + String senderMail + ,String recipient + ,String subject + ,String mailBody + ) + throws MailException; +} diff --git a/Rapla/src/org/rapla/components/mail/MailToFile.java b/Rapla/src/org/rapla/components/mail/MailToFile.java new file mode 100644 index 0000000..eccc0c8 --- /dev/null +++ b/Rapla/src/org/rapla/components/mail/MailToFile.java @@ -0,0 +1,82 @@ +package org.rapla.components.mail; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; + +import org.apache.avalon.framework.configuration.Configurable; +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.ConfigurationException; + +/** +Dummy Implementation of the MailInterface writes Mail to a file. +This can be used for testing the notification service without sending +an email. + +Sample Configuration. +

    +    <mail-to-file id="mail-to-file">
    +       <dir>/tmp/rapla-mails/</dir>
    +    </mail-to-file>
    +
    + + +*/ +public class MailToFile implements Configurable,MailInterface { + String dirPath; + + public MailToFile( ) throws ConfigurationException{ + } + + public MailToFile( Configuration config) throws ConfigurationException{ + configure( config); + } + + + public void configure(Configuration config) throws ConfigurationException { + // get the configuration entry text with the default-value "Welcome" + dirPath = config.getChild("dir").getValue(); + } + + public void sendMail(String senderMail,String recipient, String subject, String mailBody) throws MailException { + File dir = null; + File file = null; + PrintWriter writer = null; + try { + dir = new File(dirPath); + if (!dir.exists()) + throw new MailException(dirPath + " doesn't exist."); + } catch (SecurityException e) { + throw new MailException("Can't access " + dirPath + " \n" + e.getMessage()); + } + + try { + int messageNumber = 1; + while (file == null || file.exists()) { + file = new File(dir,"msg" + messageNumber); + messageNumber ++; + } + writer = new PrintWriter(new FileWriter(file)); + writer.println("From: " + senderMail); + writer.println("To: " + recipient); + writer.println("Subject: " + subject); + writer.println(""); + writer.print(mailBody); + } catch (SecurityException e) { + throw new MailException("Can't access " + file + " \n" + e.getMessage()); + } catch (IOException e) { + throw new MailException("I/O Error while writing file: " + + file + " \n" + e.getMessage()); + } finally { + if (writer != null) + writer.close(); + } + return; + } + +} + + + + + diff --git a/Rapla/src/org/rapla/components/mail/MailapiClient.java b/Rapla/src/org/rapla/components/mail/MailapiClient.java new file mode 100644 index 0000000..11c8c46 --- /dev/null +++ b/Rapla/src/org/rapla/components/mail/MailapiClient.java @@ -0,0 +1,145 @@ +package org.rapla.components.mail; + + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.Properties; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.ConfigurationException; + + + +public class MailapiClient implements MailInterface +{ + + String mailhost = "localhost"; + int port = 25; + String username; + String password; + + public MailapiClient(Configuration config) throws ConfigurationException { + // get the configuration entry text with the default-value "Welcome" + setPort(config.getChild("smtp-port").getValueAsInteger(25)); + setSmtpHost(config.getChild("smtp-host").getValue()); + setUsername( config.getChild("username").getValue("")); + setPassword( config.getChild("password").getValue("")); + } + + public MailapiClient() + { + } + + + public void sendMail( String senderMail, String recipient, String subject, String mailBody ) throws MailException + { + Properties props = new Properties(); + props.put("mail.smtp.host", mailhost); + props.put("mail.smtp.port", new Integer(port)); + + try { + Class TransportC = Class.forName("javax.mail.Transport"); + Class SessionC = Class.forName("javax.mail.Session"); + Class MessageC = Class.forName("javax.mail.Message"); + Class RecipientTypeC = Class.forName("javax.mail.Message$RecipientType"); + Class MimeMessageC = Class.forName("javax.mail.internet.MimeMessage"); + Class InternetAddressC = Class.forName("javax.mail.internet.InternetAddress"); + Class AddressC = Class.forName("javax.mail.Address"); + Method GetInstanceM = SessionC.getMethod("getInstance", new Class[] {Properties.class}); + Constructor MimeMessageConst = MimeMessageC.getConstructor(new Class[] {SessionC}); + Constructor InternetAdressConst = InternetAddressC.getConstructor(new Class[] {String.class}); + Method SetFromM = MessageC.getMethod("setFrom", new Class[] {AddressC}); + Method SetSubjectM = MessageC.getMethod("setSubject", new Class[] {String.class}); + Method SetTextM = MessageC.getMethod("setText", new Class[] {String.class}); + Method SetRecipientM = MessageC.getMethod("setRecipient", new Class[] {RecipientTypeC,AddressC}); + Method SendM = TransportC.getMethod("send", new Class[] {MessageC}); + Object RecipientTypeTo = RecipientTypeC.getField("TO").get( null); + + // Session session = Session.getInstance(props, null); + Object session = GetInstanceM.invoke( null, new Object[] {props}); + + //MimeMessage msg = new MimeMessage(session); + Object msg = MimeMessageConst.newInstance( new Object[] {session}); + //msg.setFrom(new InternetAddress( senderMail)); + Object senderMailAddr = InternetAdressConst.newInstance( new Object[] { senderMail}); + SetFromM.invoke( msg, new Object[] {senderMailAddr} ); + // msg.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress( recipient)); + Object recipientAddr = InternetAdressConst.newInstance( new Object[] { recipient}); + SetRecipientM.invoke( msg, new Object[] { RecipientTypeTo, recipientAddr} ); + //msg.setSubject(subject); + SetSubjectM.invoke( msg, new Object[] {subject} ); + //msg.setText(mailBody); + SetTextM.invoke( msg, new Object[] {mailBody} ); + //Transport.send(msg); + SendM.invoke( null, new Object[] {msg}); + + } catch (ClassNotFoundException ex ) { + throw new MailException( "Mail API not available (please provide the mailapi jars from sun in the classpath):" + ex.getMessage()); + } + catch (Exception e) + { + throw new MailException( e.getMessage(), e); + } + } + + /* + final PasswordAuthentication passw = new PasswordAuthentication(username, password); + Authenticator auth = new Authenticator() + { + protected PasswordAuthentication getPasswordAuthentication() + { + return passw; + } + }; + Session session = Session.getInstance(props, auth); + */ + + + public String getSmtpHost() + { + return mailhost; + } + + + public void setSmtpHost( String mailhost ) + { + this.mailhost = mailhost; + } + + + public String getPassword() + { + return password; + } + + + public void setPassword( String password ) + { + this.password = password; + } + + + public int getPort() + { + return port; + } + + + public void setPort( int port ) + { + this.port = port; + } + + + public String getUsername() + { + return username; + } + + + public void setUsername( String username ) + { + this.username = username; + } + +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/components/mail/MockMailServer.java b/Rapla/src/org/rapla/components/mail/MockMailServer.java new file mode 100644 index 0000000..83c463d --- /dev/null +++ b/Rapla/src/org/rapla/components/mail/MockMailServer.java @@ -0,0 +1,104 @@ +package org.rapla.components.mail; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.ServerSocket; +import java.net.Socket; + +public class MockMailServer +{ + String senderMail; + String recipient; + int port = 25; + + public int getPort() + { + return port; + } + + public void setPort( int port ) + { + this.port = port; + } + + public static void main(String[] args) + { + new MockMailServer().startMailer(false); + } + + public void startMailer(boolean deamon) + { + Thread serverThread = new Thread() + { + public void run() + { + try + { + ServerSocket socket = new ServerSocket(port); + System.out.println("MockMail server started and listening on port " + port); + Socket smtpSocket = socket.accept(); + smtpSocket.setKeepAlive(true); + BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(smtpSocket.getOutputStream())); + BufferedReader reader = new BufferedReader(new InputStreamReader(smtpSocket.getInputStream())); + writer.write("220\n"); + writer.flush(); + + String helloString = reader.readLine(); + System.out.println( helloString ); + writer.write("250\n"); + writer.flush(); + + senderMail = reader.readLine().substring("MAIL FROM:".length()); + System.out.println( senderMail ); + writer.write("250\n"); + writer.flush(); + + recipient = reader.readLine().substring("RCPT TO: ".length()); + System.out.println( recipient ); + writer.write("250\n"); + writer.flush(); + + String dataHeader = reader.readLine(); + System.out.println( dataHeader ); + + writer.write("354\n"); + writer.flush(); + String line; + do + { + line = reader.readLine(); + System.out.println( line ); + } while ( line.length() == 1 && line.charAt( 0) == 46); + reader.readLine(); + writer.write("250\n"); + writer.flush(); + String quit = reader.readLine(); + System.out.println( quit ); + writer.write("221\n"); + writer.flush(); + + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + }; + serverThread.setDaemon( deamon); + serverThread.start(); + } + + public String getRecipient() + { + return recipient; + } + + public String getSenderMail() + { + return senderMail; + } +} diff --git a/Rapla/src/org/rapla/components/mail/Pop3.java b/Rapla/src/org/rapla/components/mail/Pop3.java new file mode 100644 index 0000000..76badec --- /dev/null +++ b/Rapla/src/org/rapla/components/mail/Pop3.java @@ -0,0 +1,812 @@ +package org.rapla.components.mail; + +/* + * Pop3.java + * Copyright (c) 1996 John Thomas jthomas@cruzio.com + * All Rights Reserved. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for commercial or non-commercial purposes + * is hereby granted provided that this copyright notice + * appears in all copies. + * + * LIMITATION OF LIABILITY. UNDER NO CIRCUMSTANCES AND UNDER NO + * LEGAL THEORY, SHALL THE AUTHOR OF THIS CLASS BE LIABLE TO YOU + * OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES OF ANY KIND. + * + */ + +import java.io.*; +import java.util.*; +import java.net.*; + +/** Interface to a Pop3 mail server. Can be used to check, fetch and delete mail messages. + *

    Get the latest version of this and other classes in + * + * Stefano Locati's Java page. + * + * Based on the + * rfc1939.txt + * definition for the Post Office Protocol - Version 3 (obsoletes + * RFC 1725). + * If mailhost, user, password are not supplied to the + * constructor, then they must be specified on the connect and + * login calls. + * + *

    The Apop command is not supported by the + * Pop3 class. But there is an Apop class + * that extends Pop3 to add Apop support. + * It can be used just like the pop class, just create an Apop object + * instead a pop object. The Apop class still works even if the + * Pop3 server doesn't support Apop login. + * + *

    Simple Usage Example to display the size of each message. + *

    + *   Pop3 pop = new Pop3(host, user, password);
    + *   PopStatus status = pop.connect();
    + *   if ( status.OK() )
    + *      status = pop.login();
    + *   if ( status.OK() ) {
    + *       status = pop.list();
    + *       String[] responses = status.Responses();
    + *       for(int i=0; i< responses.length; i++) {
    + *         System.out.println("Message[" + i + "]='" + responses[i] + "'");
    + *       }
    + *       status = pop.quit();
    + *   }
    + * 
    + * + *

    The following methods try to closely implement the corresponding + * Pop3 server commands. See RFC1939. + * + *

    + *   PopStatus     stat()
    + *   PopStatus     list()
    + *   PopStatus     list(msgnum)
    + *   PopStatus     retr(msgnum)
    + *   PopStatus     dele(msgnum)
    + *   PopStatus     noop()
    + *   PopStatus     quit()
    + *   PopStatus     top(msgnum,numlines)
    + *   PopStatus     uidl(msgnum)
    + * 
    + * + *
    • The indicated methods have additional multiline output + * that can be retrieved with the get_Responses method for + * the PopStatus object. i.e. + *
      + *       PopStatus status = mypopserver.list()
      + *       String[] list = status.get_Responses()
      + *  
      + * + *
    • The following methods are convenience functions for the client + *
      + *       PopStatus  appendFile(filename,msgnum)
      + *
      + *       int get_TotalMsgs() Number of mail messages on server
      + *       int get_TotalSize() Total size of all mail messages
      + *                           _TotalSize and _TotalMsgs are set during
      + *                           login() by an internal stat() command
      + *  
      + * + *
    • The status of a POP request is returned in an instance of + * the PopStatus class. + * PopStatus has the following methods to extract the returned info. + *
      + *       boolean    OK()        True if request had no errors
      + *       String     Response()  The initial line returned by POP server
      + *                              that starts with either +OK or -ERR
      + *       String[]   Responses() If command returns multiple lines of
      + *                              data (RETR, TOP, LIST) then this method
      + *                              will return the lines in an array.
      + *  
      + * + *
    • Public debugging Methods. + *
      + *       void     setDebugOn(boolean)      turn on debug output
      + *       void     set_DebugFile(filename)  Set filename for debug output
      + *       void     debug(String DebugInfo)  Display string on stdout
      + *  
      + *
    + * + * @author Original author: John Thomas + * jthomas@cruzio.com + * @author Current maintainer: Stefano Locati + * slocati@geocities.com or + * stefano.locati@usa.net + * @version $Revision: 710 $ $Date: 2004-09-28 05:34:50 -0400 (Tue, 28 Sep 2004) $ + * + * + */ + +public +class Pop3 { + + /** Authorization State */ + protected final int AUTHORIZATION = 1; + + /** Transaction State */ + protected final int TRANSACTION = 2; + + /** Update State */ + protected final int UPDATE = 3; + + + /** Number of mail messages on server */ + protected int _TotalMsgs = 0; + + /** Total size of all messages on server */ + protected int _TotalSize = 0; + + /** status used by Send/Recv */ + protected boolean _StatusOK = false; + + /** Session State */ + protected int State = 0; + + /** The last Pop3 command sent to the server */ + protected String LastCmd; + + /** Pop3 server host name */ + protected String Host = null; + + /** Port on which the Pop3 server listens to */ + protected int Port = 110; + + /** Mailbox user name */ + protected String User = null; + + /** Mailbox password */ + protected String Password = null; + + /** Socket connected to the server */ + protected Socket server; + + /** Input stream connected to the server socket */ + protected BufferedReader serverInputStream; + + /** Output stream connected to the server socket */ + protected DataOutputStream serverOutputStream; + + /** debug On switch */ + private boolean debugOn=false; + + + /** + * Creates the object. No work is done. + * @param host a Pop3 server host name + * @param user a mailbox user name + * @param password a mailbox password + */ + public Pop3(String host, String user, String password) { + Host = host; + User = user; + Password = password; + } + + + /** + * Creates the object. No work is done + * You will have to supply host, user and password through + * connect() and login() methods. + * @see #connect(java.lang.String) + * @see #login(java.lang.String, java.lang.String) + */ + public Pop3() { + } + + + /** + * Makes a socket connection to the specified + * host (port 110). + * @param host a Pop3 server host name + * @return PopStatus: result of this operation + */ + public PopStatus connect(String host) { + // If method specifies the host name then save it and + // call the default connect method + Host = host; + return this.connect(); + } + + /** + * Makes a socket connection to the specified + * host and port. + * @param host Pop3 server host name + * @param port TCP port to connect to + * @return PopStatus: result of this operation + */ + public PopStatus connect(String host, int port) { + // If both host and port are specified then save them + // and then call the default connect method + Host = host; + Port = port; // Normally this would be 110 (RFC 1725) + return this.connect(); + } + + /** + * Makes a socket connection to the host specified + * in the constructor (port 110). + * @return PopStatus: result of this operation + */ + public synchronized PopStatus connect() { + PopStatus status = new PopStatus(); + debug("Connecting to " + Host + " at port " + Port); + if (Host == null) { + status._Response = "-ERR Host not specified"; + status._OK = false; + return status; + } + + try { + server = new Socket(Host,Port); + if (server == null) { // a failure with no exception???? + debug("-ERR Error while connecting to Pop3 server"); + status._OK = false; + status._Response = "-ERR Error while connecting to Pop3 server"; + } else { + debug("Connected"); + // get the input stream that we will use to read from the server + serverInputStream = new BufferedReader( + new InputStreamReader(server.getInputStream())); + if (serverInputStream == null) { + debug("Failed to setup an input stream."); + status._OK = false; + status._Response = "-ERR Error setting up input stream"; + server = null; + } + serverOutputStream = new DataOutputStream( + server.getOutputStream() ); + if (serverOutputStream == null) { + debug("Failed to setup an output stream."); + status._OK = false; + status._Response = "-ERR Error setting up output stream"; + server = null; + } + } + } + catch (Exception e) { + String msg = "Exception! " + e.toString(); + debug(msg); + status._OK = false; + status._Response = msg; + server = null; + } + + if (server != null) { + status._OK = true; + // POP protocol requires server to send a response on the + // connect. We will now get that response and parse it + _StatusOK = true; // Fake doing send() before recv() + status._Response = recv(); + Parse(status,2); + debug("Response=" + status._Response); + } + + if (status._OK) + State = AUTHORIZATION; + + return status; + } + + /** Login the specified user with the specified password. + * If the login is successful, a STAT command is issued + * to get the current number of messages. + * @param user a mailbox user name + * @param password a mailbox password + * @return PopStatus: result of this operation + */ + public PopStatus + login(String user, String password) { + User = user; + Password = password; + return login(); + } + + /** Login with the user and password specified in the constructor. + * If the login is successful, a STAT + * command is issued to get the current number of + * messages. + * @return PopStatus: result of this operation + */ + public synchronized PopStatus login() { + PopStatus status = new PopStatus(); + + if (User == null || Password == null) { + status._Response = "-ERR Userid or Password not specified"; + return status; + } + if ( server != null ) { + send("USER " + User); + status._Response = recv(); + Parse(status,1); + if (status._OK) { + send("PASS " + Password); + status._Response = recv(); + Parse(status,1); + if (status._OK) { + State = TRANSACTION; + // Now we will do an internal STAT function + stat(); + } + } + } + return status; + } + + /** Closes the socket connection. + * Use quit for a normal termination. + * @see #quit() + */ + public synchronized void close() { + debug("Closing socket"); + try { + server.close(); + State = 0; + } catch (IOException e) { + debug("Failure in server.close()"); + } + } + + /** Gets the number of messages and their total size from the server. + * @return PopStatus: result of this operation + */ + public synchronized PopStatus stat() { + PopStatus status = new PopStatus(); + if (State != TRANSACTION) { + status._Response = "-ERR Server not in transaction mode"; + return status; + } + send("STAT"); // Issue the STAT command + status._Response = recv(); // read the response + String[] tokens = Parse(status, 4); + + if (status._OK) { + _TotalMsgs = Convert.toInt(tokens[1]); + _TotalSize = Convert.toInt(tokens[2]); + } + + return status; + } + + /** + * Quits the session with the Pop3 server. + * After receiving a goodbye message from the server, + * the socket is closed. + * @return PopStatus + */ + public synchronized PopStatus quit() { + PopStatus status = new PopStatus(); + send("QUIT"); // Issue the STAT command + State = UPDATE; + status._Response = recv(); // read the response + Parse(status,2); + close(); + + return status; + } + + /** + * Gets the size of the specified mail message. + * @param msgnum message number + * @return PopStatus: result of this operation + */ + public synchronized PopStatus list(int msgnum) { + PopStatus status = new PopStatus(); + + send("LIST " + msgnum); // Issue the LIST n command + status._Response = recv(); // read the response + Parse(status,2); + return status; + } + + /** Gets a list of messages and the size of each one. + * @return PopStatus: result of this operation + */ + public synchronized PopStatus list() { + + PopStatus status = new PopStatus(); + send("LIST"); // Issue the LIST command + + recvN(status); // read the response + Parse(status,2); + + return status; + } + + /** Gets the uidl of the specified mail msg. + * @param msgnum message number + * @return PopStatus: result of this operation + */ + public synchronized PopStatus uidl(int msgnum) { + PopStatus status = new PopStatus(); + send("UIDL " + msgnum); // Issue the UIDL msgnum command + status._Response = recv(); // read the response + Parse(status,2); + + return status; + } + + /** Gets a list of messages and the UIDL of + * each one. + * UIDL is a message identifier that is + * garanteed to remain the same even across different + * sessions. + * @return PopStatus: result of this operation + */ + public synchronized PopStatus uidl() { + + PopStatus status = new PopStatus(); + send("UIDL"); // Issue the UIDL command + + recvN(status); // read the responses + Parse(status,2); + + return status; + } + + /** Gets the contents of a mail message. + * The array of strings obtained are the lines of the + * specified mail message. + * The lines have CR/LF stripped, any leading + * "." fixed up and the ending "." + * removed.
    + * The array can be retrieved with the status.Responses() method. + * The +OK or -ERR status line is + * returned. + * @param msgnum message number + * @return PopStatus: result of this operation + */ + public synchronized PopStatus retr(int msgnum) { + + PopStatus status = new PopStatus(); + send("RETR " + msgnum); // Issue the RETR n command + + // This may produce more than one response so we call the + // recvN method and save an array of strings in status._Responses. + recvN(status); // read the response + // The initial string that contains the status is in the + // status._Response state variable. + Parse(status,2); + + return status; + } + + + /** Gets the top n lines of a mail message. + * The array of strings obtained are the lines of the + * mail headers and the top N lines of the indicated mail msg. + * The lines have CR/LF striped, any leading + * "." fixed up and the ending "." + * removed.
    + * The array can be retrieved with status.Responses() method. + * The +OK or -ERR status line is + * returned. + * @param msgnum the message number + * @param n how many body lines should be retrieved. + * If n=0, you'll get just the headers, + * unfortunately I've bumped into a buggy Pop3 server that + * didn't like zeroes, so I suggest to use n=1 + * if you want just headers. + * @return PopStatus: result of this operation + */ + public synchronized PopStatus top(int msgnum, int n) { + + PopStatus status = new PopStatus(); + send("TOP " + msgnum + " " + n); // Issue the TOP msgnum n command + + // This may produce more than one response so we call the + // recvN method and set multiline output into _Responses + recvN(status); // read the response + + Parse(status,2); + + return status; + } + + + /** Marks the mail message for deletion + * This mail message will be deleted when QUIT is issued. + * If you lose the connection the message is not deleted. + * @param msgnum a message number + * @return PopStatus: result of this operation + * @see #rset() + */ + public synchronized PopStatus dele(int msgnum) { + PopStatus status = new PopStatus(); + send("DELE " + msgnum); // Issue the DELE n command + + status._Response = recv(); // read the response + Parse(status,2); + + return status; + } + + /** Resets the mail messages that have been marked for deletion. + * Nothing will be deleted if QUIT is issued next. + * @return PopStatus: result of this operation + * @see #dele(int) + */ + public synchronized PopStatus rset() { + PopStatus status = new PopStatus(); + send("RSET"); // Issue the RSET command + + status._Response = recv(); // read the response + Parse(status,2); + + return status; + } + + /** Does not do anything but it will keep the server active. + * @return PopStatus: result of this operation + */ + public synchronized PopStatus noop() { + + PopStatus status = new PopStatus(); + send("NOOP"); // Issue the NOOP command + + status._Response = recv(); // read the response + Parse(status,2); + + return status; + } + + /** Returns the number of messages on the server. + * This value is set by an internal STAT + * issued at login. + * @return the number of messages on this server. + * @see #get_TotalSize() + */ + public int get_TotalMsgs() { + return _TotalMsgs; + } + + /** Returns the size of messages on the server. + * This value is set by an internal STAT + * issued at login. + * @return the total size of messages on this server. + * @see #get_TotalMsgs() + */ + public int get_TotalSize() { + return _TotalSize; + } + + /** Returns the contents of a mail message and append it to the + * specified mail file. + * It will internally call RETR and then write + * the results to the specified file. + * @param filename the name of the file to be extended + * @param msgnum a message number + * @return PopStatus: result of this operation + */ + public synchronized PopStatus appendFile(String filename, int msgnum) { + PopStatus status = new PopStatus(); + + String[] contents; + + send("RETR " + msgnum); // RETR n will return the contents + // of message n + + recvN(status); // read the response + Parse(status,2); + if (status._OK) { + RandomAccessFile openfile; + try { + openfile = new RandomAccessFile(filename,"rw"); + } catch (IOException e) { + status._OK = false; + status._Response = "-ERR File open failed"; + return status; + } + Date datestamp = new Date(); + contents = status.Responses(); + try { + openfile.seek(openfile.length()); + openfile.writeBytes("From - " + datestamp.toString() + "\r\n"); + for(int i=0; i+OK + * and return an array of strings each representing a white space + * delimited token. The remainder of the response after + * maxToParse is returned as a single String. + */ + String[] Parse(PopStatus status, int maxToParse) { + String[] tokens = null; + + status._OK = false; + String response = status._Response; + if (response != null) { + int i=0; + int max; + if (response.trim().startsWith("+OK")) + status._OK = true; + else + debug(response); + // This will break the line into a set of tokens. + StringTokenizer st = new StringTokenizer(response); + //tokens = new String[st.countTokens()]; + if (maxToParse == -1) + max = st.countTokens(); + else + max = maxToParse; + tokens = new String[max+1]; + while (st.hasMoreTokens() && i < max) { + tokens[i] = new String(st.nextToken()); + //debug("Token " + i + "= '" + tokens[i] + "'"); + i++; + } + // Now get any remaining tokens as a single string + if (st.hasMoreTokens()) { + StringBuffer rest = new StringBuffer(st.nextToken()); + while (st.hasMoreTokens() ) + rest.append(" " + st.nextToken()); + tokens[max] = new String(rest); + //debug("Token " + max + "= '" + tokens[max] + "'"); + } + } + return tokens; + } + + /** Sends the passed command to the server. + */ + void send (String cmdline) { + debug(">> " + cmdline); + LastCmd = cmdline; // Save command for error msg + + try { + // Write string as a set of bytes + serverOutputStream.writeBytes(cmdline + "\r\n"); + _StatusOK = true; + } catch (IOException i){ + System.err.println("Caught exception while sending command to server"); + _StatusOK = false; + + } catch (Exception e) { + System.err.println("Send: Unexpected exception: " + e.toString()); + _StatusOK = false; + } + } + + /** Gets the next response to a previously sent command from the server. + */ + String recv() { + //debug("entered recv"); + + String line = ""; + if ( ! _StatusOK ) { + line = "-ERR Failed sending command to server"; + return line; + } + // send() has written a command to the + // server so now we will try to read the result + try { + line = serverInputStream.readLine(); + debug("<<" + line); + } catch (IOException i){ + System.err.println("Caught exception while reading"); + line = "-ERR Caught IOException while reading from server"; + } catch (Exception e) { + System.err.println("Unexpected exception: " + e.toString()); + line = "-ERR Unexpected exception while reading from server"; + } + if (line == null) { // prevent crash if reading a null line + debug("Read a null line from server"); + line = "-ERR "; + } + if (line.trim().startsWith("-ERR")) { + debug("Result from server has error!"); + debug("Sent: '" + LastCmd + "'"); + debug("Received: '" + line + "'"); + return line; + } else { + if (line.trim().startsWith("+OK")) { + return line; + } else { + debug("Received strange response"); + debug("'" + line + "'"); + line = "-ERR Invalid response"; + return line; + } + } + } + + /** Gets the responses to a previously sent command from the server. + * This is used when more than one line is expected. + * The last line of output should be ".\r\n" + */ + void recvN(PopStatus status) { + debug("entered recvN"); + Vector v = new Vector(100,100); + String line = ""; + + // send() has written a command to the + // server so now we will try to read the result + try { + boolean done = false; + int linenum=0; + while (!done) { + line = serverInputStream.readLine(); + linenum++; + debug("<<" + line.length() + " '" + line +"'"); + if (linenum == 1) { // process the initial line + if (line.trim().startsWith("-ERR ")) { + debug("Result from server has error!"); + debug("Sent: '" + LastCmd + "'"); + debug("Received: '" + line + "'"); + done = true; + status._Response = line; + } else { + if (line.trim().startsWith("+OK")) { + //Everything looks OK + status._Response = line; + } else { + debug("Received strange response"); + debug("'" + line + "'"); + done = true; + status._Response = "-ERR Invalid response"; + } + } + } else { + // process line 2 - n + if (line.startsWith(".")) { + if (line.length() == 1) + done = true; + else + v.addElement(line.substring(1)); + } else + v.addElement(line); + } + + } // end of while(!done) + } catch (IOException i){ + System.err.println("Caught exception while reading"); + status._Response = "-ERR Caught IOException while reading from server"; + } catch (Exception e) { + System.err.println("Unexpected exception: " + e.toString()); + status._Response = "-ERR Unexpected exception while reading from server"; + } + + status._Responses = new String[v.size()]; + v.copyInto(status._Responses); + return; + } + + /** Sets debug on or off. + * Debug messages are written to standard error. + * @param OnOff true to set on debugging, false to + * shut it up. + */ + public void setDebugOn(boolean OnOff) { + debugOn = OnOff; + } + + /** If debugOn switch is set, display debug info. + * @param debugstr a debug message + */ + public void debug(String debugstr) { + if (debugOn) { + System.err.println(debugstr); + } + } + + //------------------------------------------------------- + +} // end of Class Pop3 diff --git a/Rapla/src/org/rapla/components/mail/PopStatus.java b/Rapla/src/org/rapla/components/mail/PopStatus.java new file mode 100644 index 0000000..e311780 --- /dev/null +++ b/Rapla/src/org/rapla/components/mail/PopStatus.java @@ -0,0 +1,77 @@ +package org.rapla.components.mail; +/* + * PopStatus.java + * Copyright (c) 1996 John Thomas jthomas@cruzio.com + * All Rights Reserved. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for commercial or non-commercial purposes + * is hereby granted provided that this copyright notice + * appears in all copies. + * + * LIMITATION OF LIABILITY. UNDER NO CIRCUMSTANCES AND UNDER NO + * LEGAL THEORY, SHALL THE AUTHOR OF THIS CLASS BE LIABLE TO YOU + * OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES OF ANY KIND. + * + */ + +/**Class that holds the information returned from a pop request. + *

    Get the latest version of this and other classes on + * + * Stefano Locati's Java page. + * + * + * This is required so that the POP class can be thread safe. + * + * @author Original author: John Thomas + * jthomas@cruzio.com + * @author Current maintainer: Stefano Locati + * slocati@geocities.com or + * stefano.locati@usa.net + * @version $Revision: 713 $ $Date: 2004-11-16 09:37:01 -0500 (Tue, 16 Nov 2004) $ + * + */ + +public class PopStatus { + + + boolean _OK=false; // True if last command returned +OK + + String _Response; // Set to initial response from server + + String[] _Responses= new String[0]; // Set to last multiline response. + + +//---------------------------------------------------------- +/** + * Returns the multi-line output from a command. + * @return a multi-line response in an array of String. + */ +public String[] Responses() { + return _Responses; +} + +//---------------------------------------------------------- +/** + * Returns the initial status line output from a command + * @return the initial status line output from a command. + */ +public String Response() { + return _Response; +} + +//---------------------------------------------------------- +/** + * Returns the completion status (+OK true or + * -ERR false) from the last command issued + * to the server. + * @return true in case of success (+OK), + * false otherwise (-ERR). + */ +public boolean OK() { + return _OK; +} + +//------------------------------------------------------- +} // end of Class PopStatus diff --git a/Rapla/src/org/rapla/components/mail/SmtpClient.java b/Rapla/src/org/rapla/components/mail/SmtpClient.java new file mode 100644 index 0000000..62fc188 --- /dev/null +++ b/Rapla/src/org/rapla/components/mail/SmtpClient.java @@ -0,0 +1,234 @@ +package org.rapla.components.mail; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.net.Socket; +import java.net.UnknownHostException; + +import org.apache.avalon.framework.configuration.Configurable; +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.ConfigurationException; +import org.apache.avalon.framework.logger.AbstractLogEnabled; +import org.apache.avalon.framework.logger.Logger; + +/** +

    Default Implementation of the MailInterface with smtp protocol +at http://www.ietf.org/rfc/rfc821.txt. +

    +Sample Configuration for local Mail-server. +
    +    <smtp-service id="default-smpt-client">
    +       <smtp-host>localhost</smtp-host>
    +       <smtp-port>25</smtp-port>
    +    </smtp-service>
    +
    + + Sample Configuration with pop3 before smpt enabled. + Auhtentifies on the pop3 server before establashing a smtp connection. +
    +    <smtp-service id="default-smpt-client">
    +       <pop3-before-smtp>
    +         <host>pop-server.your.mail.provider</host>
    +         <user>your.username</user>
    +         <password>your.password</password>
    +       </pop3-before-smtp>
    +       <smtp-host>smpt-server.mail.provider</smtp-host>
    +       <smtp-port>25</smtp-port>
    +    </smtp-service>
    +
    + +*/ +public class SmtpClient extends AbstractLogEnabled implements Configurable,MailInterface { + //connection information + boolean smtpAfterPop; + String popHost; + String popUser; + String popPwd; + String smtpHost; + int smtpPort; + String localHost; + + public SmtpClient(Logger logger, Configuration config) throws ConfigurationException{ + enableLogging( logger); + configure( config); + } + + public SmtpClient(){ + smtpAfterPop = false; + smtpHost = "localhost"; + smtpPort = 25; + localHost = "localhost";//msp.getLocalHost(); + } + + public void configure(Configuration config) throws ConfigurationException { + // get the configuration entry text with the default-value "Welcome" + setSmtpPort(config.getChild("smtp-port").getValueAsInteger(25)); + setSmtpHost(config.getChild("smtp-host").getValue()); + Configuration pop3Config = config.getChild("pop3-before-smtp",false); + if (pop3Config != null) { + setPopBeforeSmtp(true); + setPopHost(pop3Config.getChild("host").getValue()); + setPopUser(pop3Config.getChild("username").getValue()); + setPopPwd(pop3Config.getChild("password").getValue()); + } + + } + + public void setPopHost(String s){ + popHost = s; + } + public void setPopUser(String s){ + popUser = s; + } + public void setPopPwd(String s){ + popPwd = s; + } + + public void setPopBeforeSmtp(boolean enable){ + smtpAfterPop = enable; + } + + public void setSmtpHost(String s){ + smtpHost = s; + } + public void setSmtpPort(int i){ + smtpPort = i; + } + + public void setLocalHost(String s){ + localHost = s; + } + public Pop3 popBeforeSmtp() throws MailException { + Pop3 pop = new Pop3(popHost, popUser, popPwd); + + getLogger().debug("Try to connect to host " + popHost + " for user " + popUser + "...."); + + PopStatus status = pop.connect(); + + if ( status.OK() ) + getLogger().debug("Connection established..."); + else + throw new MailException("Connection to pop server failed..."); + + if ( status.OK() ) status = pop.login(); + + if ( status.OK() ) + getLogger().debug("Login accepted!"); + + if ( status.OK() ) + pop.get_TotalMsgs(); + + if ( !status.OK() ) + throw new MailException("pop failed..."); + + return pop; + } + + + + private boolean isResponse(BufferedReader in, String expectedServerResponse) throws MailException,IOException { + String responseLine = in.readLine(); + if (responseLine == null) + return false; + getLogger().debug("Server: " + responseLine); + return responseLine.indexOf(expectedServerResponse)>=0; + } + + public void sendMail(String senderMail,String recipient, String subject, String mailBody) throws MailException { + Socket smtpSocket = null; //socket object + Writer out = null; //outputstream + BufferedReader reader = null; //inputStream + Pop3 pop = null; + if (smtpAfterPop){ + pop = popBeforeSmtp(); + getLogger().debug("popBeforeSmtp() has been succesfully executed, now trying to establish Smtp-connection..."); + + } + try { + smtpSocket = new Socket(smtpHost, smtpPort); + out = new OutputStreamWriter(smtpSocket.getOutputStream()); + reader = new BufferedReader(new InputStreamReader(smtpSocket.getInputStream())); + if (smtpSocket != null && out != null && reader != null) + { + if (!isResponse(reader, "220")) + throw new MailException("no greeting, return failed..."); + + + getLogger().debug("Client: HELO " + localHost); + out.write("HELO " + localHost + "\n"); + out.flush(); + if (!isResponse(reader, "250")) + throw new MailException("HELO return failed..."); + + getLogger().debug("Client: MAIL FROM: " + senderMail + ""); + out.write("MAIL FROM: " + senderMail + "\n"); + out.flush(); + if (!isResponse(reader, "250")) + throw new MailException("MAIL FROM return failed..."); + + + getLogger().debug("Client: RCPT TO: <" + recipient + ">"); + out.write("RCPT TO: " + recipient + "\n"); + out.flush(); + if (!isResponse(reader, "250")) + throw new MailException("RCPT return failed..."); + + getLogger().debug("Client: DATA"); + out.write("DATA\n"); + out.flush(); + if (!isResponse(reader, "354")) + throw new MailException("DATA return failed..."); + + getLogger().debug("Client: SUBJECT: " + subject); + out.write("Subject: " + subject); + out.write(13); + out.write(10); + /* + getLogger().debug("Client: FROM: " + senderName );} + out.writeBytes("FROM: " + senderName); + out.write(13); + out.write(10); + */ + + getLogger().debug("Client: " + mailBody); + out.write(mailBody); + getLogger().debug("Client: newline . newline"); + out.write(13); + out.write(10); + out.write(46); + out.write(13); + out.write(10); + out.flush(); + + if (isResponse(reader, "250") == true) { + getLogger().debug("Client: QUIT"); + out.write("QUIT" + "\n"); + out.flush(); + if (isResponse(reader, "221") == false) + throw new MailException("QUIT return failed..."); + } + out.close(); + reader.close(); + smtpSocket.close(); + } + } catch (UnknownHostException e) { + throw new MailException("Don't know about host: " + smtpHost); + } catch (IOException e) { + throw new MailException("I/O Error while connecting to: " + smtpHost + ":" + smtpPort + " \n" + e.getMessage()); + } finally { + if (pop != null) + pop.quit(); + } + return; + + + } + +} + + + + + diff --git a/Rapla/src/org/rapla/components/mail/package.html b/Rapla/src/org/rapla/components/mail/package.html new file mode 100644 index 0000000..93ffd69 --- /dev/null +++ b/Rapla/src/org/rapla/components/mail/package.html @@ -0,0 +1,7 @@ + +Very simple implementation of smtp-client-protocol. Supports "Pop before smtp" authentication +with the Pop3 classed from John Thomas. + + + + diff --git a/Rapla/src/org/rapla/components/package.html b/Rapla/src/org/rapla/components/package.html new file mode 100644 index 0000000..903f30d --- /dev/null +++ b/Rapla/src/org/rapla/components/package.html @@ -0,0 +1,8 @@ + +Contains all components and classes that are independant from the rest of rapla, +and can easily be reused in other projects. Dependencies with other components or +packages are explicitly stated in a file called DEPENDENCIES. + + + + diff --git a/Rapla/src/org/rapla/components/tablesorter/TableSorter.java b/Rapla/src/org/rapla/components/tablesorter/TableSorter.java new file mode 100644 index 0000000..9442390 --- /dev/null +++ b/Rapla/src/org/rapla/components/tablesorter/TableSorter.java @@ -0,0 +1,545 @@ +/* + * Sun Microsystems grants you ("Licensee") a non-exclusive, royalty + * free, license to use, modify and redistribute this software in + * source and binary code form, provided that i) this copyright notice + * and license appear on all copies of the software; and ii) Licensee + * does not utilize the software in a manner which is disparaging to + * Sun Microsystems. + * + * The software media is distributed on an "As Is" basis, without + * warranty. Neither the authors, the software developers nor Sun + * Microsystems make any representation, or warranty, either express + * or implied, with respect to the software programs, their quality, + * accuracy, or fitness for a specific purpose. Therefore, neither the + * authors, the software developers nor Sun Microsystems shall have + * any liability to you or any other person or entity with respect to + * any liability, loss, or damage caused or alleged to have been + * caused directly or indirectly by programs contained on the + * media. This includes, but is not limited to, interruption of + * service, loss of data, loss of classroom time, loss of consulting + * or anticipatory *profits, or consequential damages from the use of + * these programs. +*/ +package org.rapla.components.tablesorter; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.swing.Icon; +import javax.swing.JLabel; +import javax.swing.JTable; +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.JTableHeader; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumnModel; +import javax.swing.table.TableModel; + +/** + * TableSorter is a decorator for TableModels; adding sorting + * functionality to a supplied TableModel. TableSorter does + * not store or copy the data in its TableModel; instead it maintains + * a map from the row indexes of the view to the row indexes of the + * model. As requests are made of the sorter (like getValueAt(row, col)) + * they are passed to the underlying model after the row numbers + * have been translated via the internal mapping array. This way, + * the TableSorter appears to hold another copy of the table + * with the rows in a different order. + *

    + * TableSorter registers itself as a listener to the underlying model, + * just as the JTable itself would. Events recieved from the model + * are examined, sometimes manipulated (typically widened), and then + * passed on to the TableSorter's listeners (typically the JTable). + * If a change to the model has invalidated the order of TableSorter's + * rows, a note of this is made and the sorter will resort the + * rows the next time a value is requested. + *

    + * When the tableHeader property is set, either by using the + * setTableHeader() method or the two argument constructor, the + * table header may be used as a complete UI for TableSorter. + * The default renderer of the tableHeader is decorated with a renderer + * that indicates the sorting status of each column. In addition, + * a mouse listener is installed with the following behavior: + *

      + *
    • + * Mouse-click: Clears the sorting status of all other columns + * and advances the sorting status of that column through three + * values: {NOT_SORTED, ASCENDING, DESCENDING} (then back to + * NOT_SORTED again). + *
    • + * SHIFT-mouse-click: Clears the sorting status of all other columns + * and cycles the sorting status of the column through the same + * three values, in the opposite order: {NOT_SORTED, DESCENDING, ASCENDING}. + *
    • + * CONTROL-mouse-click and CONTROL-SHIFT-mouse-click: as above except + * that the changes to the column do not cancel the statuses of columns + * that are already sorting - giving a way to initiate a compound + * sort. + *
    + *

    + * This is a long overdue rewrite of a class of the same name that + * first appeared in the swing table demos in 1997. + * + * @author Philip Milne + * @author Brendon McLean + * @author Dan van Enckevort + * @author Parwinder Sekhon + * @version 2.0 02/27/04 + */ + +public class TableSorter extends AbstractTableModel { + private static final long serialVersionUID = 1L; + + protected TableModel tableModel; + + public static final int DESCENDING = -1; + public static final int NOT_SORTED = 0; + public static final int ASCENDING = 1; + private Map enabled= new HashMap(); + + private static Directive EMPTY_DIRECTIVE = new Directive(-1, NOT_SORTED); + + public static final Comparator COMPARABLE_COMAPRATOR = new Comparator() { + public int compare(Object o1, Object o2) { + return ((Comparable) o1).compareTo(o2); + } + }; + public static final Comparator LEXICAL_COMPARATOR = new Comparator() { + public int compare(Object o1, Object o2) { + return o1.toString().compareTo(o2.toString()); + } + }; + + private Row[] viewToModel; + private int[] modelToView; + + private JTableHeader tableHeader; + private MouseListener mouseListener; + private TableModelListener tableModelListener; + private Map columnComparators = new HashMap(); + private List sortingColumns = new ArrayList(); + + public TableSorter() { + this.mouseListener = new MouseHandler(); + this.tableModelListener = new TableModelHandler(); + } + + public TableSorter(TableModel tableModel) { + this(); + setTableModel(tableModel); + } + + public TableSorter(TableModel tableModel, JTableHeader tableHeader) { + this(); + setTableHeader(tableHeader); + setTableModel(tableModel); + } + + private void clearSortingState() { + viewToModel = null; + modelToView = null; + } + + public TableModel getTableModel() { + return tableModel; + } + + public void setTableModel(TableModel tableModel) { + if (this.tableModel != null) { + this.tableModel.removeTableModelListener(tableModelListener); + } + + this.tableModel = tableModel; + if (this.tableModel != null) { + this.tableModel.addTableModelListener(tableModelListener); + } + + clearSortingState(); + fireTableStructureChanged(); + } + + public JTableHeader getTableHeader() { + return tableHeader; + } + + public void setTableHeader(JTableHeader tableHeader) { + if (this.tableHeader != null) { + this.tableHeader.removeMouseListener(mouseListener); + TableCellRenderer defaultRenderer = this.tableHeader.getDefaultRenderer(); + if (defaultRenderer instanceof SortableHeaderRenderer) { + this.tableHeader.setDefaultRenderer(((SortableHeaderRenderer) defaultRenderer).tableCellRenderer); + } + } + this.tableHeader = tableHeader; + if (this.tableHeader != null) { + this.tableHeader.addMouseListener(mouseListener); + this.tableHeader.setDefaultRenderer( + new SortableHeaderRenderer(this.tableHeader.getDefaultRenderer())); + } + } + + public boolean isSorting() { + return sortingColumns.size() != 0; + } + + private Directive getDirective(int column) { + for (int i = 0; i < sortingColumns.size(); i++) { + Directive directive = (Directive)sortingColumns.get(i); + if (directive.column == column) { + return directive; + } + } + return EMPTY_DIRECTIVE; + } + + public int getSortingStatus(int column) { + return getDirective(column).direction; + } + + private void sortingStatusChanged() { + clearSortingState(); + fireTableDataChanged(); + if (tableHeader != null) { + tableHeader.repaint(); + } + } + + public void setSortingStatus(int column, int status) { + Directive directive = getDirective(column); + if (directive != EMPTY_DIRECTIVE) { + sortingColumns.remove(directive); + } + if (status != NOT_SORTED) { + sortingColumns.add(new Directive(column, status)); + } + sortingStatusChanged(); + } + + protected Icon getHeaderRendererIcon(int column, int size) { + Directive directive = getDirective(column); + if (directive == EMPTY_DIRECTIVE) { + return null; + } + return new Arrow(directive.direction == DESCENDING, size, sortingColumns.indexOf(directive)); + } + + private void cancelSorting() { + sortingColumns.clear(); + sortingStatusChanged(); + } + + public void setColumnComparator(int column, Comparator comparator) { + if (comparator == null) { + columnComparators.remove(column); + } else { + columnComparators.put(column, comparator); + } + setSortingStatus(column, ASCENDING); + } + + protected Comparator getComparator(int column) { + Comparator comparator = (Comparator) columnComparators.get(column); + if (comparator != null) { + return comparator; + } + Class columnType = tableModel.getColumnClass(column); + if (Comparable.class.isAssignableFrom(columnType)) { + return COMPARABLE_COMAPRATOR; + } + return LEXICAL_COMPARATOR; + } + + private Row[] getViewToModel() { + if (viewToModel == null) { + int tableModelRowCount = tableModel.getRowCount(); + viewToModel = new Row[tableModelRowCount]; + for (int row = 0; row < tableModelRowCount; row++) { + viewToModel[row] = new Row(row); + } + + if (isSorting()) { + Arrays.sort(viewToModel); + } + } + return viewToModel; + } + + public int modelIndex(int viewIndex) { + return getViewToModel()[viewIndex].modelIndex; + } + + private int[] getModelToView() { + if (modelToView == null) { + int n = getViewToModel().length; + modelToView = new int[n]; + for (int i = 0; i < n; i++) { + modelToView[modelIndex(i)] = i; + } + } + return modelToView; + } + + // TableModel interface methods + + public int getRowCount() { + return (tableModel == null) ? 0 : tableModel.getRowCount(); + } + + public int getColumnCount() { + return (tableModel == null) ? 0 : tableModel.getColumnCount(); + } + + public String getColumnName(int column) { + return tableModel.getColumnName(column); + } + + public Class getColumnClass(int column) { + return tableModel.getColumnClass(column); + } + + public boolean isCellEditable(int row, int column) { + return tableModel.isCellEditable(modelIndex(row), column); + } + + public Object getValueAt(int row, int column) { + return tableModel.getValueAt(modelIndex(row), column); + } + + public void setValueAt(Object aValue, int row, int column) { + tableModel.setValueAt(aValue, modelIndex(row), column); + } + + // Helper classes + + private class Row implements Comparable { + private int modelIndex; + + public Row(int index) { + this.modelIndex = index; + } + + public int compareTo(Object o) { + int row1 = modelIndex; + int row2 = ((Row) o).modelIndex; + + for (Iterator it = sortingColumns.iterator(); it.hasNext();) { + Directive directive = (Directive) it.next(); + int column = directive.column; + Object o1 = tableModel.getValueAt(row1, column); + Object o2 = tableModel.getValueAt(row2, column); + + int comparison = 0; + // Define null less than everything, except null. + if (o1 == null && o2 == null) { + comparison = 0; + } else if (o1 == null) { + comparison = -1; + } else if (o2 == null) { + comparison = 1; + } else { + if ( isSortabe(column) ) + { + comparison = getComparator(column).compare(o1, o2); + } + } + if (comparison != 0) { + return directive.direction == DESCENDING ? -comparison : comparison; + } + } + return 0; + } + } + + private class TableModelHandler implements TableModelListener { + public void tableChanged(TableModelEvent e) { + // If we're not sorting by anything, just pass the event along. + if (!isSorting()) { + clearSortingState(); + fireTableChanged(e); + return; + } + + // If the table structure has changed, cancel the sorting; the + // sorting columns may have been either moved or deleted from + // the model. + if (e.getFirstRow() == TableModelEvent.HEADER_ROW) { + cancelSorting(); + fireTableChanged(e); + return; + } + + // We can map a cell event through to the view without widening + // when the following conditions apply: + // + // a) all the changes are on one row (e.getFirstRow() == e.getLastRow()) and, + // b) all the changes are in one column (column != TableModelEvent.ALL_COLUMNS) and, + // c) we are not sorting on that column (getSortingStatus(column) == NOT_SORTED) and, + // d) a reverse lookup will not trigger a sort (modelToView != null) + // + // Note: INSERT and DELETE events fail this test as they have column == ALL_COLUMNS. + // + // The last check, for (modelToView != null) is to see if modelToView + // is already allocated. If we don't do this check; sorting can become + // a performance bottleneck for applications where cells + // change rapidly in different parts of the table. If cells + // change alternately in the sorting column and then outside of + // it this class can end up re-sorting on alternate cell updates - + // which can be a performance problem for large tables. The last + // clause avoids this problem. + int column = e.getColumn(); + if (e.getFirstRow() == e.getLastRow() + && column != TableModelEvent.ALL_COLUMNS + && getSortingStatus(column) == NOT_SORTED + && modelToView != null) { + int viewIndex = getModelToView()[e.getFirstRow()]; + fireTableChanged(new TableModelEvent(TableSorter.this, + viewIndex, viewIndex, + column, e.getType())); + return; + } + + // Something has happened to the data that may have invalidated the row order. + clearSortingState(); + fireTableDataChanged(); + return; + } + } + + private class MouseHandler extends MouseAdapter { + public void mouseClicked(MouseEvent e) { + JTableHeader h = (JTableHeader) e.getSource(); + TableColumnModel columnModel = h.getColumnModel(); + int viewColumn = columnModel.getColumnIndexAtX(e.getX()); + int column = columnModel.getColumn(viewColumn).getModelIndex(); + if (column != -1) { + if ( !isSortabe( column)) + { + return; + } + int status = getSortingStatus(column); + if (!e.isControlDown()) { + cancelSorting(); + } + // Cycle the sorting states through {NOT_SORTED, ASCENDING, DESCENDING} or + // {NOT_SORTED, DESCENDING, ASCENDING} depending on whether shift is pressed. + status = status + (e.isShiftDown() ? -1 : 1); + status = (status + 4) % 3 - 1; // signed mod, returning {-1, 0, 1} + setSortingStatus(column, status); + } + } + } + + private static class Arrow implements Icon { + private boolean descending; + private int size; + private int priority; + + public Arrow(boolean descending, int size, int priority) { + this.descending = descending; + this.size = size; + this.priority = priority; + } + + public void paintIcon(Component c, Graphics g, int x, int y) { + Color color = c == null ? Color.gray : c.getBackground(); + // In a compound sort, make each succesive triangle 20% + // smaller than the previous one. + int dx = (int)(size/2*Math.pow(0.8, priority)); + int dy = descending ? dx : -dx; + // Align icon (roughly) with font baseline. + y = y + 5*size/6 + (descending ? -dy : 0); + int shift = descending ? 1 : -1; + g.translate(x, y); + + // Right diagonal. + g.setColor(color.darker()); + g.drawLine(dx / 2, dy, 0, 0); + g.drawLine(dx / 2, dy + shift, 0, shift); + + // Left diagonal. + g.setColor(color.brighter()); + g.drawLine(dx / 2, dy, dx, 0); + g.drawLine(dx / 2, dy + shift, dx, shift); + + // Horizontal line. + if (descending) { + g.setColor(color.darker().darker()); + } else { + g.setColor(color.brighter().brighter()); + } + g.drawLine(dx, 0, 0, 0); + + g.setColor(color); + g.translate(-x, -y); + } + + public int getIconWidth() { + return size; + } + + public int getIconHeight() { + return size; + } + } + + private class SortableHeaderRenderer implements TableCellRenderer { + private TableCellRenderer tableCellRenderer; + + public SortableHeaderRenderer(TableCellRenderer tableCellRenderer) { + this.tableCellRenderer = tableCellRenderer; + } + + public Component getTableCellRendererComponent(JTable table, + Object value, + boolean isSelected, + boolean hasFocus, + int row, + int column) { + Component c = tableCellRenderer.getTableCellRendererComponent(table, + value, isSelected, hasFocus, row, column); + if (c instanceof JLabel) { + JLabel l = (JLabel) c; + l.setHorizontalTextPosition(JLabel.LEFT); + int modelColumn = table.convertColumnIndexToModel(column); + l.setIcon(getHeaderRendererIcon(modelColumn, l.getFont().getSize())); + } + return c; + } + } + + private static class Directive { + private int column; + private int direction; + + public Directive(int column, int direction) { + this.column = column; + this.direction = direction; + } + } + + public void setSortable(int column, boolean b) { + enabled.put( column,b); + } + + public boolean isSortabe( int column) + { + final Boolean sortable = enabled.get(column); + if ( sortable == null) + { + return true; + } + return sortable; + } +} diff --git a/Rapla/src/org/rapla/components/treetable/AbstractTreeTableModel.java b/Rapla/src/org/rapla/components/treetable/AbstractTreeTableModel.java new file mode 100644 index 0000000..efa49a5 --- /dev/null +++ b/Rapla/src/org/rapla/components/treetable/AbstractTreeTableModel.java @@ -0,0 +1,210 @@ +/* + * AbstractTreeTableModel.java + * + * Copyright (c) 1998 Sun Microsystems, Inc. All Rights Reserved. + * + * Sun Microsystems grants you ("Licensee") a non-exclusive, royalty + * free, license to use, modify and redistribute this software in + * source and binary code form, provided that i) this copyright notice + * and license appear on all copies of the software; and ii) Licensee + * does not utilize the software in a manner which is disparaging to + * Sun Microsystems. + * + * The software media is distributed on an "As Is" basis, without + * warranty. Neither the authors, the software developers nor Sun + * Microsystems make any representation, or warranty, either express + * or implied, with respect to the software programs, their quality, + * accuracy, or fitness for a specific purpose. Therefore, neither the + * authors, the software developers nor Sun Microsystems shall have + * any liability to you or any other person or entity with respect to + * any liability, loss, or damage caused or alleged to have been + * caused directly or indirectly by programs contained on the + * media. This includes, but is not limited to, interruption of + * service, loss of data, loss of classroom time, loss of consulting + * or anticipatory *profits, or consequential damages from the use of + * these programs. +*/ + +package org.rapla.components.treetable; + +import javax.swing.tree.*; +import javax.swing.event.*; + +/** + * @version 1.2 10/27/98 + * An abstract implementation of the TreeTableModel interface, handling the list + * of listeners. + * @author Philip Milne + */ + +public abstract class AbstractTreeTableModel implements TreeTableModel { + protected Object root; + protected EventListenerList listenerList = new EventListenerList(); + + public AbstractTreeTableModel(Object root) { + this.root = root; + } + + // + // Default implementations for methods in the TreeModel interface. + // + + public Object getRoot() { + return root; + } + + public boolean isLeaf(Object node) { + return getChildCount(node) == 0; + } + + public void valueForPathChanged(TreePath path, Object newValue) {} + + // This is not called in the JTree's default mode: use a naive implementation. + public int getIndexOfChild(Object parent, Object child) { + for (int i = 0; i < getChildCount(parent); i++) { + if (getChild(parent, i).equals(child)) { + return i; + } + } + return -1; + } + + public void addTreeModelListener(TreeModelListener l) { + listenerList.add(TreeModelListener.class, l); + } + + public void removeTreeModelListener(TreeModelListener l) { + listenerList.remove(TreeModelListener.class, l); + } + + /* + * Notify all listeners that have registered interest for + * notification on this event type. The event instance + * is lazily created using the parameters passed into + * the fire method. + * @see EventListenerList + */ + protected void fireTreeNodesChanged(Object source, Object[] path, + int[] childIndices, + Object[] children) { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + TreeModelEvent e = null; + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==TreeModelListener.class) { + // Lazily create the event: + if (e == null) + e = new TreeModelEvent(source, path, + childIndices, children); + ((TreeModelListener)listeners[i+1]).treeNodesChanged(e); + } + } + } + + /* + * Notify all listeners that have registered interest for + * notification on this event type. The event instance + * is lazily created using the parameters passed into + * the fire method. + * @see EventListenerList + */ + protected void fireTreeNodesInserted(Object source, Object[] path, + int[] childIndices, + Object[] children) { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + TreeModelEvent e = null; + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==TreeModelListener.class) { + // Lazily create the event: + if (e == null) + e = new TreeModelEvent(source, path, + childIndices, children); + ((TreeModelListener)listeners[i+1]).treeNodesInserted(e); + } + } + } + + /* + * Notify all listeners that have registered interest for + * notification on this event type. The event instance + * is lazily created using the parameters passed into + * the fire method. + * @see EventListenerList + */ + protected void fireTreeNodesRemoved(Object source, Object[] path, + int[] childIndices, + Object[] children) { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + TreeModelEvent e = null; + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==TreeModelListener.class) { + // Lazily create the event: + if (e == null) + e = new TreeModelEvent(source, path, + childIndices, children); + ((TreeModelListener)listeners[i+1]).treeNodesRemoved(e); + } + } + } + + /* + * Notify all listeners that have registered interest for + * notification on this event type. The event instance + * is lazily created using the parameters passed into + * the fire method. + * @see EventListenerList + */ + protected void fireTreeStructureChanged(Object source, Object[] path, + int[] childIndices, + Object[] children) { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + TreeModelEvent e = null; + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==TreeModelListener.class) { + // Lazily create the event: + if (e == null) + e = new TreeModelEvent(source, path, + childIndices, children); + ((TreeModelListener)listeners[i+1]).treeStructureChanged(e); + } + } + } + + // + // Default impelmentations for methods in the TreeTableModel interface. + // + + public Class getColumnClass(int column) { return Object.class; } + + /** By default, make the column with the Tree in it the only editable one. + * Making this column editable causes the JTable to forward mouse + * and keyboard events in the Tree column to the underlying JTree. + */ + public boolean isCellEditable(Object node, int column) { + return getColumnClass(column) == TreeTableModel.class; + } + + public void setValueAt(Object aValue, Object node, int column) {} + + + // Left to be implemented in the subclass: + + /* + * public Object getChild(Object parent, int index) + * public int getChildCount(Object parent) + * public int getColumnCount() + * public String getColumnName(Object node, int column) + * public Object getValueAt(Object node, int column) + */ +} diff --git a/Rapla/src/org/rapla/components/treetable/DEPENDENCIES b/Rapla/src/org/rapla/components/treetable/DEPENDENCIES new file mode 100644 index 0000000..00a596d --- /dev/null +++ b/Rapla/src/org/rapla/components/treetable/DEPENDENCIES @@ -0,0 +1,4 @@ +This component depends on the following packages (including subpackages): + +java.* +javax.swing.* diff --git a/Rapla/src/org/rapla/components/treetable/JTreeTable.java b/Rapla/src/org/rapla/components/treetable/JTreeTable.java new file mode 100644 index 0000000..b697531 --- /dev/null +++ b/Rapla/src/org/rapla/components/treetable/JTreeTable.java @@ -0,0 +1,910 @@ +/* + * Sun Microsystems grants you ("Licensee") a non-exclusive, royalty + * free, license to use, modify and redistribute this software in + * source and binary code form, provided that i) this copyright notice + * and license appear on all copies of the software; and ii) Licensee + * does not utilize the software in a manner which is disparaging to + * Sun Microsystems. + * + * The software media is distributed on an "As Is" basis, without + * warranty. Neither the authors, the software developers nor Sun + * Microsystems make any representation, or warranty, either express + * or implied, with respect to the software programs, their quality, + * accuracy, or fitness for a specific purpose. Therefore, neither the + * authors, the software developers nor Sun Microsystems shall have + * any liability to you or any other person or entity with respect to + * any liability, loss, or damage caused or alleged to have been + * caused directly or indirectly by programs contained on the + * media. This includes, but is not limited to, interruption of + * service, loss of data, loss of classroom time, loss of consulting + * or anticipatory *profits, or consequential damages from the use of + * these programs. +*/ + +package org.rapla.components.treetable; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.EventObject; +import java.util.List; + +import javax.swing.JComponent; +import javax.swing.JTable; +import javax.swing.JTree; +import javax.swing.KeyStroke; +import javax.swing.ListSelectionModel; +import javax.swing.LookAndFeel; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.event.CellEditorListener; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.event.TreeExpansionEvent; +import javax.swing.event.TreeExpansionListener; +import javax.swing.event.TreeModelEvent; +import javax.swing.event.TreeModelListener; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableCellRenderer; +import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.tree.DefaultTreeSelectionModel; +import javax.swing.tree.TreeCellRenderer; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; + +/** + * Original version Philip Milne and Scott Violet + * Modified by Christopher Kohlhaas to support editing and keyboard handling. + */ + +public class JTreeTable extends JTable { + private static final long serialVersionUID = 1L; + + private RendererTree tree = new RendererTree(); + private TreeTableEditor treeCellEditor; + private TableToolTipRenderer toolTipRenderer = null; + private int focusedRow = -1; + private String cachedSearchKey = ""; + + public JTreeTable(TreeTableModel model) { + super(); + + setTreeTableModel( model ); + + // Force the JTable and JTree to share their row selection models. + ListToTreeSelectionModelWrapper selectionWrapper = new + ListToTreeSelectionModelWrapper(); + setSelectionModel(selectionWrapper.getListSelectionModel()); + setShowGrid( false); + // No intercell spacing + setIntercellSpacing(new Dimension(1, 0)); + setShowVerticalLines(true); + + tree.setEditable(false); + tree.setSelectionModel(selectionWrapper); + tree.setShowsRootHandles(true); + tree.setRootVisible(false); + setDefaultRenderer( TreeTableModel.class, tree ); + setTreeCellEditor(null); + + // And update the height of the trees row to match that of + // the table. + if (tree.getRowHeight() < 1) { + // Metal looks better like this. + setRowHeight(22); + } + } + + public void setToolTipRenderer(TableToolTipRenderer renderer) { + toolTipRenderer = renderer; + } + + public TableToolTipRenderer getToolTipRenderer() { + return toolTipRenderer; + } + + public String getToolTipText(MouseEvent evt) { + if (toolTipRenderer == null) + return super.getToolTipText(evt); + Point p = new Point(evt.getX(),evt.getY()); + int column = columnAtPoint(p); + int row = rowAtPoint(p); + if (row >=0 && column>=0) + return toolTipRenderer.getToolTipText(this,row,column); + else + return super.getToolTipText(evt); + } + + /** + * Overridden to message super and forward the method to the tree. + * Since the tree is not actually in the component hierarchy it will + * never receive this unless we forward it in this manner. + */ + public void updateUI() { + super.updateUI(); + if(tree != null) { + tree.updateUI(); + } + // Use the tree's default foreground and background colors in the + // table. + LookAndFeel.installColorsAndFont(this, "Tree.background", + "Tree.foreground", "Tree.font"); + } + + /** Set a custom TreeCellEditor. The default one is a TextField.*/ + public void setTreeCellEditor(TreeTableEditor editor) { + treeCellEditor = editor; + setDefaultEditor( TreeTableModel.class, new DelegationgTreeCellEditor(treeCellEditor) ); + } + + + /** Returns the tree that is being shared between the model. + If you set a different TreeCellRenderer for this tree it should + inherit from DefaultTreeCellRenderer. Otherwise the selection-color + and focus color will not be set correctly. + */ + public JTree getTree() { + return tree; + } + + /** + * search for given search term in child nodes of selected nodes + * @param search what to search for + * @param parentNode where to search fo + * @return first childnode where its tostring representation in tree starts with search term, null if no one found + */ + private TreeNode getNextTreeNodeMatching(String search, TreeNode parentNode) { + TreeNode result = null; + + Enumeration children = parentNode.children(); + while (children.hasMoreElements()) { + TreeNode treeNode = (TreeNode) children.nextElement(); + String compareS = treeNode.toString().toLowerCase(); + if (compareS.startsWith(search)) { + result = treeNode; + break; + } + } + return result; + } + + /** + * create treepath from treenode + * @param treeNode Treenode + * @return treepath object + */ + public static TreePath getPath(TreeNode treeNode) { + List nodes = new ArrayList(); + if (treeNode != null) { + + nodes.add(treeNode); + treeNode = treeNode.getParent(); + while (treeNode != null) { + nodes.add(0, treeNode); + treeNode = treeNode.getParent(); + } + } + + return nodes.isEmpty() ? null : new TreePath(nodes.toArray()); + } + + + /** overridden to support keyboard expand/collapse for the tree.*/ + protected boolean processKeyBinding(KeyStroke ks, + KeyEvent e, + int condition, + boolean pressed) + { + if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) + { + if (tree != null && !isEditing() && getSelectedColumn() == getTreeColumnNumber()) { + + if (e.getID() == KeyEvent.KEY_PRESSED) + { + if ( (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyChar() =='+')) { + int row = getSelectedRow(); + if (row >= 0) { + if (tree.isExpanded(row)) { + tree.collapseRow(row); + } else { + tree.expandPath(tree.getPathForRow(row)); + } + } + return true; + } + + if (e.getKeyCode() == KeyEvent.VK_LEFT) { + int row = getSelectedRow(); + if (row >= 0) { + TreePath pathForRow = tree.getPathForRow(row); + // if selected node is expanded than collapse it + // else selected parents node + if (tree.isExpanded(pathForRow)) { + // only if root is visible we should collapse first level + final boolean canCollapse = pathForRow.getPathCount() > (tree.isRootVisible() ? 0 : 1); + if (canCollapse) + tree.collapsePath(pathForRow); + } else { + // only if root is visible we should collapse first level or select parent node + final boolean canCollapse = pathForRow.getPathCount() > (tree.isRootVisible() ? 1 : 2); + + if (canCollapse) { + pathForRow = pathForRow.getParentPath(); + final int parentRow = tree.getRowForPath(pathForRow ); + tree.setSelectionInterval(parentRow, parentRow); + } + } + + if (pathForRow != null) { + Rectangle rect = getCellRect(tree.getRowForPath(pathForRow), 0, false); + scrollRectToVisible(rect ); + } + + + + } + return true; + } + if (e.getKeyCode() == KeyEvent.VK_RIGHT) { + int row = getSelectedRow(); + if (row >= 0) { + final TreePath path = tree.getPathForRow(row); + if (tree.isCollapsed(path)) { + tree.expandPath(path); + } + } + return true; + } + + // live search in current parent node + if ((Character.isLetterOrDigit(e.getKeyChar()))) { + char keyChar = e.getKeyChar(); + + + // we are searching in the current parent node + // first we assume that we have selected a parent node + // so we should have children + TreePath selectedPath = tree.getSelectionModel().getLeadSelectionPath(); + TreeNode selectedNode = (TreeNode) selectedPath.getLastPathComponent(); + + + // if we don't have children we might have selected a leaf so choose its parent + if (selectedNode.getChildCount() == 0) { + // set new selectedNode + selectedPath = selectedPath.getParentPath(); + selectedNode = (TreeNode) selectedPath.getLastPathComponent(); + } + + // search term + String search = ("" + keyChar).toLowerCase(); + + // try to find node with matching searchterm plus the search before + TreeNode nextTreeNodeMatching = getNextTreeNodeMatching(cachedSearchKey + search, selectedNode); + + // if we did not find anything, try to find search term only: restart! + if (nextTreeNodeMatching == null) { + nextTreeNodeMatching = getNextTreeNodeMatching(search, selectedNode); + cachedSearchKey = ""; + } + // if we found a node, select it, make it visible and return true + if (nextTreeNodeMatching != null) { + TreePath foundPath = getPath(nextTreeNodeMatching); + + // select our found path + this.tree.getSelectionModel().setSelectionPath(foundPath); + + //make it visible + this.tree.expandPath(foundPath); + this.tree.makeVisible(foundPath); + // Scroll to the found row + int row = tree.getRowForPath( foundPath); + int col = 0; + Rectangle rect = getCellRect(row, col, false); + scrollRectToVisible(rect ); + + // store found treepath + cachedSearchKey = cachedSearchKey + search; + + return true; + } + } + cachedSearchKey = ""; + + /* Uncomment this if you don't want to start tree-cell-editing + on a non navigation key stroke. + + if (e.getKeyCode() != e.VK_TAB && e.getKeyCode() != e.VK_F2 + && e.getKeyCode() != e.VK_DOWN && e.getKeyCode() != e.VK_UP + && e.getKeyCode() != e.VK_LEFT && e.getKeyCode() != e.VK_RIGHT + && e.getKeyCode() != e.VK_PAGE_UP && e.getKeyCode() != e.VK_PAGE_DOWN + ) + return true; + */ + } + } + } + // reset cachedkey to null if we did not find anything + + return super.processKeyBinding(ks,e,condition,pressed); + } + + public void setTreeTableModel(TreeTableModel model) { + tree.setModel(model); + super.setModel(new TreeTableModelAdapter(model)); + } + + /** + * Workaround for BasicTableUI anomaly. Make sure the UI never tries to + * resize the editor. The UI currently uses different techniques to + * paint the renderers and editors; overriding setBounds() below + * is not the right thing to do for an editor. Returning -1 for the + * editing row in this case, ensures the editor is never painted. + */ + public int getEditingRow() { + int column = getEditingColumn(); + if( getColumnClass(column) == TreeTableModel.class ) + return -1; + return editingRow; + } + + /** + * Returns the actual row that is editing as getEditingRow + * will always return -1. + */ + private int realEditingRow() { + return editingRow; + } + + /** Overridden to pass the new rowHeight to the tree. */ + public void setRowHeight(int rowHeight) { + super.setRowHeight(rowHeight); + if (tree != null && tree.getRowHeight() != rowHeight) + tree.setRowHeight( rowHeight ); + } + + private int getTreeColumnNumber() { + for (int counter = getColumnCount() - 1; counter >= 0;counter--) + if (getColumnClass(counter) == TreeTableModel.class) + return counter; + return -1; + } + + /** isCellEditable returns true for the Tree-Column, even if it is not editable. + isCellRealEditable returns true only if the underlying TreeTableModel-Cell is + editable. + */ + private boolean isCellRealEditable(int row,int column) { + TreePath treePath = tree.getPathForRow(row); + if (treePath == null) + return false; + return (((TreeTableModel)tree.getModel()).isCellEditable(treePath.getLastPathComponent() + ,column)); + + } + + class RendererTree extends JTree implements TableCellRenderer { + private static final long serialVersionUID = 1L; + + protected int rowToPaint; + Color borderColor = Color.gray; + + /** Border to draw around the tree, if this is non-null, it will + * be painted. */ + protected Border highlightBorder; + + public RendererTree() { + super(); + } + + public void setRowHeight(int rowHeight) { + if (rowHeight > 0) { + super.setRowHeight(rowHeight); + if (JTreeTable.this != null && + JTreeTable.this.getRowHeight() != rowHeight) { + JTreeTable.this.setRowHeight(getRowHeight()); + } + } + } + + // Move and resize the tree to the table position + public void setBounds( int x, int y, int w, int h ) { + super.setBounds( x, 0, w, JTreeTable.this.getHeight() ); + } + + public void paintEditorBackground(Graphics g,int row) { + tree.rowToPaint = row; + g.translate( 0, -row * getRowHeight()); + Rectangle rect = g.getClipBounds(); + if (rect.width >0 && rect.height >0) + super.paintComponent(g); + g.translate( 0, row * getRowHeight()); + } + + // start painting at the rowToPaint + public void paint( Graphics g ) { + int row = rowToPaint; + + g.translate( 0, -rowToPaint * getRowHeight() ); + super.paint(g); + + int x = 0; + TreePath path = getPathForRow(row); + Object value = path.getLastPathComponent(); + boolean isSelected = tree.isRowSelected(row); + x = tree.getRowBounds(row).x; + if (treeCellEditor != null) { + x += treeCellEditor.getGap(tree,value,isSelected,row); + } else { + TreeCellRenderer tcr = getCellRenderer(); + if (tcr instanceof DefaultTreeCellRenderer) { + DefaultTreeCellRenderer dtcr = ((DefaultTreeCellRenderer)tcr); + // super.paint must have been called before + x += dtcr.getIconTextGap() + dtcr.getIcon().getIconWidth(); + } + } + + // Draw the Table border if we have focus. + if (highlightBorder != null) { + highlightBorder.paintBorder(this, g, x, rowToPaint * + getRowHeight(), getWidth() -x, + getRowHeight() ); + } // Paint the selection rectangle + } + + public Component getTableCellRendererComponent(JTable table, + Object value, + boolean isSelected, + boolean hasFocus, + int row, int column) { + Color background; + Color foreground; + if (hasFocus) + focusedRow = row; + + if(isSelected) { + background = table.getSelectionBackground(); + foreground = table.getSelectionForeground(); + } + else { + background = table.getBackground(); + foreground = table.getForeground(); + } + highlightBorder = null; + if (realEditingRow() == row && getEditingColumn() == column) { + background = UIManager.getColor("Table.focusCellBackground"); + foreground = UIManager.getColor("Table.focusCellForeground"); + } else if (hasFocus) { + highlightBorder = UIManager.getBorder + ("Table.focusCellHighlightBorder"); + if (isCellRealEditable(row,convertColumnIndexToModel(column))) { + background = UIManager.getColor + ("Table.focusCellBackground"); + foreground = UIManager.getColor + ("Table.focusCellForeground"); + } + } + + this.rowToPaint = row; + setBackground(background); + + TreeCellRenderer tcr = getCellRenderer(); + if (tcr instanceof DefaultTreeCellRenderer) { + DefaultTreeCellRenderer dtcr = ((DefaultTreeCellRenderer)tcr); + if (isSelected) { + dtcr.setTextSelectionColor(foreground); + dtcr.setBackgroundSelectionColor(background); + } + else { + dtcr.setTextNonSelectionColor(foreground); + dtcr.setBackgroundNonSelectionColor(background); + } + } + return this; + } + } + + class DelegationgTreeCellEditor implements TableCellEditor + { + TreeTableEditor delegate; + JComponent lastComp = null; + int textOffset = 0; + + MouseListener mouseListener = new MouseAdapter() { + public void mouseClicked(MouseEvent evt) + { + if (lastComp == null) + return; + if (delegate == null) + return; + if (evt.getY() < 0 || evt.getY()>lastComp.getHeight()) + delegate.stopCellEditing(); + // User clicked left from the text + if (textOffset > 0 && evt.getX()< textOffset ) + delegate.stopCellEditing(); + } + }; + + public DelegationgTreeCellEditor(TreeTableEditor delegate) { + this.delegate = delegate; + } + + public void addCellEditorListener(CellEditorListener listener) { + delegate.addCellEditorListener(listener); + } + + public void removeCellEditorListener(CellEditorListener listener) { + delegate.removeCellEditorListener(listener); + } + + public void cancelCellEditing() { + delegate.cancelCellEditing(); + } + + public Object getCellEditorValue() { + return delegate.getCellEditorValue(); + } + + public boolean stopCellEditing() { + return delegate.stopCellEditing(); + + } + + public boolean shouldSelectCell(EventObject anEvent) { + return true; + } + + private int getTextOffset(Object value,boolean isSelected,int row) { + int gap = delegate.getGap(tree,value,isSelected,row); + TreePath path = tree.getPathForRow(row); + return tree.getUI().getPathBounds(tree,path).x + gap; + } + + public boolean inHitRegion(int x,int y) { + int row = tree.getRowForLocation(x,y); + TreePath path = tree.getPathForRow(row); + if (path == null) + return false; + int gap = (delegate != null) ? + delegate.getGap(tree,null,false,row) + :16; + + return (x - gap >= tree.getUI().getPathBounds(tree,path).x || x<0); + } + + public Component getTableCellEditorComponent(JTable table, Object value, + boolean isSelected, + int row, + int column) + { + JTreeTable.this.tree.rowToPaint = row; + JComponent comp = (JComponent) delegate.getEditorComponent(tree,value,isSelected,row); + if (lastComp != comp) { + if (comp != null) + comp.removeMouseListener(mouseListener); + comp.addMouseListener(mouseListener); + } + lastComp = comp; + + textOffset = getTextOffset(value,isSelected,row); + Border outerBorder = new TreeBorder(0, textOffset , 0, 0,row); + Border editBorder = UIManager.getBorder("Tree.editorBorder"); + Border border = new CompoundBorder(outerBorder + ,editBorder + ); + comp.setBorder(border); + return comp; + } + + public boolean isCellEditable( EventObject evt ) { + int col = getTreeColumnNumber(); + if( evt instanceof MouseEvent ) { + MouseEvent me = (MouseEvent)evt; + if (col >= 0) { + int xPosRelativeToCell = me.getX() - getCellRect(0, col, true).x; + if (me.getClickCount() > 1 + && inHitRegion(xPosRelativeToCell,me.getY()) + && isCellRealEditable(tree.getRowForLocation(me.getX(),me.getY()) + ,convertColumnIndexToModel(col))) + return true; + MouseEvent newME = new MouseEvent(tree, me.getID(), + me.getWhen(), me.getModifiers(), + xPosRelativeToCell, + me.getY(), me.getClickCount(), + me.isPopupTrigger()); + if (! inHitRegion(xPosRelativeToCell,me.getY()) || me.getClickCount() > 1) + tree.dispatchEvent(newME); + } + return false; + } + + if (delegate != null && isCellRealEditable(focusedRow,convertColumnIndexToModel(col))) + return delegate.isCellEditable(evt); + else + return false; + } + } + + + class TreeBorder implements Border { + int row; + Insets insets; + + public TreeBorder(int top,int left,int bottom,int right,int row) { + this.row = row; + insets = new Insets(top,left,bottom,right); + } + + public Insets getBorderInsets(Component c) { + return insets; + } + + public void paintBorder(Component c,Graphics g,int x,int y,int width,int height) { + Shape originalClip = g.getClip(); + g.clipRect(0,0,insets.left -1 ,tree.getHeight()); + tree.paintEditorBackground(g,row); + g.setClip(originalClip); + } + + public boolean isBorderOpaque() { + return false; + } + + } + + + + /** + * ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel + * to listen for changes in the ListSelectionModel it maintains. Once + * a change in the ListSelectionModel happens, the paths are updated + * in the DefaultTreeSelectionModel. + */ + class ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel implements ListSelectionListener{ + private static final long serialVersionUID = 1L; + + /** Set to true when we are updating the ListSelectionModel. */ + protected boolean updatingListSelectionModel; + + public ListToTreeSelectionModelWrapper() { + super(); + getListSelectionModel().addListSelectionListener + (createListSelectionListener()); + } + + /** + * Returns the list selection model. ListToTreeSelectionModelWrapper + * listens for changes to this model and updates the selected paths + * accordingly. + */ + ListSelectionModel getListSelectionModel() { + return listSelectionModel; + } + + /** + * This is overridden to set updatingListSelectionModel + * and message super. This is the only place DefaultTreeSelectionModel + * alters the ListSelectionModel. + */ + public void resetRowSelection() { + if(!updatingListSelectionModel) { + updatingListSelectionModel = true; + try { + super.resetRowSelection(); + } + finally { + updatingListSelectionModel = false; + } + } + // Notice how we don't message super if + // updatingListSelectionModel is true. If + // updatingListSelectionModel is true, it implies the + // ListSelectionModel has already been updated and the + // paths are the only thing that needs to be updated. + } + + /** + * Creates and returns an instance of ListSelectionHandler. + */ + protected ListSelectionListener createListSelectionListener() { + return this; + } + + /** + * If updatingListSelectionModel is false, this will + * reset the selected paths from the selected rows in the list + * selection model. + */ + protected void updateSelectedPathsFromSelectedRows() { + if(!updatingListSelectionModel) { + updatingListSelectionModel = true; + try { + // This is way expensive, ListSelectionModel needs an + // enumerator for iterating. + int min = listSelectionModel.getMinSelectionIndex(); + int max = listSelectionModel.getMaxSelectionIndex(); + + clearSelection(); + if(min != -1 && max != -1) { + for(int counter = min; counter <= max; counter++) { + if(listSelectionModel.isSelectedIndex(counter)) { + TreePath selPath = tree.getPathForRow + (counter); + + if(selPath != null) { + addSelectionPath(selPath); + } + } + } + } + } finally { + updatingListSelectionModel = false; + } + } + } + /** Implemention of ListSelectionListener Interface: + * Class responsible for calling updateSelectedPathsFromSelectedRows + * when the selection of the list changse. + */ + public void valueChanged(ListSelectionEvent e) { + updateSelectedPathsFromSelectedRows(); + } + } + + class TreeTableModelAdapter extends AbstractTableModel implements TreeExpansionListener,TreeModelListener + { + private static final long serialVersionUID = 1L; + + TreeTableModel treeTableModel; + public TreeTableModelAdapter(TreeTableModel treeTableModel) { + this.treeTableModel = treeTableModel; + + tree.addTreeExpansionListener(this); + // Install a TreeModelListener that can update the table when + // tree changes. We use delayedFireTableDataChanged as we can + // not be guaranteed the tree will have finished processing + // the event before us. + treeTableModel.addTreeModelListener(this); + } + + + // Implementation of TreeExpansionListener + public void treeExpanded(TreeExpansionEvent event) { + int row = tree.getRowForPath(event.getPath()); + if (row + 1 < tree.getRowCount()) + fireTableRowsInserted(row + 1,row + 1); + } + public void treeCollapsed(TreeExpansionEvent event) { + int row = tree.getRowForPath(event.getPath()); + if (row < getRowCount()) + fireTableRowsDeleted(row + 1,row + 1); + } + + // Implementation of TreeModelLstener + public void treeNodesChanged(TreeModelEvent e) { + int firstRow = 0; + int lastRow = tree.getRowCount() -1; + delayedFireTableRowsUpdated(firstRow,lastRow); + } + + public void treeNodesInserted(TreeModelEvent e) { + delayedFireTableDataChanged(); + } + + public void treeNodesRemoved(TreeModelEvent e) { + delayedFireTableDataChanged(); + } + + public void treeStructureChanged(TreeModelEvent e) { + delayedFireTableDataChanged(); + } + + // Wrappers, implementing TableModel interface. + public int getColumnCount() { + return treeTableModel.getColumnCount(); + } + + public String getColumnName(int column) { + return treeTableModel.getColumnName(column); + } + + public Class getColumnClass(int column) { + return treeTableModel.getColumnClass(column); + } + + public int getRowCount() { + return tree.getRowCount(); + } + + private Object nodeForRow(int row) { + TreePath treePath = tree.getPathForRow(row); + if (treePath == null) + return null; + return treePath.getLastPathComponent(); + } + + public Object getValueAt(int row, int column) { + Object node = nodeForRow(row); + if (node == null) + return null; + return treeTableModel.getValueAt(node, column); + } + + public boolean isCellEditable(int row, int column) { + if (getColumnClass(column) == TreeTableModel.class) { + return true; + } else { + Object node = nodeForRow(row); + if (node == null) + return false; + return treeTableModel.isCellEditable(node, column); + } + } + + public void setValueAt(Object value, int row, int column) { + Object node = nodeForRow(row); + if (node == null) + return; + treeTableModel.setValueAt(value, node, column); + } + + /** + * Invokes fireTableDataChanged after all the pending events have been + * processed. SwingUtilities.invokeLater is used to handle this. + */ + protected void delayedFireTableRowsUpdated(int firstRow,int lastRow) { + SwingUtilities.invokeLater(new UpdateRunnable(firstRow,lastRow)); + } + + class UpdateRunnable implements Runnable { + int lastRow; + int firstRow; + UpdateRunnable(int firstRow,int lastRow) { + this.firstRow = firstRow; + this.lastRow = lastRow; + } + public void run() { + fireTableRowsUpdated(firstRow,lastRow); + } + } + /** + * Invokes fireTableDataChanged after all the pending events have been + * processed. SwingUtilities.invokeLater is used to handle this. + */ + protected void delayedFireTableDataChanged() { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + fireTableDataChanged(); + } + }); + + } + } + + + /* + public void paintComponent(Graphics g) { + super.paintComponent( g ); + Rectangle r = g.getClipBounds(); + g.setColor( Color.white); + g.fillRect(0,0, r.width, r.height ); + + } + */ + +} + + diff --git a/Rapla/src/org/rapla/components/treetable/TableToolTipRenderer.java b/Rapla/src/org/rapla/components/treetable/TableToolTipRenderer.java new file mode 100644 index 0000000..7f75b1c --- /dev/null +++ b/Rapla/src/org/rapla/components/treetable/TableToolTipRenderer.java @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.treetable; +import javax.swing.JTable; +public interface TableToolTipRenderer { + public String getToolTipText(JTable table, int row, int column); +} diff --git a/Rapla/src/org/rapla/components/treetable/TreeTableEditor.java b/Rapla/src/org/rapla/components/treetable/TreeTableEditor.java new file mode 100644 index 0000000..dcd2718 --- /dev/null +++ b/Rapla/src/org/rapla/components/treetable/TreeTableEditor.java @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.treetable; + +import javax.swing.JComponent; +import javax.swing.JTree; +import javax.swing.CellEditor; + +public interface TreeTableEditor extends CellEditor { + JComponent getEditorComponent(JTree tree,Object value,boolean isSelected,int row); + int getGap(JTree tree, Object value, boolean isSelected, int row); +} + diff --git a/Rapla/src/org/rapla/components/treetable/TreeTableModel.java b/Rapla/src/org/rapla/components/treetable/TreeTableModel.java new file mode 100644 index 0000000..47b9bba --- /dev/null +++ b/Rapla/src/org/rapla/components/treetable/TreeTableModel.java @@ -0,0 +1,73 @@ +/* + * Sun Microsystems grants you ("Licensee") a non-exclusive, royalty + * free, license to use, modify and redistribute this software in + * source and binary code form, provided that i) this copyright notice + * and license appear on all copies of the software; and ii) Licensee + * does not utilize the software in a manner which is disparaging to + * Sun Microsystems. + * + * The software media is distributed on an "As Is" basis, without + * warranty. Neither the authors, the software developers nor Sun + * Microsystems make any representation, or warranty, either express + * or implied, with respect to the software programs, their quality, + * accuracy, or fitness for a specific purpose. Therefore, neither the + * authors, the software developers nor Sun Microsystems shall have + * any liability to you or any other person or entity with respect to + * any liability, loss, or damage caused or alleged to have been + * caused directly or indirectly by programs contained on the + * media. This includes, but is not limited to, interruption of + * service, loss of data, loss of classroom time, loss of consulting + * or anticipatory *profits, or consequential damages from the use of + * these programs. +*/ +package org.rapla.components.treetable; + +import javax.swing.tree.TreeModel; + +/** + * TreeTableModel is the model used by a JTreeTable. It extends TreeModel + * to add methods for getting inforamtion about the set of columns each + * node in the TreeTableModel may have. Each column, like a column in + * a TableModel, has a name and a type associated with it. Each node in + * the TreeTableModel can return a value for each of the columns and + * set that value if isCellEditable() returns true. + * + * @author Philip Milne + * @author Scott Violet + */ +public interface TreeTableModel extends TreeModel +{ + /** + * Returns the number ofs available columns. + */ + public int getColumnCount(); + + /** + * Returns the name for column number column. + */ + public String getColumnName(int column); + + /** + * Returns the type for column number column. + * Should return TreeTableModel.class for the tree-column. + */ + public Class getColumnClass(int column); + + /** + * Returns the value to be displayed for node node, + * at column number column. + */ + public Object getValueAt(Object node, int column); + + /** + * Indicates whether the the value for node node, + * at column number column is editable. + */ + public boolean isCellEditable(Object node, int column); + + /** + * Sets the value for node node, + * at column number column. + */ + public void setValueAt(Object aValue, Object node, int column); +} diff --git a/Rapla/src/org/rapla/components/treetable/package.html b/Rapla/src/org/rapla/components/treetable/package.html new file mode 100644 index 0000000..58a7cba --- /dev/null +++ b/Rapla/src/org/rapla/components/treetable/package.html @@ -0,0 +1,6 @@ + +Contains all classes for the treetable widget. + + + + diff --git a/Rapla/src/org/rapla/components/util/Assert.java b/Rapla/src/org/rapla/components/util/Assert.java new file mode 100644 index 0000000..32fa485 --- /dev/null +++ b/Rapla/src/org/rapla/components/util/Assert.java @@ -0,0 +1,76 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.util; + +/** Some of the assert functionality of 1.4 for 1.3 versions of Rapla*/ +public class Assert { + static String NOT_NULL_ASSERTION = "notNull-Assertion"; + static String IS_TRUE_ASSERTION = "isTrue-Assertion"; + static String ASSERTION_FAIL = "Assertion fail"; + static boolean _bActivate = true; + + public static void notNull(Object obj,String text) { + if ( obj == null && isActivated()) { + doAssert(getText(NOT_NULL_ASSERTION,text)); + } + } + + public static void notNull(Object obj) { + if ( obj == null && isActivated()) { + doAssert(getText(NOT_NULL_ASSERTION,"")); + } + } + + public static void isTrue(boolean condition,String text) { + if ( !condition && isActivated()) { + doAssert(getText(IS_TRUE_ASSERTION,text)); + } // end of if () + } + + public static void isTrue(boolean condition) { + if ( !condition && isActivated()) { + doAssert(getText(IS_TRUE_ASSERTION,"")); + } // end of if () + } + + public static void fail() throws AssertionError { + doAssert(getText(ASSERTION_FAIL,"")); + } + + public static void fail(String text) throws AssertionError { + doAssert(getText(ASSERTION_FAIL,text)); + } + + private static void doAssert(String text) throws AssertionError { + System.err.println(text); + throw new AssertionError(text); + } + + static boolean isActivated() { + return _bActivate; + } + + static void setActivated(boolean bActivate) { + _bActivate = bActivate; + } + + static String getText(String type, String text) { + return ( type + " failed '" + text + "'"); + } +} + + + + + + diff --git a/Rapla/src/org/rapla/components/util/AssertionError.java b/Rapla/src/org/rapla/components/util/AssertionError.java new file mode 100644 index 0000000..b082c99 --- /dev/null +++ b/Rapla/src/org/rapla/components/util/AssertionError.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.util; + +public class AssertionError extends RuntimeException { + private static final long serialVersionUID = 1L; + + String text = ""; + + public AssertionError() { + } + public AssertionError(String text) { + this.text = text; + } + + public String toString() { + return text; + } +} + + + + diff --git a/Rapla/src/org/rapla/components/util/Command.java b/Rapla/src/org/rapla/components/util/Command.java new file mode 100644 index 0000000..4227ace --- /dev/null +++ b/Rapla/src/org/rapla/components/util/Command.java @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------* +| Copyright (C) 2006 Christopher Kohlhaas | +| | +| This program is free software; you can redistribute it and/or modify | +| it under the terms of the GNU General Public License as published by the | +| Free Software Foundation. A copy of the license has been included with | +| these distribution in the COPYING file, if not go to www.fsf.org | +| | +| As a special exception, you are granted the permissions to link this | +| program with every library, which license fulfills the Open Source | +| Definition as published by the Open Source Initiative (OSI). | +*--------------------------------------------------------------------------*/ + +package org.rapla.components.util; + +public interface Command { + public void execute() throws Exception; +} + + diff --git a/Rapla/src/org/rapla/components/util/CommandQueue.java b/Rapla/src/org/rapla/components/util/CommandQueue.java new file mode 100644 index 0000000..2d7ce2d --- /dev/null +++ b/Rapla/src/org/rapla/components/util/CommandQueue.java @@ -0,0 +1,83 @@ +/*--------------------------------------------------------------------------* +| Copyright (C) 2006 Christopher Kohlhaas | +| | +| This program is free software; you can redistribute it and/or modify | +| it under the terms of the GNU General Public License as published by the | +| Free Software Foundation. A copy of the license has been included with | +| these distribution in the COPYING file, if not go to www.fsf.org | +| | +| As a special exception, you are granted the permissions to link this | +| program with every library, which license fulfills the Open Source | +| Definition as published by the Open Source Initiative (OSI). | +*--------------------------------------------------------------------------*/ + +package org.rapla.components.util; + +import java.util.Vector; + +/** Creates a new thread that successively executes the queued command objects + * @see Command + */ +public class CommandQueue { + private Vector v = new Vector(); + public synchronized void enqueue(Command object) { + v.addElement( object ); + } + + public synchronized Command dequeue() { + if ( v.size() == 0) + return null; + Object firstElement =v.firstElement(); + if ( firstElement != null) { + v.removeElementAt( 0 ); + } + return (Command) firstElement; + } + + public void dequeueAll() { + while ( dequeue() != null); + } + + /** Creates a new Queue for Command Object. + The commands will be executed in succession in a seperate Daemonthread. + @see Command + */ + public static CommandQueue createCommandQueue() { + CommandQueue commandQueue = new CommandQueue(); + Thread eventThread= new MyEventThread(commandQueue); + eventThread.setDaemon(true); + eventThread.start(); + return commandQueue; + } + + + static class MyEventThread extends Thread { + CommandQueue commandQueue; + MyEventThread(CommandQueue commandQueue) { + this.commandQueue = commandQueue; + } + public void run() { + try { + while (true) { + Command command = commandQueue.dequeue(); + if (command == null) { + sleep(100); + continue; + } + try { + command.execute(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + } catch (InterruptedException ex) { + } + } + } + + + + +} + + diff --git a/Rapla/src/org/rapla/components/util/DEPENDENCIES b/Rapla/src/org/rapla/components/util/DEPENDENCIES new file mode 100644 index 0000000..be45b1b --- /dev/null +++ b/Rapla/src/org/rapla/components/util/DEPENDENCIES @@ -0,0 +1,7 @@ +All classes except the XMLUtil depend on the java.* packages. + +The classes in the xml package depend on: + +java.* +javax.xml.* +org.xml.sax.* diff --git a/Rapla/src/org/rapla/components/util/DateTools.java b/Rapla/src/org/rapla/components/util/DateTools.java new file mode 100644 index 0000000..b0b88ff --- /dev/null +++ b/Rapla/src/org/rapla/components/util/DateTools.java @@ -0,0 +1,158 @@ + /*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.util; + +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + +/** Tools for manipulating dates. + * At the moment of writing rapla internaly stores all appointments + * in the GMT timezone. + */ +public abstract class DateTools +{ + public static final int DAYS_PER_WEEK= 7; + public static final long MILLISECONDS_PER_MINUTE = 1000 * 60; + public static final long MILLISECONDS_PER_HOUR = MILLISECONDS_PER_MINUTE * 60; + public static final long MILLISECONDS_PER_DAY = 24 * MILLISECONDS_PER_HOUR; + public static final long MILLISECONDS_PER_WEEK = 7 * MILLISECONDS_PER_DAY; + public static TimeZone GMT = TimeZone.getTimeZone("GMT+0"); + + public static int getHourOfDay(long date) { + return (int) ((date % MILLISECONDS_PER_DAY)/ MILLISECONDS_PER_HOUR); + } + + public static int getMinuteOfHour(long date) { + return (int) ((date % MILLISECONDS_PER_HOUR)/ MILLISECONDS_PER_MINUTE); + } + + /** sets time of day to 0:00. + @see #cutDate(Date) + */ + public static long cutDate(long date) { + return (date - (date % MILLISECONDS_PER_DAY)); + } + + public static boolean isMidnight(long date) { + return cutDate( date ) == date ; + } + + public static boolean isMidnight(Date date) { + return isMidnight( date.getTime()); + } + + /** sets time of day to 0:00. + @see #cutDate(Date) + */ + public static void cutDate( Calendar calendar ) { + calendar.set( Calendar.HOUR_OF_DAY, 0 ); + calendar.set( Calendar.MINUTE, 0 ); + calendar.set( Calendar.SECOND, 0 ); + calendar.set( Calendar.MILLISECOND, 0 ); + } + + + /** sets time of day to 0:00. */ + public static Date cutDate(Date date) { + return new Date(cutDate(date.getTime())); + } + + static TimeZone timeZone =TimeZone.getTimeZone("GMT"); + /** same as TimeZone.getTimeZone("GMT"). */ + public static TimeZone getTimeZone() { + return timeZone; + } + /** sets time of day to 0:00 and increases day. + @see #fillDate(Date) + */ + public static long fillDate(long date) { + // cut date + long cuttedDate = (date - (date % MILLISECONDS_PER_DAY)); + return cuttedDate + MILLISECONDS_PER_DAY; + } + + public static Date fillDate(Date date) { + return new Date(fillDate(date.getTime())); + } + + /** Monday 24:00 = tuesday 0:00. + But the first means end of monday and the second start of tuesday. + The default DateFormat always displays tuesday. + If you want to edit the first interpretation in calendar components. + call addDay() to add 1 day to the given date before displaying + and subDay() for mapping a day back after editing. + @see #subDay + @see #addDays + */ + public static Date addDay(Date date) { + return new Date(date.getTime() + MILLISECONDS_PER_DAY); + } + + /** see #addDay*/ + public static Date addDays(Date date,int days) { + return new Date(date.getTime() + MILLISECONDS_PER_DAY * days); + } + + /** + @see #addDay + @see #subDays + */ + public static Date subDay(Date date) { + return new Date(date.getTime() - MILLISECONDS_PER_DAY); + } + + /** + @see #addDay + */ + public static Date subDays(Date date,int days) { + return new Date(date.getTime() - MILLISECONDS_PER_DAY * days); + } + /** returns if the two dates are one the same date. + * Dates must be in GMT */ + static public boolean isSameDay( long d1, long d2) { + return cutDate( d1 ) == cutDate ( d2 ); + } + + /** uses the calendar-object for date comparison. + * Use this for non GMT Dates*/ + static public boolean isSameDay( Calendar calendar, Date d1, Date d2 ) { + calendar.setTime( d1 ); + int era1 = calendar.get( Calendar.ERA ); + int year1 = calendar.get( Calendar.YEAR ); + int day_of_year1 = calendar.get( Calendar.DAY_OF_YEAR ); + calendar.setTime( d2 ); + int era2 = calendar.get( Calendar.ERA ); + int year2 = calendar.get( Calendar.YEAR ); + int day_of_year2 = calendar.get( Calendar.DAY_OF_YEAR ); + return ( era1 == era2 && year1 == year2 && day_of_year1 == day_of_year2 ); + } + + static public long countDays(Date start,Date end) { + return (cutDate(end.getTime()) - cutDate(start.getTime())) / MILLISECONDS_PER_DAY; + } + + static public Calendar createGMTCalendar() + { + return Calendar.getInstance( GMT); + } + + +} + + + + + + + diff --git a/Rapla/src/org/rapla/components/util/IOUtil.java b/Rapla/src/org/rapla/components/util/IOUtil.java new file mode 100644 index 0000000..2af3808 --- /dev/null +++ b/Rapla/src/org/rapla/components/util/IOUtil.java @@ -0,0 +1,277 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.util; + +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.StringTokenizer; + +import org.apache.avalon.framework.CascadingException; + +/** Some IOHelper methods. */ +abstract public class IOUtil { + + /** returns the path of the url without the last path component */ + public static URL getBase(URL url) { + try { + String file = url.getPath(); + String separator = "/"; + if (url.getProtocol().equals("file") && file.indexOf(File.separator)>0) { + separator = File.separator; + } + int index = file.lastIndexOf(separator); + String dir = (index<0) ? file: file.substring(0,index + 1); + return new URL(url.getProtocol() + ,url.getHost() + ,url.getPort() + ,dir); + } catch ( MalformedURLException e) { + // This should not happen + e.printStackTrace(); + throw new RuntimeException("Unknown error while getting the base of the url!"); + } // end of try-catch + } + + /** reads the content form an url into a ByteArray*/ + public static byte[] readBytes(URL url) throws IOException { + InputStream in = null; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + in = url.openStream(); + byte[] buffer = new byte[1024]; + int count = 0; + do { + out.write(buffer, 0, count); + count = in.read(buffer, 0, buffer.length); + } while (count != -1); + return out.toByteArray(); + } finally { + if ( in != null) { + in.close(); + } // end of if () + } + } + + + + /** same as {@link URLDecoder#decode}. + * But calls the deprecated method under 1.3. + */ + public static String decode(String s,String enc) throws UnsupportedEncodingException { + return callEncodeDecode(URLDecoder.class,"decode",s,enc); + } + + /** same as {@link URLEncoder#encode}. + * But calls the deprecated method under 1.3. + */ + public static String encode(String s,String enc) throws UnsupportedEncodingException { + return callEncodeDecode(URLEncoder.class,"encode",s,enc); + } + + private static String callEncodeDecode(Class clazz,String methodName,String s,String enc) throws UnsupportedEncodingException { + Assert.notNull(s); + Assert.notNull(enc); + try { + Method method = + clazz.getMethod(methodName + ,new Class[] { + String.class + ,String.class + } + ); + return (String) method.invoke(null,new Object[] {s,enc}); + } catch (NoSuchMethodException ex) { + try { + Method method = + URLDecoder.class.getMethod(methodName + ,new Class[] {String.class} + ); + return (String) method.invoke(null,new Object[] {s}); + } catch (Exception ex2) { + ex2.printStackTrace(); + throw new IllegalStateException("Should not happen" + ex2.getMessage()); + } + } catch (InvocationTargetException ex) { + throw (UnsupportedEncodingException) ex.getTargetException(); + } catch (IllegalAccessException ex) { + ex.printStackTrace(); + throw new IllegalStateException("Should not happen" + ex.getMessage()); + } + } + + + /** returns a BufferedInputStream from the url. + If the url-protocol is "file" no url connection will + be opened. + */ + public static InputStream getInputStream(URL url) throws IOException { + if (url.getProtocol().equals("file")) { + String path = decode(url.getPath(),"UTF-8"); + return new BufferedInputStream(new FileInputStream(path)); + } else { + return new BufferedInputStream(url.openStream()); + } // end of else + } + + public static File getFileFrom(URL url) throws IOException { + String path = decode(url.getPath(),"UTF-8"); + return new File( path ); + } + + + /** copies a file. + * @param srcPath the source-path. Thats the path of the file that should be copied. + * @param destPath the destination-path + */ + + public static void copy( String srcPath, String destPath) throws IOException{ + copy( srcPath, destPath, false); + } + + /** copies a file. + * @param srcPath the source-path. Thats the path of the file that should be copied. + * @param destPath the destination-path + */ + public static void copy( String srcPath, String destPath,boolean onlyOverwriteIfNewer ) throws IOException{ + copy ( new File( srcPath ) , new File( destPath ), onlyOverwriteIfNewer ); + } + + /** copies a file. + */ + public static void copy(File srcFile, File destFile, boolean onlyOverwriteIfNewer) throws IOException { + if ( ! srcFile.exists() ) { + throw new IOException( srcFile.getPath() + " doesn't exist!!"); + } + if ( destFile.exists() && destFile.lastModified() >= srcFile.lastModified() && onlyOverwriteIfNewer) + { + return; + } + FileInputStream in = null; + FileOutputStream out = null; + try { + in = new FileInputStream( srcFile ); + out = new FileOutputStream( destFile); + copyStreams ( in, out ); + } finally { + if ( in != null ) + in.close(); + + if ( out != null ) + out.close(); + } + } + + /** copies the contents of the input stream to the output stream. + * @param in + * @param out + * @throws IOException + */ + public static void copyStreams( InputStream in, OutputStream out ) throws IOException { + byte[] buf = new byte[ 32000 ]; + int n = 0; + while ( n != -1 ) { + out.write( buf, 0, n ); + n = in.read(buf, 0, buf.length ); + } + } + + /** returns the relative path of file to base. + * @throws IOException if position of file is not relative to base + */ + public static String getRelativePath(File base,File file) throws IOException { + String filePath = file.getAbsoluteFile().getCanonicalPath(); + String basePath = base.getAbsoluteFile().getCanonicalPath(); + int start = filePath.indexOf(basePath); + if (start != 0) + throw new IOException(basePath + " not ancestor of " + filePath); + return filePath.substring(basePath.length()); + } + + /** returns the relative path of file to base. + * same as {@link #getRelativePath(File, File)} but replaces windows-plattform-specific + * file separator \ with / + * @throws IOException if position of file is not relative to base + */ + public static String getRelativeURL(File base,File file) throws IOException { + StringBuffer result= new StringBuffer(getRelativePath(base,file)); + for (int i=0;i" + ex.getMessage() +"
    "); + ex.printStackTrace(writer); + while (ex instanceof CascadingException) { + ex = ((CascadingException) ex).getCause(); + if (ex != null) { + writer.println("

    Caused by: "+ ex.getMessage() + "


    "); + ex.printStackTrace(writer); + } else { + break; + } + } + return bytes.toString(); + } + + +} + + + + + + + + + + + diff --git a/Rapla/src/org/rapla/components/util/InverseComparator.java b/Rapla/src/org/rapla/components/util/InverseComparator.java new file mode 100644 index 0000000..4d6c612 --- /dev/null +++ b/Rapla/src/org/rapla/components/util/InverseComparator.java @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------* +| Copyright (C) 2006 Christopher Kohlhaas | +| | +| This program is free software; you can redistribute it and/or modify | +| it under the terms of the GNU General Public License as published by the | +| Free Software Foundation. A copy of the license has been included with | +| these distribution in the COPYING file, if not go to www.fsf.org | +| | +| As a special exception, you are granted the permissions to link this | +| program with every library, which license fulfills the Open Source | +| Definition as published by the Open Source Initiative (OSI). | +*--------------------------------------------------------------------------*/ + +package org.rapla.components.util; + +import java.util.Comparator; + +/** + * + * Reverts the Original Comparator + * -1 -> 1 + * 1 -> -1 + * 0 -> 0 + */ +public class InverseComparator implements Comparator { + Comparator original; + public InverseComparator( Comparator original) { + this.original = original; + } + public int compare( Object arg0, Object arg1 ) + { + return -1 * original.compare( arg0, arg1); + } +} + + diff --git a/Rapla/src/org/rapla/components/util/JNLPUtil.java b/Rapla/src/org/rapla/components/util/JNLPUtil.java new file mode 100644 index 0000000..933cc71 --- /dev/null +++ b/Rapla/src/org/rapla/components/util/JNLPUtil.java @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.components.util; + +import java.net.URL; +import java.lang.reflect.Method; +/** returns the codebase in an webstart application */ +abstract public class JNLPUtil { + final static String basicService = "javax.jnlp.BasicService"; + final public static URL getCodeBase() throws Exception { + try { + Class serviceManagerC = Class.forName("javax.jnlp.ServiceManager"); + Class basicServiceC = Class.forName( basicService ); + //Class unavailableServiceException = Class.forName("javax.jnlp.UnavailableServiceException"); + + Method lookup = serviceManagerC.getMethod("lookup", new Class[] {String.class}); + Method getCodeBase = basicServiceC.getMethod("getCodeBase", new Class[] {}); + Object service = lookup.invoke( null, new Object[] { basicService }); + return (URL) getCodeBase.invoke( service, new Object[] {}); + } catch (ClassNotFoundException ex ) { + throw new Exception( "Webstart not available :" + ex.getMessage()); + } + } +} diff --git a/Rapla/src/org/rapla/components/util/Mutex.java b/Rapla/src/org/rapla/components/util/Mutex.java new file mode 100644 index 0000000..d8f035c --- /dev/null +++ b/Rapla/src/org/rapla/components/util/Mutex.java @@ -0,0 +1,90 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.util; + + +/** Mutex-lock for synchronization in multithreaded enviroments.*/ +public class Mutex { + boolean locked = false; + boolean debugging = false; + String name; + + public Mutex() { + this.name = ""; + } + + public Mutex(String name) { + this.name = name; + } + + /** tries to aquire a lock. If the lock is hold by another thread, the current thread will + * wait until the lock is released. + * */ + final public synchronized void aquire() throws InterruptedException { + aquire(0); + } + + /** Releases a lock. All threads that are waiting for the lock gets notified, but only one can + * continue executing the aquire method (because its synchronized) and get the new lock.*/ + final public synchronized void release() { + log("Lock released"); + + locked = false; + notifyAll(); + } + + + /** Tries to aquire a lock for the specified time and returns false, if the lock can't be aquired in that time.*/ + final public synchronized boolean aquire(int time) throws InterruptedException { + log("Try to aquire lock"); + + if ( time <= 0 ) { + while (locked) { + wait(); + } + } else { + long startTime = System.currentTimeMillis(); + long currentTime = startTime; + + //The synchronization monitor will be released during wait + while (locked) { + long diff = (currentTime - startTime); + if ( diff >= time) { + log("Timeout when waiting for the log"); + return false; + } + //Many Threads can be in the aquire method (but they will loose the monitor at this point) + wait( time - diff); + currentTime = System.currentTimeMillis(); + } + } + // Only one thread can get the monitor in the aquire method, + //and continue after wait. So the next state changing call is thread safe. + locked = true; + log("Lock aquired"); + return true; + } + + protected String getLoggingPrefix() { + return "Mutex " + name + " " + Thread.currentThread().toString() + " "; + } + + protected void log(String message) { + if ( debugging ) + System.out.println( getLoggingPrefix() + message); + } +} + + + + diff --git a/Rapla/src/org/rapla/components/util/OrderedMap.java b/Rapla/src/org/rapla/components/util/OrderedMap.java new file mode 100644 index 0000000..834353d --- /dev/null +++ b/Rapla/src/org/rapla/components/util/OrderedMap.java @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.util; + +import java.util.*; +/**

    An implemention of a map that keeps the values (not the keys) sorted.

    */ +public class OrderedMap extends HashMap { + private static final long serialVersionUID = 1L; + + SortedSet sorted; + + public OrderedMap() { + super(); + sorted = new TreeSet(); + } + + public OrderedMap(Comparator comparator) { + super(); + sorted = new TreeSet(comparator); + } + + public Object put(Object key,Object value) { + Object result = super.put(key,value); + if (result != null) { + //#TODO This is not very performant + rebuildTree(); + //sorted.add(value); + } else { + sorted.add(value); + } + return result; + } + + private void rebuildTree() { + sorted.clear(); + sorted.addAll(super.values()); + } + + public void putAll(Map all) { + super.putAll(all); + rebuildTree(); + } + + public Object remove(Object key) { + Object result = super.remove(key); + if (result != null) + if (!sorted.remove(result)) + rebuildTree(); + return result; + } + + public void clear() { + super.clear(); + sorted.clear(); + } + + public Collection values() { + return sorted; + } +} + + + + diff --git a/Rapla/src/org/rapla/components/util/SerializableDateTimeFormat.java b/Rapla/src/org/rapla/components/util/SerializableDateTimeFormat.java new file mode 100644 index 0000000..8288f70 --- /dev/null +++ b/Rapla/src/org/rapla/components/util/SerializableDateTimeFormat.java @@ -0,0 +1,147 @@ +package org.rapla.components.util; + +import java.text.ParseException; +import java.util.Calendar; +import java.util.Date; + +import org.rapla.components.util.iterator.IntIterator; + + +/** +Provides methods for parsing and formating dates +and times in the following format:
    +2002-25-05 for dates and 13:00:00 for times. +This is according to the xschema specification for dates and time. +WARNING: Do not share instances of this class between threads. Its NOT thread safe +*/ +public class SerializableDateTimeFormat +{ + Calendar calendar; + + public SerializableDateTimeFormat() { + this( Calendar.getInstance( DateTools.getTimeZone())); + } + + public SerializableDateTimeFormat(Calendar calendar) { + this.calendar = calendar; + } + + private Date parseDate( String date, String time, boolean fillDate ) throws ParseException { + if( date == null || date.length()==0 ) + throwParseDateException("empty", 0); + IntIterator it = new IntIterator(); + it.init( date, '-' ); + if ( !it.hasNext() ) + throwParseDateException( date, it.getPos() ); + calendar.set( Calendar.YEAR, it.next() ); + if ( !it.hasNext() ) + throwParseDateException( date, it.getPos() ); + calendar.set( Calendar.MONTH, it.next() -1 ); + if ( !it.hasNext() ) + throwParseDateException( date, it.getPos() ); + calendar.set( Calendar.DATE, it.next() ); + if (fillDate ) + calendar.add( Calendar.DATE, 1 ); + + if ( time != null ) { + it.init( time, ':' ); + if ( !it.hasNext() ) + throwParseTimeException( time, it.getPos() ); + calendar.set( Calendar.HOUR_OF_DAY, it.next() ); + if ( !it.hasNext() ) + throwParseTimeException( time, it.getPos() ); + calendar.set( Calendar.MINUTE, it.next() ); + if ( !it.hasNext() ) + throwParseTimeException( time, it.getPos() ); + calendar.set( Calendar.SECOND, it.next() ); + calendar.set( Calendar.MILLISECOND, 0 ); + } else { + DateTools.cutDate( calendar ); + } + // logger.log( "parsed to " + calendar.getTime() ); + return calendar.getTime(); + } + + private void throwParseDateException( String date, int pos ) throws ParseException { + throw new ParseException( "No valid date format: " + date, pos ); + } + + private void throwParseTimeException( String time, int pos ) throws ParseException { + throw new ParseException( "No valid time format: " + time, pos ); + } + + /** The date-string must be in the following format 2001-10-21. + The format of the time-string is 18:00:00. + @return The parsed date + @throws ParseException when the date cannot be parsed. + */ + public Date parseDateTime( String date, String time) throws ParseException { + return parseDate( date, time, false); + } + + /** The date-string must be in the following format 2001-10-21. + * @param fillDate if this flag is set the time will be 24:00 instead of 0:00 + When this flag is set the time parameter should be null + @return The parsed date + @throws ParseException when the date cannot be parsed. + */ + public Date parseDate( String date, boolean fillDate ) throws ParseException { + return parseDate( date, null, fillDate); + } + + + /** returns the time object in the following format: 13:00:00.
    */ + public String formatTime( Date date ) { + StringBuffer buf = new StringBuffer(); + if ( date != null) + { + calendar.setTime( date ); + } + append( buf, calendar.get(Calendar.HOUR_OF_DAY), 2 ); + buf.append( ':' ); + append( buf, calendar.get(Calendar.MINUTE), 2 ); + buf.append( ':' ); + append( buf, calendar.get(Calendar.SECOND), 2 ); + return buf.toString(); + } + + /** returns the date object in the following format: 2001-10-21.
    + @param adaptDay if the flag is set 2001-10-21 will be stored as 2001-10-20. + This is usefull for end-dates: 2001-10-21 00:00 is then interpreted as + 2001-10-20 24:00. + */ + public String formatDate( Date date, boolean adaptDay ) { + StringBuffer buf = new StringBuffer(); + + if ( adaptDay ) + calendar.setTime( new Date( date.getTime() - DateTools.MILLISECONDS_PER_DAY)); + else + calendar.setTime( date ); + append( buf, calendar.get(Calendar.YEAR), 4 ); + buf.append( '-' ); + append( buf, calendar.get(Calendar.MONTH)+1, 2 ); + buf.append( '-' ); + append( buf, calendar.get(Calendar.DATE), 2 ); + return buf.toString(); + + } + + /** same as formatDate(date, false). + @see #formatDate(Date,boolean) + */ + public String formatDate( Date date ) { + return formatDate( date, false ); + } + + private void append( StringBuffer buf, int number, int minLength ) { + int limit = 1; + for ( int i=0;iSame as map but for small positive int-values.

    + +

    This class is useful if you want to model a 1:1 relation between +an int value and an object.

    + +

    This map is only efficient for small ints because the hashMap size +is always larger or equal to the largest key. It is optimized for get.

    +@see #get +*/ +public class SmallIntMap implements java.io.Serializable { + // Don't forget to increase the serialVersionUID when you change the fields + private static final long serialVersionUID = 1; + + Object[] positiveValues; + Object[] negativeValues; + Set keys = new TreeSet(); + public SmallIntMap() { + this(10); + } + + public SmallIntMap(int initSize) { + positiveValues = new Object[initSize]; + } + + public SmallIntMap(int[] keys,Object[] values) { + Assert.isTrue(keys.length == values.length,"number of keys is different from number of values"); + int maxSize = 0; + for (int i=0;imaxSize) + maxSize = keys[i]; + this.positiveValues = new Object[maxSize + 1]; + for (int i=0;i= 0) { + if (key < positiveValues.length) { + return positiveValues[key]; + } + } else { + if ( negativeValues != null && -key < negativeValues.length) { + return negativeValues[-key]; + } + } + return null; + } + + public Object put(int key,Object value) { + Object keyObj = new Integer(key); + if (!keys.contains(keyObj)) + keys.add(keyObj); + + Object oldValue; + if ( key >= 0) { + if (key>= positiveValues.length) { + Object[] newValues = new Object[key * 2]; + System.arraycopy(positiveValues,0,newValues,0,positiveValues.length); + positiveValues = newValues; + } + oldValue = positiveValues[key]; + positiveValues[key] = value; + } else { + if ( negativeValues == null) { + negativeValues = new Object[10]; + } + if (-key>= negativeValues.length) { + Object[] newValues = new Object[(-key) * 2]; + System.arraycopy(negativeValues,0,newValues,0,negativeValues.length); + negativeValues = newValues; + } + oldValue = negativeValues[- key]; + negativeValues[ -key] = value; + } + + return oldValue; + } + + public void clear() { + positiveValues = new Object[10]; + negativeValues = null; + keys.clear(); + } + + public Object remove(int key) { + keys.remove(new Integer(key)); + Object oldValue = null; + if ( key >= 0) { + if (key < positiveValues.length) { + oldValue = positiveValues[key]; + positiveValues[key] = null; + } + } else { + if ( negativeValues != null && -key < negativeValues.length) { + oldValue = negativeValues[-key]; + negativeValues[-key] = null; + } + } + return oldValue; + } + + public int[] findMatchingKeys(Object value) { + ArrayList matching = new ArrayList(); + Iterator it = keys.iterator(); + while (it.hasNext()) { + Integer key = (Integer)it.next(); + if (get(key.intValue()).equals(value)) + matching.add(key); + } + int[] result = new int[matching.size()]; + for (int i=0;iArrayIndexOutOfBoundsException if string.length()<width. + */ + public static String left(String string,int width) { + return string.substring(0, Math.min(string.length(), width -1)); + } + + /** Convert a byte array into a printable format containing aString of hexadecimal digit characters (two per byte). + * This method is taken form the apache jakarata + * tomcat project. + */ + public static String convert(byte bytes[]) { + StringBuffer sb = new StringBuffer(bytes.length * 2); + for (int i = 0; i < bytes.length; i++) { + sb.append(convertDigit((int) (bytes[i] >> 4))); + sb.append(convertDigit((int) (bytes[i] & 0x0f))); + } + return (sb.toString()); + } + + /** Convert the specified value (0-15) to the corresponding hexadecimal digit. + * This method is taken form the apache jakarata tomcat project. + */ + public static char convertDigit(int value) { + value &= 0x0f; + if (value >= 10) + return ((char) (value - 10 + 'a')); + else + return ((char) (value + '0')); + } + + public static boolean equalsOrBothNull(Object o1, Object o2) { + if (o1 == null) { + if (o2 != null) { + return false; + } + } else if ( o2 == null) { + return false; + } else if (!o1.equals( o2 ) ) { + return false; + } + return true; + } + + /** 1.3 compatibility method */ + public static String[] split(String stringToSplit, char delimiter) { + List keys = new ArrayList(); + int lastIndex = 0; + while( true ) { + int index = stringToSplit.indexOf( delimiter,lastIndex); + if ( index < 0) + { + String token = stringToSplit.substring( lastIndex ); + if ( token.length() >= 0) + { + keys.add( token ); + } + break; + } + String token = stringToSplit.substring( lastIndex , index ); + keys.add( token ); + lastIndex = index + 1; + } + return (String[])keys.toArray( new String[] {}); + + } + + /** 1.3 compatibility method */ + public static String replaceAll( String string, String stringToReplace, String newString ) { + if ( stringToReplace.equals( newString)) + return string; + int length = stringToReplace.length(); + int oldPos = 0; + while ( true ) { + int pos = string.indexOf( stringToReplace,oldPos); + if ( pos < 0 ) + return string; + + string = string.substring(0, pos) + newString + string.substring( pos + length); + oldPos = pos + 1; + if ( oldPos >= string.length() ) + return string; + + } + } + + /** reads a table from a csv file. You can specify a minimum number of columns */ + public static String[][] csvRead(Reader reader, int expectedColumns) throws IOException { + //System.out.println( "Using Encoding " + reader.getEncoding() ); + StringBuffer buf = new StringBuffer(); + while (true) { + int c = reader.read(); + if ( c == -1 ) + break; + buf.append( (char) c ); + } + String[] lines = buf.toString().split(System.getProperty("line.separator")); //BJO + //String[] lines = split( buf.toString(),'\n'); + String[][] lineEntries = new String[ lines.length ][]; + for ( int i=0;i= array.length) + throw new NoSuchElementException(); + + return array[ pos ++]; + } +} + diff --git a/Rapla/src/org/rapla/components/util/iterator/FilterIterator.java b/Rapla/src/org/rapla/components/util/iterator/FilterIterator.java new file mode 100644 index 0000000..8aaeb95 --- /dev/null +++ b/Rapla/src/org/rapla/components/util/iterator/FilterIterator.java @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.util.iterator; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** Filters the objects of an Iterator by overiding the isInIterator method*/ +public abstract class FilterIterator implements Iterator { + Iterator it; + Object obj; + + public FilterIterator(Iterator it) { + this.it = it; + obj = getNextObject(); + } + + public boolean hasNext() { + return obj != null; + } + + protected abstract boolean isInIterator(Object obj); + + public void remove() { + throw new UnsupportedOperationException(); + } + + private Object getNextObject() { + Object o; + do { + if ( !it.hasNext() ) { + return null; + } + o = it.next(); + } while (!isInIterator( o)); + return o; + } + + public Object next() { + if ( obj == null) + throw new NoSuchElementException(); + + Object result = obj; + obj = getNextObject(); + return result; + } +} + diff --git a/Rapla/src/org/rapla/components/util/iterator/IntIterator.java b/Rapla/src/org/rapla/components/util/iterator/IntIterator.java new file mode 100644 index 0000000..20bc348 --- /dev/null +++ b/Rapla/src/org/rapla/components/util/iterator/IntIterator.java @@ -0,0 +1,100 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.util.iterator; + +import java.util.NoSuchElementException; + +/** This class can iterate over a string containing a list of integers. + Its tuned for performance, so it will return int instead of Integer and + can be reused without recreating by calling the init method. +*/ +public class IntIterator { + int parsePosition = 0; + String text; + char delimiter; + int len; + int next; + boolean hasNext=false; + + public void init(String text,char delimiter) { + this.text = text; + len = text.length(); + this.delimiter = delimiter; + parsePosition = 0; + parseNext(); + } + + public boolean hasNext() { + return hasNext; + } + + public int next() { + if (!hasNext()) + throw new NoSuchElementException(); + int result = next; + parseNext(); + return result; + } + + private void parseNext() { + boolean isNegative = false; + int relativePos = 0; + + next = 0; + + if (parsePosition == len) { + hasNext = false; + return; + } + + while (parsePosition< len) { + char c = text.charAt(parsePosition ); + if (relativePos == 0 && c=='-') { + isNegative = true; + continue; + } + + if (c == delimiter) { + parsePosition++; + break; + } + + int digit = c-'0'; + if (digit<0 || digit>9) { + hasNext = false; + return; + } + + next *= 10; + next += digit; + parsePosition++; + relativePos++; + } + + if (isNegative) + next *= -1; + + hasNext = true; + } + public int getPos() { + return parsePosition; + } +} + + + + + + + + diff --git a/Rapla/src/org/rapla/components/util/iterator/IteratorChain.java b/Rapla/src/org/rapla/components/util/iterator/IteratorChain.java new file mode 100644 index 0000000..1707798 --- /dev/null +++ b/Rapla/src/org/rapla/components/util/iterator/IteratorChain.java @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.util.iterator; + +import java.util.Iterator; + +/** concatenates two Iterators */ +public class IteratorChain implements Iterator { + protected Iterator firstIt; + protected Iterator secondIt; + Object nextElement; + boolean isIteratingFirst = true; + + public IteratorChain(Iterator firstIt, Iterator secondIt) { + this.firstIt = firstIt; + this.secondIt = secondIt; + } + + + public boolean hasNext() { + return (isIteratingFirst && firstIt.hasNext()) + || secondIt.hasNext(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + public Object next() { + if (isIteratingFirst && !firstIt.hasNext()) + isIteratingFirst = false; + + if ( isIteratingFirst ) + return firstIt.next(); + else + return secondIt.next(); + } +} + diff --git a/Rapla/src/org/rapla/components/util/iterator/NestedIterator.java b/Rapla/src/org/rapla/components/util/iterator/NestedIterator.java new file mode 100644 index 0000000..949b51a --- /dev/null +++ b/Rapla/src/org/rapla/components/util/iterator/NestedIterator.java @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.util.iterator; + +import java.util.NoSuchElementException; +import java.util.Iterator; + +/**Successivly iterates over the elements specified in the nested Iterators. +Example of an recursive traversal of an Entity Tree: +
    +class RecursiveEntityIterator extends NestedIterator {
    +    public RecursiveEntityIterator(Iterator it) {
    +        super(it);
    +    }
    +    public Iterator getNestedIterator(Object obj) {
    +        return new RecursiveEntityIterator(((Entity)obj).getSubEntities());
    +    }
    +}
    +
    +*/ + +public abstract class NestedIterator implements Iterator { + protected Iterator outerIt; + protected Iterator innerIt; + Object nextElement; + boolean isInitialized; + public NestedIterator(Iterator outerIt) { + this.outerIt = outerIt; + } + + private Object nextElement() { + while (outerIt.hasNext() || (innerIt != null && innerIt.hasNext())) { + if (innerIt != null && innerIt.hasNext()) + return innerIt.next(); + innerIt = getNestedIterator(outerIt.next()); + } + return null; + } + + public abstract Iterator getNestedIterator(Object obj); + + public boolean hasNext() { + if (!isInitialized) + { + nextElement = nextElement(); + isInitialized = true; + } + return nextElement != null; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + public Object next() { + if (!hasNext()) + throw new NoSuchElementException(); + Object result = nextElement; + nextElement = nextElement(); + return result; + } +} + diff --git a/Rapla/src/org/rapla/components/util/iterator/package.html b/Rapla/src/org/rapla/components/util/iterator/package.html new file mode 100644 index 0000000..7b426d4 --- /dev/null +++ b/Rapla/src/org/rapla/components/util/iterator/package.html @@ -0,0 +1,4 @@ + +

    Iterators used by Rapla.

    + + diff --git a/Rapla/src/org/rapla/components/util/package.html b/Rapla/src/org/rapla/components/util/package.html new file mode 100644 index 0000000..b806f9f --- /dev/null +++ b/Rapla/src/org/rapla/components/util/package.html @@ -0,0 +1,4 @@ + +

    Some more helpful tools.

    + + diff --git a/Rapla/src/org/rapla/components/util/xml/XMLReaderAdapter.java b/Rapla/src/org/rapla/components/util/xml/XMLReaderAdapter.java new file mode 100644 index 0000000..e553804 --- /dev/null +++ b/Rapla/src/org/rapla/components/util/xml/XMLReaderAdapter.java @@ -0,0 +1,60 @@ +/*---------------------------------------------------------------------------* + | (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org . | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.util.xml; + +import javax.xml.parsers.SAXParserFactory; + +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; + +final public class XMLReaderAdapter { + /** Here you can set the xml-reader implementation that should be + used. The default implementation is the aelfred-parser from + the saxon project: net.sf.saxon.aelfred.SAXDriver. + */ + //public static String XML_READER_IMPL ="net.sf.saxon.aelfred.SAXDriver"; + public static String XML_READER_IMPL ="java.Saxon"; + + private XMLReaderAdapter() + { + } + + private static ClassLoader getClassLoader() { + return XMLReaderAdapter.class.getClassLoader(); + } + + public static void checkXMLSupport() throws ClassNotFoundException { + try { + getClassLoader().loadClass("javax.xml.parsers.SAXParserFactory"); + } catch (ClassNotFoundException ex) { + throw new ClassNotFoundException + ("Couldn't find SAX-XML-PARSER API: javax.xml.parsers" + + " You need java 1.4 or higher. For java-versions below 1.4 please download" + + " the saxon.jar from rapla.sourceforge.net" + + " and put it into the lib directory."); + } + } + + public static XMLReader createXMLReader(boolean validating) throws SAXException { + try { + SAXParserFactory spf = SAXParserFactory.newInstance(); + spf.setNamespaceAware(true); + spf.setValidating(validating); + return spf.newSAXParser().getXMLReader(); + + } catch (Exception ex2) { + throw new SAXException("Couldn't create XMLReader '" + + XML_READER_IMPL +"' : " + ex2.getMessage()); + } + } +} diff --git a/Rapla/src/org/rapla/components/util/xml/XMLTransformerAdapter.java b/Rapla/src/org/rapla/components/util/xml/XMLTransformerAdapter.java new file mode 100644 index 0000000..b371757 --- /dev/null +++ b/Rapla/src/org/rapla/components/util/xml/XMLTransformerAdapter.java @@ -0,0 +1,66 @@ +/*---------------------------------------------------------------------------* + | (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org . | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.util.xml; + +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.TransformerFactoryConfigurationError; +import javax.xml.transform.sax.SAXTransformerFactory; + +import org.xml.sax.SAXException; + +final public class XMLTransformerAdapter { + /** Here you can set the xslt-transformer-factory implementation that should be used if + TransformerFactory.newInstance() fails. The default implementation + is the saxon transformer-factory from the saxon project: net.sf.saxon.TransformerFactoryImpl + */ + public static String XSLT_TRANSFORMER_FACTORY_IMPL = "com.icl.saxon.TransformerFactoryImpl"; + + private static ClassLoader getClassLoader() { + return XMLTransformerAdapter.class.getClassLoader(); + } + + public static void checkXMLSupport() throws ClassNotFoundException { + try { + getClassLoader().loadClass("javax.xml.transform.sax.SAXTransformerFactory"); + } catch (ClassNotFoundException ex) { + throw new ClassNotFoundException + ("Couldn't find Transformer-API: javax.xml.transform" + + " You need java 1.4 or higher. For java-versions below 1.4 please download" + + " the saxon.jar from rapla.sourceforge.net" + + " and put it into the lib directory."); + } + } + + public static SAXTransformerFactory getTransformerFactory() throws SAXException{ + try { + return (SAXTransformerFactory) TransformerFactory.newInstance(); + } catch (TransformerFactoryConfigurationError ex) { + System.err.println("Couldn't initialize default SAXTransformerFactory. Now trying '" + + XSLT_TRANSFORMER_FACTORY_IMPL + "'"); + try { + getClassLoader().loadClass("javax.xml.parsers.SAXParserFactory"); + return (SAXTransformerFactory) getClassLoader().loadClass(XSLT_TRANSFORMER_FACTORY_IMPL).newInstance(); + } catch (ClassNotFoundException ex2) { + throw new SAXException("Couldn't find '" + XSLT_TRANSFORMER_FACTORY_IMPL + +"' on classpath. Requiered library is missing!"); + } catch (ClassCastException ex2) { + throw new SAXException("Wrong class: " + XSLT_TRANSFORMER_FACTORY_IMPL + + " doesnt implement SAXTransformerFactory"); + } catch (Exception ex2) { + throw new SAXException("Couldn't load SAXTransformerFactory '" + + XSLT_TRANSFORMER_FACTORY_IMPL +"' : " + ex2.getMessage()); + } + } + } + +} diff --git a/Rapla/src/org/rapla/components/util/xml/XMLWriter.java b/Rapla/src/org/rapla/components/util/xml/XMLWriter.java new file mode 100644 index 0000000..d73e4e8 --- /dev/null +++ b/Rapla/src/org/rapla/components/util/xml/XMLWriter.java @@ -0,0 +1,191 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.util.xml; + +import java.io.IOException; +import java.io.BufferedWriter; + +import org.xml.sax.Attributes; + +/** Provides some basic functionality for xml-file creation. This + * is the SAX like alternative to the creation of a DOM Tree.*/ +public class XMLWriter { + BufferedWriter writer; + boolean xmlSQL = false; + public void setWriter(BufferedWriter writer) { + this.writer = writer; + } + + public BufferedWriter getWriter() { + return this.writer; + } + + int level = 0; + private void indent() throws IOException { + if( !xmlSQL) //BJO do not indent for sql db, XML_VALUE column will be too small + for (int i = 0; i < level * 3; i++) writer.write(' '); + } + + protected void increaseIndentLevel() { + level ++; + } + + protected void decreaseIndentLevel() { + if (level > 0) + level --; + } + + public int getIndentLevel() { + return level; + } + + public void setIndentLevel(int level) { + this.level = level; + } + + public static String encode(String text) { + boolean needsEncoding = false; + int size = text.length(); + for ( int i= 0; i': + case '&': + case '"': + needsEncoding = true; + break; + } + } + if ( !needsEncoding ) + return text; + StringBuffer buf = new StringBuffer(); + for ( int i= 0; i': + buf.append(">"); + break; + case '&': + buf.append("&"); + break; + case '"': + buf.append("""); + break; + default: + buf.append(c); + break; + } // end of switch () + } // end of for () + return buf.toString(); + } + + protected void printEncode(String text) throws IOException { + if (text == null) + return; + writer.write( encode(text) ); + } + + protected void openTag(String start) throws IOException { + indent(); + writer.write('<'); + writer.write(start); + level++; + } + + protected void openElement(String start) throws IOException { + indent(); + writer.write('<'); + writer.write(start); + writer.write('>');writer.newLine(); + level++; + } + + protected void openElementOnLine(String start) throws IOException { + indent(); + writer.write('<'); + writer.write(start); + writer.write('>'); + level++; + } + + protected void att(Attributes attr) throws IOException{ + for (int i=0;i');writer.newLine(); + } + + protected void closeTagOnLine() throws IOException{ + writer.write('>'); + } + + protected void closeElementOnLine(String element) throws IOException { + level--; + writer.write('<'); + writer.write('/'); + writer.write(element); + writer.write('>'); + } + + protected void closeElement(String element) throws IOException { + level--; + indent(); + writer.write('<'); + writer.write('/'); + writer.write(element); + writer.write('>');writer.newLine(); + } + + protected void closeElementTag() throws IOException { + level--; + writer.write('/'); + writer.write('>');writer.newLine(); + } + + /** writes the line to the specified PrintWriter */ + public void println(String text) throws IOException { + indent(); + writer.write(text);writer.newLine(); + } + + /** writes the text to the specified PrintWriter */ + public void print(String text) throws IOException { + writer.write(text); + } + + /** writes the line to the specified PrintWriter */ + public void println() throws IOException { + writer.newLine(); + } + + public void setSQL(boolean sql) { + this.xmlSQL = sql; + } +} diff --git a/Rapla/src/org/rapla/components/xmlbundle/CompoundI18n.java b/Rapla/src/org/rapla/components/xmlbundle/CompoundI18n.java new file mode 100644 index 0000000..656d433 --- /dev/null +++ b/Rapla/src/org/rapla/components/xmlbundle/CompoundI18n.java @@ -0,0 +1,84 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.xmlbundle; + +import java.net.URL; +import java.text.MessageFormat; +import java.util.Locale; +import java.util.MissingResourceException; + +import javax.swing.ImageIcon; +/** Allows the combination of two resource-bundles. + First the inner bundle will be searched. + If the requested resource was not found the + outer bundle will be searched. + */ +public class CompoundI18n implements I18nBundle { + I18nBundle inner; + I18nBundle outer; + public CompoundI18n(I18nBundle inner,I18nBundle outer) { + this.inner = inner; + this.outer = outer; + } + + public String format(String key,Object obj1) { + Object[] array1 = new Object[1]; + array1[0] = obj1; + return format(key,array1); + } + + public String format(String key,Object obj1,Object obj2) { + Object[] array2 = new Object[2]; + array2[0] = obj1; + array2[1] = obj2; + return format(key,array2); + } + + public String format(String key,Object[] obj) { + MessageFormat msg = new MessageFormat(getString(key)); + return msg.format(obj); + } + + public ImageIcon getIcon(String key) { + try { + return inner.getIcon(key); + } catch (MissingResourceException ex) { + return outer.getIcon(key); + } + } + + public String getString(String key) { + try { + return inner.getString(key); + } catch (MissingResourceException ex) { + return outer.getString(key); + } + } + + + public URL getResource(String key) { + try { + return inner.getResource(key); + } catch (MissingResourceException ex) { + return outer.getResource(key); + } + } + + public String getLang() { + return inner.getLang(); + } + + public Locale getLocale() { + return inner.getLocale(); + } +} diff --git a/Rapla/src/org/rapla/components/xmlbundle/DEPENDENCIES b/Rapla/src/org/rapla/components/xmlbundle/DEPENDENCIES new file mode 100644 index 0000000..051531a --- /dev/null +++ b/Rapla/src/org/rapla/components/xmlbundle/DEPENDENCIES @@ -0,0 +1,7 @@ +This component depends on the following packages (including subpackages): +java.* +javax.xml.* +javax.swing.Icon +org.xml.sax.* +org.rapla.components.util.* +org.apache.avalon.framework.* diff --git a/Rapla/src/org/rapla/components/xmlbundle/I18nBundle.java b/Rapla/src/org/rapla/components/xmlbundle/I18nBundle.java new file mode 100644 index 0000000..6ea408e --- /dev/null +++ b/Rapla/src/org/rapla/components/xmlbundle/I18nBundle.java @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.xmlbundle; + +import java.net.URL; +import java.util.Locale; +import java.util.MissingResourceException; + +import javax.swing.ImageIcon; + +/**The interface provides access to a resourcebundle that + can be defined in XML or as an java-object. +Example Usage: +
    +   I18nBundle i18n = (I18nBundle)serviceManager.lookup(I18nBundle.ROLE);
    +   i18n.getString("yes"); // will get the translation for yes.
    +
    +*/ + +public interface I18nBundle { + String ROLE = I18nBundle.class.getName(); + /** same as format(key,new Object[] {obj1}); + @see #format(String,Object[]) + */ + String format(String key,Object obj1) throws MissingResourceException; + /** same as format(key,new Object[] {obj1, obj2}); + @see #format(String,Object[]) + */ + String format(String key,Object obj1,Object obj2) throws MissingResourceException; + /** same as + + (new MessageFormat(getString(key))).format(obj); + + @see java.text.MessageFormat + */ + String format(String key,Object[] obj) throws MissingResourceException; + + /** returns the specified icon from the image-resource-file. + @throws MissingResourceException if not found or can't be loaded. + */ + ImageIcon getIcon(String key) throws MissingResourceException; + + /** returns the specified string from the selected resource-file. + @throws MissingResourceException if not found or can't be loaded. + */ + String getString(String key) throws MissingResourceException; + + /** returns the URL of a resource, with the specified key. Use this method to directly + * access the Image Resources instead of creating an Icon.*/ + URL getResource(String key) throws MissingResourceException; + + /** @return the selected language. */ + String getLang(); + + /** @return the selected Locale. */ + Locale getLocale(); +} diff --git a/Rapla/src/org/rapla/components/xmlbundle/LocaleChangeEvent.java b/Rapla/src/org/rapla/components/xmlbundle/LocaleChangeEvent.java new file mode 100644 index 0000000..b3076f6 --- /dev/null +++ b/Rapla/src/org/rapla/components/xmlbundle/LocaleChangeEvent.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.xmlbundle; +import java.util.EventObject; +import java.util.Locale; +public class LocaleChangeEvent extends EventObject{ + private static final long serialVersionUID = 1L; + + Locale locale; + public LocaleChangeEvent(Object source,Locale locale) { + super(source); + this.locale = locale; + } + public Locale getLocale() { + return locale; + } +} diff --git a/Rapla/src/org/rapla/components/xmlbundle/LocaleChangeListener.java b/Rapla/src/org/rapla/components/xmlbundle/LocaleChangeListener.java new file mode 100644 index 0000000..cfbf009 --- /dev/null +++ b/Rapla/src/org/rapla/components/xmlbundle/LocaleChangeListener.java @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.xmlbundle; +import java.util.EventListener; +public interface LocaleChangeListener extends EventListener{ + void localeChanged(LocaleChangeEvent evt); +} diff --git a/Rapla/src/org/rapla/components/xmlbundle/LocaleSelector.java b/Rapla/src/org/rapla/components/xmlbundle/LocaleSelector.java new file mode 100644 index 0000000..5aef543 --- /dev/null +++ b/Rapla/src/org/rapla/components/xmlbundle/LocaleSelector.java @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.xmlbundle; + +import java.util.Locale; + + +/** If you want to change the locales during runtime put a LocaleSelector + in the base-context. Instances of I18nBundle will then register them-self + as {@link LocaleChangeListener LocaleChangeListeners}. Change the locale + with {@link #setLocale} and all bundles will try to load the appropriate resources. + */ +public interface LocaleSelector { + String ROLE = LocaleSelector.class.getName(); + + void addLocaleChangeListener(LocaleChangeListener listener); + + void removeLocaleChangeListener(LocaleChangeListener listener); + + void setLocale(Locale locale); + + Locale getLocale(); + + void setLanguage(String language); + + void setCountry(String country); + + String getLanguage(); + +} diff --git a/Rapla/src/org/rapla/components/xmlbundle/impl/DictionaryEntry.java b/Rapla/src/org/rapla/components/xmlbundle/impl/DictionaryEntry.java new file mode 100644 index 0000000..82cc0bc --- /dev/null +++ b/Rapla/src/org/rapla/components/xmlbundle/impl/DictionaryEntry.java @@ -0,0 +1,61 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.components.xmlbundle.impl; +import java.util.*; + +class DictionaryEntry { + String key; + Map translations = new TreeMap(); + + public DictionaryEntry(String key) { + this.key = key; + } + public void add(String lang,String value) { + translations.put(lang,value); + } + + public String getKey() { + return key; + } + + public String get(String lang) { + return (String) translations.get(lang); + } + + public String get(String lang,String defaultLang) { + Object content = translations.get(lang); + if ( content == null) { + content = translations.get(defaultLang); + } // end of if () + + if ( content == null) { + Iterator it = translations.values().iterator(); + content = it.next(); + } + return (String) content; + } + + public String[] availableLanguages() { + String[] result = new String[translations.keySet().size()]; + Iterator it = translations.keySet().iterator(); + int i = 0; + while ( it.hasNext()) { + result[i++] = (String) it.next(); + } + return result; + } + +} + + diff --git a/Rapla/src/org/rapla/components/xmlbundle/impl/I18nBundleImpl.java b/Rapla/src/org/rapla/components/xmlbundle/impl/I18nBundleImpl.java new file mode 100644 index 0000000..c73098e --- /dev/null +++ b/Rapla/src/org/rapla/components/xmlbundle/impl/I18nBundleImpl.java @@ -0,0 +1,566 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.xmlbundle.impl; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.net.URL; +import java.text.MessageFormat; +import java.util.Collections; +import java.util.Locale; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.PropertyResourceBundle; +import java.util.ResourceBundle; +import java.util.TreeMap; + +import javax.swing.ImageIcon; + +import org.apache.avalon.framework.activity.Disposable; +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.DefaultConfiguration; +import org.apache.avalon.framework.logger.Logger; +import org.rapla.components.util.IOUtil; +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.components.xmlbundle.LocaleChangeEvent; +import org.rapla.components.xmlbundle.LocaleChangeListener; +import org.rapla.components.xmlbundle.LocaleSelector; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +/** The default implementation of the xmlbundle component allows reading from + a compiled ResourceBundle as well as directly from the source-xml-file. +

    + Sample Configuration 1: (Resources are loaded from the compiled ResourceBundles) +

    + <resource-bundle id="org.rapla.RaplaResources"/>
    + 
    +

    +

    + Sample Configuration 2: (Resources will be loaded directly from the resource-file) +

    + <resource-bundle id="org.rapla.plugin.periodwizard.WizardResources">
    + <file>/home/christopher/Rapla/src/org/rapla/periodwizard/WizardResources.xml</file>
    + </resource-bundle>
    + 
    +

    +

    + This class looks for a LocaleSelector on the context and registers itself as + a LocaleChangeListener and switches to the new Locale on a LocaleChangeEvent. +

    + @see TranslationParser + @see LocaleSelector + */ + +public class I18nBundleImpl implements I18nBundle, LocaleChangeListener, Disposable +{ + String className; + String dictionaryFile; + Locale locale; + Logger logger = null; + I18nBundle m_parentBundle; + LocaleSelectorImpl m_localeSelector; + + Map stringCache = Collections.synchronizedMap( new TreeMap() ); + Map iconCache = Collections.synchronizedMap( new TreeMap() ); + ResourceBundle resourceBundle; + RaplaDictionary dict; + String parentId = null; + + /** + * @throws RaplaException when the resource-file is missing or can't be accessed + or can't be parsed + */ + public I18nBundleImpl( RaplaContext serviceManager, Configuration config, Logger logger ) throws RaplaException + { + enableLogging( logger ); + dictionaryFile = config.getChild( "file" ).getValue( null ); + try + { + if ( dictionaryFile == null ) + { + className = config.getChild( "classname" ).getValue( null ); + if ( className == null ) + className = config.getAttribute( "id" ); + else + className = className.trim(); + } + + if ( dictionaryFile != null ) + { + try + { + TranslationParser parser = new TranslationParser(); + String path = new File( dictionaryFile ).getCanonicalPath(); + getLogger().info( "getting lanaguageResources from " + path ); + dict = parser.parse( new FileInputStream( new File( dictionaryFile ) ) ); + } + finally + { + + } + } + } + catch ( Exception ex ) + { + throw new RaplaException( ex ); + } + + m_localeSelector = ( (LocaleSelectorImpl) serviceManager.lookup( LocaleSelector.ROLE ) ); + + if ( m_localeSelector != null ) + { + m_localeSelector.addLocaleChangeListenerFirst( this ); + setLocale( m_localeSelector.getLocale() ); + } + else + { + setLocale( Locale.getDefault() ); + } + + try + { + parentId = lookup( TranslationParser.PARENT_BUNDLE_IDENTIFIER ); + } + catch ( MissingResourceException ex ) + { + } + + if ( parentId != null ) + { + m_parentBundle = (I18nBundle) serviceManager.lookup( I18nBundle.ROLE + "/" + parentId ); + } + } + + public String getParentId() + { + return parentId; + } + + public static Configuration createConfig( String resourceFile ) + { + DefaultConfiguration config = new DefaultConfiguration( "component", "auto-configuration of " + resourceFile ); + config.setAttribute( "id", resourceFile.toString() ); + return config; + } + + /* + private void init(I18nBundle parentBundle,LocaleSelector ) { + if (m_localeSelector != null) { + m_localeSelector.addLocaleChangeListenerFirst(this); + setLocale(m_localeSelector.getLocale()); + } else { + setLocale(Locale.getDefault()); + } + } + */ + + public void dispose() + { + if ( m_localeSelector != null ) + m_localeSelector.removeLocaleChangeListener( this ); + } + + public void localeChanged( LocaleChangeEvent evt ) + { + try + { + setLocale( evt.getLocale() ); + } + catch ( Exception ex ) + { + getLogger().error( "Can't set new locale " + evt.getLocale(), ex ); + } + } + + public void enableLogging( Logger logger ) + { + this.logger = logger; + } + + protected Logger getLogger() + { + return logger; + } + + public String format( String key, Object obj1 ) + { + Object[] array1 = new Object[1]; + array1[0] = obj1; + return format( key, array1 ); + } + + public String format( String key, Object obj1, Object obj2 ) + { + Object[] array2 = new Object[2]; + array2[0] = obj1; + array2[1] = obj2; + return format( key, array2 ); + } + + public String format( String key, Object[] obj ) + { + MessageFormat msg = new MessageFormat( getString( key ) ); + return msg.format( obj ); + } + + private final byte[] loadResource( String fileName ) throws IOException + { + return IOUtil.readBytes( getResourceFromFile( fileName ) ); + } + + private URL getResourceFromFile( String fileName ) throws IOException + { + URL resource = null; + String base; + if ( dict == null ) + { + base = resourceBundle.getClass().getName(); + resource = resourceBundle.getClass().getResource( fileName ); + } + else + { + base = ( new File( dictionaryFile ) ).getParent(); + if ( getLogger().isDebugEnabled() ) + getLogger().debug( "Looking for resourcefile " + fileName + " in directory " + base ); + File resourceFile = new File( base, fileName ); + if ( resourceFile.exists() ) + resource = resourceFile.toURI().toURL(); + } + if ( resource == null ) + throw new IOException( "File '" + + fileName + + "' not found. " + + " in bundle " + + className + + " It must be in the same location as '" + + base + + "'" ); + return resource; + } + + public URL getResource( String key ) throws MissingResourceException + { + String resourceFile; + try + { + resourceFile = lookup( key ); + } + catch ( MissingResourceException ex ) + { + if ( m_parentBundle != null ) + return m_parentBundle.getResource( key ); + throw ex; + } + try + { + return getResourceFromFile( resourceFile ); + } + catch ( Exception ex ) + { + String message = "Resourcefile " + resourceFile + " not found: " + ex.getMessage(); + getLogger().error( message ); + throw new MissingResourceException( message, className, key ); + } + + } + + public ImageIcon getIcon( String key ) throws MissingResourceException + { + String iconfile; + try + { + iconfile = lookup( key ); + } + catch ( MissingResourceException ex ) + { + if ( m_parentBundle != null ) + return m_parentBundle.getIcon( key ); + throw ex; + } + try + { + ImageIcon icon = (ImageIcon) iconCache.get( iconfile ); + if ( icon == null ) + { + icon = new ImageIcon( loadResource( iconfile ), key ); + iconCache.put( iconfile, icon ); + } // end of if () + return icon; + } + catch ( Exception ex ) + { + String message = "Icon " + iconfile + " can't be created: " + ex.getMessage(); + getLogger().error( message ); + throw new MissingResourceException( message, className, key ); + } + } + + public Locale getLocale() + { + if ( locale == null ) + throw new IllegalStateException( "Call setLocale first!" ); + return locale; + } + + public String getLang() + { + if ( locale == null ) + throw new IllegalStateException( "Call setLocale first!" ); + return locale.getLanguage(); + } + + /** this method imitates the orginal + * ResourceBundle.getBundle(String className,Locale + * locale) which causes problems when the locale is changed + * to the base locale (english). For a full description see + * ResourceBundle.getBundle(String className) in the java-api.*/ + protected ResourceBundle loadResourceBundle( String className, Locale locale ) + { + String tries[] = new String[7]; + StringBuffer buf = new StringBuffer(); + tries[6] = className; + buf.append( className ); + if ( locale.getLanguage().length() > 0 ) + { + buf.append( '_' ); + buf.append( locale.getLanguage() ); + tries[2] = buf.toString(); + } + if ( locale.getCountry().length() > 0 ) + { + buf.append( '_' ); + buf.append( locale.getCountry() ); + tries[1] = buf.toString(); + } + if ( locale.getVariant().length() > 0 ) + { + buf.append( '_' ); + buf.append( locale.getVariant() ); + tries[0] = buf.toString(); + } + buf.delete( className.length(), buf.length() - 1 ); + Locale defaultLocale = Locale.getDefault(); + if ( defaultLocale.getLanguage().length() > 0 ) + { + buf.append( defaultLocale.getLanguage() ); + tries[5] = buf.toString(); + } + if ( defaultLocale.getCountry().length() > 0 ) + { + buf.append( '_' ); + buf.append( defaultLocale.getCountry() ); + tries[4] = buf.toString(); + } + if ( defaultLocale.getVariant().length() > 0 ) + { + buf.append( '_' ); + buf.append( defaultLocale.getVariant() ); + tries[3] = buf.toString(); + } + + ResourceBundle bundle = null; + for ( int i = 0; i < tries.length; i++ ) + { + if ( tries[i] == null ) + continue; + bundle = loadBundle( tries[i] ); + if ( bundle != null ) + { + loadParent( tries, i, bundle ); + return bundle; + } + } + throw new MissingResourceException( "'" + className + "' not found. The resource-file is missing.", className, + "" ); + } + + private String getPropertyFileNameFromClassName( String classname ) + { + StringBuffer result = new StringBuffer( classname ); + for ( int i = 0; i < result.length(); i++ ) + { + if ( result.charAt( i ) == '.' ) + result.setCharAt( i, '/' ); + } + result.insert( 0, '/' ); + result.append( ".properties" ); + return result.toString(); + } + + private ResourceBundle loadBundle( String name ) + { + try + { + getLogger().debug( "Trying to load bundle " + name ); + String pathName = getPropertyFileNameFromClassName( name ); + InputStream io = this.getClass().getResourceAsStream( pathName ); + if ( io != null ) + { + return new PropertyResourceBundle( io ); + } + ResourceBundle bundle = (ResourceBundle) Class.forName( name ).newInstance(); + getLogger().debug( "Bundle found " + name ); + return bundle; + } + catch ( Exception ex ) + { + return null; + } + catch ( ClassFormatError ex ) + { + return null; + } + } + + private void loadParent( String[] tries, int i, ResourceBundle bundle ) + { + ResourceBundle parent = null; + if ( i == 0 || i == 3 ) + { + parent = loadBundle( tries[i++] ); + if ( parent != null ) + setParent( bundle, parent ); + bundle = parent; + } + if ( i == 1 || i == 4 ) + { + parent = loadBundle( tries[i++] ); + if ( parent != null ) + setParent( bundle, parent ); + bundle = parent; + } + if ( i == 2 || i == 5 ) + { + parent = loadBundle( tries[6] ); + if ( parent != null ) + setParent( bundle, parent ); + } + } + + private void setParent( ResourceBundle bundle, ResourceBundle parent ) + { + try + { + Method method = bundle.getClass().getMethod( "setParent", new Class[] + { ResourceBundle.class } ); + method.invoke( bundle, new Object[] + { parent } ); + } + catch ( Exception ex ) + { + } + } + + private String lookup( String key ) throws MissingResourceException + { + if ( dict == null ) + { + return resourceBundle.getString( key ); + } + else + { + String result = dict.lookup( key, getLang() ); + if ( result != null ) + return result; + String message = "Can't find resourcestring " + key + " in class " + className; + throw new MissingResourceException( message, className, key ); + } // end of else + } + + public String getString( String key ) throws MissingResourceException + { + String result = (String) stringCache.get( key ); + if ( result != null ) + return result; + result = getUncachedString( key ); + stringCache.put( key, result ); + return result; + } + + private String getUncachedString( String key ) throws MissingResourceException + { + String result; + try + { + result = lookup( key ); + } + catch ( MissingResourceException ex ) + { + if ( m_parentBundle != null ) + return m_parentBundle.getString( key ); + throw ex; + } + + if ( getLogger() != null && getLogger().isDebugEnabled() ) + getLogger().debug( "string requested: " + result ); + + return filterXHTML( result ); + } + + public I18nBundle getParentBundle() + { + return m_parentBundle; + } + + /* replaces XHTML with HTML because swing can't display proper XHTML*/ + String filterXHTML( String text ) + { + if ( text.indexOf( "
    " ) >= 0 ) + { + return applyXHTMLFilter( text ); + } + else + { + return text; + } // end of else + } + + public static String replaceAll( String text, String token, String with ) + { + StringBuffer buf = new StringBuffer(); + int i = 0; + int lastpos = 0; + while ( ( i = text.indexOf( token, lastpos ) ) >= 0 ) + { + if ( i > 0 ) + buf.append( text.substring( lastpos, i ) ); + buf.append( with ); + i = ( lastpos = i + token.length() ); + } // end of if () + buf.append( text.substring( lastpos, text.length() ) ); + return buf.toString(); + } + + private String applyXHTMLFilter( String text ) + { + return replaceAll( text, "
    ", "

    " ); + } + + public void setLocale( Locale locale ) + { + this.locale = locale; + stringCache.clear(); + iconCache.clear(); + getLogger().debug( "Locale changed to " + locale ); + if ( dict == null ) + { + resourceBundle = loadResourceBundle( className, locale ); + //resourceBundle = ResourceBundle.getBundle(className, locale); + } + } + +} diff --git a/Rapla/src/org/rapla/components/xmlbundle/impl/LocaleSelectorImpl.java b/Rapla/src/org/rapla/components/xmlbundle/impl/LocaleSelectorImpl.java new file mode 100644 index 0000000..30b5326 --- /dev/null +++ b/Rapla/src/org/rapla/components/xmlbundle/impl/LocaleSelectorImpl.java @@ -0,0 +1,86 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.xmlbundle.impl; + +import java.util.Locale; +import java.util.Vector; + +import org.rapla.components.xmlbundle.LocaleChangeEvent; +import org.rapla.components.xmlbundle.LocaleChangeListener; +import org.rapla.components.xmlbundle.LocaleSelector; + + +/** If you want to change the locales during runtime put a LocaleSelector + in the base-context. Instances of {@link I18nBundleImpl} will then register them-self + as {@link LocaleChangeListener LocaleChangeListeners}. Change the locale + with {@link #setLocale} and all bundles will try to load the appropriate resources. + */ +public class LocaleSelectorImpl implements LocaleSelector { + public static final String ROLE = LocaleSelectorImpl.class.getName(); + Locale locale; + Vector localeChangeListeners = new Vector(); + + public LocaleSelectorImpl() { + locale = Locale.getDefault(); + } + + public void addLocaleChangeListener(LocaleChangeListener listener) { + localeChangeListeners.add(listener); + } + public void removeLocaleChangeListener(LocaleChangeListener listener) { + localeChangeListeners.remove(listener); + } + + public void setLocale(Locale locale) { + this.locale = locale; + fireLocaleChanged(); + } + + public Locale getLocale() { + return this.locale; + } + + public LocaleChangeListener[] getLocaleChangeListeners() { + return (LocaleChangeListener[])localeChangeListeners.toArray(new LocaleChangeListener[]{}); + } + + public void setLanguage(String language) { + setLocale(new Locale(language,locale.getCountry())); + } + + public void setCountry(String country) { + setLocale(new Locale(locale.getLanguage(),country)); + } + + public String getLanguage() { + return locale.getLanguage(); + } + + protected void fireLocaleChanged() { + if (localeChangeListeners.size() == 0) + return; + LocaleChangeListener[] listeners = getLocaleChangeListeners(); + LocaleChangeEvent evt = new LocaleChangeEvent(this,getLocale()); + for (int i=0;i0 && i> 4) & 0x0f], hexDigit[b & 0x0f] }; + return new String(array); + } + + static public String charToHex(char c) { + // Returns hex String representation of char c + byte hi = (byte) (c >>> 8); + byte lo = (byte) (c & 0xff); + return byteToHex(hi) + byteToHex(lo); + } + + private String convertToJava(String text) { + StringBuffer result = new StringBuffer(); + for ( int i = 0;i< text.length();i++) { + char c = text.charAt(i); + + switch ( c) { + case '\n': // LineBreaks + result.append("\" \n + \""); + break; + case '\\': // \ + result.append("\\\\"); + break; + case '\"': // " + result.append("\\\""); + break; + default: + if ( (int)c > 127) { + result.append("\\u" + charToHex(c)); + } else { + result.append(c); + } // end of else + break; + } // end of switch () + } // end of for () + return result.toString(); + } + +} + + diff --git a/Rapla/src/org/rapla/components/xmlbundle/impl/TranslationParser.java b/Rapla/src/org/rapla/components/xmlbundle/impl/TranslationParser.java new file mode 100644 index 0000000..98911dc --- /dev/null +++ b/Rapla/src/org/rapla/components/xmlbundle/impl/TranslationParser.java @@ -0,0 +1,398 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.components.xmlbundle.impl; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.HashSet; +import java.util.Set; +import java.util.Stack; + +import org.apache.avalon.framework.configuration.ConfigurationException; +import org.apache.avalon.framework.logger.ConsoleLogger; +import org.apache.avalon.framework.logger.Logger; +import org.rapla.components.util.Assert; +import org.rapla.components.util.IOUtil; +import org.rapla.components.util.xml.XMLReaderAdapter; +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.DefaultHandler; + +/** This class reads *Resources.xml files and generates + the appropriate ResourceBundle java-files. +
    + Usage :
    + org.rapla.components.xmlbundle.TranslationParser PATH_TO_SOURCES [DESTINATION_PATH]
    +
    + Note: a xml-parser must be on your classpath.
    +
    + Example usage under windows:
    + java -classpath lib\saxon.jar;lib\fortress.jar;build\classes org.rapla.components.xmlbundle.TranslationParser src
    + 
    + + */ +public class TranslationParser extends DefaultHandler +{ + RaplaDictionary dict; + DictionaryEntry currentEntry = null; + String currentLang = null; + String defaultLang = null; + String currentIconSrc = null; + + static Logger log = new ConsoleLogger( ConsoleLogger.LEVEL_INFO ); + int level = 0; + + // used to store the nested content in the translation element + StringBuffer charBuffer; + XMLReader xmlReader; + /** The translation parser will add an extra identifer {$i18nbundle_parent$} to + the translation table if a parentbundle is specified. + */ + public final static String PARENT_BUNDLE_IDENTIFIER = "{$i18nbundle_parent$}"; + DefaultHandler handler = new DefaultHandler() + { + public InputSource resolveEntity( String publicId, String systemId ) throws SAXException + { + if ( systemId.endsWith( "resources.dtd" ) ) + { + try + { + URL resource = getClass().getResource( "/org/rapla/components/xmlbundle/resources.dtd" ); + Assert.notNull( resource, "resources.dtd not found on classpath" ); + return new InputSource( IOUtil.getInputStream( resource ) ); + } + catch ( IOException ex ) + { + throw new SAXException( ex ); + } + } + else + { + // use the default behaviour + try + { + return super.resolveEntity( publicId, systemId ); + } + catch ( SAXException ex ) + { + throw ex; + } + catch ( Exception ex ) + { + throw new SAXException( ex ); + } + } + } + + public void startElement( String uri, String name, String qName, Attributes atts ) throws SAXException + { + if ( log.isDebugEnabled() ) + log.debug( indent() + "Start element: " + qName + "(" + name + ")" ); + + level: + { + if ( name.equals( "resources" ) ) + { + String defaultLang = atts.getValue( "", "default" ); + String parentDict = atts.getValue( "", "parent" ); + dict = new RaplaDictionary( defaultLang ); + if ( parentDict != null && parentDict.trim().length() > 0 ) + { + DictionaryEntry entry = new DictionaryEntry( PARENT_BUNDLE_IDENTIFIER ); + entry.add( "en", parentDict.trim() ); + try + { + dict.addEntry( entry ); + } + catch ( UniqueKeyException ex ) + { + //first entry must be unique + } + } + break level; + } + + if ( name.equals( "entry" ) ) + { + String key = atts.getValue( "", "key" ); + currentEntry = new DictionaryEntry( key ); + break level; + } + + if ( name.equals( "text" ) ) + { + currentLang = atts.getValue( "", "lang" ); + if ( currentLang == null ) + currentLang = dict.getDefaultLang();; + charBuffer = new StringBuffer(); + break level; + } + + if ( name.equals( "icon" ) ) + { + currentLang = atts.getValue( "", "lang" ); + if ( currentLang == null ) + currentLang = dict.getDefaultLang(); + currentIconSrc = atts.getValue( "", "src" ); + charBuffer = new StringBuffer(); + break level; + } + + // copy startag + if ( charBuffer != null ) + { + copyStartTag( name, atts ); + } + } + level++; + } + + public void endElement( String uri, String name, String qName ) throws SAXException + { + level--; + if ( log.isDebugEnabled() ) + log.debug( indent() + "End element: " + qName + "(" + name + ")" ); + + level: + { + if ( name.equals( "icon" ) ) + { + if ( currentIconSrc != null ) + currentEntry.add( currentLang, currentIconSrc ); + break level; + } + + if ( name.equals( "text" ) ) + { + removeWhiteSpaces( charBuffer ); + currentEntry.add( currentLang, charBuffer.toString() ); + break level; + } + + if ( name.equals( "entry" ) ) + { + try + { + dict.addEntry( currentEntry ); + } + catch ( UniqueKeyException e ) + { + throw new SAXException( e.getMessage() ); + } // end of try-catch + currentEntry = null; + break level; + } + + // copy endtag + if ( charBuffer != null ) + { + copyEndTag( name ); + } // end of if () + } + } + + public void characters( char ch[], int start, int length ) + { + // copy nested content + if ( charBuffer != null ) + { + charBuffer.append( ch, start, length ); + } // end of if () + } + }; + + TranslationParser() throws ConfigurationException + { + super(); + try + { + xmlReader = XMLReaderAdapter.createXMLReader( false ); + xmlReader.setContentHandler( handler ); + xmlReader.setErrorHandler( handler ); + xmlReader.setDTDHandler( handler ); + xmlReader.setEntityResolver( handler ); + } + catch ( SAXException ex ) + { + if ( ex.getException() != null ) + { + throw new ConfigurationException( "", ex.getException() ); + } + else + { + throw new ConfigurationException( "", ex ); + } // end of else + } + } + + RaplaDictionary parse( InputStream in ) throws IOException, SAXException + { + dict = null; + xmlReader.parse( new InputSource( in ) ); + checkDict(); + return dict; + } + + RaplaDictionary parse( String systemID ) throws IOException, SAXException + { + dict = null; + xmlReader.parse( systemID ); + checkDict(); + return dict; + } + + private void checkDict() throws IOException + { + if ( dict == null ) + { + throw new IOException( "Dictionary file empty " ); + } + } + + private void copyStartTag( String name, Attributes atts ) + { + charBuffer.append( '<' ); + charBuffer.append( name ); + for ( int i = 0; i < atts.getLength(); i++ ) + { + charBuffer.append( ' ' ); + charBuffer.append( atts.getLocalName( i ) ); + charBuffer.append( '=' ); + charBuffer.append( '\"' ); + charBuffer.append( atts.getValue( i ) ); + charBuffer.append( '\"' ); + } + charBuffer.append( '>' ); + } + + private void copyEndTag( String name ) + { + if ( ( charBuffer != null ) + && ( charBuffer.length() > 0 ) + && ( charBuffer.charAt( charBuffer.length() - 1 ) == '>' ) ) + { + // --> + charBuffer.insert( charBuffer.length() - 1, "/" ); + } + else + { + // + charBuffer.append( "" ); + } // end of else + + } + + private void removeWhiteSpaces( StringBuffer buf ) + { + for ( int i = 1; i < buf.length(); i++ ) + { + if ( ( buf.charAt( i ) == ' ' ) && ( buf.charAt( i - 1 ) == ' ' ) ) + buf.deleteCharAt( --i ); + } // end of for () + } + + private String indent() + { + StringBuffer buffer = new StringBuffer(); + for ( int i = 0; i <= level; i++ ) + buffer.append( ' ' ); + return buffer.toString(); + } + + public static final String USAGE = new String( "Usage : \n" + + "PATH_TO_SOURCES [DESTINATION_PATH]\n" + + "Note: a xml-parser must be on your classpath.\n" + + "Example usage under windows:\n" + + "java -classpath lib\\saxon.jar;lib\\fortress.jar;build\\classes " + + "org.rapla.components.xmlbundle.TranslationParser " + + "src \n" ); + + public static void processDir( String srcDir, String destDir ) throws IOException, SAXException, + ConfigurationException + { + TranslationParser parser = new TranslationParser(); + ResourceFileGenerator generator = new ResourceFileGenerator(); + Set languages = new HashSet(); + Stack stack = new Stack(); + File topDir = new File( srcDir ); + stack.push( topDir ); + while ( !stack.empty() ) + { + File file = (File) stack.pop(); + if ( file.isDirectory() ) + { + // System.out.println("Checking Dir: " + file.getName()); + File[] files = file.listFiles(); + for ( int i = 0; i < files.length; i++ ) + stack.push( files[i] ); + } + else + { + // System.out.println("Checking File: " + file.getName()); + if ( file.getName().endsWith( "Resources.xml" ) ) + { + String absolut = file.getAbsolutePath(); + System.out.println( "Transforming source:" + file ); + String relativePath = absolut.substring( topDir.getAbsolutePath().length() ); + String prefix = file.getName().substring( 0, file.getName().length() - "Resources.xml".length() ); + String pathName = relativePath.substring( 0, relativePath.indexOf( file.getName() ) ); + RaplaDictionary dict = parser.parse( file.toURI().toURL().toExternalForm() ); + + File dir = new File( destDir, pathName ); + System.out.println( "destination:" + dir ); + dir.mkdirs(); + + String packageName = ResourceFileGenerator.toPackageName( pathName ); + generator.transform( dict, packageName, prefix + "Resources", "en", dir ); + String[] langs = dict.getAvailableLanguages(); + for ( int i = 0; i < langs.length; i++ ) + languages.add( langs[i] ); + } + } + } + } + + public static void main( String[] args ) + { + try + { + if ( args.length < 1 ) + { + System.out.println( USAGE ); + return; + } // end of if () + + String sourceDir = args[0]; + String destDir = ( args.length > 1 ) ? args[1] : sourceDir; + processDir( sourceDir, destDir ); + + } + catch ( SAXParseException ex ) + { + log.error( "Line:" + ex.getLineNumber() + " Column:" + ex.getColumnNumber() + " " + ex.getMessage(), ex ); + System.exit( 1 ); + } + catch ( Throwable e ) + { + log.error( e.getMessage(), e ); + System.exit( 1 ); + } + } // end of main () + +} diff --git a/Rapla/src/org/rapla/components/xmlbundle/impl/UniqueKeyException.java b/Rapla/src/org/rapla/components/xmlbundle/impl/UniqueKeyException.java new file mode 100644 index 0000000..786857e --- /dev/null +++ b/Rapla/src/org/rapla/components/xmlbundle/impl/UniqueKeyException.java @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.components.xmlbundle.impl; + +/** thrown by the RaplaDictionary when a duplicated entry is found */ +class UniqueKeyException extends Exception { + private static final long serialVersionUID = 1L; + + public UniqueKeyException(String text) { + super(text); + } +} + diff --git a/Rapla/src/org/rapla/components/xmlbundle/package.html b/Rapla/src/org/rapla/components/xmlbundle/package.html new file mode 100644 index 0000000..9a7f540 --- /dev/null +++ b/Rapla/src/org/rapla/components/xmlbundle/package.html @@ -0,0 +1,10 @@ + +

    Components for storing locale-specific resources in xml-files. +Java Resource-Bundles can be created automatically. +

    +

    +For adding a new language to Rapla take a look at resources.xml. +

    +@see rapla.sourceforge.net + + diff --git a/Rapla/src/org/rapla/components/xmlbundle/resources.dtd b/Rapla/src/org/rapla/components/xmlbundle/resources.dtd new file mode 100644 index 0000000..60a5155 --- /dev/null +++ b/Rapla/src/org/rapla/components/xmlbundle/resources.dtd @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Rapla/src/org/rapla/entities/Annotatable.java b/Rapla/src/org/rapla/entities/Annotatable.java new file mode 100644 index 0000000..1523cab --- /dev/null +++ b/Rapla/src/org/rapla/entities/Annotatable.java @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities; + + + +public interface Annotatable { + void setAnnotation(String key, String annotation) throws IllegalAnnotationException; + String getAnnotation(String key); + String getAnnotation(String key, String defaultValue); + String[] getAnnotationKeys(); +} + + + + diff --git a/Rapla/src/org/rapla/entities/Category.java b/Rapla/src/org/rapla/entities/Category.java new file mode 100644 index 0000000..1448e24 --- /dev/null +++ b/Rapla/src/org/rapla/entities/Category.java @@ -0,0 +1,61 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities; +import java.util.Locale; + +/** Hierarchical categorization of information. + * Categories can be used as attribute values. + * @see org.rapla.entities.dynamictype.Attribute + */ +public interface Category extends MultiLanguageNamed,Entity,RaplaObject, Annotatable +{ + final RaplaType TYPE = new RaplaType(Category.class, "category"); + + /** add a sub-category. + * This category is set as parent of the passed category.*/ + void addCategory(Category category); + /** remove a sub-category */ + void removeCategory(Category category); + /** returns all subcategories */ + Category[] getCategories(); + /** returns the subcategory with the specified key. + * null if subcategory was not found. */ + Category getCategory(String key); + /** find a sub-category in that equals the specified category. */ + Category findCategory(Category copy); + /** Returns the parent of this category or null if the category has no parent.*/ + Category getParent(); + /** returns true if the passed category is a direct child of this category */ + boolean hasCategory(Category category); + /** set the key of the category. The can be used in the getCategory() method for lookup. */ + void setKey(String key); + /** returns the key of the category */ + String getKey(); + /** returns true this category is an ancestor + * (parent or parent of parent, ...) of the specified + * category */ + boolean isAncestorOf(Category category); + /** returns the path form the rootCategory to this category. + * Path elements are the category-names in the selected locale separated + * with the / operator. If the rootCategory is null the path will be calculated + * to the top-most parent. + * Example: area51/aliencell + */ + String getPath(Category rootCategory,Locale locale); + + /** returns the number of ancestors. + * (How many Time you must call getParent() until you receive null) */ + int getDepth(); + + Category[] CATEGORY_ARRAY = new Category[0]; +} diff --git a/Rapla/src/org/rapla/entities/CategoryAnnotations.java b/Rapla/src/org/rapla/entities/CategoryAnnotations.java new file mode 100644 index 0000000..a06d4ae --- /dev/null +++ b/Rapla/src/org/rapla/entities/CategoryAnnotations.java @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities; + +public interface CategoryAnnotations{ + String KEY_NAME_COLOR="color"; +} + + + + + + + + + + + + diff --git a/Rapla/src/org/rapla/entities/DependencyException.java b/Rapla/src/org/rapla/entities/DependencyException.java new file mode 100644 index 0000000..af835c2 --- /dev/null +++ b/Rapla/src/org/rapla/entities/DependencyException.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities; +import java.util.Collection; + +import org.rapla.framework.RaplaException; + +public class DependencyException extends RaplaException { + private static final long serialVersionUID = 1L; + + Collection dependentObjects; + public DependencyException(Collection dependentObjectsNames) { + super("Dependencies exist for this objects"); + this.dependentObjects = dependentObjectsNames; + } + + public Collection getDependencies() { + return dependentObjects; + } +} + + + + + + + diff --git a/Rapla/src/org/rapla/entities/Entity.java b/Rapla/src/org/rapla/entities/Entity.java new file mode 100644 index 0000000..dc82a27 --- /dev/null +++ b/Rapla/src/org/rapla/entities/Entity.java @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities; + +public interface Entity extends RaplaObject { + /** To identities are identical, if they represent + the same object. */ + boolean isIdentical(Entity id2); + + /** returns if the instance of the entity is persisant and the cache or just a local copy. + * Persistant objects are usably not editable and are updated in a multiuser system. + * Persistant instances with the same id should therefore have the same content and + * persistant1.isIdentical(persistant2) implies persistant1 == persistant2. + * A non persistant instance has never the same reference as the persistant entity with the same id. + * persistant1.isIdentical(nonPersitant1) implies persistant1 != nonPersistant2. + */ + boolean isPersistant(); + + public static Entity[] ENTITY_ARRAY = new Entity[0]; + +} + + + + + + + diff --git a/Rapla/src/org/rapla/entities/EntityNotFoundException.java b/Rapla/src/org/rapla/entities/EntityNotFoundException.java new file mode 100644 index 0000000..45a8d40 --- /dev/null +++ b/Rapla/src/org/rapla/entities/EntityNotFoundException.java @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities; +import org.rapla.framework.RaplaException; +public class EntityNotFoundException extends RaplaException { + private static final long serialVersionUID = 1L; + + public EntityNotFoundException(String text) + { + super(text); + } +} + + + + + + + diff --git a/Rapla/src/org/rapla/entities/IllegalAnnotationException.java b/Rapla/src/org/rapla/entities/IllegalAnnotationException.java new file mode 100644 index 0000000..7a360de --- /dev/null +++ b/Rapla/src/org/rapla/entities/IllegalAnnotationException.java @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities; +import org.rapla.framework.RaplaException; + +public class IllegalAnnotationException extends RaplaException { + private static final long serialVersionUID = 1L; + + public IllegalAnnotationException(String text) + { + super(text); + } + public IllegalAnnotationException(String text, Exception ex) + { + super(text, ex); + } +} + + + + + + + diff --git a/Rapla/src/org/rapla/entities/MultiLanguageName.java b/Rapla/src/org/rapla/entities/MultiLanguageName.java new file mode 100644 index 0000000..c509e1d --- /dev/null +++ b/Rapla/src/org/rapla/entities/MultiLanguageName.java @@ -0,0 +1,106 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities; +import java.util.Collection; +import java.util.Iterator; +import java.util.TreeMap; + + + + + +/** Some entities (especially dynamic-types and attributes) + can have multiple names to allow easier reuse of created schemas or + support for multi-language-environments. + @see MultiLanguageNamed +*/ +public class MultiLanguageName implements java.io.Serializable { + // Don't forget to increase the serialVersionUID when you change the fields + private static final long serialVersionUID = 1; + private boolean readOnly; + TreeMap mapLocales = new TreeMap(); + + public MultiLanguageName(String language,String translation) { + setName(language,translation); + } + + public MultiLanguageName(String[][] data) { + for (int i=0;i newFilter = new ArrayList(Arrays.asList( filters)); + for (Iterator f=newFilter.iterator();f.hasNext();) { + ClassificationFilter filter = f.next(); + if (filter.getType().equals(type)) + { + f.remove(); + break; + } + } + classificationFilters = newFilter.toArray( ClassificationFilter.CLASSIFICATIONFILTER_ARRAY); + + } + + public ClassificationFilter[] getFilter() { + return classificationFilters; + } + + + +} + + + + + + + diff --git a/Rapla/src/org/rapla/entities/configuration/internal/CalendarModelConfigurationImpl.java b/Rapla/src/org/rapla/entities/configuration/internal/CalendarModelConfigurationImpl.java new file mode 100644 index 0000000..49767c2 --- /dev/null +++ b/Rapla/src/org/rapla/entities/configuration/internal/CalendarModelConfigurationImpl.java @@ -0,0 +1,167 @@ +/*--------------------------------------------------------------------------* +| Copyright (C) 2006 Christopher Kohlhaas | +| | +| This program is free software; you can redistribute it and/or modify | +| it under the terms of the GNU General Public License as published by the | +| Free Software Foundation. A copy of the license has been included with | +| these distribution in the COPYING file, if not go to www.fsf.org | +| | +| As a special exception, you are granted the permissions to link this | +| program with every library, which license fulfills the Open Source | +| Definition as published by the Open Source Initiative (OSI). | +*--------------------------------------------------------------------------*/ +package org.rapla.entities.configuration.internal; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; + +import org.rapla.components.util.iterator.IteratorChain; +import org.rapla.entities.EntityNotFoundException; +import org.rapla.entities.RaplaType; +import org.rapla.entities.configuration.CalendarModelConfiguration; +import org.rapla.entities.configuration.RaplaMap; +import org.rapla.entities.dynamictype.ClassificationFilter; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.storage.CannotExistWithoutTypeException; +import org.rapla.entities.storage.DynamicTypeDependant; +import org.rapla.entities.storage.EntityReferencer; +import org.rapla.entities.storage.EntityResolver; +import org.rapla.entities.storage.RefEntity; + +/** + * + * @author ckohlhaas + * @version 1.00.00 + * @since 2.03.00 + */ +public class CalendarModelConfigurationImpl extends AbstractClassifiableFilter implements CalendarModelConfiguration, Serializable,EntityReferencer, DynamicTypeDependant { + // Don't forget to increase the serialVersionUID when you change the fields + private static final long serialVersionUID = 1; + + RaplaMapImpl selected; + String title; + Date startDate; + Date endDate; + Date selectedDate; + String view; + RaplaMapImpl optionMap; + boolean defaultEventTypes; + boolean defaultResourceTypes; + + public CalendarModelConfigurationImpl( RaplaMap selected, ClassificationFilter[] filter, + boolean defaultResourceTypes, boolean defaultEventTypes,String title, Date startDate, Date endDate, Date selectedDate,String view,RaplaMap extensionMap) { + this.view = view; + this.defaultEventTypes = defaultEventTypes; + this.defaultResourceTypes = defaultResourceTypes; + this.title = title; + this.startDate = startDate; + this.endDate = endDate; + this.selectedDate = selectedDate; + super.setClassificationFilter( filter ); + this.selected = (RaplaMapImpl)selected; + if (selected == null) + { + this.selected = new RaplaMapImpl(); + } + this.optionMap = (RaplaMapImpl)extensionMap; + if (optionMap == null) + { + this.optionMap= new RaplaMapImpl(); + } + } + + public void resolveEntities( EntityResolver resolver) throws EntityNotFoundException { + super.resolveEntities( resolver ); + selected.resolveEntities( resolver ); + optionMap.resolveEntities( resolver ); + } + + + public RaplaType getRaplaType() { + return TYPE; + } + + public Date getStartDate() { + return startDate; + } + + public Date getEndDate() { + return endDate; + } + + public Date getSelectedDate() { + return selectedDate; + } + + public String getTitle() { + return title; + } + + public String getView() { + return view; + } + + public Collection getSelected() { + return selected.values(); + } + + public RaplaMap getSelectedMap() { + return selected; + } + + /** + * @see org.rapla.entities.storage.EntityReferencer#getReferences() + */ + public Iterator getReferences() { + RaplaMapImpl map = (RaplaMapImpl)selected; + return new IteratorChain(super.getReferences(), map.getReferences()); + + } + + /** + * @see org.rapla.entities.storage.EntityReferencer#isRefering(org.rapla.entities.storage.RefEntity) + */ + public boolean isRefering(RefEntity object) { + RaplaMapImpl map = (RaplaMapImpl)selected; + if ( map.isRefering( object ) ) + return true; + if ( super.isRefering(object)) { + return true; + } + return false; + } + + public boolean needsChange(DynamicType type) { + if ( super.needsChange( type )) + return true; + return selected.needsChange( type ); + } + + public void commitChange(DynamicType type) { + super.commitChange( type ); + selected.commitChange( type ); + } + + public void commitRemove(DynamicType type) throws CannotExistWithoutTypeException { + super.commitRemove( type ); + selected.commitRemove( type ); + } + + public RaplaMap getOptionMap() + { + return optionMap; + } + + public boolean isDefaultEventTypes() + { + return defaultEventTypes; + } + + public boolean isDefaultResourceTypes() + { + return defaultResourceTypes; + } + +} diff --git a/Rapla/src/org/rapla/entities/configuration/internal/PreferencesImpl.java b/Rapla/src/org/rapla/entities/configuration/internal/PreferencesImpl.java new file mode 100644 index 0000000..b764aa4 --- /dev/null +++ b/Rapla/src/org/rapla/entities/configuration/internal/PreferencesImpl.java @@ -0,0 +1,238 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.configuration.internal; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Locale; + +import org.rapla.components.util.iterator.FilterIterator; +import org.rapla.components.util.iterator.NestedIterator; +import org.rapla.entities.EntityNotFoundException; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.RaplaType; +import org.rapla.entities.configuration.Preferences; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.storage.CannotExistWithoutTypeException; +import org.rapla.entities.storage.DynamicTypeDependant; +import org.rapla.entities.storage.EntityReferencer; +import org.rapla.entities.storage.EntityResolver; +import org.rapla.entities.storage.Mementable; +import org.rapla.entities.storage.RefEntity; +import org.rapla.entities.storage.internal.SimpleEntity; + +public class PreferencesImpl extends SimpleEntity + implements + Preferences + , DynamicTypeDependant + ,Mementable + ,java.io.Serializable +{ + // Don't forget to increase the serialVersionUID when you change the fields + private static final long serialVersionUID = 1; + + HashMap map = new HashMap(); + + final public RaplaType getRaplaType() {return TYPE;} + + public void putEntry(String role,RaplaObject entry) { + checkWritable(); + if ( entry == null) + { + map.remove( role); + } + else + { + map.put( role ,entry); + } + } + + public void putEntry(String role,String entry) { + checkWritable(); + if ( entry == null) + { + map.remove( role); + } + else + { + map.put( role ,entry); + } + } + + public void resolveEntities( EntityResolver resolver) throws EntityNotFoundException { + super.resolveEntities( resolver); + for (Iterator it = getEntityReferencers();it.hasNext();){ + Object obj = it.next(); + ((EntityReferencer) obj).resolveEntities( resolver); + } + } + + public Object getEntry(String role) { + return map.get( role ); + } + + public boolean hasEntry(String role) { + return map.get( role ) != null; + } + + public String getEntryAsString(String role) { + return (String) map.get( role ); + } + + public String getEntryAsString(String role, String defaultValue) { + String value = getEntryAsString( role); + if ( value != null) + return value; + return defaultValue; + } + + public Iterator getPreferenceEntries() { + return map.keySet().iterator(); + } + + private Iterator getEntityReferencers() { + return new FilterIterator( map.values().iterator()) { + protected boolean isInIterator(Object obj) { + return obj instanceof EntityReferencer; + } + }; + } + + public Iterator getReferences() { + return new NestedIterator( getEntityReferencers() ) { + public Iterator getNestedIterator(Object obj) { + return ((EntityReferencer) obj).getReferences(); + } + }; + } + + public boolean isRefering(RefEntity object) { + for (Iterator it = getEntityReferencers();it.hasNext();) { + final EntityReferencer entityReferencer = (EntityReferencer) it.next(); + if (entityReferencer.isRefering( object)) { + return true; + } + } + return false; + } + + public boolean isEmpty() { + return map.keySet().isEmpty(); + } + + static private void copy(PreferencesImpl source,PreferencesImpl dest) { + dest.map.clear(); + for (Iterator it = source.map.keySet().iterator();it.hasNext();) + { + String role = (String) it.next(); + Object entry = source.map.get( role ); + dest.map.put( role , entry ); + } + } + + public void copy(Object obj) { + super.copy((PreferencesImpl) obj); + copy((PreferencesImpl) obj,this); + } + + public Object deepClone() { + PreferencesImpl clone = new PreferencesImpl(); + super.deepClone(clone); + copy(this,clone); + return clone; + } + + public Object clone() { + PreferencesImpl clone = new PreferencesImpl(); + super.clone(clone); + copy(this,clone); + return clone; + } + + /** + * @see org.rapla.entities.Named#getName(java.util.Locale) + */ + public String getName(Locale locale) { + StringBuffer buf = new StringBuffer(); + if ( getOwner() != null) { + buf.append( "Preferences of "); + buf.append( getOwner().getName( locale)); + } else { + buf.append( "Rapla Preferences!"); + } + return buf.toString(); + } + /* (non-Javadoc) + * @see org.rapla.entities.configuration.Preferences#getEntryAsBoolean(java.lang.String, boolean) + */ + public boolean getEntryAsBoolean(String role, boolean defaultValue) { + String entry = getEntryAsString( role); + if ( entry == null) + return defaultValue; + return Boolean.valueOf(entry).booleanValue(); + } + + /* (non-Javadoc) + * @see org.rapla.entities.configuration.Preferences#getEntryAsInteger(java.lang.String, int) + */ + public int getEntryAsInteger(String role, int defaultValue) { + String entry = getEntryAsString( role); + if ( entry == null) + return defaultValue; + return Integer.parseInt(entry); + } + + public boolean needsChange(DynamicType type) { + for (Iterator it = map.values().iterator();it.hasNext();) { + Object obj = it.next(); + if ( obj instanceof DynamicTypeDependant) { + if (((DynamicTypeDependant) obj).needsChange( type )) + return true; + } + } + return false; + } + + public void commitChange(DynamicType type) { + for (Iterator it = map.values().iterator();it.hasNext();) { + Object obj = it.next(); + if ( obj instanceof DynamicTypeDependant) { + ((DynamicTypeDependant) obj).commitChange( type ); + } + } + } + + + public void commitRemove(DynamicType type) throws CannotExistWithoutTypeException + { + for (Iterator it = map.values().iterator();it.hasNext();) { + Object obj = it.next(); + if ( obj instanceof DynamicTypeDependant) { + ((DynamicTypeDependant) obj).commitRemove( type ); + } + } + } + +} + + + + + + + + + + + + diff --git a/Rapla/src/org/rapla/entities/configuration/internal/RaplaMapImpl.java b/Rapla/src/org/rapla/entities/configuration/internal/RaplaMapImpl.java new file mode 100644 index 0000000..bc77a8e --- /dev/null +++ b/Rapla/src/org/rapla/entities/configuration/internal/RaplaMapImpl.java @@ -0,0 +1,275 @@ +/*--------------------------------------------------------------------------* +| Copyright (C) 2006 Christopher Kohlhaas | +| | +| This program is free software; you can redistribute it and/or modify | +| it under the terms of the GNU General Public License as published by the | +| Free Software Foundation. A copy of the license has been included with | +| these distribution in the COPYING file, if not go to www.fsf.org | +| | +| As a special exception, you are granted the permissions to link this | +| program with every library, which license fulfills the Open Source | +| Definition as published by the Open Source Initiative (OSI). | +*--------------------------------------------------------------------------*/ +package org.rapla.entities.configuration.internal; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +import org.rapla.components.util.Assert; +import org.rapla.components.util.iterator.FilterIterator; +import org.rapla.components.util.iterator.IteratorChain; +import org.rapla.components.util.iterator.NestedIterator; +import org.rapla.entities.EntityNotFoundException; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.RaplaType; +import org.rapla.entities.ReadOnlyException; +import org.rapla.entities.configuration.RaplaMap; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.storage.CannotExistWithoutTypeException; +import org.rapla.entities.storage.DynamicTypeDependant; +import org.rapla.entities.storage.RefEntity; +import org.rapla.entities.storage.EntityReferencer; +import org.rapla.entities.storage.EntityResolver; +import org.rapla.entities.storage.internal.ReferenceHandler; + +public class RaplaMapImpl implements RaplaMap,RaplaObject, Serializable,EntityReferencer, DynamicTypeDependant { + private static final long serialVersionUID = 1; + + private Map map; + private ReferenceHandler referenceHandler = new ReferenceHandler(); + private Map childMap = new HashMap(); + + public RaplaMapImpl() { + this.map = new TreeMap(); + } + + public RaplaMapImpl( Collection list) { + this( makeMap(list) ); + } + + private static Map makeMap(Collection list) { + Map map = new TreeMap(); + int key = 0; + for ( Iterator it = list.iterator();it.hasNext();) { + map.put( new String( String.valueOf(key++)), it.next()); + } + return map; + } + + public RaplaMapImpl( Map map) { + this.map = Collections.unmodifiableMap(map); + for ( Iterator it = this.map.keySet().iterator();it.hasNext();) { + String key = (String)it.next(); + Object o = this.map.get(key ); + if ( ! (o instanceof RaplaObject ) && !(o instanceof String) ) + { + throw new IllegalArgumentException("Only map entries of type RaplaObject are allowed."); + } + if ( o instanceof RefEntity) { + getReferenceHandler().put( key, o); + } else { + childMap.put( key, o ); + } + } + } + + public Map getChildMap() { + return childMap; + } + + /** This method is only used in storage operations, please dont use it from outside*/ + public void putString(String key, String value) + { + childMap.put( key, value); + } + + public Iterator getReferences() { + Iterator refIt = new NestedIterator( getEntityReferencers()) { + public Iterator getNestedIterator(Object obj) { + return ((EntityReferencer) obj).getReferences(); + } + }; + return new IteratorChain( refIt, getReferenceHandler().getReferences()); + } + + private Iterator getEntityReferencers() { + return new FilterIterator( map.values().iterator()) { + protected boolean isInIterator(Object obj) { + return obj instanceof EntityReferencer; + } + }; + } + + + public boolean isRefering(RefEntity object) { + if ( getReferenceHandler().isRefering( object )) { + return true; + } + for (Iterator it = getEntityReferencers();it.hasNext();) { + if (((EntityReferencer) it.next()).isRefering( object)) { + return true; + } + } + return false; + } + /* + public Iterator getReferences() { + return getReferenceHandler().getReferences(); + } + + public boolean isRefering(Entity entity) { + return getReferenceHandler().isRefering( entity); + }*/ + + public void resolveEntities( EntityResolver resolver) throws EntityNotFoundException { + referenceHandler.resolveEntities( resolver ); + Map map = new HashMap(); + for ( Iterator it = childMap.keySet().iterator();it.hasNext();) { + String key = (String)it.next(); + Object entity = childMap.get(key) ; + if ( entity instanceof EntityReferencer) { + ((EntityReferencer) entity).resolveEntities( resolver); + } + map.put( key, entity); + } + for ( Iterator it = getReferenceHandler().getReferenceKeys();it.hasNext();) { + String key = (String)it.next(); + RefEntity entity = (RefEntity)getReferenceHandler().get(key) ; + Assert.notNull( entity ); + map.put( key, entity); + } + this.map = Collections.unmodifiableMap( map ); + } + + public ReferenceHandler getReferenceHandler() { + return referenceHandler; + } + + public RaplaType getRaplaType() { + return TYPE; + } + + public boolean needsChange(DynamicType type) { + for (Iterator it = childMap.values().iterator();it.hasNext();) { + Object obj = it.next(); + if ( obj instanceof DynamicTypeDependant) { + if (((DynamicTypeDependant) obj).needsChange( type )) + return true; + } + } + return false; + } + + public void commitChange(DynamicType type) { + for (Iterator it = childMap.values().iterator();it.hasNext();) { + Object obj = it.next(); + if ( obj instanceof DynamicTypeDependant) { + ((DynamicTypeDependant) obj).commitChange( type ); + } + } + } + + public void commitRemove(DynamicType type) throws CannotExistWithoutTypeException { + for (Iterator it = childMap.values().iterator();it.hasNext();) { + Object obj = it.next(); + if ( obj instanceof DynamicTypeDependant) { + ((DynamicTypeDependant) obj).commitRemove( type ); + } + } + } + /** + * @see java.util.Map#size() + */ + public int size() { + return map.size(); + } + + /** + * @see java.util.Map#isEmpty() + */ + public boolean isEmpty() { + return map.isEmpty(); + } + + /** + * @see java.util.Map#containsKey(java.lang.Object) + */ + public boolean containsKey(Object key) { + return map.containsKey( key); + } + + /** + * @see java.util.Map#containsValue(java.lang.Object) + */ + public boolean containsValue(Object key) { + return map.containsValue( key); + } + + /** + * @see java.util.Map#get(java.lang.Object) + */ + public Object get(Object key) { + return map.get(key); + } + + /** + * @see java.util.Map#put(java.lang.Object, java.lang.Object) + */ + public Object put(Object arg0, Object arg1) { + throw createReadOnlyException(); + } + + /** + * @see java.util.Map#remove(java.lang.Object) + */ + public Object remove(Object arg0) { + throw createReadOnlyException(); + } + + /** + * @see java.util.Map#putAll(java.util.Map) + */ + public void putAll(Map arg0) { + throw createReadOnlyException(); + } + + /** + * @see java.util.Map#clear() + */ + public void clear() { + throw createReadOnlyException(); + } + + ReadOnlyException createReadOnlyException() { + return new ReadOnlyException("RaplaMap is readonly you must create a new Object"); + } + /** + * @see java.util.Map#keySet() + */ + public Set keySet() { + return map.keySet(); + } + + /** + * @see java.util.Map#values() + */ + public Collection values() { + return map.values(); + } + + /** + * @see java.util.Map#entrySet() + */ + public Set entrySet() { + return map.entrySet(); + } + + + +} diff --git a/Rapla/src/org/rapla/entities/domain/Allocatable.java b/Rapla/src/org/rapla/entities/domain/Allocatable.java new file mode 100644 index 0000000..9821ca1 --- /dev/null +++ b/Rapla/src/org/rapla/entities/domain/Allocatable.java @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.domain; + +import java.util.Date; + +import org.rapla.entities.Entity; +import org.rapla.entities.Named; +import org.rapla.entities.Ownable; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.RaplaType; +import org.rapla.entities.Timestamp; +import org.rapla.entities.User; +import org.rapla.entities.dynamictype.Classifiable; + +/** Objects that implement allocatable can be allocated by reservations. + @see Reservation + */ +public interface Allocatable extends Entity,RaplaObject,Named,Classifiable,Ownable,Timestamp { + final RaplaType TYPE = new RaplaType(Allocatable.class, "resource"); + + /** Conflicts for this allocatable should be ignored, if this flag is enabled.*/ + void setHoldBackConflicts(boolean enable); + boolean isHoldBackConflicts(); + /** Static empty dummy Array. Mainly for using the toArray() method of the collection interface */ + Allocatable[] ALLOCATABLE_ARRAY = new Allocatable[0]; + void addPermission( Permission permission ); + boolean removePermission( Permission permission ); + + /** returns if the user has the permission to allocate the resource in the given + time. It returns true if for at least one permission calling + permission.covers() + and permission.affectsUser yields true. + */ + boolean canAllocate( User user, Date start, Date end, Date today ); + /** returns if the user has the permission to create a conflict for the resource.*/ + boolean canCreateConflicts( User user ); + /** returns if the user has the permission to modify the allocatable (and also its permission-table).*/ + boolean canModify( User user ); + /** returns if the user has the permission to read the allocations of this resource.*/ + boolean canRead( User user ); + + Permission[] getPermissions(); + Permission newPermission(); + boolean isPerson(); +} + + + + + + + + + + + + diff --git a/Rapla/src/org/rapla/entities/domain/Appointment.java b/Rapla/src/org/rapla/entities/domain/Appointment.java new file mode 100644 index 0000000..b883171 --- /dev/null +++ b/Rapla/src/org/rapla/entities/domain/Appointment.java @@ -0,0 +1,126 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.domain; +import java.util.Collection; +import java.util.Date; + +import org.rapla.entities.Entity; +import org.rapla.entities.Ownable; +import org.rapla.entities.RaplaType; +/** The basic building blocks of reservations. + @see Reservation + @see Repeating*/ +public interface Appointment extends Entity, Comparable,Ownable { + final RaplaType TYPE = new RaplaType(Appointment.class, "appointment" ); + Date getStart(); + Date getEnd(); + /**

    + If no repeating is set this method will return the same + as getEnd(). +

    +

    + If the repeating has no end the method will return Null. + Oterwise the maximum of getEnd() and repeating.getEnd() will be returned. +

    + @see #getEnd + @see Repeating + */ + Date getMaxEnd(); + + /** returns the reservation that owns the appointment. + @return the reservation that owns the appointment or null if + the appointment does not belong to a reservation. + */ + Reservation getReservation(); + + /** @return null if the appointment has no repeating + */ + Repeating getRepeating(); + + /** Enables repeating for this appointment. + Use getRepeating() to manipulate the repeating. */ + void setRepeatingEnabled(boolean enableRepeating); + + /** returns if the appointment has a repeating */ + boolean isRepeatingEnabled(); + + /** Changes the start- and end-time of the appointment. + */ + void move(Date start,Date end); + /** Moves the start-time of the appointment. + The end-time will be adjusted accordingly to the duration of the appointment. + */ + void move(Date newStart); + + /** Tests two appointments for overlap. + Important: Times like 13:00-14:00 and 14:00-15:00 do not overlap + The overlap-relation must be symmetric a1.overlaps(a2) == a2.overlaps(a1) + @return true if the appointment overlaps the given appointment. + */ + boolean overlaps(Appointment appointment); + + /** Test for overlap with a period. + * same as overlaps( start, end, true) + * @return true if the overlaps with the given period. + */ + boolean overlaps(Date start,Date end); + + /** Test for overlap with a period. You can specify if exceptions should be considered in the overlapping algorithm. + @return true if the overlaps with the given period. + */ + boolean overlaps(Date start,Date end, boolean considerExceptions); + + /** Returns if the exceptions, repeatings, start and end dates of the Appoinemnts are the same.*/ + boolean matches(Appointment appointment); + + /** @param maxDate must not be null, specifies the last date that should be searched + + returns the first date at which the two appointments differ (dates after maxDate will not be calculated) + */ + Date getFirstDifference( Appointment a2, Date maxDate ); + + /** @param maxDate must not be null, specifies the last date that should be searched + + returns the last date at which the two appointments differ. (dates after maxDate will not be calculated)*/ + Date getLastDifference( Appointment a2, Date maxDate ); + + /** this method will be used for future enhancements */ + boolean isWholeDaysSet(); + + /** this method will be used for future enhancements */ + void setWholeDays(boolean enable); + + /** adds all Appointment-blocks in the given period to the appointmentBlockArray. + A block is in the period if its starttimestart. Exceptions are excluded, i.e. there is no block on an exception date. + */ + void createBlocks(Date start,Date end,Collection blocks); + + /** adds all Appointment-blocks in the given period to the appointmentBlockArray. + A block is in the period if its starttimestart. You can specify if exceptions should be excluded. + */ + void createBlocks(Date start,Date end,Collection blocks, boolean excludeExceptions); + + final Appointment[] EMPTY_ARRAY = new Appointment[0]; +} + + + + + + + + + + + + diff --git a/Rapla/src/org/rapla/entities/domain/AppointmentBlock.java b/Rapla/src/org/rapla/entities/domain/AppointmentBlock.java new file mode 100644 index 0000000..44838be --- /dev/null +++ b/Rapla/src/org/rapla/entities/domain/AppointmentBlock.java @@ -0,0 +1,98 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2011 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.domain; + + + +/** + * This class represents a time block of an appointment and is currently only used for performance + * reasons + * + * @since Rapla 1.4 + */ +public class AppointmentBlock implements Comparable +{ + long start; + long end; + boolean isException; + private Appointment appointment; + + /** + * Basic constructor + */ + public AppointmentBlock(long start, long end, Appointment appointment, boolean isException) + { + this.start = start; + this.end = end; + this.appointment = appointment; + this.isException = isException; + } + + /** + * Returns the start date of this block + * + * @return Date + */ + public long getStart() + { + return start; + } + + /** + * Returns the end date of this block + * + * @return Date + */ + public long getEnd() + { + return end; + } + + /** + * Returns if the block is an exception from the appointment rule + * + */ + public boolean isException() + { + return isException; + } + /** + * Returns the appointment to which this block belongs + * + * @return Appointment + */ + public Appointment getAppointment() + { + return appointment; + } + + /** + * This method is used to compare two appointment blocks by their start dates + */ + public int compareTo(AppointmentBlock other) + { + if (other.start > start) + return -1; + if (other.start < start) + return 1; + if (other.end > end) + return 1; + if (other.end < end) + return -1; + if ( other == this) + { + return 0; + } + return appointment.compareTo(other.appointment); + } +} diff --git a/Rapla/src/org/rapla/entities/domain/AppointmentFormater.java b/Rapla/src/org/rapla/entities/domain/AppointmentFormater.java new file mode 100644 index 0000000..b96c6a3 --- /dev/null +++ b/Rapla/src/org/rapla/entities/domain/AppointmentFormater.java @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.domain; +import java.util.List; + + + +/** Formats the different appointment outputs. */ +public interface AppointmentFormater +{ + public final static String ROLE = AppointmentFormater.class.getName(); + String getShortSummary(Appointment appointment); + String getVeryShortSummary(Appointment appointment); + String getSummary( Appointment a ); + String getSummary( Repeating r , List periods); + String getSummary( Repeating r ); + String getExceptionSummary( Repeating r ); +} diff --git a/Rapla/src/org/rapla/entities/domain/AppointmentStartComparator.java b/Rapla/src/org/rapla/entities/domain/AppointmentStartComparator.java new file mode 100644 index 0000000..86d6ec5 --- /dev/null +++ b/Rapla/src/org/rapla/entities/domain/AppointmentStartComparator.java @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.domain; +import java.util.*; + + +public class AppointmentStartComparator implements Comparator { + public int compare(Object o1,Object o2) { + if (o1 instanceof Date) + return compare((Date) o1, o2); + if (o2 instanceof Date) + return -1 * compare((Date) o2, o1 ); + + if ( o1.equals(o2)) return 0; + Appointment a1 = (Appointment) o1; + Appointment a2 = (Appointment) o2; + if (a1.getStart().before(a2.getStart())) + return -1; + if (a1.getStart().after(a2.getStart())) + return 1; + + return (o1.hashCode() < o2.hashCode()) ? -1 : 1; + } + public int compare(Date d1,Object o2) { + if (o2 instanceof Date) + return d1.compareTo((Date) o2); + + Appointment a2 = (Appointment) o2; + if (d1.before(a2.getStart())) { + //System.out.println(a2 + ">" + d1); + return -1; + } + if (d1.after(a2.getStart())) { + // System.out.println(a2 + "<" + d1); + return 1; + } + + // If appointment.getStart().equals(date) + // set the appointment before the date + return 1; + } + +} + + \ No newline at end of file diff --git a/Rapla/src/org/rapla/entities/domain/Period.java b/Rapla/src/org/rapla/entities/domain/Period.java new file mode 100644 index 0000000..4f7dc27 --- /dev/null +++ b/Rapla/src/org/rapla/entities/domain/Period.java @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.domain; + +import java.util.*; + +import org.rapla.entities.Entity; +import org.rapla.entities.Named; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.RaplaType; +/** +Most universities and schools are planning for fixed periods/terms +rather than arbitrary dates. Rapla provides support for this periods. +*/ +public interface Period extends Entity,RaplaObject,Comparable,Named { + final RaplaType TYPE = new RaplaType(Period.class, "period"); + + Date getStart(); + Date getEnd(); + int getWeeks(); + String getName(); + + void setStart(Date start); + void setEnd(Date end); + void setName(String name); + + boolean contains(Date date); + /** returns the week of the specified date relative to the period. + @throws NoSuchElementException if the period doesn't contain the date + */ + int weekOf(Date date); + String toString(); + + /** compares the period to a date. + Compares endDates with date. + If dates are equal 1 will be returned: + The date is before the endDate and therefore in the period. + */ + int compareTo(Date date); + /** compares the period to another period. + First compares startDates and if they are equal + compares endDates. If endDates are equal the hashValues, names + or some other unique value should be compared to ensure: + equals() is true <=> compareTo() returns 0; + */ + int compareTo(Period period); + + /** compares the period to another period or a date object. + @see #compareTo(Date) + @see #compareTo(Period) + */ + int compareTo(Object object); + + public static Period[] PERIOD_ARRAY = new Period[0]; +} + + + + + + + + diff --git a/Rapla/src/org/rapla/entities/domain/Permission.java b/Rapla/src/org/rapla/entities/domain/Permission.java new file mode 100644 index 0000000..f8bab3b --- /dev/null +++ b/Rapla/src/org/rapla/entities/domain/Permission.java @@ -0,0 +1,98 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org . | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.entities.domain; + +import java.util.Date; +import org.rapla.components.util.SmallIntMap; +import org.rapla.entities.Category; +import org.rapla.entities.User; + +/** New feature to restrict the access to allocatables on a per user/group basis. + * Specify absolute and relative booking-timeframes for each resource + * per user/group. You can, for example, prevent modifing appointments + * in the past, by setting the relative start-time to 0. +*/ +public interface Permission +{ + String GROUP_CATEGORY_KEY = "user-groups"; + String GROUP_REGISTERER_KEY = "registerer"; + String GROUP_MODIFY_PREFERENCES_KEY = "modify-preferences"; + String GROUP_CAN_READ_EVENTS_FROM_OTHERS = "read-events-from-others"; + int DENIED = 0; + int READ = 1; + int ALLOCATE =2; + int ALLOCATE_CONFLICTS = 3; + int ADMIN = 4; + + String[] ACCESS_LEVEL_NAMES = {"denied","read","allocate","allocate-conflicts","admin"}; + final static int[] ACCESS_LEVEL_TYPES = {DENIED,READ,ALLOCATE,ALLOCATE_CONFLICTS,ADMIN}; + + final static SmallIntMap ACCESS_LEVEL_NAMEMAP = new SmallIntMap(ACCESS_LEVEL_TYPES,ACCESS_LEVEL_NAMES); + + /** sets a user for the permission. + * If a user is not null, the group will be set to null. + */ + void setUser(User user); + User getUser(); + + /** sets a group for the permission. + * If the group ist not null, the user will be set to null. + */ + void setGroup(Category category); + Category getGroup(); + + /** set the minumum number of days a resource must be booked in advance. If days is null, a reservation can be booked anytime. + * Example: If you set days to 7, a resource must be allocated 7 days before its acutual use */ + void setMinAdvance(Long days); + Long getMinAdvance(); + + /** set the maximum number of days a reservation can be booked in advance. If days is null, a reservation can be booked anytime. + * Example: If you set days to 7, a resource can only be for the next 7 days. */ + void setMaxAdvance(Long days); + Long getMaxAdvance(); + + /** sets the starttime of the period in which the resource can be booked*/ + void setStart(Date end); + Date getStart(); + + /** sets the endtime of the period in which the resource can be booked*/ + void setEnd(Date end); + Date getEnd(); + + /** Convenince Method: returns the last date for which the resource can be booked */ + Date getMaxAllowed(Date today); + /** Convenince Method: returns the first date for which the resource can be booked */ + Date getMinAllowed(Date today); + + /** returns if the user or a group of the user is affected by the permission. + * Groups are hierarchical. If the user belongs + * to a subgroup of the permission-group the user is also + * affected by the permission. + */ + boolean affectsUser( User user); + + /** returns if the permission covers the interval specified by the start and end date. + * The current date must be passed to calculate the permissable + * interval from minAdvance and maxAdvance. + */ + boolean covers( Date start, Date end, Date currentDate); + + void setAccessLevel(int access); + int getAccessLevel(); + + /** Static empty dummy Array. + * Mainly for using the toArray() method of the collection interface */ + Permission[] PERMISSION_ARRAY = new Permission[0]; + +} diff --git a/Rapla/src/org/rapla/entities/domain/Repeating.java b/Rapla/src/org/rapla/entities/domain/Repeating.java new file mode 100644 index 0000000..db78cf3 --- /dev/null +++ b/Rapla/src/org/rapla/entities/domain/Repeating.java @@ -0,0 +1,86 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.domain; +import java.util.Date; + +/** Encapsulates the repeating rule for an appointment. + @see Appointment*/ +public interface Repeating { + RepeatingType DAILY = RepeatingType.DAILY; + RepeatingType WEEKLY = RepeatingType.WEEKLY; + RepeatingType MONTHLY = RepeatingType.MONTHLY; + RepeatingType YEARLY = RepeatingType.YEARLY; + + void setInterval(int interval); + /** returns the number of intervals between two repeatings. + * That are in the selected context: + *
  • For weekly repeatings: Number of weeks.
  • + *
  • For dayly repeatings: Number of days.
  • + */ + int getInterval(); + /** The value returned depends which method was called last. + * If setNumber() has been called with a parameter + * >=0 fixedNumber() will return true. If + * setEnd() has been called + * fixedNumber() will return false. + * @see #setEnd + * @see #setNumber + */ + boolean isFixedNumber(); + /** Set the end of repeating. + * If this value is set to null and the + * number is set to -1 the appointment will repeat + * forever. + * @param end If not null isFixedNumber will return true. + * @see #setNumber + */ + void setEnd(Date end); + /* @return end of repeating or null if unlimited */ + Date getEnd(); + /** Set a fixed number of repeating. + * If this value is set to -1 + * and the repeating end is set to null the appointment will + * repeat forever. + * @param number If >=0 isFixedNumber will return true. + * @see #setEnd + * @see #isFixedNumber + */ + void setNumber(int number); + /* @return number of repeating or -1 if it repeats forever. */ + int getNumber(); + /* daily,weekly, monthly */ + RepeatingType getType(); + /* daily,weekly, monthly */ + void setType(RepeatingType type); + /* exceptions for this repeating. */ + Date[] getExceptions(); + boolean hasExceptions(); + + boolean isWeekly(); + boolean isDaily(); + boolean isMonthly(); + boolean isYearly(); + void addException(Date date); + void removeException(Date date); + void clearExceptions(); + + /** returns the appointment of this repeating. + @see Appointment + */ + Appointment getAppointment(); + + /* tests if an exception is added for the given date */ + boolean isException(long date); + Object clone(); +} + diff --git a/Rapla/src/org/rapla/entities/domain/RepeatingEnding.java b/Rapla/src/org/rapla/entities/domain/RepeatingEnding.java new file mode 100644 index 0000000..0ca0fa9 --- /dev/null +++ b/Rapla/src/org/rapla/entities/domain/RepeatingEnding.java @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.domain; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +/**Currently Rapla supports the following types: +
  • weekly
  • +
  • daily
  • + */ +public class RepeatingEnding implements Serializable { + // Don't forget to increase the serialVersionUID when you change the fields + private static final long serialVersionUID = 1; + + private String type; + + final static public RepeatingEnding END_DATE = new RepeatingEnding("repeating.end_date"); + final static public RepeatingEnding N_TIMES = new RepeatingEnding("repeating.n_times"); + final static public RepeatingEnding FOREVEVER = new RepeatingEnding("repeating.forever"); + + private static Map types; + + private RepeatingEnding(String type) { + this.type = type; + if (types == null) { + types = new HashMap(); + } + types.put( type, this); + } + + public boolean is(RepeatingEnding other) { + if ( other == null) + return false; + return type.equals( other.type); + } + + public static RepeatingEnding findForString(String string ) { + RepeatingEnding type = (RepeatingEnding) types.get( string ); + return type; + } + + public String toString() { + return type; + } + + public boolean equals( Object other) { + if ( !(other instanceof RepeatingEnding)) + return false; + return is( (RepeatingEnding)other); + } + + public int hashCode() { + return type.hashCode(); + } +} + + + + diff --git a/Rapla/src/org/rapla/entities/domain/RepeatingType.java b/Rapla/src/org/rapla/entities/domain/RepeatingType.java new file mode 100644 index 0000000..471dba6 --- /dev/null +++ b/Rapla/src/org/rapla/entities/domain/RepeatingType.java @@ -0,0 +1,72 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.domain; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +/**Currently Rapla supports the following types: +
  • weekly
  • +
  • daily
  • + */ +public class RepeatingType implements Serializable { + // Don't forget to increase the serialVersionUID when you change the fields + private static final long serialVersionUID = 1; + + private String type; + + final static public RepeatingType WEEKLY = new RepeatingType("weekly"); + final static public RepeatingType DAILY = new RepeatingType("daily"); + final static public RepeatingType MONTHLY = new RepeatingType("monthly"); + final static public RepeatingType YEARLY = new RepeatingType("yearly"); + + private static Map types; + + private RepeatingType(String type) { + this.type = type; + if (types == null) { + types = new HashMap(); + } + types.put( type, this); + } + + public boolean is(RepeatingType other) { + if ( other == null) + return false; + return type.equals( other.type); + } + + public static RepeatingType findForString(String string ) { + RepeatingType type = (RepeatingType) types.get( string ); + return type; + } + + public String toString() { + return type; + } + + public boolean equals( Object other) { + if ( !(other instanceof RepeatingType)) + return false; + return is( (RepeatingType)other); + } + + public int hashCode() { + return type.hashCode(); + } +} + + + + diff --git a/Rapla/src/org/rapla/entities/domain/Reservation.java b/Rapla/src/org/rapla/entities/domain/Reservation.java new file mode 100644 index 0000000..d4a425b --- /dev/null +++ b/Rapla/src/org/rapla/entities/domain/Reservation.java @@ -0,0 +1,100 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org . | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.entities.domain; + +import java.util.Date; +import org.rapla.entities.Entity; +import org.rapla.entities.Named; +import org.rapla.entities.Ownable; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.RaplaType; +import org.rapla.entities.Timestamp; +import org.rapla.entities.dynamictype.Classifiable; + +/** The Reservation interface is the central interface of + * Rapla. Objects implementing this interface are the courses or + * events to be scheduled. A Reservation consist + * of a group of appointments and a set of allocated + * resources (rooms, notebooks, ..) and persons. + * By default all resources and persons are allocated on every appointment. + * If you want to associate allocatable objects to special appointments + * use Restrictions. + * + * @see Classifiable + * @see Appointment + * @see Allocatable + */ +public interface Reservation extends Entity,RaplaObject,Classifiable,Named,Ownable,Timestamp +{ + final RaplaType TYPE = new RaplaType(Reservation.class,"reservation"); + + public final int MAX_RESERVATION_LENGTH = 100; + + void addAppointment(Appointment appointment); + void removeAppointment(Appointment appointment); + /** returns all appointments that are part off the reservation.*/ + Appointment[] getAppointments(); + /** Restrict an allocation to one ore more appointments. + * By default all objects of a reservation are allocated + * on every appointment. Restrictions allow to model + * relations between allocatables and appointments. + * A resource or person is restricted if its connected to + * one or more appointments instead the whole reservation. + */ + void setRestriction(Allocatable alloc,Appointment[] appointments); + Appointment[] getRestriction(Allocatable alloc); + + /** returns all appointments for an allocatable. This are either the restrictions, if there are any or all appointments + * @see #getRestriction + * @see #getAppointments*/ + Appointment[] getAppointmentsFor(Allocatable alloc); + + /** find an appointment in the reservation that equals the specified appointment. This is usefull if you have the + * persistant version of an appointment and want to discover the editable appointment in the working copy of a reservation */ + Appointment findAppointment(Appointment appointment); + + void addAllocatable(Allocatable allocatable); + void removeAllocatable(Allocatable allocatable); + Allocatable[] getAllocatables(); + + Allocatable[] getRestrictedAllocatables(Appointment appointment); + + /** returns if an the reservation has allocated the specified object. */ + boolean hasAllocated(Allocatable alloc); + + /** returns if the allocatable is reserved on the specified appointment. */ + boolean hasAllocated(Allocatable alloc,Appointment appointment); + + /** returns all persons that are associated with the reservation. + Need not necessarily to be users of the System. + */ + Allocatable[] getPersons(); + + /** returns all resources that are associated with the reservation. */ + Allocatable[] getResources(); + + public static final Reservation[] RESERVATION_ARRAY = new Reservation[0]; + + /** @deprecated This are only used during the integration period of the occupation plugin. Will be removed in the next version*/ + void setSelectedSlotDate(Date start); + /** @deprecated This are only used during the integration period of the occupation plugin. Will be removed in the next version*/ + Date getSelectedSlotDate(); +} + + + + + + + diff --git a/Rapla/src/org/rapla/entities/domain/ReservationHelper.java b/Rapla/src/org/rapla/entities/domain/ReservationHelper.java new file mode 100644 index 0000000..fb42967 --- /dev/null +++ b/Rapla/src/org/rapla/entities/domain/ReservationHelper.java @@ -0,0 +1,62 @@ +package org.rapla.entities.domain; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.rapla.facade.PeriodModel; + +public class ReservationHelper +{ + + static public void makeRepeatingForPeriod(PeriodModel model, Appointment appointment, RepeatingType repeatingType, int repeatings) { + appointment.setRepeatingEnabled(true); + Repeating repeating = appointment.getRepeating(); + repeating.setType( repeatingType ); + Period period = model.getNearestPeriodForStartDate( appointment.getStart()); + if ( period != null && repeatings <=1) { + repeating.setEnd(period.getEnd()); + } else { + repeating.setNumber( repeatings ); + } + } + + /** find the first visible reservation*/ + static public Date findFirst( List reservationList) { + Date firstStart = null; + Iterator it = reservationList.iterator(); + while (it.hasNext()) { + Appointment[] appointments = ((Reservation) it.next()).getAppointments(); + for (int i=0;i blocks = new ArrayList(); + appointments[i].createBlocks( start, firstStart, blocks ); + for (AppointmentBlock block: blocks) { + if (block.getStart()" + d1); + return -1; + } + if (d1.after(getStart(r2))) { + // System.out.println(a2 + "<" + d1); + return 1; + } + + // If appointment.getStart().equals(date) + // set the appointment before the date + return 1; + } + +} + diff --git a/Rapla/src/org/rapla/entities/domain/internal/AllocatableImpl.java b/Rapla/src/org/rapla/entities/domain/internal/AllocatableImpl.java new file mode 100644 index 0000000..dc9ff27 --- /dev/null +++ b/Rapla/src/org/rapla/entities/domain/internal/AllocatableImpl.java @@ -0,0 +1,299 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.domain.internal; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.Locale; + +import org.rapla.components.util.iterator.IteratorChain; +import org.rapla.components.util.iterator.NestedIterator; +import org.rapla.entities.EntityNotFoundException; +import org.rapla.entities.Ownable; +import org.rapla.entities.RaplaType; +import org.rapla.entities.User; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Permission; +import org.rapla.entities.dynamictype.Classification; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.dynamictype.DynamicTypeAnnotations; +import org.rapla.entities.dynamictype.internal.ClassificationImpl; +import org.rapla.entities.internal.ModifiableTimestamp; +import org.rapla.entities.storage.CannotExistWithoutTypeException; +import org.rapla.entities.storage.DynamicTypeDependant; +import org.rapla.entities.storage.EntityResolver; +import org.rapla.entities.storage.Mementable; +import org.rapla.entities.storage.RefEntity; +import org.rapla.entities.storage.internal.SimpleEntity; + +public class AllocatableImpl extends SimpleEntity implements Allocatable,Mementable,java.io.Serializable, DynamicTypeDependant, ModifiableTimestamp, Ownable { + // Don't forget to increase the serialVersionUID when you change the fields + private static final long serialVersionUID = 1; + + private ClassificationImpl classification; + private boolean holdBackConflicts; + private ArrayList permissions = new ArrayList(); + private Date lastChanged; + private Date createDate; + + transient private boolean permissionArrayUpToDate = false; + transient private Permission[] permissionArray; + + AllocatableImpl() { + this (null, null); + } + + public AllocatableImpl(Date createDate, Date lastChanged ) { + this.createDate = createDate; + this.lastChanged = lastChanged; + if (lastChanged == null) + this.lastChanged = this.createDate; + } + + + + public void resolveEntities( EntityResolver resolver) throws EntityNotFoundException { + super.resolveEntities( resolver); + classification.resolveEntities( resolver); + for (Iterator it = permissions.iterator();it.hasNext();) + { + ((PermissionImpl) it.next()).resolveEntities( resolver); + } + } + + public void setReadOnly(boolean enable) { + super.setReadOnly( enable ); + classification.setReadOnly( enable ); + Iterator it = permissions.iterator(); + while (it.hasNext()) { + ((PermissionImpl) it.next()).setReadOnly(enable); + } + } + + public Date getLastChangeTime() { + return lastChanged; + } + + public Date getCreateTime() { + return createDate; + } + + public void setLastChanged(Date date) { + lastChanged = date; + } + + public RaplaType getRaplaType() { + return TYPE; + } + + // Implementation of interface classifiable + public Classification getClassification() { return classification; } + public void setClassification(Classification classification) { + this.classification = (ClassificationImpl) classification; + } + + public void setHoldBackConflicts(boolean enable) { + holdBackConflicts = enable; + } + public boolean isHoldBackConflicts() { + return holdBackConflicts; + } + + public String getName(Locale locale) { + Classification c = getClassification(); + if (c == null) + return ""; + return c.getName(locale); + } + + public boolean isPerson() { + final Classification classification2 = getClassification(); + if ( classification2 == null) + { + return false; + } + final String annotation = classification2.getType().getAnnotation(DynamicTypeAnnotations.KEY_CLASSIFICATION_TYPE); + return annotation != null && annotation.equals( DynamicTypeAnnotations.VALUE_PERSON_CLASSIFICATION); + } + + private boolean hasAccess( User user, int accessLevel ) { + Permission[] permissions = getPermissions(); + if ( user == null || user.isAdmin() ) + return true; + + for ( int i = 0; i < permissions.length; i++ ) { + Permission p = permissions[i]; + if ( p.affectsUser(user) + && (p.getAccessLevel() >= accessLevel )) + { + return true; + } + } + return false; + } + + public boolean canCreateConflicts( User user ) { + return hasAccess( user, Permission.ALLOCATE_CONFLICTS); + } + + public boolean canModify(User user) { + return hasAccess( user, Permission.ADMIN); + } + + public boolean canRead(User user) { + return hasAccess( user, Permission.READ ); + } + + public boolean canAllocate( User user, Date start, Date end, Date today ) { + if (user == null) + return false; + + Permission[] permissions = getPermissions(); + if ( user.isAdmin() ) + return true; + + for ( int i = 0; i < permissions.length; i++ ) { + Permission p = permissions[i]; + if ( p.affectsUser(user) + && p.getAccessLevel() >= Permission.ALLOCATE + && (p.getAccessLevel() == Permission.ADMIN || p.covers( start, end, today ) )) + { + return true; + } + } + return false; + } + + public void addPermission(Permission permission) { + checkWritable(); + permissionArrayUpToDate = false; + permissions.add(permission); + } + + public boolean removePermission(Permission permission) { + checkWritable(); + permissionArrayUpToDate = false; + return permissions.remove(permission); + } + + public Permission newPermission() { + return new PermissionImpl(); + } + + public Permission[] getPermissions() { + updatePermissionArray(); + return permissionArray; + } + + private void updatePermissionArray() { + if ( permissionArrayUpToDate ) + return; + + permissionArray = (Permission[])permissions.toArray(Permission.PERMISSION_ARRAY); + permissionArrayUpToDate = true; + } + + public Iterator getReferences() { + return new IteratorChain + ( + classification.getReferences() + ,new NestedIterator( permissions.iterator() ) { + public Iterator getNestedIterator(Object obj) { + return ((PermissionImpl)obj).getReferences(); + } + } + ); + } + + public boolean needsChange(DynamicType type) { + return classification.needsChange( type ); + } + + public void commitChange(DynamicType type) { + classification.commitChange( type ); + } + + public void commitRemove(DynamicType type) throws CannotExistWithoutTypeException + { + classification.commitRemove(type); + } + + public boolean isRefering(RefEntity object) { + if (super.isRefering(object)) + return true; + if (classification.isRefering(object)) + return true; + Permission[] permissions = getPermissions(); + for ( int i = 0; i < permissions.length; i++ ) { + if ( ((PermissionImpl) permissions[i]).isRefering( object ) ) + return true; + } + return false; + } + + static private void copy(AllocatableImpl source,AllocatableImpl dest) { + dest.permissionArrayUpToDate = false; + dest.classification = (ClassificationImpl) source.classification.clone(); + + dest.permissions.clear(); + Iterator it = source.permissions.iterator(); + while ( it.hasNext() ) { + dest.permissions.add(((PermissionImpl)it.next()).clone()); + } + + dest.holdBackConflicts = source.holdBackConflicts; + dest.createDate = source.createDate; + dest.lastChanged = source.lastChanged; + } + + public void copy(Object obj) { + super.copy((AllocatableImpl)obj); + copy((AllocatableImpl)obj,this); + } + + public Object deepClone() { + AllocatableImpl clone = new AllocatableImpl(); + super.deepClone(clone); + copy(this,clone); + return clone; + } + + public Object clone() { + AllocatableImpl clone = new AllocatableImpl(); + super.clone(clone); + copy(this,clone); + return clone; + } + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append(" ["); + buf.append(super.toString()); + buf.append(","); + buf.append(super.getVersion()); + buf.append("] "); + if ( getClassification() != null) { + buf.append (getClassification().toString()) ; + } + return buf.toString(); + } + + +} + + + + + + + diff --git a/Rapla/src/org/rapla/entities/domain/internal/AppointmentImpl.java b/Rapla/src/org/rapla/entities/domain/internal/AppointmentImpl.java new file mode 100644 index 0000000..0e6b083 --- /dev/null +++ b/Rapla/src/org/rapla/entities/domain/internal/AppointmentImpl.java @@ -0,0 +1,714 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org . | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.domain.internal; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; + +import org.rapla.components.util.Assert; +import org.rapla.components.util.DateTools; +import org.rapla.components.util.Tools; +import org.rapla.entities.RaplaType; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.AppointmentBlock; +import org.rapla.entities.domain.Repeating; +import org.rapla.entities.domain.RepeatingType; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.storage.RefEntity; +import org.rapla.entities.storage.internal.SimpleEntity; +import org.rapla.entities.storage.internal.SimpleIdentifier; + +public class AppointmentImpl extends SimpleEntity implements Appointment,java.io.Serializable +{ + // Don't forget to increase the serialVersionUID when you change the fields + private static final long serialVersionUID = 1; + + private Date start; + private Date end; + private RepeatingImpl repeating; + private boolean isWholeDaysSet = false; + /** set DE (DebugDisabled) to false for debuging output. You must change in code + because this flag is final for efficience reasons.*/ + public final static boolean DE = true; + public final static String BUG = null; + public static String DD = null; + + final public RaplaType getRaplaType() {return TYPE;} + + private AppointmentImpl() { + } + + public AppointmentImpl(Date start,Date end) { + this.start = start; + this.end = end; + if ( start != null && end!= null && DateTools.cutDate( start ).equals( start) && DateTools.cutDate( end).equals(end)) + { + isWholeDaysSet = true; + } + + } + + public AppointmentImpl(Date start,Date end, RepeatingType type, int repeatingDuration) { + this(start,end); + this.repeating = new RepeatingImpl(type,this); + repeating.setNumber(repeatingDuration); + } + + + + + void setParent(Reservation parent) { + getReferenceHandler().put("parent",parent); + if (parent != null) + setOwner(parent.getOwner()); + } + + public void removeParent() + { + getReferenceHandler().removeId("parent"); + setOwner( null ); + } + + + public Date getStart() { return start;} + public Date getEnd() { return end;} + + public void setReadOnly(boolean enable) { + super.setReadOnly( enable ); + if ( repeating != null ) + repeating.setReadOnly( enable ); + } + + public void move(Date newStart) { + long diff = this.end.getTime() - this.start.getTime(); + move(newStart, new Date(newStart.getTime() + diff)); + } + + public void move(Date start,Date end) { + checkWritable(); + this.start = start; + this.end = end; + if ( isWholeDaysSet) + { + if (start.getTime() != DateTools.cutDate(start.getTime()) || end.getTime() != DateTools.cutDate(end.getTime())) + { + isWholeDaysSet = false; + } + } + } + + public String toString() { + if (start != null && end != null) + return f(start.getTime(),end.getTime()) + + ((repeating != null) ? (" [" + repeating.toString()) + "]": ""); + else + return start + "-" + end; + } + + public Reservation getReservation() { + return (Reservation)getReferenceHandler().get("parent"); + } + + public boolean isWholeDaysSet() { + return isWholeDaysSet; + } + + public void setWholeDays(boolean enable) { + checkWritable(); + if (enable) { + if (start.getTime() != DateTools.cutDate(start.getTime())) + this.start = DateTools.cutDate(this.start); + if (end.getTime() != DateTools.cutDate(end.getTime())) + this.end = DateTools.fillDate(this.end); + if ( end.getTime() <= start.getTime()) + { + this.end = DateTools.fillDate(this.start); + } + } + isWholeDaysSet = enable; + } + + public int compareTo(Appointment a2) { + Date start2 = a2.getStart(); + Date end2 = a2.getEnd(); + if (start.before( start2)) + return -1; + if (start.after( start2)) + return 1; + if (getEnd().before( end2)) + return -1; + if (getEnd().after( end2)) + return 1; + if ( a2 == this) + { + return 0; + } + + SimpleIdentifier id1 = (SimpleIdentifier) getId(); + SimpleIdentifier id2 = (SimpleIdentifier)((RefEntity)a2).getId(); + if ( id1 == null || id2 == null) + { + return hashCode() < a2.hashCode() ? -1 : 1; + } + if ( id1.getKey() == id2.getKey()) + return 0; + return (id1.getKey() < id2.getKey()) ? -1 : 1; + } + + transient Date maxDate; + + /** returns the largest date that covers the appointment + and null if the appointments repeats forever. + */ + public Date getMaxEnd() { + long end = (this.end!= null) ? this.end.getTime():0; + if (repeating != null) + if (repeating.getEnd() != null) + end = Math.max(end + ,repeating.getEnd().getTime()); + else + end = 0; + if (end == 0) + return null; + + // cache max date object + if (maxDate == null || maxDate.getTime() != end) + maxDate = new Date(end); + return maxDate; + } + + public Repeating getRepeating() { + return repeating; + } + + public void setRepeatingEnabled(boolean enableRepeating) { + checkWritable(); + if (this.repeating == null) { + if (enableRepeating) { + this.repeating = new RepeatingImpl(Repeating.WEEKLY,this); + } + } else { + if (!enableRepeating) { + this.repeating = null; + } + } + } + + public boolean isRepeatingEnabled() { + return repeating != null; + } + + public Date getFirstDifference( Appointment a2, Date maxDate ) { + List blocks1 = new ArrayList(); + createBlocks( start, maxDate, blocks1); + List blocks2 = new ArrayList(); + a2.createBlocks(a2.getStart(), maxDate, blocks2); + // System.out.println("block sizes " + blocks1.size() + ", " + blocks2.size() ); + int i=0; + for ( AppointmentBlock block:blocks1) { + long a1Start = block.getStart(); + long a1End = block.getEnd(); + if ( i >= blocks2.size() ) { + return new Date( a1Start ); + } + long a2Start = blocks2.get( i ).getStart(); + long a2End = blocks2.get( i ).getEnd(); + //System.out.println("a1Start " + a1Start + " a1End " + a1End); + //System.out.println("a2Start " + a2Start + " a2End " + a2End); + if ( a1Start != a2Start ) + return new Date( Math.min ( a1Start, a2Start ) ); + + if ( a1End != a2End ) + return new Date( Math.min ( a1End, a2End ) ); + i++; + } + if ( blocks2.size() > blocks1.size() ) { + return new Date( blocks2.get( blocks1.size() ).getStart() ); + } + return null; + } + + public Date getLastDifference( Appointment a2, Date maxDate ) { + List blocks1 = new ArrayList(); + createBlocks( start, maxDate, blocks1); + List blocks2 = new ArrayList(); + a2.createBlocks(a2.getStart(), maxDate, blocks2); + if ( blocks2.size() > blocks1.size() ) { + return new Date( blocks2.get( blocks1.size() ).getEnd() ); + } + if ( blocks1.size() > blocks2.size() ) { + return new Date( blocks1.get( blocks2.size() ).getEnd() ); + } + for ( int i = blocks1.size() - 1 ; i >= 0; i-- ) { + long a1Start = blocks1.get( i ).getStart(); + long a1End = blocks1.get( i ).getEnd(); + long a2Start = blocks2.get( i ).getStart(); + long a2End = blocks2.get( i ).getEnd(); + if ( a1End != a2End ) + return new Date( Math.max ( a1End, a2End ) ); + + if ( a1Start != a2Start ) + return new Date( Math.max ( a1Start, a2Start ) ); + } + return null; + } + + public boolean matches(Appointment a2) { + if (!Tools.equalsOrBothNull(this.start, a2.getStart())) + return false; + + if (!Tools.equalsOrBothNull(this.end, a2.getEnd())) + return false; + + Repeating r1 = this.repeating; + Repeating r2 = a2.getRepeating(); + + // No repeatings. The two appointments match + if (r1 == null && r2 == null) { + return true; + } else if (r1 == null || r2 == null) { + // one repeating is null the other not so the appointments don't match + return false; + } + + if (!r1.getType().equals(r2.getType())) + return false; + + if (r1.getInterval() != r2.getInterval()) + return false; + + if (!Tools.equalsOrBothNull(r1.getEnd(), r2.getEnd())) + return false; + + // The repeatings match regulary, so we must test the exceptions + Date[] e1 = r1.getExceptions(); + Date[] e2 = r2.getExceptions(); + if (e1.length != e2.length) { + //System.out.println("Exception-length don't match"); + return false; + } + + for (int i=0;i blocks) { + createBlocks(start,end, blocks, true); + } + + + public void createBlocks(Date start,Date end,Collection blocks, boolean excludeExceptions) { + Assert.notNull(blocks); + Assert.notNull(start,"You must set a startDate"); + Assert.notNull(end, "You must set an endDate"); + processBlocks(start.getTime(), end.getTime(), blocks, excludeExceptions); + } + /* returns true if there is at least one block in an array. If the passed blocks array is not null it will contain all blocks + * that overlap the start,end period after a call.*/ + private boolean processBlocks(long start,long end,Collection blocks, boolean excludeExceptions) { + boolean checkOnly = (blocks == null); + long c1 = start; + long c2 = end; + long s = this.start.getTime(); + long e = this.end.getTime(); + // if there is no repeating + if (repeating==null) { + if (s c1) { + if ( !checkOnly ) + blocks.add(new AppointmentBlock(s,e,this, false)); + return true; + } + return false; + } + + DD=DE?BUG: print("s = appointmentstart, e = appointmentend, c1 = intervalstart c2 = intervalend"); + DD=DE?BUG: print("s:" + n(s) + " e:" + n(e) + " c2:" + n(c2) + " c1:" + n(c1)); + if (s c1 && (!repeating.isException(s) || !excludeExceptions)) { + if (checkOnly) { + return true; + } else { + blocks.add(new AppointmentBlock(s,e,this, repeating.isException(s))); + } + } + + long l = repeating.getIntervalLength( s ); + //System.out.println( "l in days " + l / DateTools.MILLISECONDS_PER_DAY ); + Assert.isTrue(l>0); + long timeFromStart = l ; + if ( repeating.isFixedIntervalLength()) + { + timeFromStart = Math.max(l,((c1-e) / l)* l); + } + int maxNumber = repeating.getNumber(); + long maxEnding = Long.MAX_VALUE; + if ( maxNumber >= 0) + { + maxEnding = repeating.getEnd().getTime(); + } + + DD=DE?BUG: print("l = repeatingInterval (in minutes), x = stepcount"); + DD=DE?BUG: print("Maxend " + f( maxEnding)); + long currentPos = s + timeFromStart; + DD=DE?BUG: print( " currentPos:" + n(currentPos) + " c2-s:" + n(c2-s) + " c1-e:" + n(c1-e)); + long blockLength = Math.max(0, e - s); + while (currentPos <= c2 && (maxNumber<0 || (currentPos<=maxEnding ))) { + DD=DE?BUG: print(" current pos:" + f(currentPos)); + if (( currentPos + blockLength > c1 ) && ( currentPos < c2 ) && (( end!=DateTools.cutDate(end) || !repeating.isDaily() || currentPos < maxEnding))) { + boolean isException =repeating.isException( currentPos ); + if ((!isException || !excludeExceptions)) { + if ( checkOnly ) { + return true; + } else { + blocks.add( new AppointmentBlock(currentPos,currentPos + blockLength,this, isException)); + } + } + } + currentPos += repeating.getIntervalLength( currentPos) ; + + } + return false; + } + + public boolean overlaps(Date start,Date end) { + return overlaps( start, end , true ); + } + + public boolean overlaps(Date start,Date end, boolean excludeExceptions) { + if (start == null && end == null) + return true; + if (start == null) + start = this.start; + if (end == null) + { + // there must be an overlapp because there can't be infinity exceptions + if (getMaxEnd() == null) + return true; + end = getMaxEnd(); + } + + if (getMaxEnd() != null && getMaxEnd().before(start)) + return false; + + if (this.start.after(end)) + return false; + + boolean overlaps = processBlocks( start.getTime(), end.getTime(), null, excludeExceptions ); + return overlaps; + } + + public boolean overlaps(long start,long end, boolean excludeExceptions) { + if (getMaxEnd() != null && getMaxEnd().getTime() end) + return false; + + boolean overlaps = processBlocks( start, end, null, excludeExceptions ); + return overlaps; + } + + private static Date getOverlappingEnd(Repeating r1,Repeating r2) { + Date maxEnd = null; + if (r1.getEnd() != null) + maxEnd = r1.getEnd(); + if (r2.getEnd() != null) + if (maxEnd != null && r2.getEnd().before(maxEnd)) + maxEnd = r2.getEnd(); + return maxEnd; + } + + public boolean overlaps(Appointment a2) { + if ( a2 == this) + return true; + Date start2 =a2.getStart(); + Date end2 =a2.getEnd(); + long s1 = this.start.getTime(); + long s2 = start2.getTime(); + long e1 = this.end.getTime(); + long e2 = a2.getEnd().getTime(); + RepeatingImpl r1 = this.repeating; + RepeatingImpl r2 = (RepeatingImpl)a2.getRepeating(); + DD=DE?BUG: print("Testing overlap of"); + DD=DE?BUG: print(" A1: " + toString()); + DD=DE?BUG: print(" A2: " + a2.toString()); + + if (r1 == null && r2 == null) { + if (e2<=s1 || e1<=s2) + return false; + return true; + } + if (r1 == null) { + return a2.overlaps(this.start,this.end); + } + if (r2 == null) { + return overlaps(start2,end2); + } + + // So both appointments have a repeating + + // If r2 has no exceptions we can check if a1 overlaps the first appointment of a2 + if (overlaps(start2,end2) && !r2.isException(start2.getTime())) { + DD=DE?BUG: print("Primitive overlap for " + getReservation() + " with " + a2.getReservation()); + return true; + } + + // Check if appointments could overlap because of the end-dates of an repeating + Date end = getOverlappingEnd(r1,r2); + if (end != null && (end.getTime()<=s1 || end.getTime()<=s2)) + return false; + + // We cant compare the fixed interval length here so we have to compare the blocks + if ( !r1.isFixedIntervalLength()) + { + return overlapsHard( (AppointmentImpl)a2); + } + if ( !r2.isFixedIntervalLength()) + { + return ((AppointmentImpl)a2).overlapsHard( this); + } + // O.K. we found 2 Candidates for the hard way + long l1 = r1.getFixedIntervalLength(); + long l2 = r2.getFixedIntervalLength(); + // The greatest common divider of the two intervals + long gcd = gcd(l1,l2); + long startx1 = Math.max(0,(s2-e1))/l1; + long startx2 = Math.max(0,(s1-e2))/l2; + + DD=DE?BUG: print("l? = intervalsize for A?, x? = stepcount for A? "); + long max_x1 = l2/gcd + startx1; + if (end!= null && (end.getTime()-s1)/l1 + startx1 < max_x1) + max_x1 = (end.getTime()-s1)/l1 + startx1; + long max_x2 = l1/gcd + startx2; + if (end!= null && (end.getTime()-s2)/l2 + startx2 < max_x2) + max_x2 = (end.getTime()-s2)/l2 + startx2; + long x1 =startx1; + long x2 =startx2; + + DD=DE?BUG: print( + "l1: " + n(l1) + + " l2: " + n(l2) + + " gcd: " + n(gcd) + + " start_x1: " + startx1 + + " start_x2: " + startx2 + + " max_x1: " + max_x1 + + " max_x2: " + max_x2 + ); + boolean overlaps = false; + long current1 = x1 *l1; + long current2 = x2 *l2; + long maxEnd1 = max_x1*l1; + long maxEnd2 = max_x2*l2; + + while (current1<=maxEnd1 && current2<=maxEnd2) { + // DD=DE?BUG: print("x1: " + x1 + " x2:" + x2); + DD=DE?BUG: print(" A1: " + f(s1 + current1, e1 + current1)); + DD=DE?BUG: print(" A2: " + f(s2 + current2, e2 + current2)); + if ((s1 + current1) < (e2 + current2) && + (e1 + current1) > (s2 + current2)) { + if (!isException(s1 + current1,s2 + current2,r2)) { + overlaps = true; + break; + } + } + if ((s1 + current1) < (s2 + current2)) + current1+=l1; + else + current2+=l2; + } + if (overlaps) + DD=DE?BUG: print("Appointments overlap"); + else + DD=DE?BUG: print("Appointments don't overlap"); + return overlaps; + } + + private boolean overlapsHard( AppointmentImpl a2 ) + { + RepeatingImpl r2 = (RepeatingImpl)a2.getRepeating(); + Collection array = new ArrayList(); + Date maxEnd =r2.getEnd(); + if ( maxEnd == null) + { + // overlaps will be checked two 250 weeks (5 years) from now on + maxEnd = new Date(a2.getStart().getTime() + DateTools.MILLISECONDS_PER_WEEK * 250); + } + createBlocks( getStart(), maxEnd, array); + for ( AppointmentBlock block:array) + { + long start = block.getStart(); + long end = block.getEnd(); + if (a2.overlaps( start, end, true)) + { + return true; + } + } + return false; + } + + /** the greatest common divider of a and b (Euklids Algorithm) */ + public static long gcd(long a,long b){ + return (b == 0) ? a : gcd(b, a%b); + } + + + /* Prueft im Abstand von "gap" millisekunden das Intervall von start bis ende + auf Ausnahmen. Gibt es fuer einen Punkt keine Ausnahme wird false zurueckgeliefert. + */ + private boolean isException(long s1,long s2,RepeatingImpl r2) { + RepeatingImpl r1 = repeating; + Date end= getOverlappingEnd(r1,r2); + if (end == null) + return false; + + if ((!r1.hasExceptions() && !r2.hasExceptions())) + return false; + + long l1 = r1.getFixedIntervalLength(); + long l2 = r2.getFixedIntervalLength(); + long gap = (l1 * l2) / gcd(l1,l2); + Date[] exceptions1 = r1.getExceptions(); + Date[] exceptions2 = r2.getExceptions(); + DD=DE?BUG: print(" Testing Exceptions for overlapp " + f(s1) + " with " + f(s2) + " gap " + n(gap)); + int i1 = 0; + int i2 = 0; + long x = 0; + if (exceptions1.length>i1) + DD=DE?BUG: print("Exception a1: " + fe(exceptions1[i1].getTime())); + if (exceptions2.length>i2) + DD=DE?BUG: print("Exception a2: " + fe(exceptions2[i2].getTime())); + while (s1 + x * gap < end.getTime()) { + DD=DE?BUG: print("Looking for exception for gap " + x + " s1: " + fe(s1+x*gap) + " s2: " + fe(s2+x*gap)); + while ((i1i1) { + DD=DE?BUG: print("Exception a1: " + fe(exceptions1[i1].getTime())); + } + } + while ((exceptions2.length>i2) + && (exceptions2[i2].getTime() + DateTools.MILLISECONDS_PER_DAY <= s2 + x*gap)) { + i2 ++; + if (exceptions2.length>i2) { + DD=DE?BUG: print("Exception a2: " + fe(exceptions2[i2].getTime())); + } + } + if ((exceptions1.length==i1) + || (s1 + x*gap >= exceptions1[i1].getTime() + DateTools.MILLISECONDS_PER_DAY)) { + DD=DE?BUG: print("Exception from a1 doesnt match "); + if ((exceptions2.length==i2) + || (s2 + x*gap >= exceptions2[i2].getTime() + DateTools.MILLISECONDS_PER_DAY)) { + DD=DE?BUG: print("Exception from a2 doesnt match. !"); + return false; + } else { + DD=DE?BUG: print("Exception from a2 matches!"); + } + } else { + DD=DE?BUG: print("Exception from a1 matches!"); + } + DD=DE?BUG: print("Exception found for gap " + x); + x ++; + } + DD=DE?BUG: print("Exceptions found for every gap. No overlapping. "); + return true; + } + + private static String print(String string) { + if (string != null) + System.out.println(string); + return string; + } + + /* cuts the milliseconds and seconds. Usefull for debugging output.*/ + private long n(long n) { + return n / (1000 * 60); + } + + /* Formats milliseconds as date. Usefull for debugging output.*/ + static String f(long n) { + SimpleDateFormat format = new SimpleDateFormat("E yyyy-MM-dd HH:mm"); + format.setTimeZone(DateTools.getTimeZone()); + return format.format(new Date(n)); + } + + /* Formats milliseconds as date without time. Usefull for debugging output.*/ + static String fe(long n) { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + format.setTimeZone(DateTools.getTimeZone()); + return format.format(new Date(n)); + } + + /* Formats 2 dates in milliseconds as appointment. Usefull for debugging output.*/ + static String f(long s,long e) { + Date start = new Date(s); + Date end = new Date(e); + SimpleDateFormat formatLong = new SimpleDateFormat("E yyyy-MM-dd HH:mm"); + formatLong.setTimeZone(DateTools.getTimeZone()); + SimpleDateFormat formatTime = new SimpleDateFormat("HH:mm"); + formatTime.setTimeZone(DateTools.getTimeZone()); + if (DateTools.isSameDay(s,e)) { + return formatLong.format(start) + "-" + formatTime.format(end); + } else { + return formatLong.format(start) + "-" + formatLong.format(end); + } + } + + static private void copy(AppointmentImpl source,AppointmentImpl dest) { + dest.isWholeDaysSet = source.isWholeDaysSet; + dest.start = source.start; + dest.end = source.end; + dest.repeating = (RepeatingImpl) ((source.repeating != null) ? + source.repeating.clone() + : null); + if (dest.repeating != null) + dest.repeating.setAppointment(dest); + } + + public void copy(Appointment obj) { + super.copy((SimpleEntity)obj); + copy((AppointmentImpl) obj,this); + } + + public Appointment deepClone() { + AppointmentImpl clone = new AppointmentImpl(); + super.deepClone(clone); + copy(this,clone); + return clone; + } + + public Appointment clone() { + AppointmentImpl clone = new AppointmentImpl(); + super.clone(clone); + copy(this,clone); + return clone; + } + +} + + + + + + + + + diff --git a/Rapla/src/org/rapla/entities/domain/internal/PeriodImpl.java b/Rapla/src/org/rapla/entities/domain/internal/PeriodImpl.java new file mode 100644 index 0000000..43d63fe --- /dev/null +++ b/Rapla/src/org/rapla/entities/domain/internal/PeriodImpl.java @@ -0,0 +1,165 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.domain.internal; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; + +import org.rapla.components.util.DateTools; +import org.rapla.entities.RaplaType; +import org.rapla.entities.domain.Period; +import org.rapla.entities.storage.Mementable; +import org.rapla.entities.storage.internal.SimpleEntity; + +public class PeriodImpl extends SimpleEntity + implements + Period + ,java.io.Serializable + ,Mementable +{ + // Don't forget to increase the serialVersionUID when you change the fields + private static final long serialVersionUID = 1; + + private final static long WEEK_MILLIS= DateTools.MILLISECONDS_PER_WEEK; + + String name; + Date start; + Date end; + transient Calendar cal; + + public PeriodImpl() { + + } + + public PeriodImpl(Date start, Date end) { + this.start = start; + this.end = end; + } + + final public RaplaType getRaplaType() {return TYPE;} + + public Date getStart() { + return start; + } + + public void setStart(Date start) { + checkWritable(); + this.start = start; + } + + public Date getEnd() { + return end; + } + public void setEnd(Date end) { + checkWritable(); + this.end = end; + } + + public int getWeeks() + { + long diff= end.getTime()-start.getTime(); + return (int)(((diff-1)/WEEK_MILLIS )+ 1); + } + + public String getName(Locale locale) { + return name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + checkWritable(); + this.name = name; + } + + public boolean contains(Date date) { + return (date.before(end) && !date.before(start)); + } + + public int weekOf(Date date) { + Calendar cal = Calendar.getInstance(DateTools.getTimeZone()); + if (!contains(date)) + return -1; + long duration = date.getTime() - start.getTime(); + long weeks = duration / (DateTools.MILLISECONDS_PER_WEEK); + // setTimeInMillis has protected access in JDK 1.3.1 + cal.setTime(new Date(date.getTime() - weeks * DateTools.MILLISECONDS_PER_WEEK)); + int week_of_year = cal.get(Calendar.WEEK_OF_YEAR); + cal.setTime(getStart()); + return ((int)weeks) + 1 + + (((week_of_year) != cal.get(Calendar.WEEK_OF_YEAR))? 1 :0); + } + + public String toString() { + return getName(); + } + + public int compareTo(Date date) { + int result = getEnd().compareTo(date); + if (result == 0) + return 1; + else + return result; + } + + public int compareTo(Period period) { + int result = getStart().compareTo(period.getStart()); + if (result != 0) return result; + + if (equals(period)) + return 0; + + return (hashCode() < period.hashCode()) ? -1 : 1; + } + + public int compareTo(Object o2) { + if (o2 instanceof Date) { + return compareTo((Date) o2); + } + if (o2 instanceof Period) { + return compareTo((Period) o2); + } + throw new ClassCastException("Object needs to be an instance of Date or Period"); + } + + static private void copy(PeriodImpl source,PeriodImpl dest) { + dest.start = source.start; + dest.end = source.end; + dest.name = source.name; + } + + public void copy(Object obj) { + super.copy((PeriodImpl)obj); + copy((PeriodImpl) obj,this); + } + + public Object deepClone() { + PeriodImpl clone = new PeriodImpl(); + super.deepClone(clone); + copy(this,clone); + return clone; + } + + public Object clone() { + PeriodImpl clone = new PeriodImpl(); + super.clone(clone); + copy(this,clone); + return clone; + } +} + + + + diff --git a/Rapla/src/org/rapla/entities/domain/internal/PermissionImpl.java b/Rapla/src/org/rapla/entities/domain/internal/PermissionImpl.java new file mode 100644 index 0000000..dc7f695 --- /dev/null +++ b/Rapla/src/org/rapla/entities/domain/internal/PermissionImpl.java @@ -0,0 +1,238 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org . | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, of which license fullfill the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.entities.domain.internal; + +import java.util.Date; +import java.util.Iterator; + +import org.rapla.components.util.DateTools; +import org.rapla.entities.Category; +import org.rapla.entities.EntityNotFoundException; +import org.rapla.entities.ReadOnlyException; +import org.rapla.entities.User; +import org.rapla.entities.domain.Period; +import org.rapla.entities.domain.Permission; +import org.rapla.entities.storage.RefEntity; +import org.rapla.entities.storage.EntityReferencer; +import org.rapla.entities.storage.EntityResolver; +import org.rapla.entities.storage.internal.ReferenceHandler; + +public class PermissionImpl + implements + Permission + ,EntityReferencer + ,java.io.Serializable +{ + // Don't forget to increase the serialVersionUID when you change the fields + private static final long serialVersionUID = 1; + + boolean readOnly = false; + ReferenceHandler referenceHandler = new ReferenceHandler(); + Date pEnd = null; + Date pStart = null; + Long maxAdvance = null; + Long minAdvance = null; + int accessLevel = ALLOCATE_CONFLICTS; + + public void resolveEntities( EntityResolver resolver) throws EntityNotFoundException { + referenceHandler.resolveEntities( resolver ); + } + + public void setUser(User user) { + checkWritable(); + if (user != null) + referenceHandler.put("group",null); + referenceHandler.put("user",user); + } + + public void setEnd(Date end) { + checkWritable(); + this.pEnd = end; + if ( end != null ) + this.maxAdvance = null; + } + + public Date getEnd() { + return pEnd; + } + + public void setStart(Date start) { + checkWritable(); + this.pStart = start; + if ( start != null ) + this.minAdvance = null; + } + + public Date getStart() { + return pStart; + } + + public void setMinAdvance(Long minAdvance) { + checkWritable(); + this.minAdvance = minAdvance; + if ( minAdvance != null ) + this.pStart = null; + } + + public Long getMinAdvance() { + return minAdvance; + } + + public void setMaxAdvance(Long maxAdvance) { + checkWritable(); + this.maxAdvance = maxAdvance; + if ( maxAdvance != null ) + this.pEnd = null; + } + + public Long getMaxAdvance() { + return maxAdvance; + } + + public void setReadOnly(boolean enable) { + this.readOnly = enable; + } + + public boolean isReadOnly() { + return readOnly; + } + + public void checkWritable() { + if ( readOnly ) + throw new ReadOnlyException( this ); + } + + public boolean affectsUser(User user) { + User pUser = getUser(); + Category pGroup = getGroup(); + if ( pUser == null && pGroup == null ) { + return true; + } + if ( pUser != null && user.equals( pUser ) ) { + return true; + } else if ( pGroup != null ) { + Category[] uGroups = user.getGroups(); + for ( int i = 0; i < uGroups.length; i++ ) { + if ( pGroup.equals ( uGroups[i] ) + || pGroup.isAncestorOf ( uGroups[i] ) + ) { + return true; + } + } + } + return false; + } + + public void setAccessLevel(int accessLevel) { + this.accessLevel = accessLevel; + } + + public int getAccessLevel() { + return accessLevel; + } + + public User getUser() { + return (User) referenceHandler.get("user"); + } + + public void setGroup(Category group) { + if (group != null) + referenceHandler.put("user",null); + referenceHandler.put("group",group); + } + + public Period getPeriod() { + return (Period) referenceHandler.get("period"); + } + + public void setPeriod(Period period) { + referenceHandler.put("period",period); + } + + public ReferenceHandler getReferenceHandler() { + return referenceHandler; + } + + public Iterator getReferences() { + return referenceHandler.getReferences(); + } + + public boolean isRefering( RefEntity object ) { + return referenceHandler.isRefering( object ); + } + + public Category getGroup() { + return (Category) referenceHandler.get("group"); + } + + public Date getMinAllowed(Date today) { + if ( pStart != null ) + return pStart; + if ( minAdvance != null) + return new Date( today.getTime() + + DateTools.MILLISECONDS_PER_DAY * minAdvance.longValue() ); + return null; + } + + public Date getMaxAllowed(Date today) { + if ( pEnd != null ) + return pEnd; + if ( maxAdvance != null) + return new Date( today.getTime() + + DateTools.MILLISECONDS_PER_DAY * (maxAdvance.longValue() + 1) ); + return null; + } + + public boolean covers( Date start, Date end, Date today ) { + if ( pStart != null && (start == null || start.before ( pStart ) ) ) { + //System.out.println( " start before permission "); + return false; + } + if ( pEnd != null && ( end == null || pEnd.before ( end ) ) ) { + //System.out.println( " end before permission "); + return false; + } + if ( minAdvance != null ) { + long pStartTime = today.getTime() + + DateTools.MILLISECONDS_PER_DAY * minAdvance.longValue(); + + if ( start == null || start.getTime() < pStartTime ) { + //System.out.println( " start before permission " + start + " < " + pStartTime ); + return false; + } + } + if ( maxAdvance != null ) { + long pEndTime = today.getTime() + + DateTools.MILLISECONDS_PER_DAY * (maxAdvance.longValue() + 1); + if ( end == null || pEndTime < end.getTime() ) { + //System.out.println( " end after permission " + end + " > " + pEndTime ); + return false; + } + } + return true; + } + + public Object clone() { + PermissionImpl clone = new PermissionImpl(); + // This must be done first + clone.referenceHandler = (ReferenceHandler) referenceHandler.clone(); + clone.accessLevel = accessLevel; + clone.pEnd = pEnd; + clone.pStart = pStart; + clone.minAdvance = minAdvance; + clone.maxAdvance = maxAdvance; + return clone; + } + +} diff --git a/Rapla/src/org/rapla/entities/domain/internal/RepeatingImpl.java b/Rapla/src/org/rapla/entities/domain/internal/RepeatingImpl.java new file mode 100644 index 0000000..6c966d6 --- /dev/null +++ b/Rapla/src/org/rapla/entities/domain/internal/RepeatingImpl.java @@ -0,0 +1,403 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.domain.internal; +import java.util.Calendar; +import java.util.Date; +import java.util.TreeSet; + +import org.rapla.components.util.Assert; +import org.rapla.components.util.DateTools; +import org.rapla.entities.ReadOnlyException; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Repeating; +import org.rapla.entities.domain.RepeatingType; + +class RepeatingImpl implements Repeating,java.io.Serializable { + // Don't forget to increase the serialVersionUID when you change the fields + private static final long serialVersionUID = 1; + + private boolean readOnly = false; + + private int interval = 1; + private boolean isFixedNumber; + private int number = -1; + private Date end; + private RepeatingType repeatingType; + private TreeSet exceptions; + transient private Date[] exceptionArray; + transient private boolean arrayUpToDate = false; + private Appointment appointment; + private long frequency; + boolean monthly; + boolean yearly; + + RepeatingImpl(RepeatingType type,Appointment appointment) { + setType(type); + setAppointment(appointment); + setNumber( 1) ; + } + + public void setType(RepeatingType repeatingType) { + if ( repeatingType == null ) + { + throw new IllegalStateException("Repeating type cannot be null"); + } + checkWritable(); + this.repeatingType = repeatingType; + monthly = false; + yearly = false; + if (repeatingType.equals( RepeatingType.WEEKLY )) + { + frequency = 7 * DateTools.MILLISECONDS_PER_DAY; + } + else if (repeatingType.equals( RepeatingType.MONTHLY)) + { + frequency = 7 * DateTools.MILLISECONDS_PER_DAY; + monthly = true; + } + else if (repeatingType.equals( RepeatingType.DAILY)) + { + frequency = DateTools.MILLISECONDS_PER_DAY; + } + else if (repeatingType.equals( RepeatingType.YEARLY)) + { + frequency = DateTools.MILLISECONDS_PER_DAY; + yearly = true; + } + else + { + throw new UnsupportedOperationException(" repeatingType " + repeatingType + " not supported"); + } + } + + public RepeatingType getType() { + return repeatingType; + + } + + void setAppointment(Appointment appointment) { + this.appointment = appointment; + } + + public void setReadOnly(boolean enable) { + this.readOnly = enable; + } + + public boolean isReadOnly() { + return readOnly; + } + + public void checkWritable() { + if ( readOnly ) + throw new ReadOnlyException( this ); + } + + public Appointment getAppointment() { + return appointment; + } + + public void setInterval(int interval) { + checkWritable(); + if (interval<1) + interval = 1; + this.interval = interval; + } + + public int getInterval() { + return interval; + } + + public boolean isFixedNumber() { + return isFixedNumber; + } + + public boolean isWeekly() { + return RepeatingType.WEEKLY.equals( getType()); + } + + public boolean isDaily() { + return RepeatingType.DAILY.equals( getType()); + } + + public boolean isMonthly() { + return monthly; + } + + public boolean isYearly() { + return yearly; + } + + public void setEnd(Date end) { + checkWritable(); + isFixedNumber = false; + number = -1; + this.end = end; + } + + transient Date endTime; + public Date getEnd() { + if (!isFixedNumber) + return end; + if ( this.appointment == null) + return null; + + if (endTime == null) + endTime = new Date(); + + if ( number < 1 ) + { + return null; + } + + if ( !isFixedIntervalLength()) + { + int counts = (int) ((number -1) * interval) ; + Calendar cal= DateTools.createGMTCalendar(); + cal.setTime( appointment.getStart()); + for ( int i=0;i< counts;i++) + { + if ( monthly) + { + gotoNextMonth( cal, cal.getTime()); + } + else + { + gotoNextYear( cal, cal.getTime()); + } + + } + return cal.getTime(); + } + else + { + long intervalLength = frequency * interval; + endTime.setTime(DateTools.fillDate( this.appointment.getStart().getTime() + + (this.number -1)* intervalLength + )); + } + return endTime; + } + + /** returns interval-length in milliseconds. + @see #getInterval + */ + public long getFixedIntervalLength() { + return frequency * interval; + } + + public void setNumber(int number) { + checkWritable(); + if (number>-1) { + isFixedNumber = true; + this.number = Math.max(number,1); + } else { + isFixedNumber = false; + this.number = -1; + setEnd(null); + } + + } + + public boolean isException(long time) { + if (!hasExceptions()) + return false; + + Date[] exceptions = getExceptions(); + if (exceptions.length == 0) { + // System.out.println("no exceptions"); + return false; + } + for (int i=0;i-1) + return number; + if (end==null) + return -1; + // System.out.println("End " + end.getTime() + " Start " + appointment.getStart().getTime() + " Duration " + duration); + + if ( isFixedIntervalLength() ) + { + long duration = end.getTime() + - DateTools.fillDate(appointment.getStart().getTime()); + if (duration<0) + return 0; + long intervalLength = getFixedIntervalLength(); + return (int) ((duration/ intervalLength) + 1); + } + else + { + Calendar cal= DateTools.createGMTCalendar(); + int number = 0; + do + { + number ++; + if ( monthly) + { + gotoNextMonth( cal, cal.getTime()); + } + else + { + gotoNextYear( cal, cal.getTime()); + } + } + while ( cal.getTime().before( end)); + return number; + } + + } + + public void addException(Date date) { + checkWritable(); + if (exceptions == null) + exceptions = new TreeSet(); + exceptions.add(date); + arrayUpToDate = false; + } + + public void removeException(Date date) { + checkWritable(); + if (exceptions == null) + return; + exceptions.remove(date); + if (exceptions.size()==0) + exceptions = null; + arrayUpToDate = false; + } + + public void clearExceptions() { + if (exceptions == null) + return; + exceptions.clear(); + exceptions = null; + arrayUpToDate = false; + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("Repeating type="); + buf.append(repeatingType); + buf.append(" interval="); + buf.append(interval); + if (isFixedNumber()) { + buf.append(" number="); + buf.append(number); + } else { + if (end != null) { + buf.append(" end-date="); + buf.append(AppointmentImpl.fe(end.getTime())); + } + } + return buf.toString(); + } + + public Object clone() { + RepeatingImpl clone = new RepeatingImpl(repeatingType,appointment); + clone.monthly = monthly; + clone.yearly = yearly; + clone.interval = interval; + clone.isFixedNumber = isFixedNumber; + clone.number = number; + clone.end = end; + clone.interval = interval; + clone.readOnly = false;// clones are always writable + clone.exceptions = (TreeSet) ((exceptions != null) ? exceptions.clone(): null); + return clone; + } + + private static Date[] DATE_ARRAY = new Date[0]; + public Date[] getExceptions() { + if (!arrayUpToDate) { + if (exceptions != null) + exceptionArray = (Date[])exceptions.toArray(DATE_ARRAY); + else + exceptionArray = DATE_ARRAY; + arrayUpToDate = true; + } + return exceptionArray; + } + public boolean hasExceptions() { + return exceptions != null && exceptions.size()>0; + } + + final public long getIntervalLength( long s ) + { + if ( isFixedIntervalLength()) + { + return getFixedIntervalLength(); + } + + Date startDate = new Date(s); + Calendar cal= DateTools.createGMTCalendar(); + if ( monthly) + { + gotoNextMonth( cal, startDate); + } + else + { + gotoNextYear( cal, startDate); + } + Date newDate = cal.getTime(); + long newTime = newDate.getTime(); + Assert.isTrue( newTime > s ); + return newTime- s; + + // yearly + + } + + private void gotoNextMonth( Calendar cal, Date beginDate ) + { + cal.setTime( appointment.getStart()); + int dayofweekinmonth = cal.get( Calendar.DAY_OF_WEEK_IN_MONTH); + cal.setTime( beginDate); + cal.add( Calendar.WEEK_OF_YEAR, 4); + while ( cal.get( Calendar.DAY_OF_WEEK_IN_MONTH) != dayofweekinmonth ) + { + //System.out.println( new MonthMapper().getName(month)); + cal.add( Calendar.WEEK_OF_YEAR, 1); + } + } + + private void gotoNextYear( Calendar cal, Date beginDate ) + { + cal.setTime( appointment.getStart()); + int dayOfMonth = cal.get( Calendar.DAY_OF_MONTH); + int month = cal.get( Calendar.MONTH); + cal.setTime( beginDate); + cal.add( Calendar.YEAR,1); + while ( cal.get( Calendar.DAY_OF_MONTH) != dayOfMonth) + { + cal.add( Calendar.YEAR,1); + cal.set( Calendar.MONTH, month); + cal.set( Calendar.DAY_OF_MONTH, dayOfMonth); + } + } + + final public boolean isFixedIntervalLength() + { + return !monthly &&!yearly; + } + + +} + diff --git a/Rapla/src/org/rapla/entities/domain/internal/ReservationImpl.java b/Rapla/src/org/rapla/entities/domain/internal/ReservationImpl.java new file mode 100644 index 0000000..6abdfda --- /dev/null +++ b/Rapla/src/org/rapla/entities/domain/internal/ReservationImpl.java @@ -0,0 +1,434 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org . | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, of which license fullfill the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.domain.internal; +/** The default Implementation of the Reservation + * @see ModificationEvent + * @see org.rapla.facade.ClientFacade + */ +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; + +import org.rapla.components.util.Assert; +import org.rapla.components.util.iterator.IteratorChain; +import org.rapla.entities.EntityNotFoundException; +import org.rapla.entities.RaplaType; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.Classification; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.dynamictype.internal.ClassificationImpl; +import org.rapla.entities.internal.ModifiableTimestamp; +import org.rapla.entities.storage.CannotExistWithoutTypeException; +import org.rapla.entities.storage.DynamicTypeDependant; +import org.rapla.entities.storage.EntityResolver; +import org.rapla.entities.storage.Mementable; +import org.rapla.entities.storage.RefEntity; +import org.rapla.entities.storage.internal.SimpleEntity; + + +public class ReservationImpl extends SimpleEntity implements Reservation,Mementable,java.io.Serializable, ModifiableTimestamp, DynamicTypeDependant +{ + // Don't forget to increase the serialVersionUID when you change the fields + private static final long serialVersionUID = 1; + + private ClassificationImpl classification; + private Map restrictions; + private Date lastChanged; + private Date createDate; + private Date slotDate; + transient private boolean allocatableArrayUpToDate = false; + transient private boolean appointmentArrayUpToDate = false; + // The resolved references + transient private Allocatable[] allocatables; + transient private Allocatable[] persons; + transient private Allocatable[] resources; + transient private Appointment[] appointments; + + ReservationImpl() { + this (null, null); + } + + public ReservationImpl( Date createDate, Date lastChanged ) { + this.createDate = createDate; + if (createDate == null) + this.createDate = new Date(); + this.lastChanged = lastChanged; + if (lastChanged == null) + this.lastChanged = this.createDate; + } + + public void resolveEntities( EntityResolver resolver) throws EntityNotFoundException { + super.resolveEntities( resolver); + allocatableArrayUpToDate = false; + appointmentArrayUpToDate = false; + classification.resolveEntities( resolver); + } + + public void setReadOnly(boolean enable) { + super.setReadOnly( enable ); + classification.setReadOnly( enable ); + } + + final public RaplaType getRaplaType() {return TYPE;} + + // Implementation of interface classifiable + public Classification getClassification() { return classification; } + public void setClassification(Classification classification) { + checkWritable(); + this.classification = (ClassificationImpl) classification; + } + + public String getName(Locale locale) { + Classification c = getClassification(); + if (c == null) + return ""; + return c.getName(locale); + } + + public Date getLastChangeTime() { + return lastChanged; + } + + public Date getCreateTime() { + return createDate; + } + + public void setLastChanged(Date date) { + lastChanged = date; + } + + public Appointment[] getAppointments() { + updateAppointmentArray(); + return appointments; + } + + public Iterator getReferences() { + return new IteratorChain + ( + super.getReferences() + ,classification.getReferences() + ) + ; + } + + public boolean isRefering(RefEntity object) { + if (super.isRefering(object)) + return true; + return classification.isRefering(object); + } + + public void addAppointment(Appointment appointment) { + checkWritable(); + if (super.isSubEntity((RefEntity) appointment)) + return; + if (appointment.getReservation() != null + && !this.isIdentical(appointment.getReservation())) + throw new IllegalStateException("Appointment '" + appointment + + "' belongs to another reservation :" + + appointment.getReservation()); + ((AppointmentImpl) appointment).setParent(this); + appointmentArrayUpToDate = false; + super.addEntity((RefEntity)appointment); + } + + + public void removeAppointment(Appointment appointment) { + checkWritable(); + if ( findAppointment( appointment ) == null) + return; + appointmentArrayUpToDate = false; + super.removeEntity((RefEntity)appointment); + clearRestrictions(appointment); + if (this.equals(appointment.getReservation())) + ((AppointmentImpl) appointment).setParent(null); + } + + protected void clearRestrictions(Appointment appointment) { + if (restrictions == null) + return; + Iterator it = restrictions.keySet().iterator(); + ArrayList list = null; + while (it.hasNext()) { + Object key = it.next(); + Appointment[] appointments = (Appointment[]) restrictions.get(key); + for ( int i = 0; i < appointments.length; i++) { + if (list == null) + list = new ArrayList(); + list.add(key); + } + } + if (list == null) + return; + it = list.iterator(); + while (it.hasNext()) { + Object key = it.next(); + Appointment[] appointments = (Appointment[]) restrictions.get(key); + ArrayList newApps = new ArrayList(); + for ( int i=0; i< appointments.length; i++) { + if ( !appointments[i].equals( appointment ) ) { + newApps.add( appointments[i] ); + } + } + setRestrictionForId( key, (Appointment[]) newApps.toArray( Appointment.EMPTY_ARRAY) ); + } + } + + public void addAllocatable(Allocatable allocatable) { + checkWritable(); + if (getReferenceHandler().isRefering((RefEntity)allocatable)) + return; + allocatableArrayUpToDate = false; + getReferenceHandler().add((RefEntity)allocatable); + } + + public void removeAllocatable(Allocatable allocatable) { + checkWritable(); + if (!getReferenceHandler().isRefering((RefEntity)allocatable)) + return; + getReferenceHandler().remove((RefEntity)allocatable); + allocatableArrayUpToDate = false; + } + + public Allocatable[] getAllocatables() { + updateAllocatableArrays(); + return allocatables; + } + + public Allocatable[] getResources() { + updateAllocatableArrays(); + return resources; + } + + public Allocatable[] getPersons() { + updateAllocatableArrays(); + return persons; + } + + private void updateAppointmentArray() { + if (appointmentArrayUpToDate) + return; + Collection appointmentList = new ArrayList(); + Iterator it = super.getSubEntities(); + while (it.hasNext()) { + RefEntity o = (RefEntity) it.next(); + if (o.getRaplaType().equals( Appointment.TYPE) ) { + appointmentList.add(o); + // System.out.println("Appointment " + o + " belongs to reservation " + this); + } + } + + appointments = (Appointment[]) appointmentList.toArray(Appointment.EMPTY_ARRAY); + //notwendig? + //Arrays.sort(appointments, new AppointmentStartComparator()); + } + + private void updateAllocatableArrays() { + if (allocatableArrayUpToDate) + return; + Collection allocatableList = new ArrayList(); + Collection resourceList = new ArrayList(); + Collection personList = new ArrayList(); + Iterator it = super.getReferences(); + while (it.hasNext()) { + RefEntity o = (RefEntity) it.next(); + if (o.getRaplaType().equals( Allocatable.TYPE)) { + if (((Allocatable) o).isPerson()) { + personList.add(o); + } else { + resourceList.add(o); + } + allocatableList.add(o); + } + } + allocatables = (Allocatable[]) allocatableList.toArray(Allocatable.ALLOCATABLE_ARRAY); + persons = (Allocatable[]) personList.toArray(Allocatable.ALLOCATABLE_ARRAY); + resources = (Allocatable[]) resourceList.toArray(Allocatable.ALLOCATABLE_ARRAY); + allocatableArrayUpToDate = true; + } + + public boolean hasAllocated(Allocatable allocatable) { + return getReferenceHandler().isRefering((RefEntity)allocatable); + } + + public boolean hasAllocated(Allocatable allocatable,Appointment appointment) { + if (!hasAllocated(allocatable)) + return false; + Appointment[] restrictions = getRestriction(allocatable); + for ( int i = 0; i < restrictions.length; i++) { + if ( restrictions[i].equals( appointment ) ) + return true; + } + return restrictions.length == 0; + } + + public void setRestriction(Allocatable allocatable,Appointment[] appointments) { + Object id = ((RefEntity)allocatable).getId(); + Assert.notNull(id,"Allocatable object has no ID"); + setRestrictionForId(id,appointments); + } + + public void setRestrictionForId(Object id,Appointment[] appointments) { + if (restrictions == null) + restrictions = new HashMap(); + if (appointments == null || appointments.length == 0) + restrictions.remove(id); + else + restrictions.put(id, appointments); + } + + public void addRestrictionForId(Object id,Appointment appointment) { + if (restrictions == null) + restrictions = new HashMap(); + Appointment[] appointmentsNew; + Appointment[] appointments = (Appointment[])restrictions.get( id ); + if ( appointments != null) + { + appointmentsNew = new Appointment[appointments.length + 1]; + System.arraycopy( appointments, 0,appointmentsNew, 0, appointments.length ); + } + else + { + appointmentsNew = new Appointment[1]; + } + appointmentsNew[appointmentsNew.length -1] = appointment; + restrictions.put(id, appointmentsNew); + } + + public Appointment[] getRestriction(Allocatable allocatable) { + Object id = ((RefEntity)allocatable).getId(); + Assert.notNull(id,"Allocatable object has no ID"); + if (restrictions != null) { + Appointment[] restriction = (Appointment[]) restrictions.get(id); + if (restriction != null) + return restriction; + } + return Appointment.EMPTY_ARRAY; + } + + public Appointment[] getAppointmentsFor(Allocatable allocatable) { + Appointment[] restrictedAppointments = getRestriction( allocatable); + if ( restrictedAppointments.length == 0) + return getAppointments(); + else + return restrictedAppointments; + } + + public Allocatable[] getRestrictedAllocatables(Appointment appointment) { + HashSet set = new HashSet(); + Allocatable[] allocatables = getAllocatables(); + for (int i=0;istring +
  • int
  • +
  • date
  • +
  • boolean
  • +
  • rapla:category
  • +@see DynamicType */ +public interface Attribute extends RaplaObject,MultiLanguageNamed,Annotatable { + + final RaplaType TYPE = new RaplaType(Attribute.class, "attribute"); + + AttributeType getType(); + /** Set the type of the Attribute. +Warning: Changing the type after initialization can lead to data loss, + if there are already classifications that use this attribute and the classification value + can't be converted to the new type. Example a non numerical string can't be converted to an int.*/ + void setType(AttributeType type); + void setKey(String key); + /** The Key is identifier in string-form. Keys could be helpfull + for interaction with other modules. An Invoice-Plugin could + work on attributes with a "price" key. Keys also allow for a + better readability of the XML-File. Changing of a key is + possible, but should be used with care. + */ + String getKey(); + Object defaultValue(); + /** converts the passed value to fit the attributes type. + Example Conversions are: +
      +
    • to string: The result of the method toString() will be the new value.
    • +
    • boolean to int: The new value will be 1 when the oldValue is true. Otherwise it is 0.
    • +
    • other types to int: First the value will be converted to string-type. And then the + trimmed string will be parsed for Integer-values. If that is not possible the new value will + be null
    • +
    • to boolean: First the value will be converted to string-type. If the trimmed string equals + "0" or "false" the new value is false. Otherwise it is true
    • +
    + + */ + Object convertValue(Object oldValue); + /** Checks if the passed value matches the attribute type or needs conversion. + @see #convertValue + */ + boolean needsChange(Object value); + + boolean isValid(Object object); + boolean isOptional(); + void setOptional(boolean bOptional); + Object getConstraint(String key); + Class getConstraintClass(String key); + void setConstraint(String key,Object constraint); + String[] getConstraintKeys(); + DynamicType getDynamicType(); + + public static final Attribute[] ATTRIBUTE_ARRAY = new Attribute[0]; + + void setDefaultValue(Object value); + +} + + + + diff --git a/Rapla/src/org/rapla/entities/dynamictype/AttributeAnnotations.java b/Rapla/src/org/rapla/entities/dynamictype/AttributeAnnotations.java new file mode 100644 index 0000000..b5b0ddc --- /dev/null +++ b/Rapla/src/org/rapla/entities/dynamictype/AttributeAnnotations.java @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.dynamictype; + +public interface AttributeAnnotations{ + + String KEY_EDIT_VIEW = "edit-view"; + String KEY_EXPECTED_ROWS = "expected-rows"; + String KEY_EXPECTED_COLUMNS = "expected-columns"; + String VALUE_MAIN_VIEW = "main-view"; + String VALUE_ADDITIONAL_VIEW = "additional-view"; + String VALUE_NO_VIEW = "no-view"; + + + +} + + + + + + + + + + + + diff --git a/Rapla/src/org/rapla/entities/dynamictype/AttributeType.java b/Rapla/src/org/rapla/entities/dynamictype/AttributeType.java new file mode 100644 index 0000000..b1c7a98 --- /dev/null +++ b/Rapla/src/org/rapla/entities/dynamictype/AttributeType.java @@ -0,0 +1,76 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.dynamictype; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +/** Attributes are to DynamicTypes, what properties are to Beans. +Currently Rapla supports the following types: +
  • string
  • +
  • int
  • +
  • date
  • +
  • boolean
  • +
  • rapla:category
  • +@see DynamicType */ +public class AttributeType implements Serializable { + // Don't forget to increase the serialVersionUID when you change the fields + private static final long serialVersionUID = 1; + + private String type; + + static public AttributeType STRING = new AttributeType("string"); + static public AttributeType INT = new AttributeType("int"); + static public AttributeType DATE = new AttributeType("date"); + static public AttributeType BOOLEAN = new AttributeType("boolean"); + static public AttributeType CATEGORY = new AttributeType("rapla:category"); + + private static Map types; + + private AttributeType(String type) { + this.type = type; + if (types == null) { + types = new HashMap(); + } + types.put( type, this); + } + + public boolean is(AttributeType other) { + if ( other == null) + return false; + return type.equals( other.type); + } + + public static AttributeType findForString(String string ) { + return (AttributeType) types.get( string ); + } + + public String toString() { + return type; + } + + public boolean equals( Object other) { + if ( !(other instanceof AttributeType)) + return false; + return is( (AttributeType)other); + } + + public int hashCode() { + return type.hashCode(); + } +} + + + + diff --git a/Rapla/src/org/rapla/entities/dynamictype/Classifiable.java b/Rapla/src/org/rapla/entities/dynamictype/Classifiable.java new file mode 100644 index 0000000..2ced836 --- /dev/null +++ b/Rapla/src/org/rapla/entities/dynamictype/Classifiable.java @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.dynamictype; + + +/** This Interfaces is implemented by all Rapla-Objects that can + * have classification information: Reservation, Resource, Person. + * @see Classification + */ +public interface Classifiable { + Classification getClassification(); + void setClassification(Classification classification); + + final Classifiable[] CLASSIFIABLE_ARRAY = new Classifiable[0]; +} + + + + + + + diff --git a/Rapla/src/org/rapla/entities/dynamictype/Classification.java b/Rapla/src/org/rapla/entities/dynamictype/Classification.java new file mode 100644 index 0000000..f00d0f7 --- /dev/null +++ b/Rapla/src/org/rapla/entities/dynamictype/Classification.java @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.dynamictype; +import java.util.Locale; + +import org.rapla.entities.Named; +/** A Classification is an instance of a DynamicType. It holds the + * attribute values for the attributesof the corresponding type. You + * need one classification for each object you want to + * classify. + */ +public interface Classification extends Named,Cloneable { + DynamicType getType(); + String getName(Locale locale); + Attribute[] getAttributes(); + Attribute getAttribute(String key); + void setValue(Attribute attribute,Object value); + /** calls setValue(getAttribte(key),value)*/ + void setValue(String key,Object value); + /** calls getValue(getAttribte(key))*/ + Object getValue(Attribute attribute); + Object getValue(String key); + /** returns the value as a String in the selected locale.*/ + String getValueAsString(Attribute attribute,Locale locale); + Object clone(); +} + + + + diff --git a/Rapla/src/org/rapla/entities/dynamictype/ClassificationFilter.java b/Rapla/src/org/rapla/entities/dynamictype/ClassificationFilter.java new file mode 100644 index 0000000..70d8d3b --- /dev/null +++ b/Rapla/src/org/rapla/entities/dynamictype/ClassificationFilter.java @@ -0,0 +1,113 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.dynamictype; + +import java.util.Iterator; + +/**

    A new ClassificationFilter for a classifications belonging to the + same DynamicType can be created by the newClassificationFilter of + the corresponding DynamicType object. +

    + +

    You can set rules for the attributes of the DynamicType. A + Classification (object implementing Classifiable) is matched by + the filter when the conditions for each attribute-rule are + matched (AND - function).

    + +

    A condition is an array of size 2, the first field contains the + operator of the condition and the second the test value. + When an attribute-rule has more than one condition, at least + one of the conditions must be matched (OR - function ) . +

    +

    + The following Example matches all classifications + with a title-value that contains either "rapla" or "sourceforge" + ,a size-value that is > 5 and a category-department-value that + is either the departmentA or the departmentB. +

    + +
    +       DynamicType eventType = facade.getDynamicType("event");
    +       ClassificationFilter f = eventType.newClassificationFilter();
    +       f.addRule(
    +                 "title"
    +                 ,new Object {
    +                    {"contains", "rapla"}
    +                    ,{"contains", "sourceforge"}
    +                  });
    +      f.addRule(
    +                 "size"
    +                 ,new Object{
    +                      {">", new Integer(5)}
    +                  });
    +
    +	   Category departemntCategory = facade.getRootCategory().getCategory("departments");
    +       Category departmentA = departmentCategory.getCategory("departmentA");
    +       Category departmentB = departmentCategory.getCategory("departmentB");
    +       f.addRule(
    +                 "department"
    +                 ,new Object{
    +                      {"=", departmentA}
    +                     ,{ "=", departmentB}
    +                  });
    +    
    + + @see Classification + */ +public interface ClassificationFilter extends Cloneable { + DynamicType getType(); + + /** Defines a rule for the passed attribute. + */ + void setRule(int index, String attributeName,Object[][] conditions); + void setRule(int index, Attribute attribute,Object[][] conditions); + /** appends a rule. + * @see #setRule*/ + void addRule(String attributeName,Object[][] conditions); + + /** shortcut to + *
    +     * f.addRule(
    +                 attributeName
    +                 ,new Object{
    +                            {"=", object}}
    +                  });
    +     * 
    + * @param attributeName + * @param object + */ + void addEqualsRule( String attributeName,Object object); + /** shortcut to + *
    +     * f.addRule(
    +                 attributeName
    +                 ,new Object{
    +                            {"is", object}}
    +                  });
    +     * 
    + * @param attributeName + * @param object + */ + void addIsRule( String attributeName,Object object); + int ruleSize(); + Iterator ruleIterator(); + void removeAllRules(); + void removeRule(int index); + + boolean matches(Classification classification); + Object clone(); + + final ClassificationFilter[] CLASSIFICATIONFILTER_ARRAY = new ClassificationFilter[0]; + ClassificationFilter[] toArray(); +} + diff --git a/Rapla/src/org/rapla/entities/dynamictype/ClassificationFilterRule.java b/Rapla/src/org/rapla/entities/dynamictype/ClassificationFilterRule.java new file mode 100644 index 0000000..c64583b --- /dev/null +++ b/Rapla/src/org/rapla/entities/dynamictype/ClassificationFilterRule.java @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.dynamictype; + + +public interface ClassificationFilterRule { + public Attribute getAttribute(); + public String[] getOperators(); + public Object[] getValues(); + +} + diff --git a/Rapla/src/org/rapla/entities/dynamictype/ConstraintIds.java b/Rapla/src/org/rapla/entities/dynamictype/ConstraintIds.java new file mode 100644 index 0000000..41dfddf --- /dev/null +++ b/Rapla/src/org/rapla/entities/dynamictype/ConstraintIds.java @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.dynamictype; + +public interface ConstraintIds{ + String KEY_ROOT_CATEGORY="root-category"; +} + + + + + + + + + + + + diff --git a/Rapla/src/org/rapla/entities/dynamictype/DynamicType.java b/Rapla/src/org/rapla/entities/dynamictype/DynamicType.java new file mode 100644 index 0000000..39b12f4 --- /dev/null +++ b/Rapla/src/org/rapla/entities/dynamictype/DynamicType.java @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.dynamictype; + +import org.rapla.entities.Annotatable; +import org.rapla.entities.Entity; +import org.rapla.entities.MultiLanguageNamed; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.RaplaType; + +/** In rapla it is possible to dynamicly classify a reservation, resource or person with + customized attributes. You can for example define a dynamicType called room with the + attributes name and seats and classify all your room-resources as room. + */ +public interface DynamicType extends Entity,RaplaObject,MultiLanguageNamed,Annotatable +{ + final RaplaType TYPE = new RaplaType(DynamicType.class, "dynamictype"); + + Attribute[] getAttributes(); + /** returns null if the attribute is not found */ + Attribute getAttribute(String key); + void addAttribute(Attribute attribute); + + /** find an attribute in the dynamic type that equals the specified attribute This is usefull if you have the + * persistant version of an attribute and want to discover the editable version in the working copy of a dynamic type */ + Attribute findAttribute(Attribute attribute); + + boolean hasAttribute(Attribute attribute); + void removeAttribute(Attribute attribute); + /** exchange the two attribute positions */ + void exchangeAttributes(int index1, int index2); + void setElementKey(String elementKey); + String getElementKey(); + /* creates a new classification + * @throws IllegalStateException when called from a non persistant instance of DynamicType */ + Classification newClassification(); + /* creates a new classification and tries to fill it with the values of the originalClassification. + * @throws IllegalStateException when called from a non persistant instance of DynamicType */ + Classification newClassification(Classification originalClassification); + /* @throws IllegalStateException when called from a non persistant instance of DynamicType */ + ClassificationFilter newClassificationFilter(); + + final DynamicType[] DYNAMICTYPE_ARRAY = new DynamicType[0]; +} + + + + diff --git a/Rapla/src/org/rapla/entities/dynamictype/DynamicTypeAnnotations.java b/Rapla/src/org/rapla/entities/dynamictype/DynamicTypeAnnotations.java new file mode 100644 index 0000000..1ff8146 --- /dev/null +++ b/Rapla/src/org/rapla/entities/dynamictype/DynamicTypeAnnotations.java @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.dynamictype; + +public interface DynamicTypeAnnotations{ + + String KEY_NAME_FORMAT="nameformat"; + String KEY_COLOR_ATTRIBUTE_KEY="color-attribute-key"; + String KEY_CLASSIFICATION_TYPE="classification-type"; + String VALUE_RESOURCE_CLASSIFICATION="resource"; + String VALUE_RESERVATION_CLASSIFICATION="reservation"; + String VALUE_PERSON_CLASSIFICATION="person"; + + + +} + + + + + + + + + + + + diff --git a/Rapla/src/org/rapla/entities/dynamictype/internal/AttributeImpl.java b/Rapla/src/org/rapla/entities/dynamictype/internal/AttributeImpl.java new file mode 100644 index 0000000..48579d6 --- /dev/null +++ b/Rapla/src/org/rapla/entities/dynamictype/internal/AttributeImpl.java @@ -0,0 +1,415 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.dynamictype.internal; +import java.text.ParseException; +import java.util.Date; +import java.util.HashMap; +import java.util.Locale; + +import org.rapla.components.util.SerializableDateTimeFormat; +import org.rapla.components.util.Tools; +import org.rapla.entities.Category; +import org.rapla.entities.EntityNotFoundException; +import org.rapla.entities.IllegalAnnotationException; +import org.rapla.entities.MultiLanguageName; +import org.rapla.entities.RaplaType; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.AttributeType; +import org.rapla.entities.dynamictype.ConstraintIds; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.internal.CategoryImpl; +import org.rapla.entities.storage.EntityResolver; +import org.rapla.entities.storage.Mementable; +import org.rapla.entities.storage.RefEntity; +import org.rapla.entities.storage.internal.SimpleEntity; +import org.rapla.storage.LocalCache; + +public class AttributeImpl extends SimpleEntity implements Attribute,Mementable,java.io.Serializable { + // Don't forget to increase the serialVersionUID when you change the fields + private static final long serialVersionUID = 2; + + private MultiLanguageName name = new MultiLanguageName(); + private AttributeType type; + private String key; + private boolean bOptional = false; + private HashMap annotations = new HashMap(); + private Object defaultValue =null; + + public final static AttributeType DEFAULT_TYPE = AttributeType.STRING; + + public AttributeImpl() { + this.type = DEFAULT_TYPE; + } + + public AttributeImpl(AttributeType type) { + setType(type); + } + + void setParent(DynamicType parent) { + getReferenceHandler().put("parent",parent); + } + + public DynamicType getDynamicType() { + return (DynamicType)getReferenceHandler().get("parent"); + } + + final public RaplaType getRaplaType() {return TYPE;} + + public AttributeType getType() { + return type; + } + + public void setType(AttributeType type) + { + Object oldValue = defaultValue; + if ( type.equals( AttributeType.CATEGORY)) + { + oldValue = getReferenceHandler().get("default.category"); + } + this.type = type; + defaultValue = convertValue( oldValue); + } + + public MultiLanguageName getName() { + return name; + } + + public void setReadOnly(boolean enable) { + super.setReadOnly( enable ); + name.setReadOnly( enable ); + } + + public String getName(Locale locale) { + return name.getName(locale.getLanguage()); + } + + public String getKey() { + return key; + } + + public void setConstraint(String key,Object constraint) { + checkWritable(); + if ( getConstraintClass( key ) == Category.class ) { + getReferenceHandler().put("constraint." + key,(Category)constraint); + } + } + + public void setDefaultValue(Object object) + { + defaultValue = object; + if ( type.equals( AttributeType.CATEGORY)) + { + getReferenceHandler().put("default.category",(Category)object); + } + } + + public Object getConstraint(String key) { + if ( getConstraintClass( key ) == Category.class ) { + return getReferenceHandler().get("constraint." + key); + } + return null; + } + + public Class getConstraintClass(String key) { + if (key.equals(ConstraintIds.KEY_ROOT_CATEGORY)) { + return Category.class; + } + return String.class; + } + + public String[] getConstraintKeys() { + if (type.equals( AttributeType.CATEGORY)) { + return new String[] {ConstraintIds.KEY_ROOT_CATEGORY}; + } else { + return new String[0]; + } + } + + public void setKey(String key) { + checkWritable(); + this.key = key; + } + + public boolean isValid(Object obj) { + return true; + } + + public boolean isOptional() { + return bOptional; + } + + public void setOptional(boolean bOptional) { + checkWritable(); + this.bOptional = bOptional; + } + + public Object defaultValue() + { + return defaultValue; + } + + public boolean needsChange(Object value) { + if (value == null) + return false; + + if (type.equals( AttributeType.STRING )) { + return !(value instanceof String); + } + if (type.equals( AttributeType.INT )) { + return !(value instanceof Long); + } + if (type.equals( AttributeType.DATE )) { + return !(value instanceof Date); + } + if (type.equals( AttributeType.BOOLEAN )) { + return !(value instanceof Boolean); + } + if (type.equals( AttributeType.CATEGORY )) { + if (!(value instanceof Category)) + return true; + + Category temp = (Category) value; + + // look if the attribute category is a ancestor of the value category + Category rootCategory = (Category) getConstraint(ConstraintIds.KEY_ROOT_CATEGORY); + boolean change = ( rootCategory == null || !rootCategory.isAncestorOf( temp )); + return change; + } + return false; + } + + public Object convertValue(Object value) { + if (type.equals( AttributeType.STRING )) { + if (value == null) + return null; + return value.toString(); + } + if (type.equals( AttributeType.DATE )) { + if (value == null) + return null; + else if (value instanceof Date) + return value; + return null; + } + if (type.equals( AttributeType.INT )) { + if (value == null) + return null; + + if (value instanceof Boolean) + return ((Boolean) value).booleanValue() ? new Long(1) : new Long(0); + String str = value.toString().trim().toLowerCase(); + try { + return new Long(str); + } catch (NumberFormatException ex) { + return null; + } + } + if (type.equals( AttributeType.BOOLEAN )) { + if (value == null) + return Boolean.FALSE; + String str = value.toString().trim().toLowerCase(); + if (str.equals("")) + { + return Boolean.FALSE; + } + if (str.equals("0") || str.equals("false")) + return Boolean.FALSE; + else + return Boolean.TRUE; + } + if (type.equals( AttributeType.CATEGORY )) { + if (value == null) + return null; + if (value instanceof Category) { + Category temp = (Category) value; + Category rootCategory = (Category) getConstraint(ConstraintIds.KEY_ROOT_CATEGORY); + if ( temp != null + && rootCategory != null + && rootCategory.isAncestorOf( temp ) + ) { + return value; + } + } + } + return null; + } + + public String getAnnotation(String key) { + return (String) annotations.get(key); + } + + public String getAnnotation(String key, String defaultValue) { + String annotation = getAnnotation( key ); + return annotation != null ? annotation : defaultValue; + } + + public void setAnnotation(String key,String annotation) throws IllegalAnnotationException { + checkWritable(); + if (annotation == null) { + annotations.remove(key); + return; + } + annotations.put(key,annotation); + } + + public String[] getAnnotationKeys() { + return (String[]) annotations.keySet().toArray(Tools.EMPTY_STRING_ARRAY); + } + + + static private void copy(AttributeImpl source,AttributeImpl dest) { + dest.name = (MultiLanguageName) source.name.clone(); + dest.annotations = (HashMap) source.annotations.clone(); + dest.type = source.getType(); + dest.setKey(source.getKey()); + dest.setOptional(source.isOptional()); + String[] constraintKeys = source.getConstraintKeys(); + for ( int i = 0;i < constraintKeys.length; i++) { + String key = constraintKeys[ i ]; + dest.setConstraint( key, source.getConstraint(key)); + } + dest.setDefaultValue( source.defaultValue()); + } + + public void copy(Object obj) { + super.copy((AttributeImpl)obj); + copy((AttributeImpl) obj,this); + } + + public Object deepClone() { + AttributeImpl clone = new AttributeImpl(); + super.deepClone(clone); + copy(this,clone); + return clone; + } + + public Object clone() { + AttributeImpl clone = new AttributeImpl(); + super.clone(clone); + copy(this,clone); + return clone; + } + + public String toString() { + MultiLanguageName name = getName(); + if (name != null) { + return name.toString()+ " ID='" + getId() + "'"; + } else { + return getKey() + " " + getId(); + } + } + + /** @param idResolver if this is set the category will be resolved over the id value*/ + static public Object parseAttributeValue(Attribute attribute,String text, EntityResolver idResolver) throws ParseException { + AttributeType type = attribute.getType(); + final String trim = text.trim(); + if (type.equals( AttributeType.STRING )) { + return trim; + } + else if (type.equals( AttributeType.CATEGORY )) { + String path = trim; + if (path.length() == 0) { + return null; + } + if (idResolver != null) { + try { + Object id = LocalCache.getId( Category.TYPE, path); + return (Category) idResolver.resolve( id ); + } catch (EntityNotFoundException ex) { + throw new ParseException(ex.getMessage(), 0); + } + } else { + CategoryImpl rootCategory = (CategoryImpl)attribute.getConstraint(ConstraintIds.KEY_ROOT_CATEGORY); + if (rootCategory == null) { + //System.out.println( attribute.getConstraintKeys()); + throw new ParseException("Can't find " + ConstraintIds.KEY_ROOT_CATEGORY + " for attribute " + attribute, 0); + } + try { + return rootCategory.getCategoryFromPath(path); + } catch (Exception ex) { + throw new ParseException(ex.getMessage(), 0); + } + } + } else if (trim.length() == 0) + { + return null; + } + else if (type.equals(AttributeType.BOOLEAN)) { + return trim.equals("true") ? + Boolean.TRUE : Boolean.FALSE; + } else if (type.equals( AttributeType.DATE )) { + return new SerializableDateTimeFormat().parseDate( trim, false); + } else if (type.equals( AttributeType.INT)) { + try { + return new Long( trim ); + } catch (NumberFormatException ex) { + throw new ParseException( ex.getMessage(), 0); + } + } + + throw new ParseException("Unknown attribute type: " + type , 0); + } + + public static String attributeValueToString( Attribute attribute, Object value, boolean idOnly) throws EntityNotFoundException { + AttributeType type = attribute.getType(); + if (type.equals( AttributeType.CATEGORY )) + { + CategoryImpl rootCategory = (CategoryImpl) attribute.getConstraint(ConstraintIds.KEY_ROOT_CATEGORY); + if ( idOnly) { + return ((RefEntity)value).getId().toString(); + } else { + return rootCategory.getPathForCategory((Category)value) ; + } + } + else if (type.equals( AttributeType.DATE )) + { + return new SerializableDateTimeFormat().formatDate( (Date)value ) ; + } + else + { + return value.toString() ; + } + } + + static public class IntStrategy { + String[] constraintKeys = new String[] {"min","max"}; + + public String[] getConstraintKeys() { + return constraintKeys; + } + + public boolean needsChange(Object value) { + return !(value instanceof Long); + } + + public Object convertValue(Object value) { + if (value == null) + return null; + + if (value instanceof Boolean) + return ((Boolean) value).booleanValue() ? new Long(1) : new Long(0); + String str = value.toString().trim().toLowerCase(); + try { + return new Long(str); + } catch (NumberFormatException ex) { + return null; + } + } + } + +} + + + + + diff --git a/Rapla/src/org/rapla/entities/dynamictype/internal/ClassificationFilterImpl.java b/Rapla/src/org/rapla/entities/dynamictype/internal/ClassificationFilterImpl.java new file mode 100644 index 0000000..540c4f3 --- /dev/null +++ b/Rapla/src/org/rapla/entities/dynamictype/internal/ClassificationFilterImpl.java @@ -0,0 +1,386 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.dynamictype.internal; + +import java.util.Date; +import java.util.Iterator; +import java.util.LinkedList; + +import org.rapla.components.util.Assert; +import org.rapla.components.util.iterator.IteratorChain; +import org.rapla.components.util.iterator.NestedIterator; +import org.rapla.entities.Category; +import org.rapla.entities.EntityNotFoundException; +import org.rapla.entities.ReadOnlyException; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.AttributeType; +import org.rapla.entities.dynamictype.Classification; +import org.rapla.entities.dynamictype.ClassificationFilter; +import org.rapla.entities.dynamictype.ClassificationFilterRule; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.storage.CannotExistWithoutTypeException; +import org.rapla.entities.storage.DynamicTypeDependant; +import org.rapla.entities.storage.EntityReferencer; +import org.rapla.entities.storage.EntityResolver; +import org.rapla.entities.storage.RefEntity; +import org.rapla.entities.storage.internal.ReferenceHandler; + +public final class ClassificationFilterImpl + implements + ClassificationFilter + ,DynamicTypeDependant + ,EntityReferencer + ,java.io.Serializable +{ + // Don't forget to increase the serialVersionUID when you change the fields + private static final long serialVersionUID = 1; + + boolean readOnly; + + LinkedList list = new LinkedList(); + transient boolean arrayUpToDate = false; + transient ClassificationFilterRuleImpl[] rulesArray; + ReferenceHandler referenceHandler = new ReferenceHandler(); + + ClassificationFilterImpl(DynamicType dynamicType) { + referenceHandler.put("parent",dynamicType); + } + + public void resolveEntities( EntityResolver resolver) throws EntityNotFoundException { + referenceHandler.resolveEntities( resolver ); + for (Iterator it=list.iterator();it.hasNext();) + { + ((ClassificationFilterRuleImpl) it.next()).resolveEntities( resolver ); + } + } + + public DynamicType getType() { + return (DynamicType) referenceHandler.get("parent"); + } + + public boolean isRefering(RefEntity object) { + if (referenceHandler.isRefering(object)) + return true; + ClassificationFilterRuleImpl[] rules = getRules(); + for (int i=0;i")) + return; + if (operator.equals("=")) + return; + if (operator.equals("contains")) + return; + if (operator.equals("is")) + return; + if (operator.equals("<=")) + return; + if (operator.equals(">=")) + return; + if (operator.equals("<>")) + return; + throw new IllegalArgumentException("operator '" + operator + "' not supported!"); + } + + public void addEqualsRule( String attributeName, Object object ) + { + addRule( attributeName, new Object[][] {{"=",object}}); + } + + public void addIsRule( String attributeName, Object object ) + { + addRule( attributeName, new Object[][] {{"is",object}}); + } + + + public int ruleSize() { + return list.size(); + } + + public Iterator ruleIterator() { + return list.iterator(); + } + + public void removeAllRules() { + checkWritable(); + list.clear(); + arrayUpToDate = false; + } + + public void removeRule(int index) { + checkWritable(); + list.remove(index); + arrayUpToDate = false; + //System.out.println("Rule " + index + " for '" + dynamicType + "' removed."); + } + + private ClassificationFilterRuleImpl[] getRules() { + if (!arrayUpToDate) + rulesArray = (ClassificationFilterRuleImpl[]) list.toArray(new ClassificationFilterRuleImpl[0]); + arrayUpToDate = true; + return rulesArray; + } + + public boolean matches(Classification classification) { + if (!getType().equals(classification.getType())) + return false; + ClassificationFilterRule[] rules = getRules(); + for (int i=0;i=0); + } + } + else if (type.equals( AttributeType.BOOLEAN)) + { + Boolean boolean1 = (Boolean)ruleValue; + Boolean boolean2 = (Boolean)value; + if (boolean1 == null) + { + return (boolean2 == null || boolean2.booleanValue()); + } + if (boolean2 == null) + { + return !boolean1.booleanValue(); + } + return (boolean1.equals(boolean2)); + } + else if (type.equals( AttributeType.INT) || type.equals(AttributeType.DATE)) + { + if(ruleValue == null) { + if (operator.equals("<>")) + if(value == null) + return false; + else + return true; + else if (operator.equals("=")) + if(value == null) + return true; + else + return false; + else + return false; + } + + if(value == null) + return false; + + long long1 = type.equals( AttributeType.INT) ? ((Long) value).longValue() : ((Date) value).getTime(); + long long2 = type.equals( AttributeType.INT) ? ((Long) ruleValue).longValue() : ((Date) ruleValue).getTime(); + + if (operator.equals("<")) + { + return long1 < long2; + } + else if (operator.equals("=")) + { + return long1 == long2; + } + else if (operator.equals(">")) + { + return long1 > long2; + } + else if (operator.equals(">=")) + { + return long1 >= long2; + } + else if (operator.equals("<=")) + { + return long1 >= long2; + } + else if (operator.equals("<>")) + { + return long1 != long2; + } + } + + return false; + } + + boolean hasType(DynamicType type) { + return getType().equals( type); + } + + public boolean needsChange(DynamicType newType) { + if (!hasType( newType )) + return false; + + if ( !newType.getElementKey().equals( getType().getElementKey())) + return true; + + ClassificationFilterRuleImpl[] rules = getRules(); + for (int i=0;inewClassification() of class DynamicType to + * create a classification. Once created it is not possible to change the + * type of a classifiction. But you can replace the classification of an + * object implementing Classifiable with a new one. + * @see DynamicType + * @see org.rapla.entities.dynamictype.Classifiable + */ +public class ClassificationImpl implements Classification,java.io.Serializable, DynamicTypeDependant, EntityReferencer { + // Don't forget to increase the serialVersionUID when you change the fields + private static final long serialVersionUID = 1; + + boolean readOnly = false; + + transient String nameString; + transient boolean isNameUpToDate; + transient ParsedAnnotation lastParsedAnnotation; + + /** stores the nonreference values like integers,boolean and string.*/ + HashMap attributeValueMap = new HashMap(); + + /** stores the references to the dynamictype and the reference values */ + ReferenceHandler referenceHandler = new ReferenceHandler(); + + + ClassificationImpl(DynamicType dynamicType) { + referenceHandler.put("parent",dynamicType); + } + + public void resolveEntities( EntityResolver resolver) throws EntityNotFoundException { + referenceHandler.resolveEntities( resolver); + } + + public void setReadOnly(boolean enable) { + this.readOnly = enable; + } + + public boolean isReadOnly() { + return readOnly; + } + + public void checkWritable() { + if ( readOnly ) + throw new ReadOnlyException( this ); + } + + public boolean isRefering(RefEntity obj) { + return referenceHandler.isRefering(obj); + } + + public Iterator getReferences() { + return referenceHandler.getReferences(); + } + + public DynamicType getType() { + return (DynamicType) referenceHandler.get("parent"); + } + + public String getName(Locale locale) { + DynamicTypeImpl type = (DynamicTypeImpl)getType(); + ParsedAnnotation parsedAnnotation = type.getParsedAnnotation( DynamicTypeAnnotations.KEY_NAME_FORMAT ); + if ( parsedAnnotation == null) { + return type.toString(); + } + + if (isNameUpToDate) + { + if (parsedAnnotation.equals(lastParsedAnnotation)) + return nameString; + } + lastParsedAnnotation = parsedAnnotation; + nameString = parsedAnnotation.formatName(type, this, locale); + isNameUpToDate = true; + return nameString; + } + + public String getValueAsString(Attribute attribute,Locale locale) + { + Object value = getValue(attribute); + if (value == null) + return ""; + if (value instanceof Category) { + Category rootCategory = (Category) attribute.getConstraint(ConstraintIds.KEY_ROOT_CATEGORY); + return ((Category) value).getPath(rootCategory, locale); + } + if (value instanceof Date) { + DateFormat format = DateFormat.getDateInstance(DateFormat.MEDIUM,locale); + format.setTimeZone(DateTools.getTimeZone()); + return format.format((Date) value); + } else { + return value.toString(); + } + } + + public Attribute getAttribute(String key) { + return getType().getAttribute(key); + } + + public Attribute[] getAttributes() { + return getType().getAttributes(); + } + + public boolean needsChange(DynamicType newType) { + if ( !hasType (newType )) { + return false; + } + if ( !newType.getElementKey().equals( getType().getElementKey())) + return true; + Iterator it = referenceHandler.getReferenceKeys(); + while (it.hasNext()) { + String referenceKey = (String)it.next(); + RefEntity attribute = ((RefEntity)findAttributeByReferenceKey( getType(), referenceKey)); + if (attribute == null) + continue; + + if (((DynamicTypeImpl)getType()).hasAttributeChanged( (DynamicTypeImpl)newType , attribute.getId())) + return true; + } + it = attributeValueMap.keySet().iterator(); + while (it.hasNext()) { + Object id = it.next(); + if (((DynamicTypeImpl)getType()).hasAttributeChanged( (DynamicTypeImpl)newType , id)) + return true; + } + return false; + } + + boolean hasType(DynamicType type) { + return getType().equals( type); + } + + public void commitChange(DynamicType type) { + if ( !hasType (type )) { + return; + } + // update referenced values + referenceHandler.put("parent", type); + Iterator it = referenceHandler.getReferenceKeys(); + Collection attributes = new ArrayList(); + Collection removedKeys = new ArrayList(); + Collection removedIds = new ArrayList(); + while (it.hasNext()) { + String referenceKey = (String)it.next(); + if ( referenceKey.equals ("parent") ) + continue; + Attribute attribute = findAttributeByReferenceKey(type, referenceKey ) ; + if (attribute != null ) + attributes.add( attribute); + else + removedKeys.add( referenceKey ); + } + it = attributeValueMap.keySet().iterator(); + while (it.hasNext()) { + Object id = it.next(); + Attribute attribute = findAttribute(type, id ); + if (attribute != null) { + attributes.add ( attribute ); + } else { + removedIds.add( id ); + } + } + + it = attributes.iterator(); + while (it.hasNext()) { + Attribute attribute = (Attribute)it.next(); + Object oldValue = getValue( attribute); + Object newValue = attribute.convertValue(oldValue); + setValue( attribute,newValue); + } + + it = removedKeys.iterator(); + while (it.hasNext()) { + referenceHandler.removeId ( it.next().toString() ); + } + it = removedIds.iterator(); + while (it.hasNext()) { + attributeValueMap.remove ( it.next().toString() ); + } + isNameUpToDate = false; + } + + /** find the attribute of the given type that matches the id */ + private Attribute findAttribute(DynamicType type,Object id) { + Attribute[] typeAttributes = type.getAttributes(); + for (int i=0; i 0) + buf.append(", "); + buf.append( att[i].getKey()); + buf.append("="); + buf.append( getValue( att[i])); + } + return buf.toString(); + } + + public void commitRemove(DynamicType type) throws CannotExistWithoutTypeException + { + throw new CannotExistWithoutTypeException(); + } +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/entities/dynamictype/internal/DynamicTypeImpl.java b/Rapla/src/org/rapla/entities/dynamictype/internal/DynamicTypeImpl.java new file mode 100644 index 0000000..48cf6e1 --- /dev/null +++ b/Rapla/src/org/rapla/entities/dynamictype/internal/DynamicTypeImpl.java @@ -0,0 +1,350 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.dynamictype.internal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Locale; + +import org.rapla.components.util.Tools; +import org.rapla.entities.EntityNotFoundException; +import org.rapla.entities.IllegalAnnotationException; +import org.rapla.entities.MultiLanguageName; +import org.rapla.entities.Named; +import org.rapla.entities.RaplaType; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.Classification; +import org.rapla.entities.dynamictype.ClassificationFilter; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.storage.EntityResolver; +import org.rapla.entities.storage.Mementable; +import org.rapla.entities.storage.RefEntity; +import org.rapla.entities.storage.internal.SimpleEntity; + +public class DynamicTypeImpl extends SimpleEntity implements DynamicType,Named,Mementable,java.io.Serializable +{ + // Don't forget to increase the serialVersionUID when you change the fields + private static final long serialVersionUID = 2; + + // added an attribute array for performance reasons + transient private boolean attributeArrayUpToDate = false; + transient Attribute[] attributes; + + MultiLanguageName name = new MultiLanguageName(); + String elementKey = ""; + + HashMap annotations = new HashMap(); + + public DynamicTypeImpl() { + } + + public void resolveEntities( EntityResolver resolver) throws EntityNotFoundException { + super.resolveEntities( resolver); + attributeArrayUpToDate = false; + } + + public RaplaType getRaplaType() {return TYPE;} + + public Classification newClassification() { + if ( !isPersistant()) { + throw new IllegalStateException("You can only create Classifications from a persistant Version of DynamicType"); + } + final ClassificationImpl classification = new ClassificationImpl(this); + // Array could not be up todate + final Attribute[] attributes2 = getAttributes(); + for ( Attribute att: attributes2) + { + final Object defaultValue = att.defaultValue(); + if ( defaultValue != null) + { + classification.setValue(att, defaultValue); + } + } + return classification; + } + + public Classification newClassification(Classification original) { + if ( !isPersistant()) { + throw new IllegalStateException("You can only create Classifications from a persistant Version of DynamicType"); + } + final ClassificationImpl newClassification = (ClassificationImpl) newClassification(); + { + Attribute[] attributes = original.getAttributes(); + for (int i=0;i 0) + buf.append(", "); + buf.append( att[i].getKey()); + } + } + return buf.toString(); + } + + /** + * @param newType + * @param attributeId + */ + public boolean hasAttributeChanged(DynamicTypeImpl newType, Object attributeId) { + Attribute oldAttribute = findAttributeForId(attributeId ); + Attribute newAttribute = newType.findAttributeForId(attributeId ); + if ((newAttribute == null ) || ( oldAttribute == null)) { + return true; + } + if ( !newAttribute.getKey().equals( oldAttribute.getKey() )) { + return true; + } + if ( !newAttribute.getType().equals( oldAttribute.getType())) { + return true; + } + { + String[] keys = newAttribute.getConstraintKeys(); + String[] oldKeys = oldAttribute.getConstraintKeys(); + if ( keys.length != oldKeys.length) { + return true; + } + for ( int i=0;i< keys.length;i++) { + if ( !keys[i].equals( oldKeys[i]) ) + return true; + Object oldConstr = oldAttribute.getConstraint( keys[i]); + Object newConstr = newAttribute.getConstraint( keys[i]); + if ( oldConstr == null && newConstr == null) + continue; + if ( oldConstr == null || newConstr == null) + return true; + + if ( !oldConstr.equals( newConstr)) + return true; + } + } + return false; + } + +} + + diff --git a/Rapla/src/org/rapla/entities/dynamictype/internal/ParsedAnnotation.java b/Rapla/src/org/rapla/entities/dynamictype/internal/ParsedAnnotation.java new file mode 100644 index 0000000..6b7f639 --- /dev/null +++ b/Rapla/src/org/rapla/entities/dynamictype/internal/ParsedAnnotation.java @@ -0,0 +1,99 @@ +/** + * + */ +package org.rapla.entities.dynamictype.internal; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Locale; + +import org.rapla.components.util.Tools; +import org.rapla.entities.IllegalAnnotationException; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.Classification; +import org.rapla.entities.storage.RefEntity; + +class ParsedAnnotation implements Serializable { + private static final long serialVersionUID = 1; + + /** the terminal format elements*/ + String[] nonVariables; + /** the variable format elements*/ + Object[] variables; + + public ParsedAnnotation(String formatString, DynamicTypeImpl type) throws IllegalAnnotationException { + + ArrayList variablesList = new ArrayList(); + ArrayList nonVariablesList = new ArrayList(); + int pos = 0; + int length = formatString.length(); + while (pos < length) + { + int start = formatString.indexOf('{',pos) + 1; + if (start < 1) { + nonVariablesList.add(formatString.substring(pos, length )); + break; + } + int end = formatString.indexOf('}',start) ; + if (end < 1 ) + throw new IllegalAnnotationException("Closing bracket } missing! in " + formatString); + + nonVariablesList.add(formatString.substring(pos, start -1)); + String key = formatString.substring(start,end).trim(); + Attribute attribute = type.getAttribute(key); + if (attribute != null) { + variablesList.add( ((RefEntity)attribute).getId() ); + } else if (key.equals(type.getElementKey())) { + variablesList.add( type.getId() ); + } else { + throw new IllegalAnnotationException("Attribute for key '" + key + + "' not found but defined in '" + formatString + "'" + + "\n You have probably deleted or renamed the attribute. " + ); + } + pos = end + 1; + } + nonVariables = (String[]) nonVariablesList.toArray(Tools.EMPTY_STRING_ARRAY); + variables = variablesList.toArray(); + } + + public String getExternalRepresentation(DynamicTypeImpl type) { + StringBuffer buf = new StringBuffer(); + for (int i=0; i max) + max = depth; + } + return max + 1; + } + + public void removeCategory(Category category) { + checkWritable(); + if ( findCategory( category ) == null) + return; + childArrayUpToDate = false; + super.removeEntity((RefEntity) category); + if (category.getParent().equals(this)) + ((CategoryImpl)category).setParent(null); + } + + public Category findCategory(Category copy) { + return (Category) super.findEntity((RefEntity)copy); + } + + public MultiLanguageName getName() { + return name; + } + + public void setReadOnly(boolean enable) { + super.setReadOnly( enable ); + name.setReadOnly( enable ); + } + + public String getName(Locale locale) { + return name.getName(locale.getLanguage()); + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + checkWritable(); + this.key = key; + } + + public String getPath(Category rootCategory,Locale locale) { + StringBuffer buf = new StringBuffer(); + if (rootCategory != null && this.equals(rootCategory)) + return ""; + if (this.getParent() != null) { + String path = this.getParent().getPath(rootCategory,locale); + buf.append(path); + if (path.length()>0) + buf.append('/'); + } + buf.append(this.getName(locale)); + return buf.toString(); + } + + public String toString() { + MultiLanguageName name = getName(); + if (name != null) { + return name.toString() + " ID='" + getId() + "'"; + } else { + return getKey() + " " + getId(); + } + } + + + public String getPathForCategory(Category searchCategory) throws EntityNotFoundException { + return getPathForCategory(searchCategory, true); + } + + public String getPathForCategory(Category searchCategory, boolean fail) throws EntityNotFoundException { + StringBuffer buf = new StringBuffer(); + Category category = searchCategory; + Category parent = category.getParent(); + if (category == this) + return ""; + if (parent == null) + throw new EntityNotFoundException("Category has no parents!"); + while (true) { + buf.insert(0,"']"); + buf.insert(0,category.getKey()); + buf.insert(0,"category[key='"); + parent = category.getParent(); + category = parent; + if (parent == null) + { + if ( fail) + { + throw new EntityNotFoundException("Category not found!" + searchCategory); + } + return null; + } + if (!parent.equals(this)) + buf.insert(0,'/'); + else + break; + } + return buf.toString(); + } + + public Category getCategoryFromPath(String path) throws ParseException,EntityNotFoundException { + int start = 0; + int end = 0; + int pos = 0; + Category category = this; + while (category != null) { + start = path.indexOf("'",pos) + 1; + if (start==0) + break; + end = path.indexOf("'",start); + if (end < 0) + throw new ParseException("Invalid xpath expression: " + path,start); + String key = path.substring(start,end); + category = category.getCategory(key); + pos = end + 1; + } + if (category == null) + throw new EntityNotFoundException("could not resolve category xpath expression: " + path); + return category; + } + + public Category findCategory(Object copy) { + return (Category) super.findEntity((RefEntity)copy); + } + + + public String getAnnotation(String key) { + return (String) annotations.get(key); + } + + public String getAnnotation(String key, String defaultValue) { + String annotation = getAnnotation( key ); + return annotation != null ? annotation : defaultValue; + } + + public void setAnnotation(String key,String annotation) throws IllegalAnnotationException { + checkWritable(); + if (annotation == null) { + annotations.remove(key); + return; + } + annotations.put(key,annotation); + } + + public String[] getAnnotationKeys() { + return (String[]) annotations.keySet().toArray(Tools.EMPTY_STRING_ARRAY); + } + + static private void copy(CategoryImpl source,CategoryImpl dest) { + dest.name = (MultiLanguageName) source.name.clone(); + dest.annotations = (HashMap) source.annotations.clone(); + dest.key = source.key; + Iterator it = dest.getSubEntities(); + while ( it.hasNext()) { + ((CategoryImpl)it.next()).setParent(dest); + } + dest.childArrayUpToDate = false; + } + + public void copy(Object obj) { + super.copy((CategoryImpl)obj); + copy((CategoryImpl)obj,this); + } + + public Object deepClone() { + CategoryImpl clone = new CategoryImpl(); + super.deepClone(clone); + copy(this,clone); + return clone; + } + + public Object clone() { + CategoryImpl clone = new CategoryImpl(); + super.clone(clone); + copy(this,clone); + return clone; + } + + + +} + + diff --git a/Rapla/src/org/rapla/entities/internal/ModifiableTimestamp.java b/Rapla/src/org/rapla/entities/internal/ModifiableTimestamp.java new file mode 100644 index 0000000..0981a21 --- /dev/null +++ b/Rapla/src/org/rapla/entities/internal/ModifiableTimestamp.java @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org . | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.entities.internal; +import java.util.Date; + +import org.rapla.entities.Timestamp; +import org.rapla.entities.User; + +public interface ModifiableTimestamp extends Timestamp { + /** updates the last-changed timestamp */ + void setLastChanged(Date date); + void setLastChangedBy( User user); +} diff --git a/Rapla/src/org/rapla/entities/internal/UserImpl.java b/Rapla/src/org/rapla/entities/internal/UserImpl.java new file mode 100644 index 0000000..79847a5 --- /dev/null +++ b/Rapla/src/org/rapla/entities/internal/UserImpl.java @@ -0,0 +1,239 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.internal; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Locale; + +import org.rapla.entities.Category; +import org.rapla.entities.EntityNotFoundException; +import org.rapla.entities.RaplaType; +import org.rapla.entities.User; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.Classification; +import org.rapla.entities.storage.EntityResolver; +import org.rapla.entities.storage.Mementable; +import org.rapla.entities.storage.RefEntity; +import org.rapla.entities.storage.internal.ReferenceHandler; +import org.rapla.entities.storage.internal.SimpleEntity; + +public class UserImpl extends SimpleEntity implements User,Mementable,java.io.Serializable +{ + // Don't forget to increase the serialVersionUID when you change the fields + private static final long serialVersionUID = 1; + + private String username = ""; + private String email = ""; + private String name = ""; + private boolean bAdmin = false; + + transient private boolean groupArrayUpToDate = false; + // The resolved references + transient private Category[] groups; + + final public RaplaType getRaplaType() {return TYPE;} + + public boolean isAdmin() {return bAdmin;} + public String getName() + { + final Allocatable person = getPerson(); + if ( person != null) + { + return person.getName( null ); + } + return name; + } + public String getEmail() { + final Allocatable person = getPerson(); + if ( person != null) + { + final Classification classification = person.getClassification(); + final Attribute attribute = classification.getAttribute("email"); + return attribute != null ? (String)classification.getValue(attribute) : null; + } + return email; + } + + public String getUsername() { return username; } + + public String toString() + { + return getUsername(); + } + + public void setName(String name) { + checkWritable(); + this.name = name; + } + + public void setEmail(String email) { + checkWritable(); + this.email = email; + } + + public void resolveEntities( EntityResolver resolver) throws EntityNotFoundException { + super.resolveEntities(resolver); + if ( email != null && email.trim().length() > 0) + { + try + { + final RefEntity person = resolver.resolve(email); + if ( person instanceof Allocatable) + { + setPerson((Allocatable)person); + } + } + catch (EntityNotFoundException ex) + { + + } + } + } + + public void setUsername(String username) { + checkWritable(); + this.username = username; + } + + public void setAdmin(boolean bAdmin) { + checkWritable(); + this.bAdmin=bAdmin; + } + + public String getName(Locale locale) + { + final Allocatable person = getPerson(); + if ( person != null) + { + return person.getName(locale); + } + return getUsername(); + } + + public void addGroup(Category group) { + checkWritable(); + if (getReferenceHandler().isRefering((RefEntity)group)) + return; + groupArrayUpToDate = false; + getReferenceHandler().add((RefEntity)group); + } + + public boolean removeGroup(Category group) { + checkWritable(); + if (!getReferenceHandler().isRefering((RefEntity)group)) + return false; + groupArrayUpToDate = false; + return getReferenceHandler().remove((RefEntity)group); + } + + public Category[] getGroups() { + updateGroupArray(); + return groups; + } + + public boolean belongsTo( Category group ) { + return getReferenceHandler().isRefering( (RefEntity)group ); + } + + private void updateGroupArray() { + if (groupArrayUpToDate) + return; + Collection groupList = new ArrayList(); + Iterator it = super.getReferences(); + while (it.hasNext()) { + RefEntity o = (RefEntity) it.next(); + if (o.getRaplaType().equals(Category.TYPE)) { + groupList.add(o); + } + } + groups = (Category[]) groupList.toArray(Category.CATEGORY_ARRAY); + groupArrayUpToDate = true; + } + + static private void copy(UserImpl source,UserImpl dest) { + dest.groupArrayUpToDate = false; + + dest.username = source.username; + dest.name = source.name; + dest.email = source.email; + dest.bAdmin = source.bAdmin; + } + + public void copy(Object obj) { + super.copy((UserImpl)obj); + copy((UserImpl) obj,this); + } + + public Object deepClone() { + UserImpl clone = new UserImpl(); + super.deepClone(clone); + copy(this,clone); + return clone; + } + + public Object clone() { + UserImpl clone = new UserImpl(); + super.clone(clone); + copy(this,clone); + return clone; + } + + public int compareTo(Object o) { + if ( ! (o instanceof User)) + { + return super.compareTo( o); + } + + + int result = toString().compareTo( o.toString()); + if (result != 0) + { + return result; + } + else + { + return super.compareTo( (SimpleEntity) o); + } + } + + public void setPerson(Allocatable person) + { + final ReferenceHandler referenceHandler = getReferenceHandler(); + if ( person == null) + { + referenceHandler.put("person", null); + return; + } + final Classification classification = person.getClassification(); + final Attribute attribute = classification.getAttribute("email"); + final String email = attribute != null ? (String)classification.getValue(attribute) : null; + if ( email != null) + { + this.email = email; + referenceHandler.put("person", person); + setName(person.getClassification().getName(null)); + } + } + + public Allocatable getPerson() + { + final ReferenceHandler referenceHandler = getReferenceHandler(); + final Allocatable person = (Allocatable) referenceHandler.get("person"); + return person; + } + + +} diff --git a/Rapla/src/org/rapla/entities/internal/package.html b/Rapla/src/org/rapla/entities/internal/package.html new file mode 100644 index 0000000..d84dfab --- /dev/null +++ b/Rapla/src/org/rapla/entities/internal/package.html @@ -0,0 +1,5 @@ + +Contains the default implementations of the persistent entity-objects in rapla. +@see rapla.sourceforge.net + + diff --git a/Rapla/src/org/rapla/entities/package.html b/Rapla/src/org/rapla/entities/package.html new file mode 100644 index 0000000..17f1c7a --- /dev/null +++ b/Rapla/src/org/rapla/entities/package.html @@ -0,0 +1,6 @@ + +Contains the interfaces of the persistent entity-objects in rapla. +@see rapla.sourceforge.net + + + diff --git a/Rapla/src/org/rapla/entities/storage/CannotExistWithoutTypeException.java b/Rapla/src/org/rapla/entities/storage/CannotExistWithoutTypeException.java new file mode 100644 index 0000000..7ccc83c --- /dev/null +++ b/Rapla/src/org/rapla/entities/storage/CannotExistWithoutTypeException.java @@ -0,0 +1,16 @@ +package org.rapla.entities.storage; + +import org.rapla.framework.RaplaException; + +public class CannotExistWithoutTypeException extends RaplaException { + + public CannotExistWithoutTypeException() { + super("This object cannot exist without a dynamictype. Type cannot be removed."); + } + + /** + * + */ + private static final long serialVersionUID = 1L; + +} diff --git a/Rapla/src/org/rapla/entities/storage/DynamicTypeDependant.java b/Rapla/src/org/rapla/entities/storage/DynamicTypeDependant.java new file mode 100644 index 0000000..707cc3f --- /dev/null +++ b/Rapla/src/org/rapla/entities/storage/DynamicTypeDependant.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.storage; +import org.rapla.entities.dynamictype.DynamicType; + +/** DynamicTypeDependent needs to be implemented by all classes that would be affected by a change to a dynamic type. + * E.g. If you remove or modify an attribute of a dynamic resource type. All resources of this types must take certain actions.*/ +public interface DynamicTypeDependant { + /** returns true if the object needs to be changed with new dynamic type change and false if no modification of the object is requiered. + * Example: If you remove an attribute from a resource type, and one resource of the resourcetype doesnt use this attribute this resource doesnt need modifaction, so it can return false + * @param type The new dynamic type + * */ + public boolean needsChange(DynamicType type); + /** process the change in the object + *Example: If you remove an attribute from a resource type, you should remove the corresponding attriabute value in all resources of the resourcetype + * @param type The new dynamic type*/ + public void commitChange(DynamicType type); + /** throws a CannotExistWithoutTypeException when type cannot be removed*/ + public void commitRemove(DynamicType type) throws CannotExistWithoutTypeException; +} + + + + + + + diff --git a/Rapla/src/org/rapla/entities/storage/EntityReferencer.java b/Rapla/src/org/rapla/entities/storage/EntityReferencer.java new file mode 100644 index 0000000..7e13eb5 --- /dev/null +++ b/Rapla/src/org/rapla/entities/storage/EntityReferencer.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.storage; + +import java.util.Iterator; + +import org.rapla.entities.EntityNotFoundException; + +/** transforms ids into references to + * the corresponding objects. + * @see org.rapla.entities.storage.internal.ReferenceHandler; + */ + +public interface EntityReferencer +{ + void resolveEntities( EntityResolver resolver) throws EntityNotFoundException; + /**Return all References of the object*/ + Iterator getReferences(); + /** returns if the entity is refering to the Object. */ + boolean isRefering(RefEntity object); + + +} + + + + diff --git a/Rapla/src/org/rapla/entities/storage/EntityResolver.java b/Rapla/src/org/rapla/entities/storage/EntityResolver.java new file mode 100644 index 0000000..d8af17b --- /dev/null +++ b/Rapla/src/org/rapla/entities/storage/EntityResolver.java @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.storage; + +import org.rapla.entities.EntityNotFoundException; + + +/** resolves the id to a proper reference to the object. + @see org.rapla.entities.storage.internal.ReferenceHandler +*/ + +public interface EntityResolver +{ + public RefEntity resolve(Object id) throws EntityNotFoundException; +} + + + + diff --git a/Rapla/src/org/rapla/entities/storage/Mementable.java b/Rapla/src/org/rapla/entities/storage/Mementable.java new file mode 100644 index 0000000..e753b6f --- /dev/null +++ b/Rapla/src/org/rapla/entities/storage/Mementable.java @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.storage; + +/**

    Why use a Memento here ?

    + *

    + * Problem: Realization of an undo-feature when editing a object + * The user edits only a clone of the original. + * When the user aborts, the clone will be discarded. Upon committing the + * changes, the original object should be set to the state of + * the clone and the clone should be discarded after that. + *

    + *

    + * The Memento-Pattern is used to get a clone of the original. + * The newly created clone gets the state of the original: +

    + clone = original.deepClone()
    + 
    + * To apply the changes to the original call +
    + original.copy(clone)
    + 
    + *

    + */ + +public interface Mementable extends Cloneable +{ + /** Sets the attributes of the object implementing this interface + * to the attributes stored in the passed objects. + */ + public void copy( T obj ); + + /** Clones the entity and all subentities*/ + public T deepClone(); + + /** Clones the entity while preserving the references to the subentities*/ + public T clone(); +} diff --git a/Rapla/src/org/rapla/entities/storage/RefEntity.java b/Rapla/src/org/rapla/entities/storage/RefEntity.java new file mode 100644 index 0000000..989b360 --- /dev/null +++ b/Rapla/src/org/rapla/entities/storage/RefEntity.java @@ -0,0 +1,61 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.storage; +import java.util.Iterator; + +import org.rapla.entities.Entity; +import org.rapla.entities.RaplaObject; + +/** +The id is the unique key to distinct the entity from all others. +It is needed to safely update the entities and their associations (or aggregations) +with other entities.
    +Note: Use this interface only in the +storage-backend. +*/ +public interface RefEntity extends Entity, RaplaObject, EntityReferencer, Mementable, Comparable { + Object getId(); + void setId(Object id); + + long getVersion(); + void setVersion(long version); + + /** + returns all entities that are aggregated under the entity. + This information is usefull to transparently store the + subentities along with their parent. + * The difference between subEntities and other references is, + * that the subEntities are aggregated instead of associated. That + * means SubEntities should be + *
  • stored, when the parent is stored
  • + *
  • deleted, when the parent is deleted or when they are + * removed from the parent
  • + */ + Iterator getSubEntities(); + + /** returns if the entity contains the subEntity. */ + boolean isParentEntity(RefEntity subEntity); + + /** returns true, if the passed object is an instance of Entity + * and has the same id as the object. If both Entities have + * no ids, the == operator will be applied. + */ + public boolean isIdentical(Entity id2); +} + + + + + + + diff --git a/Rapla/src/org/rapla/entities/storage/internal/ReferenceHandler.java b/Rapla/src/org/rapla/entities/storage/internal/ReferenceHandler.java new file mode 100644 index 0000000..11f0927 --- /dev/null +++ b/Rapla/src/org/rapla/entities/storage/internal/ReferenceHandler.java @@ -0,0 +1,302 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.storage.internal; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; + +import org.rapla.components.util.Assert; +import org.rapla.components.util.Tools; +import org.rapla.entities.EntityNotFoundException; +import org.rapla.entities.storage.RefEntity; +import org.rapla.entities.storage.EntityReferencer; +import org.rapla.entities.storage.EntityResolver; + +/** The ReferenceHandler takes care of serializing and deserializing references to Entity objects. +

    + The references will be serialized to the ids of the corresponding entity. Deserialization of + the ids takes place in the contextualize method. You need to provide an EntityResolver on the Context. +

    +

    +The ReferenceHandler support both named and unnamed References. Use the latter one, if you don't need to refer to the particular reference by name and if you want to keep the order of the references. + +

    +
    +// put a named reference
    +referenceHandler.put("owner",user);
    +
    +// put unnamed reference
    +Iterator it = resources.iterator();
    +while (it.hasNext())
    +   referenceHandler.add(it.next());
    +
    +// returns
    +User referencedUser = referenceHandler.get("owner");
    +
    +// returns both the owner and the resources
    +Itertor references = referenceHandler.getReferences();
    +
    + +

    + @see EntityResolver + */ +public class ReferenceHandler implements EntityReferencer, java.io.Serializable{ + // Don't forget to increase the serialVersionUID when you change the fields + private static final long serialVersionUID = 1; + + private HashMap map; + private ArrayList list; + private transient boolean contextualizeCalled; + + // added for performance reasons + private transient boolean referencesUpToDate; + private transient List referenceList; + + /** + * @see org.rapla.entities.storage.EntityReferencer#resolveEntities(org.rapla.entities.storage.EntityResolver) + */ + public void resolveEntities(EntityResolver resolver) throws EntityNotFoundException { + try { + if (map != null) { + Iterator it = map.values().iterator(); + while (it.hasNext()) { + ReferenceEntry entry = (ReferenceEntry) it.next(); + entry.reference = resolver.resolve(entry.id); + } + } + if (list != null) { + Iterator it = list.iterator(); + while (it.hasNext()) { + ReferenceEntry entry = (ReferenceEntry) it.next(); + entry.reference = resolver.resolve(entry.id); + } + } + } catch (EntityNotFoundException ex) { + clearReferences(); + throw ex; + } + contextualizeCalled = true; + referencesUpToDate = false; + } + + /** Use this method if you want to implement deserialization of the object manualy. + * You have to add the reference-ids to other entities immediatly after the constructor. + * @throws IllegalStateException if contextualize has been called before. + */ + public void addId(Object id) { + if (contextualizeCalled) + throw new IllegalStateException("Contextualize has been called before."); + if (list == null) + list = new ArrayList(3); + Assert.notNull(id); + + ReferenceEntry entry = new ReferenceEntry(); + entry.id = id ; + if ( list.contains( entry)) + { + return; + } + list.add(entry); + } + + /** Use this method if you want to implement deserialization of the object manualy. + * You have to add the reference-ids to other entities immediatly after the constructor. + * @throws IllegalStateException if contextualize has been called before. + */ + public void putId(String key,Object id) { + if (contextualizeCalled) + throw new IllegalStateException("Contextualize has been called before."); + if (map == null) + map = new HashMap(5); + + if (id == null) { + map.remove(key); + return; + } + + ReferenceEntry entry = new ReferenceEntry(); + entry.id = id; + map.put(key, entry); + } + + public boolean removeId(String key) { + if (map == null) + return false; + if ( map.remove(key) != null ) { + referencesUpToDate = false; + return true; + } else { + return false; + } + } + + + public Object getId(String key) { + if (map == null) + throw new IllegalStateException("Map is empty."); + ReferenceEntry entry = (ReferenceEntry)map.get(key); + if (entry != null) + return entry.id; + throw new IllegalStateException("Key not found." + key); + } + + public void put(String key,Object obj) { + if (map == null) + map = new HashMap(5); + if (obj == null) { + map.remove(key); + return; + } + + ReferenceEntry entry = new ReferenceEntry(); + RefEntity entity = (RefEntity) obj; + entry.id = entity.getId() ; + entry.reference = entity; + map.put(key,entry); + referencesUpToDate = false; + } + + public RefEntity get(String key) { + if (map == null) + return null; + ReferenceEntry entry = (ReferenceEntry)map.get(key); + if (entry == null) + return null; + return entry.reference; + } + + + public void add(RefEntity entity) { + if (isRefering(entity)) + return; + if (list == null) + list = new ArrayList(3); + ReferenceEntry entry = new ReferenceEntry(); + entry.id = entity.getId() ; + entry.reference = entity; + list.add(entry); + referencesUpToDate = false; + } + + public boolean remove(RefEntity entity) { + if (!isRefering(entity)) { + return false; + } + if (list != null) { + Iterator it = list.iterator(); + while (it.hasNext()) { + ReferenceEntry entry = (ReferenceEntry) it.next(); + if (entry.reference.equals(entity)) + it.remove(); + } + } + if (map != null) { + Iterator it = map.keySet().iterator(); + while (it.hasNext()) { + ReferenceEntry entry = (ReferenceEntry) map.get(it.next()); + if (entry.reference.equals(entity)) + it.remove(); + } + } + referencesUpToDate = false; + return true; + } + + private Collection getReferenceList() { + if (referencesUpToDate) + return referenceList; + referenceList = new ArrayList(5); + if (list != null) { + Iterator it = list.iterator(); + while (it.hasNext()) { + ReferenceEntry entry = (ReferenceEntry) it.next(); + if (entry.reference == null) + throw new IllegalStateException("Contextualize was not called. References need to be resolved in context."); + referenceList.add(entry.reference); + } + } + if (map != null) { + Iterator it = map.keySet().iterator(); + while (it.hasNext()) { + ReferenceEntry entry = (ReferenceEntry) map.get(it.next()); + if (entry.reference == null) + throw new IllegalStateException("Contextualize was not called. References need to be resolved in context."); + referenceList.add(entry.reference); + } + } + referencesUpToDate = true; + return referenceList; + } + + public boolean isRefering(RefEntity obj) { + if (list == null && map == null) + return false; + Collection referenceList = getReferenceList(); + return referenceList.contains(obj); + } + + public Iterator getReferences() { + if (list == null && map == null) + return Tools.EMPTY_ITERATOR; + return getReferenceList().iterator(); + } + + public Iterator getReferenceKeys() { + if (map == null) + return Tools.EMPTY_ITERATOR; + return map.keySet().iterator(); + } + + public void clearReferences() { + if (map != null) + map.clear(); + if (list != null) + list.clear(); + referencesUpToDate = false; + } + + public Object clone() { + ReferenceHandler clone = new ReferenceHandler(); + if (map != null) + clone.map = (HashMap) map.clone(); + if (list != null) + clone.list = (ArrayList) list.clone(); + clone.referencesUpToDate = false; + return clone; + } + + class ReferenceEntry implements java.io.Serializable { + private static final long serialVersionUID = 1; + transient RefEntity reference; + Object id; + public boolean equals(Object obj) + { + if ( !(obj instanceof ReferenceEntry)) + { + return false; + } + Object id2 = ((ReferenceEntry)obj).id; + if ( id2 == null) + { + return false; + } + return id2.equals( id); + } + + } + + +} diff --git a/Rapla/src/org/rapla/entities/storage/internal/SimpleEntity.java b/Rapla/src/org/rapla/entities/storage/internal/SimpleEntity.java new file mode 100644 index 0000000..83bffd9 --- /dev/null +++ b/Rapla/src/org/rapla/entities/storage/internal/SimpleEntity.java @@ -0,0 +1,282 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.storage.internal; + +import java.util.ArrayList; +import java.util.Iterator; + +import org.rapla.components.util.Assert; +import org.rapla.entities.Entity; +import org.rapla.entities.EntityNotFoundException; +import org.rapla.entities.ReadOnlyException; +import org.rapla.entities.User; +import org.rapla.entities.storage.EntityReferencer; +import org.rapla.entities.storage.EntityResolver; +import org.rapla.entities.storage.Mementable; +import org.rapla.entities.storage.RefEntity; + +/** Base-class for all Rapla Entity-Implementations. Provides services + * for deep cloning and serialization of references. {@link ReferenceHandler} +*/ + +public abstract class SimpleEntity implements RefEntity, EntityReferencer , java.io.Serializable +{ + // Don't forget to increase the serialVersionUID when you change the fields + private static final long serialVersionUID = 2; + private SimpleIdentifier id; + private long version = 0; + + ReferenceHandler subEntityHandler = new ReferenceHandler(); + ReferenceHandler referenceHandler = new ReferenceHandler(); + + transient boolean readOnly = false; + + public SimpleEntity() { + + } + + public void checkWritable() { + if ( readOnly ) + throw new ReadOnlyException( this ); + } + + public boolean isPersistant() { + return isReadOnly(); + } + + public void resolveEntities( EntityResolver resolver) throws EntityNotFoundException { + referenceHandler.resolveEntities( resolver); + subEntityHandler.resolveEntities( resolver ); + } + + public void setReadOnly(boolean enable) { + this.readOnly = enable; + Iterator it = getSubEntityHandler().getReferences(); + while (it.hasNext()) { + ((SimpleEntity) it.next()).setReadOnly(enable); + } + } + + public boolean isReadOnly() { + return readOnly; + } + + public User getOwner() { + return (User) referenceHandler.get("owner"); + } + + public void setOwner(User owner) { + referenceHandler.put("owner",owner); + } + + public User getLastChangedBy() { + return (User) referenceHandler.get("last_changed_by"); + } + + public void setLastChangedBy(User user) { + referenceHandler.put("last_changed_by",user); + } + + + protected boolean isSubEntity(RefEntity obj) { + return subEntityHandler.isRefering(obj); + } + + protected void addEntity(RefEntity entity) { + subEntityHandler.add(entity); + } + + public ReferenceHandler getReferenceHandler() { + return referenceHandler; + } + + public ReferenceHandler getSubEntityHandler() { + return subEntityHandler; + } + + protected void removeEntity(RefEntity entity) { + subEntityHandler.isRefering(entity); + subEntityHandler.remove(entity); + } + + + /** sets the identifier for an object. The identifier should be + * unique accross all entities (not only accross the entities of a + * the same type). Once set, the identifier for an object should + * not change. The identifier is necessary to store the relationsships + * between enties. + * @see SimpleIdentifier + */ + + public void setId(Object id) { + this.id= (SimpleIdentifier)id; + } + + /** @return the identifier of the object. + * @see SimpleIdentifier + */ + final public Object getId() { + return id; + } + + final public boolean isIdentical(Entity ob2) { + return equals( ob2); + } + + /** two Entities are equal if they are identical. + * @see #isIdentical + */ + final public boolean equals(Object o) { + if (!( o instanceof SimpleEntity)) + { + return false; + } + SimpleEntity e2 = (SimpleEntity) o; + SimpleIdentifier id2 = e2.id; + if ( id2== null || id == null) + return e2 == this; + return (id2.key == id.key && id2.type == id.type); + + } + + /** The hashcode of the id-object will be returned. + * @return the hashcode of the id. + * @throws IllegalStateException if no id is set. + */ + public int hashCode() { + if ( id != null) { + return id.hashCode(); + } else { + throw new IllegalStateException("Id not set for type '" + getRaplaType() + + "'. You must set an Id before you can use the hashCode method." + ); + } + } + + public void setVersion(long version) { + this.version= version; + } + + public long getVersion() { + return version; + } + + public Iterator getSubEntities() { + return getSubEntityHandler().getReferences(); + } + + public Iterator getReferences() { + return getReferenceHandler().getReferences(); + } + + public boolean isRefering(RefEntity entity) { + ReferenceHandler referenceHandler = getReferenceHandler(); + return referenceHandler.isRefering(entity); + } + + public boolean isParentEntity(RefEntity object) { + return getSubEntityHandler().isRefering(object); + } + + static private void copy(SimpleEntity source,SimpleEntity dest,boolean deepCopy) { + Assert.isTrue(source != dest,"can't copy the same object"); + + dest.referenceHandler = (ReferenceHandler) source.referenceHandler.clone(); + + ArrayList newEntities = new ArrayList(); + Iterator it = source.getSubEntityHandler().getReferences(); + if (deepCopy){ + while (it.hasNext()) { + Mementable entity = (Mementable) it.next(); + Mementable oldEntity = (Mementable) dest.findEntity((RefEntity)entity); + if (oldEntity != null) { + oldEntity.copy(entity); + newEntities.add( oldEntity); + } else { + newEntities.add( entity.deepClone()); + } + } + } else { + while (it.hasNext()) { + Mementable entity = (Mementable) it.next(); + Mementable oldEntity = (Mementable) dest.findEntity((RefEntity)entity); + if (oldEntity != null) { + newEntities.add( oldEntity); + } else { + newEntities.add( entity); + } + } + } + dest.getSubEntityHandler().clearReferences(); + it = newEntities.iterator(); + while (it.hasNext()) { + RefEntity entity = (RefEntity) it.next(); + dest.addEntity( entity ); + } + // In a copy operation the target/destination object should always be writable + dest.readOnly = false; + dest.setVersion(source.getVersion()); + } + + /** find the sub-entity that has the same id as the passed copy. Returns null, if the entity was not found. */ + public Object findEntity(RefEntity copy) { + Iterator it = getSubEntities(); + while (it.hasNext()) { + RefEntity entity = (RefEntity)it.next(); + if (entity.equals(copy)) { + return entity; + } + } + return null; + } + + /** copies the references from the entity to this */ + protected void copy(SimpleEntity entity) { + copy(entity,this,false); + } + + protected void deepClone(SimpleEntity clone) { + clone.setId(id); + copy(this,clone,true); + } + + protected void clone(SimpleEntity clone) { + clone.setId(id); + copy(this,clone,false); + } + + abstract public T clone(); + + public String toString() { + if (id != null) + return id.toString(); + return "no id for " + super.toString(); + } + + public int compareTo(T o) { + if ( o == this ) + { + return 0; + } + SimpleIdentifier id1 = id; + SimpleIdentifier id2 = (SimpleIdentifier)((RefEntity) o).getId(); + if ( equals( o)) + return 0; + return (id1.getKey() < id2.getKey()) ? -1 : 1; + + } +} + + + + diff --git a/Rapla/src/org/rapla/entities/storage/internal/SimpleIdentifier.java b/Rapla/src/org/rapla/entities/storage/internal/SimpleIdentifier.java new file mode 100644 index 0000000..1c54ff1 --- /dev/null +++ b/Rapla/src/org/rapla/entities/storage/internal/SimpleIdentifier.java @@ -0,0 +1,99 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.storage.internal; + +import java.io.IOException; + +import org.rapla.entities.RaplaType; + + +/* +An identifier could be something like a URI. It is used for: +
      +
    1. Lookup or store the identified objects.
    2. +
    3. Distinct the identified objects: Two objects are identical if and + only if obj1.getId().equals(obj2.getId()). +
    4. +
    5. Serialize/Deserialize relationsships (e.g. references) between + objects.
    6. +
    +Two conditions should hold for all identifiers: +
      +
    1. An identifier is an immutable object.
    2. + +
    3. Every object that has got an identifier should keep it for it's + lifetime. There is one exception: If it is possible to + serialize/deserialize the object-map that no relationship + information get's lost and obj1.getId().equals(obj2.getId()) returns + the same information with the new ids. This exception is important, + if we want to serialize data to an XML-File.
    4. +
    +*/ +public class SimpleIdentifier implements java.io.Serializable +{ + // Don't forget to increase the serialVersionUID when you change the fields + private static final long serialVersionUID = 1; + + String type = null; + int key; + transient String name; + + private void readObject(java.io.ObjectInputStream in) + throws IOException, ClassNotFoundException { + in.defaultReadObject(); + type = type.intern(); + } + + public SimpleIdentifier(RaplaType type,int key) { + this.type = type.toString().intern(); + this.key = key; + } + + public boolean equals(Object o) { + if ( o == null) + { + return false; + } + SimpleIdentifier ident = (SimpleIdentifier)o; + return (ident.key == key && ident.type == type); + } + + public int hashCode() { + int typeHc; + if ( type != null) { + typeHc = type.hashCode(); + } else { + typeHc = 0; + } + return typeHc+ typeHc * key; + } + + public int getKey() { + return key; + } + + public String getTypeName() { + return type; + } + + public String toString() { + if (name == null) + name = type + "_" + key; + return name; + } + +} + + + + diff --git a/Rapla/src/org/rapla/examples/RaplaConnectorTest.java b/Rapla/src/org/rapla/examples/RaplaConnectorTest.java new file mode 100644 index 0000000..dab0670 --- /dev/null +++ b/Rapla/src/org/rapla/examples/RaplaConnectorTest.java @@ -0,0 +1,47 @@ +package org.rapla.examples; + +import java.util.Locale; + +import org.apache.avalon.framework.logger.ConsoleLogger; +import org.rapla.RaplaMainContainer; +import org.rapla.entities.domain.Allocatable; +import org.rapla.facade.ClientFacade; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.framework.StartupEnvironment; +/* Simple demonstration for connecting your app and importing some users. See sources*/ +public class RaplaConnectorTest +{ + public static void main(String[] args) { + final ConsoleLogger logger = new ConsoleLogger( ConsoleLogger.LEVEL_INFO); + + try + { + // Connects to http://localhost:8051/ + // and calls rapla/rpc/methodNames for interacting + StartupEnvironment env = new SimpleConnectorStartupEnvironment( "localhost", 8051,"/", false, logger); + RaplaMainContainer container = new RaplaMainContainer( env); + RaplaContext context = container.getContext(); + + // get an interface to the facade and login + ClientFacade facade = (ClientFacade)context.lookup(ClientFacade.ROLE); + + if ( !facade.login( "admin", "".toCharArray()) ) { + throw new RaplaException("Can't login"); + } + + // query resouce + Allocatable firstResource = facade.getAllocatables() [0] ; + logger.info( firstResource.getName( Locale.getDefault())); + + // cleanup the Container + container.dispose(); + } + catch ( Exception e ) + { + logger.error("Could not start test ", e ); + } + + } + +} diff --git a/Rapla/src/org/rapla/examples/RaplaImportUsers.java b/Rapla/src/org/rapla/examples/RaplaImportUsers.java new file mode 100644 index 0000000..cdee761 --- /dev/null +++ b/Rapla/src/org/rapla/examples/RaplaImportUsers.java @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.examples; +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; + +import org.apache.avalon.framework.logger.ConsoleLogger; +import org.rapla.RaplaMainContainer; +import org.rapla.components.util.Tools; +import org.rapla.entities.Category; +import org.rapla.entities.User; +import org.rapla.facade.ClientFacade; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.framework.StartupEnvironment; + + +/**Demonstration for connecting your app and importing some users + */ +public class RaplaImportUsers { + + public static void main(String[] args) { + if ( args.length< 1 ) { + System.out.println("Usage: filename"); + System.out.println("Example: users.csv "); + return; + } + + final ConsoleLogger logger = new ConsoleLogger( ConsoleLogger.LEVEL_INFO); + try + { + StartupEnvironment env = new SimpleConnectorStartupEnvironment( "localhost", 8051, "/",false, logger); + RaplaMainContainer container = new RaplaMainContainer( env); + importFile( container.getContext(), args[0] ); + // cleanup the Container + container.dispose(); + } + catch ( Exception e ) + { + logger.error("Could not start test ", e ); + } + + } + + private static void importFile(RaplaContext context,String filename) throws Exception { + + System.out.println(" Please enter the admin password "); + BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); + String adminPass = stdin.readLine(); + + // get an interface to the facade and login + ClientFacade facade = (ClientFacade)context.lookup(ClientFacade.ROLE); + if ( !facade.login("admin", adminPass.toCharArray() ) ) { + throw new RaplaException("Can't login"); + } + importUsers( facade, new FileReader( filename )); + facade.logout(); + } + + public static void importUsers(ClientFacade facade, Reader reader) throws RaplaException, IOException { + String[][] entries = Tools.csvRead( reader, 5 ); + Category rootCategory = facade.getUserGroupsCategory(); + for ( int i=0;iWarning can be null + */ + public User getUser() + { + return m_user; + } + + public Allocatable getAllocatable() + { + return m_allocatable; + } + + public Appointment getNewAppointment() + { + return m_newAppointment; + } + + public Reservation getNewReservation() + { + return m_newReservation; + } + + /** only available if type is "change" */ + public Appointment getOldAppointment() + { + return m_oldAppointment; + } + +} diff --git a/Rapla/src/org/rapla/facade/AllocationChangeListener.java b/Rapla/src/org/rapla/facade/AllocationChangeListener.java new file mode 100644 index 0000000..f27851b --- /dev/null +++ b/Rapla/src/org/rapla/facade/AllocationChangeListener.java @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.facade; + + +/** After a store all registered ChangeListeners get notified by calling + * the trigger method. A list with all changes is passed. + * At the moment only AllocationChangeEvents are triggered. + * By this you can get notified, when any Reservation changes. + * The difference between the UpdateEvent and a ChangeEvent is, + * that the UpdateEvent contains the new Versions of all updated enties, + * while a ChangeEvent contains Information about a single change. + * That change can be calculated as with the AllocationChangeEvent, which + * represents a single allocation change for one allocatable object + * ,including information about the old allocation and the new one. + * @see AllocationChangeEvent + */ +public interface AllocationChangeListener +{ + void changed(AllocationChangeEvent[] changeEvents); + /** + * Return true if you want the notification to + * be synchronized with the awt event-queue using {@link + * javax.swing.SwingUtilities#invokeLater}. Use this to avaoid + * synchronization problems with swing guis. + */ + boolean isInvokedOnAWTEventQueue(); +} diff --git a/Rapla/src/org/rapla/facade/ClientFacade.java b/Rapla/src/org/rapla/facade/ClientFacade.java new file mode 100644 index 0000000..e32c7d4 --- /dev/null +++ b/Rapla/src/org/rapla/facade/ClientFacade.java @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.facade; + +import org.rapla.storage.StorageOperator; + +/** A collection of all module-interfaces +*/ + +public interface ClientFacade + extends + UserModule + ,ModificationModule + ,QueryModule + ,UpdateModule +{ + String ROLE = ClientFacade.class.getName(); + StorageOperator getOperator(); +} + + + + + diff --git a/Rapla/src/org/rapla/facade/Conflict.java b/Rapla/src/org/rapla/facade/Conflict.java new file mode 100644 index 0000000..87ad6dd --- /dev/null +++ b/Rapla/src/org/rapla/facade/Conflict.java @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.facade; + +import org.rapla.entities.Named; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.RaplaType; +import org.rapla.entities.User; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Reservation; + +/** + * A conflict is the allocation of the same resource at the same time by different + * reservations. There's one conflict for each resource and each overlapping of + * two allocations. So if there are 3 reservations that allocate the same 2 resources + * on 2 days of the week, then we got ( 3 * 2 ) * 2 * 2 = 24 conflicts. Thats + * 3 reservations, each conflicting with two other 2 reservations on 2 days with 2 resources. + * + * @version 1.0 + * @author Christopher Kohlhaas + */ + +public interface Conflict extends Named, RaplaObject +{ + static public final RaplaType TYPE = new RaplaType(Conflict.class,"conflict"); + /** @return the first Reservation, that is involed in the conflict.*/ + public Reservation getReservation1(); + /** The appointment of the first reservation, that causes the conflict. */ + public Appointment getAppointment1(); + /** @return the allocatable, allocated for the same time by two different reservations. */ + public Allocatable getAllocatable(); + /** @return the second Reservation, that is involed in the conflict.*/ + public Reservation getReservation2(); + /** @return The User, who created the second Reservation.*/ + public User getUser2(); + /** The appointment of the second reservation, that causes the conflict. */ + public Appointment getAppointment2(); + + public static final Conflict[] CONFLICT_ARRAY= new Conflict[] {}; + +} + + + + + + + + + + diff --git a/Rapla/src/org/rapla/facade/ModificationEvent.java b/Rapla/src/org/rapla/facade/ModificationEvent.java new file mode 100644 index 0000000..f409bcc --- /dev/null +++ b/Rapla/src/org/rapla/facade/ModificationEvent.java @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.facade; + +import java.util.Set; + +/** Encapsulate the changes that are made in the backend-store.*/ +public interface ModificationEvent +{ + /** returns if the objects has changed.*/ + public boolean hasChanged(Object object); + + /** returns if the objects was removed.*/ + public boolean isRemoved(Object object); + /** returns if the objects has changed or was removed.*/ + public boolean isModified(Object object); + + public Set getRemoved(Set col); + + public Set getChanged(Set col); + + /** returns all removed objects .*/ + public Set getRemoved(); + + /** returns all changed object .*/ + public Set getChanged(); + } + + + + diff --git a/Rapla/src/org/rapla/facade/ModificationListener.java b/Rapla/src/org/rapla/facade/ModificationListener.java new file mode 100644 index 0000000..d24674d --- /dev/null +++ b/Rapla/src/org/rapla/facade/ModificationListener.java @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.facade; +import java.util.EventListener; + +import org.rapla.framework.RaplaException; + +/** Classes implementing this interface will be notified when changes to + * reservations or resources occurred. The listener can be registered by calling + * addModificationListener of the UpdateModule
    + * Don't forget to remove the listener by calling removeModificationLister + * when no longer needed. + * @author Christopher Kohlhaas + * @see UpdateModule + * @see ModificationEvent + */ + +public interface ModificationListener extends EventListener { + /** this notifies all listeners that data in the rapla-backend has changed. + * The {@link ModificationEvent} describes these changes. + */ + void dataChanged(ModificationEvent evt) throws RaplaException; + /** + * Return true if you want the notification to + * be synchronized with the awt event-queue using {@link + * javax.swing.SwingUtilities#invokeLater}. Use this to avaoid + * synchronization problems with swing guis. + * + */ + boolean isInvokedOnAWTEventQueue(); + +} + + + + + diff --git a/Rapla/src/org/rapla/facade/ModificationModule.java b/Rapla/src/org/rapla/facade/ModificationModule.java new file mode 100644 index 0000000..ae81d43 --- /dev/null +++ b/Rapla/src/org/rapla/facade/ModificationModule.java @@ -0,0 +1,98 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.facade; + +import java.util.Collection; +import java.util.Date; +import java.util.Map; + +import org.rapla.entities.Category; +import org.rapla.entities.Entity; +import org.rapla.entities.User; +import org.rapla.entities.configuration.CalendarModelConfiguration; +import org.rapla.entities.configuration.RaplaMap; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Period; +import org.rapla.entities.domain.RepeatingType; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.AttributeType; +import org.rapla.entities.dynamictype.ClassificationFilter; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.framework.RaplaException; +/** All methods that allow modifing the entity-objects. +*/ + +public interface ModificationModule { + /** check if the reservation can be saved */ + void checkReservation(Reservation reservation) throws RaplaException; + /** creates a new Rapla Map. Keep in mind that only RaplaObjects and Strings are allowed as entries for a RaplaMap!*/ + RaplaMap newRaplaMap(Map map); + /** creates an ordered RaplaMap with the entries of the collection as values and their position in the collection from 1..n as keys*/ + RaplaMap newRaplaMap(Collection col); + + /** WARNING! API could change for this method + * @param extensionMap can be null*/ + CalendarModelConfiguration newRaplaCalendarModel(RaplaMap selected , ClassificationFilter[] allocatableFilter, ClassificationFilter[] eventFilter, String title, Date startDate,Date endDate, Date selectedDate, String view, RaplaMap optionMap); + + Reservation newReservation() throws RaplaException; + Appointment newAppointment(Date startDate,Date endDate) throws RaplaException; + Appointment newAppointment(Date startDate,Date endDate, RepeatingType repeatingType, int repeatingDuration) throws RaplaException; + Allocatable newResource() throws RaplaException; + Allocatable newPerson() throws RaplaException; + Period newPeriod() throws RaplaException; + Category newCategory() throws RaplaException; + Attribute newAttribute(AttributeType attributeType) throws RaplaException; + DynamicType newDynamicType(String classificationType) throws RaplaException; + User newUser() throws RaplaException; + + /** Clones an entity. The entities will get new identifier and + won't be equal to the original. The resulting object is not persistant and therefore + can be editet. + */ + Entity clone(Entity obj) throws RaplaException; + + /** This call will be delegated to the {@link org.rapla.storage.StorageOperator}. It + * returns an editable working copy of an object. Only objects return by this method and new objects are editable. + * To get the persistant, non-editable version of a working copy use {@link #getPersistant} */ + Entity edit(Entity obj) throws RaplaException; + + /** Returns the persistant version of a working copy. + * Throws an {@link org.rapla.entities.EntityNotFoundException} when the + * object is not found + * @see #edit + * @see #clone + */ + Entity getPersistant(Entity working) throws RaplaException; + + /** This call will be delegated to the {@link org.rapla.storage.StorageOperator} */ + void storeObjects(Entity[] obj) throws RaplaException; + /** @see #storeObjects(Entity[]) */ + void store(Entity obj) throws RaplaException; + /** This call will be delegated to the {@link org.rapla.storage.StorageOperator} */ + void removeObjects(Entity[] obj) throws RaplaException; + /** @see #removeObjects(Entity[]) */ + void remove(Entity obj) throws RaplaException; + + /** stores and removes objects in the one transaction + * @throws RaplaException */ + void storeAndRemove( Entity[] storedObjects, Entity[] removedObjects) throws RaplaException; + + +} + + + + + diff --git a/Rapla/src/org/rapla/facade/PeriodModel.java b/Rapla/src/org/rapla/facade/PeriodModel.java new file mode 100644 index 0000000..4e95b61 --- /dev/null +++ b/Rapla/src/org/rapla/facade/PeriodModel.java @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.facade; +import java.util.Date; +import java.util.List; + +import org.rapla.entities.domain.Period; + +/** ListModel that contains all periods. Updates the list automatically if a period is added, changed or deleted. + * */ +public interface PeriodModel +{ + /** returns the first matching period or null if no period matches.*/ + public Period getPeriodFor(Date date); + public Period getNearestPeriodForDate(Date date); + public Period getNearestPeriodForStartDate(Date date); + public Period getNearestPeriodForStartDate(Date date, Date endDate); + public Period getNearestPeriodForEndDate(Date date); + + /** return all matching periods.*/ + public List getPeriodsFor(Date date); + public int getSize(); + public Period[] getAllPeriods(); + +} + + + diff --git a/Rapla/src/org/rapla/facade/QueryModule.java b/Rapla/src/org/rapla/facade/QueryModule.java new file mode 100644 index 0000000..2a0d116 --- /dev/null +++ b/Rapla/src/org/rapla/facade/QueryModule.java @@ -0,0 +1,128 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.facade; +import java.util.Date; + +import org.rapla.entities.Category; +import org.rapla.entities.User; +import org.rapla.entities.configuration.Preferences; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Period; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.ClassificationFilter; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.framework.RaplaException; +/** Methods for quering the various entities of the backend +*/ + +public interface QueryModule +{ + /** returns all DynamicTypes matching the specified classification + possible keys are reservation, person and resource. + @see org.rapla.entities.dynamictype.DynamicTypeAnnotations + */ + DynamicType[] getDynamicTypes(String classificationType) throws RaplaException; + + /** returns the DynamicType with the passed elementKey */ + DynamicType getDynamicType(String elementKey) throws RaplaException; + + /** returns The root category. */ + Category getSuperCategory(); + + /** returns The category that contains the all user-groups of rapla */ + Category getUserGroupsCategory() throws RaplaException; + + + /** returns all users */ + User[] getUsers() throws RaplaException; + + /** returns the user with the specified username */ + User getUser(String username) throws RaplaException; + + /** returns all allocatables that match the passed ClassificationFilter. If null all readable allocatables are returned*/ + Allocatable[] getAllocatables(ClassificationFilter[] filters) throws RaplaException; + + /** returns all readable allocatables, same as getAllocatables(null)*/ + Allocatable[] getAllocatables() throws RaplaException; + + /** returns the reservations of the specified user in the specified interval + @param user A user-object or null for all users + @param start only reservations beginning after the start-date will be returned (can be null). + @param end only reservations beginning before the end-date will be returned (can be null). + @param filters you can specify classificationfilters or null for all reservations . + */ + Reservation[] getReservations(User user,Date start,Date end,ClassificationFilter[] filters) throws RaplaException; + + /**returns all reservations that have allocated at least one Resource or Person that is part of the allocatables array. + @param allocatables only reservations that allocate at least on element of this array will be returned. + @param start only reservations beginning after the start-date will be returned (can be null). + @param end only reservations beginning before the end-date will be returned (can be null). + + **/ + Reservation[] getReservations(Allocatable[] allocatables,Date start,Date end) throws RaplaException; + + Reservation[] getReservationsForAllocatable(Allocatable[] allocatables, Date start,Date end,ClassificationFilter[] filters) throws RaplaException; + + /** returns all available periods */ + Period[] getPeriods() throws RaplaException; + + /** returns an Interface for accessing the periods + * @throws RaplaException */ + PeriodModel getPeriodModel() throws RaplaException; + + /** returns the current date in GMT+0 Timezone. If rapla operates + in multi-user mode, the date should be calculated from the + server date. + */ + Date today(); + + /** returns all allocatables, that are already allocated by different parallel reservations at the time-slices, that are described by the appointment */ + Allocatable[] getAllocatableBindings(Appointment appointment) throws RaplaException; + + /** returns all existing conflicts with the reservation */ + Conflict[] getConflicts(Reservation reservation) throws RaplaException; + + /** returns all existing conflicts that are visible for the user + conflicts, since the passed Date. If startDate is null all + conflicts are shown. + */ + Conflict[] getConflicts(Date startDate) throws RaplaException; + + /** returns if the user has the permissions to change/create an + allocation on the passed appointment. Changes of an + existing appointment that are in an permisable + timeframe are allowed. Example: The extension of an exisiting appointment, + doesn't affect allocations in the past and should not create a + conflict with the permissions. + */ + boolean hasPermissionToAllocate( Appointment appointment, Allocatable allocatable ); + + /** returns the preferences for the passed user, must be admin todo this.*/ + Preferences getPreferences(User user) throws RaplaException; + + /** returns the preferences for the login user */ + Preferences getPreferences() throws RaplaException; + + /** returns if the user is allowed to exchange the allocatables of this reservation. A user can do it if he has + * at least admin privileges for one allocatable. He can only exchange or remove or insert allocatables he has admin privileges on. + * The User cannot change appointments.*/ + boolean canExchangeAllocatables(Reservation reservation); + + boolean canReadReservationsFromOthers(User user); +} + + + + + diff --git a/Rapla/src/org/rapla/facade/RaplaComponent.java b/Rapla/src/org/rapla/facade/RaplaComponent.java new file mode 100644 index 0000000..a2552fe --- /dev/null +++ b/Rapla/src/org/rapla/facade/RaplaComponent.java @@ -0,0 +1,428 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.facade; + +import java.io.IOException; +import java.net.URL; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; + +import javax.swing.ImageIcon; + +import org.apache.avalon.framework.logger.Logger; +import org.rapla.components.xmlbundle.CompoundI18n; +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.entities.Category; +import org.rapla.entities.Named; +import org.rapla.entities.Ownable; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.User; +import org.rapla.entities.configuration.Preferences; +import org.rapla.entities.configuration.RaplaConfiguration; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.AppointmentFormater; +import org.rapla.entities.domain.Permission; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.AttributeType; +import org.rapla.entities.dynamictype.Classifiable; +import org.rapla.entities.dynamictype.Classification; +import org.rapla.entities.dynamictype.ConstraintIds; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.framework.Container; +import org.rapla.framework.PluginDescriptor; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaContextException; +import org.rapla.framework.RaplaException; +import org.rapla.framework.RaplaLocale; +import org.rapla.gui.CalendarOptions; +import org.rapla.gui.CalendarOptionsImpl; + +/** + Base class for most components. Eases + access to frequently used services, e.g. {@link I18nBundle}. + */ +public class RaplaComponent +{ + final private ClientServiceManager serviceManager; + private String childBundleName; + private Logger logger; + + public RaplaComponent(RaplaContext context) throws RaplaException { + try { + logger = (Logger)context.lookup(Logger.class.getName() ); + this.serviceManager = new ClientServiceManager(); + this.serviceManager.parent = context; + } catch ( RaplaException ex) { + throw ex; + } catch ( Exception ex) { + throw new RaplaException(ex); + } + } + + public String getChildBundleName() { + return childBundleName; + } + + public void setChildBundleName(String childBundleName) { + this.childBundleName = childBundleName; + } + + protected Container getContainer() throws RaplaException { + return ((Container)getContext().lookup(Container.class.getName())); + } + + /** returns if the session user is admin */ + public boolean isAdmin() { + try { + return getUser().isAdmin(); + } catch (RaplaException ex) { + } + return false; + } + + /** returns if the session user is a registerer */ + public boolean isRegisterer() { + if (isAdmin()) + { + return true; + } + try { + Category registererGroup = getQuery().getUserGroupsCategory().getCategory(Permission.GROUP_REGISTERER_KEY); + return getUser().belongsTo(registererGroup); + } catch (RaplaException ex) { + } + return false; + } + + public boolean isModifyPreferencesAllowed() { + if (isAdmin()) + { + return true; + } + try { + Category modifyPreferences = getQuery().getUserGroupsCategory().getCategory(Permission.GROUP_MODIFY_PREFERENCES_KEY); + if ( modifyPreferences == null ) { + return true; + } + return getUser().belongsTo(modifyPreferences); + } catch (RaplaException ex) { + } + return false; + } + + /** returns if the user has allocation rights for one or more resource */ + public boolean canUserAllocateSomething(User user) throws RaplaException { + Allocatable[] allocatables =getQuery().getAllocatables(); + if ( user.isAdmin() ) + return true; + for ( int i=0;i Permission.READ) + { + return true; + } + } + } + return false; + } + + /** returns if the current user is allowed to modify the object. */ + public boolean canModify(Object object) { + try { + User user = getUser(); + return canModify(object, user); + } catch (RaplaException ex) { + return false; + } + } + + static public boolean canModify(Object object, User user) { + if (object == null || !(object instanceof RaplaObject)) + { + return false; + } + if ( user == null) + { + return false; + } + if (user.isAdmin()) + return true; + if (object instanceof Ownable) { + Ownable ownable = (Ownable) object; + if (ownable.getOwner() == null || user.equals(ownable.getOwner())) + { + return true; + } + } + if (object instanceof Allocatable) { + Allocatable allocatable = (Allocatable) object; + if (allocatable.canModify( user )) + { + return true; + } + } + if (checkClassifiablePermissions(object, user)) + { + return true; + } + return false; + } +/** We check if an attribute with the permission_modify exists and look if the permission is set either globaly (if boolean type is used) or for a specific user group (if category type is used)*/ + public static boolean checkClassifiablePermissions(Object object, User user) { + if (object instanceof Classifiable) { + final Classifiable classifiable = (Classifiable) object; + + Classification classification = classifiable.getClassification(); + if ( classification != null) + { + final DynamicType type = classification.getType(); + final Attribute attribute = type.getAttribute("permission_modify"); + if ( attribute != null) + { + final AttributeType type2 = attribute.getType(); + if (type2 == AttributeType.BOOLEAN) + { + final Object value = classification.getValue( attribute); + return Boolean.TRUE.equals(value); + } + if ( type2 == AttributeType.CATEGORY) + { + Category cat = (Category)classification.getValue( attribute); + if ( cat == null) + { + Category rootCat = (Category)attribute.getConstraint(ConstraintIds.KEY_ROOT_CATEGORY); + if ( rootCat.getCategories().length == 0) + { + cat = rootCat; + } + } + if (user.belongsTo( cat)) + { + return true; + } + } + } + } + } + return false; + } + + public CalendarOptions getCalendarOptions() { + RaplaConfiguration conf = null; + try { + User user = getUser(); + if ( user != null) { + conf = (RaplaConfiguration)getQuery().getPreferences(user).getEntry(CalendarOptionsImpl.CALENDAR_OPTIONS); + } + if ( conf == null ) { + conf = (RaplaConfiguration)getQuery().getPreferences(null).getEntry(CalendarOptionsImpl.CALENDAR_OPTIONS); + } + if ( conf != null) { + return new CalendarOptionsImpl( conf.getConfig()); + } + } catch (RaplaException ex) { + + } + return (CalendarOptions)getService( CalendarOptions.class.getName()); + } + + protected User getUser() throws RaplaException { + return getUserModule().getUser(); + } + + protected Logger getLogger() { + return logger; + } + + /** lookup the service in the serviceManager under the specified key: + serviceManager.lookup(role). + @throws IllegalStateException if GUIComponent wasn't serviced. No service method called + @throws UnsupportedOperationException if service not available. + */ + protected Object getService(String role) { + try { + return getContext().lookup(role); + } catch (RaplaContextException ex) { + getLogger().error("Cause " , ex); + throw new UnsupportedOperationException("Service not supported in this context: " + role); + } + } + + protected RaplaContext getContext() { + return serviceManager; + } + + /** lookup RaplaLocale from the context */ + protected RaplaLocale getRaplaLocale() { + if (serviceManager.raplaLocale == null) + serviceManager.raplaLocale = (RaplaLocale) getService(RaplaLocale.ROLE); + return serviceManager.raplaLocale; + } + + + protected Locale getLocale() { + return getRaplaLocale().getLocale(); + } + + protected I18nBundle childBundle; + /** lookup I18nBundle from the serviceManager */ + protected I18nBundle getI18n() { + String childBundleName = getChildBundleName(); + if ( childBundleName != null) { + if ( childBundle == null) { + I18nBundle pluginI18n = (I18nBundle) getService(I18nBundle.ROLE + "/" + childBundleName ); + childBundle = new CompoundI18n(pluginI18n,getI18nDefault()); + } + return childBundle; + } + return getI18nDefault(); + } + + private I18nBundle getI18nDefault() { + if (serviceManager.i18n == null) + serviceManager.i18n = (I18nBundle) getService(I18nBundle.ROLE + "/org.rapla.RaplaResources"); + return serviceManager.i18n; + } + + /** lookup AppointmentFormater from the serviceManager */ + protected AppointmentFormater getAppointmentFormater() { + if (serviceManager.appointmentFormater == null) + serviceManager.appointmentFormater = (AppointmentFormater) getService(AppointmentFormater.ROLE); + return serviceManager.appointmentFormater; + } + + /** lookup PeriodModel from the serviceManager */ + protected PeriodModel getPeriodModel() { + try { + return getQuery().getPeriodModel(); + } catch (RaplaException ex) { + throw new UnsupportedOperationException("Service not supported in this context: " ); + } + } + + /** lookup QueryModule from the serviceManager */ + protected QueryModule getQuery() { + return getClientFacade(); + } + + protected ClientFacade getClientFacade() { + if (serviceManager.facade == null) + serviceManager.facade = (ClientFacade) getService( ClientFacade.ROLE ); + return serviceManager.facade; + } + + /** lookup ModificationModule from the serviceManager */ + protected ModificationModule getModification() { + return getClientFacade(); + } + + /** lookup UpdateModule from the serviceManager */ + protected UpdateModule getUpdateModule() { + return getClientFacade(); + } + + /** lookup UserModule from the serviceManager */ + protected UserModule getUserModule() { + return getClientFacade(); + } + + /** returns a translation for the object name into the selected language. If + a translation into the selected language is not possible an english translation will be tried next. + If theres no translation for the default language, the first available translation will be used. */ + public String getName(Object object) { + if (object == null) + return ""; + if (object instanceof Named) { + String name = ((Named) object).getName(getI18n().getLocale()); + return (name != null) ? name : ""; + } + return object.toString(); + } + + /** calls getI18n().getString(key) */ + public String getString(String key) { + return getI18n().getString(key); + } + + + /** calls "<html>" + getI18n().getString(key) + "</html>"*/ + public String getStringAsHTML(String key) { + return "" + getI18n().getString(key) + ""; + } + + /** calls getI18n().getIcon(key) */ + public ImageIcon getIcon(String key) { + return getI18n().getIcon(key); + } + + private static class ClientServiceManager implements RaplaContext { + I18nBundle i18n; + ClientFacade facade; + RaplaLocale raplaLocale; + RaplaContext parent; + AppointmentFormater appointmentFormater; + public Object lookup(String role) throws RaplaContextException { + return parent.lookup(role); + } + public boolean has(String role) { + return parent.has(role); + } + } + + public Preferences newEditablePreferences() throws RaplaException { + return (Preferences) getModification().edit(getQuery().getPreferences()); + } + + public PluginDescriptor findDescriptor( String pluginClassName ) throws RaplaException + { + List pluginList = (List) getService( PluginDescriptor.PLUGIN_LIST); + for (Iterator it = pluginList.iterator();it.hasNext();) { + PluginDescriptor descriptor = (PluginDescriptor)it.next(); + if (descriptor.getClass().getName().equals( pluginClassName )) { + return descriptor; + } + } + return null; + } + + public static boolean isSigned() { + try + { + final ClassLoader classLoader = RaplaComponent.class.getClassLoader(); + { + final Enumeration resources = classLoader.getResources("META-INF/RAPLA.SF"); + if (resources.hasMoreElements() ) + return true; + } + { + final Enumeration resources = classLoader.getResources("META-INF/RAPLA.DSA"); + if (resources.hasMoreElements() ) + return true; + } + } + catch ( IOException ex) + { + + } + return false; + } + +} diff --git a/Rapla/src/org/rapla/facade/UpdateErrorListener.java b/Rapla/src/org/rapla/facade/UpdateErrorListener.java new file mode 100644 index 0000000..fa05b90 --- /dev/null +++ b/Rapla/src/org/rapla/facade/UpdateErrorListener.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.facade; +import org.rapla.framework.RaplaException; + +/** Classes implementing this interface will be notified when an update error + * occurred. The listener can be registered by calling + * addUpdateErrorListener of the UpdateModule
    + * Don't forget to remove the listener by calling removeUpdateErrorLister + * when no longer need. + * @author Christopher Kohlhaas + * @see UpdateModule + */ + +public interface UpdateErrorListener { + /** this notifies all listeners that the update of the data has + caused an error. A normal source for UpdateErrors is a broken + connection to the server. + */ + void updateError(RaplaException ex); + void disconnected(); +} + + + + + diff --git a/Rapla/src/org/rapla/facade/UpdateModule.java b/Rapla/src/org/rapla/facade/UpdateModule.java new file mode 100644 index 0000000..372df72 --- /dev/null +++ b/Rapla/src/org/rapla/facade/UpdateModule.java @@ -0,0 +1,75 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.facade; + +import org.rapla.framework.RaplaException; +public interface UpdateModule +{ + public final static String REFRESH_INTERVAL_ENTRY = "org.rapla.refreshInterval"; + public final static String ARCHIVE_AGE = "org.rapla.archiveAge"; + + /** + * Refreshes the data that is in the cache (or on the client) + and notifies all registered {@link ModificationListener ModificationListeners} + with an update-event. + There are two types of refreshs. + +
      +
    • Incremental Refresh: Only the changes are propagated
    • +
    • Full Refresh: The complete data is reread. (Currently disabled in Rapla)
    • +
    + +

    + Incremental refreshs are the normal case if you have a client server basis. + (In a single user system no refreshs are necessary at all). + The refreshs are triggered in defined intervals if you use the webbased communication + and automaticaly if you use the old communication layer. You can change the refresh interval + via the admin options. +

    +

    + Of course you can call a refresh anytime you want to synchronize with the server, e.g. if + you want to ensure you are uptodate before editing. If you are on the server you dont need to refresh. +

    + + + WARNING: When using full refresh on a local file storage + all information will be changed. So use it + only if you modify the data from external. + You better re-get and re-draw all + the information in the Frontend after a full refresh. + + + */ + void refresh() throws RaplaException; + /** returns if the Facade is connected through a server (false if it has a local store)*/ + boolean isClientForServer(); + /** + * registers a new ModificationListener. + * A ModifictionEvent will be fired to every registered DateChangeListener + * when one or more entities have been added, removed or changed + * @see ModificationListener + * @see ModificationEvent + */ + void addModificationListener(ModificationListener listener); + void removeModificationListener(ModificationListener listener); + void addUpdateErrorListener(UpdateErrorListener listener); + void removeUpdateErrorListener(UpdateErrorListener listener); + + void addAllocationChangedListener(AllocationChangeListener triggerListener); + void removeAllocationChangedListener(AllocationChangeListener triggerListener); +} + + + + + diff --git a/Rapla/src/org/rapla/facade/UserModule.java b/Rapla/src/org/rapla/facade/UserModule.java new file mode 100644 index 0000000..28b69b1 --- /dev/null +++ b/Rapla/src/org/rapla/facade/UserModule.java @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.facade; + +import org.rapla.entities.User; +import org.rapla.framework.RaplaException; +/** Encapsulates the methods responsible for authentification. +*/ + +public interface UserModule { + /** The login method establishes the connection and loads data. + * It should clear the password char array. + * @return false on an invalid login. + * @throws RaplaException if the connection can't be established. + */ + boolean login(String username,char[] password) throws RaplaException; + + /** logout of the current user */ + void logout() throws RaplaException; + + /** returns if a session is active. True between a successful login and logout. */ + boolean isSessionActive(); + + + /** throws an Exception if no user has loged in. + @return the user that has loged in. */ + User getUser() throws RaplaException; + + /** the admin can switch to another user!*/ + void switchTo(User user); + /** returns true if the admin has switched to anoter user!*/ + boolean canSwitchBack(); + + void changePassword(User user,char[] oldPassword,char[] newPassword) throws RaplaException; + boolean canChangePassword(); +} + + + + + diff --git a/Rapla/src/org/rapla/facade/internal/AllocationChangeFinder.java b/Rapla/src/org/rapla/facade/internal/AllocationChangeFinder.java new file mode 100644 index 0000000..ea24a69 --- /dev/null +++ b/Rapla/src/org/rapla/facade/internal/AllocationChangeFinder.java @@ -0,0 +1,219 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.facade.internal; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.apache.avalon.framework.logger.AbstractLogEnabled; +import org.apache.avalon.framework.logger.Logger; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.RaplaType; +import org.rapla.entities.User; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Reservation; +import org.rapla.facade.AllocationChangeEvent; +import org.rapla.storage.UpdateResult; + +/** listens for allocation changes. + Collects AllocationChangeEvents. + */ +class AllocationChangeFinder extends AbstractLogEnabled +{ + ArrayList changeList = new ArrayList(); + UpdateResult updateResult; + + AllocationChangeFinder(Logger logger, UpdateResult updateResult) { + enableLogging( logger ); + if ( updateResult == null) + return; + User user = updateResult.getUser(); + for (Iterator it = updateResult.getOperations( UpdateResult.Add.class );it.hasNext();) { + UpdateResult.Add addOp = (UpdateResult.Add) it.next(); + added( (RaplaObject) addOp.getNew(), user ); + } + for (Iterator it = updateResult.getOperations( UpdateResult.Remove.class );it.hasNext();) { + UpdateResult.Remove removeOp = (UpdateResult.Remove) it.next(); + removed( (RaplaObject) removeOp.getCurrent(), user ); + } + for (Iterator it = updateResult.getOperations( UpdateResult.Change.class );it.hasNext();) { + UpdateResult.Change changeOp = (UpdateResult.Change) it.next(); + RaplaObject old = (RaplaObject) changeOp.getOld(); + RaplaObject newObj =(RaplaObject) changeOp.getNew(); + changed(old , newObj, user ); + } + } + + public List getTriggerEvents() { + return changeList; + } + + private void added(RaplaObject entity, User user) { + RaplaType raplaType = entity.getRaplaType(); + if ( raplaType.is( Reservation.TYPE )) { + Reservation newRes = (Reservation) entity; + addAppointmentAdd( + user + ,newRes + ,Arrays.asList(newRes.getAllocatables()) + ,Arrays.asList(newRes.getAppointments()) + ); + } + } + + private void removed(RaplaObject entity,User user) { + RaplaType raplaType = entity.getRaplaType(); + if ( raplaType.is( Reservation.TYPE )) { + if (getLogger().isDebugEnabled()) + getLogger().debug("Reservation removed: " + entity); + Reservation oldRes = (Reservation) entity; + addAppointmentRemove( + user + ,oldRes + ,oldRes + ,Arrays.asList(oldRes.getAllocatables()) + ,Arrays.asList(oldRes.getAppointments()) + ); + } + } + + private void changed(RaplaObject oldEntity,RaplaObject newEntity, User user) { + RaplaType raplaType = oldEntity.getRaplaType(); + if (raplaType.is( Reservation.TYPE )) { + if (getLogger().isDebugEnabled()) + getLogger().debug("Reservation changed: " + oldEntity); + Reservation oldRes = (Reservation) oldEntity; + Reservation newRes = (Reservation) newEntity; + List alloc1 = Arrays.asList(oldRes.getAllocatables()); + List alloc2 = Arrays.asList(newRes.getAllocatables()); + List app1 = Arrays.asList(oldRes.getAppointments()); + List app2 = Arrays.asList(newRes.getAppointments()); + + ArrayList removeList = new ArrayList(alloc1); + removeList.removeAll(alloc2); + // add removed allocations to the change list + addAppointmentRemove(user, oldRes,newRes, removeList, app1); + + ArrayList addList = new ArrayList(alloc2); + addList.removeAll(alloc1); + // add new allocations to the change list + addAppointmentAdd(user, newRes, addList, app2); + + ArrayList changeList = new ArrayList(alloc2); + changeList.retainAll(alloc1); + addAllocationDiff(user, changeList,oldRes,newRes); + } + if ( Appointment.TYPE.equals( raplaType )) { + if (getLogger().isDebugEnabled()) + getLogger().debug("Appointment changed: " + oldEntity + " to " + newEntity); + } + } + + /* + private void printList(List list) { + Iterator it = list.iterator(); + while (it.hasNext()) { + System.out.println(it.next()); + } + } + */ + + /** + * Calculates the allocations that have changed + */ + private void addAllocationDiff(User user,List allocatableList,Reservation oldRes,Reservation newRes) { + List app1 = Arrays.asList(oldRes.getAppointments()); + List app2 = Arrays.asList(newRes.getAppointments()); + ArrayList removeList = new ArrayList(app1); + removeList.removeAll(app2); + addAppointmentRemove(user, oldRes,newRes,allocatableList,removeList); + ArrayList addList = new ArrayList(app2); + addList.removeAll(app1); + addAppointmentAdd(user, newRes,allocatableList,addList); + /* + System.out.println("OLD appointments"); + printList(app1); + System.out.println("NEW appointments"); + printList(app2); + */ + ArrayList newList = new ArrayList(app2); + newList.retainAll(app1); + Collections.sort(newList); + + ArrayList oldList = new ArrayList(app1); + oldList.retainAll(app2); + Collections.sort(oldList); + + for (int i=0;iuser = null, see call hierarchy + * + * Changed in: Rapla 1.4 + */ + public Conflict[] getConflicts(Date startDate, User user) throws RaplaException + { + synchronized (operator.getLock()) + { + ArrayList conflictList = new ArrayList(); + + // Get all appointments since the startDate + SortedSet allAppointments = operator.getAppointments(null, startDate, null); + + // Create a new set of time blocks, ordered by their start dates + SortedSet allAppointmentBlocks = new TreeSet(new AppointmentBlockStartComparator()); + + // Get all time blocks of all appointments + for (Appointment appointment:allAppointments) + { + + // Get the end date of the appointment (if repeating, end date of last occurence) + Date end = appointment.getMaxEnd(); + + // Check if the appointment is repeating forever + if (end == null) + { + // If the repeating has no end, set the end to the start of the last appointment in the set + 100 weeks (~2 years) + end = new Date(((Appointment) allAppointments.last()).getStart().getTime() + DateTools.MILLISECONDS_PER_WEEK * 100); + } + + /* + * If the appointment has a repeating, get all single time blocks of it. If it is no + * repeating, this will just create one block, which is equal to the appointment + * itself. + */ + appointment.createBlocks(appointment.getStart(), DateTools.fillDate(end), allAppointmentBlocks); + } + + + + // Check the conflicts for each time block + for (Iterator it = allAppointmentBlocks.iterator();it.hasNext();) + { + AppointmentBlock appBlock = (AppointmentBlock)it.next(); + final Appointment appointment1 = appBlock.getAppointment(); + + /* + * Shrink the set of all time blocks down to those with a start date which is + * later than or equal to the start date of the block + */ + final SortedSet tailSet = allAppointmentBlocks.tailSet(new Long(appBlock.getStart())); + + // Check all time blocks which start after or at the same time as the block which is being checked + for (AppointmentBlock appBlock2:tailSet) + { + // If the start date of the compared block is after the end date of the block, quit the loop + if (appBlock2.getStart() > appBlock.getEnd()) + { + break; + } + // Check if the corresponding appointments of both blocks overlap each other + final Appointment appointment2 = appBlock2.getAppointment(); + if (appointment2.overlaps(appointment1)) + { + // Add appointments to conflict list + addConflicts(conflictList, appointment1, appointment2, startDate, user); + } + } + } + + return (Conflict[]) conflictList.toArray(Conflict.CONFLICT_ARRAY); + } + } + + public Set getAllocatableBindings(Appointment forAppointment) throws RaplaException { + Set allocatableSet = new HashSet(); + + synchronized (operator.getLock()) { + Iterator it = getOverlappingAppointments( forAppointment ).iterator(); + while (it.hasNext()) { + Appointment appointment = (Appointment) it.next(); + Reservation reservation = appointment.getReservation(); + Allocatable[] allocatables = reservation.getAllocatables(); + for (int i=0;i= Permission.ALLOCATE && p.covers( start, end, today ) ) + { + return true; + } + if ( original == null ) + { + continue; + } + + // We must check if the changes of the existing appointment + // are in a permisable timeframe (That should be allowed) + + // 1. check if appointment is old, + // 2. check if allocatable was already assigned to the appointment + Appointment originalAppointment = original.findAppointment( appointment ); + if ( originalAppointment == null + || !original.hasAllocated( allocatable, originalAppointment) + ) { + continue; + } + + // 3. check if the appointment has changed during + // that time + if ( appointment.matches( originalAppointment ) ) + { + return true; + } + if ( accessLevel >= Permission.ALLOCATE ) + { + Date maxTime = p.getMaxAllowed( today ); + if (maxTime == null) + maxTime = p.getMinAllowed( today); + + Date minChange = + appointment.getFirstDifference( originalAppointment, maxTime ); + Date maxChange = + appointment.getLastDifference( originalAppointment, maxTime ); + //System.out.println ( "minChange: " + minChange + ", maxChange: " + maxChange ); + + if ( p.covers( minChange, maxChange, today ) ) { + return true; + } + } + } + return false; + } + + private Collection getOverlappingAppointments( Appointment appointment) throws RaplaException { + ArrayList appointmentList = new ArrayList(); + Iterator it = operator.getAppointments + ( + null + ,appointment.getStart() + ,appointment.getMaxEnd() + ).iterator(); + + while (it.hasNext()) { + Appointment appointment2 = (Appointment) it.next(); + if ( appointment.getReservation() != null && appointment.getReservation().isIdentical(appointment2.getReservation())) + continue; + // System.out.println("Testing " + appointment); + // System.out.println(" for overlap with " + appointments[i]); + if (appointment.overlaps(appointment2)) { + appointmentList.add(appointment2); + // System.out.println(" Appointment overlaps"); + } + } + return appointmentList; + } + + + private Conflict createConflict(Appointment a1, + Appointment a2,Allocatable allocatable) { + + return new ConflictImpl(a1.getReservation() + ,a1 + ,allocatable + ,a2.getReservation() + ,a2.getReservation().getOwner() + ,a2); + } + + + private boolean isConflict(Appointment a1,Appointment a2,Allocatable allocatable, Date startDate) { + if (allocatable.isHoldBackConflicts()) + return false; + if ( startDate != null) { + if (a1.getMaxEnd() != null && startDate.after(a1.getMaxEnd())) + return false; + if (a2.getMaxEnd() != null && startDate.after(a2.getMaxEnd())) + return false; + } + if ( !a1.getReservation().hasAllocated( allocatable, a1 ) + || !a2.getReservation().hasAllocated( allocatable, a2 ) + ) + return false; + + return true; + } + + private void addConflicts(List conflictList, Appointment appointment1, Appointment appointment2, Date startDate, User user) + { + Reservation reservation = appointment1.getReservation(); + Reservation overlappingReservation = appointment2.getReservation(); + if (reservation.equals(overlappingReservation)) + return; + Allocatable[] all = reservation.getAllocatables(); + Allocatable[] all2 = overlappingReservation.getAllocatables(); + for (int j = 0; j < all.length; j++) + { + if (contains(all2, all[j]) ) + { + if ( isConflict(appointment1, appointment2, all[j], startDate) ) + { + if (user == null || RaplaComponent.canModify(reservation, user) || RaplaComponent.canModify(overlappingReservation, user)) + { + final Conflict conflict = createConflict(appointment2, appointment1, all[j]); + // Rapla 1.4: Don't add conflicts twice + if (!conflictList.contains(conflict) ) + { + conflictList.add(conflict); + } + } + } + } + } + } + + private boolean contains(Allocatable[] allocatables,Allocatable allocatable) { + for (int i=0;i a2.getStart()) + return 1; + // a1 before a2 + if (a1.getEnd() < a2.getEnd()) + return -1; + // a1 after a2 + if (a1.getEnd() > a2.getEnd()) + return 1; + // If a1 and a2 have equal start and end dates, sort by hash code + return (o1.hashCode() < o2.hashCode()) ? -1 : 1; + } + + /** + * This method is used to compare an appointment block with a date + */ + public int compare(Long d1, Object o2) + { + // Check if o2 is a date object + if (o2 instanceof Long) + { + return d1.compareTo((Long) o2); + } + + // If not, the object is an appointment block + AppointmentBlock a2 = (AppointmentBlock) o2; + + // Date before appointment block + if (d1 < a2.getStart()) + { + return -1; + } + // Date after appointment block + if (d1 > a2.getStart()) + { + return 1; + } + + // Date is equal to the startdate of the appointment block + return -1; + } + } + +} diff --git a/Rapla/src/org/rapla/facade/internal/ConflictImpl.java b/Rapla/src/org/rapla/facade/internal/ConflictImpl.java new file mode 100644 index 0000000..de4b3a6 --- /dev/null +++ b/Rapla/src/org/rapla/facade/internal/ConflictImpl.java @@ -0,0 +1,134 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.facade.internal; + +import java.util.Locale; + +import org.rapla.entities.Named; +import org.rapla.entities.RaplaType; +import org.rapla.entities.User; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Reservation; +import org.rapla.facade.Conflict; + +/** + * A conflict is the allocation of the same resource at the same time by different + * reservations. There's one conflict for each resource and each overlapping of + * two allocations. So if there are 3 reservations that allocate the same 2 resources + * on 2 days of the week, then we got ( 3 * 2 ) * 2 * 2 = 24 conflicts. Thats + * 3 reservations, each conflicting with two other 2 reservations on 2 days with 2 resources. + * + * @version 1.0 + * @author Christopher Kohlhaas + */ + +public class ConflictImpl implements Named, Conflict +{ + static public final RaplaType TYPE = new RaplaType(ConflictImpl.class,"conflict"); + + Reservation reserv1; + Appointment app1; + Allocatable allocatable; + Reservation reserv2; + User user2; + Appointment app2; + + public ConflictImpl(Reservation reserv1, + Appointment app1, + Allocatable allocatable, + Reservation reserv2, + User user2, + Appointment app2) + { + this.reserv1 = reserv1; + this.app1 = app1; + this.allocatable = allocatable; + this.reserv2 = reserv2; + this.user2 = user2; + this.app2 = app2; + } + /** @return the first Reservation, that is involed in the conflict.*/ + public Reservation getReservation1() { return reserv1; } + /** The appointment of the first reservation, that causes the conflict. */ + public Appointment getAppointment1() { return app1; } + /** @return the allocatable, allocated for the same time by two different reservations. */ + public Allocatable getAllocatable() { return allocatable; } + /** @return the second Reservation, that is involed in the conflict.*/ + public Reservation getReservation2() { return reserv2; } + /** @return The User, who created the second Reservation.*/ + public User getUser2() { return user2; } + /** The appointment of the second reservation, that causes the conflict. */ + public Appointment getAppointment2() { return app2; } + + public static final ConflictImpl[] CONFLICT_ARRAY= new ConflictImpl[] {}; + + /** + * @see org.rapla.entities.Named#getName(java.util.Locale) + */ + public String getName(Locale locale) { + return reserv1.getName( locale ); + } + + public boolean contains(Appointment appointment) { + if ( appointment == null) + return false; + if ( app1 != null && app1.equals( appointment)) + return true; + if ( app2 != null && app2.equals( appointment)) + return true; + return false; + } + + public boolean equals( Object obj) { + if (!(obj instanceof Conflict) || obj == null) + return false; + Conflict secondConflict = (Conflict) obj; + + if (!contains( secondConflict.getAppointment1())) + return false; + if (!contains( secondConflict.getAppointment2())) + return false; + if ( allocatable != null && !allocatable.equals( secondConflict.getAllocatable())) { + return false; + } + return true; + } + public RaplaType getRaplaType() + { + return Conflict.TYPE; + } + + public int hashCode() { + long value = 0; + if ( allocatable != null) + value += allocatable.hashCode(); + if ( app1 != null) + value += app1.hashCode(); + if ( app2 != null) + value += app2.hashCode(); + return (int)value % Integer.MAX_VALUE; + } + + +} + + + + + + + + + + diff --git a/Rapla/src/org/rapla/facade/internal/FacadeImpl.java b/Rapla/src/org/rapla/facade/internal/FacadeImpl.java new file mode 100644 index 0000000..ef12586 --- /dev/null +++ b/Rapla/src/org/rapla/facade/internal/FacadeImpl.java @@ -0,0 +1,1236 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.facade.internal; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; +import java.util.TreeSet; +import java.util.Vector; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.logger.AbstractLogEnabled; +import org.apache.avalon.framework.logger.Logger; +import org.rapla.components.util.Command; +import org.rapla.components.util.CommandQueue; +import org.rapla.components.util.DateTools; +import org.rapla.components.util.Tools; +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.entities.Category; +import org.rapla.entities.Entity; +import org.rapla.entities.EntityNotFoundException; +import org.rapla.entities.Ownable; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.RaplaType; +import org.rapla.entities.User; +import org.rapla.entities.UserComparator; +import org.rapla.entities.configuration.CalendarModelConfiguration; +import org.rapla.entities.configuration.Preferences; +import org.rapla.entities.configuration.RaplaMap; +import org.rapla.entities.configuration.internal.CalendarModelConfigurationImpl; +import org.rapla.entities.configuration.internal.RaplaMapImpl; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Period; +import org.rapla.entities.domain.Permission; +import org.rapla.entities.domain.RepeatingType; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.domain.internal.AllocatableImpl; +import org.rapla.entities.domain.internal.AppointmentImpl; +import org.rapla.entities.domain.internal.PeriodImpl; +import org.rapla.entities.domain.internal.ReservationImpl; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.AttributeType; +import org.rapla.entities.dynamictype.Classifiable; +import org.rapla.entities.dynamictype.Classification; +import org.rapla.entities.dynamictype.ClassificationFilter; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.dynamictype.DynamicTypeAnnotations; +import org.rapla.entities.dynamictype.internal.AttributeImpl; +import org.rapla.entities.dynamictype.internal.DynamicTypeImpl; +import org.rapla.entities.internal.CategoryImpl; +import org.rapla.entities.internal.UserImpl; +import org.rapla.entities.storage.Mementable; +import org.rapla.entities.storage.RefEntity; +import org.rapla.facade.AllocationChangeEvent; +import org.rapla.facade.AllocationChangeListener; +import org.rapla.facade.ClientFacade; +import org.rapla.facade.Conflict; +import org.rapla.facade.ModificationEvent; +import org.rapla.facade.ModificationListener; +import org.rapla.facade.PeriodModel; +import org.rapla.facade.UpdateErrorListener; +import org.rapla.facade.UpdateModule; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaContextException; +import org.rapla.framework.RaplaException; +import org.rapla.framework.RaplaLocale; +import org.rapla.storage.CachableStorageOperator; +import org.rapla.storage.RaplaSecurityException; +import org.rapla.storage.StorageOperator; +import org.rapla.storage.StorageUpdateListener; +import org.rapla.storage.UpdateResult; +/** This is the default implementation of the necessary Client-Facade to the DB-Subsystem. + *

    Sample configuration 1: +

    +   <facade id="facade">
    +      <store>file</store>
    +   </facade>
    +  
    +

    +

    Sample Configuration 2: +

    +     <facade id="facade" logger="facade">
    +        <store>remote</store>
    +        <username>homer</username>
    +        <password>duffs</password>
    +     </facade>
    +  
    + This facade automatically starts with user homer. + *

    + *

    The store entry contains the id of a storage-component. + * Storage-Components are all components that implement the + * {@link StorageOperator} interface. + *

    + */ + +public class FacadeImpl extends AbstractLogEnabled + implements + ClientFacade + ,StorageUpdateListener +{ + protected CommandQueue notifyQueue; + private Configuration operatorConfig; + private User workingUser = null; + private User originalUser = null; + private StorageOperator operator; + private Vector modificatonListenerList = new Vector(); + private Vector allocationListenerList = new Vector(); + private Vector errorListenerList = new Vector(); + private I18nBundle i18n; + private PeriodModelImpl periodModel; + private ConflictFinder conflictFinder; + String username; + String password; + Locale locale; + + public FacadeImpl(RaplaContext context,Configuration config, Logger logger) throws RaplaException + { + enableLogging( logger ); + i18n = (I18nBundle) context.lookup(I18nBundle.ROLE + "/org.rapla.RaplaResources"); + locale = ((RaplaLocale)context.lookup(RaplaLocale.ROLE)).getLocale(); + operatorConfig = config.getChild("store"); + getLogger().debug("Facade configured with operator '" + operatorConfig.getValue("*") + "'"); + username = config.getChild("username").getValue(null); + password = config.getChild("password").getValue(""); + try { + operator = (StorageOperator) context.lookup(StorageOperator.ROLE + "/" + operatorConfig.getValue("*")); + conflictFinder = new ConflictFinder( operator ); + operator.addStorageUpdateListener(this); + } + catch (RaplaContextException ex) + { + throw new RaplaContextException + ( + CachableStorageOperator.ROLE, + "Store at " + operatorConfig.getLocation() + + " is not found (or could not be initialized)" + ,ex + ); + } + notifyQueue = org.rapla.components.util.CommandQueue.createCommandQueue(); + if (username!=null) + if (!login(username,password.toCharArray())) + throw new RaplaException(i18n.getString("error.login")); + initRefresh(); + } + + public StorageOperator getOperator() { + return operator; + } + + //Implementation of StorageUpdateListener. + /** This method is called by the storage-operator, when stored objects + have changed. + + Caution: You must not lock the storage operator during processing + of this call, because it could have been locked by the store method, causing deadlocks + */ + public void objectsUpdated(UpdateResult evt) { + if (getLogger().isDebugEnabled()) + getLogger().debug("Objects updated"); + + notifyQueue.enqueue(new UserCheckCommand()); + + fireUpdateEvent( evt ); + } + + public void switchTo(User user) { + if (user == null) { + workingUser = originalUser; + originalUser = null; + } else { + originalUser = workingUser; + workingUser = user; + } + // fireUpdateEvent(new ModificationEvent()); + } + + public boolean canSwitchBack() { + return originalUser != null; + } + + public void updateError(RaplaException ex) { + getLogger().fatalError(ex.getMessage(),ex); + fireUpdateError(ex); + } + + public void storageDisconnected() { + fireStorageDisconnected(); + } + + /****************************** + * Update-module * + ******************************/ + public boolean isClientForServer() { + return operator.supportsActiveMonitoring(); + } + + public void refresh() throws RaplaException { + synchronized (operator.getLock()) { + if ( operator.supportsActiveMonitoring()) + { + operator.refresh(); + } + } + } + + public void addModificationListener(ModificationListener listener) { + modificatonListenerList.add(listener); + } + + public void removeModificationListener(ModificationListener listener) { + modificatonListenerList.remove(listener); + } + + public ModificationListener[] getModificationListeners() { + return (ModificationListener[])modificatonListenerList.toArray(new ModificationListener[]{}); + } + + private Collection getModificationListeners(boolean invokeLater) { + if (modificatonListenerList.size() == 0) + return Tools.EMPTY_LIST; + synchronized (this) { + Collection list = new ArrayList(3); + if ( periodModel != null) { + list.add( periodModel ); + } + Iterator it = modificatonListenerList.iterator(); + while (it.hasNext()) { + ModificationListener listener = (ModificationListener) it.next(); + if (listener.isInvokedOnAWTEventQueue() == invokeLater) + list.add(listener); + } + return list; + } + } + + public void addAllocationChangedListener(AllocationChangeListener listener) { + allocationListenerList.add(listener); + } + + public void removeAllocationChangedListener(AllocationChangeListener listener) { + allocationListenerList.remove(listener); + } + + private Collection getAllocationChangeListeners(boolean invokeLater) { + if (allocationListenerList.size() == 0) + return Tools.EMPTY_LIST; + synchronized (this) { + Collection list = new ArrayList(3); + Iterator it = allocationListenerList.iterator(); + while (it.hasNext()) { + AllocationChangeListener listener = (AllocationChangeListener) it.next(); + if (listener.isInvokedOnAWTEventQueue() == invokeLater) + list.add(listener); + } + return list; + } + } + + public void addUpdateErrorListener(UpdateErrorListener listener) { + errorListenerList.add(listener); + } + + public void removeUpdateErrorListener(UpdateErrorListener listener) { + errorListenerList.remove(listener); + } + + public UpdateErrorListener[] getUpdateErrorListeners() { + return (UpdateErrorListener[])errorListenerList.toArray(new UpdateErrorListener[]{}); + } + + + protected void fireUpdateError(RaplaException ex) { + UpdateErrorListener[] listeners = getUpdateErrorListeners(); + for (int i = 0;i 0) { + ((AllocationChangeListener)listener).changed( allocationChangeEvents ); + } + + } catch (RaplaException ex) { + getLogger().error("update-exception",ex); + bError = true; + } catch (Exception ex) { + getLogger().error("update-exception",ex); + bError = true; + } + } + if (getLogger().isDebugEnabled() && !bError) + getLogger().debug("GUI update took " + (System.currentTimeMillis()- time) + " ms."); + if (hasFailed()) + getLogger().error("There was an error while refreshing the displayed data. It could be different from the data stored. Restarting Rapla is recommended."); + } + }; + + /** fires update event asynchronous. + */ + protected void fireUpdateEvent(UpdateResult evt) { + if ( periodModel != null) + { + try { + periodModel.update(); + } catch (RaplaException e) { + getLogger().error( "Can't update Period Model", e ); + } + } + Collection modificationListeners = getModificationListeners(false); + Collection allocationChangeListeners = getAllocationChangeListeners( false ); + if (modificationListeners.size()>0 || allocationChangeListeners.size() > 0) + { + notifyQueue.enqueue(new UpdateCommand(modificationListeners,allocationChangeListeners, evt)); + } + modificationListeners = getModificationListeners(true); + allocationChangeListeners = getAllocationChangeListeners( true ); + if (modificationListeners.size()>0 || allocationChangeListeners.size() > 0) + { + javax.swing.SwingUtilities.invokeLater(new UpdateCommand(modificationListeners, allocationChangeListeners, evt)); + } + if (getLogger().isDebugEnabled()) + { + getLogger().debug("Update event fired"); + } + } + + + + + /****************************** + * Query-module * + ******************************/ + private Collection getVisibleAllocatables(ClassificationFilter[] filters) throws RaplaException { + Collection allocatables = new ArrayList(); + synchronized (operator.getLock()) { + allocatables.addAll(operator.getObjects(Allocatable.TYPE)); + } + Iterator it = allocatables.iterator(); + while ( it.hasNext() ) { + Allocatable allocatable = (Allocatable) it.next(); + if ( workingUser == null || workingUser.isAdmin()) + continue; + if ( !allocatable.canRead( workingUser ) ) + it.remove(); + } + + removeFilteredClassifications( allocatables, filters); + return allocatables; + } + + private Collection getVisibleReservations(User user, Date start, Date end, ClassificationFilter[] reservationFilters) throws RaplaException { + Collection reservations = new ArrayList(); + synchronized (operator.getLock()) { + reservations.addAll(operator.getReservations(user,start, end)); + } + removeFilteredClassifications( reservations, reservationFilters); + + //Category can_see = getUserGroupsCategory().getCategory( Permission.GROUP_CAN_READ_EVENTS_FROM_OTHERS); + Iterator it = reservations.iterator(); + boolean canSeeOthers = canReadReservationsFromOthers(user); + while ( it.hasNext() ) { + Reservation r = (Reservation) it.next(); + if ( workingUser == null || r.getOwner().equals( workingUser ) || canSeeOthers) + continue; + Allocatable[] allocatables = r.getAllocatables(); + boolean oneVisibleAllocatable = false; + for ( int i=0;i filter = new ArrayList(); + if ( allocatableFilter != null) + { + filter.addAll( Arrays.asList( allocatableFilter)); + for (ClassificationFilter entry: allocatableFilter) + { + resourceTypes++; + if (entry.ruleSize()>0 ) + { + defaultResourceTypes = false; + } + } + } + if ( eventFilter != null) + { + filter.addAll( Arrays.asList( eventFilter)); + for (ClassificationFilter entry: eventFilter) + { + eventTypes++; + if (entry.ruleSize()>0 ) + { + defaultEventTypes = false; + } + } + } + + try { + DynamicType[] allEventTypes; + allEventTypes = getDynamicTypes(DynamicTypeAnnotations.VALUE_RESERVATION_CLASSIFICATION); + if ( allEventTypes.length > eventTypes && eventFilter!= null) + { + defaultEventTypes = false; + } + final DynamicType[] allTypes = getDynamicTypes(null); + final int allResourceTypes = allTypes.length - allEventTypes.length; + if ( allResourceTypes > resourceTypes && allocatableFilter!= null) + { + defaultResourceTypes = false; + } + } + catch (RaplaException e) + { + getLogger().warn("Could not set default filters", e); + } + + final ClassificationFilter[] filterArray = filter.toArray(ClassificationFilter.CLASSIFICATIONFILTER_ARRAY); + return new CalendarModelConfigurationImpl( selected, filterArray, defaultResourceTypes, defaultEventTypes, title, startDate, endDate, selectedDate, view,optionMap); + } + + public Reservation newReservation() throws RaplaException + { + Date today = today(); + ReservationImpl reservation = new ReservationImpl(today ,today ); + Classification classification = getDynamicTypes(DynamicTypeAnnotations.VALUE_RESERVATION_CLASSIFICATION)[0].newClassification(); + reservation.setClassification(classification); + setNew(reservation); + return reservation; + } + + public Appointment newAppointment(Date startDate,Date endDate) throws RaplaException + { + AppointmentImpl appointment = new AppointmentImpl(startDate,endDate); + setNew(appointment); + return appointment; + } + + public Appointment newAppointment(Date startDate,Date endDate, RepeatingType repeatingType, int repeatingDuration) throws RaplaException + { + AppointmentImpl appointment = new AppointmentImpl(startDate,endDate, repeatingType, repeatingDuration); + setNew(appointment); + return appointment; + } + + public Allocatable newResource() throws RaplaException + { + return newAllocatable( false ); + } + + public Allocatable newPerson() throws RaplaException + { + return newAllocatable( true ); + } + + private Allocatable newAllocatable( boolean isPerson ) throws RaplaException { + Date today = today(); + AllocatableImpl allocatable = new AllocatableImpl(today,today); + String classificationType = isPerson ? DynamicTypeAnnotations.VALUE_PERSON_CLASSIFICATION : DynamicTypeAnnotations.VALUE_RESOURCE_CLASSIFICATION; + DynamicType[] dynamicTypes = getDynamicTypes( classificationType ); + DynamicType dynamicType = dynamicTypes[0]; + Classification classification = dynamicType.newClassification(); + User user = getUser(); + allocatable.addPermission( allocatable.newPermission() ); + if ( !user.isAdmin() ) { + Permission permission = allocatable.newPermission(); + permission.setUser( user ); + permission.setAccessLevel( Permission.ADMIN ); + allocatable.addPermission( permission ); + } + allocatable.setClassification(classification); + setNew(allocatable); + return allocatable; + } + + public Period newPeriod() throws RaplaException + { + PeriodImpl period = new PeriodImpl(); + Date today = today(); + period.setStart(DateTools.cutDate(today)); + period.setEnd(DateTools.fillDate(today)); + setNew(period); + return period; + } + + public Date today() { + return operator.today(); + } + + public Category newCategory() throws RaplaException + { + CategoryImpl category = new CategoryImpl(); + setNew(category); + return category; + } + + + private Attribute createStringAttribute(String key, String name) throws RaplaException { + Attribute attribute = newAttribute(AttributeType.STRING); + attribute.setKey(key); + attribute.getName().setName(i18n.getLang(), i18n.getString(name)); + return attribute; + } + + public DynamicType newDynamicType(String classificationType) throws RaplaException { + DynamicTypeImpl dynamicType = new DynamicTypeImpl(); + dynamicType.setAnnotation("classification-type", classificationType ); + dynamicType.setElementKey(createDynamicTypeKey(classificationType)); + setNew(dynamicType); + if (classificationType.equals (DynamicTypeAnnotations.VALUE_RESOURCE_CLASSIFICATION)) + { + dynamicType.addAttribute(createStringAttribute("name","name")); + dynamicType.setAnnotation("nameformat", "{name}"); + } + else if (classificationType.equals (DynamicTypeAnnotations.VALUE_RESERVATION_CLASSIFICATION)) + + { + dynamicType.addAttribute(createStringAttribute("title","reservation.name")); + dynamicType.setAnnotation("nameformat", "{title}"); + } + else if (classificationType.equals (DynamicTypeAnnotations.VALUE_PERSON_CLASSIFICATION)) + { + dynamicType.addAttribute(createStringAttribute("surname","surname")); + dynamicType.addAttribute(createStringAttribute("forename","forename")); + dynamicType.addAttribute(createStringAttribute("email","email")); + dynamicType.setAnnotation("nameformat", "{surname} {forename}"); + } + return dynamicType; + } + + public Attribute newAttribute (AttributeType attributeType) throws RaplaException { + AttributeImpl attribute = new AttributeImpl(attributeType); + setNew(attribute); + return attribute; + } + + public User newUser() throws RaplaException { + UserImpl user = new UserImpl(); + setNew(user); + Category modifyPreferences = getUserGroupsCategory().getCategory(Permission.GROUP_MODIFY_PREFERENCES_KEY); + if ( modifyPreferences != null) + { + user.addGroup( modifyPreferences); + } + Category readOthers = getUserGroupsCategory().getCategory(Permission.GROUP_CAN_READ_EVENTS_FROM_OTHERS); + if ( readOthers != null) + { + user.addGroup( readOthers); + } + return user; + } + + private String createDynamicTypeKey(String classificationType) throws RaplaException { + DynamicType[] dts = getDynamicTypes(classificationType); + int max = 1; + for (int i=0;i=0 + && key.length() > len + && Character.isDigit(key.charAt(len)) + ) + { + try { + int value = Integer.valueOf(key.substring(len)).intValue(); + if (value >= max) + max = value + 1; + } catch (NumberFormatException ex) { + } + } + } + return classificationType + (max); + } + + private void setNew(RefEntity entity) throws RaplaException { + setNew( entity, null); + } + + /** recursivly set new ids */ + private void setNew(RefEntity entity, User user) throws RaplaException { + + if ( entity.getSubEntities().hasNext() ) + { + throw new RaplaException("The current Rapla Version doesnt support cloning entities with sub-entities. (Except reservations)"); + } + + RaplaType raplaType = ((RaplaObject) entity).getRaplaType(); + synchronized (operator.getLock()) + { + entity.setId(operator.createIdentifier(raplaType)); + } + + entity.setVersion(0); + if (getLogger() != null && getLogger().isDebugEnabled()) + { + getLogger().debug("new " + entity.getId()); + } + + if (entity instanceof Ownable) + { + if ( user == null) + user = getUser(); + ((Ownable)entity).setOwner(user); + } + } + + public void checkReservation(Reservation reservation) throws RaplaException { + if (reservation.getAppointments().length == 0) + { + throw new RaplaException(i18n.getString("error.no_appointment")); + } + + if (reservation.getName(i18n.getLocale()).trim().length() ==0) + { + throw new RaplaException(i18n.getString("error.no_reservation_name")); + } + } + + public Entity edit(Entity obj) throws RaplaException { + if ( obj == null) + throw new NullPointerException("Can't edit null objects"); + synchronized (operator.getLock()) { + return operator.editObject( obj, workingUser ); + } + } + + public Entity clone(Entity obj) throws RaplaException { + if ( obj == null) + throw new NullPointerException("Can't clone null objects"); + + if ( obj instanceof Reservation) + { + return cloneReservation( (Reservation) obj ); + } + + try + { + RefEntity clone = (RefEntity) ((Mementable) obj).deepClone(); + removeParents( clone ); + setNew(clone, this.workingUser); + return clone; + } + catch (ClassCastException ex) + { + throw new RaplaException("This entity can't be cloned ",ex); + } + + } + + private void removeParents( RefEntity clone ) + { + if ( clone instanceof AppointmentImpl ) + { + ( (AppointmentImpl) clone ).removeParent(); + } + + if ( clone instanceof CategoryImpl ) + { + ( (CategoryImpl) clone ).removeParent(); + } + } + + + private Reservation cloneReservation( Reservation obj ) throws RaplaException + { + Reservation clone =(Reservation) ((Mementable) obj).deepClone(); + HashMap restrictions = new HashMap(); + Allocatable[] allocatables = clone.getAllocatables(); + + for ( int i=0;i< allocatables.length;i++) + { + restrictions.put( allocatables, clone.getRestriction( allocatables[i])); + } + + Appointment[] clonedAppointments = clone.getAppointments(); + for ( int i=0;i< clonedAppointments.length;i++) + { + setNew((RefEntity)clonedAppointments[i], this.workingUser); + clone.removeAppointment( clonedAppointments[i] ); + } + + setNew((RefEntity)clone, this.workingUser); + for ( int i=0;i< clonedAppointments.length;i++) + { + clone.addAppointment( clonedAppointments[i] ); + } + + for ( int i=0;i< allocatables.length;i++) + { + Appointment[] appointments = (Appointment[])restrictions.get( allocatables[i] ); + if ( appointments != null ) + { + clone.setRestriction( allocatables[i], appointments ); + } + } + + return clone; + } + + public Entity getPersistant(Entity obj) throws RaplaException { + synchronized (operator.getLock()) { + return operator.getPersistant( obj ); + } + } + + public void store(Entity obj) throws RaplaException { + if ( obj == null) + throw new NullPointerException("Can't store null objects"); + storeObjects(new Entity[] {obj}); + } + + public void remove(Entity obj) throws RaplaException { + if ( obj == null) + throw new NullPointerException("Can't remove null objects"); + removeObjects(new Entity[] {obj}); + } + + public void storeObjects(Entity[] obj) throws RaplaException { + storeAndRemove( obj, Entity.ENTITY_ARRAY ); + } + + public void removeObjects(Entity[] obj) throws RaplaException { + storeAndRemove( Entity.ENTITY_ARRAY , obj); + } + + + public void storeAndRemove(Entity[] obj, Entity[] removedObjects) throws RaplaException { + if (obj.length == 0 && removedObjects.length == 0) + return; + long time = System.currentTimeMillis(); + for (int i=0;i< obj.length;i++) { + if ( obj[i] == null) + { + throw new RaplaException("Stored Objects cant be null"); + } + if ( obj[i].getRaplaType().equals( Reservation.TYPE ) ) + { + checkReservation( (Reservation) obj[i] ); + } + } + + for (int i=0;i< removedObjects.length;i++) { + if ( removedObjects[i] == null) + { + throw new RaplaException("Removed Objects cant be null"); + } + } + + synchronized (operator.getLock()) { + operator.storeAndRemove( obj , removedObjects, workingUser); + } + if (getLogger().isDebugEnabled()) + getLogger().debug("Storing took " + (System.currentTimeMillis()- time) + " ms."); + } + +} + + + + + + + + diff --git a/Rapla/src/org/rapla/facade/internal/ModificationEventImpl.java b/Rapla/src/org/rapla/facade/internal/ModificationEventImpl.java new file mode 100644 index 0000000..8fb07f7 --- /dev/null +++ b/Rapla/src/org/rapla/facade/internal/ModificationEventImpl.java @@ -0,0 +1,184 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.facade.internal; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.rapla.components.util.Tools; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.RaplaType; +import org.rapla.facade.ModificationEvent; + +/** Encapsulate the changes that are made in the backend-store.*/ +public class ModificationEventImpl implements ModificationEvent +{ + Set updatedObjects; + Set removedObjects; + boolean isRefresh = false; + Map updatedIndex; + Map removedIndex; + + public ModificationEventImpl() { + isRefresh = true; + } + + public ModificationEventImpl(Set changedObjects,Set removedObjects) { + isRefresh = false; + this.updatedObjects = Collections.unmodifiableSet(changedObjects); + this.removedObjects = Collections.unmodifiableSet(removedObjects); + updatedIndex = new HashMap(); + Iterator it = updatedObjects.iterator(); + while (it.hasNext()) { + addToIndex(updatedIndex,(RaplaObject)it.next()); + } + makeIndexReadOnly(updatedIndex); + removedIndex = new HashMap(); + it = removedObjects.iterator(); + while (it.hasNext()) { + addToIndex(removedIndex,(RaplaObject)it.next()); + } + makeIndexReadOnly(removedIndex); + } + + private void addToIndex(Map index,RaplaObject type) { + Set set = (Set) index.get(type.getRaplaType()); + if (set == null) { + set = new HashSet(); + index.put(type.getRaplaType(),set); + } + set.add(type); + } + + private void makeIndexReadOnly(Map index) { + Iterator it = index.keySet().iterator(); + while (it.hasNext()) { + Object key = it.next(); + Set set = Collections.unmodifiableSet((Set) index.get(key)); + index.put(key,set); + } + } + + /** All objects in the cache are modified. This is not selective. */ + private boolean isRefresh() { + return isRefresh; + } + + + private Set retainObjects(Set set,Set col) { + HashSet tempSet = new HashSet(col.size()); + tempSet.addAll(col); + tempSet.retainAll(set); + if (tempSet.size() >0) + return tempSet; + else + return Tools.EMPTY_SET; + } + + /** returns the modified objects from a given set.*/ + public Set getChanged(Set col) { + checkNotRefresh(); + return retainObjects(updatedObjects,col); + } + + /** returns the removed objects from a given set.*/ + public Set getRemoved(Set col) { + checkNotRefresh(); + return retainObjects(removedObjects,col); + } + + /** returns if the objects has changed.*/ + public boolean hasChanged(Object object) { + if (isRefresh()) + { + return true; + } + checkNotRefresh(); + return updatedObjects.contains(object); + } + + /** returns if the objects was removed.*/ + public boolean isRemoved(Object object) { + if (isRefresh()) + { + return true; + } + checkNotRefresh(); + return removedObjects.contains(object); + } + + /** returns if the objects has changed or was removed.*/ + public boolean isModified(Object object) { + if (isRefresh()) + { + return true; + } + checkNotRefresh(); + return hasChanged(object) || isRemoved(object); + } + + /** returns if an objects of the specied type was changed or removed.*/ + public boolean isModified(RaplaType raplaType) { + if (isRefresh()) + { + return true; + } + checkNotRefresh(); + return updatedIndex.get(raplaType)!=null || removedIndex.get(raplaType)!=null; + } + + private Set getFromIndex(Map index,RaplaType raplaType) { + Set set = (Set) index.get(raplaType); + if (set != null) + return set; + else + return Tools.EMPTY_SET; + } + + /** returns if an objects of the specied type has changed .*/ + public Set getChanged(RaplaType raplaType) { + checkNotRefresh(); + return getFromIndex(updatedIndex,raplaType); + } + + /** returns if an objects of the specied type was removed .*/ + public Set getRemoved(RaplaType raplaType) { + checkNotRefresh(); + return getFromIndex(removedIndex,raplaType); + } + + /** returns all removed objects .*/ + public Set getRemoved() { + checkNotRefresh(); + return removedObjects; + } + + /** returns all changed object .*/ + public Set getChanged() { + checkNotRefresh(); + return updatedObjects; + } + + private void checkNotRefresh() { + if (isRefresh()) + throw new IllegalStateException("Refresh is set. All objects could be changed!"); + } +} + + + + diff --git a/Rapla/src/org/rapla/facade/internal/PeriodModelImpl.java b/Rapla/src/org/rapla/facade/internal/PeriodModelImpl.java new file mode 100644 index 0000000..36c642c --- /dev/null +++ b/Rapla/src/org/rapla/facade/internal/PeriodModelImpl.java @@ -0,0 +1,207 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.facade.internal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; +import java.lang.Math; + +import org.rapla.components.util.Assert; +import org.rapla.entities.domain.Period; +import org.rapla.facade.ClientFacade; +import org.rapla.facade.PeriodModel; +import org.rapla.facade.QueryModule; +import org.rapla.framework.RaplaException; + + +class PeriodModelImpl implements PeriodModel +{ + //public static final String ROLE = PeriodModel.class.getName(); + TreeSet m_periods = new TreeSet(new Comparator() { + public int compare(Object o1, + Object o2) { + if (o1 instanceof Date) { + if (o2 instanceof Date) + return ((Date)o1).compareTo((Date)o2); + else + + return -1 * ((Period)o2).compareTo(o1); + } else { + return ((Period)o1).compareTo(o2); + } + } + } + ); + QueryModule query; + Period defaultPeriod; + + PeriodModelImpl( ClientFacade query ) throws RaplaException { + this.query = query; + update(); + } + + public void update() throws RaplaException { + Period[] periodArray = getQuery().getPeriods(); + m_periods.clear(); + m_periods.addAll(Arrays.asList(periodArray)); + } + + /** The PeriodModel listeners may not be thread safe.*/ + public boolean isInvokedOnAWTEventQueue() { + return false; + } + + protected QueryModule getQuery() { + return query; + } + + + /** returns the first matching period or null if no period matches.*/ + public Period getPeriodFor(Date date) { + if (date == null) + return null; + SortedSet set = m_periods.tailSet(date); + if (!set.isEmpty()) { + Period period = (Period) set.first(); + if (period.contains(date)) + return period; + } + Iterator it = m_periods.tailSet(date).iterator(); + while (it.hasNext()) { + Period period = (Period) it.next(); + if (period.contains(date)) { + return period; + } + } + return null; + } + + static private long diff(Date d1,Date d2) { + long diff = d1.getTime()-d2.getTime(); + if (diff<0) + diff = diff * -1; + return diff; + } + + public Period getNearestPeriodForDate(Date date) { + return getNearestPeriodForStartDate( m_periods, date, null); + } + + public Period getNearestPeriodForStartDate(Date date) { + return getNearestPeriodForStartDate( date, null); + } + + public Period getNearestPeriodForStartDate(Date date, Date endDate) { + return getNearestPeriodForStartDate( getPeriodsFor( date ), date, endDate); + } + + public Period getNearestPeriodForEndDate(Date date) { + return getNearestPeriodForEndDate( getPeriodsFor( date ), date); + } + + static private Period getNearestPeriodForStartDate(Collection periodList, Date date, Date endDate) { + Period result = null; + long min_from_start=Long.MAX_VALUE, min_from_end=0; + long from_start, from_end=0; + Iterator it = periodList.iterator(); + while (it.hasNext()) + { + Period period = (Period) it.next(); + if ( period == null) + { // EXCO: Why this test ? + continue; + } + from_start = diff(period.getStart(),date); + if ( endDate != null ) + { + from_end = Math.abs(diff(period.getEnd(), endDate)); + } + if ( from_start < min_from_start + || (from_start == min_from_start && from_end < min_from_end) + ) + { + min_from_start = from_start; + min_from_end = from_end; + result = period; + } + } + return result; + } + + static private Period getNearestPeriodForEndDate(Collection periodList, Date date) { + Period result = null; + long min=-1; + Iterator it = periodList.iterator(); + while (it.hasNext()) { + Period period = (Period) it.next(); + if (min == -1) { + min = diff(period.getEnd(),date); + result = period; + } + if (diff(period.getEnd(),date) < min) { + min = diff(period.getStart(),date); + result = period; + } + } + return result; + } + + + /** return all matching periods.*/ + public List getPeriodsFor(Date date) { + ArrayList list = new ArrayList(); + if (date == null) + return list; + + SortedSet set = m_periods.tailSet(date); + Iterator it = set.iterator(); + while (it.hasNext()) { + Period period = (Period) it.next(); + //System.out.println(m_periods[i].getStart() + " - " + m_periods[i].getEnd()); + if (period.contains(date)) { + list.add( period ); + } + } + return list; + } + + public int getSize() { + Assert.notNull(m_periods,"Componenet not setup!"); + return m_periods.size(); + } + + public Period[] getAllPeriods() { + return (Period[])m_periods.toArray( Period.PERIOD_ARRAY); + } + + public Object getElementAt(int index) { + Assert.notNull(m_periods,"Componenet not setup!"); + Iterator it = m_periods.iterator(); + for (int i=0;it.hasNext();i++) { + Object obj = it.next(); + if (i == index) + return obj; + } + return null; + } + +} + + + diff --git a/Rapla/src/org/rapla/facade/package.html b/Rapla/src/org/rapla/facade/package.html new file mode 100644 index 0000000..753dbcf --- /dev/null +++ b/Rapla/src/org/rapla/facade/package.html @@ -0,0 +1,6 @@ + +

    This package contains the facade, that encapsulate the storage and the entitie package and +provides a simple interface for accessing the rapla-system; Study this package to get a good summary of the functionality of Rapla.

    + + + diff --git a/Rapla/src/org/rapla/framework/Container.java b/Rapla/src/org/rapla/framework/Container.java new file mode 100644 index 0000000..9084856 --- /dev/null +++ b/Rapla/src/org/rapla/framework/Container.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.framework; + +import java.util.Collection; +import java.util.Map; + +import org.apache.avalon.framework.configuration.Configuration; + +public interface Container +{ + String ROLE = Container.class.getName(); + StartupEnvironment getStartupEnvironment(); + void addContainerProvidedComponentInstance(String role,Object component); + void addContainerProvidedComponent(String role,String classname); + void addContainerProvidedComponent(String role,String classname, String hint,Configuration config); + RaplaContext getContext(); + /** returns a set with all hints to the services*/ + Collection getAllServicesFor(String role); + /** lookup all services for this role (grouped by their hint)*/ + Map lookupServicesFor(String role) throws RaplaContextException; + } + diff --git a/Rapla/src/org/rapla/framework/DEPENDENCIES b/Rapla/src/org/rapla/framework/DEPENDENCIES new file mode 100644 index 0000000..308fd6c --- /dev/null +++ b/Rapla/src/org/rapla/framework/DEPENDENCIES @@ -0,0 +1,4 @@ +This component depends on the following packages + +org.apache.avalon.framework.* +org.rapla.components.util.* diff --git a/Rapla/src/org/rapla/framework/PluginDescriptor.java b/Rapla/src/org/rapla/framework/PluginDescriptor.java new file mode 100644 index 0000000..2bb6b7e --- /dev/null +++ b/Rapla/src/org/rapla/framework/PluginDescriptor.java @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.framework; + +import org.apache.avalon.framework.configuration.Configuration; + +public interface PluginDescriptor{ + public final static String PLUGIN_LIST = "plugin-list"; + + public void provideServices(Container container, Configuration configuration); + public Object getPluginMetaInfos(String key); +} + diff --git a/Rapla/src/org/rapla/framework/RaplaContext.java b/Rapla/src/org/rapla/framework/RaplaContext.java new file mode 100644 index 0000000..a2b22fa --- /dev/null +++ b/Rapla/src/org/rapla/framework/RaplaContext.java @@ -0,0 +1,14 @@ +package org.rapla.framework; + + + +public interface RaplaContext +{ + /** Returns a reference to the requested object (e.g. a component instance). + * throws a RaplaContextException if the object can't be returned. This could have + * different reasons: For example it is not found in the context, or there has been + * a problem during the component creation. + */ + Object lookup( String key ) throws RaplaContextException; + boolean has( String key ); +} diff --git a/Rapla/src/org/rapla/framework/RaplaContextException.java b/Rapla/src/org/rapla/framework/RaplaContextException.java new file mode 100644 index 0000000..917e3fe --- /dev/null +++ b/Rapla/src/org/rapla/framework/RaplaContextException.java @@ -0,0 +1,20 @@ +package org.rapla.framework; + + +public class RaplaContextException + extends RaplaException + +{ + private static final long serialVersionUID = 1L; + + public RaplaContextException( final String key, final String message ) { + super( key + " " + message); + } + + public RaplaContextException( final String key, final String message, final Throwable throwable ) + { + super( message, throwable ); + } + + +} diff --git a/Rapla/src/org/rapla/framework/RaplaDefaultContext.java b/Rapla/src/org/rapla/framework/RaplaDefaultContext.java new file mode 100644 index 0000000..b41a067 --- /dev/null +++ b/Rapla/src/org/rapla/framework/RaplaDefaultContext.java @@ -0,0 +1,72 @@ +package org.rapla.framework; + +import java.util.HashMap; + + + +public class RaplaDefaultContext + implements RaplaContext +{ + private final HashMap contextObjects = new HashMap(); + private final RaplaContext m_parent; + + public RaplaDefaultContext() + { + this( null ); + } + + public RaplaDefaultContext( final RaplaContext parent ) + { + m_parent = parent; + } + + + public Object lookup( final String key ) + throws RaplaContextException + { + final Object object = contextObjects.get( key ); + if( null != object ) + { + return object; + } + else if( null != m_parent ) + { + return m_parent.lookup( key ); + } + else + { + final String message = "Unable to provide implementation for " + key; + throw new RaplaContextException( key, message, null ); + } + } + + + public boolean has( final String key ) + { + if (contextObjects.get( key ) != null) + return true; + + if ( m_parent == null) + return false; + + return m_parent.has( key ); + } + + public void put( final String key, final Object object ) + { + contextObjects.put( key, object ); + } + + + protected Object getUnsave(String key) { + if ( has( key)) { + try { + return lookup( key ); + } catch (RaplaContextException ex) { + } + } + return null; + } + + +} diff --git a/Rapla/src/org/rapla/framework/RaplaException.java b/Rapla/src/org/rapla/framework/RaplaException.java new file mode 100644 index 0000000..3d8c7c2 --- /dev/null +++ b/Rapla/src/org/rapla/framework/RaplaException.java @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.framework; + +import org.apache.avalon.framework.CascadingException; + +/** the base-class for all Rapla specific Exceptions */ +public class RaplaException extends CascadingException { + private static final long serialVersionUID = 1L; + + public RaplaException(String text) { + super(text); + } + + public RaplaException(Throwable throwable) { + super(throwable.getMessage(),throwable); + } + + public RaplaException(String text,Throwable ex) { + super(text,ex); + } + +} + + + + + + + diff --git a/Rapla/src/org/rapla/framework/RaplaLocale.java b/Rapla/src/org/rapla/framework/RaplaLocale.java new file mode 100644 index 0000000..70d4a64 --- /dev/null +++ b/Rapla/src/org/rapla/framework/RaplaLocale.java @@ -0,0 +1,84 @@ +package org.rapla.framework; + +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + + +/** This class contains all locale specific information for Rapla. Like +
      +
    • Selected language.
    • +
    • Selected country.
    • +
    • Available languages (if the user has the possibility to choose a language)
    • +
    • TimeZone for appointments (This is always GMT+0)
    • +
    +

    +Also it provides basic formating information for the dates. +

    +

    +Configuration is done in the rapla.xconf: +

    +<locale>
    + <languages default="de">
    +   <language>de</language>
    +   <language>en</language>
    + </languages>
    + <country>US</country>
    +</locale>
    +
    +If languages default is not set, the system default wil be used.
    +If country code is not set, the system default will be used.
    +

    + */ + +public interface RaplaLocale +{ + String ROLE = RaplaLocale.class.getName(); + String LANGUAGE_ENTRY = "org.rapla.language"; + + String[] getAvailableLanguages(); + + /** creates a calendar initialized with the Rapla timezone ( that is always GMT+0 for Rapla ) and the selected locale*/ + Calendar createCalendar(); + + String formatTime( Date date ); + + /** sets time to 0:00:00 or 24:00:00 */ + Date toDate( Date date, boolean fillDate ); + + /** sets time to 0:00:00 */ + Date toDate( int year, int month, int date ); + + /** sets date to 0:00:00 */ + Date toTime( int hour, int minute, int second ); + + /** Uses the first date parameter for year, month, date information and + the second for hour, minutes, second, millisecond information.*/ + Date toDate( Date date, Date time ); + + /** format long with the local NumberFormat */ + String formatNumber( long number ); + + /** format without year */ + String formatDateShort( Date date ); + + /** format with locale DateFormat.SHORT */ + String formatDate( Date date ); + + /** format with locale DateFormat.MEDIUM */ + String formatDateLong( Date date ); + + /** Abbreviation of locale weekday name of date. */ + String getWeekday( Date date ); + + /** Monthname of date. */ + String getMonth( Date date ); + + String getCharsetNonUtf(); + + TimeZone getTimeZone(); + + Locale getLocale(); + +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/framework/ServiceListCreator.java b/Rapla/src/org/rapla/framework/ServiceListCreator.java new file mode 100644 index 0000000..5ae9707 --- /dev/null +++ b/Rapla/src/org/rapla/framework/ServiceListCreator.java @@ -0,0 +1,78 @@ +/*--------------------------------------------------------------------------* +| Copyright (C) 2006 Christopher Kohlhaas | +| | +| This program is free software; you can redistribute it and/or modify | +| it under the terms of the GNU General Public License as published by the | +| Free Software Foundation. A copy of the license has been included with | +| these distribution in the COPYING file, if not go to www.fsf.org | +| | +| As a special exception, you are granted the permissions to link this | +| program with every library, which license fulfills the Open Source | +| Definition as published by the Open Source Initiative (OSI). | +*--------------------------------------------------------------------------*/ +package org.rapla.framework; + + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.util.Stack; + + + +/** Helper Class for automated creation of the rapla-plugin.list in the + * META-INF directory. Can be used in the build environment. + */ +public class ServiceListCreator { + public static void main (String[] args) { + try { + String sourceDir = args[0]; + String destDir = (args.length>1) ? args[1] : sourceDir; + processDir(sourceDir,destDir); + } catch (IOException e) { + throw new RuntimeException( e.getMessage()); + } catch (ClassNotFoundException e) { + throw new RuntimeException( e.getMessage()); + } + } + + public static void processDir(String srcDir,String destFile) + throws ClassNotFoundException, IOException + { + Stack stack = new Stack(); + File topDir = new File(srcDir); + stack.push(topDir); + Writer writer = new BufferedWriter(new FileWriter( destFile )); + try { + while (!stack.empty()) { + File file = (File) stack.pop(); + if (file.isDirectory()) { + File[] files = file.listFiles(); + for (int i=0;i + [-?|-c PATH_TO_CONFIG_FILE] [ACTION] + + Possible map entries: +
      +
    • config: the config-file
    • +
    • action: the start action
    • +
    + + @return a map with the parameter-entries or null if format is invalid or -? is used + */ + public static Map parseParams( String[] args ) + { + boolean bInvalid = false; + Map map = new HashMap(); + String config = null; + String action = null; + + // Investigate the passed arguments + for ( int i = 0; i < args.length; i++ ) + { + if ( args[i].toLowerCase().equals( "-c" ) ) + { + if ( i + 1 == args.length ) + { + bInvalid = true; + break; + } + config = args[++i]; + continue; + } + if ( args[i].toLowerCase().equals( "-?" ) ) + { + bInvalid = true; + break; + } + if ( args[i].toLowerCase().substring( 0, 1 ).equals( "-" ) ) + { + bInvalid = true; + break; + } + action = args[i].toLowerCase(); + } + + if ( bInvalid ) + { + return null; + } + + if ( config != null ) + map.put( "config", config ); + if ( action != null ) + map.put( "action", action ); + return map; + } + + /** Creates a configuration from a URL.*/ + public static Configuration createConfig( String configURL ) throws RaplaException + { + try + { + + URLConnection conn = new URL( configURL).openConnection(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + InputStream stream = conn.getInputStream(); + //System.out.println( "File Content:"); + int s= -1; + do { + s = stream.read(); + // System.out.print( (char)s ); + if ( s != -1) + { + out.write( s ); + } + } while ( s != -1); + out.close(); + stream.close(); + Assert.notNull( stream ); + InputStream in = new ByteArrayInputStream(out.toByteArray()); + DefaultConfigurationBuilder configurationBuilder = new DefaultConfigurationBuilder( + XMLReaderAdapter + .createXMLReader( false ) ); + //Configuration config = configurationBuilder.build( configURL ); + Configuration config = configurationBuilder.build( in ); + in.close(); + Assert.notNull( config ); + return config; + } + catch ( org.xml.sax.SAXParseException ex ) + { + throw new RaplaException( "Error parsing configuration " + + configURL + + " Line:" + + ex.getLineNumber() + + " Column:" + + ex.getColumnNumber() + + " " + + ex.getMessage() ,ex); + } + catch ( EOFException ex ) + { + throw new RaplaException( "Can't load configuration-file at " + configURL ); + } + catch ( Exception ex ) + { + throw new RaplaException( ex ); + } + } + + /** Creates an configuration URL from a configuration path. + If path is null the URL of the defaultPropertiesFile + will be returned. + */ + public static URL configFileToURL( String path, String defaultPropertiesFile ) throws RaplaException + { + URL configURL = null; + try + { + if ( path != null ) + { + File file = new File( path ); + if ( file.exists() ) + { + configURL = ( file.getCanonicalFile() ).toURI().toURL(); + } + } + if ( configURL == null ) + { + configURL = ConfigTools.class.getClassLoader().getResource( defaultPropertiesFile ); + if ( configURL == null ) + { + File file = new File( defaultPropertiesFile ); + if ( !file.exists() ) + { + file = new File( "webapp/WEB-INF/" + defaultPropertiesFile ); + } + if ( file.exists() ) + { + configURL = file.getCanonicalFile().toURI().toURL(); + } + } + } + } + catch ( MalformedURLException ex ) + { + throw new RaplaException( "malformed config path" + path ); + } + catch ( IOException ex ) + { + throw new RaplaException( "Can't resolve config path" + path ); + } + + if ( configURL == null ) + { + throw new RaplaException( defaultPropertiesFile + + " not found on classpath and in working folder " + + " Path config file with -c argument. " + + " For more information start rapla -? or read the api-docs." ); + } + return configURL; + } + + /** Creates an configuration URL from a configuration filename and + the webstart codebae. + If filename is null the URL of the defaultPropertiesFile + will be returned. + */ + public static URL webstartConfigToURL( String config, String defaultPropertiesFilename ) throws RaplaException + { + try + { + URL base = JNLPUtil.getCodeBase(); + URL configURL; + if ( config != null && config.trim().length() > 0 ) + { + return new URL( base, config ); + } + else + { + configURL = new URL( base, defaultPropertiesFilename ); + } + return configURL; + } + catch ( Exception ex ) + { + throw new RaplaException( "Can't get configuration file in webstart mode." ); + } + } + + /** resolves a context value in the passed string. + If the string begins with ${ the method will lookup the String-Object in the context and returns it. + If it doesn't, the method returns the unmodified string. + Example: + resolveContext("${download-server}") returns the same as + context.get("download-server").toString(); + + @throws ConfigurationException when no contex-object was found for the given variable. + */ + public static String resolveContext( String s, RaplaContext sm ) throws ConfigurationException + { + int startToken = s.indexOf( "${" ); + if ( startToken < 0 ) + return s; + int endToken = s.indexOf( "}", startToken ); + String token = s.substring( startToken + 2, endToken ); + StringBuffer value = new StringBuffer(); + value.append( s.substring( 0, startToken ) ); + try + { + Object contextObject = sm.lookup( token ); + String stringRep = contextObject.toString(); + value.append( stringRep ); + } + catch ( RaplaContextException ex ) + { + throw new ConfigurationException( ex.getMessage(), ex ); + } + value.append( s.substring( endToken + 1 ) ); + return value.toString(); + } + +} diff --git a/Rapla/src/org/rapla/framework/internal/ContainerImpl.java b/Rapla/src/org/rapla/framework/internal/ContainerImpl.java new file mode 100644 index 0000000..3e04e39 --- /dev/null +++ b/Rapla/src/org/rapla/framework/internal/ContainerImpl.java @@ -0,0 +1,457 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.framework.internal; + +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.avalon.framework.activity.Disposable; +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.ConfigurationException; +import org.apache.avalon.framework.container.ContainerUtil; +import org.apache.avalon.framework.logger.AbstractLogEnabled; +import org.apache.avalon.framework.logger.ConsoleLogger; +import org.apache.avalon.framework.logger.Logger; +import org.rapla.framework.Container; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaContextException; +import org.rapla.framework.RaplaException; +import org.rapla.framework.StartupEnvironment; + +/** Base class for the ComponentContainers in Rapla. + * Containers are the RaplaMainContainer, the Client- and the Server-Service + */ +public class ContainerImpl extends AbstractLogEnabled implements Container, Disposable +{ + protected Container m_parent; + protected RaplaContext m_context; + protected Configuration m_config; + + protected List m_componentHandler = new ArrayList(); + protected HashMap m_roleMap = new HashMap(); + protected LogManagerAdapter m_loggerManager; + + public ContainerImpl(RaplaContext parentContext, Configuration config) throws RaplaException { + m_config = config; + service( parentContext ); + init( ); + } + + + protected void init() throws RaplaException { + configure( m_config ); + addContainerProvidedComponentInstance( Container.class.getName(), this ); + addContainerProvidedComponentInstance( Logger.class.getName(), getLogger()); + } + + public StartupEnvironment getStartupEnvironment() { + try + { + return (StartupEnvironment)getContext().lookup( StartupEnvironment.ROLE); + } + catch ( RaplaContextException e ) + { + throw new IllegalStateException(" Container not initialized with a startup environment"); + } + } + + private void service(final RaplaContext parent) throws RaplaContextException { + if (parent.has( "logger-manager" )) { + m_loggerManager = (LogManagerAdapter) parent.lookup("logger-manager"); + } else { + final Logger logger; + if ( parent.has(Logger.class.getName() ) ) + { + logger = (Logger) parent.lookup( Logger.class.getName()); + } + else + { + logger = new ConsoleLogger(ConsoleLogger.LEVEL_INFO); + } + + m_loggerManager = new LogManagerAdapter() + { + public Logger getLoggerForCategory(String categoryName) + { + return logger.getChildLogger( categoryName ); + } + + public Logger getDefaultLogger() + { + return logger; + } + }; + } + enableLogging( m_loggerManager.getDefaultLogger()); + if ( parent.has(Container.ROLE )) { + m_parent = (Container) parent.lookup( Container.ROLE); + } + m_context = new RaplaContext() { + + public Object lookup(String role) throws RaplaContextException { + ComponentHandler handler = getHandler( role ); + if ( handler != null ) { + return handler.get(); + } + return parent.lookup( role); + } + + public boolean has(String role) { + if (getHandler( role ) != null) + return true; + return parent.has( role ); + + } + + ComponentHandler getHandler( String role) { + int hintSeperator = role.indexOf('/'); + String roleName = role; + String hint = null; + if ( hintSeperator > 0 ) { + roleName = role.substring( 0, hintSeperator ); + hint = role.substring( hintSeperator + 1 ); + } + return ContainerImpl.this.getHandler( roleName, hint ); + } + + }; + } + + + + protected void configure( final Configuration config ) + throws RaplaException + { + Map m_componentInfos = getComponentInfos(); + final Configuration[] elements = config.getChildren(); + for ( int i = 0; i < elements.length; i++ ) + { + final Configuration element = elements[i]; + final String id = element.getAttribute( "id", null ); + if ( null == id ) + { + // Only components with an id attribute are treated as components. + getLogger().debug( "Ignoring configuration for component, " + element.getName() + + ", because the id attribute is missing." ); + } + else + { + final String className; + final String[] roles; + if ( "component".equals( element.getName() ) ) + { + try { + className = element.getAttribute( "class" ); + Configuration[] roleConfigs = element.getChildren("roles"); + roles = new String[ roleConfigs.length ]; + for ( int j=0;j< roles.length;j++) { + roles[j] = roleConfigs[j].getValue(); + } + } catch ( ConfigurationException ex) { + throw new RaplaException( ex); + } + } + else + { + String configName = element.getName(); + final ComponentInfo roleEntry = (ComponentInfo) m_componentInfos.get( configName ); + if ( null == roleEntry ) + { + final String message = "No class found matching configuration name " + + "[name: " + element.getName() + ", location: " + element.getLocation() + "]"; + getLogger().error( message ); + + continue; + } + roles = roleEntry.getRoles(); + className = roleEntry.getClassname(); + } + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Configuration processed for: " + className ); + } + Logger logger = m_loggerManager.getLoggerForCategory( id ); + ComponentHandler handler =new ComponentHandler( element, className, logger); + for ( int j=0;j< roles.length;j++) { + String roleName = (roles[j]); + addHandler( roleName, id, handler ); + } + } + } + } + + protected Map getComponentInfos() { + return Collections.EMPTY_MAP; + } + + synchronized public void addContainerProvidedComponentInstance(String role,Object component) { + addContainerProvidedComponentInstance( role, component.getClass().getName(),component); + } + + synchronized public void addContainerProvidedComponentInstance(String roleName,String hint,Object component) { + addHandler( roleName, hint, new ComponentHandler(component)); + } + + synchronized public void addContainerProvidedComponent(String classname) { + addContainerProvidedComponent( classname, classname); + } + + synchronized public void addContainerProvidedComponent(String role,String classname) { + addContainerProvidedComponent( new String[] {role}, classname, classname, null); + } + + synchronized public void addContainerProvidedComponent(String role,String classname, Configuration config) { + addContainerProvidedComponent( new String[] {role}, classname, classname, config); + } + + synchronized public void addContainerProvidedComponent(String role,String classname, String hint,Configuration config) { + addContainerProvidedComponent( new String[] {role}, classname, hint, config); + } + + synchronized public void addContainerProvidedComponent(String[] roles,String classname,String hint, Configuration config) { + ComponentHandler handler = new ComponentHandler( config, classname, getLogger() ); + for ( int i=0;i +

    This package contains the framework, that is +responsible for component creation with dependency injection. +It also provides the basic services for the plugin facility +of Rapla.

    +

    +It combines functionality of the avalon framework with that +of the pico container. It was programmed to fit the need of Rapla +but contains no domain knowledge. It can also +be used in other Software. +

    + + + diff --git a/Rapla/src/org/rapla/gui/CalendarModel.java b/Rapla/src/org/rapla/gui/CalendarModel.java new file mode 100644 index 0000000..111e49d --- /dev/null +++ b/Rapla/src/org/rapla/gui/CalendarModel.java @@ -0,0 +1,52 @@ +package org.rapla.gui; + +import java.util.Collection; +import java.util.Date; + +import org.rapla.entities.User; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Reservation; +import org.rapla.framework.RaplaException; + +public interface CalendarModel extends Cloneable +{ + public String ROLE = CalendarModel.class.getName(); + + String getNonEmptyTitle(); + + User getUser(); + + Date getSelectedDate(); + + void setSelectedDate( Date date ); + + Date getStartDate(); + + void setStartDate( Date date ); + + int getSize(); + + Date getEndDate(); + + void setEndDate( Date date ); + + Collection getSelectedObjects(); + + /** Calendar View Plugins can use the calendar options to store their requiered optional parameters for a calendar view */ + Object getOption(String name); + + Collection getSelectedObjectsAndChildren() throws RaplaException; + + /** Convenience method to extract the allocatables from the selectedObjects and their children + * @see #getSelectedObjectsAndChildren */ + Allocatable[] getSelectedAllocatables() throws RaplaException; + + User[] getSelectedUsers() throws RaplaException; + + Reservation[] getReservations( Date startDate, Date endDate ) throws RaplaException; + + Reservation[] getReservations() throws RaplaException; + + + Object clone(); +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/gui/CalendarOptions.java b/Rapla/src/org/rapla/gui/CalendarOptions.java new file mode 100644 index 0000000..5017e65 --- /dev/null +++ b/Rapla/src/org/rapla/gui/CalendarOptions.java @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui; +import java.util.Set; + +import org.rapla.entities.domain.RepeatingEnding; +import org.rapla.entities.domain.RepeatingType; + + +/** This class contains the. + * Worktimes and dates (Useful for the week-view) +Configuration is done in the calendar option menu. + +Hours belonging to the worktime get a different color in the +weekview. This is also the minimum interval that will be used for +printing.
    + +Excluded Days are only visible, when there is an appointment to +display.
    + */ + +public interface CalendarOptions { + public static final String ROLE = CalendarOptions.class.getName(); + + int getWorktimeStart(); + int getRowsPerHour(); + int getWorktimeEnd(); + Set getExcludeDays(); + boolean isExceptionsVisible(); + boolean isCompactColumns(); + boolean isResourceColoring(); + boolean isEventColoring(); + //boolean isConflictsVisible(); + boolean isInfiniteRepeating(); + boolean isNtimesRepeating(); + boolean isUntilRepeating(); + int getnTimes(); + RepeatingEnding getRepeatingDuration(); + RepeatingType getRepeatingType(); + String getEventType(); +} diff --git a/Rapla/src/org/rapla/gui/CalendarOptionsImpl.java b/Rapla/src/org/rapla/gui/CalendarOptionsImpl.java new file mode 100644 index 0000000..9bab9dd --- /dev/null +++ b/Rapla/src/org/rapla/gui/CalendarOptionsImpl.java @@ -0,0 +1,177 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui; +import java.util.HashSet; +import java.util.Set; +import java.util.StringTokenizer; + +import org.apache.avalon.framework.configuration.Configuration; +import org.rapla.entities.domain.RepeatingEnding; +import org.rapla.entities.domain.RepeatingType; +import org.rapla.framework.RaplaException; + +/** WARNING!! This class should not be public to the outside. Please use the interface */ +public class CalendarOptionsImpl implements CalendarOptions { + public final static String CALENDAR_OPTIONS="org.rapla.calendarview"; + + public static final String WORKTIME = "worktime"; + public static final String EXCLUDE_DAYS = "exclude-days"; + public static final String ROWS_PER_HOUR = "rows-per-hour"; + public final static String EXCEPTIONS_VISIBLE="exceptions-visible"; + public final static String COMPACT_COLUMNS="compact-columns"; + public final static String COLOR_BLOCKS="color"; + public final static String COLOR_RESOURCES="resources"; + public final static String COLOR_EVENTS="reservations"; + + public final static String REPEATING="repeating"; + public final static String NTIMES="repeating.ntimes"; + public final static String CALNAME="calendar-name"; + int nTimes; + RepeatingEnding repeatingField; + RepeatingType repeatingType; + + + public final static String REPEATINGTYPE="repeatingtype"; + public final static String EVENTTYPE = "eventtype"; + + + Set excludeDays = new HashSet(); + + int maxtime = -1; + int mintime = -1; + int rowsPerHour = 4; + boolean exceptionsVisible; + boolean compactColumns = false; // use for strategy.setFixedSlotsEnabled + Configuration config; + String colorField; + String eventType; + + public CalendarOptionsImpl(Configuration config ) throws RaplaException { + this.config = config; + Configuration worktime = config.getChild( WORKTIME ); + StringTokenizer tokenizer = new StringTokenizer( worktime.getValue("8-18"), "-" ); + try { + if ( tokenizer.hasMoreTokens() ) + mintime = new Integer( tokenizer.nextToken().toLowerCase().trim()).intValue( ); + if ( tokenizer.hasMoreTokens() ) + maxtime = new Integer( tokenizer.nextToken().toLowerCase().trim()).intValue( ); + } catch ( NumberFormatException e ) { + throw new RaplaException( "Invalid time in " + worktime.getLocation( ) + + ". only numbers are allowed e.g. 8-18!"); + } + if ( mintime == -1 || maxtime == -1 /*|| mintime >= maxtime*/ ) + throw new RaplaException("Invalid intervall in " + + worktime.getLocation() + + ". Use the following format 8-16 !"); + + Configuration exclude = config.getChild( EXCLUDE_DAYS ); + tokenizer = new StringTokenizer( exclude.getValue(""), "," ); + while ( tokenizer.hasMoreTokens()) { + String token = tokenizer.nextToken().toLowerCase().trim(); + try { + excludeDays.add( new Integer(token) ); + } catch ( NumberFormatException e ) { + throw new RaplaException("Invalid day in " + + exclude.getLocation() + + ". only numbers are allowed!"); + } + } // end of while () + + rowsPerHour = config.getChild( ROWS_PER_HOUR ).getValueAsInteger( 4 ); + exceptionsVisible = config.getChild(EXCEPTIONS_VISIBLE).getValueAsBoolean(false); + + colorField = config.getChild( COLOR_BLOCKS ).getValue( COLOR_RESOURCES ); + + + repeatingField = RepeatingEnding.findForString(config.getChild( REPEATING ).getValue( RepeatingEnding.END_DATE.toString() )); + nTimes = config.getChild( NTIMES ).getValueAsInteger( 1 ); + + repeatingType = RepeatingType.findForString(config.getChild( REPEATINGTYPE ).getValue( RepeatingType.WEEKLY.toString() )); + + eventType = config.getChild( EVENTTYPE ).getValue(null); + + } + + public Configuration getConfig() { + return config; + } + + public int getWorktimeStart() { + return mintime; + } + + public int getRowsPerHour() { + return rowsPerHour; + } + + public int getWorktimeEnd() { + return maxtime; + } + + public Set getExcludeDays() { + return excludeDays; + } + +// BJO 00000012 + public RepeatingEnding getRepeatingDuration() { + return repeatingField; + } + + public boolean isInfiniteRepeating() { + return repeatingField.equals( RepeatingEnding.FOREVEVER ); + } + + public boolean isNtimesRepeating() { + boolean rt=false; + if(repeatingField.equals( RepeatingEnding.FOREVEVER )) + rt = false; + else if(repeatingField.equals( RepeatingEnding.N_TIMES )) + rt = true; + else if(repeatingField.equals( RepeatingEnding.END_DATE )) + rt = true; + return rt; + } + + public boolean isUntilRepeating() { + return repeatingField.equals( RepeatingEnding.END_DATE ); + } + + public int getnTimes() { + return nTimes; + } + + public boolean isExceptionsVisible() { + return exceptionsVisible; + } + + public boolean isCompactColumns() { + return compactColumns; + } + + public boolean isResourceColoring() { + return colorField.equals( COLOR_RESOURCES ); + } + + public boolean isEventColoring() { + return colorField.equals( COLOR_EVENTS ); + } + + public RepeatingType getRepeatingType() { + return repeatingType; + } + + public String getEventType() { + return eventType; + } + +} diff --git a/Rapla/src/org/rapla/gui/DefaultPluginOption.java b/Rapla/src/org/rapla/gui/DefaultPluginOption.java new file mode 100644 index 0000000..c65532b --- /dev/null +++ b/Rapla/src/org/rapla/gui/DefaultPluginOption.java @@ -0,0 +1,121 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui; + +import java.awt.BorderLayout; +import java.util.Locale; + +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JPanel; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.DefaultConfiguration; +import org.rapla.entities.configuration.Preferences; +import org.rapla.entities.configuration.RaplaConfiguration; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.plugin.RaplaPluginMetaInfo; + +abstract public class DefaultPluginOption extends RaplaGUIComponent implements OptionPanel { + public DefaultPluginOption(RaplaContext sm) throws RaplaException { + super(sm); + } + + protected JCheckBox activate = new JCheckBox("Aktivieren"); + protected Configuration config; + protected Preferences preferences; + JComponent container; + + abstract public String getDescriptorClassName(); + + protected JPanel createPanel() throws RaplaException { + JPanel panel = new JPanel(); + panel.setLayout( new BorderLayout()); + panel.add( activate, BorderLayout.NORTH ); + return panel; + } + + /** + * @see org.rapla.gui.OptionPanel#setPreferences(org.rapla.entities.configuration.Preferences) + */ + public void setPreferences(Preferences preferences) { + this.preferences = preferences; + } + + /** + * @see org.rapla.gui.OptionPanel#commit() + */ + public void commit() throws RaplaException { + RaplaConfiguration config = (RaplaConfiguration) preferences.getEntry("org.rapla.plugin"); + String className = getDescriptorClassName(); + //getDescritorClassName() + + DefaultConfiguration newChild = new DefaultConfiguration("plugin" ); + newChild.setAttribute( "enabled", activate.isSelected()); + newChild.setAttribute( "class", className); + addChildren( newChild ); + RaplaConfiguration newConfig = config.replace(config.find("class", className), newChild); + preferences.putEntry( "org.rapla.plugin",newConfig); + } + + protected void addChildren( DefaultConfiguration newConfig) { + } + + protected void readConfig( Configuration config) { + + } + + + /** + * @see org.rapla.gui.OptionPanel#show() + */ + public void show() throws RaplaException { + activate.setText( getString("selected")); + container = createPanel(); + RaplaConfiguration raplaConfig = (RaplaConfiguration)preferences.getEntry("org.rapla.plugin"); + if ( raplaConfig == null) { + throw new RaplaException("No PluginConfiguration found. Please try a reimport!" ); + } + config = raplaConfig.find( "class" ,getDescriptorClassName()); + if ( config == null) { + config = new DefaultConfiguration("plugin"); + } + + Boolean defaultValueAsBoolean = (Boolean) findDescriptor( getDescriptorClassName()).getPluginMetaInfos(RaplaPluginMetaInfo.METAINFO_PLUGIN_ENABLED_BY_DEFAULT); + boolean defaultSelection = defaultValueAsBoolean != null ? defaultValueAsBoolean.booleanValue() : false; + activate.setSelected( config.getAttributeAsBoolean("enabled", defaultSelection)); + readConfig( config ); + } + + + /** + * @see org.rapla.gui.toolkit.RaplaWidget#getComponent() + */ + public JComponent getComponent() { + return container; + } + + + + /** + * @see org.rapla.entities.Named#getName(java.util.Locale) + */ + public String getName(Locale locale) { + return getDescriptorClassName(); + } + + +} + + diff --git a/Rapla/src/org/rapla/gui/EditComponent.java b/Rapla/src/org/rapla/gui/EditComponent.java new file mode 100644 index 0000000..624c624 --- /dev/null +++ b/Rapla/src/org/rapla/gui/EditComponent.java @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui; + +import org.rapla.framework.RaplaException; +import org.rapla.gui.toolkit.RaplaWidget; + +public interface EditComponent extends RaplaWidget +{ + /** maps all fields back to the current object.*/ + public void mapToObject() throws RaplaException; + public Object getObject(); + public void setObject(Object o) throws RaplaException; +} diff --git a/Rapla/src/org/rapla/gui/EditController.java b/Rapla/src/org/rapla/gui/EditController.java new file mode 100644 index 0000000..bac208d --- /dev/null +++ b/Rapla/src/org/rapla/gui/EditController.java @@ -0,0 +1,18 @@ +package org.rapla.gui; + +import java.awt.Component; + +import org.rapla.entities.Entity; +import org.rapla.entities.RaplaObject; +import org.rapla.framework.RaplaException; + +public interface EditController +{ + public final static String ROLE = EditController.class.getName(); + + EditComponent createUI( RaplaObject obj ) throws RaplaException; + + void edit( Entity obj, Component owner ) throws RaplaException; + void edit( Entity obj, String title, Component owner ) throws RaplaException; + +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/gui/InfoFactory.java b/Rapla/src/org/rapla/gui/InfoFactory.java new file mode 100644 index 0000000..ec14bac --- /dev/null +++ b/Rapla/src/org/rapla/gui/InfoFactory.java @@ -0,0 +1,29 @@ +package org.rapla.gui; + +import java.awt.Component; +import java.awt.Point; + +import javax.swing.JComponent; + +import org.rapla.framework.RaplaException; +import org.rapla.gui.toolkit.DialogUI; + +public interface InfoFactory +{ + String ROLE = InfoFactory.class.getName(); + + JComponent createInfoComponent( Object object ) throws RaplaException; + + /** same as getToolTip(obj, true) */ + String getToolTip( Object obj ); + + /** @param wrapHtml wraps an html Page arround the tooltip */ + String getToolTip( Object obj, boolean wrapHtml ); + + void showInfoDialog( Object object, Component owner ) throws RaplaException; + + void showInfoDialog( Object object, Component owner, Point point ) throws RaplaException; + + DialogUI createDeleteDialog( Object[] deletables, Component owner ) throws RaplaException; + +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/gui/MenuContext.java b/Rapla/src/org/rapla/gui/MenuContext.java new file mode 100644 index 0000000..d4d6768 --- /dev/null +++ b/Rapla/src/org/rapla/gui/MenuContext.java @@ -0,0 +1,72 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas, Bettina Lademann | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui; +import java.awt.Component; +import java.awt.Point; +import java.util.Collection; +import java.util.Collections; + +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaDefaultContext; + +public class MenuContext extends RaplaDefaultContext +{ + public static String PARENT_COMPONENT = "parent_component"; + public static String POPUP_POINT = "popup_point"; + public static String FOCUSED_OBJECT = "menu_object"; + public static String SELECTED_OBJECTS = "selected_objects"; + + public MenuContext(RaplaContext parentContext, Object focusedObject) { + this( parentContext, focusedObject, null, null ); + } + + public MenuContext(RaplaContext parentContext, Object focusedObject, Component parent,Point p) { + super( parentContext); + put( FOCUSED_OBJECT, focusedObject ); + put( PARENT_COMPONENT, parent ); + put( POPUP_POINT, p ); + put( SELECTED_OBJECTS, Collections.EMPTY_LIST ); + } + + public void setSelectedObjects(Collection selectedObjects) { + put ( SELECTED_OBJECTS, selectedObjects); + } + + public Collection getSelectedObjects() { + return (Collection) getUnsave( SELECTED_OBJECTS); + } + + public Point getPoint() { + return (Point) getUnsave( POPUP_POINT ); + + } + + public Component getComponent() { + return (Component) getUnsave( PARENT_COMPONENT ); + } + + public Object getFocusedObject() { + return getUnsave( FOCUSED_OBJECT ); + } + + +} + + + + + + + + + diff --git a/Rapla/src/org/rapla/gui/MenuExtensionPoint.java b/Rapla/src/org/rapla/gui/MenuExtensionPoint.java new file mode 100644 index 0000000..b83d09d --- /dev/null +++ b/Rapla/src/org/rapla/gui/MenuExtensionPoint.java @@ -0,0 +1,10 @@ +package org.rapla.gui; + +import javax.swing.JMenuItem; +import javax.swing.JSeparator; + +public interface MenuExtensionPoint +{ + public void insert(JMenuItem item); + public void insert(JSeparator seperator); +} diff --git a/Rapla/src/org/rapla/gui/MenuFactory.java b/Rapla/src/org/rapla/gui/MenuFactory.java new file mode 100644 index 0000000..c13d1fc --- /dev/null +++ b/Rapla/src/org/rapla/gui/MenuFactory.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas, Bettina Lademann | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui; +import org.rapla.framework.RaplaException; +import org.rapla.gui.toolkit.MenuInterface; + +public interface MenuFactory +{ + public static final String ROLE = MenuFactory.class.getName(); + + public MenuInterface addObjectMenu(MenuInterface menu, MenuContext context, String afterId) throws RaplaException; + public MenuInterface addReservationWizards(MenuInterface menu, MenuContext context, String afterId) throws RaplaException; + +} + + + + + + + + + diff --git a/Rapla/src/org/rapla/gui/ObjectMenuFactory.java b/Rapla/src/org/rapla/gui/ObjectMenuFactory.java new file mode 100644 index 0000000..e54b291 --- /dev/null +++ b/Rapla/src/org/rapla/gui/ObjectMenuFactory.java @@ -0,0 +1,9 @@ +package org.rapla.gui; + +import org.rapla.entities.RaplaObject; +import org.rapla.gui.toolkit.RaplaMenuItem; + +public interface ObjectMenuFactory +{ + RaplaMenuItem[] create(MenuContext menuContext,RaplaObject focusedObject); +} diff --git a/Rapla/src/org/rapla/gui/OptionPanel.java b/Rapla/src/org/rapla/gui/OptionPanel.java new file mode 100644 index 0000000..e9c48b3 --- /dev/null +++ b/Rapla/src/org/rapla/gui/OptionPanel.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui; + +import org.rapla.entities.Named; +import org.rapla.entities.configuration.Preferences; +import org.rapla.framework.RaplaException; +import org.rapla.gui.toolkit.RaplaWidget; + +public interface OptionPanel extends RaplaWidget, Named { + void setPreferences(Preferences preferences); + + /** commits the changes in the option Dialog.*/ + void commit() throws RaplaException; + + /** called when the option Panel is selected for displaying.*/ + void show() throws RaplaException; + + +} + diff --git a/Rapla/src/org/rapla/gui/RaplaAction.java b/Rapla/src/org/rapla/gui/RaplaAction.java new file mode 100644 index 0000000..2c0df55 --- /dev/null +++ b/Rapla/src/org/rapla/gui/RaplaAction.java @@ -0,0 +1,81 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas, Bettina Lademann | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.Action; + +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +public abstract class RaplaAction extends RaplaGUIComponent implements Action { + private Map values = new HashMap(); + private ArrayList listenerList = new ArrayList(); + + public RaplaAction(RaplaContext sm) throws RaplaException { + super( sm ); + setEnabled(true); + } + + public Object getValue(String key) { + return values.get(key); + } + public void putValue(String key,Object value) { + Object oldValue = getValue(key); + values.put(key,value); + firePropertyChange(key,oldValue,value); + } + + public void addPropertyChangeListener(PropertyChangeListener listener) { + listenerList.add(listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + listenerList.remove(listener); + } + + protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { + if (listenerList.size() == 0) + return; + if (oldValue == newValue) + return; + + // if (oldValue != null && newValue != null && oldValue.equals(newValue)) + //return; + + PropertyChangeEvent evt = new PropertyChangeEvent(this,propertyName,oldValue,newValue); + PropertyChangeListener[] listeners = getPropertyChangeListeners(); + for (int i = 0;i
    "); + Iterator it = ex.getDependencies().iterator(); + int i = 0; + while (it.hasNext()) { + Object obj = it.next(); + buf.append((++i)); + buf.append(") "); + + + buf.append( obj); + + buf.append("
    "); + if (i == 30 && it.hasNext()) { + buf.append("... " + (ex.getDependencies().size() - 30) + " more"); + break; + } + } + return buf.toString(); + } + + /** Creates a new ErrorDialog with the specified owner and displays the waring */ + public void showWarning(String warning,Component owner) { + try { + ErrorDialog dialog = new ErrorDialog(getContext()); + dialog.showWarningDialog(warning,owner); + } catch (RaplaException ex2) { + getLogger().error(ex2.getMessage(),ex2); + } + } + + public RaplaCalendar createRaplaCalendar() { + RaplaCalendar cal = new RaplaCalendar( getI18n().getLocale() ); + cal.setDateRenderer(getDateRenderer()); + cal.setTimeZone(DateTools.getTimeZone()); + return cal; + } + + /** lookup DateRenderer from the serviceManager */ + final protected DateRenderer getDateRenderer() { + return (DateRenderer) getService(DateRenderer.class.getName()); + } + + static Color NON_WORKTIME = new Color(0xcc, 0xcc, 0xcc); + + final protected TimeRenderer getTimeRenderer() { + final int start = getCalendarOptions().getWorktimeStart(); + final int end = getCalendarOptions().getWorktimeEnd(); + return new TimeRenderer() { + public Color getBackgroundColor( int hourOfDay, int minute ) + { + if ( start >= end) + { + if ( hourOfDay >= end && hourOfDay < start) + { + return NON_WORKTIME; + } + } + else if ( hourOfDay < start || hourOfDay >= end) { + return NON_WORKTIME; + } + return null; + } + + public String getToolTipText( int hourOfDay, int minute ) + { + return null; + } + + }; + } + + + public RaplaTime createRaplaTime() { + RaplaTime cal = new RaplaTime( getI18n().getLocale() ); + cal.setTimeRenderer( getTimeRenderer() ); + int rowsPerHour =getCalendarOptions().getRowsPerHour() ; + cal.setRowsPerHour( rowsPerHour ); + cal.setTimeZone(DateTools.getTimeZone()); + return cal; + } + + + + public Map getSessionMap() { + return (Map) getService( ClientService.SESSION_MAP); + } + + protected InfoFactory getInfoFactory() { + return (InfoFactory) getService( InfoFactory.ROLE ); + } + + protected EditController getEditController() { + return (EditController) getService( EditController.ROLE ); + } + + protected ReservationController getReservationController() { + return (ReservationController) getService( ReservationController.ROLE ); + } + + public Component getMainComponent() { + return (Component) getService(ClientService.MAIN_COMPONENT); + } + + public void addCopyPaste(final JTextComponent text) throws RaplaException { + final JPopupMenu menu = new JPopupMenu(); + { + final JMenuItem copyItem = new JMenuItem(); + copyItem.addActionListener( new ActionListener() { + + public void actionPerformed(ActionEvent e) { + copy(text,e); + } + }); + copyItem.setText(getString("copy")); + + menu.add(copyItem); + } + { + final JMenuItem copyItem = new JMenuItem(); + copyItem.addActionListener( new ActionListener() { + + public void actionPerformed(ActionEvent e) { + paste(text,e); + } + }); + copyItem.setText(getString("paste")); + + menu.add(copyItem); + } + + text.add(menu); + text.addMouseListener(new MouseAdapter() + { + private void showMenuIfPopupTrigger(MouseEvent e) { + if (e.isPopupTrigger()) { + menu.show(text,e.getX() + 3, e.getY() + 3); + } + } + + public void mousePressed(MouseEvent e) { + showMenuIfPopupTrigger(e); + } + + public void mouseReleased(MouseEvent e) { + showMenuIfPopupTrigger(e); + } + } + ); + } + + + + + private IOInterface getIOService() + { + try { + return (IOInterface) getContext().lookup( IOInterface.ROLE); + } catch (RaplaContextException e) { + return null; + } + } + + protected void copy(final JTextComponent comp,ActionEvent evt) { + final IOInterface service = getIOService(); + if (service != null) { + String text = comp.getSelectedText(); + final StringSelection transferable = new StringSelection(text); + service.setContents(transferable, null); + } else { + comp.getActionMap().get(DefaultEditorKit.copyAction).actionPerformed(evt); + } + } + + private void paste(final JTextComponent comp,ActionEvent evt) { + final IOInterface service = getIOService(); + if (service != null) { + final Transferable transferable = service.getContents( null); + Object transferData; + try { + transferData = transferable.getTransferData(DataFlavor.stringFlavor); + if ( transferData != null) + { + comp.replaceSelection( transferData.toString()); + } + } catch (Exception e) { + } + + } + else + { + comp.getActionMap().get(DefaultEditorKit.pasteAction).actionPerformed(evt); + } + } + +} diff --git a/Rapla/src/org/rapla/gui/ReservationController.java b/Rapla/src/org/rapla/gui/ReservationController.java new file mode 100644 index 0000000..1434ca2 --- /dev/null +++ b/Rapla/src/org/rapla/gui/ReservationController.java @@ -0,0 +1,52 @@ +package org.rapla.gui; + +import java.awt.Component; +import java.awt.Point; +import java.util.Date; + +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Reservation; +import org.rapla.framework.RaplaException; + +/** Use the ReservationController to modify or create a {@link Reservation}. + This class handles all interactions with the user. Examples: +
  • + If you edit a reservation it will first check, if there is already is an + open edit-window for the reservation and will give focus to that window instead of + creating a new one. +
  • +
  • + If you move or delete an repeating appointment it will display dialogs + where the user will be asked if he wants to delete/move the complete appointment + or just the occurance on the selected date. +
  • +
  • + If conflicts are found, a conflict panel will be displayed on saving. +
  • + */ +public interface ReservationController +{ + + public final static String ROLE = ReservationController.class.getName(); + + void edit( Reservation reservation ) throws RaplaException; + void edit( Appointment appointment ) throws RaplaException; + boolean save(Reservation reservation,Component sourceComponent,boolean showOnlyWhenConflicts) throws RaplaException; + + public ReservationEdit[] getEditWindows(); + + /** copys an appointment without interaction */ + Appointment copyAppointment( Appointment appointment ) throws RaplaException; + + void deleteAppointment( Appointment appointment, Date from, Component sourceComponent, Point point ) throws RaplaException; + + Appointment copyAppointment( Appointment appointment, Date from, Component sourceComponent, Point point ) throws RaplaException; + + void pasteAppointment( Appointment appointment, Reservation reservation, Date start, Component sourceComponent, Point point,Allocatable[] restrictedAllocatables, boolean asNewReservation ) throws RaplaException; + + void moveAppointment( Appointment appointment, Date from, Date newStart, Component sourceComponent, Point point ) throws RaplaException; + + void resizeAppointment( Appointment appointment, Date from, Date newStart, Date newEnd, Component sourceComponent, Point p ) throws RaplaException; + +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/gui/ReservationEdit.java b/Rapla/src/org/rapla/gui/ReservationEdit.java new file mode 100644 index 0000000..1adec6c --- /dev/null +++ b/Rapla/src/org/rapla/gui/ReservationEdit.java @@ -0,0 +1,16 @@ +package org.rapla.gui; + +import java.util.Date; + +import org.rapla.entities.domain.RepeatingType; +import org.rapla.entities.domain.Reservation; +import org.rapla.framework.RaplaException; + +public interface ReservationEdit +{ + boolean isModifiedSinceLastChange(); + void addAppointment( Date start, Date end, RepeatingType repeatingType, int repeatings ) throws RaplaException; + Reservation getReservation(); + void save() throws RaplaException; + void delete() throws RaplaException; +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/gui/ReservationWizard.java b/Rapla/src/org/rapla/gui/ReservationWizard.java new file mode 100644 index 0000000..66377c5 --- /dev/null +++ b/Rapla/src/org/rapla/gui/ReservationWizard.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui; + +import java.awt.Component; + +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.framework.RaplaException; + +public interface ReservationWizard { + /** Show a new wizard dialog. + @param owner the owner-component for the wizard dialog + @param model a reference to the current CalendarModel. Here you can get information about the currently selected objects and times + @param type the default reservation-type. Can be null. + */ + + void start(Component owner,CalendarModel model, DynamicType type) throws RaplaException; + /** Wizards should override this method to return a meaningful name that will be displayed in the wizard list*/ + String toString(); +} + diff --git a/Rapla/src/org/rapla/gui/SwingCalendarView.java b/Rapla/src/org/rapla/gui/SwingCalendarView.java new file mode 100644 index 0000000..f913618 --- /dev/null +++ b/Rapla/src/org/rapla/gui/SwingCalendarView.java @@ -0,0 +1,32 @@ + +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.gui; + +import javax.swing.JComponent; + +import org.rapla.framework.RaplaException; +import org.rapla.gui.toolkit.RaplaWidget; + + +public interface SwingCalendarView extends RaplaWidget +{ + public void update( ) throws RaplaException; + /** you can provide a DateSelection component if you want */ + public JComponent getDateSelection(); + /** Most times you can only scroll programaticaly if the window is visible and the size of + * the component is known, so this method gets called when the window is visible. + * */ + public void scrollToStart(); +} diff --git a/Rapla/src/org/rapla/gui/TreeFactory.java b/Rapla/src/org/rapla/gui/TreeFactory.java new file mode 100644 index 0000000..c3d4a78 --- /dev/null +++ b/Rapla/src/org/rapla/gui/TreeFactory.java @@ -0,0 +1,29 @@ +package org.rapla.gui; + +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreeCellRenderer; +import javax.swing.tree.TreeModel; + +import org.rapla.entities.Category; +import org.rapla.entities.Named; +import org.rapla.entities.dynamictype.Classifiable; +import org.rapla.framework.RaplaException; +import org.rapla.gui.toolkit.TreeToolTipRenderer; + +public interface TreeFactory { + + String ROLE = TreeFactory.class.getName(); + + TreeModel createClassifiableModel(Classifiable[] classifiables) throws RaplaException; + + DefaultMutableTreeNode newNamedNode(Named element); + + TreeModel createModel(Category category) throws RaplaException; + + TreeModel createModelFlat(Named[] element); + + TreeToolTipRenderer createTreeToolTipRenderer(); + + TreeCellRenderer createRenderer(); + +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/gui/ViewFactory.java b/Rapla/src/org/rapla/gui/ViewFactory.java new file mode 100644 index 0000000..9afe794 --- /dev/null +++ b/Rapla/src/org/rapla/gui/ViewFactory.java @@ -0,0 +1,33 @@ + +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.gui; + +import javax.swing.Icon; + +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.servletpages.RaplaPageGenerator; + +public interface ViewFactory +{ + public String PRINT_CONTEXT = "org.rapla.PrintContext"; + public SwingCalendarView createSwingView(RaplaContext context, CalendarModel model, boolean editable) throws RaplaException; + public RaplaPageGenerator createHTMLView(RaplaContext context, CalendarModel model) throws RaplaException; + public String getViewId(); + /** return the key that is responsible for placing the view in the correct position in the drop down selection menu*/ + public String getMenuSortKey(); + public String getName(); + public Icon getIcon(); +} diff --git a/Rapla/src/org/rapla/gui/images/Images.java b/Rapla/src/org/rapla/gui/images/Images.java new file mode 100644 index 0000000..5cdf8db --- /dev/null +++ b/Rapla/src/org/rapla/gui/images/Images.java @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------* +| Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | +| | +| This program is free software; you can redistribute it and/or modify | +| it under the terms of the GNU General Public License as published by the | +| Free Software Foundation. A copy of the license has been included with | +| these distribution in the COPYING file, if not go to www.fsf.org | +| | +| As a special exception, you are granted the permissions to link this | +| program with every library, which license fulfills the Open Source | +| Definition as published by the Open Source Initiative (OSI). | +*--------------------------------------------------------------------------*/ + +package org.rapla.gui.images; + +import java.awt.Image; +import java.awt.Toolkit; +import java.io.InputStream; +import java.net.URL; + +import javax.swing.ImageIcon; + + + +/** + * Offers direct access to the images. + */ +public class Images +{ + public static InputStream getInputStream(String filename) + { + return Images.class.getResourceAsStream(filename); + } + + public static Image getImage(String filename) + { + try { + URL url = Images.class.getResource(filename); + if ( url == null) + return null; + return Toolkit.getDefaultToolkit().createImage( url); + } catch (Exception ex) { + return null; + } + } + + public static ImageIcon getIcon(String filename) + { + return new ImageIcon(getImage( filename)); + } +} diff --git a/Rapla/src/org/rapla/gui/images/admin.png b/Rapla/src/org/rapla/gui/images/admin.png new file mode 100644 index 0000000..02706e8 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/admin.png differ diff --git a/Rapla/src/org/rapla/gui/images/calendar.png b/Rapla/src/org/rapla/gui/images/calendar.png new file mode 100644 index 0000000..7fcf207 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/calendar.png differ diff --git a/Rapla/src/org/rapla/gui/images/calendar_small.png b/Rapla/src/org/rapla/gui/images/calendar_small.png new file mode 100644 index 0000000..e57a5e6 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/calendar_small.png differ diff --git a/Rapla/src/org/rapla/gui/images/categories.png b/Rapla/src/org/rapla/gui/images/categories.png new file mode 100644 index 0000000..17ccafa Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/categories.png differ diff --git a/Rapla/src/org/rapla/gui/images/choice_no.png b/Rapla/src/org/rapla/gui/images/choice_no.png new file mode 100644 index 0000000..a9e3a1b Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/choice_no.png differ diff --git a/Rapla/src/org/rapla/gui/images/choice_yes.png b/Rapla/src/org/rapla/gui/images/choice_yes.png new file mode 100644 index 0000000..21ff2d4 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/choice_yes.png differ diff --git a/Rapla/src/org/rapla/gui/images/conflicts_small.png b/Rapla/src/org/rapla/gui/images/conflicts_small.png new file mode 100644 index 0000000..688d977 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/conflicts_small.png differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/addtsk_tsk.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/addtsk_tsk.gif new file mode 100644 index 0000000..a5ac28d Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/addtsk_tsk.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/arrow_left.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/arrow_left.gif new file mode 100644 index 0000000..4fb4150 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/arrow_left.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/arrow_right.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/arrow_right.gif new file mode 100644 index 0000000..e3a7cc7 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/arrow_right.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder.gif new file mode 100644 index 0000000..bda0e6c Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_categories.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_categories.gif new file mode 100644 index 0000000..1c89679 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_categories.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_conflicts.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_conflicts.gif new file mode 100644 index 0000000..0399277 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_conflicts.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_events.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_events.gif new file mode 100644 index 0000000..2e316e7 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_events.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_events_filtered.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_events_filtered.gif new file mode 100644 index 0000000..4bb6f40 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_events_filtered.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_filtered.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_filtered.gif new file mode 100644 index 0000000..eeb8e03 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_filtered.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_orig.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_orig.gif new file mode 100644 index 0000000..dc7d3a7 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_orig.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_periods.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_periods.gif new file mode 100644 index 0000000..615fe41 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_periods.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_resources.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_resources.gif new file mode 100644 index 0000000..fe1f368 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_resources.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_resources_filtered.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_resources_filtered.gif new file mode 100644 index 0000000..910c726 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_resources_filtered.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_users.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_users.gif new file mode 100644 index 0000000..7b9ae57 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/big_folder_users.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/blank.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/blank.gif new file mode 100644 index 0000000..4574cde Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/blank.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/checked.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/checked.gif new file mode 100644 index 0000000..9cacb96 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/checked.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/clock.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/clock.gif new file mode 100644 index 0000000..0cb9751 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/clock.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/conflict.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/conflict.gif new file mode 100644 index 0000000..94fa057 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/conflict.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/copy.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/copy.gif new file mode 100644 index 0000000..71d7c95 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/copy.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/cross.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/cross.gif new file mode 100644 index 0000000..98377bf Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/cross.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/cut.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/cut.gif new file mode 100644 index 0000000..d044e59 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/cut.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/edit.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/edit.gif new file mode 100644 index 0000000..9312d7e Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/edit.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/export.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/export.gif new file mode 100644 index 0000000..5a0837d Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/export.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/filter.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/filter.gif new file mode 100644 index 0000000..cf1fe52 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/filter.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/filter_small.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/filter_small.gif new file mode 100644 index 0000000..4541a15 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/filter_small.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/folder_open.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/folder_open.gif new file mode 100644 index 0000000..51e703b Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/folder_open.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/gray.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/gray.gif new file mode 100644 index 0000000..a1b1748 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/gray.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/green.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/green.gif new file mode 100644 index 0000000..7d24707 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/green.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/help.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/help.gif new file mode 100644 index 0000000..9d70301 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/help.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/import.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/import.gif new file mode 100644 index 0000000..d38085a Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/import.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/info.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/info.gif new file mode 100644 index 0000000..a688b8c Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/info.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/minus.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/minus.gif new file mode 100644 index 0000000..d353b83 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/minus.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/new.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/new.gif new file mode 100644 index 0000000..d68588b Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/new.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/no_perm.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/no_perm.gif new file mode 100644 index 0000000..486faff Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/no_perm.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/paste.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/paste.gif new file mode 100644 index 0000000..39dd4d9 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/paste.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/permissions.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/permissions.gif new file mode 100644 index 0000000..e637951 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/permissions.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/plus.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/plus.gif new file mode 100644 index 0000000..252d7eb Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/plus.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/print.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/print.gif new file mode 100644 index 0000000..04cb84b Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/print.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/radio.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/radio.gif new file mode 100644 index 0000000..7c3356c Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/radio.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/refresh.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/refresh.gif new file mode 100644 index 0000000..2139781 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/refresh.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/reservations.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/reservations.gif new file mode 100644 index 0000000..fdde5fb Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/reservations.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/save.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/save.gif new file mode 100644 index 0000000..499dd0c Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/save.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/trash.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/trash.gif new file mode 100644 index 0000000..f9c8a2b Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/trash.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/tree_minus.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/tree_minus.gif new file mode 100644 index 0000000..1a90785 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/tree_minus.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/tree_plus.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/tree_plus.gif new file mode 100644 index 0000000..bbad168 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/tree_plus.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/unchecked.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/unchecked.gif new file mode 100644 index 0000000..f6b9f8a Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/unchecked.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/update.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/update.gif new file mode 100644 index 0000000..1b724a6 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/update.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/warning.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/warning.gif new file mode 100644 index 0000000..2b2e50f Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/warning.gif differ diff --git a/Rapla/src/org/rapla/gui/images/eclipse-icons/yellow.gif b/Rapla/src/org/rapla/gui/images/eclipse-icons/yellow.gif new file mode 100644 index 0000000..563743d Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/eclipse-icons/yellow.gif differ diff --git a/Rapla/src/org/rapla/gui/images/edit_window_small.png b/Rapla/src/org/rapla/gui/images/edit_window_small.png new file mode 100644 index 0000000..7102177 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/edit_window_small.png differ diff --git a/Rapla/src/org/rapla/gui/images/empty.png b/Rapla/src/org/rapla/gui/images/empty.png new file mode 100644 index 0000000..89ac9a7 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/empty.png differ diff --git a/Rapla/src/org/rapla/gui/images/error.png b/Rapla/src/org/rapla/gui/images/error.png new file mode 100644 index 0000000..5ad474b Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/error.png differ diff --git a/Rapla/src/org/rapla/gui/images/exception.png b/Rapla/src/org/rapla/gui/images/exception.png new file mode 100644 index 0000000..f1d2968 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/exception.png differ diff --git a/Rapla/src/org/rapla/gui/images/filter.png b/Rapla/src/org/rapla/gui/images/filter.png new file mode 100644 index 0000000..43fe182 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/filter.png differ diff --git a/Rapla/src/org/rapla/gui/images/green.png b/Rapla/src/org/rapla/gui/images/green.png new file mode 100644 index 0000000..a4450c1 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/green.png differ diff --git a/Rapla/src/org/rapla/gui/images/info.png b/Rapla/src/org/rapla/gui/images/info.png new file mode 100644 index 0000000..9a1df6e Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/info.png differ diff --git a/Rapla/src/org/rapla/gui/images/info_small.png b/Rapla/src/org/rapla/gui/images/info_small.png new file mode 100644 index 0000000..ea31930 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/info_small.png differ diff --git a/Rapla/src/org/rapla/gui/images/list.png b/Rapla/src/org/rapla/gui/images/list.png new file mode 100644 index 0000000..0cd3d1e Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/list.png differ diff --git a/Rapla/src/org/rapla/gui/images/mail.png b/Rapla/src/org/rapla/gui/images/mail.png new file mode 100644 index 0000000..a337885 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/mail.png differ diff --git a/Rapla/src/org/rapla/gui/images/new_repeating.gif b/Rapla/src/org/rapla/gui/images/new_repeating.gif new file mode 100644 index 0000000..2d6350c Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/new_repeating.gif differ diff --git a/Rapla/src/org/rapla/gui/images/no_perm.png b/Rapla/src/org/rapla/gui/images/no_perm.png new file mode 100644 index 0000000..fa1aa42 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/no_perm.png differ diff --git a/Rapla/src/org/rapla/gui/images/option_small.png b/Rapla/src/org/rapla/gui/images/option_small.png new file mode 100644 index 0000000..ffc272e Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/option_small.png differ diff --git a/Rapla/src/org/rapla/gui/images/paste_new.gif b/Rapla/src/org/rapla/gui/images/paste_new.gif new file mode 100644 index 0000000..550ea66 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/paste_new.gif differ diff --git a/Rapla/src/org/rapla/gui/images/period.png b/Rapla/src/org/rapla/gui/images/period.png new file mode 100644 index 0000000..241e80c Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/period.png differ diff --git a/Rapla/src/org/rapla/gui/images/persons.png b/Rapla/src/org/rapla/gui/images/persons.png new file mode 100644 index 0000000..08d41af Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/persons.png differ diff --git a/Rapla/src/org/rapla/gui/images/persons_yellow.png b/Rapla/src/org/rapla/gui/images/persons_yellow.png new file mode 100644 index 0000000..7ea2a4f Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/persons_yellow.png differ diff --git a/Rapla/src/org/rapla/gui/images/rapla_128x128.ico b/Rapla/src/org/rapla/gui/images/rapla_128x128.ico new file mode 100644 index 0000000..63f32c1 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/rapla_128x128.ico differ diff --git a/Rapla/src/org/rapla/gui/images/rapla_16x16.ico b/Rapla/src/org/rapla/gui/images/rapla_16x16.ico new file mode 100644 index 0000000..4bf9bb2 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/rapla_16x16.ico differ diff --git a/Rapla/src/org/rapla/gui/images/rapla_32x32.ico b/Rapla/src/org/rapla/gui/images/rapla_32x32.ico new file mode 100644 index 0000000..0c6439d Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/rapla_32x32.ico differ diff --git a/Rapla/src/org/rapla/gui/images/rapla_small.png b/Rapla/src/org/rapla/gui/images/rapla_small.png new file mode 100644 index 0000000..1a8b8b6 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/rapla_small.png differ diff --git a/Rapla/src/org/rapla/gui/images/refresh.png b/Rapla/src/org/rapla/gui/images/refresh.png new file mode 100644 index 0000000..60db566 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/refresh.png differ diff --git a/Rapla/src/org/rapla/gui/images/repeating.png b/Rapla/src/org/rapla/gui/images/repeating.png new file mode 100644 index 0000000..a4edf85 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/repeating.png differ diff --git a/Rapla/src/org/rapla/gui/images/resources.png b/Rapla/src/org/rapla/gui/images/resources.png new file mode 100644 index 0000000..81fd2e9 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/resources.png differ diff --git a/Rapla/src/org/rapla/gui/images/single.png b/Rapla/src/org/rapla/gui/images/single.png new file mode 100644 index 0000000..9fb4e90 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/single.png differ diff --git a/Rapla/src/org/rapla/gui/images/tafel.png b/Rapla/src/org/rapla/gui/images/tafel.png new file mode 100644 index 0000000..48ee122 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/tafel.png differ diff --git a/Rapla/src/org/rapla/gui/images/tafel_old.png b/Rapla/src/org/rapla/gui/images/tafel_old.png new file mode 100644 index 0000000..e0645a5 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/tafel_old.png differ diff --git a/Rapla/src/org/rapla/gui/images/tafel_tocompare.png b/Rapla/src/org/rapla/gui/images/tafel_tocompare.png new file mode 100644 index 0000000..e418366 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/tafel_tocompare.png differ diff --git a/Rapla/src/org/rapla/gui/images/taken.png b/Rapla/src/org/rapla/gui/images/taken.png new file mode 100644 index 0000000..b89eed1 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/taken.png differ diff --git a/Rapla/src/org/rapla/gui/images/tree.png b/Rapla/src/org/rapla/gui/images/tree.png new file mode 100644 index 0000000..4a96c6c Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/tree.png differ diff --git a/Rapla/src/org/rapla/gui/images/un_flag.png b/Rapla/src/org/rapla/gui/images/un_flag.png new file mode 100644 index 0000000..d705d72 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/un_flag.png differ diff --git a/Rapla/src/org/rapla/gui/images/users.png b/Rapla/src/org/rapla/gui/images/users.png new file mode 100644 index 0000000..c8e408a Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/users.png differ diff --git a/Rapla/src/org/rapla/gui/images/yellow.png b/Rapla/src/org/rapla/gui/images/yellow.png new file mode 100644 index 0000000..88a2047 Binary files /dev/null and b/Rapla/src/org/rapla/gui/images/yellow.png differ diff --git a/Rapla/src/org/rapla/gui/internal/CalendarEditor.java b/Rapla/src/org/rapla/gui/internal/CalendarEditor.java new file mode 100644 index 0000000..6c7860a --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/CalendarEditor.java @@ -0,0 +1,192 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.gui.internal; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; + +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JSplitPane; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.rapla.facade.ModificationEvent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.internal.action.ShowConflictsAction; +import org.rapla.gui.internal.common.CalendarSelectionModel; +import org.rapla.gui.internal.common.MultiCalendarView; +import org.rapla.gui.internal.splitpanes.RaplaConflictSelectionPane; +import org.rapla.gui.internal.splitpanes.RaplaResourceSelectionPane; +import org.rapla.gui.toolkit.RaplaWidget; + +final public class CalendarEditor extends RaplaGUIComponent implements RaplaWidget { + JSplitPane content = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); + final private RaplaSelectionPane resourceSelection; + final private SavedCalendarView savedViews; + final private RaplaSelectionPane conflictsView; + final public MultiCalendarView calendarContainer; + private boolean clearingConflictSelection; + private boolean clearingResourceSelection; + class TreeListener implements ChangeListener + { + + + public void stateChanged(ChangeEvent e) { + clearConflictSelection(); + + } + + } + public CalendarEditor(RaplaContext context, CalendarSelectionModel model) throws RaplaException { + super(context); + final ChangeListener treeListener = new ChangeListener() { + + public void stateChanged(ChangeEvent e) { + clearConflictSelection(); + } + }; + + calendarContainer = new MultiCalendarView(getContext(), model); + resourceSelection = new RaplaResourceSelectionPane(context, calendarContainer, model); + resourceSelection.getTreeSelection().addChangeListener( treeListener); + //selection.add(getI18n().getString("resources"), resourcesView.getComponent()); +// if (this.isAdmin()) { +// adminView = new RaplaSelectionPane(context, calendarContainer, model) +// { +// protected MenuContext createMenuContext(Point p, Object obj) { +// final MenuContext menuContext = super.createMenuContext(p, obj); +// menuContext.put("adminpane", "true"); +// return menuContext; +// }; +// }; +// adminView.getTreeSelection().addChangeListener( treeListener); +// selection.add(getI18n().getString("admin"), adminView.getComponent()); +// selection.setSelectedComponent(adminView.getComponent()); +// content.setDividerLocation(320); +// } else { + content.setDividerLocation(285); +// } + +// selection.addChangeListener(new ChangeListener() { +// public void stateChanged(ChangeEvent arg0) { +// +// clearConflictSelection(); +// +// } +// +// +// }); + + conflictsView = new RaplaConflictSelectionPane(context, calendarContainer, model); + conflictsView.getTreeSelection().addChangeListener( new ChangeListener() { + + + public void stateChanged(ChangeEvent e) + { + if (clearingConflictSelection) + { + return; + } + clearingResourceSelection = true; + try + { +// final Component selectedComponent = selection.getSelectedComponent(); +// if (selectedComponent == resourceSelection.getComponent()) +// { + resourceSelection.clearSelection(); +// } +// +// if ( adminView != null && selectedComponent == adminView.getComponent()) +// { +// adminView.clearSelection(); +// } + + } + finally + { + clearingResourceSelection = false; + } + } + }); + JPanel left = new JPanel(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + c.gridheight = 1; + c.gridx = 1; + c.gridy = 1; + c.weightx = 1; + c.weighty = 0; + c.fill = GridBagConstraints.HORIZONTAL; + savedViews = new SavedCalendarView(context, calendarContainer, resourceSelection,model); + left.add(savedViews.getComponent(), c); + c.fill = GridBagConstraints.BOTH; + c.gridy = 2; + c.weightx = 1; + c.weighty = 2.5; + left.add(resourceSelection.getComponent(), c); + c.weighty = 1.0; + c.gridy = 3; + left.add(conflictsView.getComponent(), c); + + content.setLeftComponent(left); + content.setRightComponent(calendarContainer.getComponent()); + updateConflicts(); + } + + private void clearConflictSelection() { + if ( clearingResourceSelection) + { + return; + } + clearingConflictSelection = true; + try + { + conflictsView.clearSelection(); + + + } + finally + { + clearingConflictSelection = false; + } + + } + + public void dataChanged(ModificationEvent evt) throws RaplaException { + resourceSelection.dataChanged(evt); + calendarContainer.update(); +// if (adminView != null) { +// adminView.updateTree(); +// } + savedViews.update(); + conflictsView.updateTree(); + updateConflicts(); + } + + private void updateConflicts() throws RaplaException { + boolean showConflicts = getClientFacade().getPreferences().getEntryAsBoolean( ShowConflictsAction.CONFIG_ENTRY, true); + conflictsView.getComponent().setVisible( showConflicts); + } + + public void start() throws RaplaException { + calendarContainer.getSelectedCalendar().scrollToStart(); + } + + public JComponent getComponent() { + return content; + } + +} diff --git a/Rapla/src/org/rapla/gui/internal/CalendarOption.java b/Rapla/src/org/rapla/gui/internal/CalendarOption.java new file mode 100644 index 0000000..858e3a0 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/CalendarOption.java @@ -0,0 +1,333 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Calendar; +import java.util.Locale; + +import javax.swing.BoxLayout; +import javax.swing.DefaultListCellRenderer; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; + +import org.apache.avalon.framework.configuration.DefaultConfiguration; +import org.rapla.components.calendar.RaplaNumber; +import org.rapla.components.calendar.RaplaTime; +import org.rapla.components.calendarview.WeekdayMapper; +import org.rapla.components.layout.TableLayout; +import org.rapla.entities.configuration.Preferences; +import org.rapla.entities.configuration.RaplaConfiguration; +import org.rapla.entities.domain.RepeatingEnding; +import org.rapla.entities.domain.RepeatingType; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.dynamictype.DynamicTypeAnnotations; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarOptions; +import org.rapla.gui.CalendarOptionsImpl; +import org.rapla.gui.OptionPanel; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.internal.common.NamedListCellRenderer; + +public class CalendarOption extends RaplaGUIComponent implements OptionPanel +{ + JPanel panel = new JPanel(); + JCheckBox showExceptionsField = new JCheckBox(); + JComboBox colorBlocks = new JComboBox( new String[] { + CalendarOptionsImpl.COLOR_RESOURCES + , CalendarOptionsImpl.COLOR_EVENTS + } + ); + RaplaNumber rowsPerHourField = new RaplaNumber(new Double(1),new Double(1),new Double(12), false); + Preferences preferences; + CalendarOptions options; + RaplaTime worktimeStart; + RaplaTime worktimeEnd; + JPanel excludeDaysPanel = new JPanel(); + JCheckBox[] box = new JCheckBox[7]; + WeekdayMapper mapper; + + + JCheckBox showConflictsField = new JCheckBox(); + + JComboBox repeatingDuration = new JComboBox( new RepeatingEnding[] { + RepeatingEnding.FOREVEVER + , RepeatingEnding.N_TIMES + + // , CalendarOptionsImpl.REPEATING_UNTIL + }); + RaplaNumber nTimesField = new RaplaNumber(new Double(1),new Double(1),new Double(365), false); + + + + JComboBox repeatingType = new JComboBox( new RepeatingType[] { + RepeatingType.DAILY, + RepeatingType.WEEKLY, + RepeatingType.MONTHLY, + RepeatingType.YEARLY + }); + + JComboBox eventTypeSelector; + + public CalendarOption(RaplaContext sm) throws RaplaException { + super( sm); + mapper = new WeekdayMapper(getLocale()); + worktimeStart = createRaplaTime(); + worktimeStart.setRowsPerHour( 1 ); + worktimeEnd = createRaplaTime(); + worktimeEnd.setRowsPerHour( 1 ); + double pre = TableLayout.PREFERRED; + double fill = TableLayout.FILL; + // rows = 8 columns = 4 + panel.setLayout( new TableLayout(new double[][] {{pre, 5, pre, 5 , pre, 5, pre}, {pre,5,pre,5,pre,5,pre,5,pre,5,pre,5,pre,5,pre,5,pre,5,pre,5,pre,5,pre,5,pre,5,fill}})); + + showExceptionsField.setText(""); + panel.add( new JLabel(getString("rows_per_hour")),"0,0" ); + panel.add( rowsPerHourField,"2,0"); + panel.add( new JLabel(getString("start_time")),"0,2" ); + panel.add( worktimeStart, "2,2"); + panel.add( new JLabel(getString("end_time")),"0,4" ); + panel.add( worktimeEnd,"2,4"); + panel.add( new JLabel(getString("color")),"0,6" ); + panel.add( colorBlocks,"2,6"); + + ListRenderer listRenderer = new ListRenderer(); + + colorBlocks.setRenderer( listRenderer ); + showExceptionsField.setText(""); + panel.add( new JLabel(getString("display_exceptions")),"0,8"); + panel.add( showExceptionsField,"2,8"); + +// BJO 00000030 +// showConflictsField.setText(""); // +// panel.add( new JLabel(getString("show_conflicts")),"0,10"); +// panel.add( showConflictsField,"2,10"); +// BJO 00000030 +// BJO 00000079 +// show_tips.setText(""); // +// panel.add( new JLabel(getString("show_tips")),"0,12"); +// panel.add( show_tips,"2,12"); +// BJO 00000079 +// BJO 00000012 + // panel.add( new JLabel(getString("repeating")),"0,16" ); +// BJO 00000012 +// BJO 00000052 +// panel.add( repeatingType,"2,16"); + repeatingType.setRenderer( listRenderer ); +// BJO 00000052 +// BJO 00000012 + // panel.add( repeatingDuration,"4,16"); + // panel.add( nTimesField,"6,16"); + + repeatingDuration.setRenderer( listRenderer ); + ActionListener repeatingListener = new ActionListener() { + public void actionPerformed(ActionEvent evt) { + if(repeatingDuration.getSelectedIndex()==0) + nTimesField.setEnabled(false); + else + nTimesField.setEnabled(true); + } + }; + repeatingDuration.addActionListener(repeatingListener); + +// BJO 00000063 + // panel.add( new JLabel(getString("reservation_type")),"0,18" ); + DynamicType[] types = getQuery().getDynamicTypes( DynamicTypeAnnotations.VALUE_RESERVATION_CLASSIFICATION); + eventTypeSelector = new JComboBox( types ); + // panel.add( eventTypeSelector,"2,18"); + eventTypeSelector.setRenderer(new NamedListCellRenderer(getI18n().getLocale())); + //eventTypeSelector.addActionListener( this ); +// BJO 00000063 + +// BJO 00000012 + panel.add( new JLabel(getString("exclude_days")),"0,22,l,t"); + panel.add( excludeDaysPanel,"2,22"); + excludeDaysPanel.setLayout( new BoxLayout( excludeDaysPanel,BoxLayout.Y_AXIS)); + for ( int i=0;i 0) + days.append(","); + days.append( mapper.dayForIndex( i )); + } + } + calendarOptions.addChild( rowsPerHour); + + excludeDays.setValue( days.toString()); + calendarOptions.addChild( excludeDays); + + preferences.putEntry( CalendarOptionsImpl.CALENDAR_OPTIONS,new RaplaConfiguration( calendarOptions)); + } + + + + private class ListRenderer extends DefaultListCellRenderer { + private static final long serialVersionUID = 1L; + + public Component getListCellRendererComponent(JList list,Object value, int index, boolean isSelected, boolean cellHasFocus) { + if ( value != null) { + setText(getString( value.toString())); + } + return this; + } + } +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/gui/internal/ConnectionOption.java b/Rapla/src/org/rapla/gui/internal/ConnectionOption.java new file mode 100644 index 0000000..9a14244 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/ConnectionOption.java @@ -0,0 +1,72 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal; + +import java.util.Locale; + +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; + +import org.rapla.components.calendar.RaplaNumber; +import org.rapla.components.layout.TableLayout; +import org.rapla.entities.configuration.Preferences; +import org.rapla.facade.UpdateModule; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.OptionPanel; +import org.rapla.gui.RaplaGUIComponent; + +public class ConnectionOption extends RaplaGUIComponent implements OptionPanel { + JPanel panel = new JPanel(); + RaplaNumber seconds = new RaplaNumber(new Double(10),new Double(10),null, false); + Preferences preferences; + + public ConnectionOption(RaplaContext sm) throws RaplaException { + super( sm); + seconds.getNumberField().setBlockStepSize( 60); + seconds.getNumberField().setStepSize( 10); + double pre = TableLayout.PREFERRED; + double fill = TableLayout.FILL; + panel.setLayout( new TableLayout(new double[][] {{pre, 5, pre,5, pre}, {pre,fill}})); + + panel.add( new JLabel(getString("refresh") + ": " + getI18n().format("interval.format", "","")),"0,0" ); + panel.add( seconds,"2,0"); + panel.add( new JLabel(getString("seconds")),"4,0" ); + + } + + public JComponent getComponent() { + return panel; + } + public String getName(Locale locale) { + return getString("connection"); + } + + public void setPreferences( Preferences preferences) { + this.preferences = preferences; + + } + + public void show() throws RaplaException { + int delay = preferences.getEntryAsInteger( UpdateModule.REFRESH_INTERVAL_ENTRY, 30000); + seconds.setNumber( new Long(delay / 1000)); + } + + public void commit() { + int delay = seconds.getNumber().intValue() * 1000; + preferences.putEntry( UpdateModule.REFRESH_INTERVAL_ENTRY, "" + delay ); + } + + +} diff --git a/Rapla/src/org/rapla/gui/internal/LocaleOption.java b/Rapla/src/org/rapla/gui/internal/LocaleOption.java new file mode 100644 index 0000000..5dec39b --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/LocaleOption.java @@ -0,0 +1,72 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal; + +import java.util.Locale; + +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; + +import org.rapla.client.internal.LanguageChooser; +import org.rapla.components.layout.TableLayout; +import org.rapla.entities.configuration.Preferences; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.framework.RaplaLocale; +import org.rapla.gui.OptionPanel; +import org.rapla.gui.RaplaGUIComponent; + +public class LocaleOption extends RaplaGUIComponent implements OptionPanel { + JPanel panel = new JPanel(); + LanguageChooser languageChooser; + Preferences preferences; + + public LocaleOption(RaplaContext sm) throws RaplaException { + super( sm); + languageChooser= new LanguageChooser( getLogger(), getContext()); + + double pre = TableLayout.PREFERRED; + double fill = TableLayout.FILL; + panel.setLayout( new TableLayout(new double[][] {{pre, 5, pre,5, pre}, {pre,fill}})); + + panel.add( new JLabel(getString("language") + ": "),"0,0" ); + panel.add( languageChooser.getComponent(),"2,0"); + //panel.add( new JLabel(getString("seconds")),"4,0" ); + + } + + public JComponent getComponent() { + return panel; + } + public String getName(Locale locale) { + return getString("language"); + } + + public void setPreferences( Preferences preferences) { + this.preferences = preferences; + + } + + public void show() throws RaplaException { + String language = preferences.getEntryAsString( RaplaLocale.LANGUAGE_ENTRY,null); + languageChooser.setSelectedLanguage( language); + } + + public void commit() { + String language = languageChooser.getSelectedLanguage(); + preferences.putEntry( RaplaLocale.LANGUAGE_ENTRY,language ); + } + + +} diff --git a/Rapla/src/org/rapla/gui/internal/MainFrame.java b/Rapla/src/org/rapla/gui/internal/MainFrame.java new file mode 100644 index 0000000..56f7169 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/MainFrame.java @@ -0,0 +1,257 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Window; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyVetoException; +import java.beans.VetoableChangeListener; + +import javax.swing.Box; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JMenuBar; +import javax.swing.JPanel; + +import org.rapla.client.ClientService; +import org.rapla.entities.User; +import org.rapla.facade.ModificationEvent; +import org.rapla.facade.ModificationListener; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.internal.common.CalendarSelectionModel; +import org.rapla.gui.internal.common.InternMenus; +import org.rapla.gui.toolkit.DialogUI; +import org.rapla.gui.toolkit.RaplaFrame; + + + +public class MainFrame extends RaplaGUIComponent + implements + ModificationListener +{ + RaplaMenuBar menuBar; + RaplaFrame frame = null; + Listener listener = new Listener(); + CalendarEditor cal; + JLabel statusBar = new JLabel(""); + public MainFrame(RaplaContext sm) throws RaplaException { + super(sm); + menuBar = new RaplaMenuBar(getContext()); + frame = (RaplaFrame) getService( ClientService.MAIN_COMPONENT ); + String title = getQuery().getPreferences( null ).getEntryAsString(RaplaStartOption.TITLE, getString("rapla.title")); + // CKO TODO Title should be set in config allong with the facade used + frame.setTitle(title ); + + getUpdateModule().addModificationListener(this); + CalendarSelectionModel model = (CalendarSelectionModel) getService( CalendarSelectionModel.ROLE); + cal = new CalendarEditor(sm,model); + + JMenuBar menuBar = (JMenuBar) getService( InternMenus.MENU_BAR); + menuBar.add(Box.createHorizontalGlue()); + menuBar.add(statusBar); + menuBar.add(Box.createHorizontalStrut(5)); + frame.setJMenuBar( menuBar ); + + getContentPane().setLayout( new BorderLayout() ); + // getContentPane().add ( statusBar, BorderLayout.SOUTH); + + getContentPane().add( cal.getComponent() , BorderLayout.CENTER ); + } + + public void show() throws RaplaException { + getLogger().debug("Creating Main-Frame"); + createFrame(); + //dataChanged(null); + setStatus(); + cal.start(); + frame.setIconImage(getI18n().getIcon("icon.rapla_small").getImage()); + frame.setVisible(true); + getFrameList().setMainWindow(frame); + } + + private JPanel getContentPane() { + return (JPanel) frame.getContentPane(); + } + + private void createFrame() { + Dimension dimension = java.awt.Toolkit.getDefaultToolkit().getScreenSize(); + frame.setSize(new Dimension( + Math.min(dimension.width,1200) + ,Math.min(dimension.height-20,900) + ) + ); + + frame.addVetoableChangeListener(listener); + //statusBar.setBorder( BorderFactory.createEtchedBorder()); + } + + + class Listener implements VetoableChangeListener { + public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException { + if (shouldExit()) + close(); + else + throw new PropertyVetoException("Don't close",evt); + } + + } + + public Window getFrame() { + return frame; + } + + public JComponent getComponent() { + return (JComponent) frame.getContentPane(); + } + + public void dataChanged(ModificationEvent e) throws RaplaException { + cal.dataChanged( e ); + new StatusFader(statusBar).updateStatus(); + } + + private void setStatus() throws RaplaException{ + statusBar.setMaximumSize( new Dimension(400,20)); + final StatusFader runnable = new StatusFader(statusBar); + final Thread fadeThread = new Thread(runnable); + fadeThread.start(); + + } + + class StatusFader implements Runnable{ + JLabel label; + + StatusFader(JLabel label) + { + this.label=label; + } + + public void run() { + try { + + + { + User user = getUser(); + final boolean admin = user.isAdmin(); + String name = user.getName(); + if ( name == null || name.length() == 0 ) + { + name = user.getUsername(); + } + String message = getI18n().format("rapla.welcome",name); + if ( admin) + { + message = message + " " + getString("admin.login"); + } + + statusBar.setText(message); + fadeIn( statusBar ); + } + Thread.sleep(2000); + { + fadeOut( statusBar); + updateStatus(); + fadeIn( statusBar ); + } + } catch (InterruptedException ex) { + //Logger.getLogger(Fader.class.getName()).log(Level.SEVERE, null, ex); + } catch (RaplaException e) { + } + } + + public void updateStatus() throws RaplaException { + User user = getUser(); + final boolean admin = user.isAdmin(); + String message = + "" + + user.toString() + + "" + ; + statusBar.setText(message); + final Font boldFont = statusBar.getFont().deriveFont(Font.BOLD); + statusBar.setFont( boldFont); + if ( admin) + { + statusBar.setForeground( new Color(220,30,30)); + } + else + { + statusBar.setForeground( new Color(30,30,30) ); + } + } + + private void fadeIn(JLabel label) throws InterruptedException { + int alpha=0; + Color c = label.getForeground(); + while(alpha<=230){ + alpha+=25; + final Color color = new Color(c.getRed(), c.getGreen(), c.getBlue(), alpha); + label.setForeground(color); + label.repaint(); + Thread.sleep(200); + } + } + + private void fadeOut(JLabel label) throws InterruptedException { + int alpha=250; + Color c = label.getForeground(); + while(alpha>0){ + alpha-=25; + final Color color = new Color(c.getRed(), c.getGreen(), c.getBlue(), alpha); + label.setForeground(color); + label.repaint(); + Thread.sleep(200); + } + } + } + + protected boolean shouldExit() { + try { + DialogUI dlg = DialogUI.create(getContext() + ,frame.getRootPane() + ,true + ,getString("exit.title") + ,getString("exit.question") + ,new String[] { + getString("exit.ok") + ,getString("exit.abort") + } + ); + dlg.setIcon(getIcon("icon.question")); + //dlg.getButton(0).setIcon(getIcon("icon.confirm")); + dlg.getButton(0).setIcon(getIcon("icon.abort")); + dlg.setDefault(1); + dlg.start(); + return (dlg.getSelectedIndex() == 0); + } catch (RaplaException e) { + getLogger().error( e.getMessage(), e); + return true; + } + + } + + public void close() { + getUpdateModule().removeModificationListener(this); + frame.close(); + } + +} + + + + + diff --git a/Rapla/src/org/rapla/gui/internal/MenuFactoryImpl.java b/Rapla/src/org/rapla/gui/internal/MenuFactoryImpl.java new file mode 100644 index 0000000..baaa4df --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/MenuFactoryImpl.java @@ -0,0 +1,426 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas, Bettina Lademann | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal; +import java.awt.Component; +import java.awt.Point; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import javax.swing.Action; +import javax.swing.JMenuItem; + +import org.rapla.components.util.DateTools; +import org.rapla.entities.Category; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.RaplaType; +import org.rapla.entities.User; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Period; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.dynamictype.DynamicTypeAnnotations; +import org.rapla.facade.Conflict; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.MenuContext; +import org.rapla.gui.MenuFactory; +import org.rapla.gui.ObjectMenuFactory; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.ReservationWizard; +import org.rapla.gui.internal.action.DynamicTypeAction; +import org.rapla.gui.internal.action.RaplaObjectAction; +import org.rapla.gui.internal.action.ReservationAction; +import org.rapla.gui.internal.action.user.PasswordChangeAction; +import org.rapla.gui.internal.action.user.UserAction; +import org.rapla.gui.internal.common.CalendarSelectionModel; +import org.rapla.gui.toolkit.MenuInterface; +import org.rapla.gui.toolkit.RaplaMenuItem; +import org.rapla.gui.toolkit.RaplaSeparator; +import org.rapla.plugin.RaplaExtensionPoints; + +public class MenuFactoryImpl extends RaplaGUIComponent implements MenuFactory +{ + public MenuInterface addReservationWizards( MenuInterface menu, MenuContext context, String afterId ) throws RaplaException + { + Component parent = context.getComponent(); + Object focusedObject = context.getFocusedObject(); + Point p = context.getPoint(); + Iterator it = getContainer().lookupServicesFor( RaplaExtensionPoints.RESERVATION_WIZARD_EXTENSION).values().iterator(); + while (it.hasNext()) + { + ReservationWizard wizard = (ReservationWizard) it.next(); + addReservationAction(menu,parent,p, afterId).setNew(wizard).changeObject( focusedObject ); + } + return menu; + } + + + public MenuFactoryImpl(RaplaContext sm) throws RaplaException { + super(sm); + } + + public MenuInterface addNew( MenuInterface menu, MenuContext context,String afterId ) throws RaplaException + { + // Do nothing if the user can't allocate anything + if (!canUserAllocateSomething( getUser()) ) + return menu; + + addReservationWizards(menu, context, afterId); + + Component parent = context.getComponent(); + Object focusedObject = context.getFocusedObject(); + Point p = context.getPoint(); + + + + boolean allocatableType = false; + boolean reservationType = false; + if ( focusedObject instanceof DynamicType) + { + DynamicType type = (DynamicType) focusedObject; + String classificationType = type.getAnnotation( DynamicTypeAnnotations.KEY_CLASSIFICATION_TYPE ); + allocatableType = classificationType.equals( DynamicTypeAnnotations.VALUE_PERSON_CLASSIFICATION ) || classificationType.equals( DynamicTypeAnnotations.VALUE_RESOURCE_CLASSIFICATION ); + reservationType = classificationType.equals( DynamicTypeAnnotations.VALUE_RESERVATION_CLASSIFICATION ); + } + boolean allocatableNodeContext = allocatableType || focusedObject instanceof Allocatable || focusedObject == CalendarSelectionModel.ALLOCATABLES_ROOT; + if ( isRegisterer() || isAdmin()) { + if ( allocatableNodeContext) + { + menu.addSeparator(); + addAllocatableMenuNew( menu, parent,p, focusedObject); + } + } + if ( isAdmin() ) + { + boolean reservationNodeContext = reservationType || (focusedObject!= null && focusedObject.equals( getString("reservation_type" ))); + boolean userNodeContext = focusedObject instanceof User || (focusedObject != null && focusedObject.equals( getString("users"))); + boolean periodNodeContext = focusedObject instanceof Period || (focusedObject != null && focusedObject.equals( getString("periods"))); + boolean categoryNodeContext = focusedObject instanceof Category || (focusedObject != null && focusedObject.equals( getString("categories"))); + if (userNodeContext || allocatableNodeContext || reservationNodeContext || periodNodeContext || categoryNodeContext ) + { + menu.addSeparator(); + } + if ( userNodeContext) + { + addUserMenuNew( menu , parent, p, focusedObject); + } + + + if (allocatableNodeContext) + { + addTypeMenuNew(menu, DynamicTypeAnnotations.VALUE_RESOURCE_CLASSIFICATION + ,parent, p, focusedObject); + addTypeMenuNew(menu, DynamicTypeAnnotations.VALUE_PERSON_CLASSIFICATION + ,parent, p, focusedObject); + + } + if ( periodNodeContext) + { + addPeriodMenuNew( menu , parent, p, focusedObject ); + } + if ( categoryNodeContext ) + { + Collection list = context.getSelectedObjects(); + addCategoryMenuNew( menu , parent, p, focusedObject, list ); + } + if ( reservationNodeContext) + { + addTypeMenuNew(menu, DynamicTypeAnnotations.VALUE_RESERVATION_CLASSIFICATION + ,parent, p, focusedObject); + } + /* + */ + } + + return menu; + } + + public MenuInterface addObjectMenu( MenuInterface menu, MenuContext context) throws RaplaException { + return addObjectMenu( menu, context, "EDIT_BEGIN"); + } + + public MenuInterface addObjectMenu( MenuInterface menu, MenuContext context, String afterId ) throws RaplaException + { + Component parent = context.getComponent(); + Object focusedObject = context.getFocusedObject(); + Point p = context.getPoint(); + Collection list = context.getSelectedObjects(); + + if ( focusedObject == null || !(focusedObject instanceof RaplaObject)) { + addAction(menu,parent,p, afterId).setDeleteSelection(getDeletableObjects(list)); + return menu; + } + RaplaObject obj = (RaplaObject) focusedObject; + + RaplaType type = obj.getRaplaType(); + + Iterator it = getContainer().lookupServicesFor( RaplaExtensionPoints.OBJECT_MENU_EXTENSION).values().iterator(); + while (it.hasNext()) + { + ObjectMenuFactory objectMenuFact = (ObjectMenuFactory) it.next(); + RaplaMenuItem[] items = objectMenuFact.create( context, obj); + for ( int i =0;i 0) + model.setTitle( title ); + else + model.setTitle( null); + + + String showNavEntry = showNavField.isSelected() ? "true" : "false"; + model.setOption( AbstractHTMLCalendarPage.SHOW_NAVIGATION_ENTRY, showNavEntry); + + String saveSelectedDate = saveSelectedDateField.isSelected() ? "true" : "false"; + model.setOption( AbstractHTMLCalendarPage.SAVE_SELECTED_DATE, saveSelectedDate); + + final String icalSelected = icalCheck.isSelected() ? "true" : "false"; + + model.setOption( Export2iCalPlugin.ICAL_EXPORT, icalSelected); + + final String htmlSelected = htmlCheck.isSelected() ? "true" : "false"; + model.setOption( AutoExportPlugin.HTML_EXPORT, htmlSelected); + model.save( filename); + } + catch (RaplaException ex) + { + showException( ex, parentComponent); + } + } + + + + }); + dlg.start(); + } + + + private JPanel createStatus(String filename, String generator) throws RaplaException { + final JTextField urlLabel = new JTextField(); + addCopyPaste(urlLabel); + JPanel status = new JPanel() + { + private static final long serialVersionUID = 1L; + public void setEnabled(boolean enabled) + { + super.setEnabled(enabled); + urlLabel.setEnabled( enabled); + } + }; + status.setLayout( new BorderLayout()); + urlLabel.setText( ""); + urlLabel.setEditable( true ); + urlLabel.setFont( urlLabel.getFont().deriveFont( (float)10.0)); + status.add( new JLabel("URL: "), BorderLayout.WEST ); + status.add( urlLabel, BorderLayout.CENTER ); + + final RaplaButton copyButton = new RaplaButton(); + copyButton.setBorder(BorderFactory.createEmptyBorder(2,2,2,2)); + copyButton.setFocusable(false); + copyButton.setRolloverEnabled(false); + copyButton.setIcon(getIcon("icon.copy")); + copyButton.setToolTipText(getString("copy_to_clipboard")); + copyButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + urlLabel.requestFocus(); + urlLabel.selectAll(); + copy(urlLabel,e); + } + + + }); + status.add(copyButton, BorderLayout.EAST); + urlLabel.setText( getAddress( filename,generator)); + return status; + } +} + + diff --git a/Rapla/src/org/rapla/gui/internal/RaplaDateRenderer.java b/Rapla/src/org/rapla/gui/internal/RaplaDateRenderer.java new file mode 100644 index 0000000..2497d0a --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/RaplaDateRenderer.java @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.gui.internal; +import java.awt.Color; +import java.util.Calendar; + +import org.rapla.components.calendar.DateRenderer; +import org.rapla.components.calendar.WeekendHighlightRenderer; +import org.rapla.entities.domain.Period; +import org.rapla.facade.PeriodModel; +import org.rapla.facade.RaplaComponent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +public class RaplaDateRenderer extends RaplaComponent implements DateRenderer { + protected WeekendHighlightRenderer renderer = new WeekendHighlightRenderer(); + protected Color periodColor = new Color(0xc5,0xda,0xdd); + protected PeriodModel periodModel; + Calendar calendar; + + public RaplaDateRenderer(RaplaContext sm) throws RaplaException { + super(sm); + periodModel = getPeriodModel(); + } + + public Color getBackgroundColor(int dayOfWeek,int day,int month, int year) { + Period period = + periodModel.getPeriodFor(getRaplaLocale().toDate(year,month,day)); + if (period != null) + return periodColor; + return renderer.getBackgroundColor(dayOfWeek,day,month,year); + } + + public String getToolTipText(int dayOfWeek,int day,int month, int year) { + Period period = + periodModel.getPeriodFor(getRaplaLocale().toDate(year,month,day)); + if (period != null) + return "" + getString("period") + ":
    " + period.getName(getI18n().getLocale()) + ""; + return renderer.getToolTipText(dayOfWeek,day,month,year); + } +} diff --git a/Rapla/src/org/rapla/gui/internal/RaplaMenuBar.java b/Rapla/src/org/rapla/gui/internal/RaplaMenuBar.java new file mode 100644 index 0000000..a45342d --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/RaplaMenuBar.java @@ -0,0 +1,328 @@ + +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.BorderFactory; +import javax.swing.Icon; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JScrollPane; +import javax.swing.JSeparator; +import javax.swing.KeyStroke; + +import org.rapla.client.ClientService; +import org.rapla.entities.Category; +import org.rapla.entities.User; +import org.rapla.entities.configuration.Preferences; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.EditController; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.internal.action.RaplaObjectAction; +import org.rapla.gui.internal.action.RestartRaplaAction; +import org.rapla.gui.internal.action.RestartServerAction; +import org.rapla.gui.internal.action.ShowConflictsAction; +import org.rapla.gui.internal.action.ToolTipAction; +import org.rapla.gui.internal.action.user.UserAction; +import org.rapla.gui.internal.common.CalendarSelectionModel; +import org.rapla.gui.internal.common.InternMenus; +import org.rapla.gui.internal.print.PrintAction; +import org.rapla.gui.toolkit.DialogUI; +import org.rapla.gui.toolkit.HTMLView; +import org.rapla.gui.toolkit.RaplaFrame; +import org.rapla.gui.toolkit.RaplaMenu; +import org.rapla.gui.toolkit.RaplaMenuItem; +import org.rapla.gui.toolkit.RaplaSeparator; +import org.rapla.gui.toolkit.RaplaWidget; + +public class RaplaMenuBar extends RaplaGUIComponent +{ + public RaplaMenuBar(RaplaContext context) throws RaplaException { + super(context); + JMenu systemMenu = (JMenu)getService( InternMenus.FILE_MENU_ROLE ); + systemMenu.setText( getString("file")); + + JMenu editMenu = (JMenu)getService( InternMenus.EDIT_MENU_ROLE ); + editMenu.setText( getString("edit")); + + JMenu exportMenu = (JMenu)getService( InternMenus.EXPORT_MENU_ROLE ); + exportMenu.setText( getString("export")); + + JMenu importMenu = (JMenu)getService( InternMenus.IMPORT_MENU_ROLE ); + importMenu.setText( getString("import")); + + JMenuItem newMenu = (JMenuItem)getService( InternMenus.NEW_MENU_ROLE ); + newMenu.setText( getString("new")); + + JMenuItem calendarSettings = (JMenuItem)getService( InternMenus.CALENDAR_SETTINGS ); + calendarSettings.setText( getString("calendar")); + + JMenu extraMenu = (JMenu)getService( InternMenus.EXTRA_MENU_ROLE); + extraMenu.setText( getString("help")); + + JMenu adminMenu = (JMenu)getService( InternMenus.ADMIN_MENU_ROLE ); + adminMenu.setText( getString("admin")); + + RaplaMenu viewMenu = (RaplaMenu)getService( InternMenus.VIEW_MENU_ROLE ); + viewMenu.setText( getString("view")); + + viewMenu.add( new RaplaSeparator("view_save")); + + + + systemMenu.add( newMenu); + systemMenu.add( calendarSettings); + + systemMenu.add( new JSeparator()); + + systemMenu.add( exportMenu ); + systemMenu.add( importMenu ); + systemMenu.add( adminMenu); + + + JSeparator printSep = new JSeparator(); + printSep.setName(getString("calendar")); + systemMenu.add( printSep); + + JMenuItem printMenu = new JMenuItem( getString("print")); + PrintAction printAction = new PrintAction(getContext()); + printMenu.setAction( printAction ); + printAction.setEnabled( true ); + CalendarSelectionModel model = (CalendarSelectionModel)getService(CalendarSelectionModel.ROLE); + printAction.setModel(model); + systemMenu.add( printMenu ); + + systemMenu.add( new JSeparator()); + + if ( getUserModule().canSwitchBack() ) { + JMenuItem switchBack = new JMenuItem(); + switchBack.setAction( new UserAction(getContext(),null,null).setSwitchToUser()); + adminMenu.add( switchBack ); + } + + boolean server = getUpdateModule().isClientForServer(); + if ( server && isAdmin() ) { + JMenuItem restartServer = new JMenuItem(); + restartServer.setAction( new RestartServerAction(getContext())); + adminMenu.add( restartServer ); + } + + if ( isAdmin() ) + { + JMenuItem restart = new JMenuItem(); + restart.setAction( new RestartRaplaAction(getContext())); + adminMenu.add( restart ); + } + + systemMenu.setMnemonic('F'); + JMenuItem logout = new JMenuItem(getString("exit")); + logout.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_Q, ActionEvent.CTRL_MASK ) ); + logout.setMnemonic('x'); + logout.addActionListener( new ActionListener() { + public void actionPerformed(ActionEvent event) { + RaplaFrame mainComponent = (RaplaFrame) getService( ClientService.MAIN_COMPONENT); + mainComponent.close(); + } + }); + systemMenu.add( logout ); + + RaplaMenuItem userOptions = new RaplaMenuItem("userOptions"); + editMenu.add( userOptions ); + + if ( isModifyPreferencesAllowed() ) { + userOptions.setAction( createOptionAction( getQuery().getPreferences( ))); + } else { + userOptions.setVisible( false ); + } + + final User user = getUser(); + final Preferences preferences = getQuery().getPreferences( user ); + { + RaplaMenuItem menu = new RaplaMenuItem("show_tips"); + ToolTipAction action = new ToolTipAction( context ); + menu.setAction( action ); + boolean showToolTips = preferences.getEntryAsBoolean( ToolTipAction.CONFIG_ENTRY, true); + if(showToolTips) { + menu.setSelected(true); + menu.setIcon(getIcon("icon.checked")); + } + else { + menu.setSelected(false); + menu.setIcon(getIcon("icon.unchecked")); + } + viewMenu.insertBeforeId( menu, "view_save" ); + } + { + RaplaMenuItem menu = new RaplaMenuItem("show_conflicts"); + ShowConflictsAction action = new ShowConflictsAction( context ); + menu.setAction( action ); + boolean showConflicts = preferences.getEntryAsBoolean( ShowConflictsAction.CONFIG_ENTRY, true); + if(showConflicts) { + menu.setSelected(true); + menu.setIcon(getIcon("icon.checked")); + } + else { + menu.setSelected(false); + menu.setIcon(getIcon("icon.unchecked")); + } + viewMenu.insertBeforeId( menu, "view_save" ); + } + + if ( isAdmin() ) { + RaplaMenuItem adminOptions = new RaplaMenuItem("adminOptions"); + adminOptions.setAction( createOptionAction( getQuery().getPreferences( null ))); + adminMenu.add( adminOptions ); + } + extraMenu.addSeparator(); + + RaplaMenuItem info = new RaplaMenuItem("info"); + info.setAction( createInfoAction( getContext())); + extraMenu.add( info ); + + // within the help menu we need another point for the license + RaplaMenuItem license = new RaplaMenuItem("license"); + // give this menu item an action to perform on click + license.setAction(createLicenseAction(getContext())); + // add the license dialog below the info entry + extraMenu.add(license); + + adminMenu.setEnabled( adminMenu.getMenuComponentCount() != 0 ); + exportMenu.setEnabled( exportMenu.getMenuComponentCount() != 0); + importMenu.setEnabled( importMenu.getMenuComponentCount() != 0); + } + + private Action createOptionAction( final Preferences preferences) { + AbstractAction action = new AbstractAction() { + private static final long serialVersionUID = 1L; + + public void actionPerformed(ActionEvent arg0) { + try { + EditController editContrl =(EditController) getService(EditController.ROLE); + editContrl.edit( preferences, getMainComponent()); + } catch (RaplaException ex) { + showException( ex, getMainComponent()); + } + } + + }; + action.putValue( Action.SMALL_ICON, getIcon("icon.options") ); + action.putValue( Action.NAME, getString("options")); + return action; + } + + private Action createInfoAction( RaplaContext context) throws RaplaException { + final String name = getString("info"); + final Icon icon = getIcon("icon.info_small"); + + AbstractAction action = new AbstractAction() { + private static final long serialVersionUID = 1L; + + public void actionPerformed( ActionEvent e ) + { + try { + HTMLView infoText = new HTMLView(); + infoText.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); + String javaversion; + try { + javaversion = System.getProperty("java.version"); + } catch (SecurityException ex) { + javaversion = "-"; + getLogger().warn("Permission to system properties denied!"); + } + + boolean isSigned = isSigned(); + String signed = getString( isSigned ? "yes": "no"); + + + infoText.setBody(getI18n().format("info.text",signed,javaversion)); + DialogUI dialog = DialogUI.create( getContext(),getMainComponent(),true, new JScrollPane(infoText), new String[] {getString("ok")}); + dialog.setTitle( name); + dialog.setSize( 550, 300); + dialog.startNoPack(); + } catch (RaplaException ex) { + showException( ex, getMainComponent()); + } + } + + + + }; + action.putValue( Action.SMALL_ICON, icon ); + action.putValue( Action.NAME, name); + return action; + } + + /** + * the action to perform when someone clicks on the license entry in the + * help section of the menu bar + * + * this method is a modified version of the existing method createInfoAction() + */ + private Action createLicenseAction(RaplaContext context) throws RaplaException + { + final String name = getString("licensedialog.title"); + final Icon icon = getIcon("icon.info_small"); + + // overwrite the cass AbstractAction to design our own + AbstractAction action = new AbstractAction() + { + private static final long serialVersionUID = 1L; + + // overwrite the actionPerformed method that is called on click + public void actionPerformed(ActionEvent e) + { + try + { + // we need a new instance of HTMLView to visualize the short + // version of the license text including the two links + HTMLView licenseText = new HTMLView(); + // giving the gui element some borders + licenseText.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + // we look up the text was originally meant for the welcome field + // and put it into a new instance of RaplaWidget + RaplaWidget welcomeField = (RaplaWidget) getContext().lookup("org.rapla.gui.WelcomeField"); + // the following creates the dialog that pops up, when we click + // on the license entry within the help section of the menu bar + // we call the create Method of the DialogUI class and give it all necessary things + DialogUI dialog = DialogUI.create(getContext(), getMainComponent(), true, new JScrollPane(welcomeField.getComponent()), new String[] { getString("ok") }); + // setting the dialog's title + dialog.setTitle(name); + // and the size of the popup window + dialog.setSize(550, 250); + // but I honestly have no clue what this startNoPack() does + dialog.startNoPack(); + } + catch (RaplaException ex) + { + showException(ex, getMainComponent()); + } + } + }; + + action.putValue(Action.SMALL_ICON, icon); + action.putValue(Action.NAME, name); + return action; + } + + +} + + + diff --git a/Rapla/src/org/rapla/gui/internal/RaplaSelectionPane.java b/Rapla/src/org/rapla/gui/internal/RaplaSelectionPane.java new file mode 100644 index 0000000..048aabc --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/RaplaSelectionPane.java @@ -0,0 +1,314 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.gui.internal; + +import java.awt.BorderLayout; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; + +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.tree.DefaultTreeModel; + +import org.rapla.components.layout.TableLayout; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.RaplaType; +import org.rapla.entities.User; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Period; +import org.rapla.entities.dynamictype.ClassificationFilter; +import org.rapla.facade.Conflict; +import org.rapla.facade.ModificationEvent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.MenuContext; +import org.rapla.gui.MenuFactory; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.TreeFactory; +import org.rapla.gui.internal.action.RaplaObjectAction; +import org.rapla.gui.internal.common.CalendarSelectionModel; +import org.rapla.gui.internal.common.InternMenus; +import org.rapla.gui.internal.common.MultiCalendarView; +import org.rapla.gui.internal.view.TreeFactoryImpl; +import org.rapla.gui.toolkit.PopupEvent; +import org.rapla.gui.toolkit.PopupListener; +import org.rapla.gui.toolkit.RaplaMenu; +import org.rapla.gui.toolkit.RaplaPopupMenu; +import org.rapla.gui.toolkit.RaplaTree; +import org.rapla.gui.toolkit.RaplaWidget; + +public class RaplaSelectionPane extends RaplaGUIComponent implements RaplaWidget { + protected JPanel content = new JPanel(); + public RaplaTree treeSelection = new RaplaTree(); + TableLayout tableLayout; + protected JPanel buttonsPanel = new JPanel(); + + protected final CalendarSelectionModel model; + MultiCalendarView view; + Listener listener = new Listener(); + Conflict[] conflicts = new Conflict[] {}; + + + public RaplaSelectionPane(RaplaContext context, MultiCalendarView view, CalendarSelectionModel model) throws RaplaException { + super(context); + + this.model = model; + this.view = view; + /*double[][] sizes = new double[][] { { TableLayout.FILL }, { TableLayout.PREFERRED, TableLayout.PREFERRED, TableLayout.FILL } }; + tableLayout = new TableLayout(sizes);*/ + content.setLayout(new BorderLayout()); + + content.add(treeSelection); + // content.setPreferredSize(new Dimension(260,400)); + content.setBorder(BorderFactory.createRaisedBevelBorder()); + + content.add(buttonsPanel, BorderLayout.NORTH); + + treeSelection.setToolTipRenderer(getTreeFactory().createTreeToolTipRenderer()); + treeSelection.setMultiSelect(true); + + updateTree(); + updateSelection(); + treeSelection.addChangeListener(listener); + treeSelection.addPopupListener(listener); + treeSelection.addDoubleclickListeners(listener); + javax.swing.ToolTipManager.sharedInstance().registerComponent(treeSelection.getTree()); + + updateMenu(); + + + /*JButton test = new JButton("Show Selection"); + test.addActionListener(new ActionListener() { + + + public void actionPerformed(ActionEvent e) { + try { + applyFilter(); + } catch (Exception ex) { + showException(ex, getComponent()); + } + } + + }); + buttonsPanel.add(test);*/ + } + + public RaplaTree getTreeSelection() { + return treeSelection; + } + + protected CalendarSelectionModel getModel() { + return model; + } + + public void dataChanged(ModificationEvent evt) throws RaplaException { + updateTree(); + updateMenu(); + } + + final protected TreeFactory getTreeFactory() { + return (TreeFactory) getService(TreeFactory.ROLE); + } + + boolean treeListenersEnabled = true; + + /* + * (non-Javadoc) + * + * @see org.rapla.gui.internal.view.ITreeFactory#createClassifiableModel(org.rapla.entities.dynamictype.Classifiable[], org.rapla.entities.dynamictype.DynamicType[]) + */ + protected void updateTree() throws RaplaException { + + treeSelection.getTree().setRootVisible(false); + treeSelection.getTree().setShowsRootHandles(true); + treeSelection.getTree().setCellRenderer(((TreeFactoryImpl) getTreeFactory()).createComplexRenderer()); + + DefaultTreeModel treeModel = generateTree(); + try { + treeListenersEnabled = false; + treeSelection.exchangeTreeModel(treeModel); + updateSelection(); + } finally { + treeListenersEnabled = true; + } + + } + + protected DefaultTreeModel generateTree() throws RaplaException { + ClassificationFilter[] filter = getModel().getAllocatableFilter(); + DefaultTreeModel treeModel = ((TreeFactoryImpl) getTreeFactory()).createModel(filter, null, null); + return treeModel; + } + + protected void updateSelection() throws RaplaException { + Collection selectedObjects = new ArrayList(getModel().getSelectedObjects()); + if (model.isOnlyCurrentUserSelected()) { + selectedObjects.remove(getUser()); + } + treeSelection.select(selectedObjects); + } + + public JComponent getComponent() { + return content; + } + + protected MenuContext createMenuContext(Point p, Object obj) { + MenuContext menuContext = new MenuContext(getContext(), obj, getComponent(), p); + return menuContext; + } + + protected void showTreePopup(PopupEvent evt) { + try { + + Point p = evt.getPoint(); + Object obj = evt.getSelectedObject(); + List list = treeSelection.getSelectedElements(); + + MenuContext menuContext = createMenuContext(p, obj); + menuContext.setSelectedObjects(list); + + + RaplaPopupMenu menu = new RaplaPopupMenu(); + + RaplaMenu newMenu = new RaplaMenu("new"); + newMenu.setText(getString("new")); + ((MenuFactoryImpl) getMenuFactory()).addNew(newMenu, menuContext, null); + + getMenuFactory().addObjectMenu(menu, menuContext, "EDIT_BEGIN"); + menu.insertAfterId(newMenu, "EDIT_BEGIN"); + + JComponent component = (JComponent) evt.getSource(); + + menu.show(component, p.x, p.y); + } catch (RaplaException ex) { + showException(ex, getComponent()); + } + } + + class Listener implements PopupListener, ChangeListener, ActionListener { + + public void showPopup(PopupEvent evt) { + showTreePopup(evt); + } + + public void actionPerformed(ActionEvent evt) { + try { + if (!canUserAllocateSomething( getUser()) ) + return; + if (!isRegisterer() && !isAdmin()) + return; + Object focusedObject = evt.getSource(); + if ( focusedObject == null || !(focusedObject instanceof RaplaObject)) + return; + // System.out.println(focusedObject.toString()); + RaplaType type = ((RaplaObject) focusedObject).getRaplaType(); + if ( type.equals(User.TYPE) + || type.equals(Allocatable.TYPE) + || type.equals(Period.TYPE) + ) + { + + RaplaObjectAction editAction = new RaplaObjectAction( getContext(), getComponent(),null); + if (editAction.canModify( focusedObject)) + { + editAction.setEdit(focusedObject); + editAction.actionPerformed(null); + } + } + } catch (RaplaException ex) { + showException (ex,getComponent()); + } + } + + public void stateChanged(ChangeEvent evt) { + if (!treeListenersEnabled) { + return; + } + try { + updateChange(); + } catch (Exception ex) { + showException(ex, getComponent()); + } + } + + + } + + public void updateChange() throws RaplaException { + getSelectedObjects(); + updateMenu(); + applyFilter(); + } + + public void applyFilter() throws RaplaException { + view.getSelectedCalendar().update(); + } + + protected HashSet getSelectedObjects() throws RaplaException { + HashSet elements = new HashSet(treeSelection.getSelectedElements()); + + getModel().setSelectedObjects(elements); + return elements; + } + + public void clearSelection() + { + treeListenersEnabled = false; + try + { + treeSelection.select( Collections.EMPTY_SET); + } + finally + { + treeListenersEnabled = true; + } + } + + protected void updateMenu() throws RaplaException { + RaplaMenu editMenu = (RaplaMenu) getService(InternMenus.EDIT_MENU_ROLE); + RaplaMenu newMenu = (RaplaMenu) getService(InternMenus.NEW_MENU_ROLE); + + editMenu.removeAllBetween("EDIT_BEGIN", "EDIT_END"); + newMenu.removeAll(); + + List list = treeSelection.getSelectedElements(); + Object focusedObject = null; + if (list.size() == 1) { + focusedObject = treeSelection.getSelectedElement(); + } + + MenuContext menuContext = createMenuContext( null, focusedObject); + menuContext.setSelectedObjects(list); + + getMenuFactory().addObjectMenu(editMenu, menuContext, "EDIT_BEGIN"); + ((MenuFactoryImpl) getMenuFactory()).addNew(newMenu, menuContext, null); + + newMenu.setEnabled(newMenu.getMenuComponentCount() > 0 && canUserAllocateSomething(getUser())); + editMenu.setEnabled(canUserAllocateSomething(getUser())); + } + + public MenuFactory getMenuFactory() { + return (MenuFactory) getService(MenuFactory.ROLE); + } + +} diff --git a/Rapla/src/org/rapla/gui/internal/RaplaStartOption.java b/Rapla/src/org/rapla/gui/internal/RaplaStartOption.java new file mode 100644 index 0000000..40cc44e --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/RaplaStartOption.java @@ -0,0 +1,81 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal; + +import java.util.Locale; + +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; + +import org.rapla.components.layout.TableLayout; +import org.rapla.entities.configuration.Preferences; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.OptionPanel; +import org.rapla.gui.RaplaGUIComponent; + +public class RaplaStartOption extends RaplaGUIComponent implements OptionPanel { + JPanel panel = new JPanel(); + JTextField calendarName; + Preferences preferences; + + public final static String TITLE = "org.rapla.title"; + + public RaplaStartOption(RaplaContext sm) throws RaplaException { + super( sm); + + double pre = TableLayout.PREFERRED; + double fill = TableLayout.FILL; + panel.setLayout( new TableLayout(new double[][] {{pre, 5, pre,5, pre}, {pre,fill}})); + + calendarName = new JTextField(); + addCopyPaste( calendarName); + calendarName.setColumns(30); + panel.add( new JLabel(getString("calendarname")),"0,0" ); + panel.add( calendarName,"2,0"); + calendarName.setEnabled(true); + } + + public JComponent getComponent() { + return panel; + } + public String getName(Locale locale) { + return getString("options"); + } + + public void setPreferences( Preferences preferences) { + this.preferences = preferences; + + } + + public void show() throws RaplaException { + String name = preferences.getEntryAsString( TITLE,""); + calendarName.setText(name); + } + + public void commit() { + String title = calendarName.getText(); + if ( title.trim().length() > 0) + { + preferences.putEntry( TITLE,title ); + } + else + { + preferences.putEntry( TITLE, (String)null); + } + } + + +} diff --git a/Rapla/src/org/rapla/gui/internal/SavedCalendarView.java b/Rapla/src/org/rapla/gui/internal/SavedCalendarView.java new file mode 100644 index 0000000..6258901 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/SavedCalendarView.java @@ -0,0 +1,432 @@ +package org.rapla.gui.internal; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.*; + +import javax.swing.AbstractAction; +import javax.swing.BorderFactory; +import javax.swing.ComboBoxModel; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.JToolBar; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import org.rapla.components.layout.TableLayout; +import org.rapla.entities.configuration.Preferences; +import org.rapla.entities.configuration.RaplaMap; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaAction; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.internal.common.CalendarModelImpl; +import org.rapla.gui.internal.common.CalendarSelectionModel; +import org.rapla.gui.internal.common.InternMenus; +import org.rapla.gui.internal.common.MultiCalendarView; +import org.rapla.gui.toolkit.DialogUI; +import org.rapla.gui.toolkit.RaplaMenu; +import org.rapla.plugin.abstractcalendar.AbstractHTMLCalendarPage; +import org.rapla.plugin.autoexport.AutoExportPlugin; + +public class SavedCalendarView extends RaplaGUIComponent { + + JComboBox selectionBox; + + private boolean listenersEnabled = true; + List filenames = new ArrayList(); + final MultiCalendarView calendarContainer; + final CalendarSelectionModel model; + final RaplaSelectionPane resourceSelection; + JToolBar toolbar = new JToolBar(); + class SaveAction extends RaplaAction + { + + public SaveAction(RaplaContext sm) throws RaplaException { + super(sm); + final String name = getString("save") ; + putValue(NAME,name); + putValue(SHORT_DESCRIPTION,name); + putValue(SMALL_ICON,getIcon("icon.save")); + } + + public void actionPerformed(ActionEvent arg0) { + save(); + } + } + + class PublishAction extends RaplaAction + { + PublishDialog publishDialog; + public PublishAction(RaplaContext sm) throws RaplaException { + super(sm); + final String name = getString("publish") ; + putValue(NAME,name); + putValue(SHORT_DESCRIPTION,name); + putValue(SMALL_ICON,getIcon("icon.export")); + publishDialog = new PublishDialog(getContext()); + + } + + public void actionPerformed(ActionEvent arg0) { + try + { CalendarSelectionModel model = (CalendarSelectionModel) getService( CalendarSelectionModel.ROLE); + String filename = getSelectedFile(); + Component parentComponent = getMainComponent(); + publishDialog.export(model, parentComponent, filename); + } + catch (RaplaException ex) { + showException( ex, getMainComponent()); + } + } + + public boolean hasPublishActions() + { + return publishDialog.hasPublishActions(); + } + + } + + class DeleteAction extends RaplaAction + { + public DeleteAction(RaplaContext sm) throws RaplaException { + super(sm); + final String name = getString("delete"); + putValue(NAME,name); + putValue(SHORT_DESCRIPTION,name); + putValue(SMALL_ICON,getIcon("icon.delete")); + + } + + public void actionPerformed(ActionEvent arg0) { + try + { + String[] objects = new String[] { getSelectedFile()}; + DialogUI dlg = getInfoFactory().createDeleteDialog( objects, getMainComponent()); + dlg.start(); + if (dlg.getSelectedIndex() != 0) + return; + delete(); + } + catch (RaplaException ex) { + showException( ex, getMainComponent()); + } + } + + + + } + + final SaveAction saveAction; + final PublishAction publishAction; + final DeleteAction deleteAction; + + + public SavedCalendarView(RaplaContext context, final MultiCalendarView calendarContainer, final RaplaSelectionPane resourceSelection, final CalendarSelectionModel model) throws RaplaException { + super(context); + saveAction = new SaveAction(context); + publishAction = new PublishAction(context); + deleteAction = new DeleteAction( context); + setChildBundleName(AutoExportPlugin.RESOURCE_FILE); + this.model = model; + this.calendarContainer = calendarContainer; + this.resourceSelection = resourceSelection; + JButton save = new JButton(); + JButton publish = new JButton(); + JButton delete = new JButton(); + + + toolbar.setFloatable( false); + selectionBox = new JComboBox(); + selectionBox.setMinimumSize( new Dimension(120,30)); + selectionBox.setSize( new Dimension(150,30)); + // rku: updated, the next line prevented resizing the combobox when using the divider of the splitpane + // especially, when having long filenames this is annoying + + //selectionBox.setMaximumSize( new Dimension(200,30)); + selectionBox.setPreferredSize( new Dimension(150,30)); + + save.setAction( saveAction); + publish.setAction(publishAction); + RaplaMenu settingsMenu = (RaplaMenu)getService(InternMenus.CALENDAR_SETTINGS); + settingsMenu.insertAfterId(new JMenuItem(saveAction), null); + if ( publishAction.hasPublishActions()) + { + settingsMenu.insertAfterId(new JMenuItem(publishAction), null); + } + settingsMenu.insertAfterId(new JMenuItem(deleteAction),null); + // exportMenu.insertAfterId(new JSeparator(), InternMenus.NEW_MENU_ROLE); + // exportMenu.add(publishAction); + + delete.setAction( deleteAction); + toolbar.add(new JLabel(getString("calendar"))); + toolbar.add(new JToolBar.Separator()); + toolbar.add( selectionBox); + toolbar.add(new JToolBar.Separator()); + + toolbar.add(save); + save.setText(""); + publish.setText(""); + delete.setText(""); + if ( publishAction.hasPublishActions()) + { + toolbar.add(publish); + } + toolbar.add(delete); + toolbar.setBorder( BorderFactory.createEmptyBorder(5, 5, 5, 5)); + + update(); + + final int defaultIndex = ((DefaultComboBoxModel) selectionBox.getModel()).getIndexOf(getString("default")); + if (defaultIndex != -1) + selectionBox.setSelectedIndex(defaultIndex); + selectionBox.addActionListener( new ActionListener() { + + public void actionPerformed(ActionEvent e) { + if ( !listenersEnabled) + { + return; + } + try + { + changeSelection(); + } + catch (RaplaException ex) { + showException( ex, getMainComponent()); + } + } + }); + } + + public JComponent getComponent() { + return toolbar; + } + + private void changeSelection() throws RaplaException + { + String selectedFile = getSelectedFile(); + + // store date + final Date tmpDate = model.getSelectedDate(); + model.load(selectedFile); + final String entry = (String)model.getOption(AbstractHTMLCalendarPage.SAVE_SELECTED_DATE); + if ("false".equalsIgnoreCase(entry)) { + model.setSelectedDate(tmpDate); + } + + + updateActions(); + resourceSelection.dataChanged(null); + calendarContainer.update(); + } + + public void update() throws RaplaException + { + updateActions(); + try + { + listenersEnabled = false; + final String item = getSelectedFile(); + final Preferences preferences = getQuery().getPreferences(); + Map exportMap= ((RaplaMap)preferences.getEntry(AutoExportPlugin.PLUGIN_ENTRY)); + filenames.clear(); + final String defaultEntry = getString("default"); + + filenames.add(defaultEntry); + if ( exportMap != null) { + for (Iterator it= exportMap.keySet().iterator();it.hasNext();) { + String filename = (String) it.next(); + filenames.add( filename); + } + } + // rku: sort entries by name + Collections.sort(filenames); + DefaultComboBoxModel model = new DefaultComboBoxModel(filenames.toArray()); + selectionBox.setModel(model); + + if ( item != null ) + { + model.setSelectedItem( item ); + } + } + finally + { + listenersEnabled = true; + } + + } + + private void updateActions() { + String selectedFile = getSelectedFile(); + boolean isDefault = selectedFile == null || selectedFile.equals(getString("default")); + final boolean modifyPreferencesAllowed = isModifyPreferencesAllowed(); + saveAction.setEnabled(modifyPreferencesAllowed ); + publishAction.setEnabled( modifyPreferencesAllowed); + deleteAction.setEnabled( !isDefault && modifyPreferencesAllowed); + } + + private void delete() throws RaplaException + { + final Preferences preferences = newEditablePreferences(); + Map exportMap= ((RaplaMap)preferences.getEntry(AutoExportPlugin.PLUGIN_ENTRY)); + Map newMap = new TreeMap(); + final String selectedFile = getSelectedFile(); + for (Iterator it= exportMap.keySet().iterator();it.hasNext();) { + String filename = (String)it.next(); + if (!filename.equals( selectedFile)) { + newMap.put( filename, exportMap.get( filename )); + } + } + preferences.putEntry( AutoExportPlugin.PLUGIN_ENTRY, getModification().newRaplaMap( newMap )); + getModification().store( preferences); + final int defaultIndex = ((DefaultComboBoxModel) selectionBox.getModel()).getIndexOf(getString("default")); + if (defaultIndex != -1) + selectionBox.setSelectedIndex(defaultIndex); + else + selectionBox.setSelectedIndex(0); + //changeSelection(); + } + + private void save() { + final String selectedFile = getSelectedFile(); + + final Component parentComponent = getMainComponent(); + try { + + + JPanel panel = new JPanel(); + final JTextField textField = new JTextField(20); + addCopyPaste( textField); + String dateString = getRaplaLocale().formatDate(model.getSelectedDate()); + final JCheckBox saveSelectedDateField = new JCheckBox(getI18n().format("including_date",dateString)); + + panel.setPreferredSize( new Dimension(600,300)); + panel.setLayout(new TableLayout( new double[][] {{TableLayout.PREFERRED,5,TableLayout.FILL},{TableLayout.PREFERRED,5,TableLayout.PREFERRED,5,TableLayout.FILL}})); + panel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); + panel.add(new JLabel(getString("file.enter_name") +":"), "0,0"); + panel.add(textField, "2,0"); + addCopyPaste( textField); + panel.add(saveSelectedDateField, "2,2"); + + final String entry = (String)model.getOption(AbstractHTMLCalendarPage.SAVE_SELECTED_DATE); + saveSelectedDateField.setSelected( entry == null || entry.equals("true")); + + final JList list = new JList(filenames.toArray()); + + panel.add( new JScrollPane(list), "0,4,2,1"); + + textField.setText( selectedFile); + list.addListSelectionListener( new ListSelectionListener() { + + public void valueChanged( ListSelectionEvent e ) + { + String filename = (String) list.getSelectedValue(); + if ( filename != null) { + textField.setText( filename ); + try { + final CalendarSelectionModel m = new CalendarModelImpl(getContext(), getUser()); + m.load(filename); + final String entry = (String)m.getOption(AbstractHTMLCalendarPage.SAVE_SELECTED_DATE); + saveSelectedDateField.setSelected( entry == null || entry.equals("true")); + } catch (RaplaException ex) { + showException( ex, getMainComponent()); + } + } + + } + + }); + + final DialogUI dlg = DialogUI.create( + getContext(), + parentComponent,true,panel, + new String[] { + getString("save") + ,getString("cancel") + }); + dlg.setTitle(getString("save") + " " +getString("calendar_settings")); + dlg.getButton(0).setIcon(getIcon("icon.save")); + dlg.getButton(1).setIcon(getIcon("icon.cancel")); + dlg.getButton(0).setAction( new AbstractAction() { + private static final long serialVersionUID = 1L; + + public void actionPerformed(ActionEvent e) { + String filename = textField.getText().trim(); + if (filename.length() == 0) + { + showWarning(getString("error.no_name"), parentComponent); + return; + } + dlg.close(); + + + try + { + String saveSelectedDate = saveSelectedDateField.isSelected() ? "true" : "false"; + model.setOption( AbstractHTMLCalendarPage.SAVE_SELECTED_DATE, saveSelectedDate); + + final ComboBoxModel selectionModel = selectionBox.getModel(); + + // We reset exports for newly created files + if ( !contains(selectionModel,filename)) + { + model.resetExports(); + } + model.save(filename); + try + { + listenersEnabled = false; + selectionModel.setSelectedItem( filename); + } + finally + { + listenersEnabled = true; + } + } + catch (RaplaException ex) + { + showException( ex, parentComponent); + } + + } + + private boolean contains(ComboBoxModel model2,String filename) + { + for ( int i=0;i0:=n-times + } + + public AppointmentAction setNew(Date start,Date end,Allocatable allocatable, RepeatingType repeatingType,DynamicType dynamicType, int repeatings ) { + this.start = start; + this.end = end; + this.repeatings = repeatings; + this.dynamicType = dynamicType; + this.allocatable = allocatable; + this.repeatingType = repeatingType; + this.type = NEW; + putValue(NAME, getString("new_reservation") + getRepeatingString( repeatingType ) ); + if ( repeatingType != null) + { + putValue(SMALL_ICON, getIcon("icon.new_repeating")); + } + else + { + putValue(SMALL_ICON, getIcon("icon.single")); + } + setEnabled( canAllocate( start, end, allocatable)); + return this; + } + + private String getRepeatingString(RepeatingType repeatingType) { + if ( repeatingType == null) + return ""; + return " (" + getString( repeatingType.toString() ) + ")"; + } + + public AppointmentAction setAddTo(ReservationEdit reservationEdit,Date start,Date end,Allocatable allocatable, RepeatingType repeatingType, int repeatings) { + this.reservationEdit = reservationEdit; + this.repeatings = repeatings; + this.start = start; + this.end = end; + this.allocatable = allocatable; + this.repeatingType = repeatingType; + this.type = ADD_TO_RESERVATION; + putValue(NAME, getString("new_appointment") + + " " + getString("for") + " '" + getName(reservationEdit.getReservation()) + "'" + + getRepeatingString( repeatingType )); + if ( repeatingType != null) + { + putValue(SMALL_ICON, getIcon("icon.new_repeating")); + } + else + { + putValue(SMALL_ICON, getIcon("icon.single")); + } + setEnabled( canAllocate( start, end, allocatable)); + return this; + } + + private boolean canAllocate(Date start, Date end, Allocatable allocatables) { + if ( allocatable == null) { + return true; + } + try { + return allocatable.canAllocate( getUser(), start, end, getQuery().today() ); + } catch (RaplaException ex) { + return false; + } + } + + public AppointmentAction setCopy(Appointment appointment,Date from) { + this.appointment = appointment; + this.start = from; + this.type = COPY; + putValue(NAME, getString("copy")); + putValue(SMALL_ICON, getIcon("icon.copy")); + setEnabled(canModify(appointment.getReservation())); + return this; + } + + public AppointmentAction setPaste(Date start) { + this.start = start; + this.type = PASTE; + putValue(NAME, getString("paste")); + putValue(SMALL_ICON, getIcon("icon.paste")); + setEnabled(isAppointmentOnClipboard()); + return this; + } + + public AppointmentAction setPasteAsNew(Date start) { + this.start = start; + this.type = PASTE_AS_NEW; + putValue(NAME, getString("paste_as") + " " + getString( "new_reservation" ) ); + putValue(SMALL_ICON, getIcon("icon.paste_new")); + setEnabled(isAppointmentOnClipboard()); + return this; + } + + /** + * Context menu entry to delete an appointment. + */ + public AppointmentAction setDelete(Appointment appointment, Date from){ + this.appointment = appointment; + this.start = from; + this.type = DELETE; + putValue(NAME, getI18n().format("delete.format", getString("appointment"))); + putValue(SMALL_ICON, getIcon("icon.delete")); + setEnabled(canModify(appointment.getReservation())); + return this; + } + + public AppointmentAction setView(Appointment appointment) { + this.appointment = appointment; + this.type = VIEW; + putValue(NAME, getString("view")); + putValue(SMALL_ICON, getIcon("icon.help")); + User owner = appointment.getReservation().getOwner(); + try + { + User user = getUser(); + boolean canView = getQuery().canReadReservationsFromOthers( user) || user.equals( owner); + setEnabled( canView); + } + catch (RaplaException ex) + { + getLogger().error( "Can't get user",ex); + } + return this; + } + + public AppointmentAction setEdit(Appointment appointment, Date from) { + this.start = from; + this.appointment = appointment; + this.type = EDIT; + putValue(NAME, getString("edit")); + putValue(SMALL_ICON, getIcon("icon.edit")); + setEnabled(canModify(appointment.getReservation()) || getQuery().canExchangeAllocatables(appointment.getReservation())); + return this; + } + + + public void actionPerformed(ActionEvent evt) { + try { + switch (type) { + case DELETE: delete();break; + case COPY: copy();break; + case PASTE: paste(false);break; + case PASTE_AS_NEW: paste( true);break; + case NEW: newReservation();break; + case ADD_TO_RESERVATION: addToReservation();break; + case EDIT: edit();break; + case VIEW: view();break; + } + } catch (RaplaException ex) { + showException(ex,parent); + } // end of try-catch + } + + public void view() throws RaplaException { + getInfoFactory().showInfoDialog(appointment.getReservation(),parent,point); + } + + public void edit() throws RaplaException { + appointment.getReservation().setSelectedSlotDate(start); + getReservationController().edit( appointment); + } + + private void delete() throws RaplaException { + getReservationController().deleteAppointment(appointment,start,parent,point); + } + + private void copy() throws RaplaException { + Reservation event = appointment.getReservation(); + Appointment copy = getReservationController().copyAppointment(appointment,start,parent,point); + // store on clibboard + getClipboard().setAppointment(copy,event, event.getRestrictedAllocatables(appointment)); + } + + private RaplaClipboard getClipboard() { + return (RaplaClipboard) getService(RaplaClipboard.ROLE); + } + + private void paste(boolean asNewReservation) throws RaplaException { + if (!isAppointmentOnClipboard()) + return; + Appointment appointment = getClipboard().getAppointment(); + Reservation reservation = getClipboard().getReservation(); + getReservationController().pasteAppointment( + appointment + ,reservation + ,start + ,parent + ,point + ,getClipboard().getRestrictedAllocatables() + ,asNewReservation); + } + + private void newReservation() throws RaplaException { + create(start,end,allocatable, repeatingType, dynamicType, repeatings); + } + + /* (non-Javadoc) + * @see org.rapla.gui.edit.reservation.IReservationController#startNew(java.util.Date, java.util.Date, org.rapla.entities.domain.Allocatable, java.lang.String, org.rapla.entities.dynamictype.DynamicType, int) + */ + public void create(Date start,Date end,Allocatable allocatable, RepeatingType repeatingType, DynamicType type, int repeatings) throws RaplaException { + Reservation mutableReservation = getModification().newReservation(); + if ( type != null) { + mutableReservation.setClassification( type.newClassification()); + } + Appointment appointment = getModification().newAppointment( start, end ); + if ( repeatingType != null ) { + ReservationHelper.makeRepeatingForPeriod(getPeriodModel(), appointment, repeatingType, repeatings ); + } + mutableReservation.addAppointment(appointment); + if (allocatable != null) + mutableReservation.addAllocatable(allocatable); + appointment.getReservation().setSelectedSlotDate(start); + getReservationController().edit( appointment); + } + + private void addToReservation() throws RaplaException { + reservationEdit.addAppointment(start,end, repeatingType, repeatings); + } + + public boolean isAppointmentOnClipboard() { + return (getClipboard().getAppointment() != null); + } + + +} diff --git a/Rapla/src/org/rapla/gui/internal/action/CategoryAction.java b/Rapla/src/org/rapla/gui/internal/action/CategoryAction.java new file mode 100644 index 0000000..acc00c5 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/action/CategoryAction.java @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas, Bettina Lademann | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.action; + +import java.awt.Component; +import java.awt.Point; + +import org.rapla.entities.Category; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.toolkit.DialogUI; + +public class CategoryAction extends RaplaObjectAction{ + public CategoryAction(RaplaContext sm,Component parent,Point p) throws RaplaException { + super(sm,parent,p); + } + + protected void delete() throws RaplaException { + if (object == null) + return; + Object[] objects = new Object[] {object}; + DialogUI dlg = getInfoFactory().createDeleteDialog( objects, parent); + dlg.start(); + if (dlg.getSelectedIndex() != 0) + return; + Category category = (Category) object; + Category parentClone = (Category) getModification().edit( category.getParent() ); + parentClone.removeCategory( parentClone.findCategory( category) ); + getModification().store( parentClone ); + } + +} diff --git a/Rapla/src/org/rapla/gui/internal/action/DynamicTypeAction.java b/Rapla/src/org/rapla/gui/internal/action/DynamicTypeAction.java new file mode 100644 index 0000000..39c595e --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/action/DynamicTypeAction.java @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas, Bettina Lademann | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.action; +import java.awt.Component; +import java.awt.Point; + +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +public class DynamicTypeAction extends RaplaObjectAction { + String classificationType; + public DynamicTypeAction(RaplaContext sm,Component parent) throws RaplaException { + super(sm,parent); + } + public DynamicTypeAction(RaplaContext sm,Component parent,Point p) throws RaplaException { + super(sm,parent,p); + } + + public DynamicTypeAction setNewClassificationType(String classificationType) { + this.classificationType = classificationType; + super.setNew( null ); + return this; + } + + protected void newEntity() throws RaplaException { + getEditController().edit(getModification().newDynamicType(classificationType) + ,parent); + } + + +} diff --git a/Rapla/src/org/rapla/gui/internal/action/OnlyMyAction.java b/Rapla/src/org/rapla/gui/internal/action/OnlyMyAction.java new file mode 100644 index 0000000..36037bb --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/action/OnlyMyAction.java @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas, Bettina Lademann | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.action; +import java.awt.event.ActionEvent; + +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaAction; +import org.rapla.gui.internal.common.CalendarSelectionModel; + +public class OnlyMyAction extends RaplaAction { + CalendarSelectionModel model; + + public OnlyMyAction(RaplaContext sm,CalendarSelectionModel model) throws RaplaException { + super( sm); + this.model = model; + this.setEnabled( true); + putValue(NAME,getString("only_own_reservations")); + //putValue(SMALL_ICON,getIcon("icon.filter")); + } + + public CalendarSelectionModel getModel() { + return model; + } + + public void setModel(CalendarSelectionModel model) { + } + + public void actionPerformed(ActionEvent evt) { + try { + boolean isSelected = model.isOnlyCurrentUserSelected(); + if ( !isSelected ) { + model.selectUser( getUser()); + } else { + model.selectUser( null ); + } + firePropertyChange("model",new Object(), model); + } + catch (RaplaException ex) + { + } + + } + + +} diff --git a/Rapla/src/org/rapla/gui/internal/action/RaplaObjectAction.java b/Rapla/src/org/rapla/gui/internal/action/RaplaObjectAction.java new file mode 100644 index 0000000..e7ad541 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/action/RaplaObjectAction.java @@ -0,0 +1,276 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas, Bettina Lademann | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.action; + +import java.awt.Component; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.util.Collection; +import java.util.Iterator; + +import org.rapla.entities.Category; +import org.rapla.entities.Entity; +import org.rapla.entities.RaplaType; +import org.rapla.entities.User; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Period; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.Classifiable; +import org.rapla.entities.dynamictype.Classification; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.dynamictype.DynamicTypeAnnotations; +import org.rapla.facade.ModificationModule; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaAction; +import org.rapla.gui.toolkit.DialogUI; + +public class RaplaObjectAction extends RaplaAction { + public final static int DELETE = 1; + public final static int COPY = 2; + public final static int PASTE = 3; + public final static int CUT = 4; + public final static int NEW = 5; + public final static int EDIT = 6; + public final static int VIEW = 7; + public final static int DELETE_SELECTION = 8; + protected Component parent; + Point point; + protected int type; + boolean isPerson; + protected Object object; + Collection objectList; + protected RaplaType raplaType; + + public RaplaObjectAction(RaplaContext sm) throws RaplaException { + this(sm,null); + } + + public RaplaObjectAction(RaplaContext sm,Component parent) throws RaplaException { + this(sm,parent,null); + } + + public RaplaObjectAction(RaplaContext sm,Component parent,Point point) throws RaplaException { + super( sm); + this.parent = parent; + this.point = point; + } + + public RaplaObjectAction setNew(RaplaType raplaType) { + this.raplaType = raplaType; + this.type = NEW; + putValue(NAME, getString("new")); + putValue(SMALL_ICON, getIcon("icon.new")); + update(); + return this; + } + + public RaplaObjectAction setDelete(Object object) { + this.type = DELETE; + putValue(NAME, getString("delete")); + putValue(SMALL_ICON, getIcon("icon.delete")); + changeObject(object); + return this; + } + + public RaplaObjectAction setDeleteSelection(Collection selection) { + this.type = DELETE_SELECTION; + putValue(NAME, getString("delete_selection")); + putValue(SMALL_ICON, getIcon("icon.delete")); + changeSelection( selection ); + return this; + } + + public RaplaObjectAction setView(Object object) { + this.type = VIEW; + putValue(NAME, getString("view")); + putValue(SMALL_ICON, getIcon("icon.help")); + changeObject(object); + return this; + } + + public RaplaObjectAction setEdit(Object object) { + this.type = EDIT; + putValue(NAME, getString("edit")); + putValue(SMALL_ICON, getIcon("icon.edit")); + changeObject(object); + return this; + } + + public void changeObject(Object object) { + this.object = object; + if (type == DELETE) { + if (object == null) + putValue(NAME, getString("delete")); + else + putValue(NAME, getI18n().format("delete.format",getName(object))); + } + update(); + } + + public void changeSelection(Collection objectList) { + this.objectList = objectList; + update(); + } + + protected void update() { + boolean enabled = true; + if (type == EDIT || type == DELETE) { + enabled = canModify(object); + } else if (type == NEW) { + enabled = isRegisterer() || isAdmin(); + } else if (type == DELETE_SELECTION) { + if (objectList != null && objectList.size() > 0 ) { + Iterator it = objectList.iterator(); + while (it.hasNext()) { + if (!canModify(it.next())){ + enabled = false; + break; + } + } + } else { + enabled = false; + } + } + setEnabled(enabled); + } + + + public void actionPerformed(ActionEvent evt) { + try { + switch (type) { + case DELETE: delete();break; + case DELETE_SELECTION: deleteSelection();break; + case EDIT: edit();break; + case NEW: newEntity();break; + case VIEW: view();break; + } + } catch (RaplaException ex) { + showException(ex,parent); + } // end of try-catch + } + + public void view() throws RaplaException { + getInfoFactory().showInfoDialog(object,parent); + } + + + protected Object newEntity(RaplaType raplaType) throws RaplaException { + ModificationModule m = getModification(); + if ( Reservation.TYPE.is( raplaType )) + return m.newReservation(); + if ( Allocatable.TYPE.is( raplaType )) + return (isPerson) ?m.newPerson(): m.newResource() ; + if ( Category.TYPE.is( raplaType )) + return m.newCategory(); //will probably never happen + if ( User.TYPE.is( raplaType )) + return m.newUser(); + if ( Period.TYPE.is( raplaType )) + return m.newPeriod(); + + throw new RaplaException("Can't create Entity for " + raplaType + "!"); + } + + + /** guesses the DynamicType for the new object depending on the selected element. +
  • If the selected element is a DynamicType-Folder the DynamicType will be returned. +
  • If the selected element has a Classification the appropriatie DynamicType will be returned. +
  • else the first matching DynamicType for the passed classificationType will be returned. +
  • null if none of the above criterias matched. + */ + public DynamicType guessType() throws RaplaException { + DynamicType[] types = guessTypes(); + if ( types.length > 0) + { + return types[0]; + } + else + { + return null; + } + } + + public DynamicType[] guessTypes() throws RaplaException { + DynamicType dynamicType = null; + getLogger().debug("Guessing DynamicType from " + object); + if (object instanceof DynamicType) + dynamicType = (DynamicType) object; + + if (object instanceof Classifiable) { + Classification classification= ((Classifiable) object).getClassification(); + dynamicType = classification.getType(); + } + if (dynamicType != null) + { + return new DynamicType[] {dynamicType}; + } + String classificationType = null; + if ( Reservation.TYPE.is( raplaType )) { + classificationType = DynamicTypeAnnotations.VALUE_RESERVATION_CLASSIFICATION; + } else if ( Allocatable.TYPE.is( raplaType )) { + if ( isPerson ) { + classificationType = DynamicTypeAnnotations.VALUE_PERSON_CLASSIFICATION; + } else { + classificationType = DynamicTypeAnnotations.VALUE_RESOURCE_CLASSIFICATION; + } + } + DynamicType[] dynamicTypes = getQuery().getDynamicTypes( classificationType ); + return dynamicTypes; + + } + + protected void newEntity() throws RaplaException { + if ( Category.TYPE.is( raplaType )) { + getEditController().edit((Entity)object, parent); + } else { + Object obj = newEntity(raplaType); + if (obj instanceof Classifiable) { + DynamicType type = guessType(); + final Classification newClassification = type.newClassification(); + ((Classifiable)obj).setClassification(newClassification); + } + getEditController().edit((Entity)obj, parent); + } + } + + protected void edit() throws RaplaException { + getEditController().edit((Entity)object, parent); + } + + protected void delete() throws RaplaException { + if (object == null) + return; + Entity[] objects = new Entity[] { (Entity) object}; + DialogUI dlg = getInfoFactory().createDeleteDialog( objects, parent); + dlg.start(); + if (dlg.getSelectedIndex() != 0) + return; + getModification().removeObjects(objects); + } + + protected void deleteSelection() throws RaplaException { + if (objectList == null || objectList.size() == 0) + return; + DialogUI dlg = getInfoFactory().createDeleteDialog(objectList.toArray(), parent); + dlg.start(); + if (dlg.getSelectedIndex() != 0) + return; + getModification().removeObjects((Entity[])objectList.toArray( Entity.ENTITY_ARRAY)); + } + + public void setPerson(boolean b) { + isPerson = b; + } + +} + diff --git a/Rapla/src/org/rapla/gui/internal/action/ReservationAction.java b/Rapla/src/org/rapla/gui/internal/action/ReservationAction.java new file mode 100644 index 0000000..eb9d3ba --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/action/ReservationAction.java @@ -0,0 +1,92 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas, Bettina Lademann | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.action; + +import java.awt.Component; +import java.awt.Point; + +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.dynamictype.DynamicTypeAnnotations; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.ReservationWizard; +import org.rapla.plugin.RaplaExtensionPoints; + +public class ReservationAction extends RaplaObjectAction{ + ReservationWizard m_wizard; + public final static int EXCHANGE_ALLOCATABLE = 20; + public final static int NEW_WIZARD = 800; + public ReservationAction(RaplaContext sm,Component parent,Point p) throws RaplaException { + super(sm,parent,p); + raplaType = Reservation.TYPE; + } + + public ReservationAction setExchangeAllocatables(Object obj) { + this.type = EXCHANGE_ALLOCATABLE; + putValue(NAME, getString("exchange_allocatables")); + putValue(SMALL_ICON, getIcon("icon.edit")); + changeObject( obj ); + return this; + } + + public ReservationAction setNew(ReservationWizard wizard) { + super.setNew( Reservation.TYPE); + m_wizard = wizard; + if (m_wizard == null) { + m_wizard = (ReservationWizard) getService( RaplaExtensionPoints.RESERVATION_WIZARD_EXTENSION ); + } + if (m_wizard != null) + putValue(NAME, m_wizard.toString()); + return this; + } + + protected void edit() throws RaplaException { + getReservationController().edit( (Reservation) object ); + } + + + protected boolean canModifiy(Object object) { + if (super.canModify( object) ) { + return true; + } + if ( object instanceof Reservation ) { + return getQuery().canExchangeAllocatables( (Reservation) object) ; + } + return false; + } + + public DynamicType guessType() throws RaplaException { + DynamicType type = super.guessType(); + if ( type.getAnnotation( DynamicTypeAnnotations.KEY_CLASSIFICATION_TYPE).equals( DynamicTypeAnnotations.VALUE_RESERVATION_CLASSIFICATION)) { + return type; + } + return getQuery().getDynamicTypes(DynamicTypeAnnotations.VALUE_RESERVATION_CLASSIFICATION)[0]; + } + + + public void changeObject(Object object) { + super.changeObject(object); + if (type == NEW) { + setEnabled( m_wizard != null); + } + + } + + protected void newEntity() throws RaplaException { + m_wizard.start(parent, (CalendarModel)getService( CalendarModel.ROLE), guessType()); + } + + +} diff --git a/Rapla/src/org/rapla/gui/internal/action/RestartRaplaAction.java b/Rapla/src/org/rapla/gui/internal/action/RestartRaplaAction.java new file mode 100644 index 0000000..5506e60 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/action/RestartRaplaAction.java @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas, Bettina Lademann | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.action; +import java.awt.event.ActionEvent; + +import org.rapla.client.ClientService; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaAction; + + +public class RestartRaplaAction extends RaplaAction{ + public RestartRaplaAction(RaplaContext sm) throws RaplaException { + super(sm); + putValue(NAME,getString("restart_client")); + putValue(SMALL_ICON,getIcon("icon.restart")); + } + + public void actionPerformed(ActionEvent arg0) { + ((ClientService)getService(ClientService.ROLE)).restart(); + } + + +} diff --git a/Rapla/src/org/rapla/gui/internal/action/RestartServerAction.java b/Rapla/src/org/rapla/gui/internal/action/RestartServerAction.java new file mode 100644 index 0000000..9fe31d8 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/action/RestartServerAction.java @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas, Bettina Lademann | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.action; +import java.awt.event.ActionEvent; + +import javax.swing.SwingUtilities; + +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaAction; +import org.rapla.server.RestartServer; + + +public class RestartServerAction extends RaplaAction { + /** + * @param sm + * @throws RaplaException + */ + public RestartServerAction(RaplaContext sm) throws RaplaException { + super(sm); + putValue(NAME,getString("restart_server")); + putValue(SMALL_ICON,getIcon("icon.restart")); + } + + public void actionPerformed(ActionEvent arg0) { + SwingUtilities.invokeLater( new Runnable() { + public void run() { + try { + ((RestartServer)getService(RestartServer.class.getName())).restartServer(); + } catch (RaplaException ex) { + getLogger().error("Error restarting ", ex); + } + } + }); + } + + +} diff --git a/Rapla/src/org/rapla/gui/internal/action/ShowConflictsAction.java b/Rapla/src/org/rapla/gui/internal/action/ShowConflictsAction.java new file mode 100644 index 0000000..97e7c14 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/action/ShowConflictsAction.java @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas, Bettina Lademann | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.action; +import java.awt.event.ActionEvent; + +import javax.swing.JMenuItem; + +import org.rapla.entities.configuration.Preferences; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaAction; + +public class ShowConflictsAction extends RaplaAction { + + public static final String CONFIG_ENTRY = "org.rapla.showConflicts"; + + public ShowConflictsAction(RaplaContext sm) throws RaplaException { + super( sm ); + this.setEnabled( true); + putValue(NAME,getString("show_conflicts")); + //putValue(SMALL_ICON,getIcon("icon.unchecked")); + } + + public void actionPerformed(ActionEvent evt) { + + JMenuItem menu = (JMenuItem)evt.getSource(); + if(menu.isSelected()) { + menu.setSelected(false); + javax.swing.ToolTipManager.sharedInstance().setEnabled(false); + menu.setIcon(getIcon("icon.unchecked")); + } + else { + menu.setSelected(true); + javax.swing.ToolTipManager.sharedInstance().setEnabled(true); + menu.setIcon(getIcon("icon.checked")); + } + + if ( isModifyPreferencesAllowed()) + { + try { + Preferences prefs = this.newEditablePreferences(); + prefs.putEntry( CONFIG_ENTRY, ""+ menu.isSelected()); + getModification().store( prefs); + } catch (Exception ex) { + showException( ex, null ); + } + } + } +} diff --git a/Rapla/src/org/rapla/gui/internal/action/ToolTipAction.java b/Rapla/src/org/rapla/gui/internal/action/ToolTipAction.java new file mode 100644 index 0000000..fbef00f --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/action/ToolTipAction.java @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas, Bettina Lademann | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.action; +import java.awt.event.ActionEvent; + +import javax.swing.JMenuItem; + +import org.rapla.entities.configuration.Preferences; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaAction; + +public class ToolTipAction extends RaplaAction { + + public static final String CONFIG_ENTRY = "org.rapla.showTooltips"; + + public ToolTipAction(RaplaContext sm) throws RaplaException { + super( sm ); + this.setEnabled( true); + putValue(NAME,getString("show_tips")); + //putValue(SMALL_ICON,getIcon("icon.unchecked")); + } + + public void actionPerformed(ActionEvent evt) { + + JMenuItem toolTip = (JMenuItem)evt.getSource(); + if(toolTip.isSelected()) { + toolTip.setSelected(false); + javax.swing.ToolTipManager.sharedInstance().setEnabled(false); + toolTip.setIcon(getIcon("icon.unchecked")); + } + else { + toolTip.setSelected(true); + javax.swing.ToolTipManager.sharedInstance().setEnabled(true); + toolTip.setIcon(getIcon("icon.checked")); + } + + if ( isModifyPreferencesAllowed()) + { + try { + Preferences prefs = this.newEditablePreferences(); + prefs.putEntry( CONFIG_ENTRY, ""+ toolTip.isSelected()); + getModification().store( prefs); + } catch (Exception ex) { + showException( ex, null ); + } + } + } +} diff --git a/Rapla/src/org/rapla/gui/internal/action/package.html b/Rapla/src/org/rapla/gui/internal/action/package.html new file mode 100644 index 0000000..6077158 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/action/package.html @@ -0,0 +1,6 @@ + +Actions can be reused with menus, popups or buttons. + + + + diff --git a/Rapla/src/org/rapla/gui/internal/action/user/PasswordChangeAction.java b/Rapla/src/org/rapla/gui/internal/action/user/PasswordChangeAction.java new file mode 100644 index 0000000..b9f741e --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/action/user/PasswordChangeAction.java @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas, Bettina Lademann | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.action.user; +import java.awt.Component; +import java.awt.Point; +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; + +import org.rapla.components.util.Tools; +import org.rapla.entities.User; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaAction; +import org.rapla.gui.toolkit.DialogUI; + + +public class PasswordChangeAction extends RaplaAction { + + Object object; + Component parent; + + public PasswordChangeAction(RaplaContext sm,Component parent,Point point) throws RaplaException { + super( sm); + this.parent = parent; + putValue(NAME, getI18n().format("change.format",getString("password"))); + } + + + public void changeObject(Object object) { + this.object = object; + update(); + } + + private void update() { + User user = null; + try { + user = getUser(); + setEnabled(object != null && (isAdmin() || user.equals(object))); + } catch (RaplaException ex) { + setEnabled(false); + return; + } + + } + + public void actionPerformed(ActionEvent evt) { + try { + if (object == null) + return; + changePassword((User) object, !getUser().isAdmin()); + } catch (RaplaException ex) { + showException(ex, this.parent); + } + } + + public void changePassword(User user,boolean showOld) throws RaplaException{ + new PasswordChangeActionA(user,showOld); + } + + class PasswordChangeActionA extends AbstractAction { + private static final long serialVersionUID = 1L; + PasswordChangeUI ui; + DialogUI dlg; + User user; + boolean showOld; + + PasswordChangeActionA(User user,boolean showOld) throws RaplaException{ + this.user = user; + this.showOld = showOld; + putValue(NAME, getString("change")); + ui = new PasswordChangeUI(getContext(),showOld); + dlg = DialogUI.create(getContext(),parent,true,ui.getComponent(),new String[] {getString("change"),getString("cancel")}); + dlg.setDefault(0); + dlg.setTitle(getI18n().format("change.format",getString("password"))); + dlg.getButton(0).setAction(this); + dlg.getButton(1).setIcon(getIcon("icon.cancel")); + dlg.start(); + } + + public void actionPerformed(ActionEvent evt) { + try { + char[] oldPassword = showOld ? ui.getOldPassword() : new char[0]; + char[] p1= ui.getNewPassword(); + char[] p2= ui.getPasswordVerification(); + if (!Tools.match(p1,p2)) + throw new RaplaException(getString("error.passwords_dont_match")); + getUserModule().changePassword(user , oldPassword, p1); + dlg.close(); + } catch (RaplaException ex) { + showException(ex,dlg); + } + } + } + + +} diff --git a/Rapla/src/org/rapla/gui/internal/action/user/PasswordChangeUI.java b/Rapla/src/org/rapla/gui/internal/action/user/PasswordChangeUI.java new file mode 100644 index 0000000..285c4c6 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/action/user/PasswordChangeUI.java @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.action.user; + +import java.awt.GridLayout; + +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JPasswordField; + +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.toolkit.RaplaWidget; + + +public class PasswordChangeUI extends RaplaGUIComponent + implements + RaplaWidget +{ + JPanel panel = new JPanel(); + GridLayout gridLayout1 = new GridLayout(); + // The Controller for this Dialog + + JLabel label1 = new JLabel(); + JLabel label2 = new JLabel(); + JLabel label3 = new JLabel(); + + JPasswordField tf1 = new JPasswordField(10); + JPasswordField tf2 = new JPasswordField(10); + JPasswordField tf3 = new JPasswordField(10); + + public PasswordChangeUI(RaplaContext sm) throws RaplaException{ + this(sm,true); + } + + public PasswordChangeUI(RaplaContext sm,boolean askForOldPassword) throws RaplaException{ + super( sm); + panel.setLayout(gridLayout1); + gridLayout1.setRows(askForOldPassword ? 3 : 2); + gridLayout1.setColumns(2); + gridLayout1.setHgap(10); + gridLayout1.setVgap(10); + if (askForOldPassword) { + panel.add(label1); + panel.add(tf1); + } + + panel.add(label2); + panel.add(tf2); + panel.add(label3); + panel.add(tf3); + label1.setText(getString("old_password") + ":"); + label2.setText(getString("new_password") + ":"); + label3.setText(getString("password_verification") + ":"); + } + + public JComponent getComponent() { + return panel; + } + + public char[] getOldPassword() { + return tf1.getPassword(); + } + + public char[] getNewPassword() { + return tf2.getPassword(); + } + + public char[] getPasswordVerification() { + return tf3.getPassword(); + } +} + + + + + + + + + diff --git a/Rapla/src/org/rapla/gui/internal/action/user/UserAction.java b/Rapla/src/org/rapla/gui/internal/action/user/UserAction.java new file mode 100644 index 0000000..448a55f --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/action/user/UserAction.java @@ -0,0 +1,111 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas, Bettina Lademann | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.action.user; +import java.awt.Component; +import java.awt.Point; +import java.awt.event.ActionEvent; + +import org.rapla.client.ClientService; +import org.rapla.entities.User; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.EditComponent; +import org.rapla.gui.RaplaAction; +import org.rapla.gui.internal.edit.EditDialog; + + +public class UserAction extends RaplaAction { + Object object; + Component parent; + public final int NEW = 1; + public final int SWITCH_TO_USER = 3; + int type = NEW; + + public UserAction(RaplaContext sm,Component parent,Point point) throws RaplaException { + super( sm); + this.parent = parent; + + } + + public UserAction setNew() { + type = NEW; + putValue(NAME, getString("user")); + putValue(SMALL_ICON, getIcon("icon.new")); + update(); + return this; + } + + public UserAction setSwitchToUser() { + type = SWITCH_TO_USER; + if (getUserModule().canSwitchBack()) { + putValue(NAME, getString("switch_back")); + } else { + putValue(NAME, getString("switch_to")); + } + return this; + } + + public void changeObject(Object object) { + this.object = object; + update(); + } + + private void update() { + User user = null; + try { + user = getUser(); + if (type == NEW) { + setEnabled(isAdmin()); + } else if (type == SWITCH_TO_USER) { + setEnabled(getUserModule().canSwitchBack() || + (object != null && isAdmin() && !user.equals(object ))); + } + } catch (RaplaException ex) { + setEnabled(false); + return; + } + + } + + public void actionPerformed(ActionEvent evt) { + try { + if (type == SWITCH_TO_USER) { + if (getUserModule().canSwitchBack()) { + getUserModule().switchTo(null); + ((ClientService)getService( ClientService.ROLE)).restartGUI(); + putValue(NAME, getString("switch_to")); + } else if ( object != null ){ + getUserModule().switchTo((User) object); + ((ClientService)getService( ClientService.ROLE)).restartGUI(); + putValue(NAME, getString("switch_back")); + } + } else if (type == NEW) { + User newUser = (User) getModification().newUser(); + EditComponent ui = getEditController().createUI( newUser); + EditDialog gui = new EditDialog(getContext(),ui); + if (gui.start( newUser ,getString("user"), parent) == 0 + && getUserModule().canChangePassword() ) + changePassword(newUser,false); + object = newUser; + } + } catch (RaplaException ex) { + showException(ex, this.parent); + } + } + + public void changePassword(User user,boolean showOld) throws RaplaException{ + new PasswordChangeAction(getContext(),parent, null).changePassword( user, showOld); + } + + +} diff --git a/Rapla/src/org/rapla/gui/internal/common/CalendarAction.java b/Rapla/src/org/rapla/gui/internal/common/CalendarAction.java new file mode 100644 index 0000000..2c7f988 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/common/CalendarAction.java @@ -0,0 +1,83 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas, Bettina Lademann | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.common; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.util.Date; +import java.util.List; + +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.RaplaAction; +import org.rapla.gui.toolkit.DisposingTool; +import org.rapla.gui.toolkit.RaplaFrame; + + +public class CalendarAction extends RaplaAction { + CalendarSelectionModel model; + List objects; + Component parent; + Date start; + + public CalendarAction(RaplaContext sm,Component parent,CalendarModel selectionModel) throws RaplaException { + super( sm); + this.model = (CalendarSelectionModel)selectionModel.clone(); + this.parent = parent; + putValue(NAME,getString("calendar")); + putValue(SMALL_ICON,getIcon("icon.calendar")); + } + + + public void changeObjects(List objects) { + this.objects = objects; + + } + + public void setStart(Date start) { + this.start = start; + } + + public void actionPerformed(ActionEvent evt) { + try { + RaplaFrame frame = new RaplaFrame(getContext()); + Dimension dimension = java.awt.Toolkit.getDefaultToolkit().getScreenSize(); + frame.setSize(new Dimension( + Math.min(dimension.width,700) + ,Math.min(dimension.height-10,630) + ) + ); + if (start != null) + model.setSelectedDate(start); + if (objects != null && objects.size() > 0) + model.setSelectedObjects( objects ); + + if ( model.getViewId( ).equals("table")) { + model.setViewId("week"); + } + frame.setTitle("Rapla " + getString("calendar")); + + MultiCalendarView cal = new MultiCalendarView(getContext(),model, false ); + frame.setContentPane(cal.getComponent()); + frame.addWindowListener(new DisposingTool(cal)); + boolean packFrame = false; + frame.place( true, packFrame ); + frame.setVisible(true); + cal.getSelectedCalendar().scrollToStart(); + } catch (Exception ex) { + showException(ex, parent); + } + } +} + diff --git a/Rapla/src/org/rapla/gui/internal/common/CalendarModelImpl.java b/Rapla/src/org/rapla/gui/internal/common/CalendarModelImpl.java new file mode 100644 index 0000000..162be8d --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/common/CalendarModelImpl.java @@ -0,0 +1,831 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.gui.internal.common; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +import org.rapla.components.util.Assert; +import org.rapla.components.util.DateTools; +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.entities.EntityNotFoundException; +import org.rapla.entities.Named; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.RaplaType; +import org.rapla.entities.User; +import org.rapla.entities.configuration.CalendarModelConfiguration; +import org.rapla.entities.configuration.Preferences; +import org.rapla.entities.configuration.RaplaMap; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.AppointmentBlock; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.Classifiable; +import org.rapla.entities.dynamictype.Classification; +import org.rapla.entities.dynamictype.ClassificationFilter; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.dynamictype.DynamicTypeAnnotations; +import org.rapla.facade.ClientFacade; +import org.rapla.facade.Conflict; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.framework.RaplaLocale; +import org.rapla.plugin.abstractcalendar.AbstractHTMLCalendarPage; +import org.rapla.plugin.autoexport.AutoExportPlugin; +import org.rapla.plugin.export2ical.Export2iCalPlugin; +import org.rapla.plugin.weekview.WeekViewFactory; + +public class CalendarModelImpl implements CalendarSelectionModel +{ + + Date startDate; + Date endDate; + Date selectedDate; + List selectedObjects = new ArrayList(); + String title; + int columnSize = 100; + ClientFacade m_facade; + String selectedView; + I18nBundle i18n; + RaplaContext context; + RaplaLocale raplaLocale; + User user; + Map optionMap = new HashMap(); + + boolean defaultEventTypes = true; + boolean defaultResourceTypes = true; + + Map reservationFilter = new LinkedHashMap(); + Map allocatableFilter = new LinkedHashMap(); + + public CalendarModelImpl(RaplaContext sm, User user) throws RaplaException { + this.context = sm; + this.raplaLocale = (RaplaLocale) sm.lookup(RaplaLocale.ROLE); + i18n = (I18nBundle)sm.lookup(I18nBundle.ROLE + "/org.rapla.RaplaResources"); + m_facade = (ClientFacade) sm.lookup(ClientFacade.ROLE); + if ( m_facade.isSessionActive()) { + user = m_facade.getUser(); + } + setSelectedDate( m_facade.today()); + DynamicType[] types = m_facade.getDynamicTypes( DynamicTypeAnnotations.VALUE_RESOURCE_CLASSIFICATION); + if ( types.length == 0 ) { + types = m_facade.getDynamicTypes( DynamicTypeAnnotations.VALUE_PERSON_CLASSIFICATION); + } + setSelectedObjects( Collections.singletonList( types[0]) ); + setViewId( WeekViewFactory.WEEK_VIEW); + this.user = user; + if ( user != null && !user.isAdmin()) { + selectUser( user ); + } + optionMap.put( AbstractHTMLCalendarPage.SAVE_SELECTED_DATE, "false"); + resetExports(); + } + + public void resetExports() + { + setTitle(null); + setOption( AbstractHTMLCalendarPage.SHOW_NAVIGATION_ENTRY, "true"); + setOption(AutoExportPlugin.HTML_EXPORT, "false"); + setOption(Export2iCalPlugin.ICAL_EXPORT, "false"); + } + + + private boolean setConfiguration(CalendarModelConfiguration config, final Map alternativOptions) throws RaplaException { + selectedObjects = new ArrayList(); + if ( config == null) + { + defaultEventTypes = true; + defaultResourceTypes = true; + allocatableFilter.clear(); + reservationFilter.clear(); + return true; + } + else + { + defaultEventTypes = config.isDefaultEventTypes(); + defaultResourceTypes = config.isDefaultResourceTypes(); + } + boolean couldResolveAllEntities = true; + + // get filter + title = config.getTitle(); + selectedView = config.getView(); + //selectedObjects + optionMap = new HashMap(); + if ( config.getOptionMap() != null) + { + optionMap.putAll( config.getOptionMap()); + } + if (alternativOptions != null ) + { + optionMap.putAll( alternativOptions); + } + ClassificationFilter[] filter = config.getFilter(); + final String saveDate = (String)optionMap.get( AbstractHTMLCalendarPage.SAVE_SELECTED_DATE); + if ( config.getSelectedDate() != null && (saveDate == null || saveDate.equals("true"))) { + setSelectedDate( config.getSelectedDate() ); + } + else + { + setSelectedDate( m_facade.today()); + } + if ( config.getStartDate() != null) { + setStartDate( config.getStartDate() ); + } + if ( config.getEndDate() != null) { + setEndDate( config.getEndDate() ); + } + selectedObjects.addAll( config.getSelected()); + for ( ClassificationFilter f:filter) + { + final DynamicType type = f.getType(); + final String annotation = type.getAnnotation(DynamicTypeAnnotations.KEY_CLASSIFICATION_TYPE); + boolean eventType = annotation != null &&annotation.equals( DynamicTypeAnnotations.VALUE_RESERVATION_CLASSIFICATION); + Map map = eventType ? reservationFilter : allocatableFilter; + map.put(type, f); + } + + return couldResolveAllEntities; + } + + public User getUser() { + return user; + } + + public CalendarModelConfiguration createConfiguration() throws RaplaException { + ClassificationFilter[] allocatableFilter = getAllocatableFilter(); + ClassificationFilter[] eventFilter = getReservationFilter(); + return createConfiguration(allocatableFilter, eventFilter); + } + + public CalendarModelConfiguration createConfiguration(ClassificationFilter[] allocatableFilter, ClassificationFilter[] eventFilter) throws RaplaException { + String viewName = selectedView; + for (Iterator it = selectedObjects.iterator();it.hasNext();) { + if ( it.next() instanceof Conflict) { + throw new RaplaException("Storing the conflict view is not possible with Rapla."); + } + } + + Set selected = new HashSet( selectedObjects); + final Date selectedDate = getSelectedDate(); + final Date startDate = getStartDate(); + final Date endDate = getEndDate(); + return m_facade.newRaplaCalendarModel( m_facade.newRaplaMap(selected), allocatableFilter,eventFilter, title, startDate, endDate, selectedDate, viewName, m_facade.newRaplaMap(optionMap)); + } + + public void setReservationFilter(ClassificationFilter[] array) { + reservationFilter.clear(); + try { + defaultEventTypes = createConfiguration(null,array).isDefaultEventTypes(); + } catch (RaplaException e) { + // DO Not set the types + } + for (ClassificationFilter entry: array) + { + final DynamicType type = entry.getType(); + reservationFilter.put( type, entry); + } + } + + public void setAllocatableFilter(ClassificationFilter[] array) { + allocatableFilter.clear(); + try { + defaultResourceTypes = createConfiguration(array,null).isDefaultResourceTypes(); + } catch (RaplaException e) { + // DO Not set the types + } + for (ClassificationFilter entry: array) + { + final DynamicType type = entry.getType(); + allocatableFilter.put( type, entry); + } + } + + /* (non-Javadoc) + * @see org.rapla.calendarview.CalendarModel#getSelectedDate() + */ + public Date getSelectedDate() { + return selectedDate; + } + + /* (non-Javadoc) + * @see org.rapla.calendarview.CalendarModel#setSelectedDate(java.util.Date) + */ + public void setSelectedDate(Date date) { + if ( date == null) + throw new IllegalStateException("Date can't be null"); + this.selectedDate = date; + } + + /* (non-Javadoc) + * @see org.rapla.calendarview.CalendarModel#getStartDate() + */ + public Date getStartDate() { + return startDate; + } + + /* (non-Javadoc) + * @see org.rapla.calendarview.CalendarModel#setStartDate(java.util.Date) + */ + public void setStartDate(Date date) { + this.startDate = date; + } + + /* (non-Javadoc) + * @see org.rapla.calendarview.CalendarModel#getEndDate() + */ + public Date getEndDate() { + return endDate; + } + + /* (non-Javadoc) + * @see org.rapla.calendarview.CalendarModel#setEndDate(java.util.Date) + */ + public void setEndDate(Date date) { + this.endDate = date; + } + + /* (non-Javadoc) + * @see org.rapla.calendarview.CalendarModel#getTitle() + */ + public String getTitle() { + return title; + } + + /* (non-Javadoc) + * @see org.rapla.calendarview.CalendarModel#setTitle(java.lang.String) + */ + public void setTitle(String title) { + this.title = title; + } + + /* (non-Javadoc) + * @see org.rapla.calendarview.CalendarModel#setView(java.lang.String) + */ + public void setViewId(String view) { + this.selectedView = view; + } + + /* (non-Javadoc) + * @see org.rapla.calendarview.CalendarModel#getView() + */ + public String getViewId() { + return this.selectedView; + } + + /* (non-Javadoc) + * @see org.rapla.calendarview.CalendarModel#getNonEmptyTitle() + */ + public String getNonEmptyTitle() { + if (getTitle() != null && getTitle().trim().length()>0) + return getTitle(); + + + String types = ""; + /* + String dateString = getRaplaLocale().formatDate(getSelectedDate()); + if ( isListingAllocatables()) { + try { + Collection list = getSelectedObjectsAndChildren(); + if (list.size() == 1) { + Object obj = list.iterator().next(); + if (!( obj instanceof DynamicType)) + { + types = getI18n().format("allocation_view",getName( obj ),dateString); + } + } + + } catch (RaplaException ex) { + } + if ( types == null ) + types = getI18n().format("allocation_view", getI18n().getString("resources_persons")); + } else if ( isListingReservations()) { + types = getI18n().getString("reservations"); + } else { + types = "unknown"; + } + */ + + return types; + } + + public String getName(Object object) { + if (object == null) + return ""; + if (object instanceof Named) { + String name = ((Named) object).getName(getI18n().getLocale()); + return (name != null) ? name : ""; + } + return object.toString(); + } + + public int getSize() { + return columnSize; + } + + public void setColumnSize(int columnSize) { + this.columnSize = columnSize; + } + + + private Collection getFilteredAllocatables() throws RaplaException { + List list = new ArrayList(); + Allocatable[] all = m_facade.getAllocatables(); + for (int i=0;i filter ; + if ( isDefaultEventTypes()) + { + filter = new ArrayList(); + for (DynamicType type :m_facade.getDynamicTypes( DynamicTypeAnnotations.VALUE_RESERVATION_CLASSIFICATION)) + { + filter.add( type.newClassificationFilter()); + } + } + else + { + filter = reservationFilter.values(); + } + return filter.toArray(ClassificationFilter.CLASSIFICATIONFILTER_ARRAY); + } + + /* (non-Javadoc) + * @see org.rapla.calendarview.CalendarModel#getAllocatableFilter() + */ + public ClassificationFilter[] getAllocatableFilter() throws RaplaException { + Collection filter ; + if ( isDefaultResourceTypes()) + { + filter = new ArrayList(); + for (DynamicType type :m_facade.getDynamicTypes( DynamicTypeAnnotations.VALUE_RESOURCE_CLASSIFICATION)) + { + filter.add( type.newClassificationFilter()); + } + for (DynamicType type :m_facade.getDynamicTypes( DynamicTypeAnnotations.VALUE_PERSON_CLASSIFICATION)) + { + filter.add( type.newClassificationFilter()); + } + + } + else + { + filter = allocatableFilter.values(); + } + return filter.toArray(ClassificationFilter.CLASSIFICATIONFILTER_ARRAY); + } + + public Object clone() { + CalendarModelImpl clone; + try + { + clone = (CalendarModelImpl )super.clone(); + } + catch ( CloneNotSupportedException e ) + { + throw new IllegalStateException( e.getMessage() ); + } + return clone; + } + + /* (non-Javadoc) + * @see org.rapla.calendarview.CalendarModel#getReservations(java.util.Date, java.util.Date) + */ + public Reservation[] getReservations() throws RaplaException { + return getReservations( getStartDate(), getEndDate() ); + } + + public Reservation[] getReservations(Date startDate, Date endDate) throws RaplaException { + return (Reservation[]) getReservationsAsList( startDate, endDate ).toArray( Reservation.RESERVATION_ARRAY); + } + + private boolean hasAllocatedOne( Reservation reservation,Allocatable[] allocatables ) { + for (int j=0;j 0) { + return selectedReservations; + } + ClassificationFilter[] reservationFilter2 = getReservationFilter(); + if ( isDefaultEventTypes()) + { + reservationFilter2 = null; + } + Reservation[] reservations =m_facade.getReservations((User) null, start, end,reservationFilter2 ); + return Arrays.asList( reservations ); + + } + + private List getReservationsAsList(Date start, Date end) throws RaplaException { + + List reservations = new ArrayList(getRestrictedReservations( start, end)); +// Set reservationTypes = getSelectedTypes( DynamicTypeAnnotations.VALUE_RESERVATION_CLASSIFICATION); +// if ( reservationTypes.size() > 0 ) { +// restrictReservationTypes( reservations, reservationTypes); +// } + Allocatable[] allocatables = getSelectedAllocatables(); + if ( allocatables.length> 0) { + restrict( reservations, allocatables ); + } + User[] users = getSelectedUsers(); + if ( users.length> 0 || isOnlyCurrentUserSelected()) { + restrict( reservations, users ); + } + Conflict[] conflicts = getSelectedConflicts(); + if ( conflicts.length > 0) { + restrict( reservations, conflicts ); + } + return reservations; + } + + /* (non-Javadoc) + * @see org.rapla.calendarview.CalendarModel#getAllocatables() + */ + public Allocatable[] getSelectedAllocatables() throws RaplaException { + Collection result = new HashSet(); + Iterator it = getSelectedObjectsAndChildren().iterator(); + while (it.hasNext()) { + RaplaObject object = (RaplaObject) it.next(); + if ( object.getRaplaType().equals( Allocatable.TYPE )) { + result.add( object ); + } + if ( object.getRaplaType().equals( Conflict.TYPE )) { + result.add( ((Conflict)object).getAllocatable() ); + } + } + return (Allocatable[]) result.toArray(Allocatable.ALLOCATABLE_ARRAY); + } + + public User[] getSelectedUsers() { + User currentUser = getUser(); + if ( currentUser != null && !m_facade.canReadReservationsFromOthers( currentUser)) + { + return new User[] {currentUser}; + } + return (User[]) getSelected(User.TYPE).toArray(User.USER_ARRAY); + } + + public Conflict[] getSelectedConflicts() throws RaplaException { + return (Conflict[]) getSelected(Conflict.TYPE).toArray(Conflict.CONFLICT_ARRAY); + } + + public Set getSelectedTypes(String classificationType) throws RaplaException { + Set result = new HashSet(); + Iterator it = getSelectedObjectsAndChildren().iterator(); + while (it.hasNext()) { + RaplaObject object = (RaplaObject) it.next(); + if ( object.getRaplaType().equals( DynamicType.TYPE )) { + if (classificationType == null || (( DynamicType) object).getAnnotation( DynamicTypeAnnotations.KEY_CLASSIFICATION_TYPE).equals( classificationType)) + { + result.add( object ); + } + } + } + return result; + } + + public Set getSelected(RaplaType type) { + Set result = new HashSet(); + Iterator it = getSelectedObjects().iterator(); + while (it.hasNext()) { + RaplaObject object = (RaplaObject) it.next(); + if ( object.getRaplaType().equals( type )) { + result.add( object ); + } + } + return result; + } + + public Date[] getConflictDates() throws RaplaException { + ArrayList list = new ArrayList(); + Conflict[] conflicts = getSelectedConflicts(); + for ( int i=0;i listA = new ArrayList(); + a1.createBlocks(maxStart, minEnd, listA ); + List listB = new ArrayList(); + a2.createBlocks( maxStart, minEnd, listB ); + for ( int i=0, j=0;i s2) + j++; + else + i++; + } + return null; + } + + protected I18nBundle getI18n() { + return i18n; + } + + protected RaplaLocale getRaplaLocale() { + return raplaLocale; + } + + public boolean isOnlyCurrentUserSelected() { + User[] users = getSelectedUsers(); + User currentUser = getUser(); + + + + return ( users.length == 1 && users[0].equals( currentUser)); + } + + public void selectUser(User user) { + for (Iterator it = selectedObjects.iterator();it.hasNext();) { + RaplaObject obj =(RaplaObject) it.next(); + if (obj.getRaplaType().equals( User.TYPE) ) { + it.remove(); + } + } + if ( user != null) + { + selectedObjects.add( user ); + } + } + + public Object getOption( String name ) + { + return optionMap.get( name ); + } + + public void setOption( String name, RaplaObject object ) + { + optionMap.put( name, object); + } + + public void setOption( String name, String string ) + { + optionMap.put( name, string); + } + + public boolean isDefaultEventTypes() + { + return defaultEventTypes; + } + + public boolean isDefaultResourceTypes() + { + return defaultResourceTypes; + } + + public void save(final String filename) throws RaplaException, + EntityNotFoundException { + final CalendarModelConfiguration conf = createConfiguration(); + + Preferences clone = (Preferences) m_facade.edit(m_facade.getPreferences(user)); + + if ( filename.equals(i18n.getString("default"))) + { + clone.putEntry( CalendarModelConfiguration.CONFIG_ENTRY, conf); + } + else + { + Map exportMap= ((RaplaMap)clone.getEntry(AutoExportPlugin.PLUGIN_ENTRY)); + Map newMap; + if ( exportMap == null) + newMap = new TreeMap(); + else + newMap = new TreeMap( exportMap); + newMap.put(filename, conf); + clone.putEntry( AutoExportPlugin.PLUGIN_ENTRY, m_facade.newRaplaMap( newMap )); + } + m_facade.store(clone); + } + + public void load(final String filename) + throws RaplaException, EntityNotFoundException { + final CalendarModelConfiguration modelConfig; + final Preferences preferences = m_facade.getPreferences(user); + final boolean isDefault = filename == null || filename.equals(i18n.getString("default")); + if ( isDefault ) + { + modelConfig = (CalendarModelConfiguration)preferences.getEntry(CalendarModelConfiguration.CONFIG_ENTRY); + + } + else + { + Map exportMap= ((RaplaMap)preferences.getEntry(AutoExportPlugin.PLUGIN_ENTRY)); + modelConfig = (CalendarModelConfiguration)exportMap.get(filename); + + } + if ( modelConfig == null && filename != null && !isDefault) + { + throw new EntityNotFoundException("Saved calendar with name '" + filename + "' not found."); + } + else + { + Map alternativeOptions = new HashMap(); + if (modelConfig != null && modelConfig.getOptionMap() != null) + { + // All old default calendars have no selected date + if (isDefault && (modelConfig.getOptionMap().get( AbstractHTMLCalendarPage.SAVE_SELECTED_DATE) == null)) + { + alternativeOptions.put(AbstractHTMLCalendarPage.SAVE_SELECTED_DATE , "false"); + } + // All old calendars are exported + if ( !isDefault && modelConfig.getOptionMap().get(AutoExportPlugin.HTML_EXPORT) == null) + { + alternativeOptions.put(AutoExportPlugin.HTML_EXPORT,"true"); + } + } + setConfiguration(modelConfig, alternativeOptions); + } + } +} + + diff --git a/Rapla/src/org/rapla/gui/internal/common/CalendarSelectionModel.java b/Rapla/src/org/rapla/gui/internal/common/CalendarSelectionModel.java new file mode 100644 index 0000000..65fbb87 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/common/CalendarSelectionModel.java @@ -0,0 +1,41 @@ +package org.rapla.gui.internal.common; +import java.util.Collection; +import org.rapla.entities.EntityNotFoundException; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.User; +import org.rapla.entities.configuration.RaplaConfiguration; +import org.rapla.entities.dynamictype.ClassificationFilter; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarModel; + +public interface CalendarSelectionModel extends CalendarModel, ClassifiableFilter{ + public String ROLE = CalendarSelectionModel.class.getName(); + RaplaConfiguration ALLOCATABLES_ROOT = new RaplaConfiguration("rootnode", "allocatables"); + + String getTitle(); + + void setTitle(String title); + + void setColumnSize(int columnSize); + + void setViewId(String viewId); + + String getViewId(); + + void setSelectedObjects(Collection selectedObjects); + + void setOption(String name, RaplaObject object ); + void setOption( String name, String string ); + void selectUser( User user ); + + /** If show only own reservations is selected. Thats if the current user is selected with select User*/ + boolean isOnlyCurrentUserSelected(); + + void setReservationFilter(ClassificationFilter[] array); + + void setAllocatableFilter(ClassificationFilter[] filters); + + public void resetExports(); + public void save(final String filename) throws RaplaException; + public void load(final String filename) throws RaplaException, EntityNotFoundException; +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/gui/internal/common/ClassifiableFilter.java b/Rapla/src/org/rapla/gui/internal/common/ClassifiableFilter.java new file mode 100644 index 0000000..ded4158 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/common/ClassifiableFilter.java @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.common; + +import org.rapla.entities.dynamictype.ClassificationFilter; +import org.rapla.framework.RaplaException; + + +public interface ClassifiableFilter +{ + ClassificationFilter[] getReservationFilter() throws RaplaException ; + ClassificationFilter[] getAllocatableFilter() throws RaplaException ; + boolean isDefaultEventTypes(); + boolean isDefaultResourceTypes(); +} + diff --git a/Rapla/src/org/rapla/gui/internal/common/InternMenus.java b/Rapla/src/org/rapla/gui/internal/common/InternMenus.java new file mode 100644 index 0000000..157cda1 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/common/InternMenus.java @@ -0,0 +1,18 @@ +package org.rapla.gui.internal.common; + +public interface InternMenus +{ + + public static final String FILE_MENU_ROLE = "org.rapla.gui.SystemMenu"; + public static final String EXTRA_MENU_ROLE = "org.rapla.gui.ExtraMenu"; + public static final String VIEW_MENU_ROLE ="org.rapla.gui.ViewMenu"; + public static final String EXPORT_MENU_ROLE ="org.rapla.gui.ExportMenu"; + public static final String ADMIN_MENU_ROLE ="org.rapla.gui.AdminMenu"; + public static final String EDIT_MENU_ROLE = "org.rapla.gui.EditMenu"; + public static final String IMPORT_MENU_ROLE ="org.rapla.gui.ImportMenu"; + public static final String NEW_MENU_ROLE ="org.rapla.gui.NewMenu"; + public static final String MENU_BAR = "org.rapla.gui.MenuBar"; + public static final String CALENDAR_SETTINGS = "org.rapla.gui.CalendarSettings"; + + +} diff --git a/Rapla/src/org/rapla/gui/internal/common/MultiCalendarView.java b/Rapla/src/org/rapla/gui/internal/common/MultiCalendarView.java new file mode 100644 index 0000000..25fda4f --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/common/MultiCalendarView.java @@ -0,0 +1,399 @@ + +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.gui.internal.common; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; +import javax.swing.DefaultListCellRenderer; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.Popup; +import javax.swing.PopupFactory; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.rapla.components.calendar.RaplaArrowButton; +import org.rapla.components.layout.TableLayout; +import org.rapla.components.util.Tools; +import org.rapla.entities.dynamictype.ClassificationFilter; +import org.rapla.entities.dynamictype.DynamicTypeAnnotations; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.SwingCalendarView; +import org.rapla.gui.ViewFactory; +import org.rapla.gui.internal.edit.ClassifiableFilterEdit; +import org.rapla.gui.toolkit.IdentifiableMenuEntry; +import org.rapla.gui.toolkit.RaplaMenu; +import org.rapla.gui.toolkit.RaplaMenuItem; +import org.rapla.gui.toolkit.RaplaWidget; +import org.rapla.plugin.RaplaExtensionPoints; + + +public class MultiCalendarView extends RaplaGUIComponent + implements + RaplaWidget +{ + + private final JPanel page = new JPanel(); + private final JPanel header = new JPanel(); + Map viewMenuItems = new HashMap(); + JComboBox viewChooser; + + // Default view, when no plugin defined + private SwingCalendarView currentView = new SwingCalendarView() { + JLabel noViewDefined = new JLabel("No view defined"); + + public JComponent getDateSelection() + { + return null; + } + + public void scrollToStart() + { + } + + public JComponent getComponent() + { + return noViewDefined; + } + + public void update( ) throws RaplaException + { + } + + }; + + private final CalendarSelectionModel model; + final Map map; + /** renderer for weekdays in month-view */ + boolean editable = true; + boolean listenersEnabled = true; + + public MultiCalendarView(RaplaContext context,CalendarSelectionModel model) throws RaplaException { + this( context, model, true); + } + public MultiCalendarView(RaplaContext context,CalendarSelectionModel model, boolean editable) throws RaplaException { + super( context); + this.editable = editable; + map = getContainer().lookupServicesFor(RaplaExtensionPoints.CALENDAR_VIEW_EXTENSION); + this.model = model; + String[] ids = getIds(); + { + ViewFactory factory = findFactory( model.getViewId()); + if ( factory == null) + { + if ( ids.length != 0 ) { + String firstId = ids[0]; + model.setViewId( firstId ); + factory = findFactory( firstId ); + } + } + } + RaplaMenu view = (RaplaMenu) getContext().lookup( InternMenus.VIEW_MENU_ROLE); + if ( !view.hasId( "views") ) + { + addMenu( model, ids, view ); + } + + addTypeChooser( ids ); + header.setLayout(new BorderLayout()); + header.add( viewChooser, BorderLayout.CENTER); + FilterEdit filter =new FilterEdit(context,model); + final JPanel filterContainer = new JPanel(); + filterContainer.setLayout( new BorderLayout()); + filterContainer.add(filter.getButton(), BorderLayout.WEST); + header.add( filterContainer, BorderLayout.SOUTH); + page.setLayout(new TableLayout( new double[][]{ + {TableLayout.PREFERRED, TableLayout.FILL} + ,{TableLayout.PREFERRED, TableLayout.FILL}})); + page.add( header, "0,0,f,f"); + page.setBackground( Color.white ); + update(); + } + private void addTypeChooser( String[] ids ) + { + viewChooser = new JComboBox( ids); + viewChooser.setVisible( viewChooser.getModel().getSize() > 0); + viewChooser.setSelectedItem( getModel().getViewId() ); + viewChooser.addActionListener( new ActionListener() { + public void actionPerformed(ActionEvent evt) { + if ( !listenersEnabled ) + return; + String viewId = (String) ((JComboBox)evt.getSource()).getSelectedItem(); + try { + selectView( viewId ); + } catch (RaplaException ex) { + showException(ex, page); + } + } + } + ); + viewChooser.setRenderer( new DefaultListCellRenderer() { + private static final long serialVersionUID = 1L; + + public Component getListCellRendererComponent(JList arg0, Object selectedItem, int index, boolean arg3, boolean arg4) { + super.getListCellRendererComponent( arg0, selectedItem, index, arg3, arg4); + if ( selectedItem == null) { + setIcon( null ); + } else { + ViewFactory factory = findFactory( (String)selectedItem); + setText( factory.getName() ); + setIcon( factory.getIcon()); + } + return this; + } + }); + } + + public class FilterEdit extends RaplaGUIComponent + { + protected RaplaArrowButton filterButton; + Popup popup; + ClassifiableFilterEdit ui; + + public FilterEdit(final RaplaContext context,final CalendarSelectionModel model) throws RaplaException + { + super(context); + filterButton = new RaplaArrowButton('v'); + filterButton.setText(getString("filter")); + filterButton.setSize(80,18); + filterButton.addActionListener( new ActionListener() + { + public void actionPerformed(ActionEvent e) { + + if ( popup != null) + { + popup.hide(); + popup= null; + filterButton.setChar('v'); + return; + } + boolean isResourceOnly = false; + try { + if ( ui != null) + { + ui.removeChangeListener( listener); + } + ui = new ClassifiableFilterEdit( context, isResourceOnly); + ui.addChangeListener(listener); + ui.setTypes( getQuery().getDynamicTypes( DynamicTypeAnnotations.VALUE_RESERVATION_CLASSIFICATION )); + ui.mapFrom(model); + final Point locationOnScreen = filterButton.getLocationOnScreen(); + final int y = locationOnScreen.y + 18; + final int x = locationOnScreen.x; + popup = PopupFactory.getSharedInstance().getPopup( filterButton, ui.getComponent(), x, y); + popup.show(); + filterButton.setChar('^'); + } catch (Exception ex) { + showException(ex, getComponent()); + } + } + + }); + + } + + private ChangeListener listener = new ChangeListener() { + + public void stateChanged(ChangeEvent e) { + try { + final ClassificationFilter[] filters = ui.getFilters(); + model.setReservationFilter( filters ); + update(); + } catch (Exception ex) { + showException(ex, getComponent()); + } + } + }; + public JComponent getButton() + { + return filterButton; + } + + } + private void addMenu( CalendarSelectionModel model, String[] ids, RaplaMenu view ) + { + RaplaMenu viewMenu = new RaplaMenu("views"); + viewMenu.setText(getString("show_as")); + view.insertBeforeId( viewMenu, "show_tips"); + ButtonGroup group = new ButtonGroup(); + for (int i=0;i 0) { + for ( Iterator it = viewMenuItems.values().iterator();it.hasNext();) + { + RaplaMenuItem item = (RaplaMenuItem) it.next(); + if ( item.isSelected() ) + { + item.setIcon( getIcon("icon.empty")); + } + + } + RaplaMenuItem item = (RaplaMenuItem)viewMenuItems.get( viewId ); + item.setSelected( true ); + item.setIcon( getIcon("icon.radio")); + } + viewChooser.setSelectedItem( viewId ); + } finally { + listenersEnabled = true; + } + } + + private String[] getIds() { + List factoryList = new ArrayList(map.values()); + Collections.sort( factoryList, new Comparator() { + public int compare( Object arg0, Object arg1 ) + { + ViewFactory f1 = (ViewFactory ) arg0; + ViewFactory f2 = (ViewFactory ) arg1; + return f1.getMenuSortKey().compareTo( f2.getMenuSortKey() ); + } + }); + List list = new ArrayList(); + for (Iterator it = factoryList.iterator();it.hasNext();) { + ViewFactory factory = (ViewFactory ) it.next(); + list.add(factory.getViewId()); + } + return (String[] )list.toArray( Tools.EMPTY_STRING_ARRAY); + } + + private CalendarSelectionModel getModel() { + return model; + } + + private final HashMap cache = new HashMap(); + + public void update() throws RaplaException { + try + { + listenersEnabled = false; + if ( currentView!= null ) { + currentView.getComponent().setVisible( false ); + if ( currentView.getDateSelection() != null) + currentView.getDateSelection().setVisible( false ); + } + + final String viewId = model.getViewId(); + ViewFactory factory = findFactory( viewId ); + viewChooser.setSelectedItem( viewId ); + if ( factory == null) { + return; + } + + SwingCalendarView cal = (SwingCalendarView)cache.get( factory.getViewId() ); + if ( cal == null ) { + cal = factory.createSwingView( getContext(), getModel(), editable); + cache.put( factory.getViewId(), cal); + if ( cal.getDateSelection() != null) + page.add( cal.getDateSelection(), "1,0,f,f" ); + page.add( cal.getComponent(), "0,1,1,1,f,f" ); + cal.getComponent().setBorder( BorderFactory.createEtchedBorder()); + page.invalidate(); + } else { + cal.getComponent().setVisible( true ); + if ( cal.getDateSelection() != null) + { + cal.getDateSelection().setVisible( true ); + } + cal.update( ); + } + + currentView = cal; + } + finally + { + listenersEnabled = true; + } + } + + public SwingCalendarView getSelectedCalendar() { + return currentView; + } + + public JComponent getComponent() { + return page; + } + + +} diff --git a/Rapla/src/org/rapla/gui/internal/common/NamedListCellRenderer.java b/Rapla/src/org/rapla/gui/internal/common/NamedListCellRenderer.java new file mode 100644 index 0000000..43340db --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/common/NamedListCellRenderer.java @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.common; + +import java.awt.Component; +import java.util.Locale; +import java.text.MessageFormat; + +import javax.swing.JList; +import javax.swing.DefaultListCellRenderer; + +import org.rapla.entities.Named; + +public class NamedListCellRenderer extends DefaultListCellRenderer { + private static final long serialVersionUID = 1L; + + Locale locale; + MessageFormat format = null; + + public NamedListCellRenderer(Locale locale) { + this.locale = locale; + } + + public NamedListCellRenderer(Locale locale,String formatString) { + this(locale); + this.format = new MessageFormat(formatString); + } + + + public Component getListCellRendererComponent(JList list, + Object value, + int index, + boolean isSelected, + boolean cellHasFocus) { + if (value instanceof Named) + value = ((Named) value).getName(locale); + if (format != null) + value = format.format(new Object[] {value}); + return super.getListCellRendererComponent(list,value,index,isSelected,cellHasFocus); + } +} diff --git a/Rapla/src/org/rapla/gui/internal/common/PeriodChooser.java b/Rapla/src/org/rapla/gui/internal/common/PeriodChooser.java new file mode 100644 index 0000000..850c9cc --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/common/PeriodChooser.java @@ -0,0 +1,241 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas, Bettina Lademann | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.common; + +import java.awt.Component; +import java.util.Date; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.DefaultListCellRenderer; +import javax.swing.JComboBox; +import javax.swing.JList; + +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.entities.domain.Period; +import org.rapla.facade.ClientFacade; +import org.rapla.facade.PeriodModel; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +public class PeriodChooser extends JComboBox + { + private static final long serialVersionUID = 1L; + + Date selectedDate = null; + Period selectedPeriod = null; + + public static int START_ONLY = 1; + public static int START_AND_END = 0; + public static int END_ONLY = -1; + + int visiblePeriods; + I18nBundle i18n; + PeriodModel periodModel; + private boolean listenersEnabled = true; + private boolean isWeekOfPeriodVisible = true; + + public PeriodChooser( RaplaContext sm) throws RaplaException { + this(sm,START_AND_END); + } + + public PeriodChooser(RaplaContext sm,int visiblePeriods) throws RaplaException { + // super(RaplaButton.SMALL); + this.visiblePeriods = visiblePeriods; + i18n = (I18nBundle) sm.lookup(I18nBundle.ROLE + "/org.rapla.RaplaResources"); + setPeriodModel(( (ClientFacade) sm.lookup(ClientFacade.ROLE) ).getPeriodModel()); + } + + + public void setPeriodModel(PeriodModel model) { + this.periodModel = model; + if ( periodModel != null ) { + try { + listenersEnabled = false; + this.setModel(new DefaultComboBoxModel(model.getAllPeriods())); + } finally { + listenersEnabled = true; + } + } + this.setRenderer(new PeriodListCellRenderer()); + update(); + } + + + private String formatPeriod(Period period) { + if ( !isWeekOfPeriodVisible) + { + return period.getName(); + } + + int lastWeek = period.getWeeks(); + int week = period.weekOf(selectedDate); + if (week != 1 && week >= lastWeek) { + return i18n.format( + "period.format.end" + ,period.getName() + ); + } else { + return i18n.format( + "period.format.week" + ,String.valueOf(period.weekOf(selectedDate)) + ,period.getName() + ); + } + } + + private String formatPeriodList(Period period) { + if (visiblePeriods == START_ONLY) { + return i18n.format( + "period.format.start" + ,period.getName() + ); + } else if (visiblePeriods == END_ONLY) { + return i18n.format( + "period.format.end" + ,period.getName() + ); + } else { + return period.getName(); + } + } + + public void setDate(Date date, Date endDate) { + try { + listenersEnabled = false; + + if (date != selectedDate) // Compute period only on date change + { + selectedPeriod = getPeriod(date, endDate); + } + + if ( selectedPeriod != null ) + { + selectedDate = date; + setSelectedItem(selectedPeriod); + } + else + { + selectedDate = date; + setSelectedItem(null); + } + repaint(); + revalidate(); + } finally { + listenersEnabled = true; + } + } + + public void setDate(Date date) { + setDate(date, null); + } + + private String getSelectionText() { + Period period = selectedPeriod; + if ( period != null ) { + return formatPeriod(period); + } else { + return i18n.getString("period.not_set"); + } + } + + public void setSelectedPeriod(Period period) { + selectedPeriod = period; // EXCO + listenersEnabled = false; + setSelectedItem(period); + listenersEnabled = true; + if (visiblePeriods == END_ONLY) { + selectedDate = period.getEnd(); + } else { + selectedDate = period.getStart(); + } + } + + + public Period getPeriod() { + return selectedPeriod; // getPeriod(selectedDate); + } + + private Period getPeriod(Date date, Date endDate) { + if (periodModel == null ) + return null; + if ( visiblePeriods == END_ONLY) { + return periodModel.getNearestPeriodForEndDate(date); + } else { + return periodModel.getNearestPeriodForStartDate(date, endDate); + } + } + + public Date getDate() { + return selectedDate; + } + + private void update() { + setVisible(periodModel != null && periodModel.getSize() > 0); + setDate(getDate()); + } + + protected void fireActionEvent() { + if ( !listenersEnabled ) + { + return ; + } + Period period = (Period) getSelectedItem(); + selectedPeriod = period; // EXCO + if (period != null) + { + if (visiblePeriods == END_ONLY) { + selectedDate = period.getEnd(); + } else { + selectedDate = period.getStart(); + } + } + super.fireActionEvent(); + } + + + class PeriodListCellRenderer extends DefaultListCellRenderer { + private static final long serialVersionUID = 1L; + + public Component getListCellRendererComponent( + JList list, + Object value, + int index, + boolean isSelected, + boolean cellHasFocus) + { + if (index == -1) { + value = getSelectionText(); + } else { + Period period = (Period) value; + value = formatPeriodList(period); + } + return super.getListCellRendererComponent(list, + value, + index, + isSelected, + cellHasFocus); + } + } + + + public boolean isWeekOfPeriodVisible() + { + return isWeekOfPeriodVisible; + } + + public void setWeekOfPeriodVisible( boolean isWeekOfPeriodVisible ) + { + this.isWeekOfPeriodVisible = isWeekOfPeriodVisible; + } + +} diff --git a/Rapla/src/org/rapla/gui/internal/common/RaplaClipboard.java b/Rapla/src/org/rapla/gui/internal/common/RaplaClipboard.java new file mode 100644 index 0000000..88aa856 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/common/RaplaClipboard.java @@ -0,0 +1,105 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.common; + +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Reservation; +import org.rapla.facade.ModificationEvent; +import org.rapla.facade.ModificationListener; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaGUIComponent; + +public class RaplaClipboard extends RaplaGUIComponent implements ModificationListener +{ + public static final String ROLE = RaplaClipboard.class.getName(); + private Appointment appointment; + private Reservation reservation; + private Allocatable[] restrictedAllocatables; + + public RaplaClipboard( RaplaContext sm ) throws RaplaException + { + super( sm ); + getUpdateModule().addModificationListener( this ); + } + + public void dataChanged( ModificationEvent evt ) throws RaplaException + { + if ( appointment == null ) + return; + if ( evt.isRemoved( reservation) ) + { + clearAppointment(); + } + } + + private void clearAppointment() + { + this.appointment = null; + this.reservation = null; + this.restrictedAllocatables = null; + } + + public void setAppointment( Appointment appointment, Reservation reservation, Allocatable[] restrictedAllocatables ) + { + this.appointment = appointment; + this.reservation = reservation; + this.restrictedAllocatables = restrictedAllocatables; + } + + public Appointment getAppointment() + { + return appointment; + } + + public Reservation getReservation() + { + return reservation; + } + + public Allocatable[] getRestrictedAllocatables() + { + return restrictedAllocatables; + } + +} + +/* + class AllocationData implements Transferable { + public static final DataFlavor allocationFlavor = new DataFlavor(java.util.Map.class, "Rapla Allocation"); + private static DataFlavor[] flavors = new DataFlavor[] {allocationFlavor}; + + Map data; + + AllocationData(Map data) { + this.data = data; + } + + public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException { + if (isDataFlavorSupported(flavor)) + return data; + else + throw new UnsupportedFlavorException(flavor); + } + + public DataFlavor[] getTransferDataFlavors() { + return flavors; + } + + public boolean isDataFlavorSupported(DataFlavor flavor) { + return flavor.equals(allocationFlavor); + } + + }*/ + diff --git a/Rapla/src/org/rapla/gui/internal/edit/AbstractEditField.java b/Rapla/src/org/rapla/gui/internal/edit/AbstractEditField.java new file mode 100644 index 0000000..4e373f7 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/AbstractEditField.java @@ -0,0 +1,149 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.edit; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; + +import javax.swing.JComponent; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaGUIComponent; + +/** Base class for most rapla edit fields. Provides some mapping + functionality such as reflection invocation of getters/setters. + A fieldName "username" will result in a getUsername() and setUsername() + method. +*/ +public abstract class AbstractEditField extends RaplaGUIComponent + implements EditField + +{ + final static int DEFAULT_LENGTH = 30; + String fieldName; + + ArrayList listenerList = new ArrayList(); + + public AbstractEditField(RaplaContext sm) throws RaplaException { + super(sm); + } + + abstract public JComponent getComponent(); + MappingDelegate delegate; + + protected void setFieldName(String fieldName) { + this.fieldName = fieldName; + } + + void setDelegate(MappingDelegate delegate) { + this.delegate = delegate; + } + + public String getFieldName() { + return this.fieldName; + } + + public boolean isBlock() { + return false; + } + + public boolean isVariableSized() { + return false; + } + + public String getName() { + if (delegate != null) + return delegate.getName(); + + return getString(fieldName.toLowerCase()); + } + + protected String setMethodName() { + return "set" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1); + } + + protected String getMethodName() { + return "get" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1); + } + + private Method getMethod(String name,Object o,int params) throws RaplaException { + Method[] methods = o.getClass().getMethods(); + for (int i=0;i1 + && key.charAt(0) =='a' + && Character.isDigit(key.charAt(1)) + ) + { + try { + int value = Integer.valueOf(key.substring(1)).intValue(); + if (value >= max) + max = value + 1; + } catch (NumberFormatException ex) { + } + } + } + return "a" + (max); + } + + void removeAttribute() throws RaplaException { + int index = getSelectedIndex(); + Attribute att = dt.getAttributes() [index]; + dt.removeAttribute(att); + updateModel(); + } + + void createAttribute() throws RaplaException { + confirmEdits(); + AttributeType type = AttributeType.STRING; + Attribute att = (Attribute) getModification().newAttribute(type); + att.setKey(createNewKey()); + dt.addAttribute(att); + updateModel(); + listEdit.getList().setSelectedIndex( dt.getAttributes().length -1 ); + } + + public void addChangeListener(ChangeListener listener) { + listenerList.add(listener); + } + + public void removeChangeListener(ChangeListener listener) { + listenerList.remove(listener); + } + + public ChangeListener[] getChangeListeners() { + return (ChangeListener[])listenerList.toArray(new ChangeListener[]{}); + } + + protected void fireContentChanged() { + if (listenerList.size() == 0) + return; + ChangeEvent evt = new ChangeEvent(this); + ChangeListener[] listeners = getChangeListeners(); + for (int i = 0;i 1) + newRows = size.toString(); + + size = (Long) expectedColumns.getNumber(); + String newColumns = null; + if ( size != null && size.longValue() > 1) + newColumns = size.toString(); + final Object defaultValue = defaultSelectText.getValue(); + attribute.setDefaultValue( defaultValue); + attribute.setAnnotation(AttributeAnnotations.KEY_EXPECTED_ROWS , newRows); + attribute.setAnnotation(AttributeAnnotations.KEY_EXPECTED_COLUMNS, newColumns); + } else { + attribute.setAnnotation(AttributeAnnotations.KEY_EXPECTED_ROWS, null); + attribute.setAnnotation(AttributeAnnotations.KEY_EXPECTED_COLUMNS, null); + } + + String selectedTab = tabs[tabSelect.getSelectedIndex()]; + if ( selectedTab != null && !selectedTab.equals(AttributeAnnotations.VALUE_MAIN_VIEW)) { + attribute.setAnnotation(AttributeAnnotations.KEY_EDIT_VIEW, selectedTab); + } else { + attribute.setAnnotation(AttributeAnnotations.KEY_EDIT_VIEW, null); + } + } + + private void update() { + AttributeType type = types[classSelect.getSelectedIndex()]; + boolean categoryVisible = type.equals(AttributeType.CATEGORY); + final boolean textVisible = type.equals(AttributeType.STRING); + final boolean booleanVisible = type.equals(AttributeType.BOOLEAN); + final boolean numberVisible = type.equals(AttributeType.INT); + final boolean dateVisible = type.equals(AttributeType.DATE); + boolean expectedRowsVisible = textVisible; + boolean expectedColumnsVisible = textVisible; + categoryLabel.setVisible( categoryVisible ); + categorySelect.getComponent().setVisible( categoryVisible ); + expectedRowsLabel.setVisible( expectedRowsVisible ); + expectedRows.setVisible( expectedRowsVisible ); + expectedColumnsLabel.setVisible( expectedColumnsVisible ); + expectedColumns.setVisible( expectedColumnsVisible ); + defaultSelectCategory.getComponent().setVisible( categoryVisible); + defaultSelectText.getComponent().setVisible( textVisible); + defaultSelectBoolean.getComponent().setVisible( booleanVisible); + defaultSelectNumber.setVisible( numberVisible); + defaultSelectDate.setVisible( dateVisible); + } + + public void actionPerformed(ActionEvent evt) { + if (mapping) + return; + if ( evt.getSource() == classSelect) { + AttributeType newType = types[classSelect.getSelectedIndex()]; + if (newType.equals(AttributeType.CATEGORY)) { + categorySelect.setValue( rootCategory ); + } + } + fireContentChanged(); + update(); + } + + public void stateChanged(ChangeEvent e) { + if (mapping) + return; + + fireContentChanged(); + } + +} + diff --git a/Rapla/src/org/rapla/gui/internal/edit/BooleanField.java b/Rapla/src/org/rapla/gui/internal/edit/BooleanField.java new file mode 100644 index 0000000..1a64f50 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/BooleanField.java @@ -0,0 +1,96 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.edit; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JRadioButton; + +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + + +public class BooleanField extends AbstractEditField implements ActionListener, FocusListener +{ + JPanel panel = new JPanel(); + JRadioButton field1 = new JRadioButton(); + JRadioButton field2 = new JRadioButton(); + + public BooleanField(RaplaContext sm,String fieldName) throws RaplaException { + super( sm); + setFieldName( fieldName ); + field1.setOpaque( false ); + field2.setOpaque( false ); + panel.setOpaque( false ); + panel.setLayout( new BoxLayout(panel,BoxLayout.X_AXIS) ); + panel.add( field1 ); + panel.add( field2 ); + ButtonGroup group = new ButtonGroup(); + group.add( field1 ); + group.add( field2 ); + field2.setSelected( true ); + field1.addActionListener(this); + field2.addActionListener(this); + + field1.setText(getString("yes")); + field2.setText(getString("no")); + field1.addFocusListener(this); + + } + + protected Object getValue() { + return field1.isSelected() ? Boolean.TRUE : Boolean.FALSE; + } + + protected String getMethodName() { + return "is" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1); + } + + protected void setValue(Object object) { + if (object != null) { + boolean selected = ((Boolean)object).booleanValue(); + field1.setSelected(selected); + field2.setSelected(!selected); + } else { + field1.setSelected(false); + field2.setSelected(true); + } + } + + public void actionPerformed(ActionEvent evt) { + fireContentChanged(); + } + + public JComponent getComponent() { + return panel; + } + + public void focusGained(FocusEvent evt) { + Component focusedComponent = evt.getComponent(); + Component parent = focusedComponent.getParent(); + if(parent instanceof JPanel) { + ((JPanel)parent).scrollRectToVisible(focusedComponent.getBounds(null)); + } + } + + public void focusLost(FocusEvent evt) { + + } +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/gui/internal/edit/CategoryEditUI.java b/Rapla/src/org/rapla/gui/internal/edit/CategoryEditUI.java new file mode 100644 index 0000000..09e618c --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/CategoryEditUI.java @@ -0,0 +1,562 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.edit; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; + +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JColorChooser; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTree; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeCellRenderer; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; + +import org.rapla.components.calendar.RaplaArrowButton; +import org.rapla.components.layout.TableLayout; +import org.rapla.components.util.Tools; +import org.rapla.entities.Category; +import org.rapla.entities.CategoryAnnotations; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.EditComponent; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.TreeFactory; +import org.rapla.gui.toolkit.RaplaButton; +import org.rapla.gui.toolkit.RaplaColorList; +import org.rapla.gui.toolkit.RaplaTree; +import org.rapla.gui.toolkit.RaplaWidget; +import org.rapla.gui.toolkit.RecursiveNode; + +/** + * @author Christopher Kohlhaas + */ +public class CategoryEditUI extends RaplaGUIComponent + implements + EditComponent + ,RaplaWidget +{ + JPanel panel = new JPanel(); + + JPanel toolbar = new JPanel(); + RaplaButton newButton = new RaplaButton(); + RaplaButton newSubButton = new RaplaButton(); + RaplaButton removeButton = new RaplaButton(); + RaplaArrowButton moveUpButton = new RaplaArrowButton('^', 25); + RaplaArrowButton moveDownButton = new RaplaArrowButton('v', 25); + + JCheckBox editAdvanced = new JCheckBox(); + + CategoryNode rootNode; + Category rootCategory; + CategoryDetail detailPanel; + RaplaTreeEdit treeEdit; + DefaultTreeModel model; + boolean editKeys; + Listener listener = new Listener(); + TreeCellRenderer iconRenderer; + + public CategoryEditUI(RaplaContext sm) throws RaplaException { + super( sm); + detailPanel = new CategoryDetail(sm); + panel.setPreferredSize( new Dimension( 690,350 ) ); + treeEdit = new RaplaTreeEdit( getI18n(),detailPanel.getComponent(), listener ); + treeEdit.setListDimension( new Dimension( 250,100 ) ); + toolbar.setLayout( new BoxLayout(toolbar, BoxLayout.X_AXIS)); + toolbar.add(newButton); + toolbar.add(newSubButton); + toolbar.add( Box.createHorizontalStrut( 5 )); + toolbar.add(removeButton); + toolbar.add( Box.createHorizontalStrut( 5 )); + toolbar.add(moveUpButton); + toolbar.add(moveDownButton); + toolbar.add(editAdvanced); + panel.setLayout( new BorderLayout() ); + toolbar.add( editAdvanced ); + panel.add( toolbar, BorderLayout.NORTH ); + panel.add( treeEdit.getComponent(), BorderLayout.CENTER ); + + editAdvanced.setOpaque( false ); + newButton.addActionListener(listener); + newSubButton.addActionListener(listener); + removeButton.addActionListener(listener); + editAdvanced.addActionListener(listener); + moveUpButton.addActionListener( listener ); + moveDownButton.addActionListener( listener ); + iconRenderer = getTreeFactory().createRenderer(); + treeEdit.getTree().setCellRenderer( new TreeCellRenderer() { + public Component getTreeCellRendererComponent(JTree tree + ,Object value + ,boolean sel + ,boolean expanded + ,boolean leaf + ,int row + ,boolean hasFocus + ) { + if ( value instanceof CategoryNode) { + Category c = (Category) ((CategoryNode)value).getCategory(); + value = c.getName(getRaplaLocale().getLocale()); + if (editKeys) { + value = "{" + c.getKey() + "} " + value; + } + } + return iconRenderer.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus ); + } + }); + newButton.setText( getString("new_category") ); + newButton.setIcon( getIcon("icon.new")); + newSubButton.setText( getString("new_sub-category") ); + newSubButton.setIcon( getIcon("icon.new") ); + removeButton.setText( getString("delete") ); + editAdvanced.setText( getString("edit_advanced") ); + removeButton.setIcon( getIcon("icon.delete") ); + + detailPanel.addChangeListener( listener ); + detailPanel.setEditKeys( false ); + + editAdvanced.setText(getString("edit_advanced")); + + } + + final private TreeFactory getTreeFactory() { + return (TreeFactory) getService(TreeFactory.ROLE); + } + + + class Listener implements ActionListener,ChangeListener { + public void actionPerformed(ActionEvent evt) { + try { + if ( evt.getSource() == newButton ) { + createCategory( false ); + } else if ( evt.getSource() == newSubButton ) { + createCategory( true ); + } else if ( evt.getSource() == removeButton ) { + removeCategory(); + } else if ( evt.getSource() == moveUpButton ) { + moveCategory( -1); + } else if ( evt.getSource() == moveDownButton ) { + moveCategory( 1); + } else if ( evt.getSource() == editAdvanced ) { + editKeys = editAdvanced.isSelected(); + detailPanel.setEditKeys( editKeys ); + updateModel(); + } else if (evt.getActionCommand().equals("edit")) { + detailPanel.mapFrom( treeEdit.getSelectedValue() ); + } + } catch (RaplaException ex) { + showException(ex, getComponent()); + } + } + public void stateChanged(ChangeEvent e) { + try { + confirmEdits(); + } catch (RaplaException ex) { + showException(ex, getComponent()); + } + } + } + + public JComponent getComponent() { + return panel; + } + + public int getSelectedIndex() { + return treeEdit.getSelectedIndex(); + } + + + public void setObject(Object o) throws RaplaException { + if (!(o instanceof Category)) + throw new RaplaException("Only category objects are accepted: " + o.getClass()); + this.rootCategory = (Category)o; + updateModel(); + } + + private void createCategory(boolean bCreateSubCategory) throws RaplaException { + confirmEdits(); + Category newCategory; + RecursiveNode parentNode; + TreePath path = treeEdit.getTree().getSelectionPath(); + if (path == null) { + parentNode = rootNode; + } else { + RecursiveNode selectedNode = (RecursiveNode) path.getLastPathComponent(); + if (selectedNode.getParent() == null || bCreateSubCategory) + parentNode = selectedNode; + else + parentNode = (RecursiveNode)selectedNode.getParent(); + } + newCategory = createNewNodeAt( parentNode ); + updateModel(); + RecursiveNode newNode = rootNode.findNodeFor( newCategory ); + TreePath selectionPath = new TreePath( newNode.getPath() ); + treeEdit.getTree().setSelectionPath( selectionPath ); + detailPanel.requestFocus(); + } + + + private String createNewKey(Category[] subCategories) { + int max = 1; + for (int i=0;i1 + && key.charAt(0) =='c' + && Character.isDigit(key.charAt(1)) + ) + { + try { + int value = Integer.valueOf(key.substring(1)).intValue(); + if (value >= max) + max = value + 1; + } catch (NumberFormatException ex) { + } + } + } + return "c" + (max); + } + + // creates a new Category + private Category createNewNodeAt(RecursiveNode parentNode) throws RaplaException { + Category newCategory = getModification().newCategory(); + + Category parent = ((CategoryNode) parentNode).getCategory(); + newCategory.setKey(createNewKey(parent.getCategories())); + + newCategory.getName().setName(getI18n().getLang(), getString("new_category") ); + + parent.addCategory(newCategory); + getLogger().debug(" new category " + newCategory + " added to " + parent); + int index = parentNode.getIndexOfUserObject(newCategory); + if (index <0 ) + throw new RaplaException("Can't insert new Category"); + return newCategory; + } + + private void removeCategory() throws RaplaException { + TreePath[] paths = treeEdit.getTree().getSelectionPaths(); + if ( paths == null ) + return; + CategoryNode[] categoryNodes = new CategoryNode[paths.length]; + for (int i=0;i= 0) { + childList.add(nodes[i]); + if (getLogger().isDebugEnabled()) + getLogger().debug("Removing CategoryNode " + nodes[i].getCategory()); + } + } + if (path != null) { + int size = childList.size(); + Object[] childs = new Object[size]; + for (int i=0;i50) + { + Object[] param = new Object[3]; + param[0] = key; + param[1] = "'-', '_'"; + param[2] = "'_'"; + throw new RaplaException(getI18n().format("error.invalid_key", param)); + } + } + public void setEditKeys(boolean editKeys) { + detailPanel.setEditKeys(editKeys); + this.editKeys = editKeys; + } + + class CategoryNode extends RecursiveNode { + public CategoryNode(TreeNode parent,Category category) { + super(parent,category); + } + + protected Category getCategory() { + return (Category) getUserObject(); + } + + protected Object[] getChildObjects() { + return getCategory().getCategories(); + } + + protected RecursiveNode createChildNode(Object userObject) { + return new CategoryNode(this,(Category)userObject); + } + + public String toString() { + return getName(getCategory()); + } + } +} + +class CategoryDetail extends AbstractEditField + implements + ChangeListener +{ + JPanel panel = new JPanel(); + JLabel nameLabel = new JLabel(); + JLabel keyLabel = new JLabel(); + JLabel colorLabel = new JLabel(); + JButton colorChooserBtn = new JButton(); + Color currentColor; + + MultiLanguageField name; + TextField key; + TextField colorTextField; + JPanel colorPanel = new JPanel(); + + public CategoryDetail(RaplaContext sm) throws RaplaException { + super( sm); + name = new MultiLanguageField(sm,"name"); + key = new TextField(sm,"key"); + colorTextField = new TextField(sm,"color"); + double fill = TableLayout.FILL; + double pre = TableLayout.PREFERRED; + panel.setLayout( new TableLayout( new double[][] + {{5, pre, 5, fill }, // Columns + {5, pre ,5, pre, 5, pre, 5}} // Rows + )); + panel.add("1,1,l,f", nameLabel); + panel.add("3,1,f,f", name.getComponent() ); + panel.add("1,3,l,f", keyLabel); + panel.add("3,3,f,f", key.getComponent() ); + panel.add("1,5,l,f", colorLabel); + panel.add("3,5,f,f", colorPanel); + colorPanel.setLayout( new BorderLayout()); + colorPanel.add( colorTextField.getComponent(), BorderLayout.CENTER ); + colorPanel.add( colorChooserBtn, BorderLayout.EAST ); + + nameLabel.setText(getString("name") + ":"); + keyLabel.setText(getString("key") + ":"); + colorLabel.setText( getString("color") + ":"); + name.addChangeListener ( this ); + key.addChangeListener ( this ); + colorTextField.addChangeListener( this ); + + colorChooserBtn.setText( getString("change") ); + colorChooserBtn.addActionListener( new ActionListener() { + + public void actionPerformed(ActionEvent e) { + currentColor = JColorChooser.showDialog( + panel, + "Choose Background Color", + currentColor); + colorChooserBtn.setBackground( currentColor ); + if ( currentColor != null) { + colorTextField.setValue( RaplaColorList.getHexForColor( currentColor )); + } + fireContentChanged(); + } + + }); + + } + + public void requestFocus() { + name.requestFocus(); + } + + public void setEditKeys(boolean editKeys) { + keyLabel.setVisible( editKeys ); + key.getComponent().setVisible( editKeys ); + colorLabel.setVisible( editKeys ); + colorTextField.getComponent().setVisible( editKeys ); + colorChooserBtn.setVisible( editKeys ); + } + + public JComponent getComponent() { + return panel; + } + + public Object getValue() {return null;} + + public void setValue(Object object) {} + + public void mapFrom(Object object) throws RaplaException { + Category category = (Category) object; + name.mapFrom(category); + key.mapFrom(category); + String colorValue = category.getAnnotation(CategoryAnnotations.KEY_NAME_COLOR, null) ; + if ( colorValue != null) { + currentColor = RaplaColorList.getColorForHex( colorValue ); + colorTextField.setValue( colorValue ); + } else { + colorTextField.setValue( "" ); + currentColor = null; + } + colorChooserBtn.setBackground( currentColor ); + } + + public void mapTo(Object object) throws RaplaException { + Category category = (Category) object; + name.mapTo(category); + key.mapTo(category); + String colorValue = colorTextField.getValue().toString().trim(); + if ( colorValue.length() > 0) { + category.setAnnotation(CategoryAnnotations.KEY_NAME_COLOR, colorValue ); + } else { + category.setAnnotation(CategoryAnnotations.KEY_NAME_COLOR, null ); + } + } + + public void stateChanged(ChangeEvent e) { + fireContentChanged(); + } +} + + diff --git a/Rapla/src/org/rapla/gui/internal/edit/CategoryListField.java b/Rapla/src/org/rapla/gui/internal/edit/CategoryListField.java new file mode 100644 index 0000000..b8a26f1 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/CategoryListField.java @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.edit; + +import java.util.Vector; + +import org.rapla.entities.Category; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +public class CategoryListField extends ListField { + Category rootCategory; + + public CategoryListField(RaplaContext sm,String fieldName,Category rootCategory) throws RaplaException { + super(sm,fieldName); + this.rootCategory = rootCategory; + + Category[] obj = rootCategory.getCategories(); + Vector list = new Vector(); + list.add(nothingSelected); + for (int i=0;i 0); + i++; + } + + ruleListPanel.revalidate(); + ruleListPanel.repaint(); + } + + + public void actionPerformed(ActionEvent evt) { + if (evt.getSource() == attributeSelector) { + Attribute att = (Attribute)attributeSelector.getSelectedItem(); + try { + if (att != null) { + RuleComponent ruleComponent = getComponent(att); + if (ruleComponent == null) { + addRuleComponent().newRule( att); + } else { + ruleComponent.addOr(); + } + update(); + // invokeLater prevents a deadlock in jdk <=1.3 + javax.swing.SwingUtilities.invokeLater(new Runnable() { + public void run() { + attributeSelector.setSelectedIndex(-1); + } + }); + fireFilterChanged(); + } + } catch (RaplaException ex) { + showException(ex, getNewComponent()); + } + } + } + + public ClassificationFilter getFilter() throws RaplaException { + if ( type == null ) + return null; + ClassificationFilter filter = type.newClassificationFilter(); + int i=0; + for (Iterator it = ruleList.iterator();it.hasNext();) { + RuleComponent ruleComponent = (RuleComponent) it.next(); + Attribute attribute = ruleComponent.getAttribute(); + List ruleRows = ruleComponent.getRuleRows(); + int size = ruleRows.size(); + Object[][] conditions = new Object[size][2]; + for (int j=0;j"; + if (index == 3) + return "<>"; + if (index == 4) + return "<="; + if (index == 5) + return ">="; + + } + Assert.notNull(field,"Unknown AttributeType" + type); + return null; + } + + private void setOperatorValue(String operator) { + AttributeType type = attribute.getType(); + if ((type.equals(AttributeType.DATE) || type.equals(AttributeType.INT))) + { + if (operator == null) + operator = "<"; + JComboBox box = (JComboBox)operatorComponent; + if (operator.equals("<")) + box.setSelectedIndex(0); + if (operator.equals("=") || operator.equals("is")) + box.setSelectedIndex(1); + if (operator.equals(">")) + box.setSelectedIndex(2); + if (operator.equals("<>")) + box.setSelectedIndex(3); + if (operator.equals("<=")) + box.setSelectedIndex(4); + if (operator.equals(">=")) + box.setSelectedIndex(5); + + } + } + + private EditField createField(Attribute attribute) throws RaplaException { + operatorComponent = null; + AttributeType type = attribute.getType(); + String key = attribute.getKey(); + if (type.equals(AttributeType.CATEGORY)) + { + operatorComponent = new JLabel(""); + Category rootCategory = (Category)attribute.getConstraint(ConstraintIds.KEY_ROOT_CATEGORY); + if (rootCategory.getDepth() > 2) { + Category defaultCategory = (Category) attribute.defaultValue(); + field = new CategorySelectField(getContext(),key,rootCategory,defaultCategory); + } else { + field = new CategoryListField(getContext(),key,rootCategory); + } + } + else if (type.equals(AttributeType.STRING)) + { + operatorComponent = new JLabel(getString("filter.contains")); + field = new TextField(getContext(),key); + } + else if (type.equals(AttributeType.INT)) + { + field = new LongField(getContext(),key); + DefaultComboBoxModel model = new DefaultComboBoxModel(new String[] { + getString("filter.is_smaller_than") + ,getString("filter.equals") + ,getString("filter.is_greater_than") + ,getString("filter.not_equals") + ,getString("filter.smaller_or_equals") + ,getString("filter.greater_or_equals") + }); + operatorComponent = new JComboBox(model); + + } + else if (type.equals(AttributeType.DATE)) + { + field = new DateField(getContext(),key); + DefaultComboBoxModel model = new DefaultComboBoxModel(new String[] { + getString("filter.earlier_than") + ,getString("filter.equals") + ,getString("filter.later_than") + ,getString("filter.not_equals") + }); + operatorComponent = new JComboBox(model); } + else if (type.equals(AttributeType.BOOLEAN)) + { + operatorComponent = new JLabel(""); + field = new BooleanField(getContext(),key); + ruleValue = new Boolean(false); + } + + Assert.notNull(field,"Unknown AttributeType"); + field.setDelegate(new MyMappingDelegate(field)); + return field; + } + + + public Object getValue() throws RaplaException { + ruleValue = field.getValue(); + return ruleValue; + } + + class MyMappingDelegate implements MappingDelegate { + AbstractEditField editField; + MyMappingDelegate(AbstractEditField field) { + this.editField = field; + } + public String getName() { + return getAttName(editField.getFieldName()); + } + public void mapFrom(Object o) throws RaplaException { + } + public void mapTo(Object o) throws RaplaException { + } + }; + } +} + diff --git a/Rapla/src/org/rapla/gui/internal/edit/ClassificationEditUI.java b/Rapla/src/org/rapla/gui/internal/edit/ClassificationEditUI.java new file mode 100644 index 0000000..6f3cc98 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/ClassificationEditUI.java @@ -0,0 +1,115 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.edit; + +import org.rapla.components.util.Assert; +import org.rapla.entities.Category; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.AttributeAnnotations; +import org.rapla.entities.dynamictype.AttributeType; +import org.rapla.entities.dynamictype.Classification; +import org.rapla.entities.dynamictype.ConstraintIds; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + + +public class ClassificationEditUI extends AbstractEditUI { + public ClassificationEditUI(RaplaContext sm) throws RaplaException { + super(sm); + } + + protected Classification classification; + + private String getAttName(String key) { + return getName(classification.getAttribute(key)); + } + + protected Attribute getAttribute(int i) { + return classification.getAttribute(((AbstractEditField) fields[i]).getFieldName()); + } + + private void setAttValue(String key,Object value) { + classification.setValue( classification.getAttribute(key), value ); + } + + private Object getAttValue(String key) { + return classification.getValue(classification.getAttribute(key)); + } + + private EditField createField(Attribute attribute) throws RaplaException { + AttributeType type = attribute.getType(); + String key = attribute.getKey(); + AbstractEditField field = null; + + if (type.equals(AttributeType.STRING)) { + Integer rows = new Integer(attribute.getAnnotation(AttributeAnnotations.KEY_EXPECTED_ROWS,"1")); + Integer columns = new Integer(attribute.getAnnotation(AttributeAnnotations.KEY_EXPECTED_COLUMNS,String.valueOf(TextField.DEFAULT_LENGTH))); + field = new TextField(getContext(),key, rows.intValue(), columns.intValue()); + } else if (type.equals(AttributeType.INT)) { + field = new LongField(getContext(),key); + } else if (type.equals(AttributeType.DATE)) { + field = new DateField(getContext(),key); + } else if (type.equals(AttributeType.BOOLEAN)) { + field = new BooleanField(getContext(),key); + } else if (type.equals(AttributeType.CATEGORY)) + { + Category defaultCategory = (Category) attribute.defaultValue(); + Category rootCategory = (Category) attribute.getConstraint(ConstraintIds.KEY_ROOT_CATEGORY); + if (rootCategory.getDepth() > 2) { + field = new CategorySelectField(getContext(),key, rootCategory, defaultCategory); + } else { + field= new CategoryListField(getContext(),key, rootCategory); + } + } + Assert.notNull(field, "Unknown AttributeType"); + field.setDelegate(new MyMappingDelegate(field)); + return field; + } + + public void setObject(Object object) throws RaplaException { + //classification = (Classification) ((Classification) object).clone(); + classification = (Classification) object; + this.o = classification; + DynamicType type = classification.getType(); + Attribute[] attributes = type.getAttributes(); + EditField[] fields = new EditField[attributes.length]; + for (int i=0;iSwitch on experienced settings to show the keys. " + ,ex); + + } + } + public Object getObject() { + return dynamicType; + } + + public void setObject(Object o) throws RaplaException { + dynamicType = (DynamicType) o; + name.mapFrom(o); + elementKey.mapFrom(o); + attributeEdit.setDynamicType(dynamicType); + updateAnnotations(); + } + + private void updateAnnotations() { + annotationText.setText( dynamicType.getAnnotation( DynamicTypeAnnotations.KEY_NAME_FORMAT ) ); + } + + private void validate() throws RaplaException { + Assert.notNull(dynamicType); + if ( getName( dynamicType ).length() == 0) + throw new RaplaException(getString("error.no_name")); + + if (dynamicType.getElementKey().equals("")) { + throw new RaplaException(getI18n().format("error.no_key","")); + } + checkKey(dynamicType.getElementKey()); + Attribute[] attributes = dynamicType.getAttributes(); + for (int i=0;i50) + { + Object[] param = new Object[3]; + param[0] = key; + param[1] = "'-', '_'"; + param[2] = "'_'"; + throw new RaplaException(getI18n().format("error.invalid_key", param)); + } + + + } +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/gui/internal/edit/EditControllerImpl.java b/Rapla/src/org/rapla/gui/internal/edit/EditControllerImpl.java new file mode 100644 index 0000000..a5c8297 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/EditControllerImpl.java @@ -0,0 +1,131 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.edit; + +import java.awt.Component; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; + +import org.rapla.entities.Category; +import org.rapla.entities.Entity; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.RaplaType; +import org.rapla.entities.User; +import org.rapla.entities.configuration.Preferences; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Period; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.EditComponent; +import org.rapla.gui.EditController; +import org.rapla.gui.RaplaGUIComponent; + +/**This class handles the edit-ui for all entities (except reservations).*/ +public class EditControllerImpl extends RaplaGUIComponent implements EditController +{ + Collection editWindowList = new ArrayList(); + + public EditControllerImpl(RaplaContext sm) throws RaplaException { + super(sm); + } + + void addEditDialog(EditDialog editWindow) { + editWindowList.add(editWindow); + } + + void removeEditDialog(EditDialog editWindow) { + editWindowList.remove(editWindow); + } + + + /* (non-Javadoc) + * @see org.rapla.gui.edit.IEditController#createUI(org.rapla.entities.RaplaPersistant) + */ + public EditComponent createUI(RaplaObject obj) throws RaplaException { + RaplaType type = obj.getRaplaType(); + EditComponent ui = null; + if ( Allocatable.TYPE.equals( type )) { + ui=new AllocatableEditUI(getContext()); + } else if ( DynamicType.TYPE.equals( type )) { + ui=new DynamicTypeEditUI(getContext()); + } else if ( User.TYPE.equals( type )) { + ui=new UserEditUI(getContext()); + } else if ( Period.TYPE.equals( type )) { + ui=new PeriodEditUI(getContext()); + } else if ( Category.TYPE.equals( type )) { + ui=new CategoryEditUI(getContext()); + } else if ( Preferences.TYPE.equals( type )) { + ui=new PreferencesEditUI(getContext()); + } + + if ( ui == null) { + throw new RuntimeException("Can't edit objects of type " + type.toString()); + } + return ui; + } + + protected String guessTitle(Object obj) { + if (obj instanceof Entity) + { + String type = ((Entity) obj).getRaplaType().getLocalName(); + return getString( type ); + } + return ""; + } + + /* (non-Javadoc) + * @see org.rapla.gui.edit.IEditController#edit(org.rapla.entities.Entity, java.awt.Component) + */ + public void edit(Entity obj,Component owner) throws RaplaException { + edit(obj, guessTitle(obj), owner); + } + + /* (non-Javadoc) + * @see org.rapla.gui.edit.IEditController#edit(org.rapla.entities.Entity, java.lang.String, java.awt.Component) + */ + public void edit(Entity obj,String title,Component owner) throws RaplaException { + // Lookup if the reservation is already beeing edited + EditDialog c = null; + Iterator it = editWindowList.iterator(); + while (it.hasNext()) { + c = (EditDialog)it.next(); + Object editObj = c.ui.getObject(); + if (editObj != null && editObj instanceof Entity && ((Entity)editObj).isIdentical(obj)) + { + break; + } + else + { + c = null; + } + } + + if (c != null) { + c.dlg.requestFocus(); + c.dlg.toFront(); + } else { + if ( obj.isPersistant()) { + obj = (Entity)getModification().edit(obj); + } + EditComponent ui = createUI( obj ); + EditDialog gui = new EditDialog(getContext(),ui, false); + gui.start(obj,title,owner); + } + //return c; + + } + +} + diff --git a/Rapla/src/org/rapla/gui/internal/edit/EditDialog.java b/Rapla/src/org/rapla/gui/internal/edit/EditDialog.java new file mode 100644 index 0000000..4c5f252 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/EditDialog.java @@ -0,0 +1,153 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.edit; + +import java.awt.Component; +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; + +import org.apache.avalon.framework.activity.Disposable; +import org.rapla.components.layout.TableLayout; +import org.rapla.entities.Entity; +import org.rapla.entities.domain.Allocatable; +import org.rapla.facade.ModificationEvent; +import org.rapla.facade.ModificationListener; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.EditComponent; +import org.rapla.gui.EditController; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.toolkit.DialogUI; +import org.rapla.gui.toolkit.DisposingTool; + +public class EditDialog extends RaplaGUIComponent implements ModificationListener,Disposable { + DialogUI dlg; + boolean bSaving = false; + EditComponent ui; + boolean modal; + + public EditDialog(RaplaContext sm,EditComponent ui,boolean modal) throws RaplaException { + super( sm); + this.ui = ui; + this.modal = modal; + } + + public EditDialog(RaplaContext sm,EditComponent ui) throws RaplaException { + this(sm,ui,true); + } + + final private EditControllerImpl getPrivateEditDialog() { + return (EditControllerImpl) getService(EditController.ROLE); + } + + public int start(Object editObj,String title,Component owner) + throws + RaplaException + { + getLogger().debug("Editing Object: " + editObj); + ui.setObject(editObj); + JComponent editComponent = ui.getComponent(); + JPanel panel = new JPanel(); + panel.setLayout( new TableLayout( new double[][] { + { TableLayout.PREFERRED}, + { TableLayout.PREFERRED} + })); + if ( editObj instanceof Allocatable) + { + JLabel icon = new JLabel(" "); + icon.setIcon( getIcon("icon.resources")); + panel.add( icon, "0,0,r,t"); + } + panel.add( editComponent, "0,0"); + editComponent.setBorder(BorderFactory.createEmptyBorder(3,3,3,3)); + dlg = DialogUI.create(getContext(),owner,modal,panel,new String[] { + getString("save") + ,getString("cancel") + }); + dlg.setAbortAction(new AbortAction()); + dlg.getButton(0).setAction(new SaveAction()); + dlg.getButton(1).setAction(new AbortAction()); + dlg.getButton(0).setIcon(getIcon("icon.save")); + dlg.getButton(1).setIcon(getIcon("icon.cancel")); + dlg.setTitle(getI18n().format("edit.format",title)); + getUpdateModule().addModificationListener(this); + dlg.addWindowListener(new DisposingTool(this)); + dlg.start(); + if (modal) + { + return dlg.getSelectedIndex(); + } + else + { + getPrivateEditDialog().addEditDialog( this ); + return -1; + } + } + + protected boolean shouldCancelOnModification(ModificationEvent evt) { + return evt.hasChanged(ui.getObject()); + } + + public void dataChanged(ModificationEvent evt) throws RaplaException { + if (bSaving || dlg == null || !dlg.isVisible() || ui == null) + return; + if (shouldCancelOnModification(evt)) { + getLogger().warn("Object has been changed outside."); + DialogUI warning = + DialogUI.create(getContext() + ,ui.getComponent() + ,true + ,getString("warning") + ,getI18n().format("warning.update",ui.getObject()) + ); + warning.start(); + getPrivateEditDialog().removeEditDialog( this ); + dlg.close(); + } + } + + class SaveAction extends AbstractAction { + private static final long serialVersionUID = 1L; + + public void actionPerformed(ActionEvent evt) { + try { + ui.mapToObject(); + bSaving = true; + getModification().store( (Entity) ui.getObject()); + getPrivateEditDialog().removeEditDialog( EditDialog.this ); + dlg.close(); + } catch (RaplaException ex) { + showException(ex,dlg); + } + } + } + + class AbortAction extends AbstractAction { + private static final long serialVersionUID = 1L; + + public void actionPerformed(ActionEvent evt) { + getPrivateEditDialog().removeEditDialog( EditDialog.this ); + dlg.close(); + } + } + + public void dispose() { + getUpdateModule().removeModificationListener(this); + } +} + diff --git a/Rapla/src/org/rapla/gui/internal/edit/EditField.java b/Rapla/src/org/rapla/gui/internal/edit/EditField.java new file mode 100644 index 0000000..e6381b6 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/EditField.java @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.edit; + +import javax.swing.event.ChangeListener; + +import org.rapla.framework.RaplaException; +import org.rapla.gui.toolkit.RaplaWidget; + +/** Base class for most rapla edit fields. Provides some mapping + functionality such as reflection invocation of getters/setters. + A fieldName "username" will result in a getUsername() and setUsername() + method. +*/ +public interface EditField extends RaplaWidget +{ + public boolean isBlock(); + public boolean isVariableSized(); + /** Returns the proper translation/name of the field-name*/ + public String getName(); + public void mapTo(Object o) throws RaplaException; + public void mapFrom(Object o) throws RaplaException; + /** registers new ChangeListener for this component. + * An ChangeEvent will be fired to every registered ChangeListener + * when the component info changes. + * @see javax.swing.event.ChangeListener + * @see javax.swing.event.ChangeEvent + */ + public void addChangeListener(ChangeListener listener); + /** removes a listener from this component.*/ + public void removeChangeListener(ChangeListener listener); +} + diff --git a/Rapla/src/org/rapla/gui/internal/edit/ListField.java b/Rapla/src/org/rapla/gui/internal/edit/ListField.java new file mode 100644 index 0000000..80c330b --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/ListField.java @@ -0,0 +1,104 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.edit; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.util.Vector; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.ListCellRenderer; + +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.internal.common.NamedListCellRenderer; + +public class ListField extends AbstractEditField implements ActionListener, FocusListener { + JPanel panel; + JComboBox field; + protected String nothingSelected; + Vector list; + + public ListField(RaplaContext sm,String fieldName,Vector v) throws RaplaException { + this(sm,fieldName); + setVector(v); + } + + protected ListField(RaplaContext sm,String fieldName) throws RaplaException { + super( sm); + setFieldName(fieldName); + panel = new JPanel(); + panel.setOpaque(false); + field = new JComboBox(); + field.addActionListener(this); + panel.setLayout(new BorderLayout()); + panel.add(field,BorderLayout.WEST); + field.setRenderer(new NamedListCellRenderer(getI18n().getLocale())); + nothingSelected = getString("nothing_selected"); + field.addFocusListener(this); + } + + protected void setVector(Vector v) { + this.list = v; + field.setModel(new DefaultComboBoxModel(v)); + } + + + public void setRenderer(ListCellRenderer renderer) { + field.setRenderer( renderer ); + } + + protected Object getValue() { + Object value = field.getSelectedItem(); + if (list.contains(nothingSelected) && nothingSelected.equals(value)) { + return null; + } else { + return value; + } + } + protected void setValue(Object value) { + if (list.contains(nothingSelected) && value==null) { + field.setSelectedItem(nothingSelected); + } else { + field.setSelectedItem(value); + } + } + public JComponent getComponent() { + return panel; + } + + public void actionPerformed(ActionEvent evt) { + fireContentChanged(); + } + + public void focusGained(FocusEvent evt) { + + Component focusedComponent = evt.getComponent(); + Component parent = focusedComponent.getParent(); + if(parent instanceof JPanel) { + ((JPanel)parent).scrollRectToVisible(focusedComponent.getBounds(null)); + } + + } + + public void focusLost(FocusEvent evt) { + + } +} diff --git a/Rapla/src/org/rapla/gui/internal/edit/LongField.java b/Rapla/src/org/rapla/gui/internal/edit/LongField.java new file mode 100644 index 0000000..fd9505f --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/LongField.java @@ -0,0 +1,81 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.edit; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; + +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.rapla.components.calendar.RaplaNumber; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +public class LongField extends AbstractEditField implements ChangeListener, FocusListener { + JPanel panel = new JPanel(); + RaplaNumber field; + public LongField( RaplaContext sm,String fieldName ) throws RaplaException { + this( sm,fieldName, null ); + } + public LongField( RaplaContext sm, String fieldName, Long minimum ) throws RaplaException { + super( sm); + setFieldName( fieldName ); + panel.setLayout( new BorderLayout() ); + panel.setOpaque( false ); + field = new RaplaNumber( minimum, minimum, null, minimum == null ); + field.setColumns(8); + field.addChangeListener(this); + panel.add(field,BorderLayout.WEST); + field.addFocusListener(this); + } + protected Object getValue() throws RaplaException { + if (field.getNumber() != null) + return new Long(field.getNumber().longValue()); + else + return null; + } + protected void setValue(Object object) { + if (object != null) { + field.setNumber((Number)object); + } else { + field.setNumber(null); + } + } + + public void stateChanged(ChangeEvent evt) { + fireContentChanged(); + } + + public JComponent getComponent() { + return panel; + } + + public void focusGained(FocusEvent evt) { + + Component focusedComponent = evt.getComponent(); + Component parent = focusedComponent.getParent(); + if(parent instanceof JPanel) { + ((JPanel)parent).scrollRectToVisible(focusedComponent.getBounds(null)); + } + + } + + public void focusLost(FocusEvent evt) { + + } +} diff --git a/Rapla/src/org/rapla/gui/internal/edit/MappingDelegate.java b/Rapla/src/org/rapla/gui/internal/edit/MappingDelegate.java new file mode 100644 index 0000000..988f466 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/MappingDelegate.java @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.edit; + +import org.rapla.framework.RaplaException; +/** Allows the delegation of the mapTo, mapFrom and getName + methods of the EditField to an other class + */ +interface MappingDelegate { + public String getName(); + public void mapTo(Object o) throws RaplaException; + public void mapFrom(Object o) throws RaplaException; +} + diff --git a/Rapla/src/org/rapla/gui/internal/edit/MultiLanguageField.java b/Rapla/src/org/rapla/gui/internal/edit/MultiLanguageField.java new file mode 100644 index 0000000..b55a1ef --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/MultiLanguageField.java @@ -0,0 +1,202 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.edit; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Collection; + +import javax.swing.AbstractCellEditor; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.JTextField; +import javax.swing.event.CellEditorListener; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.table.DefaultTableModel; + +import org.rapla.entities.MultiLanguageName; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.toolkit.DialogUI; +import org.rapla.gui.toolkit.RaplaButton; + +public class MultiLanguageField extends AbstractEditField implements ChangeListener, ActionListener,CellEditorListener { + JPanel panel = new JPanel(); + TextField textField; + RaplaButton button = new RaplaButton(RaplaButton.SMALL); + MultiLanguageName name = new MultiLanguageName(); + MultiLanguageEditorDialog editorDialog; + + String[] availableLanguages; + + public MultiLanguageField(RaplaContext sm, String fieldName) throws RaplaException { + super( sm); + textField = new TextField(sm, "name"); + setFieldName(fieldName); + availableLanguages = getRaplaLocale().getAvailableLanguages(); + panel.setLayout( new BorderLayout() ); + panel.add( textField.getComponent(), BorderLayout.CENTER ); + panel.add( button, BorderLayout.EAST ); + button.addActionListener( this ); + button.setIcon( getIcon("icon.language-select") ); + textField.addChangeListener( this ); + } + + public void requestFocus() { + textField.getComponent().requestFocus(); + } + + public void stateChanged(ChangeEvent e) { + if (name != null) { + name.setName(getI18n().getLang() + ,(String)textField.getValue()); + fireContentChanged(); + } + } + + public void actionPerformed(ActionEvent evt) { + editorDialog = new MultiLanguageEditorDialog( button ); + editorDialog.addCellEditorListener( this ); + editorDialog.setEditorValue( name ); + try { + editorDialog.show(); + } catch (RaplaException ex) { + showException( ex, getComponent()); + } + } + + public void editingStopped(ChangeEvent e) { + setValue((MultiLanguageName) editorDialog.getEditorValue()); + fireContentChanged(); + } + + public void editingCanceled(ChangeEvent e) { + } + + protected Object getValue() { + return this.name; + } + + protected void setValue(Object object) { + this.name = (MultiLanguageName)object; + textField.setValue(name.getName(getI18n().getLang())); + } + + public void mapTo(Object object) { + // Don't need to map back because MultiLanguageName is modified directly + } + + public JComponent getComponent() { + return panel; + } + + class MultiLanguageEditorDialog extends AbstractCellEditor { + private static final long serialVersionUID = 1L; + + JTable table = new JTable(); + JLabel label = new JLabel(); + JPanel comp = new JPanel(); + MultiLanguageName editorValue; + Component owner; + MultiLanguageEditorDialog(JComponent owner) { + this.owner = owner; + table.setPreferredScrollableViewportSize(new Dimension(300, 200)); + comp.setLayout(new BorderLayout()); + comp.add(label,BorderLayout.NORTH); + comp.add(new JScrollPane(table),BorderLayout.CENTER); + } + + public void setEditorValue(Object value) { + this.editorValue = (MultiLanguageName)value; + table.setModel(new TranslationTableModel(editorValue)); + table.getColumnModel().getColumn(0).setPreferredWidth(30); + table.getColumnModel().getColumn(1).setPreferredWidth(200); + label.setText(getI18n().format("translation.format", editorValue)); + } + + public Object getEditorValue() { + return editorValue; + } + + public Object getCellEditorValue() { + return getEditorValue(); + } + + public void show() throws RaplaException { + DialogUI dlg = DialogUI.create(getContext(),owner,true,comp,new String[] { getString("ok"),getString("cancel")}); + dlg.setTitle(getString("translation")); + // Workaround for Bug ID 4480264 on developer.java.sun.com + if (table.getRowCount() > 0 ) { + table.editCellAt(0, 0); + table.editCellAt(0, 1); + } + dlg.start(); + if (dlg.getSelectedIndex() == 0) { + for (int i=0;i 2) { + groupSelect = new CategorySelectField(getContext(),"group", rootCategory); + } else { + groupSelect = new CategoryListField(getContext(),"group", rootCategory); + } + userPanel.add( new JLabel(getString("group") + ":"), "0,2,l,f" ); + userPanel.add( groupSelect.getComponent(),"2,2,l,f" ); + groupSelect.addChangeListener( this ); + } + + + reservationPanel = new JPanel(); + panel.add( reservationPanel , "0,2,f,f" ); + reservationPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(), getString("allocatable_in_timeframe") + ":" )); + reservationPanel.setLayout( new TableLayout( new double[][] + {{pre,3, pre, 5, pre, 5}, // Columns + {pre, 5, pre}} // Rows + )); + + reservationPanel.add( new JLabel( getString("start_date") + ":" ) , "0,0,l,f" ); + reservationPanel.add( startSelection , "2,0,l,f" ); + startSelection.setModel( createSelectionModel() ); + startSelection.setSelectedIndex( 0 ); + + startDate = new DateField(sm,"start"); + reservationPanel.add( startDate.getComponent() , "4,0,l,f" ); + + minAdvance = new LongField(sm,"minAdvance", new Long(0) ); + reservationPanel.add( minAdvance.getComponent() , "4,0,l,f" ); + + reservationPanel.add( new JLabel( getString("end_date") + ":" ), "0,2,l,f" ); + reservationPanel.add( endSelection , "2,2,l,f" ); + endSelection.setModel( createSelectionModel() ); + endSelection.setSelectedIndex( 0 ); + + endDate = new DateField(sm,"end"); + reservationPanel.add( endDate.getComponent() , "4,2,l,f" ); + + maxAdvance = new LongField(sm,"maxAdvance", new Long(1) ); + reservationPanel.add( maxAdvance.getComponent() , "4,2,l,f" ); + + userPanel.add( new JLabel(getString("permission.access") + ":"), "0,4,f,f" ); + Vector vector = new Vector(); + int[] accessLevels = Permission.ACCESS_LEVEL_TYPES; + for (int i=0;i= Permission.ALLOCATE && level < Permission.ADMIN); + + int i = startSelection.getSelectedIndex(); + startDate.getComponent().setVisible( i == 1 ); + minAdvance.getComponent().setVisible( i == 2 ); + + int j = endSelection.getSelectedIndex(); + endDate.getComponent().setVisible( j == 1 ); + maxAdvance.getComponent().setVisible( j == 2 ); + } + + boolean listenersDisabled = false; + public void setValue(Object value) throws RaplaException { + try { + listenersDisabled = true; + + permission = (Permission) value; + if ( groupSelect != null ) + groupSelect.mapFrom( permission ); + userSelect.mapFrom( permission ); + accessField.mapFrom( permission ); + + int startIndex = 0; + if ( permission.getStart() != null ) + startIndex = 1; + if ( permission.getMinAdvance() != null ) + startIndex = 2; + startSelection.setSelectedIndex( startIndex ); + + int endIndex = 0; + if ( permission.getEnd() != null ) + endIndex = 1; + if ( permission.getMaxAdvance() != null ) + endIndex = 2; + endSelection.setSelectedIndex( endIndex ); + + startDate.mapFrom( permission ); + minAdvance.mapFrom( permission ); + endDate.mapFrom( permission ); + maxAdvance.mapFrom( permission ); + toggleVisibility(); + } finally { + listenersDisabled = false; + } + } + + + public void actionPerformed(ActionEvent evt) { + if ( listenersDisabled ) + return; + + try { + if (evt.getSource() == startSelection) { + int i = startSelection.getSelectedIndex(); + if ( i == 0 ) { + permission.setStart( null ); + permission.setMinAdvance( null ); + } + if ( i == 1 ) { + Date today = getQuery().today(); + permission.setStart( today ); + startDate.mapFrom ( permission ); + } if ( i == 2 ) { + permission.setMinAdvance( new Long(0) ); + minAdvance.mapFrom ( permission ); + } + } + if (evt.getSource() == endSelection) { + int i = endSelection.getSelectedIndex(); + if ( i == 0 ) { + permission.setEnd( null ); + permission.setMaxAdvance( null ); + } + if ( i == 1 ) { + Date today = getQuery().today(); + permission.setEnd( today ); + endDate.mapFrom ( permission ); + } if ( i == 2 ) { + permission.setMaxAdvance( new Long( 30 ) ); + maxAdvance.mapFrom ( permission ); + } + } + toggleVisibility(); + fireContentChanged(); + } catch ( RaplaException ex ) { + showException( ex, getComponent() ); + } + } + + public Object getValue() { + return permission; + } + + public void stateChanged(ChangeEvent evt) { + if ( listenersDisabled ) + return; + + try { + if (evt.getSource() == groupSelect) { + groupSelect.mapTo( permission ); + userSelect.mapFrom(permission); + } else if (evt.getSource() == userSelect) { + userSelect.mapTo( permission ); + if ( groupSelect != null ) + groupSelect.mapFrom(permission); + } else if (evt.getSource() == startDate) { + startDate.mapTo( permission ); + } else if (evt.getSource() == minAdvance) { + minAdvance.mapTo( permission ); + } else if (evt.getSource() == endDate) { + endDate.mapTo( permission ); + } else if (evt.getSource() == maxAdvance) { + maxAdvance.mapTo( permission ); + } else if (evt.getSource() == accessField ) { + accessField.mapTo( permission ); + toggleVisibility(); + } + fireContentChanged(); + } catch (RaplaException ex) { + showException( ex, getComponent() ); + } + } + + class UserListField extends ListField { + + public UserListField(RaplaContext sm,String fieldName) throws RaplaException{ + super(sm,fieldName); + Object[] obj = getQuery().getUsers(); + Vector list = new Vector(); + list.add(nothingSelected); + for (int i=0;i 1 ) { + model.removeElement(permission); + allocatable.removePermission(permission); + } + } + listEdit.getList().requestFocus(); + } + + private void createPermission() { + Permission permission = allocatable.newPermission(); + allocatable.addPermission( permission ); + model.addElement(permission); + } + + class Listener implements ActionListener, ChangeListener { + public void actionPerformed(ActionEvent evt) { + try { + if (evt.getActionCommand().equals("remove")) { + removePermission(); + } else if (evt.getActionCommand().equals("new")) { + createPermission(); + } else if (evt.getActionCommand().equals("edit")) { + permissionField.setValue( listEdit.getList().getSelectedValue() ); + } + } catch (RaplaException ex) { + showException(ex,getComponent()); + } + } + + public void stateChanged(ChangeEvent evt) { + Object permission = permissionField.getValue(); + model.set( model.indexOf( permission ),permission ); + } + + } + +} + + diff --git a/Rapla/src/org/rapla/gui/internal/edit/PreferencesEditUI.java b/Rapla/src/org/rapla/gui/internal/edit/PreferencesEditUI.java new file mode 100644 index 0000000..419a1f3 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/PreferencesEditUI.java @@ -0,0 +1,263 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.edit; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Insets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSplitPane; +import javax.swing.border.Border; +import javax.swing.border.TitledBorder; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; + +import org.rapla.entities.Named; +import org.rapla.entities.NamedComparator; +import org.rapla.entities.configuration.Preferences; +import org.rapla.framework.PluginDescriptor; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.DefaultPluginOption; +import org.rapla.gui.EditComponent; +import org.rapla.gui.OptionPanel; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.TreeFactory; +import org.rapla.gui.toolkit.RaplaTree; +import org.rapla.gui.toolkit.RaplaWidget; +import org.rapla.plugin.RaplaExtensionPoints; + +public class PreferencesEditUI extends RaplaGUIComponent + implements + RaplaWidget + ,EditComponent + ,ChangeListener +{ + private JSplitPane content = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); + protected TitledBorder selectionBorder; + protected RaplaTree jPanelSelection = new RaplaTree(); + protected JPanel jPanelContainer = new JPanel(); + protected JPanel container = new JPanel(); + JLabel messages = new JLabel(); + JPanel defaultPanel = new JPanel(); + OptionPanel lastOptionPanel; + Preferences preferences; + + /** called during initialization to create the info component */ + public PreferencesEditUI(RaplaContext sm) throws RaplaException { + super( sm); + jPanelContainer.setLayout(new BorderLayout()); + jPanelContainer.add(messages,BorderLayout.SOUTH); + messages.setForeground( Color.red); + Border emptyLineBorder = new Border() { + Insets insets = new Insets(2,0,2,0); + Color COLOR = Color.LIGHT_GRAY; + public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) + { + g.setColor( COLOR ); + g.drawLine(0,1, c.getWidth(), 1); + g.drawLine(0,c.getHeight()-2, c.getWidth(), c.getHeight()-2); + } + + public Insets getBorderInsets( Component c ) + { + return insets; + } + + public boolean isBorderOpaque() + { + return true; + } + }; + content.setBorder( emptyLineBorder); + jPanelContainer.add(content,BorderLayout.CENTER); + jPanelSelection.getTree().setCellRenderer(getTreeFactory().createRenderer()); + jPanelSelection.setToolTipRenderer(getTreeFactory().createTreeToolTipRenderer()); + container.setPreferredSize( new Dimension(700,500)); + content.setLeftComponent(jPanelSelection); + content.setRightComponent(container); + content.setDividerLocation(300); + Border emptyBorder=BorderFactory.createEmptyBorder(4,4,4,4); + selectionBorder = BorderFactory.createTitledBorder(emptyBorder, getString("selection") + ":"); + jPanelSelection.setBorder(selectionBorder); + content.setResizeWeight(0.4); + jPanelSelection.addChangeListener(this); + } + + final private TreeFactory getTreeFactory() { + return (TreeFactory) getService(TreeFactory.ROLE); + } + + protected OptionPanel[] getPluginOptions() throws RaplaException { + Map optionMap = getContainer().lookupServicesFor( RaplaExtensionPoints.PLUGIN_OPTION_PANEL_EXTENSION); + List optionList = new ArrayList(); + List pluginList = (List) getService( PluginDescriptor.PLUGIN_LIST); + for (Iterator it = pluginList.iterator();it.hasNext();) { + final PluginDescriptor plugin = (PluginDescriptor) it.next(); + OptionPanel optionPanel = (OptionPanel) optionMap.get(plugin.getClass().getName()); + if ( optionPanel == null ) { + optionPanel = new DefaultPluginOption(getContext()) { + + public String getDescriptorClassName() { + return plugin.getClass().getName(); + } + + public String getName(Locale locale) { + return plugin.toString(); + } + + }; + } + optionList.add( optionPanel ); + } + sort( optionList); + return (OptionPanel[])optionList.toArray(new OptionPanel[] {}); + } + + public void sort(List list) { + Collections.sort( list, new NamedComparator(getRaplaLocale().getLocale())); + } + + public OptionPanel[] getUserOptions() throws RaplaException { + Map optionMap = getContainer().lookupServicesFor( RaplaExtensionPoints.USER_OPTION_PANEL_EXTENSION ); + List optionList = new ArrayList(optionMap.values()); + sort( optionList); + return (OptionPanel[])optionList.toArray(new OptionPanel[] {}); + } + + public OptionPanel[] getAdminOptions() throws RaplaException { + Map optionMap = getContainer().lookupServicesFor( RaplaExtensionPoints.SYSTEM_OPTION_PANEL_EXTENSION ); + List optionList = new ArrayList(optionMap.values()); + sort( optionList); + return (OptionPanel[])optionList.toArray(new OptionPanel[] {}); + } + + protected JComponent createInfoComponent() { + JPanel panel = new JPanel(); + return panel; + } + + + private void setOptionPanel(OptionPanel optionPanel) throws Exception { + String title = getString("nothing_selected"); + JComponent comp = defaultPanel; + if ( optionPanel != null) { + title = optionPanel.getName( getRaplaLocale().getLocale()); + comp = optionPanel.getComponent(); + } + + TitledBorder titledBorder = new TitledBorder(BorderFactory.createEmptyBorder(4,4,4,4),title); + container.removeAll(); + container.setLayout(new BorderLayout()); + container.setBorder(titledBorder); + container.add( comp,BorderLayout.CENTER); + container.revalidate(); + container.repaint(); + } + + public String getTitle() { + return getString("options"); + } + + /** maps all fields back to the current object.*/ + public void mapToObject() throws RaplaException { + if ( lastOptionPanel != null) + lastOptionPanel.commit(); + } + + public void setObject(Object o) throws RaplaException { + this.preferences = (Preferences) o; + if ( preferences.getOwner() == null) { + messages.setText(getString("restart_options")); + } + TreeFactory f = getTreeFactory(); + DefaultMutableTreeNode root = new DefaultMutableTreeNode(""); + if ( preferences.getOwner() != null) { + Named[] element = getUserOptions(); + for (int i=0; i< element.length; i++) { + root.add( f.newNamedNode( element[i])); + } + } else { + { + Named[] element = getAdminOptions(); + DefaultMutableTreeNode adminRoot = new DefaultMutableTreeNode("admin-options"); + for (int i=0; i< element.length; i++) { + adminRoot.add( f.newNamedNode( element[i])); + } + root.add( adminRoot ); + } + { + Named[] element = getPluginOptions(); + DefaultMutableTreeNode pluginRoot = new DefaultMutableTreeNode("plugins"); + for (int i=0; i< element.length; i++) { + pluginRoot.add( f.newNamedNode( element[i])); + } + root.add( pluginRoot ); + } + } + DefaultTreeModel treeModel = new DefaultTreeModel(root); + jPanelSelection.exchangeTreeModel(treeModel); + } + + public Object getObject() { + return preferences; + } + + public void stateChanged(ChangeEvent evt) { + try { + if ( lastOptionPanel != null) + lastOptionPanel.commit(); + + OptionPanel optionPanel = null; + if ( getSelectedElement() instanceof OptionPanel ) { + optionPanel = (OptionPanel) getSelectedElement(); + if ( optionPanel != null) { + optionPanel.setPreferences( preferences ); + optionPanel.show(); + } + } + lastOptionPanel = optionPanel; + setOptionPanel( lastOptionPanel ); + } catch (Exception ex) { + showException(ex,getComponent()); + } + } + + public Object getSelectedElement() { + return jPanelSelection.getSelectedElement(); + } + + + public JComponent getComponent() { + return jPanelContainer; + } + +} + + + diff --git a/Rapla/src/org/rapla/gui/internal/edit/RaplaFilterBox.java b/Rapla/src/org/rapla/gui/internal/edit/RaplaFilterBox.java new file mode 100644 index 0000000..c5c85a3 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/RaplaFilterBox.java @@ -0,0 +1,36 @@ +package org.rapla.gui.internal.edit; + +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JPanel; + +import org.rapla.components.calendar.RaplaComboBox; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.internal.common.CalendarSelectionModel; + +public class RaplaFilterBox extends RaplaComboBox { + ClassifiableFilterEdit ui; + public RaplaFilterBox(RaplaContext context, CalendarSelectionModel filterObj) throws RaplaException { + super(new TestComponent()); + + boolean isResourceOnly = true; + ui = new ClassifiableFilterEdit( context, isResourceOnly); + JPanel mainPanel = new JPanel(); + + mainPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); + ui.setFilter(filterObj); + + // TODO Auto-generated constructor stub + } + + @Override + protected JComponent getPopupComponent() { + return ui.getComponent(); + } + + static class TestComponent extends JPanel + { + + } +} diff --git a/Rapla/src/org/rapla/gui/internal/edit/RaplaListEdit.java b/Rapla/src/org/rapla/gui/internal/edit/RaplaListEdit.java new file mode 100644 index 0000000..27672b4 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/RaplaListEdit.java @@ -0,0 +1,315 @@ +package org.rapla.gui.internal.edit; + +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; + +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.ListModel; +import javax.swing.UIManager; +import javax.swing.border.EmptyBorder; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.ListDataEvent; +import javax.swing.event.ListDataListener; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import org.rapla.components.calendar.NavButton; +import org.rapla.components.calendar.RaplaArrowButton; +import org.rapla.components.layout.TableLayout; +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.framework.RaplaException; +import org.rapla.gui.toolkit.RaplaButton; +import org.rapla.gui.toolkit.RaplaColorList; +import org.rapla.gui.toolkit.RaplaWidget; + +final public class RaplaListEdit implements + RaplaWidget +{ + boolean coloredBackground = false; + int oldIndex = -1; + + JPanel mainPanel = new JPanel(); + + JLabel statusBar = new JLabel(); + JPanel identifierPanel = new JPanel(); + JLabel identifier = new JLabel(); + JLabel nothingSelectedLabel = new JLabel(); + JScrollPane scrollPane; + + NavButton prev = new NavButton('^'); + NavButton next = new NavButton('v'); + + RaplaArrowButton moveUpButton = new RaplaArrowButton('^', 25); + RaplaArrowButton moveDownButton = new RaplaArrowButton('v', 25); + + Color selectionBackground = UIManager.getColor("List.selectionBackground"); + Color background = UIManager.getColor("List.background"); + + JPanel jointPanel = new JPanel() { + private static final long serialVersionUID = 1L; + + int xa[] = new int[4]; + int ya[] = new int[4]; + public void paint(Graphics g) { + super.paint(g); + Dimension dim = getSize(); + int index = list.getSelectedIndex(); + + Rectangle rect = list.getCellBounds(index,index); + if (rect != null) { + int y = rect.y -scrollPane.getViewport().getViewPosition().y; + int y1= Math.min(dim.height,Math.max(0, y) + scrollPane.getLocation().y); + int y2= Math.min(dim.height,Math.max(0,y + rect.height) + scrollPane.getLocation().y); + xa[0]=0; + ya[0]=y1; + xa[1]=dim.width; + ya[1]=0; + xa[2]=dim.width; + ya[2]=dim.height; + xa[3]=0; + ya[3]=y2; + g.setColor(selectionBackground); + g.fillPolygon(xa,ya,4); + g.setColor(background); + g.drawLine(xa[0],ya[0],xa[1],ya[1]); + g.drawLine(xa[3],ya[3],xa[2],ya[2]); + } + } + }; + JPanel content = new JPanel(); + JPanel detailContainer = new JPanel(); + JPanel editPanel = new JPanel(); + + JList list = new JList() { + private static final long serialVersionUID = 1L; + + public void setModel(ListModel model) { + super.setModel( model ); + model.addListDataListener(new ListDataListener() { + public void contentsChanged(ListDataEvent e) { + modelUpdate(); + } + public void intervalAdded(ListDataEvent e) { + } + public void intervalRemoved(ListDataEvent e) { + } + }); + } + }; + + RaplaButton createNewButton = new RaplaButton(); + RaplaButton removeButton = new RaplaButton(); + + CardLayout cardLayout = new CardLayout(); + private Listener listener = new Listener(); + private ActionListener callback; + + public RaplaListEdit(I18nBundle i18n, JComponent detailContent,ActionListener callback) throws RaplaException { + this.callback = callback; + JPanel toolbar = new JPanel(); + toolbar.setLayout( new BoxLayout( toolbar, BoxLayout.X_AXIS)); + toolbar.add(createNewButton); + toolbar.add(removeButton); + toolbar.add(Box.createHorizontalStrut(5)); + toolbar.add(moveUpButton); + toolbar.add(moveDownButton); + mainPanel.setLayout(new TableLayout(new double[][] { + {TableLayout.PREFERRED,TableLayout.PREFERRED,TableLayout.FILL} + ,{TableLayout.FILL} + })); + jointPanel.setPreferredSize(new Dimension(15,50)); + mainPanel.add(content,"0,0"); + mainPanel.add(jointPanel,"1,0"); + mainPanel.add(editPanel,"2,0"); + editPanel.setLayout(cardLayout); + editPanel.add(nothingSelectedLabel, "0"); + editPanel.add(detailContainer, "1"); + content.setLayout(new BorderLayout()); + content.add(toolbar,BorderLayout.NORTH); + scrollPane = new JScrollPane(list + ,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS + ,JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + scrollPane.setPreferredSize(new Dimension(310,80)); + content.add(scrollPane, BorderLayout.CENTER); + content.add(statusBar, BorderLayout.SOUTH); + detailContainer.setLayout(new BorderLayout()); + editPanel.setBorder(BorderFactory.createRaisedBevelBorder()); + detailContainer.add(identifierPanel,BorderLayout.WEST); + detailContainer.add(detailContent, BorderLayout.CENTER); + //mainPanel.setBorder(new LineBorder(Color.black)); + + identifierPanel.setLayout(new BorderLayout()); + identifierPanel.add(prev,BorderLayout.NORTH); + identifierPanel.add(identifier,BorderLayout.CENTER); + identifierPanel.add(next,BorderLayout.SOUTH); + identifier.setBorder(new EmptyBorder(0,5,0,5)); + + next.addActionListener(listener); + prev.addActionListener(listener); + removeButton.addActionListener(listener); + createNewButton.addActionListener(listener); + moveUpButton.addActionListener(listener); + moveDownButton.addActionListener(listener); + + scrollPane.getViewport().addChangeListener(listener); + + // list.setDragEnabled(true); + list.addMouseListener(listener); + + list.addListSelectionListener(listener); + + modelUpdate(); + + createNewButton.setText(i18n.getString("new")); + createNewButton.setIcon(i18n.getIcon("icon.new")); + removeButton.setIcon(i18n.getIcon("icon.delete")); + removeButton.setText(i18n.getString("delete")); + nothingSelectedLabel.setHorizontalAlignment(JLabel.CENTER); + nothingSelectedLabel.setText(i18n.getString("nothing_selected")); + + } + + public JComponent getComponent() { + return mainPanel; + } + + public JLabel getStatusBar() { + return statusBar; + } + + public JList getList() { + return list; + } + + public void setListDimension(Dimension d) { + scrollPane.setPreferredSize(d); + } + + public void setMoveButtonVisible(boolean visible) { + moveUpButton.setVisible(visible); + moveDownButton.setVisible(visible); + } + + public int getSelectedIndex() { + return list.getSelectedIndex(); + } + + public void select(int index) { + list.setSelectedIndex(index); + if (index >=0) { + list.ensureIndexIsVisible(index); + } + } + + public void setColoredBackgroundEnabled(boolean enable) { + coloredBackground = enable; + } + + public boolean isColoredBackgroundEnabled() { + return coloredBackground; + } + + private void modelUpdate() { + removeButton.setEnabled(list.getMinSelectionIndex() >=0); + moveUpButton.setEnabled(list.getMinSelectionIndex() > 0); + moveDownButton.setEnabled(list.getMinSelectionIndex() >= 0 && + list.getMaxSelectionIndex() < (list.getModel().getSize() -1) ); + jointPanel.repaint(); + } + + + private void editSelectedEntry() { + Object selected = list.getSelectedValue(); + if (selected == null) { + cardLayout.first(editPanel); + return; + } else { + cardLayout.last(editPanel); + int index = getSelectedIndex(); + next.setEnabled((index + 1)0); + Color color = RaplaColorList.getAppointmentColor(0); + if ( isColoredBackgroundEnabled() ) { + color = RaplaColorList.getAppointmentColor(index); + } + identifierPanel.setBackground(color); + identifier.setText(String.valueOf(index + 1)); + + callback.actionPerformed(new ActionEvent(this + ,ActionEvent.ACTION_PERFORMED + ,"edit" + ) + ); + } + } + + class Listener extends MouseAdapter implements ListSelectionListener,ActionListener,ChangeListener { + public void actionPerformed(ActionEvent evt) { + if (evt.getSource() == next) { + select(Math.min(list.getModel().getSize()-1, getSelectedIndex() + 1)); + } else if (evt.getSource() == prev) { + select(Math.max(0, getSelectedIndex()-1)); + } + if (evt.getSource() == removeButton) { + callback.actionPerformed(new ActionEvent(RaplaListEdit.this + ,ActionEvent.ACTION_PERFORMED + ,"remove" + ) + ); + } else if (evt.getSource() == createNewButton) { + callback.actionPerformed(new ActionEvent(RaplaListEdit.this + ,ActionEvent.ACTION_PERFORMED + ,"new" + ) + ); + } else if (evt.getSource() == moveUpButton) { + callback.actionPerformed(new ActionEvent(RaplaListEdit.this + ,ActionEvent.ACTION_PERFORMED + ,"moveUp" + ) + ); + } else if (evt.getSource() == moveDownButton) { + callback.actionPerformed(new ActionEvent(RaplaListEdit.this + ,ActionEvent.ACTION_PERFORMED + ,"moveDown" + ) + ); + } + } + + public void valueChanged(ListSelectionEvent evt) { + //if (evt.getValueIsAdjusting()) + //return; + int index = getSelectedIndex(); + if (index != oldIndex) { + oldIndex = index; + editSelectedEntry(); + modelUpdate(); + } + } + + public void stateChanged(ChangeEvent evt) { + if (evt.getSource() == scrollPane.getViewport()) { + jointPanel.repaint(); + } + } + } +} + + + diff --git a/Rapla/src/org/rapla/gui/internal/edit/RaplaTreeEdit.java b/Rapla/src/org/rapla/gui/internal/edit/RaplaTreeEdit.java new file mode 100644 index 0000000..261b91b --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/RaplaTreeEdit.java @@ -0,0 +1,206 @@ +package org.rapla.gui.internal.edit; + +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; + +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTree; +import javax.swing.UIManager; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.TreeModelEvent; +import javax.swing.event.TreeModelListener; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; + +import org.rapla.components.layout.TableLayout; +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.framework.RaplaException; +import org.rapla.gui.toolkit.RaplaWidget; +import org.rapla.gui.toolkit.RecursiveNode; + +final class RaplaTreeEdit implements + RaplaWidget +{ + int oldIndex = -1; + + JPanel mainPanel = new JPanel(); + + JLabel nothingSelectedLabel = new JLabel(); + JScrollPane scrollPane; + + Color selectionBackground = UIManager.getColor("List.selectionBackground"); + Color background = UIManager.getColor("List.background"); + + JPanel jointPanel = new JPanel() { + private static final long serialVersionUID = 1L; + + int xa[] = new int[4]; + int ya[] = new int[4]; + public void paint(Graphics g) { + super.paint(g); + TreePath selectedPath = tree.getPathForRow( getSelectedIndex() ); + Rectangle rect = tree.getPathBounds( selectedPath ); + Dimension dim = getSize(); + if (rect != null) { + int y = rect.y -scrollPane.getViewport().getViewPosition().y; + int y1= Math.min(dim.height,Math.max(0, y) + scrollPane.getLocation().y); + int y2= Math.min(dim.height,Math.max(0,y + rect.height) + scrollPane.getLocation().y); + xa[0]=0; + ya[0]=y1; + xa[1]=dim.width; + ya[1]=0; + xa[2]=dim.width; + ya[2]=dim.height; + xa[3]=0; + ya[3]=y2; + g.setColor(selectionBackground); + g.fillPolygon(xa,ya,4); + g.setColor(background); + g.drawLine(xa[0],ya[0],xa[1],ya[1]); + g.drawLine(xa[3],ya[3],xa[2],ya[2]); + } + } + }; + JPanel content = new JPanel(); + JPanel detailContainer = new JPanel(); + JPanel editPanel = new JPanel(); + + JTree tree = new JTree() { + private static final long serialVersionUID = 1L; + + public void setModel(TreeModel model) { + super.setModel( model ); + model.addTreeModelListener(new TreeModelListener() { + public void treeNodesChanged(TreeModelEvent e) { + modelUpdate(); + } + public void treeNodesInserted(TreeModelEvent e) { + } + public void treeNodesRemoved(TreeModelEvent e) { + } + public void treeStructureChanged(TreeModelEvent e) { + } + }); + } + }; + + CardLayout cardLayout = new CardLayout(); + private Listener listener = new Listener(); + private ActionListener callback; + I18nBundle i18n; + + public RaplaTreeEdit(I18nBundle i18n,JComponent detailContent,ActionListener callback) throws RaplaException { + this.i18n = i18n; + this.callback = callback; + mainPanel.setLayout(new TableLayout(new double[][] { + {TableLayout.PREFERRED,TableLayout.PREFERRED,TableLayout.FILL} + ,{TableLayout.FILL} + })); + jointPanel.setPreferredSize(new Dimension(20,50)); + mainPanel.add(content,"0,0"); + mainPanel.add(jointPanel,"1,0"); + mainPanel.add(editPanel,"2,0"); + editPanel.setLayout(cardLayout); + editPanel.add(nothingSelectedLabel, "0"); + editPanel.add(detailContainer, "1"); + content.setLayout(new BorderLayout()); + scrollPane = new JScrollPane(tree + ,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS + ,JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + scrollPane.setPreferredSize(new Dimension(310,80)); + content.add(scrollPane, BorderLayout.CENTER); + detailContainer.setLayout(new BorderLayout()); + editPanel.setBorder(BorderFactory.createRaisedBevelBorder()); + detailContainer.add(detailContent, BorderLayout.CENTER); + scrollPane.getViewport().addChangeListener(listener); + + tree.addMouseListener(listener); + tree.addTreeSelectionListener(listener); + + modelUpdate(); + nothingSelectedLabel.setHorizontalAlignment(JLabel.CENTER); + nothingSelectedLabel.setText(i18n.getString("nothing_selected")); + } + + public JComponent getComponent() { + return mainPanel; + } + + public JTree getTree() { + return tree; + } + + public void setListDimension(Dimension d) { + scrollPane.setPreferredSize(d); + } + + public int getSelectedIndex() { + return tree.getMinSelectionRow(); + } + + public void select(int index) { + tree.setSelectionRow(index); + if (index >=0) { + TreePath selectedPath = tree.getPathForRow(index); + tree.makeVisible( selectedPath ); + } + } + + private void modelUpdate() { + jointPanel.repaint(); + } + + public Object getSelectedValue() { + TreePath treePath = tree.getSelectionPath(); + if (treePath == null) + return null; + return ((RecursiveNode)treePath.getLastPathComponent()).getUserObject(); + } + + private void editSelectedEntry() { + Object selected = getSelectedValue(); + if (selected == null) { + cardLayout.first(editPanel); + return; + } else { + cardLayout.last(editPanel); + callback.actionPerformed(new ActionEvent(this + ,ActionEvent.ACTION_PERFORMED + ,"edit" + ) + ); + } + } + + class Listener extends MouseAdapter implements TreeSelectionListener,ChangeListener { + public void valueChanged(TreeSelectionEvent evt) { + int index = getSelectedIndex(); + if (index != oldIndex) { + oldIndex = index; + editSelectedEntry(); + modelUpdate(); + } + } + + public void stateChanged(ChangeEvent evt) { + if (evt.getSource() == scrollPane.getViewport()) { + jointPanel.repaint(); + } + } + } +} + diff --git a/Rapla/src/org/rapla/gui/internal/edit/TextField.java b/Rapla/src/org/rapla/gui/internal/edit/TextField.java new file mode 100644 index 0000000..15aeb0d --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/TextField.java @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.edit; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; + +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.text.JTextComponent; + +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +public class TextField extends AbstractEditField implements ActionListener,FocusListener,KeyListener { + JTextComponent field; + JScrollPane scrollPane; + Object oldValue; + public TextField(RaplaContext sm,String fieldName) throws RaplaException { + this( sm,fieldName, 1, DEFAULT_LENGTH); + } + + public TextField(RaplaContext sm,String fieldName, int rows, int columns) throws RaplaException { + super( sm); + setFieldName( fieldName ); + if ( rows > 1 ) { + field = new JTextArea(); + scrollPane = new JScrollPane( field, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + ((JTextArea) field).setColumns( columns); + ((JTextArea) field).setRows( rows ); + } else { + field = new JTextField( columns); + } + addCopyPaste( field); + field.addFocusListener(this); + field.addKeyListener(this); + setValue(""); + } + + protected Object getValue() { + return field.getText().trim(); + } + + protected void setValue(Object object) { + if (object == null) + object = ""; + field.setText((String)object); + oldValue = (String) object; + } + public JComponent getComponent() { + if ( scrollPane != null ) { + return scrollPane; + } else { + return field; + } + } + + public void actionPerformed(ActionEvent evt) { + if (field.getText().equals(oldValue)) + return; + oldValue = field.getText(); + fireContentChanged(); + } + + public void focusLost(FocusEvent evt) { + if (field.getText().equals(oldValue)) + return; + oldValue = field.getText(); + fireContentChanged(); + } + + public void focusGained(FocusEvent evt) { + Component focusedComponent = evt.getComponent(); + Component parent = focusedComponent.getParent(); + if(parent instanceof JPanel) { + ((JPanel)parent).scrollRectToVisible(focusedComponent.getBounds(null)); + } + } + + public void keyPressed(KeyEvent evt) {} + public void keyTyped(KeyEvent evt) {} + public void keyReleased(KeyEvent evt) { + if (field.getText().equals(oldValue)) + return; + oldValue = field.getText(); + fireContentChanged(); + } + +} + diff --git a/Rapla/src/org/rapla/gui/internal/edit/UserEditUI.java b/Rapla/src/org/rapla/gui/internal/edit/UserEditUI.java new file mode 100644 index 0000000..2fa2c4d --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/UserEditUI.java @@ -0,0 +1,389 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.edit; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.swing.DefaultListCellRenderer; +import javax.swing.DefaultListModel; +import javax.swing.JComponent; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JToolBar; +import javax.swing.JTree; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; + +import org.rapla.entities.Category; +import org.rapla.entities.User; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.Classifiable; +import org.rapla.entities.dynamictype.Classification; +import org.rapla.entities.dynamictype.ClassificationFilter; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.dynamictype.DynamicTypeAnnotations; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.TreeFactory; +import org.rapla.gui.toolkit.DialogUI; +import org.rapla.gui.toolkit.RaplaButton; +import org.rapla.gui.toolkit.RaplaTree; + +/**************************************************************** + * This is the controller-class for the User-Edit-Panel * + ****************************************************************/ + +/*User + 1. username, string + 2. name,string + 3. email,string, + 4. isadmin,boolean +*/ + +class UserEditUI extends AbstractEditUI { + + /** + * @param sm + * @throws RaplaException + */ + public UserEditUI(RaplaContext sm) throws RaplaException { + super(sm); + EditField[] fields = new EditField[] { + new TextField(sm,"username") + // ,new BooleanField(sm,"maptoperson") + ,new PersonSelectField(sm) + ,new TextField(sm,"name") + ,new TextField(sm,"email") + ,new BooleanField(sm,"admin") + ,new GroupListField(sm) + }; + setFields(fields); + } + + class GroupListField extends AbstractEditField implements ChangeListener, ActionListener { + User user; + DefaultListModel model = new DefaultListModel(); + + JPanel panel = new JPanel(); + JToolBar toolbar = new JToolBar(); + + CategorySelectField newCategory; + RaplaButton removeButton = new RaplaButton(RaplaButton.SMALL); + RaplaButton newButton = new RaplaButton(RaplaButton.SMALL); + JList list = new JList(); + + /** + * @param sm + * @throws RaplaException + */ + public GroupListField(RaplaContext sm) throws RaplaException { + super(sm); + final Category rootCategory = getQuery().getUserGroupsCategory(); + if ( rootCategory == null ) + return; + newCategory = new CategorySelectField(sm,"group", rootCategory ); + newCategory.setUseNullCategory( false); + toolbar.add( newButton ); + toolbar.add( removeButton ); + toolbar.setFloatable( false ); + panel.setLayout( new BorderLayout() ); + panel.add( toolbar, BorderLayout.NORTH ); + final JScrollPane jScrollPane = new JScrollPane(list, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + jScrollPane.setPreferredSize( new Dimension( 300, 150 ) ); + panel.add( jScrollPane, BorderLayout.CENTER ); + newButton.setText( getString( "group" ) + " " + getString( "add" ) ); + removeButton.setText( getString( "group" ) + " " + getString( "remove" ) ); + newButton.setIcon( getIcon( "icon.new" ) ); + removeButton.setIcon( getIcon( "icon.remove" ) ); + newCategory.addChangeListener( this ); + newButton.addActionListener( this ); + removeButton.addActionListener( this ); + + list.setCellRenderer(new DefaultListCellRenderer() { + private static final long serialVersionUID = 1L; + + public Component getListCellRendererComponent(JList list, + Object value, + int index, + boolean isSelected, + boolean cellHasFocus) + { + if ( value != null ) { + Category category = (Category) value; + value = category.getPath( rootCategory + , getI18n().getLocale()); + } + return super.getListCellRendererComponent(list,value,index,isSelected,cellHasFocus); + } + } + ); + } + + public JComponent getComponent() { + return panel; + } + + public boolean isBlock() { + return true; + } + + public boolean isVariableSized() { + return false; + } + + public void mapFrom(Object o) throws RaplaException { + user = (User) o; + model.clear(); + Category[] groups = user.getGroups();; + for ( int i = 0; i < groups.length; i++ ) { + model.addElement( groups[i] ); + } + list.setModel(model); + } + + protected Object getValue() { + return null; + } + + protected void setValue(Object object) { + } + + public void mapTo(Object o) throws RaplaException { + } + + public void actionPerformed(ActionEvent evt) { + if ( evt.getSource() == newButton) + { + try { + newCategory.showDialog(newButton); + } catch (RaplaException ex) { + showException(ex,newButton); + } + } + if ( evt.getSource() == removeButton) + { + for ( Object value: list.getSelectedValues()) + { + Category group = (Category) value; + if (group != null) { + user.removeGroup( group ); + model.removeElement( group ); + fireContentChanged(); + } + } + } + + } + + public void stateChanged(ChangeEvent evt) { + Category newGroup = (Category) newCategory.getValue(); + if ( ! user.belongsTo( newGroup ) ) { + user.addGroup( newGroup ); + model.addElement( newGroup ); + fireContentChanged(); + } + } + } + + class PersonSelectField extends AbstractEditField implements ChangeListener, ActionListener { + User user; + + JPanel panel = new JPanel(); + JToolBar toolbar = new JToolBar(); + + RaplaButton newButton = new RaplaButton(RaplaButton.SMALL); + RaplaButton removeButton = new RaplaButton(RaplaButton.SMALL); + + /** + * @param sm + * @throws RaplaException + */ + public PersonSelectField(RaplaContext sm) throws RaplaException { + super(sm); + final Category rootCategory = getQuery().getUserGroupsCategory(); + if ( rootCategory == null ) + return; + toolbar.add( newButton ); + toolbar.add( removeButton ); + toolbar.setFloatable( false ); + panel.setLayout( new BorderLayout() ); + panel.add( toolbar, BorderLayout.NORTH ); + newButton.addActionListener( this ); + removeButton.addActionListener( this ); + removeButton.setText( getString("remove") ); + removeButton.setIcon( getIcon( "icon.remove" ) ); + newButton.setText( getString("add") ); + newButton.setIcon( getIcon( "icon.new" ) ); + + } + + private void updateButton() { + final boolean personSet = user != null && user.getPerson() != null; + removeButton.setEnabled( personSet) ; + newButton.setEnabled( !personSet) ; + + fields[2].getComponent().setEnabled( !personSet); + fields[3].getComponent().setEnabled( !personSet); + + } + + public JComponent getComponent() { + return panel; + } + + public boolean isBlock() { + return false; + } + + public boolean isVariableSized() { + return false; + } + + public String getName() + { + return getString("bind_with_person"); + } + public void mapFrom(Object o) throws RaplaException { + user = (User) o; + updateButton(); + + } + + protected Object getValue() { + return null; + } + + protected void setValue(Object object) { + } + + public void mapTo(Object o) throws RaplaException { + } + + public void actionPerformed(ActionEvent evt) { + if ( evt.getSource() == newButton) + { + + try { + showAddDialog(); + } catch (RaplaException ex) { + showException(ex,newButton); + } + + } + + if ( evt.getSource() == removeButton) + { + user.setPerson( null ); + updateButton(); + } + + } + + public void stateChanged(ChangeEvent evt) { + } + + private void showAddDialog() throws RaplaException { + final DialogUI dialog; + RaplaTree treeSelection = new RaplaTree(); + treeSelection.setMultiSelect(true); + final TreeFactory treeFactory = getTreeFactory(); + treeSelection.getTree().setCellRenderer(treeFactory.createRenderer()); + + final DynamicType[] personTypes = getQuery().getDynamicTypes(DynamicTypeAnnotations.VALUE_PERSON_CLASSIFICATION); + List filters = new ArrayList(); + for (DynamicType personType: personTypes) + { + if ( personType.getAttribute("email") != null) + { + final ClassificationFilter filter = personType.newClassificationFilter(); + filters.add( filter); + } + } + final Allocatable[] allocatables = getQuery().getAllocatables(filters.toArray(ClassificationFilter.CLASSIFICATIONFILTER_ARRAY)); + List allocatablesWithEmail = new ArrayList(); + for ( Allocatable a: allocatables) + { + final Classification classification = a.getClassification(); + final Attribute attribute = classification.getAttribute("email"); + if ( attribute != null) + { + final String email = (String)classification.getValue(attribute); + if (email != null && email.length() > 0) + { + allocatablesWithEmail.add( a ); + } + } + } + final Classifiable[] allocatableArray = allocatablesWithEmail.toArray(Classifiable.CLASSIFIABLE_ARRAY); + treeSelection.exchangeTreeModel(treeFactory.createClassifiableModel(allocatableArray)); + treeSelection.setMinimumSize(new java.awt.Dimension(300, 200)); + treeSelection.setPreferredSize(new java.awt.Dimension(400, 260)); + + + dialog = DialogUI.create( + getContext() + ,getComponent() + ,true + ,treeSelection + ,new String[] { getString("apply"),getString("cancel")}); + final JTree tree = treeSelection.getTree(); + tree.addMouseListener(new MouseAdapter() { + // End dialog when a leaf is double clicked + public void mousePressed(MouseEvent e) { + TreePath selPath = tree.getPathForLocation(e.getX(), e.getY()); + if (selPath != null && e.getClickCount() == 2) { + final Object lastPathComponent = selPath.getLastPathComponent(); + if (((TreeNode) lastPathComponent).isLeaf() ) + dialog.getButton(0).doClick(); + } + else if (selPath != null && e.getClickCount() == 1) { + final Object lastPathComponent = selPath.getLastPathComponent(); + if (((TreeNode) lastPathComponent).isLeaf() ) + dialog.getButton(0).setEnabled(true); + else + dialog.getButton(0).setEnabled(false); + } + } + }); + dialog.setTitle(getName()); + dialog.start(); + if (dialog.getSelectedIndex() == 0) { + Iterator it = treeSelection.getSelectedElements().iterator(); + while (it.hasNext()) { + user.setPerson((Allocatable) it.next()); + fields[2].mapFrom( user); + fields[3].mapFrom( user); + updateButton(); + } + } + } + } + + final private TreeFactory getTreeFactory() { + return (TreeFactory) getService(TreeFactory.ROLE); + } + +} diff --git a/Rapla/src/org/rapla/gui/internal/edit/package.html b/Rapla/src/org/rapla/gui/internal/edit/package.html new file mode 100644 index 0000000..5f694f3 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/package.html @@ -0,0 +1,6 @@ + +Edit-dialogs for all rapla entities. + + + + diff --git a/Rapla/src/org/rapla/gui/internal/edit/reservation/AbstractAppointmentEditor.java b/Rapla/src/org/rapla/gui/internal/edit/reservation/AbstractAppointmentEditor.java new file mode 100644 index 0000000..17e72c0 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/reservation/AbstractAppointmentEditor.java @@ -0,0 +1,61 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.edit.reservation; + +import java.util.ArrayList; + +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.entities.domain.Appointment; +/** Provides AppointmentListener handling.*/ +public class AbstractAppointmentEditor extends RaplaGUIComponent { + ArrayList listenerList = new ArrayList(); + + public AbstractAppointmentEditor(RaplaContext sm) throws RaplaException { + super(sm); + } + + public void addAppointmentListener(AppointmentListener listener) { + listenerList.add(listener); + } + + public void removeAppointmentListener(AppointmentListener listener) { + listenerList.remove(listener); + } + + public AppointmentListener[] getAppointmentListeners() { + return (AppointmentListener[])listenerList.toArray(new AppointmentListener[]{}); + } + + protected void fireAppointmentAdded(Appointment appointment) { + AppointmentListener[] listeners = getAppointmentListeners(); + for (int i = 0;i + * GUI for editing the allocations of a reservation. Presents two TreeTables. The left one displays + * all available Resources and Persons the right one all selected Resources and Persons. + *

    + *

    + * The second column of the first table contains information about the availability on the + * appointments of the reservation. In the second column of the second table the user can add + * special Restrictions on the selected Resources and Persons. + *

    + * + * @see Reservation + * @see Allocatable + */ +public class AllocatableSelection extends RaplaGUIComponent implements AppointmentListener, PopupListener, RaplaWidget +{ + JSplitPane content = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); + JPanel leftPanel = new JPanel(); + JTreeTable completeTable; + RaplaButton btnAdd = new RaplaButton(RaplaButton.SMALL); + RaplaButton btnCalendar1 = new RaplaButton(RaplaButton.SMALL); + JPanel rightPanel = new JPanel(); + JTreeTable selectedTable; + RaplaButton btnRemove = new RaplaButton(RaplaButton.SMALL); + RaplaButton btnCalendar2 = new RaplaButton(RaplaButton.SMALL); + + Reservation mutableReservation; + + AllocatablesModel completeModel = new CompleteModel(); + AllocatablesModel selectedModel = new SelectedModel(); + AppointmentCellEditor appointmentCellEditor; + Map appointmentMap = new HashMap(); + Appointment[] appointments; + String[] appointmentStrings; + String[] appointmentIndexStrings; + + CalendarSelectionModel calendarModel; + EventListenerList listenerList = new EventListenerList(); + Listener listener = new Listener(); + + //FilterAction filterAction; + AllocatableAction addAction; + AllocatableAction removeAction; + AllocatableAction calendarAction1; + AllocatableAction calendarAction2; + + User user; + + public AllocatableSelection(RaplaContext sm) throws RaplaException + { + this(sm, false); + } + + public AllocatableSelection(RaplaContext sm, boolean addCalendarButton) throws RaplaException + { + super(sm); + double pre = TableLayout.PREFERRED; + double fill = TableLayout.FILL; + double tableSize[][] = { { pre, 12, pre, 3, fill, pre}, // Columns + { pre, fill } }; // Rows + leftPanel.setLayout(new TableLayout(tableSize)); + + if (addCalendarButton) + leftPanel.add(btnCalendar1, "0,0,l,f"); + leftPanel.add(btnAdd, "5,0,r,f"); + rightPanel.setLayout(new TableLayout(tableSize)); + rightPanel.add(btnRemove, "0,0,l,f"); + if (addCalendarButton) + rightPanel.add(btnCalendar2, "2,0,c,c"); + content.setLeftComponent(leftPanel); + content.setRightComponent(rightPanel); + content.setResizeWeight(0.3); + + appointmentCellEditor = new AppointmentCellEditor(new RestrictionTextField()); + + btnAdd.setEnabled(false); + btnCalendar1.setEnabled(false); + btnRemove.setEnabled(false); + btnCalendar2.setEnabled(false); + + addAction = new AllocatableAction("add"); + removeAction = new AllocatableAction("remove"); + calendarAction1 = new AllocatableAction("calendar1"); + calendarAction2 = new AllocatableAction("calendar2"); + + btnAdd.setAction(addAction); + btnRemove.setAction(removeAction); + btnCalendar1.setAction(calendarAction1); + btnCalendar2.setAction(calendarAction2); + + completeTable = new JTreeTable(completeModel); + Color tableBackground = completeTable.getTree().getBackground(); + JScrollPane leftScrollpane = new JScrollPane(completeTable); + leftScrollpane.getViewport().setBackground(tableBackground); + leftPanel.add(leftScrollpane, "0,1,5,1,f,f"); + completeTable.setGridColor(RaplaColorList.darken(tableBackground, 20)); + completeTable.setToolTipRenderer(new RaplaToolTipRenderer()); + completeTable.getSelectionModel().addListSelectionListener(listener); + completeTable.setDefaultRenderer(Allocatable.class, new AllocationCellRenderer()); + completeTable.addMouseListener(listener); + + selectedTable = new JTreeTable(selectedModel); + JScrollPane rightScrollpane = new JScrollPane(selectedTable); + rightScrollpane.getViewport().setBackground(tableBackground); + rightPanel.add(rightScrollpane, "0,1,5,1,f,f"); + selectedTable.setToolTipRenderer(new RaplaToolTipRenderer()); + selectedTable.getSelectionModel().addListSelectionListener(listener); + selectedTable.setGridColor(RaplaColorList.darken(tableBackground, 20)); + selectedTable.setDefaultRenderer(Appointment[].class, new RestrictionCellRenderer()); + selectedTable.addMouseListener(listener); + + selectedTable.setDefaultEditor(Appointment[].class, appointmentCellEditor); + completeTable.getTree().setCellRenderer(new AllocationTreeCellRenderer(false)); + selectedTable.getTree().setCellRenderer(new AllocationTreeCellRenderer(true)); + completeTable.getColumnModel().getColumn(0).setMinWidth(60); + completeTable.getColumnModel().getColumn(0).setPreferredWidth(120); + completeTable.getColumnModel().getColumn(1).sizeWidthToFit(); + selectedTable.getColumnModel().getColumn(0).setMinWidth(60); + selectedTable.getColumnModel().getColumn(0).setPreferredWidth(120); + selectedTable.getColumnModel().getColumn(1).sizeWidthToFit(); + content.setDividerLocation(0.3); + + calendarModel = (CalendarSelectionModel) ((CalendarSelectionModel) getService(CalendarSelectionModel.ROLE)).clone(); + FilterEdit filter = new FilterEdit( sm, calendarModel); + leftPanel.add(filter.getButton(), "4,0,r,f"); +// filterAction = new FilterAction(getContext(), getComponent(), null); +// filterAction.setFilter(calendarModel); +// filterAction.setResourceOnly(true); + } + + public void addChangeListener(ChangeListener listener) + { + listenerList.add(ChangeListener.class, listener); + } + + public void removeChangeListener(ChangeListener listener) + { + listenerList.remove(ChangeListener.class, listener); + } + + final private TreeFactory getTreeFactory() + { + return (TreeFactory) getService(TreeFactory.ROLE); + } + + protected void fireAllocationsChanged() + { + ChangeEvent evt = new ChangeEvent(this); + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length - 2; i >= 0; i -= 2) + { + if (listeners[i] == ChangeListener.class) + { + ((ChangeListener) listeners[i + 1]).stateChanged(evt); + } + } + } + + public void refresh(ModificationEvent evt) throws RaplaException + { + boolean updateBindings = false; + { + if (evt.isModified(Allocatable.TYPE)) + { + updateBindings = true; + completeModel.setAllocatables(getAllAllocatables(), completeTable.getTree()); + } + } + { + Set allocatables = new HashSet(selectedModel.getAllocatables()); + Set removed = evt.getRemoved(allocatables); + Set changed = evt.getChanged(allocatables); + if (changed.size() > 0 || removed.size() > 0) + { + updateBindings = true; + Iterator it = removed.iterator(); + while (it.hasNext()) + { + Object obj = it.next(); + mutableReservation.removeAllocatable((Allocatable) obj); + allocatables.remove(obj); + } + + selectedModel.setAllocatables(allocatables, selectedTable.getTree()); + } + updateButtons(); + } + if (updateBindings || evt.isModified(Reservation.TYPE)) + updateBindings(mutableReservation.getAppointments()); + + } + + /** Implementation of appointment listener */ + public void appointmentAdded(Appointment appointment) + { + setAppointments(mutableReservation.getAppointments()); + updateBindings(new Appointment[] { appointment }); + } + + public void appointmentChanged(Appointment appointment) + { + setAppointments(mutableReservation.getAppointments()); + updateBindings(new Appointment[] { appointment }); + } + + private void updateBindings(Appointment[] appointments) + { + try + { + // System.out.println("getting allocated resources"); + for (int i = 0; i < appointments.length; i++) + { + Appointment appointment = appointments[i]; + Set allocatables = new HashSet(Arrays.asList(getQuery().getAllocatableBindings(appointment))); + appointmentMap.put(appointment, allocatables); + } + completeModel.treeDidChange(); + selectedModel.treeDidChange(); + } + catch (RaplaException ex) + { + showException(ex, content); + } + } + + public void appointmentRemoved(Appointment appointment) + { + appointmentMap.remove(appointment); + setAppointments(mutableReservation.getAppointments()); + updateBindings(appointments); + } + + public JComponent getComponent() + { + return content; + } + + private List getAllocatableList(Allocatable[] allocatables) throws RaplaException + { + List result = Arrays.asList(allocatables); + return result; + } + + private List getAllAllocatables() throws RaplaException + { + Allocatable[] allocatables = getQuery().getAllocatables(calendarModel.getAllocatableFilter()); + return getAllocatableList(allocatables); + + } + + private List getAllocated() throws RaplaException + { + return getAllocatableList(mutableReservation.getAllocatables()); + } + + private boolean bWorkaround = false; // Workaround for Bug ID 4480264 on developer.java.sun.com + + public void setReservation(Reservation mutableReservation) throws RaplaException + { + this.mutableReservation = mutableReservation; + this.user = getUser(); + //filter = getQuery().getAllocatableFilter("*"); + setAppointments(mutableReservation.getAppointments()); + List allocatableList = getAllAllocatables(); + completeModel.setAllocatables(allocatableList); + // Expand allocatableTree if only one DynamicType + final CalendarModel calendarModel = (CalendarModel) getService(CalendarModel.ROLE); + completeModel.expandObjects(calendarModel.getSelectedObjectsAndChildren(), completeTable.getTree()); + selectedModel.setAllocatables(getAllocated(), selectedTable.getTree()); + updateBindings(appointments); + updateButtons(); + JTree tree = selectedTable.getTree(); + for (int i = 0; i < tree.getRowCount(); i++) + tree.expandRow(i); + + // Workaround for Bug ID 4480264 on developer.java.sun.com + bWorkaround = true; + if (selectedTable.getRowCount() > 0) + { + selectedTable.editCellAt(1, 1); + selectedTable.editCellAt(1, 0); + } + bWorkaround = false; + //filterAction.removePropertyChangeListener(listener); +// filterAction.addPropertyChangeListener(listener); +// btnFilter.setAction(filterAction); + // We have to add this after processing, because the Adapter in the JTreeTable does the same + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + completeModel.selectObjects(calendarModel.getSelectedObjects(), completeTable.getTree()); + } + }); + } + + private void setAppointments(Appointment[] appointments) + { + this.appointments = appointments; + this.appointmentStrings = new String[appointments.length]; + this.appointmentIndexStrings = new String[appointments.length]; + for (int i = 0; i < appointments.length; i++) + { + this.appointmentStrings[i] = getAppointmentFormater().getVeryShortSummary(appointments[i]); + this.appointmentIndexStrings[i] = getRaplaLocale().formatNumber(i + 1); + } + } + + private boolean isAllocatableSelected(JTreeTable table) + { + return isElementSelected(table, true); + } + + private boolean isElementSelected(JTreeTable table, boolean allocatablesOnly) + { + int start = table.getSelectionModel().getMinSelectionIndex(); + int end = table.getSelectionModel().getMaxSelectionIndex(); + if (start >= 0) + { + for (int i = start; i <= end; i++) + { + TreePath path = table.getTree().getPathForRow(i); + if (path != null && (!allocatablesOnly || ((DefaultMutableTreeNode) path.getLastPathComponent()).getUserObject() instanceof Allocatable)) + return true; + } + } + return false; + } + + public Set getMarkedAllocatables() + { + return new HashSet(getSelectedAllocatables(completeTable.getTree())); + } + + protected Collection getSelectedAllocatables(JTree tree) + { + return getSelectedElements(tree, true); + } + + protected Collection getSelectedElements(JTree tree, boolean allocatablesOnly) + { + TreePath[] path = tree.getSelectionPaths(); + int size = 0; + if (path != null) + size = path.length; + Collection list = new ArrayList(); + for (int i = 0; i < size; i++) + { + DefaultMutableTreeNode node = (DefaultMutableTreeNode) path[i].getLastPathComponent(); + Object obj = node.getUserObject(); + if (obj != null && (!allocatablesOnly || obj instanceof Allocatable)) + list.add(obj); + } + return list; + } + + protected void remove(Collection elements) throws RaplaException + { + Iterator it = elements.iterator(); + boolean bChanged = false; + while (it.hasNext()) + { + Allocatable a = (Allocatable) it.next(); + if (mutableReservation.hasAllocated(a)) + { + mutableReservation.removeAllocatable(a); + bChanged = true; + } + } + if (bChanged) + { + selectedModel.setAllocatables(getAllocated(), selectedTable.getTree()); + } + fireAllocationsChanged(); + } + + protected void add(Collection elements) throws RaplaException + { + Iterator it = elements.iterator(); + boolean bChanged = false; + while (it.hasNext()) + { + Allocatable a = (Allocatable) it.next(); + if (!mutableReservation.hasAllocated(a)) + { + mutableReservation.addAllocatable(a); + bChanged = true; + } + } + if (bChanged) + { + selectedModel.setAllocatables(getAllocated(), selectedTable.getTree()); + selectedModel.expandObjects(elements, selectedTable.getTree()); + } + fireAllocationsChanged(); + } + + private Date findFirstStart() + { + Date firstStart = null; + for (int i = 0; i < appointments.length; i++) + if (firstStart == null || appointments[i].getStart().before(firstStart)) + firstStart = appointments[i].getStart(); + return firstStart; + } + + private void updateButtons() + { + { + boolean enable = isElementSelected(completeTable, false); + calendarAction1.setEnabled(enable); + enable = enable && isAllocatableSelected(completeTable); + addAction.setEnabled(enable); + } + { + boolean enable = isElementSelected(selectedTable, false); + calendarAction2.setEnabled(enable); + enable = enable && isAllocatableSelected(selectedTable); + removeAction.setEnabled(enable); + } + } + + class Listener extends MouseAdapter implements ListSelectionListener, MouseListener + { + public void valueChanged(ListSelectionEvent e) + { + updateButtons(); + } + + public void mousePressed(MouseEvent me) + { + if (me.isPopupTrigger()) + firePopup(me); + } + + public void mouseReleased(MouseEvent me) + { + if (me.isPopupTrigger()) + firePopup(me); + } + + public void mouseClicked(MouseEvent evt) + { + if (evt.getClickCount() < 2) + return; + JTreeTable table = (JTreeTable) evt.getSource(); + int row = table.rowAtPoint(new Point(evt.getX(), evt.getY())); + if (row < 0) + return; + Object obj = table.getValueAt(row, 0); + if (!(obj instanceof Allocatable)) + return; + try + { + if (table == completeTable) + add(getSelectedAllocatables(completeTable.getTree())); + else + remove(getSelectedAllocatables(selectedTable.getTree())); + } + catch (RaplaException ex) + { + showException(ex, content); + } + } + + + } + + protected void firePopup(MouseEvent me) + { + Point p = new Point(me.getX(), me.getY()); + JTreeTable table = ((JTreeTable) me.getSource()); + int row = table.rowAtPoint(p); + int column = table.columnAtPoint(p); + Object selectedObject = null; + if (row >= 0 && column >= 0) + selectedObject = table.getValueAt(row, column); + //System.out.println("row " + row + " column " + column + " selected " + selectedObject); + showPopup(new PopupEvent(table, selectedObject, p)); + } + + public void showPopup(PopupEvent evt) + { + try + { + Point p = evt.getPoint(); + JTreeTable table = ((JTreeTable) evt.getSource()); + RaplaPopupMenu menu = new RaplaPopupMenu(); + if (table == completeTable) + { + menu.add(new JMenuItem(addAction)); + menu.add(new JMenuItem(calendarAction1)); + } + else + { + menu.add(new JMenuItem(removeAction)); + menu.add(new JMenuItem(calendarAction2)); + } + getQuery().getUsers(); + menu.show(table, p.x, p.y); + } + catch (RaplaException ex) + { + showException(ex, getComponent()); + } + } + + class CompleteModel extends AllocatablesModel + { + public int getColumnCount() + { + return 2; + } + + public boolean isCellEditable(Object node, int column) + { + return false; + } + + public Object getValueAt(Object node, int column) + { + return ((DefaultMutableTreeNode) node).getUserObject(); + } + + public String getColumnName(int column) + { + switch (column) + { + case 0: + return getString("selectable"); + case 1: + return getString("selectable_on"); + } + throw new IndexOutOfBoundsException(); + } + + public Class getColumnClass(int column) + { + switch (column) + { + case 0: + return TreeTableModel.class; + case 1: + return Allocatable.class; + } + throw new IndexOutOfBoundsException(); + } + + } + + class SelectedModel extends AllocatablesModel + { + public int getColumnCount() + { + return 2; + } + + public boolean isCellEditable(Object node, int column) + { + if (column == 1 && bWorkaround) + return true; + Object o = ((DefaultMutableTreeNode) node).getUserObject(); + if (column == 1 && o instanceof Allocatable) + return true; + else + return false; + } + + public Object getValueAt(Object node, int column) + { + Object o = ((DefaultMutableTreeNode) node).getUserObject(); + if (o instanceof Allocatable) + { + switch (column) + { + case 0: + return o; + case 1: + return mutableReservation.getRestriction((Allocatable) o); + } + } + if (o instanceof DynamicType) + { + return o; + } + return o; + //throw new IndexOutOfBoundsException(); + } + + public void setValueAt(Object value, Object node, int column) + { + Object o = ((DefaultMutableTreeNode) node).getUserObject(); + if (column == 1 && o instanceof Allocatable && value instanceof Appointment[]) + { + if (mutableReservation.getRestriction((Allocatable) o) != value) + { + mutableReservation.setRestriction((Allocatable) o, (Appointment[]) value); + fireAllocationsChanged(); + } + } + fireTreeNodesChanged(node, ((DefaultMutableTreeNode) node).getPath(), new int[] {}, new Object[] {}); + } + + public String getColumnName(int column) + { + switch (column) + { + case 0: + return getString("selected"); + case 1: + return getString("selected_on"); + } + throw new IndexOutOfBoundsException(); + } + + public Class getColumnClass(int column) + { + switch (column) + { + case 0: + return TreeTableModel.class; + case 1: + return Appointment[].class; + } + throw new IndexOutOfBoundsException(); + } + } + + abstract class AllocatablesModel extends AbstractTreeTableModel implements TreeTableModel + { + TreeModel treeModel; + + public AllocatablesModel() + { + super(new DefaultMutableTreeNode()); + treeModel = new DefaultTreeModel((DefaultMutableTreeNode) super.getRoot()); + } + + // Types of the columns. + Collection allocatables; + + public void setAllocatables(Collection allocatables) throws RaplaException + { + this.allocatables = allocatables; + treeModel = getTreeFactory().createClassifiableModel((org.rapla.entities.domain.Allocatable[]) allocatables.toArray(Allocatable.ALLOCATABLE_ARRAY)); + DefaultMutableTreeNode root = (DefaultMutableTreeNode) getRoot(); + int childCount = root.getChildCount(); + int[] childIndices = new int[childCount]; + Object[] children = new Object[childCount]; + for (int i = 0; i < childCount; i++) + { + childIndices[i] = i; + children[i] = root.getChildAt(i); + } + fireTreeStructureChanged(root, root.getPath(), childIndices, children); + } + + public void setAllocatables(Collection allocatables, JTree tree) throws RaplaException + { + this.allocatables = allocatables; + Collection expanded = new HashSet(); + for (int i = 0; i < tree.getRowCount(); i++) + { + if (tree.isExpanded(i)) + { + DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getPathForRow(i).getLastPathComponent(); + expanded.add(node.getUserObject()); + } + } + setAllocatables(allocatables); + expandNodes(expanded, tree); + } + + void expandNodes(Collection expanded, JTree tree) + { + if (expanded.size() == 0) + return; + for (int i = 0; i < tree.getRowCount(); i++) + { + DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getPathForRow(i).getLastPathComponent(); + if (expanded.contains(node.getUserObject())) + { + tree.expandRow(i); + } + } + + } + + public Collection getAllocatables() + { + return allocatables; + } + + public void expandObjects(Collection expandedNodes, JTree tree) + { + // we need an enumeration, because we modife the set + Enumeration enumaration = ((DefaultMutableTreeNode) getRoot()).preorderEnumeration(); + while (enumaration.hasMoreElements()) + { + DefaultMutableTreeNode node = (DefaultMutableTreeNode) enumaration.nextElement(); + Iterator it = expandedNodes.iterator(); + while (it.hasNext()) + { + if (it.next().equals(node.getUserObject())) + { + DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node.getParent(); + int row = tree.getRowForPath(new TreePath(parent.getPath())); + tree.expandRow(row); + } + } + } + } + + public void selectObjects(Collection expandedNodes, JTree tree) + { + Enumeration enumaration = ((DefaultMutableTreeNode) getRoot()).preorderEnumeration(); + List selectionPaths = new ArrayList(); + while (enumaration.hasMoreElements()) + { + DefaultMutableTreeNode node = (DefaultMutableTreeNode) enumaration.nextElement(); + Iterator it = expandedNodes.iterator(); + while (it.hasNext()) + { + if (it.next().equals(node.getUserObject())) + { + selectionPaths.add(new TreePath(node.getPath())); + } + } + } + tree.setSelectionPaths((TreePath[]) selectionPaths.toArray(new TreePath[] {})); + } + + public void treeDidChange() + { + DefaultMutableTreeNode root = (DefaultMutableTreeNode) getRoot(); + int childCount = root.getChildCount(); + int[] childIndices = new int[childCount]; + Object[] children = new Object[childCount]; + for (int i = 0; i < childCount; i++) + { + childIndices[i] = i; + children[i] = root.getChildAt(i); + } + fireTreeNodesChanged(root, root.getPath(), childIndices, children); + } + + public Object getRoot() + { + return treeModel.getRoot(); + } + + public int getChildCount(Object node) + { + return treeModel.getChildCount(node); + } + + public Object getChild(Object node, int i) + { + return treeModel.getChild(node, i); + } + } + + class RestrictionCellRenderer extends DefaultTableCellRenderer + { + private static final long serialVersionUID = 1L; + + Object newValue; + JButton button = new JButton(); + + public void setValue(Object value) + { + newValue = value; + super.setValue(""); + } + + public void setBounds(int x, int y, int width, int heigth) + { + super.setBounds(x, y, width, heigth); + button.setBounds(x, y, width, heigth); + } + + public void paint(Graphics g) + { + Object value = newValue; + if (value instanceof Appointment[]) + { + super.paint(g); + java.awt.Font f = g.getFont(); + button.paint(g); + g.setFont(f); + paintRestriction(g, (Appointment[]) value, this); + } + } + } + + class AllocationCellRenderer extends DefaultTableCellRenderer + { + private static final long serialVersionUID = 1L; + + Object newValue; + + public void setValue(Object value) + { + newValue = value; + super.setValue(""); + } + + public void paint(Graphics g) + { + Object value = newValue; + super.paint(g); + if (value instanceof Allocatable) + { + paintAllocation(g, (Allocatable) value, this); + } + } + } + + class RaplaToolTipRenderer implements TableToolTipRenderer + { + public String getToolTipText(JTable table, int row, int column) + { + Object value = table.getValueAt(row, column); + return getInfoFactory().getToolTip(value); + } + } + + private int indexOf(Appointment appointment) + { + for (int i = 0; i < appointments.length; i++) + if (appointments[i].equals(appointment)) + return i; + return -1; + } + + private boolean conflictingAppointments[]; // stores the temp conflicting appointments + private int conflictCount; // temp value for conflicts + private int permissionConflictCount; // temp value for conflicts that are the result of denied permissions + + // returns if the user is allowed to allocate the passed allocatable + private boolean isAllowed(Allocatable allocatable, Appointment appointment) + { + Date start = appointment.getStart(); + Date end = appointment.getMaxEnd(); + Date today = getQuery().today(); + return allocatable.canAllocate(user, start, end, today); + } + + // calculates the number of conflicting appointments for this allocatable + private void calcConflictingAppointments(Allocatable allocatable) + { + if (conflictingAppointments == null || conflictingAppointments.length != appointments.length) + conflictingAppointments = new boolean[appointments.length]; + conflictCount = 0; + permissionConflictCount = 0; + final boolean holdBackConflicts = allocatable.isHoldBackConflicts(); + for (int i = 0; i < appointments.length; i++) + { + Set allocatables = (Set) appointmentMap.get(appointments[i]); + conflictingAppointments[i] = false; + + if (allocatables != null && allocatables.contains(allocatable) ) + { + if ( ! holdBackConflicts) + { + conflictingAppointments[i] = true; + conflictCount++; + } + } + else if (!isAllowed(allocatable, appointments[i]) ) + { + if ( ! holdBackConflicts) + { + conflictingAppointments[i] = true; + conflictCount++; + } + permissionConflictCount++; + } + } + } + + private void paintAllocation(Graphics g, Allocatable allocatable, JComponent c) + { + calcConflictingAppointments(allocatable); + if (appointments.length == 0) + { + } + else if (conflictCount == 0) + { + g.setColor(Color.green); + g.drawString(getString("every_appointment"), 2, c.getHeight() - 4); + return; + } /* + * else if (conflictCount == appointments.length) { + * g.setColor(Color.red); + * g.drawString(getString("zero_appointment"),2,c.getHeight()-4); + * return; + * } + */ + int x = 2; + Insets insets = c.getInsets(); + FontMetrics fm = g.getFontMetrics(); + for (int i = 0; i < appointments.length; i++) + { + if (conflictingAppointments[i]) + continue; + x = paintApp(c, g, fm, i, insets, x); + } + } + + private void paintRestriction(Graphics g, Appointment[] restriction, JComponent c) + { + if (restriction.length == 0) + { + g.drawString(getString("every_appointment"), 2, c.getHeight() - 4); + return; + } + int x = 0; + Insets insets = c.getInsets(); + FontMetrics fm = g.getFontMetrics(); + for (int i = 0; i < appointments.length; i++) + { + for (int j = 0; j < restriction.length; j++) + { + if (restriction[j].equals(appointments[i])) + x = paintApp(c, g, fm, i, insets, x); + } + } + } + + private int paintApp(Component c, Graphics g, FontMetrics fm, int index, Insets insets, int x) + { + int xborder = 4; + int yborder = 1; + int width = fm.stringWidth(appointmentIndexStrings[index]); + x += xborder; + g.setColor(RaplaColorList.getAppointmentColor(index)); + g.fillRoundRect(x, insets.top, width, c.getHeight() - insets.top - insets.bottom - yborder * 2, 4, 4); + g.setColor(c.getForeground()); + g.drawRoundRect(x - 1, insets.top, width + 1, c.getHeight() - insets.top - insets.bottom - yborder * 2, 4, 4); + g.drawString(appointmentIndexStrings[index], x, c.getHeight() - yborder - fm.getDescent()); + x += width; + x += 2; + int textWidth = fm.stringWidth(appointmentStrings[index]); + g.drawString(appointmentStrings[index], x, c.getHeight() - fm.getDescent()); + x += textWidth; + x += xborder; + return x; + } + + class RestrictionTextField extends JTextField + { + private static final long serialVersionUID = 1L; + + Object newValue; + + public void setValue(Object value) + { + newValue = value; + } + + public void paint(Graphics g) + { + Object value = newValue; + super.paint(g); + if (value instanceof Appointment[]) + { + paintRestriction(g, (Appointment[]) value, this); + } + } + } + + class AppointmentCellEditor extends DefaultCellEditor implements TableCellEditor, MouseListener, KeyListener, PopupMenuListener, ActionListener + { + private static final long serialVersionUID = 1L; + + JPopupMenu menu = new JPopupMenu(); + RestrictionTextField editingComponent; + boolean bStopEditingCalled = false; /* + * We need this variable + * to check if + * stopCellEditing + * was already called. + */ + + DefaultMutableTreeNode selectedNode; + int selectedColumn = 0; + Appointment[] restriction; + + public AppointmentCellEditor(RestrictionTextField textField) + { + super(textField); + editingComponent = (RestrictionTextField) this.getComponent(); + editingComponent.setEditable(false); + + editingComponent.addMouseListener(this); + editingComponent.addKeyListener(this); + menu.addPopupMenuListener(this); + } + + public void mouseReleased(MouseEvent evt) + { + showComp(); + } + + public void mousePressed(MouseEvent evt) + { + } + + public void mouseClicked(MouseEvent evt) + { + } + + public void mouseEntered(MouseEvent evt) + { + } + + public void mouseExited(MouseEvent evt) + { + } + + public void keyPressed(KeyEvent evt) + { + } + + public void keyTyped(KeyEvent evt) + { + } + + public void keyReleased(KeyEvent evt) + { + showComp(); + } + + /** + * This method is performed, if the user clicks on a menu item of the + * JPopupMenu in order to select invividual appointments + * for a resource. + * + * Changed in Rapla 1.4 + */ + public void actionPerformed(ActionEvent evt) + { + // Refresh the selected appointments for the resource which is being edited + updateRestriction(Integer.valueOf(evt.getActionCommand()).intValue()); + + if (evt.getSource() instanceof javax.swing.JCheckBoxMenuItem) + { + // Refresh the state of the "every Appointment" menu item + ((JMenuItem) allMenu).setSelected(restriction.length == 0); + } + else + { + // "every Appointment" has been selected, stop editing + fireEditingStopped(); + selectedTable.requestFocus(); + } + } + + public void popupMenuWillBecomeVisible(PopupMenuEvent e) + { + bStopEditingCalled = false; + } + + public void popupMenuWillBecomeInvisible(PopupMenuEvent e) + { + if (!bStopEditingCalled) + { + AppointmentCellEditor.super.stopCellEditing(); + } + } + + public void popupMenuCanceled(PopupMenuEvent e) + { + // BUGID: 4234793 + // This method is never called + } + + SmallIntMap appointmentList = new SmallIntMap(); + JMenuItem allMenu = new JRadioButtonMenuItem(); + + /** + * This method builds and shows the JPopupMenu for the appointment selection + * + * Changed in Rapla 1.4 + */ + private void showComp() + { + Object selectedObject = selectedNode.getUserObject(); + Allocatable allocatable = null; + if (selectedObject instanceof Allocatable) + { + allocatable = (Allocatable) selectedObject; + calcConflictingAppointments(allocatable); + } + Icon conflictIcon = getI18n().getIcon("icon.allocatable_taken"); + allMenu.setText(getString("every_appointment")); + appointmentList.clear(); + menu.removeAll(); + allMenu.setActionCommand("-1"); + allMenu.addActionListener(this); + menu.add(new JMenuItem(getString("close"))); + menu.add(new JSeparator()); + menu.add(allMenu); + + menu.add(new JSeparator()); + for (int i = 0; i < appointments.length; i++) + { + JMenuItem item = new JCheckBoxMenuItem(); + + // Prevent the JCheckboxMenuItem from closing the JPopupMenu + item.setUI(new StayOpenCheckBoxMenuItemUI()); + + // set conflicting icon if appointment causes conflicts + String appointmentSummary = getAppointmentFormater().getShortSummary(appointments[i]); + if (allocatable != null && conflictingAppointments[i]) + { + item.setText((i + 1) + ": " + appointmentSummary); + item.setIcon(conflictIcon); + } + else + { + item.setText((i + 1) + ": " + appointmentSummary); + } + appointmentList.put(i, item); + item.setBackground(RaplaColorList.getAppointmentColor(i)); + item.setActionCommand(String.valueOf(i)); + item.addActionListener(this); + menu.add(item); + } + + for (int i = 0; i < appointments.length; i++) + { + ((JMenuItem) appointmentList.get(i)).setSelected(false); + } + + Appointment[] apps = restriction; + ((JMenuItem) allMenu).setSelected(apps.length == 0); + for (int i = 0; i < apps.length; i++) + { + // System.out.println("Select " + indexOf(apps[i])); + ((JMenuItem) appointmentList.get(indexOf(apps[i]))).setSelected(true); + } + + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + Dimension menuSize = menu.getPreferredSize(); + Point location = editingComponent.getLocationOnScreen(); + int diffx = Math.min(0, screenSize.width - (location.x + menuSize.width)); + int diffy = Math.min(0, screenSize.height - (location.y + menuSize.height)); + menu.show(editingComponent, diffx, diffy); + } + + private void setRestriction(Appointment[] restriction) + { + this.restriction = restriction; + } + + /** select or deselect the appointment at the given index */ + private void updateRestriction(int index) + { + if (index < 0) + { + restriction = Appointment.EMPTY_ARRAY; + } + else + { + Collection newAppointments = new ArrayList(); + // get the selected appointments + + // add all previous selected appointments, except the appointment that + // is clicked + for (int i = 0; i < restriction.length; i++) + if (!restriction[i].equals(appointments[index])) + { + newAppointments.add(restriction[i]); + } + + // If the clicked appointment was selected then deselect + // otherwise select ist + if (!containsAppointment(appointments[index])) + newAppointments.add(appointments[index]); + restriction = (Appointment[]) newAppointments.toArray(Appointment.EMPTY_ARRAY); + } + // Workaround for JDK 1.4 Bug ID: 4234793 + // We have to change the table-model after cell-editing stopped + selectedModel.setValueAt(restriction, selectedNode, selectedColumn); + } + + private boolean containsAppointment(Appointment appointment) + { + for (int i = 0; i < restriction.length; i++) + if (restriction[i].equals(appointment)) + return true; + return false; + } + + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) + { + Component component = super.getTableCellEditorComponent(table, value, isSelected, row, column); + if (value instanceof Appointment[]) + { + setRestriction((Appointment[]) value); + ((RestrictionTextField) component).setText(""); + } + + ((RestrictionTextField) component).setValue(value); + // Workaround for JDK 1.4 Bug ID: 4234793 + // We have to change the table-model after cell-editing stopped + this.selectedNode = (DefaultMutableTreeNode) selectedTable.getTree().getPathForRow(row).getLastPathComponent(); + this.selectedColumn = column; + return component; + } + + public Object getCellEditorValue() + { + return restriction; + } + + public boolean shouldSelectCell(EventObject event) + { + return true; + } + + public boolean isCellEditable(EventObject event) + { + return true; + } + + public boolean stopCellEditing() + { + bStopEditingCalled = true; + boolean bResult = super.stopCellEditing(); + menu.setVisible(false); + return bResult; + } + } + + class AllocationTreeCellRenderer extends DefaultTreeCellRenderer + { + private static final long serialVersionUID = 1L; + + Icon conflictIcon; + Icon freeIcon; + Icon notAlwaysAvailableIcon; + Icon personIcon; + Icon personNotAlwaysAvailableIcon; + Icon forbiddenIcon; + boolean checkRestrictions; + + public AllocationTreeCellRenderer(boolean checkRestrictions) + { + forbiddenIcon = getI18n().getIcon("icon.no_perm"); + conflictIcon = getI18n().getIcon("icon.allocatable_taken"); + freeIcon = getI18n().getIcon("icon.allocatable_available"); + notAlwaysAvailableIcon = getI18n().getIcon("icon.allocatable_not_always_available"); + personIcon = getI18n().getIcon("icon.tree.persons"); + personNotAlwaysAvailableIcon = getI18n().getIcon("icon.tree.person_not_always_available"); + this.checkRestrictions = checkRestrictions; + setOpenIcon(getI18n().getIcon("icon.folder")); + setClosedIcon(getI18n().getIcon("icon.folder")); + setLeafIcon(freeIcon); + } + + public Icon getAvailableIcon(Allocatable allocatable) + { + if (allocatable.isPerson()) + return personIcon; + else + return freeIcon; + } + + public Icon getNotAlwaysAvailableIcon(Allocatable allocatable) + { + if (allocatable.isPerson()) + return personNotAlwaysAvailableIcon; + else + return notAlwaysAvailableIcon; + } + + private Icon getIcon(Allocatable allocatable) + { + calcConflictingAppointments(allocatable); + if (conflictCount == 0) + { + return getAvailableIcon(allocatable); + } + else if (conflictCount == appointments.length) + { + if (conflictCount == permissionConflictCount) + { + if (!checkRestrictions) + { + return forbiddenIcon; + } + } + else + { + return conflictIcon; + } + } + else if (!checkRestrictions) + { + return getNotAlwaysAvailableIcon(allocatable); + } + for (int i = 0; i < appointments.length; i++) + { + Appointment appointment = appointments[i]; + if (mutableReservation.hasAllocated(allocatable, appointment) && !getQuery().hasPermissionToAllocate(appointment, allocatable)) + { + return forbiddenIcon; + } + } + if (permissionConflictCount - conflictCount == 0) + { + return getAvailableIcon(allocatable); + } + Appointment[] restriction = mutableReservation.getRestriction(allocatable); + if (restriction.length == 0) + { + return conflictIcon; + } + else + { + boolean conflict = false; + for (int i = 0; i < restriction.length; i++) + { + Set allocatables = (Set) appointmentMap.get(restriction[i]); + if (allocatables.contains(allocatable)) + { + conflict = true; + break; + } + } + if (conflict) + return conflictIcon; + else + return getNotAlwaysAvailableIcon(allocatable); + } + } + + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) + { + DefaultMutableTreeNode node = (DefaultMutableTreeNode) value; + Object nodeInfo = node.getUserObject(); + if (nodeInfo != null && nodeInfo instanceof Named) + { + value = ((Named) nodeInfo).getName(getI18n().getLocale()); + } + + if (leaf) + { + if (nodeInfo instanceof Allocatable) + { + setLeafIcon(getIcon((Allocatable) nodeInfo)); + } + } + Component result = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); + + return result; + } + + } + + class AllocatableAction extends AbstractAction + { + private static final long serialVersionUID = 1L; + + String command; + + AllocatableAction(String command) + { + this.command = command; + if (command.equals("add")) + { + putValue(NAME, getString("add")); + putValue(SMALL_ICON, getIcon("icon.arrow_right")); + + } + if (command.equals("remove")) + { + putValue(NAME, getString("remove")); + putValue(SMALL_ICON, getIcon("icon.arrow_left")); + } + if (command.equals("calendar1") || command.equals("calendar2")) + { + putValue(NAME, getString("calendar")); + putValue(SMALL_ICON, getIcon("icon.calendar")); + } + } + + public void actionPerformed(ActionEvent evt) + { + try + { + if (command.equals("add")) + { + add(getSelectedAllocatables(completeTable.getTree())); + } + if (command.equals("remove")) + { + remove(getSelectedAllocatables(selectedTable.getTree())); + } + if (command.indexOf("calendar") >= 0) + { + JTree tree = (command.equals("calendar1") ? completeTable.getTree() : selectedTable.getTree()); + CalendarAction calendarAction = new CalendarAction(getContext(), getComponent(), calendarModel); + calendarAction.changeObjects(new ArrayList(getSelectedElements(tree, false))); + calendarAction.setStart(findFirstStart()); + calendarAction.actionPerformed(evt); + } + } + catch (RaplaException ex) + { + showException(ex, content); + } + } + } + + +/** + * This class is used to prevent the JPopupMenu from disappearing when a + * JCheckboxMenuItem is clicked. + * + * @since Rapla 1.4 + * @see http://forums.oracle.com/forums/thread.jspa?messageID=5724401#5724401 + */ + class StayOpenCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI + { + protected void doClick(MenuSelectionManager msm) + { + menuItem.doClick(0); + } + } + + public class FilterEdit extends RaplaGUIComponent + { + protected RaplaArrowButton filterButton; + Popup popup; + ClassifiableFilterEdit ui; + + public FilterEdit(final RaplaContext context,final CalendarSelectionModel model) throws RaplaException + { + super(context); + filterButton = new RaplaArrowButton('v'); + filterButton.setText(getString("filter")); + filterButton.setSize(80,18); + filterButton.addActionListener( new ActionListener() + { + public void actionPerformed(ActionEvent e) { + + if ( popup != null) + { + popup.hide(); + popup= null; + filterButton.setChar('v'); + return; + } + boolean isResourceOnly = true; + try { + if ( ui != null) + { + ui.removeChangeListener( listener); + } + ui = new ClassifiableFilterEdit( context, isResourceOnly); + ui.addChangeListener(listener); + ui.setFilter( model); + final Point locationOnScreen = filterButton.getLocationOnScreen(); + final int y = locationOnScreen.y + 18; + final int x = locationOnScreen.x; + popup = PopupFactory.getSharedInstance().getPopup( filterButton, ui.getComponent(), x, y); + popup.show(); + filterButton.setChar('^'); + } catch (Exception ex) { + showException(ex, getComponent()); + } + } + + }); + + } + + private ChangeListener listener = new ChangeListener() { + + public void stateChanged(ChangeEvent e) { + try { + final ClassificationFilter[] filters = ui.getFilters(); + calendarModel.setAllocatableFilter( filters); + completeModel.setAllocatables(getAllAllocatables(), completeTable.getTree()); + } catch (Exception ex) { + showException(ex, getComponent()); + } + } + }; + public JComponent getButton() + { + return filterButton; + } + + } +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/gui/internal/edit/reservation/AppointmentController.java b/Rapla/src/org/rapla/gui/internal/edit/reservation/AppointmentController.java new file mode 100644 index 0000000..74cbd23 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/reservation/AppointmentController.java @@ -0,0 +1,1167 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.edit.reservation; + +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; + +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.DefaultListCellRenderer; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.SwingConstants; +import javax.swing.UIManager; +import javax.swing.border.Border; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import org.apache.avalon.framework.activity.Disposable; +import org.apache.avalon.framework.container.ContainerUtil; +import org.rapla.components.calendar.DateChangeEvent; +import org.rapla.components.calendar.DateChangeListener; +import org.rapla.components.calendar.RaplaCalendar; +import org.rapla.components.calendar.RaplaNumber; +import org.rapla.components.calendar.RaplaTime; +import org.rapla.components.layout.TableLayout; +import org.rapla.components.util.DateTools; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Repeating; +import org.rapla.entities.domain.RepeatingEnding; +import org.rapla.entities.domain.RepeatingType; +import org.rapla.entities.domain.ReservationHelper; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.framework.RaplaLocale; +import org.rapla.gui.CalendarOptions; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.internal.common.PeriodChooser; +import org.rapla.gui.toolkit.DialogUI; +import org.rapla.gui.toolkit.MonthChooser; +import org.rapla.gui.toolkit.RaplaButton; +import org.rapla.gui.toolkit.RaplaWidget; +import org.rapla.gui.toolkit.WeekdayChooser; + +/** GUI for editing a single Appointment. */ +public class AppointmentController extends RaplaGUIComponent + implements + Disposable + ,RaplaWidget +{ + JPanel panel = new JPanel(); + + SingleEditor singleEditor = new SingleEditor(); + + JPanel repeatingSelection = new JPanel(); + JPanel repeatingContainer = new JPanel(); + RepeatingEditor repeatingEditor = new RepeatingEditor(); + + Appointment appointment; + Repeating repeating; + + ArrayList listenerList = new ArrayList(); + JPanel repeatingType = new JPanel(); + JRadioButton noRepeating = new JRadioButton(); + JRadioButton weeklyRepeating = new JRadioButton(); + JRadioButton dailyRepeating = new JRadioButton(); + JRadioButton monthlyRepeating = new JRadioButton(); + JRadioButton yearlyRepeating = new JRadioButton(); + + CardLayout repeatingCard = new CardLayout(); + // Button for splitting appointments + RaplaButton convertButton = new RaplaButton(); + + public AppointmentController(RaplaContext sm) throws RaplaException { + super( sm); + panel.setLayout(new BorderLayout()); + panel.add(repeatingType,BorderLayout.NORTH); + repeatingType.setLayout(new BoxLayout(repeatingType,BoxLayout.X_AXIS)); + repeatingType.add(noRepeating); + repeatingType.add(weeklyRepeating); + repeatingType.add(dailyRepeating); + repeatingType.add(monthlyRepeating); + repeatingType.add(yearlyRepeating); + + repeatingType.add( Box.createHorizontalStrut(40)); + repeatingType.add(convertButton); + ButtonGroup buttonGroup = new ButtonGroup(); + buttonGroup.add( noRepeating ); + buttonGroup.add( weeklyRepeating ); + buttonGroup.add( dailyRepeating ); + buttonGroup.add( monthlyRepeating ); + buttonGroup.add( yearlyRepeating ); + + panel.add(repeatingContainer,BorderLayout.CENTER); + + Border emptyLineBorder = new Border() { + Insets insets = new Insets(1,0,0,0); + Color COLOR = Color.LIGHT_GRAY; + public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) + { + g.setColor( COLOR ); + g.drawLine(0,0, c.getWidth(), 0); + + } + + public Insets getBorderInsets( Component c ) + { + return insets; + } + + public boolean isBorderOpaque() + { + return true; + } + + }; + + Border outerBorder = (BorderFactory.createCompoundBorder( + BorderFactory.createEmptyBorder(0,5,0,5) + //,BorderFactory.createEmptyBorder() + ,emptyLineBorder + ) + ); + + repeatingContainer.setBorder(BorderFactory.createCompoundBorder( + outerBorder + ,BorderFactory.createEmptyBorder(10,5,10,5) + ) + ); + repeatingContainer.setLayout(repeatingCard); + repeatingContainer.add(singleEditor.getComponent(),"0"); + repeatingContainer.add(repeatingEditor.getComponent(),"1"); + + singleEditor.initialize(); + repeatingEditor.initialize(); + ActionListener listener = new ActionListener() { + public void actionPerformed(ActionEvent evt) { + switchRepeatings(); + } + }; + noRepeating.addActionListener(listener); + weeklyRepeating.addActionListener(listener); + monthlyRepeating.addActionListener(listener); + dailyRepeating.addActionListener(listener); + yearlyRepeating.addActionListener(listener); + noRepeating.setText(getString("no_repeating")); + weeklyRepeating.setText(getString("weekly")); + dailyRepeating.setText(getString("daily")); + monthlyRepeating.setText(getString("monthly")); + yearlyRepeating.setText(getString("yearly")); + // Rapla 1.4: Initialize the split appointment button + convertButton.setText(getString("appointment.convert")); + } + + private void switchRepeatings() { + if (noRepeating.isSelected()) { + repeatingCard.show(repeatingContainer,"0"); + singleEditor.mapFromAppointment(); + appointment.setRepeatingEnabled(false); + } else { + RepeatingType repeatingType; + if ( monthlyRepeating.isSelected()) + { + repeatingType = RepeatingType.MONTHLY; + } + else if ( yearlyRepeating.isSelected()) + { + repeatingType = RepeatingType.YEARLY; + } + else if ( dailyRepeating.isSelected()) + { + repeatingType= RepeatingType.DAILY; + } + else + { + repeatingType = RepeatingType.WEEKLY; + } + int repeatings = getCalendarOptions().isInfiniteRepeating() ? -1 : (1 * getCalendarOptions().getnTimes()); //BJO 00000012 -1:infinite; >0:=n-times + ReservationHelper.makeRepeatingForPeriod(getPeriodModel(), appointment, repeatingType, repeatings ); + repeatingEditor.mapFromAppointment(); + repeatingCard.show(repeatingContainer,"1"); + } + fireAppointmentChanged(); + } + + public void setAppointment(Appointment appointment) { + this.appointment = appointment; + this.repeating = appointment.getRepeating(); + if (appointment.getRepeating() != null) { + repeatingEditor.mapFromAppointment(); + repeatingCard.show(repeatingContainer,"1"); + if (repeating.isWeekly()) + weeklyRepeating.setSelected(true); + if (repeating.isDaily()) + dailyRepeating.setSelected(true); + if (repeating.isMonthly()) + monthlyRepeating.setSelected(true); + if (repeating.isYearly()) + yearlyRepeating.setSelected(true); + } else { + singleEditor.mapFromAppointment(); + repeatingCard.show(repeatingContainer,"0"); + noRepeating.setSelected(true); + } + } + + public Appointment getAppointment() { + return appointment; + } + + public void dispose() { + ContainerUtil.dispose( singleEditor ); + ContainerUtil.dispose( repeatingEditor ); + } + + public JComponent getComponent() { + return panel; + } + + /** registers new ChangeListener for this component. + * An ChangeEvent will be fired to every registered ChangeListener + * when the appointment changes. + * @see javax.swing.event.ChangeListener + * @see javax.swing.event.ChangeEvent + */ + public void addChangeListener(ChangeListener listener) { + listenerList.add(listener); + } + + /** removes a listener from this component.*/ + public void removeChangeListener(ChangeListener listener) { + listenerList.remove(listener); + } + + public ChangeListener[] getChangeListeners() { + return (ChangeListener[])listenerList.toArray(new ChangeListener[]{}); + } + + protected void fireAppointmentChanged() { + if (listenerList.size() == 0) + return; + ChangeEvent evt = new ChangeEvent(this); + ChangeListener[] listeners = getChangeListeners(); + for (int i = 0;i0) { + exceptionButton.setForeground(Color.red); + } else { + exceptionButton.setForeground(UIManager.getColor("Label.foreground")); + } + String countValue = String.valueOf(count); + if ( count < 9 ) { + countValue =" " + countValue + " "; + } + exceptionButton.setText(getString("appointment.exceptions") + " (" + countValue + ")"); + } + + private void showEnding(int index) { + if (index == REPEAT_UNTIL) { + endDate.setVisible(true); + endDatePeriodPanel.setVisible(isPeriodVisible()); + numberPanel.setVisible(false); + } + if (index == REPEAT_N_TIMES) { + endDate.setVisible(false); + endDatePeriodPanel.setVisible(false); + numberPanel.setVisible(true); + } + if (index == REPEAT_FOREVER) { + endDate.setVisible(false); + endDatePeriodPanel.setVisible(false); + numberPanel.setVisible(false); + } + } + + private void mapFromAppointment() { + if (exceptionDlg != null && exceptionDlg.isVisible()) + exceptionDlg.dispose(); + repeating = appointment.getRepeating(); + if (repeating == null) { + return; + } + listenerEnabled = false; + try { + updateExceptionCount(); + if (exceptionEditor != null) + exceptionEditor.mapFromAppointment(); + + interval.setNumber(new Integer(repeating.getInterval())); + + startDate.setDate(appointment.getStart()); + startDatePeriod.setDate(appointment.getStart()); + startTime.setTime(appointment.getStart()); + endTime.setTime(appointment.getEnd()); + + weekdayInMonthPanel.setVisible(repeating.isMonthly()); + intervalPanel.setVisible( repeating.isDaily() || repeating.isWeekly()); + dayInMonthPanel.setVisible( repeating.isYearly()); + + if ( repeating.getEnd() != null ) { + endDate.setDate(DateTools.subDay(repeating.getEnd())); + endDatePeriod.setDate(DateTools.cutDate(endDate.getDate())); + number.setNumber(new Integer(repeating.getNumber())); + if (!repeating.isFixedNumber()) { + endingChooser.setSelectedIndex(REPEAT_UNTIL); + showEnding(REPEAT_UNTIL); + } else { + endingChooser.setSelectedIndex(REPEAT_N_TIMES); + showEnding(REPEAT_N_TIMES); + } + } else { + endingChooser.setSelectedIndex(REPEAT_FOREVER); + showEnding(REPEAT_FOREVER); + } + + startDatePeriod.setVisible(isPeriodVisible() && ( repeating.isDaily() || repeating.isWeekly()) ); + endDatePeriod.setVisible( repeating.isDaily() || repeating.isWeekly()); + if (repeating.isWeekly() ||repeating.isMonthly()) + { + dayLabel.setVisible(false); + weekdayChooser.setVisible(true); + monthChooser.setVisible(false); + Calendar calendar = getRaplaLocale().createCalendar(); + calendar.setTime(appointment.getStart()); + weekdayChooser.selectWeekday(calendar.get(Calendar.DAY_OF_WEEK)); + } + + if (repeating.isYearly()) + { + dayLabel.setVisible(false); + weekdayChooser.setVisible(false); + monthChooser.setVisible(true); + Calendar cal = getRaplaLocale().createCalendar(); + cal.setTime(appointment.getStart()); + monthChooser.selectMonth(cal.get(Calendar.MONTH) ); + int numb = cal.get( Calendar.DAY_OF_MONTH); + dayInMonth.setNumber( new Integer( numb)); + } + + if (repeating.isMonthly()) + { + Calendar cal = getRaplaLocale().createCalendar(); + cal.setTime( appointment.getStart()); + int numb = cal.get( Calendar.DAY_OF_WEEK_IN_MONTH); + weekdayInMonth.setNumber( new Integer( numb)); + } + + if (repeating.isDaily()) + { + dayLabel.setVisible(true); + weekdayChooser.setVisible(false); + monthChooser.setVisible(false); + } + + String typeString = repeating.getType().toString(); + startDateLabel.setText(getString(typeString) + " " + getString("repeating.start_date")); + + int daysBetween = (int) DateTools.countDays(appointment.getStart(),appointment.getEnd()); + if (daysBetween == 0) { + dayChooser.setSelectedIndex(SAME_DAY); + days.setVisible(false); + } else if (daysBetween == 1) { + dayChooser.setSelectedIndex(NEXT_DAY); + days.setVisible(false); + } else { + dayChooser.setSelectedIndex(X_DAYS); + days.setNumber(new Integer(daysBetween)); + days.setVisible(true); + } + final boolean wholeDaysSet = appointment.isWholeDaysSet(); + startTime.setEnabled( !wholeDaysSet); + endTime.setEnabled( !wholeDaysSet); + dayChooser.setEnabled( !wholeDaysSet); + oneDayEventCheckBox.setSelected( wholeDaysSet ); + } finally { + listenerEnabled = true; + } + convertButton.setEnabled( repeating.getEnd() != null); + getComponent().revalidate(); + } + + private boolean isPeriodVisible() { + try { + return getQuery().getPeriodModel().getSize()>0; + } catch (RaplaException e) { + return false; + } + } + + private void showExceptionDlg() throws RaplaException { + exceptionEditor = new ExceptionEditor(); + exceptionEditor.initialize(); + exceptionEditor.mapFromAppointment(); + exceptionEditor.getComponent().setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); + exceptionDlg = DialogUI.create( + getContext() + ,getComponent() + ,true + ,exceptionEditor.getComponent() + ,new String[] { + getString("back") + }); + exceptionDlg.setTitle(getString("appointment.exceptions")); + exceptionDlg.start(); + updateExceptionCount(); + } + } + + + class ExceptionEditor implements ActionListener,ListSelectionListener { + JPanel content = new JPanel(); + RaplaCalendar exceptionDate; + RaplaButton addButton = new RaplaButton(RaplaButton.SMALL); + RaplaButton removeButton = new RaplaButton(RaplaButton.SMALL); + JList specialExceptions = new JList(); + JList generalExceptions = new JList(new String[] {"Feiertage","Dies academicus"}); + public ExceptionEditor() { + // Create a TableLayout for the frame + double pre =TableLayout.PREFERRED; + double min =TableLayout.MINIMUM; + double fill =TableLayout.FILL; + double yborder = 8; + double size[][] = + {{pre,pre,0.1,50,100,0.9}, // Columns + {yborder,min,min,fill}}; // Rows + TableLayout tableLayout = new TableLayout(size); + content.setLayout(tableLayout); + } + + public JComponent getComponent() { + return content; + } + + public void initialize() { + addButton.setText(getString("add") ); + addButton.setIcon( getIcon("icon.arrow_right")); + removeButton.setText( getString("remove")); + removeButton.setIcon( getIcon("icon.arrow_left")); + exceptionDate = createRaplaCalendar(); + /* + this.add(new JLabel(getString("appointment.exception.general") + " "),"0,1"); + this.add(new JScrollPane(generalExceptions + ,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS + ,JScrollPane.HORIZONTAL_SCROLLBAR_NEVER) + ,"1,1,1,3,t"); + */ + JLabel label =new JLabel(getString("appointment.exception.days") + " "); + label.setHorizontalAlignment(SwingConstants.RIGHT); + content.add(label,"3,1,4,1,r,t"); + content.add(exceptionDate,"5,1,l,t"); + content.add(addButton,"4,2,f,t"); + content.add(removeButton,"4,3,f,t"); + content.add(new JScrollPane(specialExceptions + ,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS + ,JScrollPane.HORIZONTAL_SCROLLBAR_NEVER) + ,"5,2,5,3,t"); + generalExceptions.setEnabled(false); + + addButton.addActionListener(this); + removeButton.addActionListener(this); + specialExceptions.addListSelectionListener(this); + removeButton.setEnabled(false); + specialExceptions.setFixedCellWidth(200); + specialExceptions.setCellRenderer(new DefaultListCellRenderer() { + private static final long serialVersionUID = 1L; + + public Component getListCellRendererComponent(JList list, + Object value, + int index, + boolean isSelected, + boolean cellHasFocus) { + if (value instanceof Date) + value = getRaplaLocale().formatDateLong((Date)value); + return super.getListCellRendererComponent(list,value,index,isSelected,cellHasFocus); + } + }); + specialExceptions.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent evt) { + if (evt.getClickCount()>1) { + removeException(); + } + } + }); + } + + public void mapFromAppointment() { + if (appointment.getRepeating() == null) + specialExceptions.setListData(new Object[0]); + else + specialExceptions.setListData(appointment.getRepeating().getExceptions()); + // exceptionDate.setDate( appointment.getStart()); + Date expDate = appointment.getReservation().getSelectedSlotDate(); + if( expDate == null) + exceptionDate.setDate( appointment.getStart()); + else + exceptionDate.setDate(expDate); + } + + public void actionPerformed(ActionEvent evt) { + if (evt.getSource() == addButton) { + addException(); + } + if (evt.getSource() == removeButton) { + removeException(); + } + } + + private void addException() { + Date date = exceptionDate.getDate(); + if (appointment.getRepeating().isException(date.getTime())) { + return; + } + appointment.getRepeating().addException(date); + specialExceptions.setListData(appointment.getRepeating().getExceptions()); + fireAppointmentChanged(); + } + + private void removeException() { + if (specialExceptions.getSelectedValues() == null) + return; + Object[] selectedExceptions = specialExceptions.getSelectedValues(); + for ( int i = 0; i< selectedExceptions.length;i++) { + appointment.getRepeating().removeException((Date)selectedExceptions[i]); + } + specialExceptions.setListData(appointment.getRepeating().getExceptions()); + fireAppointmentChanged(); + } + + public void valueChanged(ListSelectionEvent e) { + if (e.getSource() == specialExceptions) { + removeButton.setEnabled(specialExceptions.getSelectedValue() != null); + } + } + } + + private class ListRenderer extends DefaultListCellRenderer { + private static final long serialVersionUID = 1L; + + public Component getListCellRendererComponent(JList list,Object value, int index, boolean isSelected, boolean cellHasFocus) { + if ( value != null) { + setText(getString( value.toString())); + } + return this; + } + } +} + + diff --git a/Rapla/src/org/rapla/gui/internal/edit/reservation/AppointmentIdentifier.java b/Rapla/src/org/rapla/gui/internal/edit/reservation/AppointmentIdentifier.java new file mode 100644 index 0000000..692327c --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/reservation/AppointmentIdentifier.java @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.edit.reservation; + +import java.awt.Insets; +import java.awt.FontMetrics; +import java.awt.Graphics; +import javax.swing.JLabel; +import org.rapla.gui.toolkit.RaplaColorList; + +/** A label with a background-color corresponding to the index + of the appointment. + @see RaplaColorList#getAppointmentColor + */ +public class AppointmentIdentifier extends JLabel { + private static final long serialVersionUID = 1L; + + String text; + int index = 0; + public void setIndex(int index) { + this.index = index; + } + public void setText(String text) { + this.text = text; + super.setText(text + " "); + } + + public void paintComponent(Graphics g) { + FontMetrics fm = g.getFontMetrics(); + Insets insets = getInsets(); + String s = text; + int width = fm.stringWidth(s); + int x = 1; + g.setColor(RaplaColorList.getAppointmentColor(index)); + g.fillRoundRect(x + ,insets.top + ,width +1 + ,getHeight()-insets.top -insets.bottom-1,4,4); + g.setColor(getForeground()); + g.drawRoundRect(x-1 + ,insets.top + ,width +2 + ,getHeight()-insets.top -insets.bottom-1,4,4); + g.drawString(s + ,x + ,getHeight() /2 + fm.getDescent() + 1); + } +} diff --git a/Rapla/src/org/rapla/gui/internal/edit/reservation/AppointmentListEdit.java b/Rapla/src/org/rapla/gui/internal/edit/reservation/AppointmentListEdit.java new file mode 100644 index 0000000..1289bd2 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/reservation/AppointmentListEdit.java @@ -0,0 +1,313 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.edit.reservation; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import javax.swing.BoxLayout; +import javax.swing.DefaultListModel; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.ListCellRenderer; +import javax.swing.UIManager; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.apache.avalon.framework.activity.Disposable; +import org.apache.avalon.framework.container.ContainerUtil; +import org.rapla.components.layout.TableLayout; +import org.rapla.components.util.DateTools; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.AppointmentBlock; +import org.rapla.entities.domain.AppointmentStartComparator; +import org.rapla.entities.domain.Repeating; +import org.rapla.entities.domain.Reservation; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.internal.edit.RaplaListEdit; +import org.rapla.gui.toolkit.RaplaWidget; + +/** Default GUI for editing multiple appointments.*/ +class AppointmentListEdit extends AbstractAppointmentEditor + implements + RaplaWidget + ,Disposable +{ + + private AppointmentController appointmentController; + private RaplaListEdit listEdit; + + protected Reservation mutableReservation; + private Listener listener = new Listener(); + DefaultListModel model = new DefaultListModel(); + // use sorted model to start with sorting + // respect dependencies ! on other components + //SortedListModel sortedModel = new SortedListModel(model, SortedListModel.SortOrder.ASCENDING, new AppointmentStartComparator()); + + AppointmentListEdit(RaplaContext sm) throws RaplaException { + super( sm); + appointmentController = new AppointmentController(sm); + listEdit = new RaplaListEdit(getI18n(),appointmentController.getComponent(), listener); + appointmentController.addChangeListener(listener); + // activate this as a first step + // listEdit.getList().setModel(sortedModel); + listEdit.getList().setModel(model); + listEdit.setColoredBackgroundEnabled(true); + listEdit.setMoveButtonVisible(false); + JLabel status =listEdit.getStatusBar(); + status.setFont( status.getFont().deriveFont( (float)9.0)); + listEdit.getList().setCellRenderer(new AppointmentCellRenderer()); + } + + public JComponent getComponent() { + return listEdit.getComponent(); + } + + public void setReservation(Reservation mutableReservation, Appointment appointment) { + this.mutableReservation = mutableReservation; + Appointment[] appointments = mutableReservation.getAppointments(); + model.clear(); + for (int i = 0; i 0 ){ + listEdit.select(0); + } + updateStatus(); + } + + public void dispose() { + ContainerUtil.dispose( appointmentController ); + } + + private void removeAppointments() { + Object[] objects = listEdit.getList().getSelectedValues(); + for (int i=0;i -1 ? selectedIndex :appointments.length-1; + final Appointment toClone = appointments[index]; + //this allows each appointment as template + appointment = getReservationController().copyAppointment(toClone); + + Repeating repeating = appointment.getRepeating(); + if (repeating != null) { + repeating.clearExceptions(); + } + + + } + mutableReservation.addAppointment(appointment); + model.addElement(appointment); + + updateStatus(); + fireAppointmentAdded(appointment); + listEdit.select(model.getSize()-1); + } catch (RaplaException ex) { + showException(ex, getComponent()); + } + } + + /** + * Splits a repeating Appointment into single not repeating Appointments + * + * @since Rapla 1.4 + */ + private void splitAppointment() + { + try + { + // Generate time blocks from selected appointment + List splits = new ArrayList(); + Appointment appointment = appointmentController.getAppointment(); + appointment.createBlocks(appointment.getStart(), DateTools.fillDate(appointment.getMaxEnd()), splits); + + // Switch the type of the appointment to single appointment + appointmentController.noRepeating.doClick(); + + // Remove the first time block because it is equal to the appointment + if ( splits.size() > 0) + { + splits.remove(0); + } + // Create single appointments for every time block + for (AppointmentBlock block: splits) + { + Appointment newApp = (Appointment) getModification().newAppointment(new Date(block.getStart()), new Date(block.getEnd())); + // Add appointment to list + mutableReservation.addAppointment(newApp); + model.addElement(newApp); + fireAppointmentAdded(newApp); + } + + // Refresh the status info + updateStatus(); + } + catch (RaplaException ex) + { + showException(ex, getComponent()); + } + } + + private void updateStatus() { + Reservation event = mutableReservation; + Appointment[] appointments = event.getAppointments(); + int count = 0; + for (int i = 0; i blocks = new ArrayList(); + appointment.createBlocks( appointment.getStart(), DateTools.fillDate(repeating.getEnd()), blocks); + count += blocks.size(); + } + String status = ""; + if (count >= 0) + status = getString("total_occurances")+ ": " + count; + listEdit.getStatusBar().setText( status ); + } + + class AppointmentCellRenderer implements ListCellRenderer { + Border focusBorder = UIManager.getBorder("List.focusCellHighlightBorder"); + Border emptyBorder = new EmptyBorder(1,1,1,1); + + Color selectionBackground = UIManager.getColor("List.selectionBackground"); + Color background = UIManager.getColor("List.background"); + + AppointmentRow row = new AppointmentRow(); + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + row.setAppointment((Appointment) value,index); + row.setBackground((isSelected) ? selectionBackground : background); + row.setBorder((cellHasFocus) ? focusBorder : emptyBorder); + return row; + } + }; + + class AppointmentRow extends JPanel { + private static final long serialVersionUID = 1L; + + JPanel content = new JPanel(); + AppointmentIdentifier identifier = new AppointmentIdentifier(); + AppointmentRow() { + double fill = TableLayout.FILL; + double pre = TableLayout.PREFERRED; + this.setLayout(new TableLayout(new double[][] {{pre,5,fill,10,pre},{1,fill,1}})); + this.add(identifier,"0,1,l,f"); + this.add(content,"2,1,f,c"); + + this.setMaximumSize(new Dimension(500,40)); + content.setOpaque(false); + identifier.setOpaque(true); + identifier.setBorder(null); + } + + public void setAppointment(Appointment appointment,int index) { + identifier.setText(getRaplaLocale().formatNumber(index + 1)); + identifier.setIndex(index); + content.setLayout(new BoxLayout(content,BoxLayout.Y_AXIS)); + content.removeAll(); + JLabel label1 = new JLabel(getAppointmentFormater().getSummary(appointment)); + content.add( label1 ); + if (appointment.getRepeating() != null) { + label1.setIcon( getIcon("icon.repeating") ); + Repeating r = appointment.getRepeating(); + List periods = getPeriodModel().getPeriodsFor(appointment.getStart()); + String repeatingString = + getAppointmentFormater().getSummary(r,periods); + content.add(new JLabel(repeatingString)); + if ( r.hasExceptions() ) { + content.add(new JLabel( getAppointmentFormater().getExceptionSummary( r ) ) ); + } + } else { + label1.setIcon( getIcon("icon.single") ); + } + } + } + + class Listener implements ActionListener, ChangeListener + { + public void actionPerformed(ActionEvent evt) + { + if (evt.getActionCommand().equals("remove")) + { + removeAppointments(); + } + else if (evt.getActionCommand().equals("new")) + { + createNewAppointment(); + } + else if (evt.getActionCommand().equals("split")) + { + splitAppointment(); + } + else if (evt.getActionCommand().equals("edit")) + { + Appointment app = (Appointment) listEdit.getList().getSelectedValue(); + appointmentController.setAppointment(app); + } + } + + public void stateChanged(ChangeEvent evt) + { + Appointment appointment = appointmentController.getAppointment(); + model.set(model.indexOf(appointment), appointment); + updateStatus(); + fireAppointmentChanged(appointment); + } + } +} + + diff --git a/Rapla/src/org/rapla/gui/internal/edit/reservation/AppointmentListener.java b/Rapla/src/org/rapla/gui/internal/edit/reservation/AppointmentListener.java new file mode 100644 index 0000000..d22ddcd --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/reservation/AppointmentListener.java @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.edit.reservation; +import java.util.*; + +import org.rapla.entities.domain.Appointment; + +public interface AppointmentListener extends EventListener { + void appointmentAdded(Appointment appointment); + //#TODO should accept many appointments + void appointmentRemoved(Appointment appointment); + void appointmentChanged(Appointment appointment); +} + + + diff --git a/Rapla/src/org/rapla/gui/internal/edit/reservation/ConflictTableModel.java b/Rapla/src/org/rapla/gui/internal/edit/reservation/ConflictTableModel.java new file mode 100644 index 0000000..6d8168e --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/reservation/ConflictTableModel.java @@ -0,0 +1,104 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006-2003 Christopher Kohlhaas, Bettina Lademann | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.edit.reservation; + +import javax.swing.table.AbstractTableModel; + +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.entities.Named; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.AppointmentFormater; +import org.rapla.entities.domain.Reservation; +import org.rapla.facade.Conflict; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +public class ConflictTableModel extends AbstractTableModel +{ + private static final long serialVersionUID = 1L; + + Conflict[] conflicts; + String[] columnNames; + AppointmentFormater appointmentFormater; + I18nBundle i18n; + + public ConflictTableModel(RaplaContext serviceManager,Conflict[] conflicts) throws RaplaException + { + this.conflicts = conflicts; + i18n = (I18nBundle) serviceManager.lookup(I18nBundle.ROLE + "/org.rapla.RaplaResources"); + appointmentFormater = (AppointmentFormater) serviceManager.lookup(AppointmentFormater.ROLE); + + columnNames = new String[] + { + i18n.getString("conflict.reservation1") + , i18n.getString("conflict.appointment1") + , i18n.getString("conflict.resource") + , i18n.getString("conflict.reservation2") + , i18n.getString("conflict.user") + , i18n.getString("conflict.appointment2") + }; + + } + + private String getName(Named named) { + return named.getName(i18n.getLocale()); + } + + public String getColumnName(int c) + { + return columnNames[c]; + } + + public int getColumnCount() + { + return columnNames.length; + } + + public int getRowCount() + { + return conflicts.length; + } + + public Object getValueAt(int r, int c) + { + + switch (c) { + + case 0: return(getName(conflicts[r].getReservation1())); + case 1: return(appointmentFormater.getSummary(conflicts[r].getAppointment1())); + case 2: return(getName(conflicts[r].getAllocatable())); + case 3: return(getName(conflicts[r].getReservation1())); + case 4: return(conflicts[r].getUser2().getName()); + case 5: return(appointmentFormater.getSummary(conflicts[r].getAppointment2())); + } + return null; + } + + public Reservation getReservationAt(int i) { + return conflicts[i].getReservation1(); + } + + public Appointment getAppointmentAt(int i) { + return conflicts[i].getAppointment1(); + } + +} + + + + + + + + + diff --git a/Rapla/src/org/rapla/gui/internal/edit/reservation/NoWizard.java b/Rapla/src/org/rapla/gui/internal/edit/reservation/NoWizard.java new file mode 100644 index 0000000..2b40eae --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/reservation/NoWizard.java @@ -0,0 +1,70 @@ + +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.edit.reservation; + +import java.awt.Component; +import java.util.Date; + +import org.rapla.components.util.DateTools; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.ReservationWizard; + +/** This ReservationWizard displays no wizard and directly opens a ReservationEdit Window +*/ +public class NoWizard extends RaplaGUIComponent implements ReservationWizard { + public NoWizard(RaplaContext sm) throws RaplaException { + super(sm); + } + + public void start(Component component,CalendarModel model,DynamicType type) throws RaplaException { + Date startDate = model.getSelectedDate(); + if ( startDate != null ) + { + Date time = new Date (DateTools.MILLISECONDS_PER_HOUR * getCalendarOptions().getWorktimeStart()); + startDate = getRaplaLocale().toDate(startDate,time); + } + if (startDate == null) { + startDate = getQuery().today(); + Date time = new Date (DateTools.MILLISECONDS_PER_HOUR * getCalendarOptions().getWorktimeStart()); + startDate = getRaplaLocale().toDate(startDate,time); + } + Date endDate = new Date(startDate.getTime() + DateTools.MILLISECONDS_PER_HOUR); + Reservation r = getModification().newReservation(); + if (type != null) + r.setClassification(type.newClassification()); + Appointment appointment = getModification().newAppointment(startDate, endDate); + r.addAppointment(appointment); + Allocatable[] allocatables = model.getSelectedAllocatables(); + if ( allocatables.length == 1) + { + r.addAllocatable( allocatables[0]); + } + getReservationController().edit( r ); + } + + public String toString() { + return getString("reservation.create_without_wizard"); + } +} + + + + diff --git a/Rapla/src/org/rapla/gui/internal/edit/reservation/ReservationControllerImpl.java b/Rapla/src/org/rapla/gui/internal/edit/reservation/ReservationControllerImpl.java new file mode 100644 index 0000000..c4e777e --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/reservation/ReservationControllerImpl.java @@ -0,0 +1,541 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.edit.reservation; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Point; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.HashSet; +import java.util.Iterator; + +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.table.TableColumn; + +import org.rapla.components.layout.TableLayout; +import org.rapla.components.util.Command; +import org.rapla.entities.EntityNotFoundException; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Reservation; +import org.rapla.facade.Conflict; +import org.rapla.facade.ModificationEvent; +import org.rapla.facade.ModificationListener; +import org.rapla.facade.ModificationModule; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.ReservationController; +import org.rapla.gui.ReservationEdit; +import org.rapla.gui.internal.view.ConflictInfoOldUI; +import org.rapla.gui.toolkit.DialogUI; + +public class ReservationControllerImpl extends RaplaGUIComponent + implements + ModificationListener, ReservationController +{ + /** We store all open ReservationEditWindows with their reservationId + * in a map, to lookup if the reservation is already beeing edited. + That prevents editing the same Reservation in different windows + */ + Collection editWindowList = new ArrayList(); + + public ReservationControllerImpl(RaplaContext sm) throws RaplaException { + super(sm); + getUpdateModule().addModificationListener(this); + } + + void addReservationEdit(ReservationEdit editWindow) { + editWindowList.add(editWindow); + } + + void removeReservationEdit(ReservationEdit editWindow) { + editWindowList.remove(editWindow); + } + + public void edit(Reservation reservation) throws RaplaException { + startEdit(reservation,null); + } + + public void edit(Appointment appointment)throws RaplaException { + startEdit(appointment.getReservation(), appointment); + } + + public ReservationEdit[] getEditWindows() { + return (ReservationEdit[]) editWindowList.toArray( new ReservationEdit[] {}); + } + + private ReservationEditImpl newEditWindow() throws RaplaException { + ReservationEditImpl c = new ReservationEditImpl(getContext()); + return c; + } + + private ReservationEdit startEdit(Reservation reservation,Appointment appointment) + throws RaplaException { + // Lookup if the reservation is already beeing edited + ReservationEditImpl c = null; + Iterator it = editWindowList.iterator(); + while (it.hasNext()) { + c = (ReservationEditImpl)it.next(); + if (c.getReservation().isIdentical(reservation)) + break; + else + c = null; + } + + if (c != null) { + c.frame.requestFocus(); + c.frame.toFront(); + } else { + c = newEditWindow(); + ModificationModule mod = getModification(); + boolean bNew = false; + if ( reservation.isPersistant()) { + reservation = (Reservation) mod.edit(reservation); + } else { + try { + getModification().getPersistant( reservation); + } catch ( EntityNotFoundException ex) { + bNew = true; + } + } + // only is allowed to exchange allocations + c.editReservation(reservation, appointment, bNew); + if ( !canModify( reservation) ) + { + c.deleteButton.setEnabled( false); + disableComponentAndAllChildren(((ReservationEditImpl)c).appointmentEdit.getComponent()); + disableComponentAndAllChildren(((ReservationEditImpl)c).reservationInfo.getComponent()); + } + } + return c; + } + + static void disableComponentAndAllChildren(Container component) { + component.setEnabled( false ); + Component[] components = component.getComponents(); + for ( int i=0; i< components.length; i++) + { + if ( components[i] instanceof Container) { + disableComponentAndAllChildren( (Container) components[i] ); + } + } + } + + + + public void deleteAppointment(Appointment appointment,Date from,Component sourceComponent,Point point) throws RaplaException { + Reservation reservation = appointment.getReservation(); + Reservation mutableReservation = (Reservation)getModification().edit(reservation); + Appointment app = mutableReservation.findAppointment(appointment); + + if (app.getRepeating() != null) { + String dateString = getRaplaLocale().formatDate(from); + DialogUI dialog = DialogUI.create( + getContext() + ,sourceComponent + ,true + ,getString("delete") + ,getI18n().format("delete_appointment.format" + ,dateString) + ,new String[] { + getString("serie") + ,getString("single_appointment") + ,getString("cancel") + } + ); + dialog.setIcon(getIcon("icon.question")); + dialog.getButton(0).setIcon(getIcon("icon.repeating")); + dialog.getButton(2).setIcon(getIcon("icon.cancel")); + dialog.start(point); + int index = dialog.getSelectedIndex(); + if (index == 2) + return; + if (index == 1) { + app.getRepeating().addException(from); + save(mutableReservation,sourceComponent); + return; + } + if (mutableReservation.getAppointments().length == 1) { + getModification().remove( mutableReservation ); + return; + } + } + + + // remove appointment if there are other appointments in the reservation + if ( mutableReservation.getAppointments().length > 1) { + // remove all allocatables that are restricted to the removed appointment + Allocatable[] allocatables = mutableReservation.getAllocatables(); + for (int i=0;i 0) { + JLabel warningLabel = new JLabel(); + warningLabel.setText(getString("warning.conflict")); + warningLabel.setForeground(java.awt.Color.red); + content.add(warningLabel,"0,1"); + content.add(getConflictPanel(conflicts),"0,2"); + } + content.add(infoComponent,"0,4"); + DialogUI dialog = DialogUI.create( + getContext() + ,sourceComponent + ,true + ,content + ,new String[] { + getString("save") + ,getString("back") + } + ); + + if (conflicts.length > 0) + dialog.setDefault(1); + dialog.getButton(0).setIcon(getIcon("icon.save")); + dialog.getButton(1).setIcon(getIcon("icon.cancel")); + dialog.setTitle(getI18n().format("confirm.dialog.title",getName(reservation))); + dialog.start(); + if (dialog.getSelectedIndex() == 0) { + try { + saveCommand.execute(); + } catch (Exception ex) { + showException(ex,sourceComponent); + return; + } + } + } + + class SaveCommand implements Command { + Reservation reservation; + boolean saved; + public SaveCommand(Reservation reservation) { + this.reservation = reservation; + } + + public void execute() throws RaplaException { + getModification().store( reservation ); + saved = true; + } + + public boolean hasSaved() { + return saved; + } + } + + private JComponent getConflictPanel(Conflict[] conflicts) throws RaplaException { + ConflictInfoOldUI panel = new ConflictInfoOldUI(); + ConflictTableModel model = new ConflictTableModel(getContext(),conflicts); + panel.getTable().setModel(model); + TableColumn column = panel.getTable().getColumn(getString("conflict.appointment2")); + panel.getTable().removeColumn(column); + column = panel.getTable().getColumn(getString("conflict.reservation1")); + panel.getTable().removeColumn(column); + return panel.getComponent(); + } + + + + +} + + + diff --git a/Rapla/src/org/rapla/gui/internal/edit/reservation/ReservationEditImpl.java b/Rapla/src/org/rapla/gui/internal/edit/reservation/ReservationEditImpl.java new file mode 100644 index 0000000..dfdd637 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/reservation/ReservationEditImpl.java @@ -0,0 +1,475 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.edit.reservation; + +import java.awt.BorderLayout; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyVetoException; +import java.beans.VetoableChangeListener; +import java.util.Date; + +import javax.swing.AbstractAction; +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.JToolBar; +import javax.swing.border.Border; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.apache.avalon.framework.container.ContainerUtil; +import org.rapla.components.layout.TableLayout; +import org.rapla.components.util.Command; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.RepeatingType; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.domain.ReservationHelper; +import org.rapla.facade.ModificationEvent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.ReservationController; +import org.rapla.gui.ReservationEdit; +import org.rapla.gui.toolkit.DialogUI; +import org.rapla.gui.toolkit.EmptyLineBorder; +import org.rapla.gui.toolkit.RaplaButton; +import org.rapla.gui.toolkit.RaplaFrame; + +class ReservationEditImpl extends RaplaGUIComponent implements ReservationEdit +{ + protected Reservation mutableReservation; + + JToolBar toolBar = new JToolBar(); + RaplaButton saveButtonTop = new RaplaButton(); + RaplaButton saveButton = new RaplaButton(); + RaplaButton deleteButton = new RaplaButton(); + RaplaButton closeButton = new RaplaButton(); + + JPanel mainContent = new JPanel(); + //JPanel split = new JSplitPane(JSplitPane.VERTICAL_SPLIT); + + RaplaFrame frame; + + ReservationInfoEdit reservationInfo; + AppointmentListEdit appointmentEdit ; + AllocatableSelection allocatableEdit; + + boolean bSaving = false; + boolean bDeleting = false; + boolean bSaved; + boolean bNew; + TableLayout tableLayout = new TableLayout(new double[][] { + {TableLayout.FILL} + ,{TableLayout.PREFERRED,TableLayout.PREFERRED,TableLayout.FILL} + } ); + + private final Listener listener = new Listener(); + + ReservationEditImpl(RaplaContext sm) throws RaplaException { + super( sm); + reservationInfo = new ReservationInfoEdit(sm); + appointmentEdit = new AppointmentListEdit(sm); + allocatableEdit = new AllocatableSelection(sm,true); + + // horizontalSplit.setTopComponent(appointmentEdit.getComponent()); + //horizontalSplit.setBottomComponent(allocatableEdit.getComponent()); + /* + try { + // If run on jdk < 1.3 this will throw a MethodNotFoundException + // horizontalSplit.setResizeWeight(0.1); + JSplitPane.class.getMethod("setResizeWeight",new Class[] {double.class}).invoke(horizontalSplit,new Object[] {new Double(0.1)}); + } catch (Exception ex) { + } + */ + + frame = new RaplaFrame(sm); + mainContent.setLayout( tableLayout ); + mainContent.add(reservationInfo.getComponent(),"0,0"); + mainContent.add(appointmentEdit.getComponent(),"0,1"); + mainContent.add(allocatableEdit.getComponent(),"0,2"); + //allocatableEdit.getComponent().setVisible(false); + saveButtonTop.setAction( listener ); + saveButton.setAction( listener ); + toolBar.setFloatable(false); + saveButton.setAlignmentY(JButton.CENTER_ALIGNMENT); + deleteButton.setAlignmentY(JButton.CENTER_ALIGNMENT); + closeButton.setAlignmentY(JButton.CENTER_ALIGNMENT); + JPanel buttonsPanel = new JPanel(); + //buttonsPanel.add(deleteButton); + buttonsPanel.add(saveButton); + buttonsPanel.add(closeButton); + toolBar.add(saveButtonTop); + toolBar.add(deleteButton); + deleteButton.setAction( listener ); + closeButton.addActionListener(listener); + appointmentEdit.addAppointmentListener(allocatableEdit); + appointmentEdit.addAppointmentListener(listener); + allocatableEdit.addChangeListener(listener); + reservationInfo.addChangeListener(listener); + reservationInfo.addDetailListener(listener); + frame.addVetoableChangeListener(listener); + + frame.setIconImage( getI18n().getIcon("icon.edit_window_small").getImage()); + + JPanel contentPane = (JPanel) frame.getContentPane(); + contentPane.setLayout(new BorderLayout()); + mainContent.setBorder(BorderFactory.createLoweredBevelBorder()); + contentPane.add(toolBar, BorderLayout.NORTH); + contentPane.add(buttonsPanel, BorderLayout.SOUTH); + contentPane.add(mainContent, BorderLayout.CENTER); + Dimension dimension = java.awt.Toolkit.getDefaultToolkit().getScreenSize(); + frame.setSize(new Dimension( + Math.min(dimension.width,990) + // BJO 00000032 temp fix for filter out of frame bounds + ,Math.min(dimension.height-10,720) + //,Math.min(dimension.height-10,1000) + ) + ); + + Border emptyLineBorder = new EmptyLineBorder(); + //BorderFactory.createEmptyBorder(); + Border border2 = BorderFactory.createTitledBorder(emptyLineBorder,getString("reservation.appointments")); + Border border3 = BorderFactory.createTitledBorder(emptyLineBorder,getString("reservation.allocations")); + appointmentEdit.getComponent().setBorder(border2); + allocatableEdit.getComponent().setBorder(border3); + + saveButton.setText(getString("save")); + saveButton.setIcon(getIcon("icon.save")); + + saveButtonTop.setText(getString("save")); + saveButtonTop.setMnemonic(KeyEvent.VK_S); + saveButtonTop.setIcon(getIcon("icon.save")); + + deleteButton.setText(getString("delete")); + deleteButton.setIcon(getIcon("icon.delete")); + + closeButton.setText(getString("abort")); + closeButton.setIcon(getIcon("icon.abort")); + + } + + protected void setSaved(boolean flag) { + bSaved = flag; + saveButton.setEnabled(!flag); + saveButtonTop.setEnabled(!flag); + } + + /* (non-Javadoc) + * @see org.rapla.gui.edit.reservation.IReservationEdit#isModifiedSinceLastChange() + */ + public boolean isModifiedSinceLastChange() { + return !bSaved; + } + + final private ReservationControllerImpl getPrivateReservationController() { + return (ReservationControllerImpl) getService(ReservationController.ROLE); + } + + /* (non-Javadoc) + * @see org.rapla.gui.edit.reservation.IReservationEdit#addAppointment(java.util.Date, java.util.Date, java.lang.String, int) + */ + public void addAppointment(Date start, Date end, RepeatingType repeatingType, int repeatings) throws RaplaException { + Appointment appointment = getModification().newAppointment( start, end ); + if ( repeatingType != null ) { + ReservationHelper.makeRepeatingForPeriod( getPeriodModel(),appointment, repeatingType , repeatings); + } + + mutableReservation.addAppointment(appointment); + setReservation( mutableReservation, appointment ); + setSaved(false); + frame.requestFocus(); + } + + + void deleteReservation() throws RaplaException { + if (bDeleting) + return; + getLogger().debug("Reservation has been deleted."); + DialogUI dlg = DialogUI.create( + getContext() + ,mainContent + ,true + ,getString("warning") + ,getString("warning.reservation.delete") + ); + dlg.setIcon(getIcon("icon.warning")); + dlg.start(); + closeWindow(); + } + + void updateReservation(Reservation newReservation) throws RaplaException { + if (bSaving) + return; + getLogger().debug("Reservation has been changed."); + DialogUI dlg = DialogUI.create( + getContext() + ,mainContent + ,true + ,getString("warning") + ,getString("warning.reservation.update") + ); + try { + dlg.setIcon(getIcon("icon.warning")); + dlg.start(); + setReservation((Reservation) getModification().edit(newReservation) , null); + } catch (RaplaException ex) { + showException(ex,frame); + } + } + + void refresh(ModificationEvent evt) throws RaplaException { + allocatableEdit.refresh(evt); + } + + void editReservation(Reservation mutableReservation, Appointment appointment, boolean bNew) throws RaplaException { + setSaved(!bNew); + //printBlocks( appointment ); + this.bNew = bNew; + deleteButton.setEnabled(!bNew); + + setReservation(mutableReservation, appointment); + + setTitle(); + boolean packFrame = false; + frame.place( true, packFrame); + frame.setVisible( true ); + // Insert into open ReservationEditWindows, so that + // we can't edit the same Reservation in different windows + getPrivateReservationController().addReservationEdit(this); + // #TODO this should be done in allocatableEdit + //allocatableEdit.content.setDividerLocation(0.5); + //frame.requestFocus(); + reservationInfo.requestFocus(); + getLogger().debug("New Reservation-Window created"); + } + +/* + private void printBlocks( Appointment appointment ) + { + AppointmentBlockArray array = new AppointmentBlockArray(); + { + Calendar cal = getRaplaLocale().createCalendar(); + cal.set( Calendar.YEAR, 2004); + Date start = cal.getTime(); + cal.set( Calendar.YEAR, 2007); + Date end = cal.getTime(); + appointment.createBlocks(start, end, array); + } + for ( int i=0;i< array.size();i++) + { + Date start = new Date(array.getStartAt( i )); + Date end = new Date(array.getEndAt( i )); + SimpleDateFormat format = new SimpleDateFormat(); + format.setTimeZone( getRaplaLocale().getTimeZone()); + System.out.println( format.format(start) + " - " + format.format(end)); + } + } +*/ + /* (non-Javadoc) + * @see org.rapla.gui.edit.reservation.IReservationEdit#getReservation() + */ + public Reservation getReservation() { + return mutableReservation; + } + + private void setTitle() { + String title = getI18n().format((bNew) ? + "new_reservation.format" : "edit_reservation.format" + ,getName(mutableReservation)); + frame.setTitle(title); + } + + private void setReservation(Reservation newReservation, Appointment appointment) throws RaplaException { + this.mutableReservation = newReservation; + appointmentEdit.setReservation(mutableReservation, appointment); + allocatableEdit.setReservation(mutableReservation); + reservationInfo.setReservation(mutableReservation); + } + + + public void closeWindow() { + ContainerUtil.dispose(appointmentEdit); + getPrivateReservationController().removeReservationEdit(this); + frame.dispose(); + getLogger().debug("Edit window closed."); + } + + + class Listener extends AbstractAction implements AppointmentListener,ChangeListener,VetoableChangeListener, ReservationInfoEdit.DetailListener { + private static final long serialVersionUID = 1L; + + // Implementation of ReservationListener + public void appointmentRemoved(Appointment appointment) { + setSaved(false); + } + + public void appointmentAdded(Appointment appointment) { + setSaved(false); + } + + public void appointmentChanged(Appointment appointment) { + setSaved(false); + } + + public void stateChanged(ChangeEvent evt) { + if (evt.getSource() == reservationInfo) { + getLogger().debug("ReservationInfo changed"); + setSaved(false); + setTitle(); + } + if (evt.getSource() == allocatableEdit) { + getLogger().debug("AllocatableEdit changed"); + setSaved(false); + } + } + public void detailChanged() { + boolean isMain = reservationInfo.isMainView(); + if ( isMain != appointmentEdit.getComponent().isVisible() ) { + appointmentEdit.getComponent().setVisible( isMain ); + allocatableEdit.getComponent().setVisible( isMain ); + if ( isMain ) { + tableLayout.setRow(0, TableLayout.PREFERRED); + tableLayout.setRow(1, TableLayout.PREFERRED); + tableLayout.setRow(2, TableLayout.FILL); + } else { + tableLayout.setRow(0, TableLayout.FILL); + tableLayout.setRow(1, 0); + tableLayout.setRow(2, 0); + } + mainContent.validate(); + } + } + + + public void actionPerformed(ActionEvent evt) { + try { + if (evt.getSource() == saveButton || evt.getSource() == saveButtonTop) { + save(); + } + if (evt.getSource() == deleteButton) { + delete(); + } + if (evt.getSource() == closeButton) { + if (canClose()) + closeWindow(); + } + } catch (RaplaException ex) { + showException(ex, null); + } + } + + public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException { + if (!canClose()) + throw new PropertyVetoException("Don't close",evt); + closeWindow(); + } + }; + + protected boolean canClose() { + if (!isModifiedSinceLastChange()) + return true; + + try { + DialogUI dlg = DialogUI.create( + getContext() + ,mainContent + ,true + ,getString("confirm-close.title") + ,getString("confirm-close.question") + ,new String[] { + getString("confirm-close.ok") + ,getString("back") + } + ); + dlg.setIcon(getIcon("icon.question")); + dlg.setDefault(1); + dlg.start(); + return (dlg.getSelectedIndex() == 0) ; + } catch (RaplaException e) { + return true; + } + + } + + + /* (non-Javadoc) + * @see org.rapla.gui.edit.reservation.IReservationEdit#save() + */ + public void save() throws RaplaException { + save( true ); + } + + public void save(boolean confirm) throws RaplaException { + if (mutableReservation.getAllocatables().length == 0 ) { + DialogUI dialog = DialogUI.create( + getContext() + ,frame + ,true + ,getString("warning") + ,getString("warning.no_allocatables_selected") + ,new String[] { + getString("continue") + ,getString("back") + } + ); + dialog.setIcon(getIcon("icon.warning")); + dialog.setDefault(1); + dialog.start(); + if (dialog.getSelectedIndex() != 0) + return; + } + getPrivateReservationController().save(mutableReservation,frame,new SaveCommand(), confirm); + } + + /* (non-Javadoc) + * @see org.rapla.gui.edit.reservation.IReservationEdit#delete() + */ + public void delete() throws RaplaException { + try { + DialogUI dlg = getInfoFactory().createDeleteDialog(new Object[] {mutableReservation} + ,frame); + dlg.start(); + if (dlg.getSelectedIndex() == 0) { + bDeleting = true; + getModification().remove( mutableReservation ); + closeWindow(); + } + } finally { + bDeleting = false; + } + } + + class SaveCommand implements Command { + public void execute() { + frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + try { + getModification().checkReservation(mutableReservation); + bSaving = true; + getModification().store(mutableReservation); + setSaved(true); + } catch (RaplaException ex) { + showException(ex,frame); + } finally { + frame.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + if (bSaved) + closeWindow(); + bSaving = false; + } + } + } + +} diff --git a/Rapla/src/org/rapla/gui/internal/edit/reservation/ReservationInfoEdit.java b/Rapla/src/org/rapla/gui/internal/edit/reservation/ReservationInfoEdit.java new file mode 100644 index 0000000..a1a1825 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/reservation/ReservationInfoEdit.java @@ -0,0 +1,341 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.edit.reservation; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; + +import javax.swing.BorderFactory; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.Border; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.rapla.components.layout.TableLayout; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.AttributeAnnotations; +import org.rapla.entities.dynamictype.Classifiable; +import org.rapla.entities.dynamictype.Classification; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.dynamictype.DynamicTypeAnnotations; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.internal.common.NamedListCellRenderer; +import org.rapla.gui.internal.edit.ClassificationEditUI; +import org.rapla.gui.internal.edit.EditField; +import org.rapla.gui.toolkit.EmptyLineBorder; +import org.rapla.gui.toolkit.RaplaButton; +import org.rapla.gui.toolkit.RaplaWidget; +/** + Gui for editing the {@link Classification} of a reservation. Same as + {@link org.rapla.gui.internal.edit.ClassificationEditUI}. It will only layout the + field with a {@link java.awt.FlowLayout}. + */ +public class ReservationInfoEdit extends RaplaGUIComponent + implements + RaplaWidget + ,ActionListener +{ + JPanel content = new JPanel(); + MyClassificationEditUI editUI; + + DynamicType oldDynamicType; + Classification classification; + Classification oldClassification; + Classifiable classifiable; + + ArrayList listenerList = new ArrayList(); + ArrayList detailListenerList = new ArrayList(); + JComboBox typeSelector = new JComboBox(); + RaplaButton tabSelector = new RaplaButton(); + boolean isMainViewSelected = true; + + public ReservationInfoEdit(RaplaContext sm) throws RaplaException { + super( sm); + editUI = new MyClassificationEditUI(sm); + } + + public JComponent getComponent() { + return content; + } + + + public void requestFocus() { + editUI.requestFocus(); + } + + private boolean hasSecondTab(Classification classification) { + Attribute[] atts = classification.getAttributes(); + for ( int i=0; i < atts.length; i++ ) { + String view = atts[i].getAnnotation(AttributeAnnotations.KEY_EDIT_VIEW,AttributeAnnotations.VALUE_MAIN_VIEW); + if ( view.equals(AttributeAnnotations.VALUE_ADDITIONAL_VIEW)) { + return true; + } + } + return false; + } + + public void setReservation(Classifiable classifiable) throws RaplaException { + content.removeAll(); + this.classifiable = classifiable; + classification = classifiable.getClassification(); + oldClassification = classification; + + DynamicType[] types = getQuery().getDynamicTypes( DynamicTypeAnnotations.VALUE_RESERVATION_CLASSIFICATION); + DynamicType dynamicType = classification.getType(); + oldDynamicType = dynamicType; + + typeSelector = new JComboBox( types ); + typeSelector.setSelectedItem(dynamicType); + typeSelector.setRenderer(new NamedListCellRenderer(getI18n().getLocale())); + typeSelector.addActionListener( this ); + + + content.setLayout( new BorderLayout()); + JPanel header = new JPanel(); + header.setLayout( null ); + header.add( typeSelector ); + Border border = new EmptyLineBorder(); + header.setBorder( BorderFactory.createTitledBorder( border, getString("reservation_type") +":")); + Dimension dim = typeSelector.getPreferredSize(); + typeSelector.setBounds(135,0, dim.width,dim.height); + tabSelector.setText(getString("additional-view")); + tabSelector.addActionListener( this ); + Dimension dim2 = tabSelector.getPreferredSize(); + tabSelector.setBounds(145 + dim.width ,0,dim2.width,dim2.height); + header.add( tabSelector ); + header.setPreferredSize( new Dimension(600, Math.max(dim2.height, dim.height))); + content.add( header,BorderLayout.NORTH); + content.add( editUI.getComponent(),BorderLayout.CENTER); + + tabSelector.setVisible( hasSecondTab( classification ) || !isMainViewSelected); + editUI.setObject( classification ); + + editUI.getComponent().validate(); + updateHeight(); + content.validate(); + + } + + /** registers new ChangeListener for this component. + * An ChangeEvent will be fired to every registered ChangeListener + * when the info changes. + * @see javax.swing.event.ChangeListener + * @see javax.swing.event.ChangeEvent + */ + public void addChangeListener(ChangeListener listener) { + listenerList.add(listener); + } + + /** removes a listener from this component.*/ + public void removeChangeListener(ChangeListener listener) { + listenerList.remove(listener); + } + + public ChangeListener[] getChangeListeners() { + return (ChangeListener[])listenerList.toArray(new ChangeListener[]{}); + } + + public void addDetailListener(DetailListener listener) { + detailListenerList.add(listener); + } + + /** removes a listener from this component.*/ + public void removeDetailListener(DetailListener listener) { + detailListenerList.remove(listener); + } + + public DetailListener[] getDetailListeners() { + return (DetailListener[])detailListenerList.toArray(new DetailListener[]{}); + } + + protected void fireDetailChanged() { + DetailListener[] listeners = getDetailListeners(); + for (int i = 0;i= layout.getNumColumn()) + { + col = 1; + if ( i < fields.length -1) + { + row ++; + layout.insertRow( row, 5); + height +=5; + row ++; + layout.insertRow( row, TableLayout.PREFERRED); + height += maxCompHeightInRow; + maxCompHeightInRow = 0; + } + } + } + height += maxCompHeightInRow; + + } + + public void requestFocus() { + if (fields.length>0) + fields[0].getComponent().requestFocus(); + } + + public void stateChanged(ChangeEvent evt) { + try { + Object o = this.classification; + ((EditField)evt.getSource()).mapTo( o ); + fireInfoChanged(); + } catch (RaplaException ex) { + showException(ex, this.getComponent()); + } + } + } + + public boolean isMainView() { + return isMainViewSelected; + } + + +} + diff --git a/Rapla/src/org/rapla/gui/internal/edit/reservation/SortedListModel.java b/Rapla/src/org/rapla/gui/internal/edit/reservation/SortedListModel.java new file mode 100644 index 0000000..30d1ff2 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/edit/reservation/SortedListModel.java @@ -0,0 +1,377 @@ +package org.rapla.gui.internal.edit.reservation; + +/* + * SortedListModel.java + * + * Copyright 2006 Sun Microsystems, Inc. ALL RIGHTS RESERVED Use of + * this software is authorized pursuant to the terms of the license + * found at http://developers.sun.com/berkeley_license.html . + * + */ + +import java.text.Collator; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import javax.swing.AbstractListModel; +import javax.swing.DefaultListModel; +import javax.swing.ListModel; +import javax.swing.event.EventListenerList; +import javax.swing.event.ListDataEvent; +import javax.swing.event.ListDataListener; +import java.text.Collator; + +/** + * SortedListModel decorates an unsorted ListModel to provide + * a sorted model. You can create a SortedListModel from models you + * already have. Place the SortedListModel into a JList, for example, to provide + * a sorted view of your underlying model. + * + * @author John O'Conner + */ +public class SortedListModel extends AbstractListModel { + + private SortedListModel() {} + + /** + * Create a SortedListModel from an existing model + * using a default text comparator for the default Locale. Sort + * in ascending order. + * @param model the underlying, unsorted ListModel + */ + public SortedListModel(ListModel model) { + this(model, SortOrder.ASCENDING, null); + } + + /** + * Create a SortedListModel from an existing model + * using a specific comparator and sort order. Use + * a default text comparator. + * + *@param model the unsorted list model + *@param sortOrder that should be used + */ + public SortedListModel(ListModel model, SortOrder sortOrder) { + this(model, sortOrder, null); + } + + /** + * Create a SortedListModel from an existing model. Sort the model + * in the specified sort order using the given comparator. + * + *@param model + *@param sortOrder + *@param comp + * + */ + public SortedListModel(ListModel model, SortOrder sortOrder, Comparator comp) { + unsortedModel = model; + unsortedModel.addListDataListener(new ListDataListener() { + public void intervalAdded(ListDataEvent e) { + unsortedIntervalAdded(e); + } + + public void intervalRemoved(ListDataEvent e) { + unsortedIntervalRemoved(e); + } + + public void contentsChanged(ListDataEvent e) { + unsortedContentsChanged(e); + } + + }); + this.sortOrder = sortOrder; + if (comp != null) { + comparator = comp; + } else { + comparator = Collator.getInstance(); + } + + // get base model info + int size = model.getSize(); + sortedModel = new ArrayList(size); + for (int x = 0; x < size; ++x) { + SortedListEntry entry = new SortedListEntry(x); + int insertionPoint = findInsertionPoint(entry); + sortedModel.add(insertionPoint, entry); + } + } + + /** + * Retrieve the sorted entry from the original model + * @param index index of an entry in the sorted model + * @return element in the original model to which our entry points + */ + public Object getElementAt(int index) throws IndexOutOfBoundsException { + int modelIndex = toUnsortedModelIndex(index); + Object element = unsortedModel.getElementAt(modelIndex); + return element; + } + + /** + * Retrieve the size of the underlying model + * @return size of the model + */ + public int getSize() { + int size = sortedModel.size(); + return size; + } + + + /** + * Convert sorted model index to an unsorted model index. + * + *@param index an index in the sorted model + *@return modelIndex an index in the unsorted model + * + */ + public int toUnsortedModelIndex(int index) throws IndexOutOfBoundsException { + int modelIndex = -1; + SortedListEntry entry = sortedModel.get(index); + modelIndex = entry.getIndex(); + return modelIndex; + + } + + /** + * Convert an array of sorted model indices to their unsorted model indices. Sort + * the resulting set of indices. + * + *@param sortedSelectedIndices indices of selected elements in the sorted model + * or sorted view + *@return unsortedSelectedIndices selected indices in the unsorted model + */ + public int[] toUnsortedModelIndices(int[] sortedSelectedIndices) { + int[] unsortedSelectedIndices = new int[sortedSelectedIndices.length]; + int x = 0; + for(int sortedIndex: sortedSelectedIndices) { + unsortedSelectedIndices[x++] = toUnsortedModelIndex(sortedIndex); + } + // sort the array of indices before returning + Arrays.sort(unsortedSelectedIndices); + return unsortedSelectedIndices; + + } + + /** + * Convert an unsorted model index to a sorted model index. + * + * @param unsortedIndex an element index in the unsorted model + * @return sortedIndex an element index in the sorted model + */ + public int toSortedModelIndex(int unsortedIndex) { + int sortedIndex = -1; + int x = -1; + for (SortedListEntry entry : sortedModel) { + ++x; + if (entry.getIndex() == unsortedIndex) { + sortedIndex = x; + break; + } + } + return sortedIndex; + } + + /** + * Convert an array of unsorted model selection indices to + * indices in the sorted model. Sort the model indices from + * low to high to duplicate JList's getSelectedIndices method + * + * @param unsortedModelIndices + * @return an array of selected indices in the sorted model + */ + public int[] toSortedModelIndices(int[] unsortedModelIndices) { + int[] sortedModelIndices = new int[unsortedModelIndices.length]; + int x = 0; + for(int unsortedIndex : unsortedModelIndices) { + sortedModelIndices[x++] = toSortedModelIndex(unsortedIndex); + } + Arrays.sort(sortedModelIndices); + return sortedModelIndices; + } + + private void resetModelData() { + int index = 0; + for (SortedListEntry entry : sortedModel) { + entry.setIndex(index++); + } + } + + public void setComparator(Comparator comp) { + if (comp == null) { + sortOrder = SortOrder.UNORDERED; + comparator = Collator.getInstance(); + resetModelData(); + } else { + comparator = comp; + Collections.sort(sortedModel); + } + fireContentsChanged(ListDataEvent.CONTENTS_CHANGED, 0, sortedModel.size()-1); + } + + /** + * Change the sort order of the model at runtime + * @param sortOrder + */ + public void setSortOrder(SortOrder sortOrder) { + if (this.sortOrder != sortOrder) { + this.sortOrder = sortOrder; + if (sortOrder == SortOrder.UNORDERED) { + resetModelData(); + } else { + Collections.sort(sortedModel); + } + fireContentsChanged(ListDataEvent.CONTENTS_CHANGED, 0, sortedModel.size()-1); + } + } + + /** + * Update the sorted model whenever new items + * are added to the original/decorated model. + * + */ + private void unsortedIntervalAdded(ListDataEvent e) { + int begin = e.getIndex0(); + int end = e.getIndex1(); + int nElementsAdded = end-begin+1; + + /* Items in the decorated model have shifted in flight. + * Increment our model pointers into the decorated model. + * We must increment indices that intersect with the insertion + * point in the decorated model. + */ + for (SortedListEntry entry: sortedModel) { + int index = entry.getIndex(); + // if our model points to a model index >= to where + // new model entries are added, we must bump up their index + if (index >= begin) { + entry.setIndex(index+nElementsAdded); + } + } + + // now add the new items from the decorated model + for (int x = begin; x <= end; ++x) { + SortedListEntry newEntry = new SortedListEntry(x); + int insertionPoint = findInsertionPoint(newEntry); + sortedModel.add(insertionPoint, newEntry); + fireIntervalAdded(ListDataEvent.INTERVAL_ADDED, insertionPoint, insertionPoint); + } + } + + /** + * Update this model when items are removed from the original/decorated + * model. Also, let our listeners know that we've removed items. + */ + private void unsortedIntervalRemoved(ListDataEvent e) { + int begin = e.getIndex0(); + int end = e.getIndex1(); + int nElementsRemoved = end-begin+1; + + /* + * Move from end to beginning of our sorted model, updating + * element indices into the decorated model or removing + * elements as necessary + */ + int sortedSize = sortedModel.size(); + boolean[] bElementRemoved = new boolean[sortedSize]; + for (int x = sortedSize-1; x >=0; --x) { + SortedListEntry entry = sortedModel.get(x); + int index = entry.getIndex(); + if (index > end) { + entry.setIndex(index - nElementsRemoved); + } else if (index >= begin) { + sortedModel.remove(x); + bElementRemoved[x] = true; + } + } + /* + * Let listeners know that we've removed items. + */ + for(int x = bElementRemoved.length-1; x>=0; --x) { + if (bElementRemoved[x]) { + fireIntervalRemoved(ListDataEvent.INTERVAL_REMOVED, x, x); + } + } + + } + + + /** + * Resort the sorted model if there are changes in the original + * unsorted model. Let any listeners know about changes. Since I don't + * track specific changes, sort everywhere and redisplay all items. + */ + private void unsortedContentsChanged(ListDataEvent e) { + Collections.sort(sortedModel); + fireContentsChanged(ListDataEvent.CONTENTS_CHANGED, 0, sortedModel.size()-1); + } + + /** + * Internal helper method to find the insertion point for a new + * entry in the sorted model. + */ + private int findInsertionPoint(SortedListEntry entry) { + int insertionPoint = sortedModel.size(); + if (sortOrder != SortOrder.UNORDERED) { + insertionPoint = Collections.binarySearch((List)sortedModel, entry); + if (insertionPoint < 0) { + insertionPoint = -(insertionPoint +1); + } + } + return insertionPoint; + } + + private List sortedModel; + private ListModel unsortedModel; + private Comparator comparator; + private SortOrder sortOrder; + + public enum SortOrder { + UNORDERED, + ASCENDING, + DESCENDING; + } + + class SortedListEntry implements Comparable { + private SortedListEntry() { + + } + + public SortedListEntry(int index) { + this.index = index; + } + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } + + public int compareTo(Object o) { + // retrieve the element that this entry points to + // in the original model + Object thisElement = unsortedModel.getElementAt(index); + SortedListEntry thatEntry = (SortedListEntry)o; + // retrieve the element that thatEntry points to in the original + // model + Object thatElement = unsortedModel.getElementAt(thatEntry.getIndex()); + if (comparator instanceof Collator) { + thisElement = thisElement.toString(); + thatElement = thatElement.toString(); + } + // compare the base model's elements using the provided comparator + int comparison = comparator.compare(thisElement, thatElement); + // convert to descending order as necessary + if (sortOrder == SortOrder.DESCENDING) { + comparison = -comparison; + } + return comparison; + } + + private int index; + } +} diff --git a/Rapla/src/org/rapla/gui/internal/print/CalendarPrintDialog.java b/Rapla/src/org/rapla/gui/internal/print/CalendarPrintDialog.java new file mode 100644 index 0000000..8feaf4c --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/print/CalendarPrintDialog.java @@ -0,0 +1,336 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.gui.internal.print; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.print.PageFormat; +import java.awt.print.Paper; +import java.awt.print.Printable; +import java.awt.print.PrinterException; + +import javax.swing.AbstractAction; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; + +import org.rapla.components.iolayer.ComponentPrinter; +import org.rapla.components.iolayer.IOInterface; +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaDefaultContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.SwingCalendarView; +import org.rapla.gui.ViewFactory; +import org.rapla.gui.internal.common.CalendarSelectionModel; +import org.rapla.gui.toolkit.DialogUI; +import org.rapla.gui.toolkit.ErrorDialog; +import org.rapla.gui.toolkit.RaplaButton; +import org.rapla.plugin.RaplaExtensionPoints; + + +public class CalendarPrintDialog extends DialogUI +{ + private static final long serialVersionUID = 1L; + + private JPanel titlePanel = new JPanel(); + private JPanel southPanel = new JPanel(); + private JLabel titleLabel = new JLabel(); + private JLabel sizeLabel = new JLabel(); + private JComboBox sizeChooser; + private JTextField titleEdit = new JTextField(); + + private RaplaButton cancelbutton; + private RaplaButton formatbutton; + private RaplaButton printbutton; + private RaplaButton savebutton; + private JScrollPane scrollPane; + + IOInterface printTool; + ExportServiceList exportServiceList; + + public static int[] sizes = new int[] {50,60,70,80,90,100,120,150,180,200}; + public static double defaultBorder = 11.0; //11 mm defaultBorder + + I18nBundle i18n; + + Listener listener = new Listener(); + PageFormat m_format; + + protected SwingCalendarView currentView; + CalendarSelectionModel model; + Printable printable; + + private JComponent page = new JComponent() + { + private static final long serialVersionUID = 1L; + + public void paint(Graphics g) + { + try { + paintPaper( g, m_format); + printable.print( g, m_format, 0); + } catch (PrinterException e) { + e.printStackTrace(); + } + } + + protected void paintPaper(Graphics g, PageFormat format) { + g.setColor(Color.white); + g.fillRect(1, 1, (int)format.getWidth(), (int)format.getHeight()); + g.setColor(Color.black); + g.drawRect(1, 1, (int)format.getWidth() - 2, (int)format.getHeight() - 2); + } + + }; + + + public static CalendarPrintDialog create(RaplaContext sm,Component owner,boolean modal,CalendarSelectionModel model,PageFormat format) throws RaplaException { + CalendarPrintDialog dlg; + Component topLevel = getOwnerWindow(owner); + if (topLevel instanceof Dialog) + dlg = new CalendarPrintDialog(sm,(Dialog)topLevel); + else + dlg = new CalendarPrintDialog(sm,(Frame)topLevel); + + try { + dlg.init(modal,model,format); + } catch (Exception ex) { + throw new RaplaException( ex ); + } + return dlg; + } + + protected CalendarPrintDialog(RaplaContext sm,Dialog owner) throws RaplaException { + super(sm,owner); + exportServiceList = new ExportServiceList( sm); + } + + protected CalendarPrintDialog(RaplaContext sm,Frame owner) throws RaplaException { + super(sm,owner); + exportServiceList = new ExportServiceList( sm); + } + + private void init(boolean modal,CalendarSelectionModel model,PageFormat format) throws Exception { + super.init(modal,new JPanel(),new String[] {"print","format","print_to_file","cancel"}); + this.model = model; + + RaplaContext context = getContext(); + printTool = (IOInterface) context.lookup(IOInterface.ROLE); + i18n = (I18nBundle) context.lookup(I18nBundle.ROLE + "/org.rapla.RaplaResources"); + + m_format = format; + if (m_format == null) { + m_format = createPageFormat(); + m_format.setOrientation(m_format.getOrientation()); + } + + ViewFactory factory = (ViewFactory)context.lookup( RaplaExtensionPoints.CALENDAR_VIEW_EXTENSION + "/" + model.getViewId()); + RaplaDefaultContext contextWithPrintInfo = new RaplaDefaultContext(context); + contextWithPrintInfo.put(ViewFactory.PRINT_CONTEXT, new Boolean(true)); + currentView = factory.createSwingView( contextWithPrintInfo, model, false); + if ( currentView instanceof Printable) + { + printable = (Printable)currentView; + } + else + { + printable = new ComponentPrinter( currentView.getComponent(), true); + } + String title = model.getTitle(); + content.setLayout(new BorderLayout()); + titlePanel.add(titleLabel); + titlePanel.add(titleEdit); + new RaplaGUIComponent( context).addCopyPaste(titleEdit); + + String[] blockSizes = new String[sizes.length]; + for (int i=0;i0); + //swingCalendar.setPrintView(true); + currentView.update(); + sizeLabel.setText(i18n.getString("width") + ":"); + titleLabel.setText(i18n.getString("weekview.print.title_textfield")+":"); + setTitle(i18n.getString("weekview.print.dialog_title")); + printbutton.setIcon(i18n.getIcon("icon.print")); + savebutton.setText(i18n.getString("print_to_file")); + savebutton.setIcon(i18n.getIcon("icon.save")); + printbutton.setText(i18n.getString("print")); + formatbutton.setText(i18n.getString("weekview.print.format_button")); + cancelbutton.setText(i18n.getString("cancel")); + cancelbutton.setIcon(i18n.getIcon("icon.cancel")); + + /* + if (getSession().getValue(LAST_SELECTED_SIZE) != null) + weekview.setSlotSize(((Integer)getSession().getValue(LAST_SELECTED_SIZE)).intValue()); + */ + int columnSize = model.getSize(); + sizeChooser.setSelectedItem(String.valueOf(columnSize)); + + titleEdit.addActionListener(listener); + titleEdit.addKeyListener(listener); + sizeChooser.addActionListener(listener); + + } + + private void updateSizes( PageFormat format) + { + page.setPreferredSize( new Dimension( 900,900)); + //currentView.getComponent().setBounds(0,0,2000,1000); + //currentView.getComponent().setBounds(0,0,1000,1000); + } + + private PageFormat createPageFormat() { + PageFormat format= (PageFormat) printTool.defaultPage().clone(); + format.setOrientation(PageFormat.LANDSCAPE); + Paper paper = format.getPaper(); + paper.setImageableArea( + defaultBorder * IOInterface.MM_TO_INCH * 72 + ,defaultBorder * IOInterface.MM_TO_INCH * 72 + ,paper.getWidth() - 2 * defaultBorder * IOInterface.MM_TO_INCH * 72 + ,paper.getHeight() - 2 * defaultBorder * IOInterface.MM_TO_INCH * 72 + ); + format.setPaper(paper); + return format; + } + + public void start() { + super.start(); + } + + private class Listener extends AbstractAction implements KeyListener { + private static final long serialVersionUID = 1L; + + public void keyReleased(KeyEvent evt) { + try { + model.setTitle(titleEdit.getText()); + currentView.update(); + scrollPane.invalidate(); + scrollPane.repaint(); + } catch (Exception ex) { + showException(ex); + } + } + public void keyTyped(KeyEvent evt) { + } + public void keyPressed(KeyEvent evt) { + } + public void actionPerformed(ActionEvent evt) { + try { + if (evt.getSource()==sizeChooser) { + try { + int slotSize = Integer.valueOf((String)sizeChooser.getSelectedItem()).intValue(); + model.setColumnSize(slotSize); + } catch (Exception ex) { + return; + } + currentView.update(); + scrollPane.invalidate(); + scrollPane.repaint(); + } + if (evt.getSource()==titleEdit) { + model.setTitle(titleEdit.getText()); + currentView.update(); + scrollPane.invalidate(); + scrollPane.repaint(); + } + + + if (evt.getSource()==formatbutton) { + m_format= printTool.showFormatDialog(m_format); + updateSizes( m_format); + scrollPane.invalidate(); + scrollPane.repaint(); + } + + if (evt.getSource()==printbutton) { + if (printTool.print(printable, m_format, true)) + { + // We can't close or otherwise it won't work under windows + //close(); + } + } + + if (evt.getSource()==savebutton) { + exportServiceList.export( + printable + ,m_format + ,scrollPane + ); + } + + } catch (Exception ex) { + showException(ex); + } + } + } + + public void showException(Exception ex) { + ErrorDialog dialog; + try { + dialog = new ErrorDialog(getContext()); + dialog.showExceptionDialog(ex,this); + } catch (RaplaException e) { + } + } +} + diff --git a/Rapla/src/org/rapla/gui/internal/print/ExportService.java b/Rapla/src/org/rapla/gui/internal/print/ExportService.java new file mode 100644 index 0000000..3c228b6 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/print/ExportService.java @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.gui.internal.print; + +import java.awt.Component; +import java.awt.print.PageFormat; +import java.awt.print.Printable; + + +public interface ExportService extends org.rapla.entities.Named { + void export(Printable printable,PageFormat pageFormat,Component parentComponent) throws Exception; +} diff --git a/Rapla/src/org/rapla/gui/internal/print/ExportServiceList.java b/Rapla/src/org/rapla/gui/internal/print/ExportServiceList.java new file mode 100644 index 0000000..b2fbac8 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/print/ExportServiceList.java @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas, Bettina Lademann | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.print; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.print.PageFormat; +import java.awt.print.Printable; +import java.util.Collection; +import java.util.HashMap; + +import javax.swing.BorderFactory; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.ListSelectionModel; + +import org.rapla.components.iolayer.IOInterface; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaContextException; +import org.rapla.framework.RaplaException; +import org.rapla.framework.StartupEnvironment; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.internal.common.NamedListCellRenderer; +import org.rapla.gui.toolkit.DialogUI; + + +public class ExportServiceList extends RaplaGUIComponent { + + HashMap exporters = new HashMap(); + /** + * @param sm + * @throws RaplaException + */ + public ExportServiceList(RaplaContext sm) throws RaplaException { + super(sm); + IOInterface printInterface = (IOInterface) getService( IOInterface.ROLE); + boolean applet =((StartupEnvironment)getContext().lookup(StartupEnvironment.ROLE)).getStartupMode() == StartupEnvironment.APPLET; + if (printInterface.supportsPostscriptExport() && !applet) { + PSExportService exportService = new PSExportService(getContext()); + addService("psexport",exportService); + } + } + + public void export(Printable printable,PageFormat pageFormat,Component parentComponent) throws Exception + { + Collection services = exporters.values(); + Object[] serviceArray = services.toArray(); + JList list = new JList(serviceArray); + JPanel panel = new JPanel(); + panel.setLayout(new BorderLayout()); + panel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); + panel.add(new JLabel(getString("weekview.print.choose_export")),BorderLayout.NORTH); + panel.add(list,BorderLayout.CENTER); + list.setCellRenderer(new NamedListCellRenderer(getI18n().getLocale())); + list.setSelectedIndex(0); + list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + DialogUI dlg = DialogUI.create(getContext(),parentComponent,true,panel, + new String[] { + getString("export") + ,getString("cancel") + }); + dlg.setTitle(getString("weekview.print.choose_export")); + dlg.getButton(0).setIcon(getIcon("icon.save")); + dlg.getButton(1).setIcon(getIcon("icon.cancel")); + dlg.start(); + if (dlg.getSelectedIndex() != 0 || list.getSelectedIndex() == -1) + return; + + ExportService selectedService = (ExportService)serviceArray[list.getSelectedIndex()]; + selectedService.export(printable,pageFormat, parentComponent); + } + + public void addService(Object policy,ExportService exportService) { + exporters.put(policy, exportService); + } + + public void removeService(Object policy) { + exporters.remove(policy); + } + + public ExportService select(Object policy) throws RaplaContextException { + ExportService result = (ExportService) exporters.get(policy); + if (result == null) + throw new RaplaContextException("ExportService", "Service not found for key " + policy); + return result; + } + + public boolean isSelectable(Object policy) { + return exporters.get(policy) != null; + } + + public ExportService[] getServices() { + return (ExportService[]) exporters.values().toArray(new ExportService[0]); + } + +} diff --git a/Rapla/src/org/rapla/gui/internal/print/PSExportService.java b/Rapla/src/org/rapla/gui/internal/print/PSExportService.java new file mode 100644 index 0000000..d1c2ebd --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/print/PSExportService.java @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas, Bettina Lademann | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.print; + +import java.awt.Component; +import java.awt.print.PageFormat; +import java.awt.print.Printable; +import java.util.Locale; + +import org.rapla.components.iolayer.IOInterface; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaGUIComponent; + + + +public class PSExportService extends RaplaGUIComponent implements ExportService { + public final static String EXPORT_DIR = PSExportService.class.getName() + ".dir"; + IOInterface printInterface; + + + public PSExportService(RaplaContext sm) throws RaplaException { + super(sm); + printInterface = (IOInterface) getService(IOInterface.ROLE); + } + + + public void export(Printable printable,PageFormat pageFormat,Component parentComponent) throws Exception + { + String dir = (String) getSessionMap().get(EXPORT_DIR); + String file = printInterface.saveAsPostscriptShowDialog + ( + dir + ,printable + ,pageFormat + ,false + ,parentComponent + ); + if (file != null) + getSessionMap().put(EXPORT_DIR,file); + } + + public String getName(Locale locale) { + return getI18n().getString("weekview.print.postscript"); + } +} diff --git a/Rapla/src/org/rapla/gui/internal/print/PrintAction.java b/Rapla/src/org/rapla/gui/internal/print/PrintAction.java new file mode 100644 index 0000000..d443d94 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/print/PrintAction.java @@ -0,0 +1,79 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas, Bettina Lademann | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.print; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.print.PageFormat; + +import javax.swing.SwingUtilities; + +import org.rapla.client.ClientService; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaAction; +import org.rapla.gui.internal.common.CalendarSelectionModel; + + +public class PrintAction extends RaplaAction { + CalendarSelectionModel model; + PageFormat m_pageFormat; + public PrintAction(RaplaContext sm) throws RaplaException { + super( sm); + setEnabled(false); + putValue(NAME,getString("print")); + putValue(SMALL_ICON,getIcon("icon.print")); + } + + public void setModel(CalendarSelectionModel settings) { + this.model = settings; + setEnabled(settings != null); + } + + + public void setPageFormat(PageFormat pageFormat) { + m_pageFormat = pageFormat; + } + + public void actionPerformed(ActionEvent evt) { + Component parent = (Component)getService(ClientService.MAIN_COMPONENT); + try { + boolean modal = true; + final CalendarPrintDialog dialog = CalendarPrintDialog.create(getContext(),parent,modal, model, m_pageFormat); + final Dimension dimension = java.awt.Toolkit.getDefaultToolkit().getScreenSize(); + dialog.setSize(new Dimension( + Math.min(dimension.width,900) + ,Math.min(dimension.height-10,700) + ) + ); + + SwingUtilities.invokeLater( new Runnable() { + public void run() + { + dialog.setSize(new Dimension( + Math.min(dimension.width,900) + ,Math.min(dimension.height-11,699) + ) + ); + } + + } + ); + dialog.startNoPack(); + + } catch (Exception ex) { + showException(ex, parent); + } + } +} + diff --git a/Rapla/src/org/rapla/gui/internal/splitpanes/RaplaConflictSelectionPane.java b/Rapla/src/org/rapla/gui/internal/splitpanes/RaplaConflictSelectionPane.java new file mode 100644 index 0000000..81eeb5f --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/splitpanes/RaplaConflictSelectionPane.java @@ -0,0 +1,69 @@ +/*--------------------------------------------------------------------------* | Copyright (C) 2008 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.gui.internal.splitpanes; + +import java.util.Date; + +import javax.swing.tree.DefaultTreeModel; + +import org.rapla.entities.User; +import org.rapla.entities.dynamictype.ClassificationFilter; +import org.rapla.facade.Conflict; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.internal.RaplaSelectionPane; +import org.rapla.gui.internal.common.CalendarModelImpl; +import org.rapla.gui.internal.common.CalendarSelectionModel; +import org.rapla.gui.internal.common.MultiCalendarView; +import org.rapla.gui.internal.view.TreeFactoryImpl; +import org.rapla.gui.toolkit.PopupEvent; + +public class RaplaConflictSelectionPane extends RaplaSelectionPane { + + public RaplaConflictSelectionPane(RaplaContext context, MultiCalendarView view, CalendarSelectionModel model) throws RaplaException { + super(context, view, model); + } + + + protected DefaultTreeModel generateTree() throws RaplaException { + ClassificationFilter[] filter = getModel().getAllocatableFilter(); + boolean onlyOwn = model.isOnlyCurrentUserSelected(); + User conflictUser = onlyOwn ? getUser() : null; + DefaultTreeModel treeModel = ((TreeFactoryImpl) getTreeFactory()).createConflictModel(filter, conflictUser); + return treeModel; + } + + + public void applyFilter() throws RaplaException { + Object lastSelected = treeSelection.getInfoElement(); + boolean lastClickedElementIsConflict = lastSelected instanceof Conflict; + if (lastClickedElementIsConflict) { + Date[] date = ((CalendarModelImpl) model).getConflictDates(); + if (date.length > 0) { + if (model.getStartDate() != null && model.getStartDate().after(date[0])) { + model.setStartDate(date[0]); + } + if (model.getEndDate() != null && model.getEndDate().before(date[date.length - 1])) { + model.setEndDate(date[date.length - 1]); + } + model.setSelectedDate(date[0]); + } + } + super.applyFilter(); + } + + @Override protected void showTreePopup(PopupEvent evt) + { + // No Popup visible + } +} diff --git a/Rapla/src/org/rapla/gui/internal/splitpanes/RaplaFiltrableSelectionPane.java b/Rapla/src/org/rapla/gui/internal/splitpanes/RaplaFiltrableSelectionPane.java new file mode 100644 index 0000000..8e39597 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/splitpanes/RaplaFiltrableSelectionPane.java @@ -0,0 +1,128 @@ +/*--------------------------------------------------------------------------* | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.gui.internal.splitpanes; + +import java.awt.BorderLayout; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.HashSet; + +import javax.swing.JComponent; +import javax.swing.Popup; +import javax.swing.PopupFactory; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.tree.DefaultTreeModel; + +import org.rapla.components.calendar.RaplaArrowButton; +import org.rapla.entities.dynamictype.ClassificationFilter; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.internal.RaplaSelectionPane; +import org.rapla.gui.internal.common.CalendarSelectionModel; +import org.rapla.gui.internal.common.MultiCalendarView; +import org.rapla.gui.internal.edit.ClassifiableFilterEdit; + +public abstract class RaplaFiltrableSelectionPane extends RaplaSelectionPane { + + boolean treeListenersEnabled = true; + protected FilterEdit filterEdit; + + + public RaplaFiltrableSelectionPane(final RaplaContext context, MultiCalendarView view, final CalendarSelectionModel model) throws RaplaException { + super(context, view, model); + buttonsPanel.setLayout( new BorderLayout()); + filterEdit = new FilterEdit(context, model); + buttonsPanel.add(filterEdit.getButton(), BorderLayout.EAST); + } + + + protected abstract DefaultTreeModel generateTree() throws RaplaException; + + + protected HashSet getSelectedObjects() throws RaplaException { + HashSet elements = new HashSet(treeSelection.getSelectedElements()); + getModel().setSelectedObjects(elements); + return elements; + } + + public class FilterEdit extends RaplaGUIComponent + { + protected RaplaArrowButton filterButton; + Popup popup; + ClassifiableFilterEdit ui; + + public FilterEdit(final RaplaContext context,final CalendarSelectionModel model) throws RaplaException + { + super(context); + filterButton = new RaplaArrowButton('v'); + filterButton.setText(getString("filter")); + filterButton.setSize(80,18); + filterButton.addActionListener( new ActionListener() + { + public void actionPerformed(ActionEvent e) { + + if ( popup != null) + { + popup.hide(); + popup= null; + filterButton.setChar('v'); + return; + } + boolean isResourceOnly = true; + try { + if ( ui != null) + { + ui.removeChangeListener( listener); + } + ui = new ClassifiableFilterEdit( context, isResourceOnly); + ui.addChangeListener(listener); + ui.setFilter( model); + final Point locationOnScreen = filterButton.getLocationOnScreen(); + final int y = locationOnScreen.y + 18; + final int x = locationOnScreen.x; + popup = PopupFactory.getSharedInstance().getPopup( filterButton, ui.getComponent(), x, y); + popup.show(); + filterButton.setChar('^'); + } catch (Exception ex) { + showException(ex, getComponent()); + } + } + + }); + + } + + private ChangeListener listener = new ChangeListener() { + + public void stateChanged(ChangeEvent e) { + try { + final ClassificationFilter[] filters = ui.getFilters(); + model.setAllocatableFilter( filters); + updateTree(); + applyFilter(); + } catch (Exception ex) { + showException(ex, getComponent()); + } + } + }; + public JComponent getButton() + { + return filterButton; + } + + } + +} diff --git a/Rapla/src/org/rapla/gui/internal/splitpanes/RaplaResourceSelectionPane.java b/Rapla/src/org/rapla/gui/internal/splitpanes/RaplaResourceSelectionPane.java new file mode 100644 index 0000000..fb5e9ac --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/splitpanes/RaplaResourceSelectionPane.java @@ -0,0 +1,147 @@ +/*--------------------------------------------------------------------------* | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.gui.internal.splitpanes; + +import java.awt.Font; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +import javax.swing.JCheckBox; +import javax.swing.tree.DefaultTreeModel; + +import org.rapla.entities.Category; +import org.rapla.entities.User; +import org.rapla.entities.domain.Permission; +import org.rapla.entities.dynamictype.ClassificationFilter; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.internal.action.OnlyMyAction; +import org.rapla.gui.internal.common.CalendarSelectionModel; +import org.rapla.gui.internal.common.InternMenus; +import org.rapla.gui.internal.common.MultiCalendarView; +import org.rapla.gui.internal.view.TreeFactoryImpl; +import org.rapla.gui.toolkit.RaplaMenu; +import org.rapla.gui.toolkit.RaplaMenuItem; +/** FIXME This class is not correctly named the code should be moved elsewher*/ +public final class RaplaResourceSelectionPane extends RaplaFiltrableSelectionPane { + + OnlyMyAction myEventsAction; + RaplaMenuItem ownReservations; + JCheckBox onlyOwnReservations; + + public RaplaResourceSelectionPane(RaplaContext context, MultiCalendarView view, CalendarSelectionModel model) throws RaplaException { + super(context, view, model); + + onlyOwnReservations = new JCheckBox(); + onlyOwnReservations.setFont(onlyOwnReservations.getFont().deriveFont(Font.PLAIN, (float) 10.0)); + myEventsAction = new OnlyMyAction(context, model); + onlyOwnReservations.setAction(myEventsAction); + onlyOwnReservations.setIcon(getIcon("icon.unchecked")); + onlyOwnReservations.setSelectedIcon(getIcon("icon.checked")); + + //buttonsPanel.add(onlyOwnReservations); + + myEventsAction.addPropertyChangeListener(new PropertyChangeListener() { + + public void propertyChange(PropertyChangeEvent evt) { + try { + if ( evt.getPropertyName().equals("model")) { + updateActions(); + updateTree(); + applyFilter(); + } + } catch (Exception ex) { + showException(ex, getComponent()); + } + } + }); + + ownReservations = new RaplaMenuItem("only_own_reservations"); + ownReservations.setText(getString("only_own_reservations")); + ownReservations.setAction(myEventsAction); + ownReservations.setIcon(getIcon("icon.unchecked")); + // filterAction.setResourceOnly(false); + + RaplaMenu viewMenu = (RaplaMenu)getContext().lookup( InternMenus.VIEW_MENU_ROLE); +// RaplaMenuItem filterMenu = new RaplaMenuItem("filter"); +// filterMenu.setAction( filterAction ); +// viewMenu.insertBeforeId( filterMenu,"view_save" ); + + ownReservations = new RaplaMenuItem("only_own_reservations"); + ownReservations.setText( getString("only_own_reservations")); + ownReservations.setAction( myEventsAction ); + ownReservations.setIcon( getIcon("icon.unchecked")); + viewMenu.insertBeforeId( ownReservations, "show_tips" ); + + } + + private boolean canSeeEventsFromOthers() { + try { + Category category = getQuery().getUserGroupsCategory().getCategory(Permission.GROUP_CAN_READ_EVENTS_FROM_OTHERS); + if (category == null) { + return true; + } + User user = getUser(); + return user.isAdmin() || user.belongsTo(category); + } catch (RaplaException ex) { + return false; + } + } + + +// protected HashSet getSelectedObjects() throws RaplaException { +// HashSet elements = new HashSet(treeSelection.getSelectedElements()); +// if (ownReservations.isSelected()) { +// elements.add(getUser()); +// } +// getModel().setSelectedObjects(elements); +// return elements; +// } + + + protected DefaultTreeModel generateTree() throws RaplaException { + ClassificationFilter[] filter = getModel().getAllocatableFilter(); + boolean onlyOwn = model.isOnlyCurrentUserSelected(); + User conflictUser = onlyOwn ? getUser() : null; + final TreeFactoryImpl treeFactoryImpl = (TreeFactoryImpl) getTreeFactory(); + DefaultTreeModel treeModel = treeFactoryImpl.createModel(filter, conflictUser, null); + return treeModel; + } + + + + + private void updateActions() throws RaplaException + { + if (!canSeeEventsFromOthers()) { + myEventsAction.setEnabled(false); + } + if (myEventsAction != null) { + boolean oldState = myEventsAction.isEnabled(); + myEventsAction.setEnabled(false); + + boolean isSelected = model.isOnlyCurrentUserSelected(); + ownReservations.setIcon(isSelected ? getIcon("icon.checked") : getIcon("icon.unchecked")); + onlyOwnReservations.setSelected(isSelected); + ownReservations.setSelected(isSelected); + myEventsAction.setEnabled(oldState); + } + + if ( !canSeeEventsFromOthers()) + { + myEventsAction.setEnabled( false ); + } + + + } +} diff --git a/Rapla/src/org/rapla/gui/internal/view/AllocatableInfoUI.java b/Rapla/src/org/rapla/gui/internal/view/AllocatableInfoUI.java new file mode 100644 index 0000000..113b292 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/view/AllocatableInfoUI.java @@ -0,0 +1,141 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.view; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Locale; + +import org.rapla.entities.User; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Permission; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +public class AllocatableInfoUI extends ClassificationInfoUI { + public AllocatableInfoUI(RaplaContext sm) throws RaplaException { + super(sm); + } + + void insertPermissions( Allocatable allocatable, StringBuffer buf ) { + User user; + Date today; + try { + user = getUser(); + today = getQuery().today(); + } catch (Exception ex) { + return; + } + boolean firstPermission = true; + if ( user.isAdmin() ) { + return; + } + Permission[] permissions = allocatable.getPermissions(); + boolean everytime = false; + for ( int i = 0; i < permissions.length; i++ ) { + Permission permission = permissions[i]; + if ( permission.affectsUser ( user ) + && permission.getMinAllowed( today ) == null + && permission.getMaxAllowed( today ) == null ) { + everytime = true; + break; + } + } + + for ( int i = 0; i < permissions.length; i++ ) { + Permission permission = permissions[i]; + if ( permission.affectsUser( user ) ) { + if ( firstPermission ) { + firstPermission = false; + buf.append( "" ); + buf.append( getString( "allocatable_in_timeframe" ) ); + buf.append( ":" ); + buf.append("
    "); + if ( everytime ) { + buf.append( getString("everytime") ); + break; + } + } + + if ( permission.getMinAllowed( today ) != null ) { + Date date = permission.getMinAllowed( today ); + buf.append( getRaplaLocale().formatDate( date ) ); + } else { + buf.append(getString("open")); + } + buf.append(" - "); + if ( permission.getMaxAllowed( today ) != null ) { + Date date = permission.getMaxAllowed( today ); + buf.append( getRaplaLocale().formatDate( date ) ); + } else { + buf.append(getString("open")); + } + buf.append("
    "); + } + } + } + + protected String createHTMLAndFillLinks(Object object,LinkController controller) { + Allocatable allocatable = (Allocatable) object; + StringBuffer buf = new StringBuffer(); + insertModificationRow( allocatable, buf ); + insertClassificationTitle( allocatable, buf ); + createTable( getAttributes( allocatable, controller, false),buf,false); + return buf.toString(); + } + + public List getAttributes(Allocatable allocatable,LinkController controller, boolean excludeAdditionalInfos) { + ArrayList att = new ArrayList(); + att.addAll( super.getClassificationAttributes( allocatable, excludeAdditionalInfos )); + final Locale locale = getLocale(); + User owner = allocatable.getOwner(); + User lastChangeBy = allocatable.getLastChangedBy(); + if ( owner != null) + { + final String ownerName = owner.getName(locale); + String ownerText = encode(ownerName); + if (controller != null) + ownerText = controller.createLink(owner,ownerName); + + att.add( new Row(getString("resource.owner"), ownerText)); + } + if ( lastChangeBy != null && (owner == null || !lastChangeBy.equals(owner))) { + final String lastChangedName = lastChangeBy.getName(locale); + String lastChangeByText = encode(lastChangedName); + if (controller != null) + lastChangeByText = controller.createLink(lastChangeBy,lastChangedName); + att.add( new Row(getString("last_changed_by"), lastChangeByText)); + + } + + return att; + } + + public String getTooltip(Object object) { + Allocatable allocatable = (Allocatable) object; + StringBuffer buf = new StringBuffer(); + insertClassificationTitle( allocatable, buf ); + insertModificationRow( allocatable, buf ); + Collection att = new ArrayList(); + att.addAll(getAttributes(allocatable, null, true)); + createTable(att,buf); + insertPermissions( allocatable, buf ); + return buf.toString(); + } + + + +} + diff --git a/Rapla/src/org/rapla/gui/internal/view/AppointmentInfoUI.java b/Rapla/src/org/rapla/gui/internal/view/AppointmentInfoUI.java new file mode 100644 index 0000000..88e460a --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/view/AppointmentInfoUI.java @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.view; + + +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Reservation; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +public class AppointmentInfoUI extends ReservationInfoUI { + public AppointmentInfoUI(RaplaContext sm) throws RaplaException { + super(sm); + } + + public String getTooltip(Object object) { + Appointment appointment = (Appointment) object; + Reservation reservation = (Reservation) appointment.getReservation(); + StringBuffer buf = new StringBuffer(); + insertModificationRow( reservation, buf ); + insertAppointmentSummary( appointment, buf ); + insertClassificationTitle( reservation, buf ); + createTable( getAttributes( reservation, null, null, true),buf,false); + return buf.toString(); + } + + void insertAppointmentSummary(Appointment appointment, StringBuffer buf) { + buf.append("
    "); + buf.append( getAppointmentFormater().getSummary( appointment ) ); + buf.append("
    "); + } + + +} + diff --git a/Rapla/src/org/rapla/gui/internal/view/CategoryInfoUI.java b/Rapla/src/org/rapla/gui/internal/view/CategoryInfoUI.java new file mode 100644 index 0000000..bcef496 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/view/CategoryInfoUI.java @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.view; + +import org.rapla.entities.Category; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +class CategoryInfoUI extends HTMLInfo { + public CategoryInfoUI(RaplaContext sm) throws RaplaException { + super(sm); + } + + protected String createHTMLAndFillLinks(Object object,LinkController controller) throws RaplaException{ + Category category = (Category) object; + return category.getName( getRaplaLocale().getLocale()); + } +} + diff --git a/Rapla/src/org/rapla/gui/internal/view/ClassificationInfoUI.java b/Rapla/src/org/rapla/gui/internal/view/ClassificationInfoUI.java new file mode 100644 index 0000000..f061055 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/view/ClassificationInfoUI.java @@ -0,0 +1,130 @@ + +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.view; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.Locale; + +import org.rapla.entities.Timestamp; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.AttributeAnnotations; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.Classifiable; +import org.rapla.entities.dynamictype.Classification; +import org.rapla.entities.storage.RefEntity; +import org.rapla.entities.storage.internal.SimpleIdentifier; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +class ClassificationInfoUI extends HTMLInfo { + public ClassificationInfoUI(RaplaContext sm) throws RaplaException { + super(sm); + } + + public void insertClassificationTitle( Classifiable classifiable, StringBuffer buf ) { + Classification classification = classifiable.getClassification(); + buf.append( ""); + Locale locale = getRaplaLocale().getLocale(); + encode( classification.getType().getName(locale), buf ); + if ( classifiable instanceof RefEntity ) + { + int id = ((SimpleIdentifier)((RefEntity) classifiable).getId()).getKey(); + buf.append(" ("); + buf.append(Integer.valueOf(id)); + buf.append(")"); + } + buf.append( ""); + } + + protected void insertClassification( Classifiable classifiable, StringBuffer buf ) { + insertClassificationTitle( classifiable, buf ); + Collection att = new ArrayList(); + att.addAll(getClassificationAttributes(classifiable, false)); + createTable(att,buf,false); + } + + protected Collection getClassificationAttributes(Classifiable classifiable, boolean excludeAdditionalInfos) { + Collection att = new ArrayList(); + Classification classification = classifiable.getClassification(); + + Attribute[] attributes = classification.getAttributes(); + for (int i=0; i< attributes.length; i++) { + Attribute attribute = attributes[i]; + String view = attribute.getAnnotation( AttributeAnnotations.KEY_EDIT_VIEW, AttributeAnnotations.VALUE_MAIN_VIEW ); + if ( view.equals(AttributeAnnotations.VALUE_NO_VIEW )) { + continue; + } + if ( excludeAdditionalInfos && !view.equals( AttributeAnnotations.VALUE_MAIN_VIEW ) ) { + continue; + } + Object value = classification.getValue(attribute); + /* + if (value == null) + continue; + */ + String name = getName(attribute); + String valueString = null; + Locale locale = getRaplaLocale().getLocale(); + if (value instanceof Boolean) { + valueString = getString(((Boolean) value).booleanValue() ? "yes":"no"); + } else { + valueString = classification.getValueAsString(attribute, locale); + } + att.add (new Row(name,encode(valueString))); + } + return att; + } + + protected String getTooltip(Object object) { + Classifiable classifiable = (Classifiable) object; + StringBuffer buf = new StringBuffer(); + Collection att = new ArrayList(); + att.addAll(getClassificationAttributes(classifiable, false)); + createTable(att,buf,false); + return buf.toString(); + } + + protected String createHTMLAndFillLinks(Object object,LinkController controller) { + Classifiable classifiable = (Classifiable) object; + StringBuffer buf = new StringBuffer(); + insertClassification( classifiable, buf ); + return buf.toString(); + } + + void insertModificationRow( Timestamp timestamp, StringBuffer buf ) { + final Date createTime = timestamp.getCreateTime(); + final Date lastChangeTime = timestamp.getLastChangeTime(); + if ( lastChangeTime != null) + { + buf.append("
    "); + if ( createTime != null) + { + buf.append(getString("created_at")); + buf.append(" "); + buf.append(getRaplaLocale().formatDate(createTime)); + buf.append(", "); + } + buf.append(getString("last_changed")); + buf.append(" "); + buf.append(getRaplaLocale().formatDate(lastChangeTime)); + buf.append("
    "); + buf.append("\n"); + } + } + +} + + diff --git a/Rapla/src/org/rapla/gui/internal/view/ConflictInfoOldUI.java b/Rapla/src/org/rapla/gui/internal/view/ConflictInfoOldUI.java new file mode 100644 index 0000000..c0572c6 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/view/ConflictInfoOldUI.java @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas, Bettina Lademann | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.view; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import javax.swing.Action; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; + +import org.rapla.gui.toolkit.RaplaWidget; + +public class ConflictInfoOldUI + implements RaplaWidget +{ + JPanel content = new JPanel(); + JTable jTable1 = new JTable(); + private Action editAction = null; + + public ConflictInfoOldUI() { + content.setLayout(new BorderLayout()); + jTable1.setPreferredScrollableViewportSize(new Dimension(400, 70)); + JScrollPane scrollPane = new JScrollPane(jTable1); + content.add(scrollPane,BorderLayout.CENTER); + + jTable1.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent event) { + if(event.getClickCount() == 2 && editAction!=null) { + editAction.actionPerformed(new ActionEvent(ConflictInfoOldUI.this, ActionEvent.ACTION_PERFORMED, "")); + } + } + }); + } + + public JTable getTable() { + return jTable1; + } + + public void setEditAction(Action action) { + editAction = action; + } + + public JComponent getComponent() { + return content; + } + +} + + + diff --git a/Rapla/src/org/rapla/gui/internal/view/ConflictInfoUI.java b/Rapla/src/org/rapla/gui/internal/view/ConflictInfoUI.java new file mode 100644 index 0000000..3111226 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/view/ConflictInfoUI.java @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.view; + +import org.rapla.facade.Conflict; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +class ConflictInfoUI extends HTMLInfo { + public ConflictInfoUI(RaplaContext sm) throws RaplaException { + super(sm); + } + + protected String createHTMLAndFillLinks(Object object,LinkController controller) { + Conflict conflict = (Conflict) object; + StringBuffer buf = new StringBuffer(); + + buf.append( ""); + buf.append( getName(conflict.getAllocatable() )); + buf.append( " " ); + buf.append( getAppointmentFormater().getSummary(conflict.getAppointment1())); + buf.append( ""); + buf.append( "
    " ); + buf.append( "'" ); + buf.append( getName(conflict.getReservation1() )); + buf.append( "'" ); + buf.append( "
    " ); + buf.append( getString("with")); + buf.append( " '" ); + buf.append( getName(conflict.getReservation2() )); + buf.append( "' " ); + buf.append( " " + getString("reservation.owner") + " "); + buf.append( conflict.getUser2().getName()); + + return buf.toString(); + } + + public String getTooltip( Object object ) { + return createHTMLAndFillLinks( object, null); + } +} + diff --git a/Rapla/src/org/rapla/gui/internal/view/DeleteInfoUI.java b/Rapla/src/org/rapla/gui/internal/view/DeleteInfoUI.java new file mode 100644 index 0000000..ce68bd9 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/view/DeleteInfoUI.java @@ -0,0 +1,57 @@ + +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.view; + +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +class DeleteInfoUI extends HTMLInfo { + public DeleteInfoUI(RaplaContext sm) throws RaplaException { + super(sm); + } + + protected String createHTMLAndFillLinks(Object object,LinkController controller) { + Object[] deletables = (Object[]) object; + StringBuffer buf = new StringBuffer(); + buf.append(getString("delete.question")); + buf.append("
    "); + for (int i = 0; i"); + } + return buf.toString(); + } + + public void updateInfo(Object object, ViewTable controller) throws RaplaException { + controller.setTitle( getString("delete.title") ); + controller.setText( createHTMLAndFillLinks(object, controller) ); + } +} + + + + + + + + + + + + + + diff --git a/Rapla/src/org/rapla/gui/internal/view/DependencyInfoUI.java b/Rapla/src/org/rapla/gui/internal/view/DependencyInfoUI.java new file mode 100644 index 0000000..c31e814 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/view/DependencyInfoUI.java @@ -0,0 +1,65 @@ + +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.view; + +import java.util.Iterator; + +import org.rapla.entities.DependencyException; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +class DependencyInfoUI extends HTMLInfo { + public DependencyInfoUI(RaplaContext sm) throws RaplaException { + super(sm); + } + + protected String createHTMLAndFillLinks(Object object,LinkController controller) throws RaplaException{ + DependencyException ex = (DependencyException) object; + StringBuffer buf = new StringBuffer(); + buf.append(getString("error.dependencies")+":"); + buf.append("
    "); + Iterator it = ex.getDependencies().iterator(); + int i = 0; + while (it.hasNext()) { + Object obj = it.next(); + buf.append((++i)); + buf.append(") "); + buf.append( obj ); + buf.append("
    "); + if (i == 30 && it.hasNext()) { //BJO + buf.append("... " + (ex.getDependencies().size() - 30) + " more"); //BJO + break; + } + } + return buf.toString(); + } + + protected String getTitle(Object object, LinkController controller) { + return getString("info") + ": " + getString("error.dependencies"); + } +} + + + + + + + + + + + + + + diff --git a/Rapla/src/org/rapla/gui/internal/view/DynamicTypeInfoUI.java b/Rapla/src/org/rapla/gui/internal/view/DynamicTypeInfoUI.java new file mode 100644 index 0000000..8273878 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/view/DynamicTypeInfoUI.java @@ -0,0 +1,61 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.view; + +import java.util.ArrayList; +import java.util.Collection; + +import org.rapla.entities.Category; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.AttributeType; +import org.rapla.entities.dynamictype.ConstraintIds; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +class DynamicTypeInfoUI extends HTMLInfo { + public DynamicTypeInfoUI(RaplaContext sm) throws RaplaException { + super(sm); + } + + protected String createHTMLAndFillLinks(Object object,LinkController controller){ + DynamicType dynamicType = (DynamicType) object; + StringBuffer buf = new StringBuffer(); + Collection att = new ArrayList(); + att.add(new Row(getString("dynamictype.name"), strong( encode( getName( dynamicType ) )))); + Attribute[] attributes = dynamicType.getAttributes(); + for (int i=0;i 0 ) { + StringBuffer buf = new StringBuffer(); + int size = text.length(); + for ( int i= 0; i"); + } else { + buf.append( c ); + } // end of switch () + } // end of for () + text = buf.toString(); + } + return text; + } + + + static public void addColor(String color,StringBuffer buf) { + buf.append(" color=\""); + buf.append(color); + buf.append('\"'); + } + + static public void createTable(Collection attributes,StringBuffer buf,boolean encodeValues) { + buf.append(""); + Iterator it = attributes.iterator(); + while (it.hasNext()) { + Row att = (Row) it.next(); + buf.append("\n"); + buf.append("\n"); + buf.append(""); + buf.append("\n"); + } + buf.append("
    "); + encode(att.field,buf); + buf.append(":"); + if (att.value != null) { + if (encodeValues) + encode((String)att.value,buf); + else + buf.append(att.value); + } + buf.append("
    "); + } + + static public String createTable(Collection attributes, boolean encodeValues) { + StringBuffer buf = new StringBuffer(); + createTable(attributes, buf, encodeValues); + return buf.toString(); + } + + static public void createTable(Collection attributes,StringBuffer buf) { + createTable(attributes,buf,true); + } + + + static public String createTable(Collection attributes) { + StringBuffer buf = new StringBuffer(); + createTable(attributes,buf); + return buf.toString(); + } + + + static public void highlight(String text,StringBuffer buf) { + buf.append(""); + encode(text,buf); + buf.append(""); + } + + static public String highlight(String text) { + StringBuffer buf = new StringBuffer(); + highlight(text,buf); + return buf.toString(); + } + + static public void strong(String text,StringBuffer buf) { + buf.append(""); + encode(text,buf); + buf.append(""); + } + + static public String strong(String text) { + StringBuffer buf = new StringBuffer(); + strong(text,buf); + return buf.toString(); + } + + abstract protected String createHTMLAndFillLinks(Object object,LinkController controller) throws RaplaException ; + protected String getTooltip(Object object) { + if (object instanceof Named) + return ((Named) object).getName(getI18n().getLocale()); + return null; + } + + public class Row { + String field; + String value; + Row(String field,String value) { + this.field = field; + this.value = value; + } + public String getField() { + return field; + } + public String getValue() { + return value; + } + } + +} + + + + + + diff --git a/Rapla/src/org/rapla/gui/internal/view/InfoFactoryImpl.java b/Rapla/src/org/rapla/gui/internal/view/InfoFactoryImpl.java new file mode 100644 index 0000000..41d8c1f --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/view/InfoFactoryImpl.java @@ -0,0 +1,206 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.view; + +import java.awt.Component; +import java.awt.Point; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.StringSelection; +import java.awt.event.ActionEvent; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.AbstractAction; +import javax.swing.JComponent; + +import org.rapla.components.iolayer.ComponentPrinter; +import org.rapla.components.iolayer.IOInterface; +import org.rapla.entities.Category; +import org.rapla.entities.Named; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.User; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Period; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.facade.Conflict; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.InfoFactory; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.toolkit.DialogUI; +import org.rapla.gui.toolkit.HTMLView; +/** The factory can creatres an information-panel or dialog for +the entities of rapla. +@see ViewTable*/ +public class InfoFactoryImpl extends RaplaGUIComponent implements InfoFactory + +{ + Map views = new HashMap(); + + public InfoFactoryImpl(RaplaContext sm) throws RaplaException{ + super( sm); + views.put( DynamicType.TYPE, new DynamicTypeInfoUI(sm) ); + views.put( Reservation.TYPE, new ReservationInfoUI(sm) ); + views.put( Appointment.TYPE, new AppointmentInfoUI(sm) ); + views.put( Allocatable.TYPE, new AllocatableInfoUI(sm) ); + views.put( User.TYPE, new UserInfoUI(sm) ); + views.put( Period.TYPE, new PeriodInfoUI(sm) ); + views.put( Conflict.TYPE, new ConflictInfoUI(sm) ); + views.put( Category.TYPE, new CategoryInfoUI(sm) ); + } + + /** this method is used by the viewtable to dynamicaly create an + * appropriate HTMLInfo for the passed object + */ + HTMLInfo createView( RaplaObject object ) throws RaplaException { + if ( object == null ) + throw new RaplaException( "Could not create view for null object" ); + + HTMLInfo result = ( HTMLInfo ) views.get( object.getRaplaType() ); + if (result != null) + return result; + throw new RaplaException( "Could not create view for this object: " + object.getClass() ); + } + + public JComponent createInfoComponent( Object object ) throws RaplaException { + ViewTable viewTable = new ViewTable(getContext()); + viewTable.updateInfo( object ); + return viewTable.getComponent(); + } + + public String getToolTip(Object obj) { + return getToolTip(obj,true); + } + + public String getToolTip(Object obj,boolean wrapHtml) { + try { + if ( !(obj instanceof RaplaObject)) + { + return null; + } + RaplaObject o = (RaplaObject )obj; + if ( !views.containsKey( o.getRaplaType())) + { + return null; + } + String text = createView( o ).getTooltip( o); + if (wrapHtml && text != null) + return HTMLView.createHTMLPage( text ); + else + return text; + } catch(RaplaException ex) { + getLogger().error( ex.getMessage(), ex ); + } + if (obj instanceof Named) + return ((Named) obj).getName(getI18n().getLocale()); + return null; + } + + /* (non-Javadoc) + * @see org.rapla.gui.view.IInfoUIFactory#showInfoDialog(java.lang.Object, java.awt.Component) + */ + public void showInfoDialog( Object object, Component owner ) + throws RaplaException + { + showInfoDialog( object, owner, null); + } + + /* (non-Javadoc) + * @see org.rapla.gui.view.IInfoUIFactory#showInfoDialog(java.lang.Object, java.awt.Component, java.awt.Point) + */ + public void showInfoDialog( Object object, Component owner, Point point ) + throws RaplaException + { + + final ViewTable viewTable = new ViewTable(getContext()); + final DialogUI dlg = DialogUI.create(getContext(),owner + ,false + ,viewTable.getComponent() + ,new String[] { + getString( "copy_to_clipboard" ) + ,getString( "print" ) + ,getString( "back" ) + }); + + if ( !(object instanceof RaplaObject)) { + viewTable.updateInfoHtml( object.toString()); + } + else + { + viewTable.updateInfo( object, createView((RaplaObject)object) ); + } + dlg.setTitle( viewTable.getDialogTitle() ); + dlg.setDefault(2); + dlg.start( point ); + + dlg.getButton(0).setAction( new AbstractAction() { + private static final long serialVersionUID = 1L; + + public void actionPerformed(ActionEvent e) { + try { + DataFlavor.getTextPlainUnicodeFlavor(); + viewTable.htmlView.selectAll(); + String plainText = viewTable.htmlView.getSelectedText(); + //String htmlText = viewTable.htmlView.getText(); + //InfoSelection selection = new InfoSelection( htmlText, plainText ); + StringSelection selection = new StringSelection( plainText ); + IOInterface printTool = (IOInterface) getContext().lookup(IOInterface.ROLE); + printTool.setContents( selection, null); + } catch (Exception ex) { + showException(ex, dlg); + } + } + }); + dlg.getButton(1).setAction( new AbstractAction() { + private static final long serialVersionUID = 1L; + + public void actionPerformed(ActionEvent e) { + try { + IOInterface printTool = (IOInterface) getContext().lookup(IOInterface.ROLE); + printTool.print( + new ComponentPrinter(viewTable.htmlView, true) + , printTool.defaultPage() + ,true + ); + } catch (Exception ex) { + showException(ex, dlg); + } + } + }); + } + + /* (non-Javadoc) + * @see org.rapla.gui.view.IInfoUIFactory#createDeleteDialog(java.lang.Object[], java.awt.Component) + */ + public DialogUI createDeleteDialog( Object[] deletables, Component owner ) throws RaplaException { + ViewTable viewTable = new ViewTable(getContext()); + HTMLInfo deleteView = new DeleteInfoUI(getContext()); + DialogUI dlg = DialogUI.create(getContext(),owner + ,true + ,viewTable.getComponent() + ,new String[] { + getString( "delete.ok" ) + ,getString( "delete.abort" ) + }); + dlg.setIcon( getIcon("icon.warning") ); + dlg.getButton( 0).setIcon(getIcon("icon.delete") ); + dlg.getButton( 1).setIcon(getIcon("icon.abort") ); + dlg.setDefault(1); + viewTable.updateInfo( deletables, deleteView ); + dlg.setTitle( viewTable.getDialogTitle() ); + return dlg; + } +} + diff --git a/Rapla/src/org/rapla/gui/internal/view/LicenseInfoUI.java b/Rapla/src/org/rapla/gui/internal/view/LicenseInfoUI.java new file mode 100644 index 0000000..c73883f --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/view/LicenseInfoUI.java @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.view; +import java.awt.Component; +import java.awt.Dimension; + +import javax.swing.JComponent; +import javax.swing.JScrollPane; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; + +import org.rapla.components.xmlbundle.LocaleChangeEvent; +import org.rapla.components.xmlbundle.LocaleChangeListener; +import org.rapla.components.xmlbundle.LocaleSelector; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.toolkit.DialogUI; +import org.rapla.gui.toolkit.HTMLView; +import org.rapla.gui.toolkit.RaplaWidget; + +final public class LicenseInfoUI + extends + RaplaGUIComponent + implements + HyperlinkListener + ,RaplaWidget + ,LocaleChangeListener +{ + JScrollPane scrollPane; + HTMLView license; + LocaleSelector localeSelector; + + public LicenseInfoUI(RaplaContext sm) throws RaplaException { + super( sm); + license = new HTMLView(); + license.addHyperlinkListener(this); + scrollPane= new JScrollPane(license); + scrollPane.setOpaque(true); + scrollPane.setPreferredSize(new Dimension(450, 100)); + scrollPane.setBorder(null); + localeSelector = (LocaleSelector) sm.lookup( LocaleSelector.ROLE); + localeSelector.addLocaleChangeListener(this); + setLocale(); + } + + public void localeChanged(LocaleChangeEvent evt) { + setLocale(); + scrollPane.invalidate(); + scrollPane.repaint(); + } + + private void setLocale() { + license.setBody(getString("license.text")); + } + + public void hyperlinkUpdate(HyperlinkEvent e) { + if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + String link = e.getDescription(); + viewLicense(getComponent(), false, link); + } + } + + public JComponent getComponent() { + return scrollPane; + } + + public void viewLicense(Component owner,boolean modal,String link) { + try { + LicenseUI license = new LicenseUI( getContext()); + DialogUI dialog = DialogUI.create(getContext(),owner,modal,license.getComponent(), new String[] {getString("ok")} ); + dialog.setTitle(getString("licensedialog.title")); + dialog.setSize(600,400); + if (link.equals("warranty")) { + dialog.start(); + license.getComponent().revalidate(); + license.showBottom(); + } else { + dialog.start(); + license.getComponent().revalidate(); + license.showTop(); + } + } catch (Exception ex) { + showException(ex,owner); + } + } + +} + + + + + + + + + diff --git a/Rapla/src/org/rapla/gui/internal/view/LicenseUI.java b/Rapla/src/org/rapla/gui/internal/view/LicenseUI.java new file mode 100644 index 0000000..c1b4469 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/view/LicenseUI.java @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.view; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.GridLayout; +import java.awt.Point; +import java.io.IOException; +import java.net.URL; + +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextPane; +import javax.swing.JViewport; + +import org.rapla.components.util.IOUtil; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.toolkit.RaplaWidget; + +public class LicenseUI extends RaplaGUIComponent + implements + RaplaWidget +{ + JPanel panel = new JPanel(); + BorderLayout borderLayout1 = new BorderLayout(); + GridLayout gridLayout2 = new GridLayout(); + FlowLayout flowLayout1 = new FlowLayout(); + JTextPane license = new JTextPane(); + JScrollPane jScrollPane = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + + public LicenseUI(RaplaContext sm) throws RaplaException { + super( sm); + panel.setOpaque(true); + panel.setLayout(borderLayout1); + panel.add(jScrollPane,BorderLayout.CENTER); + license.setOpaque(false); + license.setEditable(false); + panel.setPreferredSize(new Dimension(640,400)); + + try { + String text = getLicense(); + license.setText(text); + } catch (IOException ex) { + license.setText(ex.getMessage()); + } + license.revalidate(); + + } + + public JComponent getComponent() { + return panel; + } + + private String getLicense() throws IOException { + URL url= LicenseUI.class.getClassLoader().getResource("org/rapla/COPYING"); + return new String(IOUtil.readBytes(url),"UTF-8"); + } + + public void showTop() { + JViewport viewport = new JViewport(); + viewport.setView(license); + jScrollPane.setViewport(viewport); + viewport.setViewPosition(new Point(0,0)); + } + + public void showBottom() { + JViewport viewport = new JViewport(); + viewport.setView(license); + jScrollPane.setViewport(viewport); + Dimension dim = viewport.getViewSize(); + viewport.setViewPosition(new Point(dim.width,dim.height)); + } + + + } + + + diff --git a/Rapla/src/org/rapla/gui/internal/view/LinkController.java b/Rapla/src/org/rapla/gui/internal/view/LinkController.java new file mode 100644 index 0000000..935af70 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/view/LinkController.java @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.view; + +interface LinkController +{ + void createLink(Object object,String link,StringBuffer buf); + String createLink(Object object,String link); +} + diff --git a/Rapla/src/org/rapla/gui/internal/view/PeriodInfoUI.java b/Rapla/src/org/rapla/gui/internal/view/PeriodInfoUI.java new file mode 100644 index 0000000..f698004 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/view/PeriodInfoUI.java @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.view; + +import java.util.Collection; +import java.util.ArrayList; + +import org.rapla.components.util.DateTools; +import org.rapla.entities.domain.Period; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.framework.RaplaLocale; + +class PeriodInfoUI extends HTMLInfo { + public PeriodInfoUI(RaplaContext sm) throws RaplaException { + super(sm); + } + + protected String createHTMLAndFillLinks(Object object,LinkController controller) { + Period period = (Period) object; + Collection att = new ArrayList(); + RaplaLocale loc = getRaplaLocale(); + + att.add(new Row(getString("name"), strong( encode( getName( period ) )))); + att.add(new Row( + getString("start_date") + ,loc.getWeekday( period.getStart() ) + + ' ' + + loc.formatDate(period.getStart()) + ) + ); + att.add(new Row( + getString("end_date"), + loc.getWeekday( DateTools.subDay(period.getEnd()) ) + + ' ' + + loc.formatDate( DateTools.subDay(period.getEnd()) ) + ) + ); + return createTable(att, false); + } + + protected String getTooltip(Object object) { + return createHTMLAndFillLinks( object, null); + } +} + diff --git a/Rapla/src/org/rapla/gui/internal/view/ReservationInfoUI.java b/Rapla/src/org/rapla/gui/internal/view/ReservationInfoUI.java new file mode 100644 index 0000000..c4e2309 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/view/ReservationInfoUI.java @@ -0,0 +1,174 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.view; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import org.rapla.entities.User; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Repeating; +import org.rapla.entities.domain.Reservation; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +public class ReservationInfoUI extends ClassificationInfoUI { + + public ReservationInfoUI(RaplaContext sm) throws RaplaException { + super(sm); + } + + private void addRestriction(Reservation reservation, Allocatable allocatable, StringBuffer buf) { + Appointment[] appointments = reservation.getRestriction(allocatable); + if ( appointments.length == 0 ) + return; + buf.append(""); + buf.append(" ("); + for (int i=0; i0) + buf.append(", "); + encode(getAppointmentFormater().getShortSummary(appointments[i]), buf ); + } + buf.append(")"); + buf.append(""); + } + + private String allocatableList(Reservation reservation,Allocatable[] allocatables, User user, LinkController controller) { + StringBuffer buf = new StringBuffer(); + for (int i = 0;i 0) { + att.add (new Row( getString("resources"), resourceList )); + } + Allocatable[] persons = reservation.getPersons(); + String personList = allocatableList(reservation, persons, user, controller); + if (personList.length() > 0) { + att.add (new Row( getString("persons"), personList ) ); + } + return att; + } + + void insertAllAppointments(Reservation reservation, StringBuffer buf) { + buf.append( ""); + buf.append( "\n" ); + buf.append( "\n" ); + buf.append( "\n"); + Appointment[] appointments = reservation.getAppointments(); + for (int i = 0;i\n" ); + buf.append( "\n"); + buf.append( "\n"); + buf.append( ""); + buf.append( "\n"); + } + buf.append( "
    "); + String appointmentLabel = getString("appointments"); + encode(appointmentLabel, buf); + buf.append( ":"); + buf.append( "
    \n"); + if (appointments[i].getRepeating() != null) { + buf.append (""); + } else { + buf.append (""); + } + buf.append( "\n"); + String appointmentSummary = + getAppointmentFormater().getSummary( appointments[i] ); + encode( appointmentSummary, buf ); + Repeating repeating = appointments[i].getRepeating(); + if ( repeating != null ) { + buf.append("
    "); + buf.append(""); + List periods = getPeriodModel().getPeriodsFor(appointments[i].getStart()); + String repeatingSummary = + getAppointmentFormater().getSummary(repeating,periods); + encode( repeatingSummary, buf ) ; + if ( repeating.hasExceptions() ) { + buf.append("
    "); + buf.append( getAppointmentFormater().getExceptionSummary(repeating) ); + } + buf.append("
    "); + } + buf.append( "
    \n"); + } + + + +} + diff --git a/Rapla/src/org/rapla/gui/internal/view/TreeFactoryImpl.java b/Rapla/src/org/rapla/gui/internal/view/TreeFactoryImpl.java new file mode 100644 index 0000000..1fe0de8 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/view/TreeFactoryImpl.java @@ -0,0 +1,638 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas, Bettina Lademann | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.view; + +import java.awt.Component; +import java.awt.Font; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import javax.swing.BorderFactory; +import javax.swing.Icon; +import javax.swing.JTree; +import javax.swing.UIManager; +import javax.swing.border.Border; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.MutableTreeNode; +import javax.swing.tree.TreeCellRenderer; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreeNode; + +import org.rapla.components.util.Assert; +import org.rapla.components.util.InverseComparator; +import org.rapla.entities.Category; +import org.rapla.entities.Named; +import org.rapla.entities.NamedComparator; +import org.rapla.entities.RaplaType; +import org.rapla.entities.User; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Period; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.domain.ReservationStartComparator; +import org.rapla.entities.dynamictype.Classifiable; +import org.rapla.entities.dynamictype.Classification; +import org.rapla.entities.dynamictype.ClassificationFilter; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.dynamictype.DynamicTypeAnnotations; +import org.rapla.facade.Conflict; +import org.rapla.facade.QueryModule; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.TreeFactory; +import org.rapla.gui.internal.common.CalendarSelectionModel; +import org.rapla.gui.toolkit.RecursiveNode; +import org.rapla.gui.toolkit.TreeToolTipRenderer; + +public class TreeFactoryImpl extends RaplaGUIComponent implements TreeFactory { + public TreeFactoryImpl(RaplaContext sm) throws RaplaException { + super(sm); + this.defaultIcon = getIcon("icon.tree.default"); + } + + Icon defaultIcon; + + public TreeModel createClassifiableModel(Classifiable[] classifiables) throws RaplaException { + return createClassifiableModel(classifiables, null); + } + + public DefaultTreeModel createClassifiableModel(Classifiable[] classifiables, final DynamicType[] dynamicTypes) throws RaplaException { + boolean addOnlyTypesWithVisibleChildren = false; + List typeList = new ArrayList(); + if (dynamicTypes == null || dynamicTypes.length == 0) { + final QueryModule query = getQuery(); + typeList.addAll(Arrays.asList(query.getDynamicTypes(DynamicTypeAnnotations.VALUE_RESOURCE_CLASSIFICATION))); + typeList.addAll(Arrays.asList(query.getDynamicTypes(DynamicTypeAnnotations.VALUE_PERSON_CLASSIFICATION))); + typeList.addAll(Arrays.asList(query.getDynamicTypes(DynamicTypeAnnotations.VALUE_RESERVATION_CLASSIFICATION))); + addOnlyTypesWithVisibleChildren = true; + } + else + { + for (DynamicType type: dynamicTypes) { + typeList.add(type); + } + } + Map nodeMap = new HashMap(); + for (DynamicType type: typeList) { + TreeNode node = new NamedNode(type); + nodeMap.put(type, node); + } + + DefaultMutableTreeNode root = new DefaultMutableTreeNode("ROOT"); + Comparator comp = null; + if (classifiables.length > 0 && classifiables[0] instanceof Reservation) { + comp = new InverseComparator(new ReservationStartComparator(getLocale())); + } else { + comp = new NamedComparator(getLocale()); + } + Set sortedClassifiable = new TreeSet(comp); + sortedClassifiable.addAll(Arrays.asList(classifiables)); + for (Iterator it = sortedClassifiable.iterator(); it.hasNext();) { + Classifiable classifiable = (Classifiable) it.next(); + Classification classification = classifiable.getClassification(); + NamedNode childNode = new NamedNode((Named) classifiable); + DynamicType type = classification.getType(); + Assert.notNull(type); + DefaultMutableTreeNode typeNode = (DefaultMutableTreeNode) nodeMap.get(type); + Assert.notNull(typeNode); + typeNode.add(childNode); + + } + int count = 0; + for (DynamicType type: typeList) { + DefaultMutableTreeNode typeNode = (DefaultMutableTreeNode) nodeMap.get(type); + // Only add typeNodes with children + if (!typeNode.isLeaf() || !addOnlyTypesWithVisibleChildren) + root.insert(typeNode, count++); + } + return new DefaultTreeModel(root); + } + + public TreeCellRenderer createComplexRenderer() { + return new ComplexTreeCellRenderer(); + } + + private boolean isInFilter(ClassificationFilter[] filter, Classifiable classifiable) { + if (filter == null) + return true; + for (int i = 0; i < filter.length; i++) { + if (filter[i].matches(classifiable.getClassification())) { + return true; + } + } + return false; + } + + private boolean isInFilter(ClassificationFilter[] filter, DynamicType type) { + if (filter == null) + return true; + for (int i = 0; i < filter.length; i++) { + if (filter[i].getType().equals(type)) { + return true; + } + } + return false; + } + + private boolean hasRulesFor(ClassificationFilter[] filter, DynamicType type) { + if (filter == null) + return false; + for (int i = 0; i < filter.length; i++) { + if (filter[i].getType().equals(type) && filter[i].ruleSize() > 0) { + return true; + } + } + return false; + } + + private Collection getConflicts(ClassificationFilter[] allocatableFilter, User user) throws RaplaException { + + List result = new ArrayList(); + Conflict[] conflicts = getQuery().getConflicts(getQuery().today()); + for (int i = 0; i < conflicts.length; i++) { + Conflict conflict = conflicts[i]; + if (!isInFilter(allocatableFilter, conflict.getAllocatable())) { + continue; + } +// if (!isInFilter(reservationFilter, conflict.getReservation1()) && !isInFilter(reservationFilter, conflict.getReservation2())) { +// continue; +// } + if (user != null && !user.equals(conflict.getReservation1().getOwner()) && !user.equals(conflict.getReservation1().getOwner())) { + continue; + } + result.add(conflict); + } + return result; + } + + /** + * Returns the Resources root + * + * @param filter + * @param selectedUser + * @return + * @throws RaplaException + */ + public TypeNode createResourcesModel(ClassificationFilter[] filter) throws RaplaException { + TypeNode treeNode = new TypeNode(Allocatable.TYPE, CalendarSelectionModel.ALLOCATABLES_ROOT, getString("resources")); + Map nodeMap = new HashMap(); + + boolean resourcesFiltered = false; + + DynamicType[] types = getQuery().getDynamicTypes(DynamicTypeAnnotations.VALUE_RESOURCE_CLASSIFICATION); + for (int i = 0; i < types.length; i++) { + DynamicType type = types[i]; + if (hasRulesFor(filter, type)) { + resourcesFiltered = true; + } + if (!isInFilter(filter, type)) { + resourcesFiltered = true; + continue; + } + + NamedNode node = new NamedNode(type); + treeNode.add(node); + nodeMap.put(type, node); + } + + // creates typ folders + types = getQuery().getDynamicTypes(DynamicTypeAnnotations.VALUE_PERSON_CLASSIFICATION); + for (int i = 0; i < types.length; i++) { + DynamicType type = types[i]; + if (hasRulesFor(filter, type)) { + resourcesFiltered = true; + } + if (!isInFilter(filter, type)) { + resourcesFiltered = true; + continue; + } + + NamedNode node = new NamedNode(type); + treeNode.add(node); + nodeMap.put(type, node); + } + + treeNode.setFiltered(resourcesFiltered); + + // adds elements to typ folders + Allocatable[] allocatables = getQuery().getAllocatables(); + for (Allocatable classifiable: sorted(allocatables)) { + if (!isInFilter(filter, classifiable)) { + continue; + } + Classification classification = classifiable.getClassification(); + NamedNode childNode = new NamedNode((Named) classifiable); + DynamicType type = classification.getType(); + Assert.notNull(type); + DefaultMutableTreeNode typeNode = (DefaultMutableTreeNode) nodeMap.get(type); + Assert.notNull(typeNode); + typeNode.add(childNode); + } + return treeNode; + } + + private Collection sorted(T[] allocatables) { + TreeSet sortedList = new TreeSet(new NamedComparator(getLocale())); + sortedList.addAll(Arrays.asList(allocatables)); + return sortedList; + } + + public TypeNode createReservationsModel() throws RaplaException { + TypeNode treeNode = new TypeNode(Reservation.TYPE, getString("reservation_type")); + + // creates typ folders + DynamicType[] types = getQuery().getDynamicTypes(DynamicTypeAnnotations.VALUE_RESERVATION_CLASSIFICATION); + for (int i = 0; i < types.length; i++) { + DynamicType type = types[i]; + + + NamedNode node = new NamedNode(type); + treeNode.add(node); + } + treeNode.setFiltered(false); + return treeNode; + } + + public DefaultTreeModel createModel(ClassificationFilter[] filter, User selectedUser, RaplaType classificationType) throws RaplaException { + // Resources and Persons + // Add the resource types + // Add the resources + // Add the person types + // Add the persons + // Eventtypes + // Add the event types + // Conflicts (0) + // Add the conflicts + // Users + // Add the users + // Categories (the root category) + // Add the periods + + DefaultMutableTreeNode root = new DefaultMutableTreeNode("ROOT"); + + if (classificationType != null) { + if (classificationType.equals(Allocatable.TYPE)) { + TypeNode resourceRoot = createResourcesModel(filter); + root.add(resourceRoot); + + } + } + + else + { + TypeNode resourceRoot = createResourcesModel(filter); + root.add(resourceRoot); + if (isAdmin()) { + + DefaultMutableTreeNode userRoot = new TypeNode(User.TYPE, getString("users")); + User[] userList = getQuery().getUsers(); + for (final User user: sorted(userList)) { + DefaultMutableTreeNode node = new DefaultMutableTreeNode(); + node.setUserObject( user); + userRoot.add(node); + } + root.add(userRoot); + + + TypeNode reservationsRoot = createReservationsModel(); + root.add(reservationsRoot); + + CategoryNode categoryRoot = new CategoryNode(getI18n().getLocale(), null, getQuery().getSuperCategory()); + root.add(categoryRoot); + + // set category root name + getQuery().getSuperCategory().getName().setReadOnly(false); + getQuery().getSuperCategory().getName().setName(getI18n().getLang(), getString("categories")); + getQuery().getSuperCategory().getName().setReadOnly(true); + + + + + DefaultMutableTreeNode periodRoot = new TypeNode(Period.TYPE, getString("periods")); + Period[] periodList = getQuery().getPeriods(); + for (final Period period: sorted(periodList)) { + NamedNode node = new NamedNode(period); + periodRoot.add(node); + } + root.add(periodRoot); + } + } + return new DefaultTreeModel(root); + } + + public DefaultTreeModel createConflictModel(ClassificationFilter[] filter, User selectedUser) throws RaplaException { + // Resources and Persons + // Add the resource types + // Add the resources + // Add the person types + // Add the persons + // Eventtypes + // Add the event types + // Conflicts (0) + // Add the conflicts + // Users + // Add the users + // Categories (the root category) + // Add the periods + + DefaultMutableTreeNode root = new DefaultMutableTreeNode("ROOT"); + Collection conflicts = getConflicts(filter, selectedUser); + DefaultMutableTreeNode conflictRoot = new TypeNode(Conflict.TYPE, getI18n().format("conflictUC", new Integer(conflicts.size()))); + for (Iterator it = conflicts.iterator(); it.hasNext();) { + conflictRoot.add(new NamedNode((Conflict) it.next())); + } + root.add(conflictRoot); + + + return new DefaultTreeModel(root); + } + + class TypeNode extends DefaultMutableTreeNode { + private static final long serialVersionUID = 1L; + + boolean filtered; + RaplaType type; + String title; + + TypeNode(RaplaType type, Object userObject, String title) { + this.type = type; + this.title = title; + setUserObject(userObject); + } + + TypeNode(RaplaType type, Object userObject) { + this(type, userObject, null); + } + + public RaplaType getType() { + return type; + } + + public boolean isFiltered() { + return filtered; + } + + public void setFiltered(boolean filtered) { + this.filtered = filtered; + } + + public Object getTitle() { + if (title != null) { + return title; + } else { + return userObject.toString(); + } + } + + } + + public DefaultMutableTreeNode newNamedNode(Named element) { + return new NamedNode(element); + } + + public TreeModel createModel(Category category) throws RaplaException { + return new DefaultTreeModel(new CategoryNode(getI18n().getLocale(), null, category)); + } + + public TreeModel createModelFlat(Named[] element) { + DefaultMutableTreeNode root = new DefaultMutableTreeNode(""); + for (int i = 0; i < element.length; i++) { + root.add(new NamedNode(element[i])); + } + return new DefaultTreeModel(root); + } + + public TreeToolTipRenderer createTreeToolTipRenderer() { + return new RaplaTreeToolTipRenderer(); + } + + public TreeCellRenderer createRenderer() { + return new ComplexTreeCellRenderer(); + } + + static public class CategoryNode extends RecursiveNode implements MutableTreeNode { + Locale locale; + + public CategoryNode(Locale locale, TreeNode parent, Category category) { + super(parent, category); + this.locale = locale; + } + + protected Category getCategory() { + return (Category) getUserObject(); + } + + + protected Object[] getChildObjects() { + return getCategory().getCategories(); + } + + + protected RecursiveNode createChildNode(Object userObject) { + return new CategoryNode(locale, this, (Category) userObject); + } + + + public String toString() { + return getCategory().getName(locale); + } + + public void insert(MutableTreeNode child, int index) { + } + + public void remove(int index) { + } + + public void remove(MutableTreeNode node) { + } + + public void setUserObject(Object object) { + } + + public void removeFromParent() { + parent = null; + } + + public void setParent(MutableTreeNode newParent) { + parent = newParent; + + } + } + + public class NamedNode extends DefaultMutableTreeNode { + private static final long serialVersionUID = 1L; + + NamedNode(Named obj) { + super(obj); + } + + + public String toString() { + Named obj = (Named) getUserObject(); + if (obj != null) { + return obj.getName(getI18n().getLocale()); + } else { + return super.toString(); + } + } + }; + + // TODO this class is a bit of Hack + class ComplexTreeCellRenderer extends DefaultTreeCellRenderer { + private static final long serialVersionUID = 1L; + + Icon personIcon; + Icon folderClosedIcon; + Icon folderOpenIcon; + Font normalFont; + Font bigFont; + Border nonIconBorder = BorderFactory.createEmptyBorder(1, 0, 1, 0); + Border conflictBorder = BorderFactory.createEmptyBorder(2, 0, 2, 0); + + public ComplexTreeCellRenderer() { + personIcon = TreeFactoryImpl.this.getIcon("icon.tree.persons"); + // folderClosedIcon = UIManager.getIcon("Tree.closedIcon"); + // folderOpenIcon = UIManager.getIcon("Tree.openIcon"); + folderClosedIcon = getI18n().getIcon("icon.folder"); + folderOpenIcon = getI18n().getIcon("icon.folder"); + normalFont = UIManager.getFont("Tree.font"); + bigFont = normalFont.deriveFont(Font.BOLD, (float) (normalFont.getSize() * 1.2)); + setLeafIcon(defaultIcon); + + } + + public void setLeaf(Object object) { + Icon icon = null; + if (object instanceof Allocatable) { + if (((Allocatable) object).isPerson()) { + icon = personIcon; + } else { + icon = defaultIcon; + } + + } else if (object instanceof DynamicType) { + DynamicType type = (DynamicType) object; + String classificationType = type.getAnnotation(DynamicTypeAnnotations.KEY_CLASSIFICATION_TYPE); + if (DynamicTypeAnnotations.VALUE_RESERVATION_CLASSIFICATION.equals(classificationType)) { + setBorder(conflictBorder); + } else { + icon = folderClosedIcon; + } + } + if (icon == null) { + setBorder(nonIconBorder); + } + setLeafIcon(icon); + } + + + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { + setBorder(null); + setFont(normalFont); + setClosedIcon(folderClosedIcon); + setOpenIcon(folderOpenIcon); + if (value != null && value instanceof TypeNode) { + TypeNode typeNode = (TypeNode) value; + Icon bigFolderIcon; + if (typeNode.getType().equals(User.TYPE)) { + bigFolderIcon = getI18n().getIcon("icon.big_folder_users"); + } else if (typeNode.getType().equals(Period.TYPE)) { + bigFolderIcon = getI18n().getIcon("icon.big_folder_periods"); + } else if (typeNode.getType().equals(Reservation.TYPE)) { + if (typeNode.isFiltered()) { + bigFolderIcon = getI18n().getIcon("icon.big_folder_events_filtered"); + } else { + bigFolderIcon = getI18n().getIcon("icon.big_folder_events"); + } + } else if (typeNode.getType().equals(Conflict.TYPE)) { + bigFolderIcon = getI18n().getIcon("icon.big_folder_conflicts"); + } else { + if (typeNode.isFiltered()) { + bigFolderIcon = getI18n().getIcon("icon.big_folder_resources_filtered"); + } else { + bigFolderIcon = getI18n().getIcon("icon.big_folder_resources"); + } + } + setClosedIcon(bigFolderIcon); + setOpenIcon(bigFolderIcon); + setLeafIcon(bigFolderIcon); + setFont(bigFont); + value = typeNode.getTitle(); + } else if (value instanceof CategoryNode) { + Category category = ((CategoryNode) value).getCategory(); + if (category.getParent() == null) { + setClosedIcon(getI18n().getIcon("icon.big_folder_categories")); + setOpenIcon(getI18n().getIcon("icon.big_folder_categories")); + setFont(bigFont); + } else { + boolean hasChildren = category.getCategories().length > 0; + if (!hasChildren) { + setClosedIcon(null); + setOpenIcon(null); + setLeafIcon(null); + setBorder(nonIconBorder); + } + } + } else { + Object nodeInfo = getTheUserObject(value); + if (nodeInfo instanceof Conflict) { + Conflict conflict = (Conflict) nodeInfo; + String text = getInfoFactory().getToolTip(conflict); + value = text; + setBorder(conflictBorder); + setLeafIcon(null); + } else { + setClosedIcon(folderClosedIcon); + setOpenIcon(folderOpenIcon); + if (leaf) { + setLeaf(nodeInfo); + } + } + } + Component result = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); + return result; + } + + } + + private static Object getTheUserObject(Object node) { + if (node instanceof DefaultMutableTreeNode) + return ((DefaultMutableTreeNode) node).getUserObject(); + if (node instanceof RecursiveNode) + return ((RecursiveNode) node).getUserObject(); + return node; + } + + class RaplaTreeToolTipRenderer implements TreeToolTipRenderer { + public String getToolTipText(JTree tree, int row) { + Object node = tree.getPathForRow(row).getLastPathComponent(); + Object value = getTheUserObject(node); + if (value instanceof Conflict) { + return null; + } + return getInfoFactory().getToolTip(value); + } + } + +} diff --git a/Rapla/src/org/rapla/gui/internal/view/UserInfoUI.java b/Rapla/src/org/rapla/gui/internal/view/UserInfoUI.java new file mode 100644 index 0000000..5f99246 --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/view/UserInfoUI.java @@ -0,0 +1,69 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.view; + +import java.util.ArrayList; +import java.util.Collection; + +import org.rapla.entities.Category; +import org.rapla.entities.User; +import org.rapla.entities.domain.Allocatable; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +class UserInfoUI extends ClassificationInfoUI { + public UserInfoUI(RaplaContext sm) throws RaplaException { + super(sm); + } + + protected String createHTMLAndFillLinks(Object object,LinkController controller) { + User user = (User) object; + StringBuffer buf = new StringBuffer(); + if (user.isAdmin()) { + highlight(getString("admin"),buf); + } + Collection att = new ArrayList(); + att.add(new Row(getString("username"), strong( encode( user.getUsername() ) ) ) ); + + final Allocatable person = user.getPerson(); + if ( person == null) + { + att.add(new Row(getString("name"), encode(user.getName()))); + att.add(new Row(getString("email"), encode(user.getEmail()))); + } + else + { + att.addAll(getClassificationAttributes(person, false)); + } + createTable(att,buf,false); + + Category[] groups = user.getGroups(); + if ( groups.length > 0 ) { + buf.append(getString("groups") + ":"); + buf.append("
      "); + for ( int i = 0; i < groups.length; i++ ) { + buf.append("
    • "); + String groupName = groups[i].getPath( null , getI18n().getLocale()); + encode ( groupName , buf); + buf.append("
    • \n"); + } + buf.append("
    "); + } + return buf.toString(); + } + + public String getTooltip(Object object) { + return createHTMLAndFillLinks( object, null ); + } +} + diff --git a/Rapla/src/org/rapla/gui/internal/view/ViewTable.java b/Rapla/src/org/rapla/gui/internal/view/ViewTable.java new file mode 100644 index 0000000..4f5d3ff --- /dev/null +++ b/Rapla/src/org/rapla/gui/internal/view/ViewTable.java @@ -0,0 +1,148 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.internal.view; +import java.awt.Dimension; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JScrollPane; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; + +import org.rapla.components.util.Assert; +import org.rapla.entities.RaplaObject; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.toolkit.HTMLView; +import org.rapla.gui.toolkit.RaplaWidget; + +/**Information of the entity-classes displayed in an HTML-Component */ +public class ViewTable extends RaplaGUIComponent + implements + HyperlinkListener + ,RaplaWidget + ,LinkController +{ + String title; + HTMLView htmlView = new HTMLView(); + JScrollPane pane = new JScrollPane(htmlView, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER) { + private static final long serialVersionUID = 1L; + + public Dimension getPreferredSize() { + Dimension pref = super.getPreferredSize(); + Dimension max = getMaximumSize(); + //System.out.println( "PREF: " + pref + " MAX: " + max); + if ( pref.height > max.height ) + return max; + else + return pref; + } + }; + Map linkMap; + int linkId = 0; + boolean packText = true; + + public ViewTable(RaplaContext sm) throws RaplaException { + super( sm); + linkMap = new HashMap(7); + htmlView.addHyperlinkListener(this); + htmlView.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); + pane.setMaximumSize( new Dimension( 600, 500 )); + } + + /** HTML-text-component should be sized according to the displayed text. Default is true. */ + public void setPackText(boolean packText) { + this.packText = packText; + } + + public JComponent getComponent() { + return pane; + } + + public String getDialogTitle() { + return title; + } + + public void updateInfo(Object object) throws RaplaException + { + if ( object instanceof RaplaObject) + { + final InfoFactoryImpl infoFactory = (InfoFactoryImpl)getInfoFactory(); + final HTMLInfo view = infoFactory.createView((RaplaObject)object); + updateInfo(object,view); + } + else + { + updateInfoHtml( object.toString()); + } + } + + public void updateInfo(Object object, HTMLInfo info) throws RaplaException { + linkMap.clear(); + final String html = info.createHTMLAndFillLinks( object, this); + updateInfoHtml(html); + } + + public void updateInfoHtml( String html) throws RaplaException { + if (html !=null ) { + setText( html); + } else { + setText(getString("nothing_selected")); + htmlView.revalidate(); + htmlView.repaint(); + } + } + + public void setTitle(String text) { + this.title = text; + } + + public void setText(String text) { + String message = HTMLView.createHTMLPage(text); + htmlView.setText(message, packText); + } + + public void createLink(Object object,String link,StringBuffer buf) { + linkMap.put(new Integer(linkId),object); + buf.append(""); + HTMLInfo.encode(link,buf); + buf.append(""); + } + + public String createLink(Object object,String link) { + StringBuffer buf = new StringBuffer(); + createLink(object,link,buf); + return buf.toString(); + } + + public void hyperlinkUpdate(HyperlinkEvent e) { + if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + String link = e.getDescription(); + getLogger().debug("Hyperlink pressed: " + link); + Object object = linkMap.get(new Integer(link)); + Assert.notNull(object,"link was not found in linkMap"); + Assert.notNull(getInfoFactory()); + try { + getInfoFactory().showInfoDialog(object,htmlView); + } catch (RaplaException ex) { + showException(ex,getComponent()); + } // end of try-catch + } + } +} + diff --git a/Rapla/src/org/rapla/gui/package.html b/Rapla/src/org/rapla/gui/package.html new file mode 100644 index 0000000..f3f474b --- /dev/null +++ b/Rapla/src/org/rapla/gui/package.html @@ -0,0 +1,9 @@ + +This is the base package of the GUI-client. Communication through the backend +is done through the modules of org.rapla.facade package. +The gui-client is normally started through the RaplaClientService. +You can also plug-in your own components into the gui. + + + + diff --git a/Rapla/src/org/rapla/gui/toolkit/DateCellRenderer.java b/Rapla/src/org/rapla/gui/toolkit/DateCellRenderer.java new file mode 100644 index 0000000..a50424d --- /dev/null +++ b/Rapla/src/org/rapla/gui/toolkit/DateCellRenderer.java @@ -0,0 +1,38 @@ +/** + * + */ +package org.rapla.gui.toolkit; + +import java.awt.Component; +import java.util.Date; + +import javax.swing.JTable; +import javax.swing.table.DefaultTableCellRenderer; + +import org.rapla.framework.RaplaLocale; + +final public class DateCellRenderer extends DefaultTableCellRenderer { + private static final long serialVersionUID = 1L; + + RaplaLocale raplaLocale; + + public DateCellRenderer(RaplaLocale raplaLocale) { + this.raplaLocale = raplaLocale; + } + public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column ) + { + final Date date = (Date) value; + boolean appendTime = !raplaLocale.toDate(date, false ).equals( date); + if ( appendTime ) + { + value = raplaLocale.formatDateLong( date) + " " + raplaLocale.formatTime( date) ; + } + else + { + value = raplaLocale.formatDateLong( date); + } + //setComponentOrientation( ComponentOrientation.RIGHT_TO_LEFT ); + return super.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, column); + } + +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/gui/toolkit/DialogUI.java b/Rapla/src/org/rapla/gui/toolkit/DialogUI.java new file mode 100644 index 0000000..f0f1082 --- /dev/null +++ b/Rapla/src/org/rapla/gui/toolkit/DialogUI.java @@ -0,0 +1,409 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.toolkit; +import java.awt.AWTEvent; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.WindowEvent; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.KeyStroke; + +import org.rapla.components.calendar.jdk14adapter.AWTAdapterFactory; +import org.rapla.components.calendar.jdk14adapter.FocusTester; +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.components.xmlbundle.LocaleChangeEvent; +import org.rapla.components.xmlbundle.LocaleChangeListener; +import org.rapla.components.xmlbundle.LocaleSelector; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + + +public class DialogUI extends JDialog + implements + FrameController + ,LocaleChangeListener +{ + private static final long serialVersionUID = 1L; + + protected RaplaButton[] buttons; + protected JComponent content; + private JPanel jPanelButtonFrame = new JPanel(); + private JLabel label = null; + private boolean useDefaultOptions = false; + private boolean bClosed = false; + private Component parent; + + private int selectedIndex = -1; + private FrameControllerList frameList = null; + protected boolean packFrame = true; + private LocaleSelector localeSelector; + private I18nBundle i18n; + + private RaplaContext context = null; + private ButtonListener buttonListener = new ButtonListener(); + private boolean m_modal; + + private Action abortAction = new AbstractAction() { + private static final long serialVersionUID = 1L; + + public void actionPerformed(ActionEvent evt) { + close(); + } + }; + + + public static Component getOwnerWindow(Component component) { + if (component == null) + return getInvisibleSharedFrame(); + if (component instanceof Dialog) + return component; + if (component instanceof Frame) + return component; + Container owner = component.getParent(); + return getOwnerWindow(owner); + } + + private static String[] getDefaultOptions() { + return new String[] {"ok"}; + } + + public DialogUI(RaplaContext sm, Dialog parent) throws RaplaException { + super( parent ); + service( sm ); + } + + public DialogUI(RaplaContext sm, Frame parent) throws RaplaException { + super( parent ); + service( sm ); + } + + /** @see #getInvisibleSharedFrame */ + private static JFrame invisibleSharedFrame; + + /** @see #getInvisibleSharedFrame */ + private static int referenceCounter = 0; + + /** If a dialogs owner is null this frame will be used as owner. + A call to this method will increase the referenceCounter. + A new shared frame is created when the referenceCounter is 1. + The frame gets disposed if the refernceCounter is 0. + The referenceCounter is decreased in the dispose method. + */ + private static Frame getInvisibleSharedFrame() { + referenceCounter ++; + if (referenceCounter == 1) + { + invisibleSharedFrame = new JFrame(); + invisibleSharedFrame.setSize(400,400); + FrameControllerList.centerWindowOnScreen(invisibleSharedFrame); + } + return invisibleSharedFrame; + } + + public static DialogUI create(RaplaContext sm,Component owner,boolean modal,JComponent content,String[] options) throws RaplaException { + DialogUI dlg; + Component topLevel = getOwnerWindow(owner); + if ( topLevel instanceof Dialog) + dlg = new DialogUI(sm,(Dialog)topLevel); + else + dlg = new DialogUI(sm,(Frame)topLevel); + + dlg.parent = owner; + dlg.init(modal,content,options); + return dlg; + } + + + public static DialogUI create(RaplaContext sm,Component owner,boolean modal,String title,String text,String[] options) throws RaplaException { + DialogUI dlg= create(sm,owner,modal,new JPanel(),options); + dlg.createMessagePanel(text); + dlg.setTitle(title); + return dlg; + } + + + + public static DialogUI create(RaplaContext sm,Component owner,boolean modal,String title,String text) throws RaplaException { + DialogUI dlg = create(sm,owner,modal,title,text,getDefaultOptions()); + dlg.useDefaultOptions = true; + return dlg; + } + + public RaplaButton getButton(int index) { + return buttons[index]; + } + + protected void init(boolean modal,JComponent content,String[] options) { + super.setModal(modal); + m_modal = modal; + AWTAdapterFactory fact = + AWTAdapterFactory.getFactory(); + if (fact != null) { + fact.createFocusAdapter( this ).ignoreFocusComponents(new FocusTester() { + public boolean accept(Component component) { + return !(component instanceof HTMLView) ; + } + }); + } + + this.content = content; + + this.enableEvents(AWTEvent.WINDOW_EVENT_MASK); + + JPanel contentPane = (JPanel) this.getContentPane(); + contentPane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); + contentPane.setLayout(new BorderLayout()); + contentPane.add(content, BorderLayout.CENTER); + contentPane.add(jPanelButtonFrame,BorderLayout.SOUTH); + jPanelButtonFrame.setLayout(new FlowLayout(FlowLayout.CENTER)); + setButtons(options); + contentPane.setVisible(true); + + /* + We enable the escape-key for executing the abortCmd. Many thanks to John Zukowski. + Java-Tip 72 + */ + KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); + contentPane.getActionMap().put("abort",buttonListener); + contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(stroke,"abort"); + } + + protected void setButtons(String[] options) { + buttons = new RaplaButton[options.length]; + + for (int i=0;i0) + setDefault(0); + jPanelButtonFrame.invalidate(); + } + + protected JComponent createButtonPanel() { + GridLayout gridLayout = new GridLayout(); + JPanel jPanelButtons = new JPanel(); + jPanelButtons.setLayout(gridLayout); + gridLayout.setRows(1); + gridLayout.setHgap(10); + gridLayout.setVgap(5); + gridLayout.setColumns(buttons.length); + for (int i=0;i 1) { + getButton(0).setText(i18n.getString("ok")); + getButton(1).setIcon(i18n.getIcon("icon.abort")); + getButton(1).setText(i18n.getString("abort")); + } else { + getButton(0).setText(i18n.getString("ok")); + } + } + localeSelector = (LocaleSelector) sm.lookup( LocaleSelector.ROLE); + localeSelector.addLocaleChangeListener(this); + frameList = (FrameControllerList) sm.lookup(FrameControllerList.ROLE); + frameList.add(this); + } + + protected I18nBundle getI18n() { + return i18n; + } + + protected RaplaContext getContext() { + return context; + } + + /** the default implementation does nothing. Override this method + if you want to react on a locale change.*/ + public void localeChanged(LocaleChangeEvent evt) { + + } + + public void setIcon(Icon icon) { + try { + if (label != null) + label.setIcon(icon); + } catch (Exception ex) { + } + } + + FrameControllerList getFrameList() { + return frameList; + } + + /** close and set the selectedIndex to the index Value. Usefull for modal dialogs*/ + public void close(int index) { + selectedIndex = index; + close(); + } + + // The implementation of the FrameController Interface + public void close() { + if (bClosed) + return; + dispose(); + } + + public void dispose() { + bClosed = true; + try { + if (getOwner() == invisibleSharedFrame) + referenceCounter --; + super.dispose(); + if (referenceCounter == 0 && invisibleSharedFrame!= null) + invisibleSharedFrame.dispose(); + if (frameList != null) + frameList.remove(this); + if ( localeSelector != null ) + localeSelector.removeLocaleChangeListener(this); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + // The implementation of the DialogController Interface + public void setDefault(int index) { + this.getRootPane().setDefaultButton(getButton(index)); + } + + public void setTitle(String title) { + super.setTitle(title); + } + + public boolean isClosed() { + return bClosed; + } + + public void start(Point p) { + //Validate frames that have preset sizes + //Pack frames that have useful preferred size info, e.g. from their layout + if (packFrame) { + this.pack(); + } else { + this.validate(); + } + if (parent != null) { + FrameControllerList.placeRelativeToComponent(this,parent,p); + } else { + getFrameList().placeRelativeToMain(this); + } + // okButton.requestFocus(); + bClosed = false; + super.setVisible( true ); + if (m_modal) { + dispose(); + } + } + + public void start() { + start(null); + } + + public void startNoPack() { + packFrame = false; + start(null); + } + + protected void processWindowEvent(WindowEvent e) { + if (e.getID() == WindowEvent.WINDOW_CLOSING) { + abortAction.actionPerformed(new ActionEvent(this,ActionEvent.ACTION_PERFORMED,"")); + } else if (e.getID() == WindowEvent.WINDOW_CLOSED) { + close(); + } + } + + private void createMessagePanel(String text) { + JPanel panel = (JPanel) content; + panel.setLayout(new BoxLayout(panel,BoxLayout.X_AXIS)); + label = new JLabel(); + + HTMLView textView = new HTMLView(); + JEditorPaneWorkaround.packText(textView, HTMLView.createHTMLPage(text) ,450); + JPanel jContainer = new JPanel(); + jContainer.setLayout(new BorderLayout()); + panel.add(jContainer); + jContainer.add(label,BorderLayout.NORTH); + panel.add(textView); + } + +} + + + + + + + + + + + + + + + + + diff --git a/Rapla/src/org/rapla/gui/toolkit/DisposingTool.java b/Rapla/src/org/rapla/gui/toolkit/DisposingTool.java new file mode 100644 index 0000000..c4d7a73 --- /dev/null +++ b/Rapla/src/org/rapla/gui/toolkit/DisposingTool.java @@ -0,0 +1,17 @@ +package org.rapla.gui.toolkit; + +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import org.apache.avalon.framework.container.ContainerUtil; + +/** Disposes an object on window close. Must be added as a WindowListener + to the target window*/ +final public class DisposingTool extends WindowAdapter { + Object m_objectToDispose; + public DisposingTool(Object objectToDispose) { + m_objectToDispose = objectToDispose; + } + public void windowClosed(WindowEvent e) { + ContainerUtil.dispose(m_objectToDispose); + } +} diff --git a/Rapla/src/org/rapla/gui/toolkit/EmptyLineBorder.java b/Rapla/src/org/rapla/gui/toolkit/EmptyLineBorder.java new file mode 100644 index 0000000..5e0bf7e --- /dev/null +++ b/Rapla/src/org/rapla/gui/toolkit/EmptyLineBorder.java @@ -0,0 +1,32 @@ +/** + * + */ +package org.rapla.gui.toolkit; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Insets; + +import javax.swing.border.Border; + +public class EmptyLineBorder implements Border { + Insets insets = new Insets(0,0,0,0); + Color COLOR = Color.LIGHT_GRAY; + public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) + { + g.setColor( COLOR ); + g.drawLine(30,8, c.getWidth(), 8); + + } + + public Insets getBorderInsets( Component c ) + { + return insets; + } + + public boolean isBorderOpaque() + { + return true; + } +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/gui/toolkit/ErrorDialog.java b/Rapla/src/org/rapla/gui/toolkit/ErrorDialog.java new file mode 100644 index 0000000..5fc5bcd --- /dev/null +++ b/Rapla/src/org/rapla/gui/toolkit/ErrorDialog.java @@ -0,0 +1,224 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.toolkit; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.lang.reflect.Method; + +import javax.swing.DefaultListModel; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; + +import org.apache.avalon.framework.CascadingException; +import org.apache.avalon.framework.logger.Logger; +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +final public class ErrorDialog { + RaplaContext sm; + Logger logger; + I18nBundle i18n; + /** + * @param sm + * @throws RaplaException + */ + public ErrorDialog(RaplaContext sm) throws RaplaException { + this.sm = sm; + this.i18n = (I18nBundle) sm.lookup(I18nBundle.ROLE + "/org.rapla.RaplaResources"); + this.logger = (Logger) sm.lookup(Logger.class.getName()); + } + + private RaplaContext getContext() { + return sm; + } + + protected I18nBundle getI18n() { + return i18n; + } + + protected Logger getLogger() { + return logger; + } + + public static final int WARNING_MESSAGE = 1; + public static final int ERROR_MESSAGE = 2; + public static final int EXCEPTION_MESSAGE = 3; + + /** This is for the test-cases only. If this flag is set + the ErrorDialog throws an ErrorDialogException instead of + displaying the dialog. This is useful for testing. */ + public static boolean THROW_ERROR_DIALOG_EXCEPTION = false; + + private void test(String message,int type) { + if (THROW_ERROR_DIALOG_EXCEPTION) { + throw new ErrorDialogException(new RaplaException(message),type); + } + } + + private void test(Throwable ex,int type) { + if (THROW_ERROR_DIALOG_EXCEPTION) { + throw new ErrorDialogException(ex,type); + } + } + + private String createTitle(String key) { + return getI18n().format("exclamation.format",getI18n().getString(key)); + } + + public void show(String message) { + test(message,ERROR_MESSAGE); + try { + showDialog(createTitle("error"),message,null); + } catch (Exception ex) { + System.err.println(message); + } + } + + public void showWarningDialog(String message,Component owner) { + test(message,WARNING_MESSAGE); + try { + showWarningDialog(createTitle("warning"),message,owner); + } catch (Exception ex) { + System.err.println(message); + } + } + + static private String getCause(Throwable e) { + String message = e.getMessage(); + if (message != null && message.length() > 0) { + return message; + } + if (e instanceof CascadingException) { + Throwable cause = ((CascadingException)e).getCause(); + if (cause != null) + message = getCause( cause ); + } + return message; + } + + static public String getMessage(Throwable e) { + String message = getCause(e); + if (message == null || message.length() == 0) + message = e.toString(); + return message; + } + + public void showExceptionDialog(Throwable e,Component owner) { + test(e,EXCEPTION_MESSAGE); + try { + String message = getMessage(e); + if ( getLogger() != null ) + getLogger().error(message, e); + JPanel component = new JPanel(); + component.setLayout( new BorderLayout()); + component.add( new JLabel( message ),BorderLayout.NORTH); + boolean showStacktrace = true; + Throwable nestedException = e; + + do + { + if ( nestedException instanceof RaplaException) + { + showStacktrace = false; + nestedException = ((RaplaException) nestedException).getCause(); + } + else + { + showStacktrace = true; + } + } + while ( nestedException != null && !showStacktrace); + + if ( showStacktrace) + { + try { + Method getStackTrace =Exception.class.getMethod("getStackTrace",new Class[] {}); + final Object[] stackTrace = (Object[])getStackTrace.invoke( e, new Object[] {} ); + final JList lister = new JList( ); + final JScrollPane list = new JScrollPane(lister, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + list.setBorder( null); + JPanel stackTracePanel = new JPanel(); + final JCheckBox stackTraceChooser = new JCheckBox("show stacktrace"); + stackTracePanel.setLayout( new BorderLayout()); + stackTracePanel.add( stackTraceChooser, BorderLayout.NORTH); + stackTracePanel.add( list, BorderLayout.CENTER); + stackTracePanel.setPreferredSize( new Dimension(300,200)); + stackTracePanel.setMinimumSize( new Dimension(300,200)); + component.add( stackTracePanel,BorderLayout.CENTER); + lister.setVisible( false ); + stackTraceChooser.addActionListener( new ActionListener() { + + public void actionPerformed(ActionEvent e) { + DefaultListModel model =new DefaultListModel(); + if (stackTraceChooser.isSelected() ) { + for ( int i=0;i< stackTrace.length;i++) { + model.addElement( stackTrace[i]); + } + } + lister.setModel( model ); + lister.setVisible( stackTraceChooser.isSelected()); + } + + }); + } catch (Exception ex) { + } + } + + DialogUI dlg = DialogUI.create(getContext(),owner,true,component, new String[] {getI18n().getString("ok")}); + dlg.setTitle(createTitle("error")); + dlg.setIcon(getI18n().getIcon("icon.error")); + dlg.start(); + } catch (Exception ex) { + ex.printStackTrace(); + e.printStackTrace(); + } + } + + private void showDialog(String title, String message,Component owner) { + try { + DialogUI dlg = DialogUI.create(getContext(),owner,true,title,message); + dlg.setIcon(getI18n().getIcon("icon.error")); + dlg.start(); + } catch (Exception ex2) { + getLogger().error(ex2.getMessage()); + } + } + + public void showWarningDialog(String title, String message,Component owner) { + try { + DialogUI dlg = DialogUI.create(getContext(),owner,true,title,message); + dlg.setIcon(getI18n().getIcon("icon.warning")); + dlg.start(); + } catch (Exception ex2) { + getLogger().error(ex2.getMessage()); + } + } +} + + + + + + + + + + diff --git a/Rapla/src/org/rapla/gui/toolkit/ErrorDialogException.java b/Rapla/src/org/rapla/gui/toolkit/ErrorDialogException.java new file mode 100644 index 0000000..970b857 --- /dev/null +++ b/Rapla/src/org/rapla/gui/toolkit/ErrorDialogException.java @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.toolkit; + +import org.apache.avalon.framework.CascadingRuntimeException; + +/** This exception is thrown by the ErrorDialog and + is used to test error-messages. + @see ErrorDialog */ +final public class ErrorDialogException extends CascadingRuntimeException { + private static final long serialVersionUID = 1L; + + int type; + + /** @param type The type of the Error-Message. + @see ErrorDialog */ + public ErrorDialogException(Throwable throwable,int type) { + super(String.valueOf(type),throwable); + this.type = type; + } + + /** returns the type of the Error-Message. + @see ErrorDialog */ + public int getType() { + return type; + } + +} + + + + + + + + + diff --git a/Rapla/src/org/rapla/gui/toolkit/FrameController.java b/Rapla/src/org/rapla/gui/toolkit/FrameController.java new file mode 100644 index 0000000..b8f3bb4 --- /dev/null +++ b/Rapla/src/org/rapla/gui/toolkit/FrameController.java @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.toolkit; +/** All classes implementing this Interface must call + FrameControllerList.addFrameController(this) on initialization + FrameControllerList.removeFrameController(this) on close + This Class is used for automated close of all Frames on Logout. +*/ +public interface FrameController { + void close(); // must call FrameControllerList.remove(this); + } + + + + + diff --git a/Rapla/src/org/rapla/gui/toolkit/FrameControllerList.java b/Rapla/src/org/rapla/gui/toolkit/FrameControllerList.java new file mode 100644 index 0000000..ee7c5c1 --- /dev/null +++ b/Rapla/src/org/rapla/gui/toolkit/FrameControllerList.java @@ -0,0 +1,230 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.toolkit; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Toolkit; +import java.awt.Window; +import java.util.ArrayList; +import java.util.Stack; + +import org.apache.avalon.framework.logger.LogEnabled; +import org.apache.avalon.framework.logger.Logger; +import org.rapla.components.util.Assert; +import org.rapla.components.util.Tools; + +/**All rapla-windows are registered on the FrameControllerList. + The FrameControllerList is responsible for positioning the windows + and closing all open windows on exit. +*/ +final public class FrameControllerList implements LogEnabled { + public final static String ROLE = FrameControllerList.class.getName(); + private Stack openFrameController = new Stack(); + private Window mainWindow = null; + Point center; + Logger logger = null; + ArrayList listenerList = new ArrayList(); + + public FrameControllerList() { + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + center = new Point(screenSize.width / 2 + ,screenSize.height / 2); + } + + public void enableLogging(Logger logger) { + this.logger = logger; + } + + protected Logger getLogger() { + return logger; + } + + /** the center will be used by the + centerWindow() function. */ + public void setCenter(Container window) { + center.x = window.getLocationOnScreen().x + window.getSize().width/2; + center.y = window.getLocationOnScreen().y + window.getSize().height/2; + } + + /** the center will be used by the + centerWindow(Window) function. + @see #centerWindow(Window) + */ + public void setCenter(Point center) { + this.center = center; + } + + /** the main-window will be used by the + placeRelativeToMain(Window) function. + @see #placeRelativeToMain(Window) + */ + public void setMainWindow(Window window) { + this.mainWindow = window; + } + + public Window getMainWindow() { + return mainWindow; + } + + /** places the window relative to the main-window if set. + Otherwise the the centerWindow(Window) method is called. + @param newWindow the window to place + */ + public void placeRelativeToMain(Window newWindow) { + if (getLogger() != null && getLogger().isDebugEnabled() && mainWindow != null) + getLogger().debug("placeRelativeToMainWindow(" + Tools.left(mainWindow.toString(),60) + ")"); + if (mainWindow ==null) + centerWindow(newWindow); + else + placeRelativeToWindow(newWindow,mainWindow); + } + + /** adds a window to the FrameControllerList */ + public void add(FrameController c) { + Assert.notNull(c); + Assert.isTrue(!openFrameController.contains(c),"Duplicated Entries are not allowed"); + openFrameController.add(c); + } + + /** removes a window from the FrameControllerList */ + public void remove(FrameController c) { + openFrameController.remove(c); + String s = c.toString(); + if (getLogger() != null && getLogger().isDebugEnabled()) + getLogger().debug("Frame closed " + Tools.left(s,60) + "..."); + fireFrameClosed(c); + if (openFrameController.size() == 0) + fireListEmpty(); + } + + /** closes all windows registered on the FrameControllerList */ + public void closeAll() { + while (!openFrameController.empty()) { + FrameController c = (FrameController) openFrameController.peek(); + int size = openFrameController.size(); + c.close(); + if ( size <= openFrameController.size()) + getLogger().error("removeFrameController() not called in close() in " + c); + } + } + + public void addFrameControllerListener(FrameControllerListener listener) { + listenerList.add(listener); + } + + public void removeFrameControllerListener(FrameControllerListener listener) { + listenerList.remove(listener); + } + public FrameControllerListener[] getFrameControllerListeners() { + synchronized(listenerList) { + return (FrameControllerListener[])listenerList.toArray(new FrameControllerListener[]{}); + } + } + + protected void fireFrameClosed(FrameController controller) { + if (listenerList.size() == 0) + return; + FrameControllerListener[] listeners = getFrameControllerListeners(); + for (int i = 0;i screenSize.width) + x = screenSize.width - windowSize.width; + + if (y + windowSize.height > screenSize.height) + y = screenSize.height - windowSize.height; + + if (x<0) x = 0; + if (y<0) y = 0; + window.setLocation(x,y); + } + + /** places the window relative to the owner-window. + The newWindow will be placed in the middle of the owner-window. + @param newWindow the window to place + @param owner the window to place into + */ + public static void placeRelativeToWindow(Window newWindow,Window owner) { + placeRelativeToComponent(newWindow,owner,null); + } + + public static void placeRelativeToComponent(Window newWindow,Component component,Point point) { + if (component == null) + return; + Dimension dlgSize = newWindow.getSize(); + Dimension parentSize = component.getSize(); + Point loc = component.getLocationOnScreen(); + + if (point != null) { + int x = loc.x + point.x - (dlgSize.width) / 2; + int y = loc.y + point.y - ((dlgSize.height) * 2) / 3; + //System.out.println (loc + ", " + point + " x: " + x + " y: " + y); + fitIntoScreen(x,y,newWindow); + } else { + int x = (parentSize.width - dlgSize.width) / 2 + loc.x; + int y = loc.y + 10; + fitIntoScreen(x,y,newWindow); + } + } + +} + + + + + + + + + + + + + + diff --git a/Rapla/src/org/rapla/gui/toolkit/FrameControllerListener.java b/Rapla/src/org/rapla/gui/toolkit/FrameControllerListener.java new file mode 100644 index 0000000..8110f4f --- /dev/null +++ b/Rapla/src/org/rapla/gui/toolkit/FrameControllerListener.java @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.toolkit; + +public interface FrameControllerListener { + void frameClosed(FrameController frameController); + void listEmpty(); +} + + + + + + + + + + + + + + diff --git a/Rapla/src/org/rapla/gui/toolkit/HTMLView.java b/Rapla/src/org/rapla/gui/toolkit/HTMLView.java new file mode 100644 index 0000000..7f03a74 --- /dev/null +++ b/Rapla/src/org/rapla/gui/toolkit/HTMLView.java @@ -0,0 +1,98 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.toolkit; + +import java.net.URL; + +import javax.swing.JTextPane; + +final public class HTMLView extends JTextPane { + private static final long serialVersionUID = 1L; + + public HTMLView() { + setOpaque(false); + setEditable(false); + setContentType("text/html"); + setDefaultDocBase(); + } + + public static String DEFAULT_STYLE = + "body {font-family:SansSerif;font-size:12;}\n" + + ".infotable{padding:0px;margin:0px;}\n" + + ".label {vertical-align:top;}\n" + + ".value {vertical-align:top;}\n" + ; + private static URL base; + private static Exception error = null; + + /** will only work for resources inside the same jar as org/rapla/gui/images/repeating.png */ + private void setDefaultDocBase() { + if (base == null && error == null) { + try { + String marker = "org/rapla/gui/images/repeating.png"; + URL url= HTMLView.class.getClassLoader().getResource(marker); + if (url == null) { + System.err.println("Marker not found " + marker); + return; + } + //System.out.println("resource:" + url); + String urlPath = url.toString(); + base = new URL(urlPath.substring(0,urlPath.lastIndexOf(marker))); + //System.out.println("document-base:" + base); + } catch (Exception ex) { + error = ex; + System.err.println("Can't get document-base: " + ex + " in class: " + HTMLView.class.getName()); + } + } + + if (error == null) + ((javax.swing.text.html.HTMLDocument)getDocument()).setBase(base); + } + + /** calls setText(createHTMLPage(body)) */ + public void setBody(String body) { + try { + setText(createHTMLPage(body)); + } catch (Exception ex) { + setText(body); + } + } + static public String createHTMLPage(String body,String styles) { + StringBuffer buf = new StringBuffer(); + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(body); + buf.append(""); + buf.append(""); + return buf.toString(); + } + + static public String createHTMLPage(String body) { + return createHTMLPage(body,DEFAULT_STYLE); + } + + public void setText( String message, boolean packText ) + { + if (packText) { + JEditorPaneWorkaround.packText(this, message ,600); + } else { + setText( message); + } + } + +} diff --git a/Rapla/src/org/rapla/gui/toolkit/IdentifiableMenuEntry.java b/Rapla/src/org/rapla/gui/toolkit/IdentifiableMenuEntry.java new file mode 100644 index 0000000..753ca45 --- /dev/null +++ b/Rapla/src/org/rapla/gui/toolkit/IdentifiableMenuEntry.java @@ -0,0 +1,7 @@ +package org.rapla.gui.toolkit; + +/** Adds an id to the standard Swing Menu Component as JSeperator, JMenuItem and JMenu*/ +public interface IdentifiableMenuEntry +{ + String getId(); +} diff --git a/Rapla/src/org/rapla/gui/toolkit/JEditorPaneWorkaround.java b/Rapla/src/org/rapla/gui/toolkit/JEditorPaneWorkaround.java new file mode 100644 index 0000000..0a14b54 --- /dev/null +++ b/Rapla/src/org/rapla/gui/toolkit/JEditorPaneWorkaround.java @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.toolkit; +import java.awt.Dimension; +import java.awt.FontMetrics; + +import javax.swing.JEditorPane; + +/** #BUGFIX + * This is a workaround for a bug in the Sun JDK + * that don't calculate the correct size of an JEditorPane. + * The first version of this workaround caused a NullPointerException + * on JDK 1.2.2 sometimes, so this is a workaround for a workaround: + * A zero-sized component is added to the StartFrame- Window + * This component will be used to calculate the size of + * the JEditorPane Components. + */ +final class JEditorPaneWorkaround { + static public void packText(JEditorPane jText,String text,int width) { + int height; + if (width <=0 ) + return; + try { + jText.setSize(new Dimension(width,100)); + jText.setText(text); + height = jText.getPreferredScrollableViewportSize().height; + } catch ( NullPointerException e) { + jText.setSize(new Dimension(width,100)); + jText.setText(text); + FontMetrics fm = jText.getFontMetrics(jText.getFont()); + height = fm.stringWidth(text)/width * fm.getHeight() + 50; + } // end of try-catch + jText.setSize(new Dimension(width,height)); + jText.setPreferredSize(new Dimension(width,height)); + } +} + + + + + + + + + + + diff --git a/Rapla/src/org/rapla/gui/toolkit/MenuInterface.java b/Rapla/src/org/rapla/gui/toolkit/MenuInterface.java new file mode 100644 index 0000000..a798471 --- /dev/null +++ b/Rapla/src/org/rapla/gui/toolkit/MenuInterface.java @@ -0,0 +1,21 @@ +package org.rapla.gui.toolkit; + +import java.awt.Component; + +import javax.swing.JComponent; +import javax.swing.JMenuItem; + +/** JPopupMenu and JMenu don't have a common interface, so this is a common interface + * for RaplaMenu and RaplaPopupMenu +*/ +public interface MenuInterface { + JMenuItem add(JMenuItem item); + void remove(JMenuItem item); + void addSeparator(); + void removeAll(); + + void removeAllBetween(String startId, String endId); + void insertAfterId(Component component,String id); + void insertBeforeId(JComponent component,String id); + +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/gui/toolkit/MonthChooser.java b/Rapla/src/org/rapla/gui/toolkit/MonthChooser.java new file mode 100644 index 0000000..6b2d0b5 --- /dev/null +++ b/Rapla/src/org/rapla/gui/toolkit/MonthChooser.java @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas, Bettina Lademann | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.toolkit; + +import java.util.Locale; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JComboBox; + +import org.rapla.components.calendarview.MonthMapper; + +/** ComboBox that displays the weekdays in long format + */ +public final class MonthChooser extends JComboBox +{ + private static final long serialVersionUID = 1L; + + MonthMapper mapper; + + public MonthChooser() + { + this( Locale.getDefault() ); + } + + public MonthChooser( Locale locale ) + { + setLocale( locale ); + } + + public void setLocale( Locale locale ) + { + super.setLocale( locale ); + if ( locale == null ) + return; + mapper = new MonthMapper( locale ); + setModel( new DefaultComboBoxModel( mapper.getNames() ) ); + } + + public void selectMonth( int month ) + { + setSelectedIndex( month ); + } + + /** returns the selected day or -1 if no day is selected. + @see java.util.Calendar + */ + public int getSelectedMonth() + { + if ( getSelectedIndex() == -1 ) + return -1; + else + return getSelectedIndex(); + } + +} diff --git a/Rapla/src/org/rapla/gui/toolkit/PopupEvent.java b/Rapla/src/org/rapla/gui/toolkit/PopupEvent.java new file mode 100644 index 0000000..fe86e7f --- /dev/null +++ b/Rapla/src/org/rapla/gui/toolkit/PopupEvent.java @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.toolkit; + +import java.util.EventObject; +import java.awt.Point; + +public class PopupEvent extends EventObject { + private static final long serialVersionUID = 1L; + + Point m_point; + Object m_selectedObject; + + public PopupEvent(Object source, Object selectedObject, Point p) { + super(source); + m_selectedObject = selectedObject; + m_point = p; + } + public Object getSelectedObject() { + return m_selectedObject; + } + + public Point getPoint() { + return m_point; + } +} diff --git a/Rapla/src/org/rapla/gui/toolkit/PopupListener.java b/Rapla/src/org/rapla/gui/toolkit/PopupListener.java new file mode 100644 index 0000000..ce2d70c --- /dev/null +++ b/Rapla/src/org/rapla/gui/toolkit/PopupListener.java @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.toolkit; + +public interface PopupListener { + public void showPopup(PopupEvent evt); +} diff --git a/Rapla/src/org/rapla/gui/toolkit/RaplaButton.java b/Rapla/src/org/rapla/gui/toolkit/RaplaButton.java new file mode 100644 index 0000000..e70b720 --- /dev/null +++ b/Rapla/src/org/rapla/gui/toolkit/RaplaButton.java @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.toolkit; + +import java.awt.Insets; +import javax.swing.JButton; +import javax.swing.Action; + +public class RaplaButton extends JButton { + private static final long serialVersionUID = 1L; + + public static int SMALL= -1; + public static int LARGE = 1; + public static int DEFAULT = 0; + + private static Insets smallInsets = new Insets(0,0,0,0); + private static Insets largeInsets = new Insets(5,10,5,10); + + public RaplaButton(String text,int style) { + this(style); + setText(text); + } + + public RaplaButton(int style) { + if (style == SMALL) { + setMargin(smallInsets); + } else if (style == LARGE) { + setMargin(largeInsets); + } else { + setMargin(null); + } + } + + public void setAction(Action action) { + String oldText = null; + if (action.getValue(Action.NAME) == null) + oldText = getText(); + super.setAction(action); + if (oldText != null) + setText(oldText); + } + public RaplaButton() { + } + + +} + + + diff --git a/Rapla/src/org/rapla/gui/toolkit/RaplaColorList.java b/Rapla/src/org/rapla/gui/toolkit/RaplaColorList.java new file mode 100644 index 0000000..ee1b39d --- /dev/null +++ b/Rapla/src/org/rapla/gui/toolkit/RaplaColorList.java @@ -0,0 +1,188 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.toolkit; + +import java.awt.Color; +import java.util.Random; +import java.util.ArrayList; +import java.util.Arrays; + +/** WARNING: This class is about to change its API. Dont use it */ +final public class RaplaColorList { + public final static Color[] COLORS= + { + + /* + * using hex codes of colorsis easier than using + * the Color constructor with separated r, g and b values + * + * thus we decided to use the getColorForHex method + * which takes a hex String and returns a new Color object + * + * in the end this is an array of seven different colors + * + */ + + getColorForHex("#a3ddff"), // light blue + getColorForHex("#b5e97e"), // light green + getColorForHex("#ffb85e"), // orange + getColorForHex("#b099dc"), // violet + getColorForHex("#cccccc"), // light grey + getColorForHex("#fef49d"), // yellow + getColorForHex("#fc9992"), // red + + }; + + public final static Color[] APPOINTMENT_COLORS= + { + new Color(0xee, 0xee, 0xcc), + new Color(0xcc, 0x99, 0xcc), + new Color(0xad, 0xac, 0xa2), + new Color(0xcc, 0xaa, 0x66), + new Color(0xcc, 0xff, 0x88), + }; + public final static String DEFAULT_COLOR_AS_STRING = getHexForColor( COLORS[0]); + + + private static ArrayList colors = new ArrayList(Arrays.asList(COLORS)); + private static ArrayList appointmentColors = new ArrayList(Arrays.asList(APPOINTMENT_COLORS)); + private static Random randomA = null; + private static Random randomB = null; + + static private float rndA() + { + if (randomA == null) + randomA = new Random(7913); + return (float) (0.45 + randomA.nextFloat()/2.0); + } + + + static private float rndB() + { + if (randomB == null) + randomB = new Random(5513); + return (float) (0.4 + randomB.nextFloat()/2.0); + } + + final static public Color getResourceColor(int nr) + { + if (colors.size()<=nr) { + int fillSize = nr - colors.size() + 1; + for (int i=0;iprintHex( buf,10, 2 ) appends "0A" to the string buffer.*/ + static void printHex(StringBuffer buf,int value,int size) { + String hexString = Integer.toHexString(value); + int fill = size - hexString.length(); + if (fill>0) { + for (int i=0;i=0;i --) { + char c = value.charAt( i ); + int number; + if ( c >= '0' && c<='9') { + number = c - '0'; + } else if ( c >= 'A' && c<='F') { + number = (c - 'A') + 10; + } else { + throw new NumberFormatException("Can't parse HexValue " + value); + } + result += number * basis; + basis = basis * 16; + } + return result; + } + + public static Color getColorForHexOrNull(String hexString) { + if ( hexString != null ) { + try { + return RaplaColorList.getColorForHex(hexString ); + } catch ( NumberFormatException ex ) { + } + } + return null; + } + + public static Color getColorForHex(String hexString) throws NumberFormatException { + if ( hexString == null || hexString.indexOf('#') != 0 || hexString.length()!= 7 ) + throw new NumberFormatException("Can't parse HexValue " + hexString); + String rString = hexString.substring(1,3).toUpperCase(); + String gString = hexString.substring(3,5).toUpperCase(); + String bString = hexString.substring(5,7).toUpperCase(); + int r = decode( rString); + int g = decode( gString); + int b = decode( bString); + return new Color(r, g, b); + } + + + public static Color darken(Color color, int i) { + int newBlue = Math.max( color.getBlue() - i, 0); + int newRed = Math.max( color.getRed() - i, 0); + int newGreen = Math.max( color.getGreen() - i, 0); + return new Color( newRed, newGreen,newBlue, color.getAlpha()); + } + +} diff --git a/Rapla/src/org/rapla/gui/toolkit/RaplaFrame.java b/Rapla/src/org/rapla/gui/toolkit/RaplaFrame.java new file mode 100644 index 0000000..05b6feb --- /dev/null +++ b/Rapla/src/org/rapla/gui/toolkit/RaplaFrame.java @@ -0,0 +1,124 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.toolkit; + +import java.awt.AWTEvent; +import java.awt.event.WindowEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyVetoException; +import java.beans.VetoableChangeListener; +import java.util.ArrayList; + +import javax.swing.JFrame; + +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +public class RaplaFrame extends JFrame + implements + FrameController +{ + private static final long serialVersionUID = 1L; + + FrameControllerList frameList = null; + ArrayList listenerList = new ArrayList(); + /** + This frame registers itself on the FrameControllerList on contextualzize + and unregisters upon dispose(). + Use addVetoableChangeListener() to get notified on a window-close event (and throw + a veto if necessary. + * @throws RaplaException + */ + public RaplaFrame(RaplaContext sm) throws RaplaException { + enableEvents(AWTEvent.WINDOW_EVENT_MASK); + /* + AWTAdapterFactory fact = + AWTAdapterFactory.getFactory(); + if (fact != null) { + fact.createFocusAdapter( this ).ignoreFocusComponents(new FocusTester() { + public boolean accept(Component component) { + return !(component instanceof HTMLView) ; + } + }); + }*/ + frameList = (FrameControllerList) sm.lookup(FrameControllerList.ROLE); + frameList.add(this); + } + + protected void processWindowEvent(WindowEvent e) { + if (e.getID() == WindowEvent.WINDOW_CLOSING) { + try { + fireFrameClosing(); + close(); + } catch (PropertyVetoException ex) { + return; + } + } + super.processWindowEvent(e); + } + + public void addVetoableChangeListener(VetoableChangeListener listener) { + listenerList.add(listener); + } + + public void removeVetoableChangeListener(VetoableChangeListener listener) { + listenerList.remove(listener); + } + + public VetoableChangeListener[] getVetoableChangeListeners() { + return (VetoableChangeListener[])listenerList.toArray(new VetoableChangeListener[]{}); + } + + + + void fireFrameClosing() throws PropertyVetoException { + if (listenerList.size() == 0) + return; + // The propterychange event indicates that the window + // is closing. + PropertyChangeEvent evt = new PropertyChangeEvent( + this + ,"visible" + ,new Boolean(true) + ,new Boolean(false) + ) + ; + VetoableChangeListener[] listeners = getVetoableChangeListeners(); + for (int i = 0;i=0; + } + + public void insertAfterId(Component component,String id) { + if ( id == null) { + getPopupMenu().add( component ); + } else { + int index = getIndexOfEntryWithId( id ) ; + getPopupMenu().insert( component, index + 1); + } + } + + public void insertBeforeId(JComponent component,String id) { + int index = getIndexOfEntryWithId( id ); + getPopupMenu().insert( component, index); + } + + +} + + diff --git a/Rapla/src/org/rapla/gui/toolkit/RaplaMenuItem.java b/Rapla/src/org/rapla/gui/toolkit/RaplaMenuItem.java new file mode 100644 index 0000000..11237ef --- /dev/null +++ b/Rapla/src/org/rapla/gui/toolkit/RaplaMenuItem.java @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.toolkit; + +import javax.swing.JMenuItem; + +public class RaplaMenuItem extends JMenuItem implements IdentifiableMenuEntry { + private static final long serialVersionUID = 1L; + + String id; + + public RaplaMenuItem(String id) { + super(); + this.id = id; + } + + public String getId() { + return id; + } + + public static RaplaMenuItem[] EMPTY_ARRAY = new RaplaMenuItem[] {}; + +} + + diff --git a/Rapla/src/org/rapla/gui/toolkit/RaplaMenubar.java b/Rapla/src/org/rapla/gui/toolkit/RaplaMenubar.java new file mode 100644 index 0000000..36ef9f9 --- /dev/null +++ b/Rapla/src/org/rapla/gui/toolkit/RaplaMenubar.java @@ -0,0 +1,83 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.toolkit; + +import java.awt.Component; +import java.util.ArrayList; +import java.util.Iterator; + +import javax.swing.JMenuBar; + +public class RaplaMenubar extends JMenuBar { + private static final long serialVersionUID = 1L; + + public RaplaMenubar() { + super(); + } + + /** returns -1 if there is no */ + private int getIndexOfEntryWithId(String id) { + int size = getComponentCount(); + for ( int i=0;i< size;i++) + { + Component component = getComponent( i ); + if ( component instanceof IdentifiableMenuEntry) { + IdentifiableMenuEntry comp = (IdentifiableMenuEntry) component; + if ( id != null && id.equals( comp.getId() ) ) + { + return i; + } + } + } + return -1; + } + + public void insertAfterId(String id,Component component) { + int index = getIndexOfEntryWithId( id ) + 1; + insert( component, index); + } + + public void insertBeforeId(String id,Component component) { + int index = getIndexOfEntryWithId( id ); + insert( component, index); + } + + private void insert(Component component, int index) { + int size = getComponentCount(); + + ArrayList list = new ArrayList(); + + // save the components begining with index + + for (int i = index ; i < size; i++) + { + list.add( getComponent(index) ); + } + // now remove all components begining with index + for (int i = index ; i < size; i++) + { + remove(index); + } + // now add the new component + add( component ); + + // and the removed components + for (Iterator it = list.iterator();it.hasNext();) + { + add( (Component) it.next() ); + } + } + +} + + diff --git a/Rapla/src/org/rapla/gui/toolkit/RaplaPopupMenu.java b/Rapla/src/org/rapla/gui/toolkit/RaplaPopupMenu.java new file mode 100644 index 0000000..68bf98b --- /dev/null +++ b/Rapla/src/org/rapla/gui/toolkit/RaplaPopupMenu.java @@ -0,0 +1,79 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.toolkit; + +import java.awt.Component; + +import javax.swing.JComponent; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; + +public class RaplaPopupMenu extends JPopupMenu implements MenuInterface { + private static final long serialVersionUID = 1L; + + public RaplaPopupMenu() { + super(); + } + + private int getIndexOfEntryWithId(String id) { + int size = getComponentCount(); + for ( int i=0;i< size;i++) + { + Component component = getComponent( i ); + if ( component instanceof IdentifiableMenuEntry) { + IdentifiableMenuEntry comp = (IdentifiableMenuEntry) component; + if ( id != null && id.equals( comp.getId() ) ) + { + return i; + } + } + } + return -1; + } + + public void removeAllBetween(String startId, String endId) { + int startIndex = getIndexOfEntryWithId( startId ); + int endIndex = getIndexOfEntryWithId( endId); + if ( startIndex < 0 || endIndex < 0 ) + return; + + for ( int i= startIndex + 1; i< endIndex ;i++) + { + remove( startIndex ); + } + + } + + public void insertAfterId(Component component,String id) { + if ( id == null) { + add ( component ); + } else { + int index = getIndexOfEntryWithId( id ) ; + insert( component, index +1); + } + } + + public void insertBeforeId(JComponent component,String id) { + int index = getIndexOfEntryWithId( id ); + insert( component, index); + } + + public void remove( JMenuItem item ) + { + remove( item ); + } + + +} + + diff --git a/Rapla/src/org/rapla/gui/toolkit/RaplaSeparator.java b/Rapla/src/org/rapla/gui/toolkit/RaplaSeparator.java new file mode 100644 index 0000000..4aeb98f --- /dev/null +++ b/Rapla/src/org/rapla/gui/toolkit/RaplaSeparator.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.toolkit; + +import javax.swing.JSeparator; + +public class RaplaSeparator extends JSeparator implements IdentifiableMenuEntry { + private static final long serialVersionUID = 1L; + + String id; + + public RaplaSeparator(String id) { + super(); + this.id = id; + } + + public String getId() { + return id; + } + +} + + diff --git a/Rapla/src/org/rapla/gui/toolkit/RaplaTree.java b/Rapla/src/org/rapla/gui/toolkit/RaplaTree.java new file mode 100644 index 0000000..d84c948 --- /dev/null +++ b/Rapla/src/org/rapla/gui/toolkit/RaplaTree.java @@ -0,0 +1,459 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.toolkit; + +import java.awt.Color; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Stack; + +import javax.swing.BorderFactory; +import javax.swing.JScrollPane; +import javax.swing.JTree; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; +import javax.swing.tree.TreeSelectionModel; + +import org.rapla.components.util.Tools; + +/** Encapsulates the complex tree class and provides some basic functionality like + * life model exchanging while keeping the Tree state or the integration of the popup listener. + */ +final public class RaplaTree extends JScrollPane { + private static final long serialVersionUID = 1L; + + ArrayList m_popupListeners = new ArrayList(); + ArrayList m_doubleclickListeners = new ArrayList(); + ArrayList m_changeListeners = new ArrayList(); + JTree jTree = new JTree() { + private static final long serialVersionUID = 1L; + public String getToolTipText(MouseEvent evt) { + if (toolTipRenderer == null) + { + return super.getToolTipText(evt); + } + int row = getRowForLocation(evt.getX(),evt.getY()); + if (row >=0) + { + return toolTipRenderer.getToolTipText(this,row); + } + return super.getToolTipText(evt); + } + + public Point getToolTipLocation(MouseEvent evt) { + return new Point(getWidth(), 0); + } + + }; + Listener listener = new Listener(); + + private boolean treeSelectionListenerBlocked = false; + private boolean bMultiSelect = false; + TreePath selectedPath = null; + TreeToolTipRenderer toolTipRenderer; + + public RaplaTree() { + jTree.setBorder( BorderFactory.createEtchedBorder(Color.white,new Color(178, 178, 178))); + jTree.setRootVisible(false); + jTree.setShowsRootHandles(true); + //jTree.putClientProperty("JTree.lineStyle", "None"); + getViewport().add(jTree, null); + + jTree.addTreeSelectionListener( listener ); + jTree.addMouseListener( listener ); + setMultiSelect(bMultiSelect); + } + + public void setToolTipRenderer(TreeToolTipRenderer renderer) { + toolTipRenderer = renderer; + } + + public void addChangeListener(ChangeListener listener) { + m_changeListeners.add(listener); + } + public void removeChangeListener(ChangeListener listener) { + m_changeListeners.remove(listener); + } + + /** An ChangeEvent will be fired to every registered ChangeListener + * when the selection has changed. + */ + protected void fireValueChanged() { + if (m_changeListeners.size() == 0) + return; + ChangeListener[] listeners = getChangeListeners(); + ChangeEvent evt = new ChangeEvent(this); + for (int i = 0;i 1"); + // System.out.println("Button= " + me.getButton() + "Cliks= " + me.getClickCount() + " " + me.getComponent().getClass().getName()); + } + } + } + + public void setEnabled(boolean enabled) { + jTree.setEnabled(enabled); + } + + private Object getFromNode(TreeNode node) { + if (node == null) return null; + return getObject(node); + } + + private Object getLastSelectedElement() { + if (selectedPath != null) { + TreeNode node = (TreeNode) + selectedPath.getLastPathComponent(); + return getFromNode(node); + } else { + return null; + } + } + + private static Object getObject(Object treeNode) { + try { + if (treeNode == null) + return null; + if (treeNode instanceof DefaultMutableTreeNode) + return ((DefaultMutableTreeNode) treeNode).getUserObject(); + if (treeNode instanceof RecursiveNode) + return ((RecursiveNode) treeNode).getUserObject(); + return treeNode.getClass().getMethod("getUserObject",Tools.EMPTY_CLASS_ARRAY).invoke(treeNode, Tools.EMPTY_ARRAY); + } catch (Exception ex) { + return null; + } + } + + public void exchangeTreeModel(TreeModel model) { + boolean notifySelection; + try { + treeSelectionListenerBlocked = true; + notifySelection = exchangeTreeModel( model, jTree ) ; + } finally { + treeSelectionListenerBlocked = false; + } + if ( notifySelection ) { + this.fireValueChanged(); + } + } + + /** Exchanges the tree-model while trying to preserve the selection an expansion state. + * Returns if the selection has been affected by the excahnge.*/ + public static boolean exchangeTreeModel(TreeModel model,JTree tree) { + Collection expanded = new HashSet(); + Collection selected = new HashSet(); + for (int i=0;i0) { + return col.iterator().next(); + } else { + return null; + } // end of else + } + + + public List getSelectedElements() { + TreePath[] path = jTree.getSelectionPaths(); + int size = 0; + if (path != null) + size = path.length; + List list = new LinkedList(); + for (int i=0;i < size;i++) { + TreeNode node = (TreeNode) path[i].getLastPathComponent(); + Object obj = getFromNode(node); + if (obj != null) + list.add(obj); + } + return list; + } + + public Object getInfoElement() { + if ( bMultiSelect) { + return getLastSelectedElement(); + } else { + return getSelectedElement(); + } // end of else + } + + public void unselectAll() { + jTree.setSelectionInterval(-1,-1); + } + + public void requestFocus() { + jTree.requestFocus(); + } + + public void expandAll() { + int i = 0; + while (i 1) { + RecursiveNode[] nodes2 = new RecursiveNode[nodes.length-1]; + System.arraycopy(nodes,0,nodes2,0,nodes.length-1); + tree.expandPath(new TreePath(nodes2)); + } + tree.setSelectionPath(treePath); + tree.scrollPathToVisible(treePath); + } + + /* returns the path from the root to the node*/ + public TreeNode[] getPath() { + int parentCount = countParents(); + TreeNode[] path = new TreeNode[parentCount + 1]; + for (int i=parentCount ;i>0;i--) { + if (i == parentCount) + path[i-1] = getParent(); + else + path[i-1] = path[i].getParent(); + } + path[parentCount] = this; + return path; + } + + /* returns the path from the root to the node*/ + public TreePath getTreePath() { + Object[] nodes = getPath(); + return new TreePath( nodes ); + } + + public int getIndex(TreeNode treeNode) { + Object[] childNodes = getChildNodes(); + for (int i=0;i +

    This document is the description of the classes and interfaces used in rapla.

    +

     Version @doc.version@

    +

    For more information contact the developers mailinglist +or take look at the documentation section on our homepage. +

    +@see @doc.homepage@ +@see mailinglist + + + diff --git a/Rapla/src/org/rapla/package.html b/Rapla/src/org/rapla/package.html new file mode 100644 index 0000000..fb35784 --- /dev/null +++ b/Rapla/src/org/rapla/package.html @@ -0,0 +1,8 @@ + +

    This package contains the classes for initializing a rapla-system. It provides +to default entry-points for starting a rapla system. That are the Main and +the MainServer classes. +@see rapla.sourceforge.net + + + diff --git a/Rapla/src/org/rapla/plugin/RaplaExtensionPoints.java b/Rapla/src/org/rapla/plugin/RaplaExtensionPoints.java new file mode 100644 index 0000000..72966ac --- /dev/null +++ b/Rapla/src/org/rapla/plugin/RaplaExtensionPoints.java @@ -0,0 +1,72 @@ +package org.rapla.plugin; + + + + +/** Constant Pool of all basic extension points of the Rapla system. + * You can add your extension in the provideService Method of your PluginDescriptor + *

    + * container.addContainerProvidedComponent( REPLACE_WITH_EXTENSION_POINT_NAME, REPLACE_WITH_CLASS_IMPLEMENTING_EXTENSION, config);
    + * 
    + * @see org.rapla.framework.PluginDescriptor +*/ + +public interface RaplaExtensionPoints +{ + /** add your own views to Rapla, by providing a org.rapla.gui.ViewFactory */ + String CALENDAR_VIEW_EXTENSION = "org.rapla.gui.CalendarView"; + + /** A client extension is started automaticaly when a user has successfully login into the Rapla system*/ + String CLIENT_EXTENSION = "org.rapla.clientPlugin"; + /** A server extension is started automaticaly when the server is up and running and connected to a data store*/ + String SERVER_EXTENSION = "org.rapla.serverPlugin"; + + /** + * You can add arbitrary serlvet pages to your rapla webapp. + * + * Example that adds a page with the name "my-page-name" and the class + * "org.rapla.plugin.myplugin.MyPageGenerator". You can call this page with rapla?page=my-page-name + *

    + * In the provideService Method of your PluginDescriptor do the following +

    +     container.addContainerProvidedComponent( RaplaExtensionPoints.SERVLET_PAGE_EXTENSION, "org.rapla.plugin.myplugin.MyPageGenerator", "my-page-name", config);
    +     
    + + *@see org.rapla.servletpages.RaplaPageGenerator + m*/ + String SERVLET_PAGE_EXTENSION = "org.rapla.serverPage"; + + /** You can add a specific configuration panel for your plugin.*/ + String PLUGIN_OPTION_PANEL_EXTENSION = "org.rapla.plugin.Option"; + /** You can add additional option panels for editing the user preference. + * @see org.rapla.entities.configuration.Preferences*/ + String USER_OPTION_PANEL_EXTENSION = "org.rapla.UserOptions"; + /** You can add additional option panels for the editing the system preferences + * @see org.rapla.entities.configuration.Preferences*/ + String SYSTEM_OPTION_PANEL_EXTENSION = "org.rapla.SystemOptions"; + + /** add your own wizard to create events. See ReservationWizard class */ + String RESERVATION_WIZARD_EXTENSION = "org.rapla.gui.Reservationwizard"; + + /** add your own menu entries in the context menu of an object. To do this add + an ObjectMenuFactory under this entry.*/ + String OBJECT_MENU_EXTENSION = "org.rapla.gui.ObjectMenuFactory"; + + + /** add your own submenus to the admin menu. Get the MenuExtensionPoint via the lookup method and add the menu. + * */ + String ADMIN_MENU_EXTENSION_POINT ="org.rapla.gui.AdminMenuInsert"; + /** add your own import-menu submenus*/ + String IMPORT_MENU_EXTENSION_POINT ="org.rapla.gui.ImportMenuInsert"; + /** add your own export-menu submenus*/ + String EXPORT_MENU_EXTENSION_POINT ="org.rapla.gui.ExportMenuInsert"; + /** add your own view-menu submenus*/ + String VIEW_MENU_EXTENSION_POINT ="org.rapla.gui.ViewMenuInsert"; + /** add your own edit-menu submenus*/ + String EDIT_MENU_EXTENSION_POINT = "org.rapla.gui.EditMenuInsert"; + /** add your own help-menu submenus*/ + String HELP_MENU_EXTENSION_POINT = "org.rapla.gui.ExtraMenuInsert"; + + /** you can add your own entries on the index page Just add a HTMLMenuEntry to the list*/ + String HTML_MAIN_MENU_EXTENSION_POINT = "org.rapla.servletpages"; +} diff --git a/Rapla/src/org/rapla/plugin/RaplaPluginMetaInfo.java b/Rapla/src/org/rapla/plugin/RaplaPluginMetaInfo.java new file mode 100644 index 0000000..ab6a8b3 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/RaplaPluginMetaInfo.java @@ -0,0 +1,13 @@ +package org.rapla.plugin; + + + + +/** Constant Pool of all meta infos Rapla system */ +public interface RaplaPluginMetaInfo +{ + + /** return Boolean.TRUE in the getPluginMetaInfos( String key ) to enable your plugin by default in the plugin options method */ + String METAINFO_PLUGIN_ENABLED_BY_DEFAULT = "org.rapla.framework.EnablePluginByDefault"; + +} diff --git a/Rapla/src/org/rapla/plugin/abstractcalendar/AbstractHTMLCalendarPage.java b/Rapla/src/org/rapla/plugin/abstractcalendar/AbstractHTMLCalendarPage.java new file mode 100644 index 0000000..b28a0c4 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/abstractcalendar/AbstractHTMLCalendarPage.java @@ -0,0 +1,302 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.abstractcalendar; + +import java.io.IOException; +import java.text.ParseException; +import java.util.Calendar; +import java.util.Date; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.rapla.components.calendarview.html.AbstractHTMLView; +import org.rapla.components.util.SerializableDateTimeFormat; +import org.rapla.entities.User; +import org.rapla.entities.configuration.RaplaConfiguration; +import org.rapla.facade.RaplaComponent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.framework.RaplaLocale; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.CalendarOptions; +import org.rapla.gui.CalendarOptionsImpl; +import org.rapla.servletpages.RaplaPageGenerator; + +public abstract class AbstractHTMLCalendarPage extends RaplaComponent implements RaplaPageGenerator +{ + public static final String SHOW_NAVIGATION_ENTRY = "org.rapla.plugin.abstractcalendar.show_navigation"; + public static final String SAVE_SELECTED_DATE = "org.rapla.plugin.abstractcalendar.save_selected_date"; + protected AbstractHTMLView view; + String calendarviewHTML; + protected CalendarModel model = null; + RaplaBuilder builder; + + public AbstractHTMLCalendarPage(RaplaContext context, CalendarModel calendarModel) throws RaplaException { + super( context); + this.model = (CalendarModel) calendarModel.clone(); + } + + protected RaplaBuilder createBuilder() throws RaplaException { + RaplaBuilder builder = new HTMLRaplaBuilder( getContext()); + builder.setRepeatingVisible( false); + builder.setExceptionsExcluded( true ); + builder.setFromModel( model, view.getStartDate(), view.getEndDate() ); + return builder; + } + + abstract protected AbstractHTMLView createCalendarView(); + abstract protected int getIncrementSize(); + + public String getCalendarHTML() { + return calendarviewHTML; + } + + public String getDateChooserHTML( Date date) { + Calendar calendar = getRaplaLocale().createCalendar(); + calendar.setTime( date ); + return HTMLDateComponents.getDateSelection( "", calendar,getLocale()); + } + + public Date getStartDate() { + return view.getStartDate(); + } + + public Date getEndDate() { + return view.getEndDate(); + } + + public String getTitle() { + return model.getNonEmptyTitle(); + } + + public int getDay( Date date) { + Calendar calendarview = getRaplaLocale().createCalendar(); + calendarview.setTime( date); + return calendarview.get( Calendar.DATE); + } + + public int getMonth( Date date) { + Calendar calendarview = getRaplaLocale().createCalendar(); + calendarview.setTime( date); + return calendarview.get( Calendar.MONTH) + 1; + } + + public int getYear( Date date) { + Calendar calendarview = getRaplaLocale().createCalendar(); + calendarview.setTime( date); + return calendarview.get( Calendar.YEAR); + } + + public void generatePage( ServletContext context,HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + response.setContentType("text/html; charset=" + getRaplaLocale().getCharsetNonUtf() ); + java.io.PrintWriter out = response.getWriter(); + + String filename = getFilename( request ); + boolean navigationVisible = isNavigationVisible( request ); + + RaplaLocale raplaLocale= getRaplaLocale(); + Calendar calendarview = raplaLocale.createCalendar(); + calendarview.setTime( model.getSelectedDate() ); + if ( request.getParameter("today") != null ) { + calendarview.setTime( getQuery().today() ); + } else if ( request.getParameter("day") != null ) { + String dateString = request.getParameter("year") + "-" + + request.getParameter("month") + "-" + + request.getParameter("day"); + try { + SerializableDateTimeFormat format = new SerializableDateTimeFormat( raplaLocale.createCalendar()); + calendarview.setTime( format.parseDate( dateString, false ) ); + } catch (ParseException ex) { + throw new ServletException( ex); + } + if ( request.getParameter("next") != null) + calendarview.add( getIncrementSize(), 1); + if ( request.getParameter("prev") != null) + calendarview.add( getIncrementSize(), -1); + } + + Date currentDate = calendarview.getTime(); + model.setSelectedDate( currentDate ); + view = createCalendarView(); + view.setLocale( getRaplaLocale().getLocale() ); + view.setTimeZone(getRaplaLocale().getTimeZone()); + view.setToDate(model.getSelectedDate()); + model.setStartDate( view.getStartDate() ); + model.setEndDate( view.getEndDate() ); + + try { + builder = createBuilder(); + } catch (RaplaException ex) { + getLogger().error("Can't create builder ", ex); + throw new ServletException( ex ); + } + view.rebuild( builder); + + calendarviewHTML = view.getHtml(); + + + out.println(""); + out.println(""); + out.println(" " + getTitle() + ""); + out.println(" "); + out.println(" "); + // tell the html page where its favourite icon is stored + out.println(" "); + out.println(" "); + out.println(""); + out.println(""); + + // Start DateChooser + if (navigationVisible) + { + out.println("
    "); + out.println("
    "); + out.println(getHiddenField("page", "calendar")); + out.println(getHiddenField("user", model.getUser().getUsername())); + out.println(getHiddenField("file", filename)); + // add the "previous" button including the css class="super button" + out.println(" "); + out.println("  "); + out.println(getDateChooserHTML(currentDate)); + // add the "goto" button including the css class="super button" + out.println(""); + out.println(" "); + out.println(" "); + // add the "today" button including the css class="super button" + out.println(""); + out.println(" "); + // add the "next" button including the css class="super button" + out.println(""); + out.println("
    "); + out.println("
    "); + } + + // End DateChooser + // Start weekview + out.println("

    "); + out.println(getTitle()); + out.println("

    "); + out.println("
    "); + out.println(getCalendarHTML()); + out.println("
    "); + + // end weekview + out.println(""); + out.println(""); + + } + + public String getFilename(HttpServletRequest request) { + return request.getParameter("file"); + } + + + public boolean isNavigationVisible( HttpServletRequest request) { + String config = (String)model.getOption( SHOW_NAVIGATION_ENTRY ); + if ( config == null || config.equals( "true" )) + { + return true; + } + return !config.equals( "false" ) && request.getParameter("hide_nav") == null; + } + + String getHiddenField( String fieldname, String value) { + return ""; + } + + String getHiddenField( String fieldname, int value) { + return getHiddenField( fieldname, String.valueOf(value)); + } + + /* + public String getLegend() { + if ( !getCalendarOptions().isResourceColoring()) { + return ""; + } + Iterator it = view.getBlocks().iterator(); + LinkedList coloredAllocatables = new LinkedList(); + while (it.hasNext()) { + List list = ((HTMLRaplaBlock)it.next()).getContext().getColoredAllocatables(); + for (int i=0;i\n"); + buf.append("\n"); + buf.append(""); + buf.append( getI18n().getString("legend")); + buf.append(":"); + buf.append(""); + try { + AllocatableInfoUI allocatableInfo = new AllocatableInfoUI(getContext()); + while (it.hasNext()) { + Allocatable allocatable = (Allocatable) it.next(); + String color = (String) builder.getColorMap().get(allocatable); + if (color == null) // (!color_map.containsKey(allocatable)) + continue; + + buf.append(""); + buf.append(""); + buf.append( allocatable.getName(getRaplaLocale().getLocale()) ); + buf.append(""); + buf.append( allocatableInfo.getTooltip( allocatable)); + buf.append(""); + buf.append(""); + buf.append(""); + } + } catch (RaplaException ex) { + getLogger().error( "Error generating legend",ex); + } + buf.append("\n"); + buf.append(""); + return buf.toString(); + } + */ + + public CalendarOptions getCalendarOptions() { + RaplaConfiguration conf = null; + try { + User user = model.getUser(); + if ( user != null) + { + conf = (RaplaConfiguration)getQuery().getPreferences( user ).getEntry(CalendarOptionsImpl.CALENDAR_OPTIONS); + } + if ( conf == null) + { + conf = (RaplaConfiguration)getQuery().getPreferences( null ).getEntry(CalendarOptionsImpl.CALENDAR_OPTIONS); + } + if ( conf != null) + { + return new CalendarOptionsImpl( conf.getConfig()); + } + } catch (RaplaException ex) { + + } + return (CalendarOptions)getService( CalendarOptions.ROLE); + } + +} + diff --git a/Rapla/src/org/rapla/plugin/abstractcalendar/AbstractRaplaBlock.java b/Rapla/src/org/rapla/plugin/abstractcalendar/AbstractRaplaBlock.java new file mode 100644 index 0000000..69791ae --- /dev/null +++ b/Rapla/src/org/rapla/plugin/abstractcalendar/AbstractRaplaBlock.java @@ -0,0 +1,149 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Gereon Fassbender, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.plugin.abstractcalendar; + +import java.util.Date; +import java.util.List; + +import org.rapla.components.util.DateTools; +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.entities.Named; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Reservation; +import org.rapla.framework.RaplaLocale; + + +public abstract class AbstractRaplaBlock +{ + RaplaBuilder.RaplaBlockContext m_context; + Date m_start; + Date m_end; + RaplaLocale m_raplaLocale; + + AbstractRaplaBlock() { + + } + + void contextualize(RaplaBuilder.RaplaBlockContext context) { + m_context = context; + m_raplaLocale = getBuildContext().getRaplaLocale(); + } + + public String getName(Named named) { + return named.getName(m_raplaLocale.getLocale()); + } + + public Date getStart() { + return m_start; + } + + public Date getEnd() { + return m_end; + } + + protected I18nBundle getI18n() { + return getBuildContext().getI18n(); + } + + public void setStart(Date start) { + m_start = start; + } + + public void setEnd(Date end) { + m_end = end; + } + + public Appointment getAppointment() { + return getContext().getAppointment(); + } + + public Reservation getReservation() { + return getAppointment().getReservation(); + } + + RaplaBuilder.RaplaBlockContext getContext() { + return m_context; + } + + RaplaBuilder.BuildContext getBuildContext() { + return getContext().getBuildContext(); + } + + public boolean isMovable() { + return getContext().isMovable() && !isException(); + } + + public boolean startsAndEndsOnSameDay() { + return DateTools.isSameDay( + getAppointment().getStart().getTime() + ,getAppointment().getEnd().getTime() -1 + ) + ; + } + + String[] getColorsAsHex() { + List allocatables = getContext().getColoredAllocatables(); + if (allocatables.size() ==0 ) { + String color = RaplaBuilder.getColorForClassifiable( getReservation() ); + if ( color == null) { + color = getBuildContext().lookupColorString(null); + } + return new String[] {color}; + } + String[] results = new String[allocatables.size()]; + for (int i=0;i', 28); + int incrementSize = Calendar.WEEK_OF_YEAR; + CalendarModel model; + Listener listener = new Listener(); + JPanel periodPanel; + + JButton todayButton= new RaplaButton(getString("today"), RaplaButton.SMALL); + + public DateChooserPanel(RaplaContext sm, CalendarModel model) throws RaplaException { + super( sm ); + this.model = model; + periodChooser = new PeriodChooser(getContext(),PeriodChooser.START_ONLY); + dateSelection = createRaplaCalendar(); + + //prevButton.setText("<"); + //nextButton.setText(">"); + double pre =TableLayout.PREFERRED; + double[][] sizes = {{0.02,pre,5,pre,2,pre,0.02,0.9,5,0.02} + ,{/*0.5,*/pre/*,0.5*/}}; + TableLayout tableLayout = new TableLayout(sizes); + JPanel calendarPanel = new JPanel(); + TitledBorder titleBorder = BorderFactory.createTitledBorder(getI18n().getString("date")); + calendarPanel.setBorder(titleBorder); + panel.setLayout(tableLayout); + calendarPanel.add(dateSelection); + calendarPanel.add(todayButton); + calendarPanel.add(prevButton); + //rku: eingefuegt + + + calendarPanel.add(nextButton); + panel.add(calendarPanel, "1, 0"); + periodPanel = new JPanel(new GridLayout(1,1)); + titleBorder = BorderFactory.createTitledBorder(getI18n().getString("period")); + periodPanel.setBorder(titleBorder); + periodPanel.add(periodChooser); + panel.add(periodPanel,"7,0"); + + + periodChooser.setDate(getQuery().today()); + + nextButton.addActionListener( listener ); + prevButton.addActionListener( listener); + + dateSelection.addDateChangeListener( listener); + periodChooser.addActionListener( listener); + + todayButton.addActionListener(listener); + update(); + } + + boolean listenersEnabled = true; + public void update() throws RaplaException + { + listenersEnabled = false; + try { + final PeriodModel periodModel = getPeriodModel(); + periodChooser.setPeriodModel( periodModel); + if ( model.getSelectedDate() == null) { + model.setSelectedDate( getQuery().today()); + } + Date date = model.getSelectedDate(); + periodChooser.setDate( date); + dateSelection.setDate( date); + periodPanel.setVisible( periodModel.getSize() > 0); + + } finally { + listenersEnabled = true; + } + } + + + public void dispose() { + periodChooser.removeActionListener( listener ); + ContainerUtil.dispose( periodChooser ); + } + + public void setNavigationVisible( boolean enable) { + nextButton.setVisible( enable); + prevButton.setVisible( enable); + } + + /** possible values are Calendar.DATE, Calendar.WEEK_OF_YEAR, Calendar.MONTH and Calendar.YEAR. + Default is Calendar.WEEK_OF_YEAR. + */ + public void setIncrementSize(int incrementSize) { + this.incrementSize = incrementSize; + } + + /** registers new DateChangeListener for this component. + * An DateChangeEvent will be fired to every registered DateChangeListener + * when the a different date is selected. + * @see DateChangeListener + * @see DateChangeEvent + */ + public void addDateChangeListener(DateChangeListener listener) { + listenerList.add(listener); + } + + /** removes a listener from this component.*/ + public void removeDateChangeListener(DateChangeListener listener) { + listenerList.remove(listener); + } + + public DateChangeListener[] getDateChangeListeners() { + return (DateChangeListener[])listenerList.toArray(new DateChangeListener[]{}); + } + + /** An ActionEvent will be fired to every registered ActionListener + * when the a different date is selected. + */ + protected void fireDateChange(Date date) { + if (listenerList.size() == 0) + return; + DateChangeListener[] listeners = getDateChangeListeners(); + DateChangeEvent evt = new DateChangeEvent(this,date); + for (int i = 0;i\n"); + for (int i=1;i<=31;i++) { + buf.append(""); + buf.append("\n"); + } + buf.append(""); + return buf.toString(); + } + + static public String getMonthSelection(String name, int selectedValue, Locale locale) { + StringBuffer buf = new StringBuffer(); + buf.append(""); + return buf.toString(); + } + + static public String getYearSelection(String name, int selectedValue, int minYear, int maxYear) { + StringBuffer buf = new StringBuffer(); + buf.append(""); + return buf.toString(); + } +} + + diff --git a/Rapla/src/org/rapla/plugin/abstractcalendar/HTMLRaplaBlock.java b/Rapla/src/org/rapla/plugin/abstractcalendar/HTMLRaplaBlock.java new file mode 100644 index 0000000..f8db37e --- /dev/null +++ b/Rapla/src/org/rapla/plugin/abstractcalendar/HTMLRaplaBlock.java @@ -0,0 +1,143 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.plugin.abstractcalendar; + + +import org.rapla.components.calendarview.html.HTMLBlock; +import org.rapla.components.util.xml.XMLWriter; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.framework.RaplaException; +import org.rapla.gui.internal.view.AppointmentInfoUI; + + +class HTMLRaplaBlock extends AbstractRaplaBlock implements HTMLBlock { + private int m_day; + private int m_row; + private int m_rowCount; + private int index = 0; + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } + + public void setRowCount(int rows) { + m_rowCount = rows; + } + + public void setRow(int row) { + m_row = row; + } + + public int getRowCount() { + return m_rowCount; + } + + public int getRow() { + return m_row; + } + + public void setDay(int day) { + m_day = day; + } + + public int getDay() { + return m_day; + } + + public String getBackgroundColor() { + return getColorsAsHex()[0]; + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + String label = XMLWriter.encode(getName( getReservation())); + String timeString = getTimeString(false); + + if ( getContext().isAnonymous()) { + String anonymous = "    ???"; + if ( timeString != null) { + return timeString + " " + anonymous; + } else { + return anonymous; + } + } + + if ( timeString != null) { + label = timeString + "
    " + label; + } + try { + AppointmentInfoUI reservationInfo = new AppointmentInfoUI(getContext().getBuildContext().getServiceManager()); + String url = null; + Attribute[] attributes = getReservation().getClassification().getAttributes(); + for ( int i=0;i" ); + if ( url != null) { + buf.append( ""); + } + buf.append( label ); + if ( url != null) { + buf.append( ""); + } + buf.append( ""); + buf.append(reservationInfo.getTooltip(getAppointment())); + buf.append( ""); + buf.append( "" ); + } catch (RaplaException ex) { + buf.append( label ); + } + if (getBuildContext().isPersonVisible()) { + Allocatable[] persons = getReservation().getPersons(); + for (int i=0; i"); + buf.append(""); + buf.append(XMLWriter.encode(getName(persons[i]))); + buf.append(""); + } + } + if (getBuildContext().isResourceVisible()) { + Allocatable[] resources = getReservation().getResources(); + for (int i=0; i"); + buf.append(""); + buf.append(XMLWriter.encode(getName(resources[i]))); + buf.append(""); + } + } + return buf.toString(); + } +} diff --git a/Rapla/src/org/rapla/plugin/abstractcalendar/HTMLRaplaBuilder.java b/Rapla/src/org/rapla/plugin/abstractcalendar/HTMLRaplaBuilder.java new file mode 100644 index 0000000..2a78b19 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/abstractcalendar/HTMLRaplaBuilder.java @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.plugin.abstractcalendar; + +import java.util.Calendar; +import java.util.Date; + +import org.rapla.components.calendarview.Block; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + + +class HTMLRaplaBuilder extends RaplaBuilder { + static String COLOR_NO_RESOURCE = "#BBEEBB"; + int m_rowsPerHour = 4; + /** shared calendar instance. Only used for temporary stored values. */ + String m_html; + int index = 0; + + public HTMLRaplaBuilder(RaplaContext sm) throws RaplaException { + super(sm); + } + + boolean isExceptionsExcluded() { + return true; + } + + Block createBlock(RaplaBlockContext blockContext, Date start, Date end) { + HTMLRaplaBlock block = new HTMLRaplaBlock(); + block.setIndex( index ++ ); + block.setStart(start); + block.setEnd(end); + block.contextualize(blockContext); + + Calendar calendar = getRaplaLocale().createCalendar(); + calendar.setTime(start); + int row = (int) ( + calendar.get(Calendar.HOUR_OF_DAY)* m_rowsPerHour + + Math.round((calendar.get(Calendar.MINUTE) * m_rowsPerHour)/60.0) + ); + block.setRow(row); + block.setDay(calendar.get(Calendar.DAY_OF_WEEK)); + + calendar.setTime(block.getEnd()); + int endRow = (int) ( + calendar.get(Calendar.HOUR_OF_DAY)* m_rowsPerHour + + Math.round((calendar.get(Calendar.MINUTE) * m_rowsPerHour)/60.0) + ); + int rowCount = endRow -row; + block.setRowCount(rowCount); + /* + System.out.println("Start " + start + " End " + end); + System.out.println("Block " + block.getReservation().getName(null) + + " Row: " + row + " Endrow: " + endRow + " Rowcount " + rowCount ); + */ + return block; + } + +} diff --git a/Rapla/src/org/rapla/plugin/abstractcalendar/IntervallChooserPanel.java b/Rapla/src/org/rapla/plugin/abstractcalendar/IntervallChooserPanel.java new file mode 100644 index 0000000..5e11a34 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/abstractcalendar/IntervallChooserPanel.java @@ -0,0 +1,223 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.plugin.abstractcalendar; + +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.Date; + +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.border.TitledBorder; + +import org.rapla.components.calendar.DateChangeEvent; +import org.rapla.components.calendar.DateChangeListener; +import org.rapla.components.calendar.RaplaCalendar; +import org.rapla.components.layout.TableLayout; +import org.rapla.components.util.DateTools; +import org.rapla.entities.domain.Period; +import org.rapla.facade.PeriodModel; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.internal.common.PeriodChooser; +import org.rapla.gui.toolkit.RaplaWidget; + +public class IntervallChooserPanel extends RaplaGUIComponent + implements + RaplaWidget +{ + Collection listenerList = new ArrayList(); + PeriodChooser periodChooser; + + JPanel panel = new JPanel(); + RaplaCalendar startDateSelection; + RaplaCalendar endDateSelection; + boolean listenersEnabled = true; + CalendarModel model; + Listener listener = new Listener(); + JPanel periodPanel; + + public IntervallChooserPanel(RaplaContext sm, CalendarModel model) throws RaplaException { + super(sm); + this.model = model; + + periodChooser = new PeriodChooser(getContext(),PeriodChooser.START_AND_END); + periodChooser.setWeekOfPeriodVisible( false ); + + startDateSelection = createRaplaCalendar(); + endDateSelection = createRaplaCalendar(); + //prevButton.setText("<"); + //nextButton.setText(">"); + double pre =TableLayout.PREFERRED; + double[][] sizes = {{0.02,pre, 5, pre, 5,0.9,0.02} + ,{pre}}; + TableLayout tableLayout = new TableLayout(sizes); + panel.setLayout(tableLayout); + + JPanel startPanel = new JPanel(); + TitledBorder titleBorder = BorderFactory.createTitledBorder(getString("start_date")); + startPanel.setBorder(titleBorder); + + startPanel.add(startDateSelection); + panel.add(startPanel,"1,0"); + + JPanel endPanel = new JPanel(); + titleBorder = BorderFactory.createTitledBorder(getString("end_date")); + endPanel.setBorder(titleBorder); + endPanel.add(endDateSelection); + + panel.add(endPanel,"3,0"); + + periodPanel = new JPanel(new GridLayout(1,1)); + titleBorder = BorderFactory.createTitledBorder(getString("period")); + periodPanel.setBorder(titleBorder); + periodPanel.add(periodChooser); + + panel.add( periodPanel,"5,0"); + periodChooser.addActionListener( listener ); + + + startDateSelection.addDateChangeListener( listener ); + endDateSelection.addDateChangeListener( listener ); + update(); + } + + public void update() throws RaplaException + { + listenersEnabled = false; + try { + if ( model.getStartDate() == null) { + model.setStartDate( getQuery().today()); + } + if ( model.getEndDate() == null) { + Calendar cal = getRaplaLocale().createCalendar(); + cal.setTime( model.getStartDate()); + cal.add( Calendar.YEAR, 1); + model.setEndDate( cal.getTime()); + } + if ( model.getSelectedDate() == null) { + model.setSelectedDate( model.getStartDate() ); + } + Date startDate = model.getStartDate(); + startDateSelection.setDate( startDate); + final PeriodModel periodModel = getPeriodModel(); + periodChooser.setPeriodModel( periodModel); + periodChooser.setDate( startDate ); + Date endDate = model.getEndDate(); + periodPanel.setVisible( periodModel.getSize() > 0); + endDateSelection.setDate( DateTools.subDay(endDate)); + } finally { + listenersEnabled = true; + } + } + + /** registers new DateChangeListener for this component. + * An DateChangeEvent will be fired to every registered DateChangeListener + * when the a different date is selected. + * @see DateChangeListener + * @see DateChangeEvent + */ + public void addDateChangeListener(DateChangeListener listener) { + listenerList.add(listener); + } + + /** removes a listener from this component.*/ + public void removeDateChangeListener(DateChangeListener listener) { + listenerList.remove(listener); + } + + public DateChangeListener[] getDateChangeListeners() { + return (DateChangeListener[])listenerList.toArray(new DateChangeListener[]{}); + } + + /** An ActionEvent will be fired to every registered ActionListener + * when the a different date is selected. + */ + protected void fireDateChange(Date date) { + if (listenerList.size() == 0) + return; + DateChangeListener[] listeners = getDateChangeListeners(); + DateChangeEvent evt = new DateChangeEvent(this,date); + for (int i = 0;i in top of all HTML- and Swing-View Blocks + private boolean splitByAllocatables = false; + protected boolean isEmptyBlockVisible = false; + private HashMap colors = new HashMap(); //This currently only works with HashMap + private User editingUser; + private boolean isResourceColoring; + /** default buildStrategy is {@link GroupAllocatablesStrategy}.*/ + BuildStrategy buildStrategy; + + HashSet allReservationsForAllocatables = new HashSet(); + int max =0; + int min =0; + + List preparedBlocks = null; + private boolean conflictsSelected = false; + + public RaplaBuilder(RaplaContext sm) throws RaplaException { + super(sm); + buildStrategy = new GroupAllocatablesStrategy( getRaplaLocale().getLocale() ); + } + + public void setFromModel(CalendarModel model, Date startDate, Date endDate) throws RaplaException { + Collection reservations = new HashSet(Arrays.asList( model.getReservations( startDate, endDate ))); + Allocatable[] all = model.getSelectedAllocatables(); + Collection allocatables = Arrays.asList( all); + if ( model.getStartDate() != null && allocatables.size()> 0) { + allReservationsForAllocatables.addAll( Arrays.asList(getQuery().getReservations( all, startDate, endDate))); + } + conflictsSelected = false; + + for (Object selectedObject: model.getSelectedObjects()) + { + if (selectedObject instanceof Conflict) + { + conflictsSelected = true; + } + } + isEmptyBlockVisible = allocatables.size() == 0; + isResourceColoring =getCalendarOptions( model.getUser()).isResourceColoring(); + selectReservations( reservations ); + + + /* Uncomment this to color allocatables in the reservation view + if ( allocatables.size() == 0) { + allocatables = new ArrayList(); + for (int i=0;i< reservations.size();i++) { + Reservation r = (Reservation) reservations.get( i ); + Allocatable[] a = r.getAllocatables(); + for (int j=0;jo2.hashCode()) + return -1; + if (o1.hashCode() getAffectedAppointments(Collection selectedReservations, Collection selectedAllocatables, + Date start, Date end, boolean emptyBlockVisible, boolean excludeExceptions) { + if ( selectedAllocatables == null ) + selectedAllocatables = Tools.EMPTY_LIST; + List preparedBlocks = new ArrayList(); + // The appointments that are explictly connected to + // a selected allocatable + ArrayList connectedAppointments = new ArrayList(); + Iterator it = selectedReservations.iterator(); + while (it.hasNext()){ + Reservation r= (Reservation) it.next(); + //System.out.println("Selected : " + r.getName(Locale.getDefault())); + Appointment[] appointments= r.getAppointments(); + Allocatable[] allocatables = r.getAllocatables(); + + // this flag is set true if one of the allocatables of a + // reservation matches a selected allocatable. + boolean allocatableMatched = false; + + // a reservation is wildcardConnected, if at least one of its + // allocatables is connected to all appointments and + // this allocatable is selected. + boolean wildcardConnected = false; + + connectedAppointments.clear(); + for (int i=0; i splitBlocks(Collection preparedBlocks, Date startDate, Date endDate) { + List result = new ArrayList(); + for (AppointmentBlock block:preparedBlocks) { + long blockStart = block.getStart(); + long blockEnd = block.getEnd(); + Appointment appointment = block.getAppointment(); + boolean isException = block.isException(); + if (DateTools.isSameDay(blockStart, blockEnd)) { + result.add( new AppointmentBlock(blockStart, blockEnd, appointment, isException)); + } else { + long firstBlockDate = Math.max(blockStart, startDate.getTime()); + long lastBlockDate = Math.min(blockEnd, endDate.getTime()); + long currentBlockDate = firstBlockDate; + while ( currentBlockDate >= blockStart && DateTools.cutDate( currentBlockDate ) < lastBlockDate) { + long start; + long end; + if (DateTools.isSameDay(blockStart, currentBlockDate)) { + start= blockStart; + } else { + start = DateTools.cutDate(currentBlockDate); + } + if (DateTools.isSameDay(blockEnd, currentBlockDate) && !DateTools.isMidnight(blockEnd)) { + end = blockEnd; + }else { + end = DateTools.fillDate( currentBlockDate ) -1; + } + //System.out.println("Adding Block " + new Date(start) + " - " + new Date(end)); + result.add ( new AppointmentBlock(start, end, appointment,isException)); + currentBlockDate+= DateTools.MILLISECONDS_PER_DAY; + } + } + } + return result; + } + + + /** selects all blocks that should be visible and calculates the max start- and end-time */ + public void prepareBuild(Date start,Date end) { + boolean excludeExceptions = isExceptionsExcluded(); + HashSet allReservations = new HashSet( selectedReservations); + allReservations.addAll( allReservationsForAllocatables); + preparedBlocks = getAffectedAppointments(allReservations, selectedAllocatables, start, end, isEmptyBlockVisible, excludeExceptions); + preparedBlocks = splitBlocks(preparedBlocks, start, end); + + // calculate new start and end times + max =0; + min =24; + for (AppointmentBlock block:preparedBlocks) + { + int starthour = DateTools.getHourOfDay(block.getStart()); + int startminute = DateTools.getMinuteOfHour(block.getStart()); + int endhour = DateTools.getHourOfDay(block.getEnd()); + int endminute = DateTools.getMinuteOfHour(block.getEnd()); + if ((starthour != 0 || startminute != 0) && starthour0 || endminute>0)) + min = Math.max(0,endhour-1); + if ((endhour != 0 || endminute != 0) && (endhour != 23 && endminute!=59) && endhour>max) + max = Math.min(24,endhour + 1); + if (starthour>=max) + max = Math.min(24,starthour +1); + } + } + + public int getMin() { + Assert.notNull(preparedBlocks, "call prepareBuild first"); + return min; + } + + public int getMax() { + Assert.notNull(preparedBlocks, "call prepareBuild first"); + return max; + } + + abstract Block createBlock(RaplaBlockContext blockContext, Date start, Date end); + + public void build(CalendarView wv) { + ArrayList blocks = new ArrayList(); + BuildContext buildContext = new BuildContext(this, blocks); + Assert.notNull(preparedBlocks, "call prepareBuild first"); + for (AppointmentBlock block:preparedBlocks) + { + Date start = new Date( block.getStart() ); + Date end = new Date( block.getEnd() ); + Appointment appointment = block.getAppointment(); + RaplaBlockContext[] blockContext = getBlocksForAppointment( appointment, buildContext ); + for ( int j=0;j< blockContext.length; j++) { + blocks.add( createBlock(blockContext[j], start, end)); + } + } + + buildStrategy.build(wv, blocks); + } + + private RaplaBlockContext[] getBlocksForAppointment(Appointment appointment, BuildContext buildContext) { + boolean isEventSelected = selectedReservations.contains( appointment.getReservation()); + RaplaBlockContext firstContext = new RaplaBlockContext( appointment, this, buildContext, null, isEventSelected ); + List selectedAllocatables = firstContext.getColoredAllocatables(); + if ( !splitByAllocatables || selectedAllocatables.size() < 2) { + return new RaplaBlockContext[] { firstContext }; + } + RaplaBlockContext[] context = new RaplaBlockContext[ selectedAllocatables.size() ]; + for ( int i= 0;i0:=n-times + //int repeatingDuration = getCalendarOptions().getRepeating(); // 0:infinite; >0:=n-times <0: until + showPopupMenu( component, p, start,end, slotNr, selectedAllocatables, selectedAllocatable, repeatingDuration); + } + + protected void showPopupMenu(Component component,Point p,Date start,Date end, int slotNr, Allocatable[] selectedAllocatables, Allocatable selectedAllocatable, int repeatingDuration) + { + try { + User user = getUser(); + Date today = getQuery().today(); + boolean canAllocate = false; + for ( int i=0;i< selectedAllocatables.length; i++) { + if (selectedAllocatables[i].canAllocate( user, start, end, today)) + canAllocate = true; + } + RaplaPopupMenu menu= new RaplaPopupMenu(); + CalendarOptions options = getCalendarOptions(); + if ( canAllocate || (selectedAllocatables.length == 0 && canUserAllocateSomething( getUser())) ) { + ReservationAction action = new ReservationAction(getContext(), component,p); + Collection list = getModel().getSelectedObjectsAndChildren(); + if ( list.size() > 0) { + action.changeObject ( list.iterator().next()); + } + // set a default eventype: 1. from user preference 2. guess the type + String eventType = options.getEventType(); + DynamicType guessedType; + if(eventType==null) + guessedType = action.guessType(); + else + guessedType = getQuery().getDynamicType(eventType); + addAppointmentAction(menu,component,p).setNew(start,end, selectedAllocatable, null, guessedType, repeatingDuration); // 1x + RepeatingType rt = options.getRepeatingType(); + addAppointmentAction(menu,component,p).setNew(start,end, selectedAllocatable, rt, guessedType, repeatingDuration); // default repeating + + ReservationEdit[] editWindows = getReservationController().getEditWindows(); + for ( int i=0;i" + text + ""; + } + + private Color adjustColor( String org, int alpha ) + { + Map colorMap = (Map) alphaMap.get( alpha ); + if ( colorMap == null ) + { + colorMap = new HashMap(); + alphaMap.put( alpha, colorMap ); + } + Color color = (Color) colorMap.get( org ); + if ( color == null ) + { + Color or; + try + { + or = RaplaColorList.getColorForHex( org ); + } + catch ( NumberFormatException nf ) + { + or = RaplaColorList.getColorForHex( "#FFFFFF" ); + } + color = new Color( or.getRed(), or.getGreen(), or.getBlue(), alpha ); + colorMap.put( org, color ); + } + + return color; + } + + public void paint( Graphics g ) + { + Dimension dim = getSize(); + paint( g, dim.width, dim.height ); + } + + public void paint( Graphics g, int width, int height ) + { + int alpha = g.getColor().getAlpha(); + + if ( !getContext().isEventSelected() ) + { + alpha = 80; + paintBackground( g, width, height, alpha ); + } + else + { + paintBackground( g, width, height, alpha ); + } + + //boolean isException = getAppointment().getRepeating().isException(getStart().getTime()); + Color fg = adjustColor( FOREGROUND_COLOR, alpha ); //(isException() ? Color.white : Color.black); + g.setColor( fg ); + + if ( getAppointment().getRepeating() != null && getBuildContext().isRepeatingVisible() ) + { + if ( !getContext().isAnonymous() && getContext().isEventSelected() && !isException() ) + { + getBuildContext().getRepeatingIcon().paintIcon( this, g, width - 17, 0 ); + } + /* + if ( getBuildContext().isTimeVisible() ) + g.clipRect(0,0, width -17, height); + */ + } + // y will store the y-position of the carret + int y = -2; + // Draw the Reservationname + boolean small = (height < 30); + + String timeString = getTimeString(small); + StringBuffer buf = new StringBuffer(); + if ( timeString != null ) + { + if ( !small) + { + g.setFont( FONT_SMALL_TITLE ); + y = drawString( g, timeString, y, 2, false ) - 1; + } + else + { + buf.append( timeString ); + buf.append( " " ); + } + } + if ( !small) + { + g.setFont( FONT_TITLE ); + } + else + { + g.setFont( FONT_SMALL_TITLE ); + } + + if ( getContext().isAnonymous() ) + { + y += 4; + g.setFont( FONT_INVISIBLE ); + String label = getI18n().getString( "not_visible" ); + buf.append(label); + y = drawString( g, buf.toString(), y, 5, true ) + 2; + return; + } + + String label = getName( getReservation() ); + buf.append(label); + y = drawString( g, buf.toString(), y, 2, true ) + 2; + + // If the y reaches the lowerBound "..." will be displayed + double lowerBound = height - 11; + + if ( getBuildContext().isPersonVisible() ) + { + g.setFont( FONT_PERSON ); + g.setColor( fg ); + Allocatable[] persons = getReservation().getPersons(); + for ( int i = 0; i < persons.length; i++ ) + { + if ( !getContext().isVisible( persons[i] ) ) + continue; + String text = getName( persons[i] ); + if ( y > lowerBound ) + { + text = "..."; + y -= 7; + } + y = drawString( g, text, y, 7, true ); + } + } + + if ( getBuildContext().isResourceVisible() ) + { + Allocatable[] resources = getReservation().getResources(); + g.setFont( FONT_RESOURCE ); + g.setColor( fg ); + for ( int i = 0; i < resources.length; i++ ) + { + if ( !getContext().isVisible( resources[i] ) ) + continue; + String text = getName( resources[i] ); + if ( y > lowerBound ) + { + text = "..."; + y -= 7; + } + y = drawString( g, text, y, 7, true ); + } + } + } + + private void setExceptionPaint( Graphics g ) + { + Paint p = new TexturePaint( getExceptionImage(), new Rectangle( 14, 14 ) ); + ( (Graphics2D) g ).setPaint( p ); + } + + private void paintBackground( Graphics g, int width, int height, int alpha ) + { + String[] colors = getColorsAsHex(); + double colWidth = (double) ( width - 2 ) / colors.length; + int x = 0; + for ( int i = 0; i < colors.length; i++ ) + { + g.setColor( adjustColor( colors[i], alpha ) ); + g.fillRect( (int) Math.ceil( x ) + 1, 1, (int) Math.ceil( colWidth ), height - 2 ); + if ( isException() ) + { + setExceptionPaint( g ); + g.fillRect( (int) Math.ceil( x ) + 1, 1, (int) Math.ceil( colWidth ), height - 2 ); + } + x += colWidth; + } + + //g.setColor( adjustColor( "#000000", alpha ) ); + g.setColor( linecolor ); + g.drawRoundRect( 0, 0, width - 1, height - 1, 5, 5 ); + } + + private int findBreakingSpace( char[] c, int offset, int len, int maxWidth, FontMetrics fm ) + { + int index = -1; + for ( int i = offset; i < offset + len; i++ ) + { + if ( c[i] == ' ' && fm.charsWidth( c, offset, i - offset ) < maxWidth ) + index = i; + } + return index; + } + + private int findBreaking( char[] c, int offset, int len, int maxWidth, FontMetrics fm ) + { + int index = 0; + for ( int i = offset; i < offset + len; i++ ) + { + if ( fm.charsWidth( c, offset, i - offset ) < maxWidth ) + index = i; + } + return index - 1; + } + + // @return the new y-coordiante below the text + private int drawString( Graphics g, String text, int y, int indent, boolean breakLines ) + { + FontMetrics fm = g.getFontMetrics(); + //g.setFont(new Font("SimSun",Font.PLAIN, 12)); + char[] c = text.toCharArray(); + int cWidth = getSize().width - indent; + int height = fm.getHeight(); + + int len = c.length; + int offset = 0; + int x = indent; + int maxWidth = ( y >= 14 || getAppointment().getRepeating() == null || !getBuildContext() + .isRepeatingVisible() ) + ? cWidth + : cWidth - 12; + if ( !breakLines ) + { + maxWidth = maxWidth - 5; + } + else + { + while ( offset < c.length && fm.charsWidth( c, offset, len ) > maxWidth ) + { + int breakingSpace = findBreakingSpace( c, offset, len, maxWidth, fm ); + //int x = bCenter ? (getSize().width - width)/2 : indent ; + y = y + height; + if ( breakingSpace > 0 && breakLines ) + { + g.drawChars(c,offset,breakingSpace-offset,x,y); + // System.out.println("Drawing " + new String(c,offset,breakingSpace-offset)); + len -= breakingSpace - offset + 1; + offset = breakingSpace + 1; + } + else + { + int breaking = findBreaking( c, offset, len, maxWidth, fm ); + if ( breaking > 0 ) + { + g.drawChars(c,offset,breaking-offset,x,y); + g.drawString( "\\", fm.charsWidth( c, offset, breaking - offset ) + indent, y ); + // System.out.println("Drawing " + new String(c,offset,breaking-offset) + "\\"); + len -= breaking - offset; + offset = breaking; + } + else + { + return y; + } + } + // System.out.println("New len " + len + " new offset " + offset); + maxWidth = cWidth; + } + } + y = y + height; + + g.drawChars( c, offset, len, x, y ); + // System.out.println("Drawing rest " + new String(c,offset,len)); + return y; + } + + public void mouseClicked( MouseEvent arg0 ) + { + + } + + public void mousePressed( MouseEvent arg0 ) + {} + + public void mouseReleased( MouseEvent evt ) + { + Point mp = evt.getPoint(); + boolean inside = mp.x >= 0 && mp.y >= 0 && mp.x <= getWidth() && mp.y <= getHeight(); + changeLineBorder( inside && getContext().isEventSelected() ); + } + + public void mouseEntered( MouseEvent evt ) + { + if ( ( ( evt.getModifiers() & MouseEvent.BUTTON1_MASK ) > 0 ) || !getContext().isEventSelected() ) + { + return; + } + changeLineBorder( true ); + } + + public void mouseExited( MouseEvent evt ) + { + if ( ( evt.getModifiers() & MouseEvent.BUTTON1_MASK ) > 0 ) + { + return; + } + changeLineBorder( false ); + } + + public void mouseDragged( MouseEvent arg0 ) + {} + + public void mouseMoved( MouseEvent arg0 ) + { + // TODO Auto-generated method stub + + } + + private void changeLineBorder( boolean active ) + { + List blocks = getBuildContext().getBlocks(); + for ( Iterator it = blocks.iterator(); it.hasNext(); ) + { + SwingRaplaBlock block = (SwingRaplaBlock) it.next(); + + if ( block.getAppointment().equals( getAppointment() ) ) + { + block.linecolor = active ? LINECOLOR_ACTIVE : LINECOLOR_INACTIVE; + block.m_view.repaint(); + } + else if ( block.getReservation().equals( getReservation() ) ) + { + block.linecolor = active ? LINECOLOR_SAME_RESERVATION : LINECOLOR_INACTIVE; + block.m_view.repaint(); + } + } + } + + } + +} diff --git a/Rapla/src/org/rapla/plugin/abstractcalendar/SwingRaplaBuilder.java b/Rapla/src/org/rapla/plugin/abstractcalendar/SwingRaplaBuilder.java new file mode 100644 index 0000000..7a49567 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/abstractcalendar/SwingRaplaBuilder.java @@ -0,0 +1,40 @@ + +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.plugin.abstractcalendar; + +import java.util.Date; + +import org.rapla.components.calendarview.Block; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + + +public class SwingRaplaBuilder extends RaplaBuilder +{ + public SwingRaplaBuilder(RaplaContext sm) throws RaplaException { + super(sm); + } + + /** + * @see org.rapla.plugin.abstractcalendar.RaplaBuilder#createBlock(calendar.RaplaBuilder.RaplaBlockContext, java.util.Date, java.util.Date) + */ + Block createBlock(RaplaBlockContext blockContext, Date start, Date end) { + SwingRaplaBlock block = new SwingRaplaBlock(); + block.contextualize(blockContext); + block.setStart(start); + block.setEnd(end); + return block; + } +} diff --git a/Rapla/src/org/rapla/plugin/abstractcalendar/package.html b/Rapla/src/org/rapla/plugin/abstractcalendar/package.html new file mode 100644 index 0000000..6c0f8f3 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/abstractcalendar/package.html @@ -0,0 +1,8 @@ + +Contains the rapla-specific classes that helps in the creation +of the calendar-like view plugins. +API in this package is likly to change + + + + diff --git a/Rapla/src/org/rapla/plugin/appointmentmarker/AppointmentMarker.java b/Rapla/src/org/rapla/plugin/appointmentmarker/AppointmentMarker.java new file mode 100644 index 0000000..d75cf36 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/appointmentmarker/AppointmentMarker.java @@ -0,0 +1,126 @@ +package org.rapla.plugin.appointmentmarker; + +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.TreeSet; + +import org.rapla.components.util.DateTools; +import org.rapla.components.util.SerializableDateTimeFormat; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.AppointmentBlock; +import org.rapla.entities.domain.Repeating; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.facade.RaplaComponent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +public class AppointmentMarker extends RaplaComponent +{ + static public String ROLE = AppointmentMarker.class.getName(); + + public AppointmentMarker( RaplaContext context ) throws RaplaException + { + super( context ); + } + + /** returns if the event has the appointmentmarker attribute*/ + public boolean canBeMarked(Reservation event) + { + Attribute att = event.getClassification().getType().getAttribute( AppointmentMarkerPlugin.MARKER_ATTRIBUTE_KEY); + return att!= null; + } + + /** returns all the marked Dates as an Set (of Date object(*/ + public Set getMarkedDates(Reservation event) { + final Set markedDates = new TreeSet(); + Attribute att = event.getClassification().getType().getAttribute( AppointmentMarkerPlugin.MARKER_ATTRIBUTE_KEY); + + Object value = event.getClassification().getValue( att ); + final SerializableDateTimeFormat format = new SerializableDateTimeFormat(); + + // Parse the marked dates + if ( value != null) + { + StringTokenizer tokenizer = new StringTokenizer((String) value,";"); + while ( tokenizer.hasMoreTokens()) + { + String dateString=tokenizer.nextToken(); + try + { + Date markedDate = format.parseDate( dateString, false); + markedDates.add(markedDate); + } + catch (ParseException e) + { + getLogger().error("Couldnt parse date " + dateString + " for appointment marking. Ignoring"); + } + } + } + return markedDates; + } + + /** returns if the event is marked on the passed dates*/ + public boolean isMarked( Reservation event, Date date) + { + return isMarked( date, getMarkedDates( event)); + } + + /** same as isMarked but you can pass the markedDates*/ + static public boolean isMarked( Date date, Collection markedDates) + { + Date normalizedDate = DateTools.cutDate( date); + return markedDates.contains( normalizedDate ); + } + + /** marks/unmarks an event on the passed date*/ + public void setMarked( Reservation event, Date dateToMark, boolean mark) + { + final SerializableDateTimeFormat format = new SerializableDateTimeFormat(); + Set markedDates = getMarkedDates( event ); + Date currentSelectedDate = DateTools.cutDate( dateToMark ); + if ( mark ) + { + markedDates.add( currentSelectedDate ); + } + else + { + markedDates.remove( currentSelectedDate ); + } + String newValue = ""; + for (Iterator it = markedDates.iterator();it.hasNext();) + { + Date markedDate = (Date) it.next(); + newValue+=format.formatDate( markedDate) + ";"; + } + event.getClassification().setValue(AppointmentMarkerPlugin.MARKER_ATTRIBUTE_KEY, newValue); + } + + /** returns null if the event contains an appointment that repeats forever*/ + public List getAllBlocks(Reservation event) { + List blocks = new ArrayList(); + Appointment[] appointments = event.getAppointments(); + for (int i = 0; i array = marker.getAllBlocks( event ); + Set markedDates = marker.getMarkedDates( event ); + if ( array == null) + { + return null; + } + for ( AppointmentBlock block: array) + { + Date start = new Date(block.getStart()); + String dateString = getRaplaLocale().formatDate( start ); + if ( AppointmentMarker.isMarked( start, markedDates)) + { + dateString = " * " + dateString ; + } + else + { + dateString = " " + dateString ; + } + list.add( dateString); + } + return list; + + } + +} diff --git a/Rapla/src/org/rapla/plugin/appointmentmarker/AppointmentMarkerOption.java b/Rapla/src/org/rapla/plugin/appointmentmarker/AppointmentMarkerOption.java new file mode 100644 index 0000000..14ba200 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/appointmentmarker/AppointmentMarkerOption.java @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.appointmentmarker; + +import java.awt.BorderLayout; +import java.util.Locale; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.DefaultConfiguration; +import org.rapla.components.layout.TableLayout; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.DefaultPluginOption; +import org.rapla.gui.OptionPanel; + +/** Not used yet */ +public class AppointmentMarkerOption extends DefaultPluginOption implements OptionPanel { + JTextField markerLabel = new JTextField(); + + public AppointmentMarkerOption( RaplaContext sm ) throws RaplaException + { + super( sm ); + } + + protected JPanel createPanel() throws RaplaException { + JPanel panel = super.createPanel(); + JPanel content = new JPanel(); + double[][] sizes = new double[][] { + {5,TableLayout.PREFERRED, 5,TableLayout.PREFERRED,TableLayout.FILL,5} + ,{TableLayout.PREFERRED,5,TableLayout.PREFERRED,5,TableLayout.PREFERRED} + }; + addCopyPaste( markerLabel); + markerLabel.setColumns( 10); + TableLayout tableLayout = new TableLayout(sizes); + content.setLayout(tableLayout); + content.add(new JLabel("Add a new text attribute (hidden) with the key 'appointmentmarker' to an eventtype."), "1,0,4,0"); + content.add(new JLabel("Then you can mark events of that type by right clicking in the calendar view."), "1,2,4,2"); + content.add(new JLabel("Marker Menu Label:"), "1,4"); + content.add( markerLabel, "3,4"); + panel.add( content, BorderLayout.CENTER); + return panel; + } + + + protected void addChildren( DefaultConfiguration newConfig) { + DefaultConfiguration markerLabelConf = new DefaultConfiguration("marker-label"); + markerLabelConf.setValue( markerLabel.getText() ); + newConfig.addChild( markerLabelConf ); + } + + protected void readConfig( Configuration config) { + String markerLabelText = config.getChild("marker-label").getValue("mark"); + markerLabel.setText( markerLabelText ); + } + + public void show() throws RaplaException { + super.show(); + } + + public void commit() throws RaplaException { + super.commit(); + } + + /** + * @see org.rapla.gui.DefaultPluginOption#getDescriptorClassName() + */ + public String getDescriptorClassName() { + return AppointmentMarkerPlugin.class.getName(); + } + + public String getName(Locale locale) { + return "Appointment Marker Plugin"; + } + +} diff --git a/Rapla/src/org/rapla/plugin/appointmentmarker/AppointmentMarkerPlugin.java b/Rapla/src/org/rapla/plugin/appointmentmarker/AppointmentMarkerPlugin.java new file mode 100644 index 0000000..82b6970 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/appointmentmarker/AppointmentMarkerPlugin.java @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.appointmentmarker; +import org.apache.avalon.framework.configuration.Configuration; +import org.rapla.framework.Container; +import org.rapla.framework.PluginDescriptor; +import org.rapla.plugin.RaplaExtensionPoints; + +public class AppointmentMarkerPlugin implements PluginDescriptor +{ + public static final String PLUGIN_CLASS = AppointmentMarkerPlugin.class.getName(); + public static final String MARKER_ATTRIBUTE_KEY = "appointmentmarker"; + public String toString() { + return "Appointment Marker"; + } + + public void provideServices(Container container, Configuration config) { + container.addContainerProvidedComponent( RaplaExtensionPoints.PLUGIN_OPTION_PANEL_EXTENSION,AppointmentMarkerOption.class.getName(),AppointmentMarkerPlugin.class.getName(), config); + + if ( !config.getAttributeAsBoolean("enabled", false) ) + return; + + container.addContainerProvidedComponent( RaplaExtensionPoints.OBJECT_MENU_EXTENSION, AppointmentMarkerMenuFactory.class.getName(), PLUGIN_CLASS, config); + container.addContainerProvidedComponent( AppointmentMarker.ROLE, AppointmentMarker.class.getName(), PLUGIN_CLASS, config); + } + + public Object getPluginMetaInfos( String key ) + { + return null; + } + +} + diff --git a/Rapla/src/org/rapla/plugin/archiver/ArchiverOption.java b/Rapla/src/org/rapla/plugin/archiver/ArchiverOption.java new file mode 100644 index 0000000..89e435c --- /dev/null +++ b/Rapla/src/org/rapla/plugin/archiver/ArchiverOption.java @@ -0,0 +1,112 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.archiver; + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Locale; + +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JPanel; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.DefaultConfiguration; +import org.rapla.components.calendar.RaplaNumber; +import org.rapla.components.layout.TableLayout; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.DefaultPluginOption; +import org.rapla.gui.OptionPanel; + +public class ArchiverOption extends DefaultPluginOption implements OptionPanel { + + RaplaNumber dayField = new RaplaNumber(new Integer(25), new Integer(0),null,false); + JCheckBox removeOlderYesNo = new JCheckBox(); + + + + public ArchiverOption(RaplaContext sm) throws RaplaException { + super(sm); + } + + protected JPanel createPanel() throws RaplaException { + JPanel panel = super.createPanel(); + JPanel content = new JPanel(); + double[][] sizes = new double[][] { + {5,TableLayout.PREFERRED, 5,TableLayout.PREFERRED,5, TableLayout.PREFERRED} + ,{TableLayout.PREFERRED,5,TableLayout.PREFERRED, 5, TableLayout.PREFERRED, 5, TableLayout.PREFERRED} + }; + TableLayout tableLayout = new TableLayout(sizes); + content.setLayout(tableLayout); + content.add(new JLabel("Remove old events"), "1,0"); + content.add( removeOlderYesNo, "3,0"); + content.add(new JLabel("Older than"), "1,2"); + content.add( dayField, "3,2"); + content.add( new JLabel("days"), "5,2"); + removeOlderYesNo.addActionListener( new ActionListener(){ + + public void actionPerformed( ActionEvent e ) + { + dayField.setEnabled( removeOlderYesNo.isSelected()); + } + + }); + panel.add( content, BorderLayout.CENTER); + return panel; + } + + + protected void addChildren( DefaultConfiguration newConfig) { + if ( removeOlderYesNo.isSelected()) + { + DefaultConfiguration smtpPort = new DefaultConfiguration("remove-older-than"); + smtpPort.setValue(dayField.getNumber().intValue() ); + newConfig.addChild( smtpPort ); + } + } + + protected void readConfig( Configuration config) { + int days = config.getChild("remove-older-than").getValueAsInteger(-20); + boolean isEnabled = days != -20; + removeOlderYesNo.setSelected( isEnabled ); + dayField.setEnabled( isEnabled); + if ( days == -20 ) + { + days = 30; + } + dayField.setNumber( new Integer(days)); + } + + public void show() throws RaplaException { + super.show(); + } + + public void commit() throws RaplaException { + super.commit(); + } + + + /** + * @see org.rapla.gui.DefaultPluginOption#getDescriptorClassName() + */ + public String getDescriptorClassName() { + return ArchiverPlugin.class.getName(); + } + + public String getName(Locale locale) { + return "Archiver Plugin"; + } + +} diff --git a/Rapla/src/org/rapla/plugin/archiver/ArchiverPlugin.java b/Rapla/src/org/rapla/plugin/archiver/ArchiverPlugin.java new file mode 100644 index 0000000..4a98d86 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/archiver/ArchiverPlugin.java @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.archiver; +import org.apache.avalon.framework.configuration.Configuration; +import org.rapla.framework.Container; +import org.rapla.framework.PluginDescriptor; +import org.rapla.plugin.RaplaExtensionPoints; + +public class ArchiverPlugin implements PluginDescriptor +{ + + public static final String PLUGIN_CLASS = ArchiverPlugin.class.getName(); + public String toString() { + return "Archiver"; + } + + public void provideServices(Container container, Configuration config) { + container.addContainerProvidedComponent( RaplaExtensionPoints.PLUGIN_OPTION_PANEL_EXTENSION,ArchiverOption.class.getName(),ArchiverPlugin.class.getName(), config); + + if ( !config.getAttributeAsBoolean("enabled", false) ) + return; + + container.addContainerProvidedComponent( RaplaExtensionPoints.SERVER_EXTENSION, ArchiverService.class.getName(), PLUGIN_CLASS, config); + } + + public Object getPluginMetaInfos( String key ) + { + return null; + } + +} + diff --git a/Rapla/src/org/rapla/plugin/archiver/ArchiverService.java b/Rapla/src/org/rapla/plugin/archiver/ArchiverService.java new file mode 100644 index 0000000..08cef9a --- /dev/null +++ b/Rapla/src/org/rapla/plugin/archiver/ArchiverService.java @@ -0,0 +1,107 @@ +package org.rapla.plugin.archiver; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; + +import org.apache.avalon.framework.configuration.Configuration; +import org.rapla.components.util.DateTools; +import org.rapla.entities.User; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.ClassificationFilter; +import org.rapla.facade.RaplaComponent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +public class ArchiverService extends RaplaComponent +{ + + String REMOVE_OLDER_THAN_ENTRY = "remove-older-than"; + + public ArchiverService( RaplaContext context, Configuration config ) throws RaplaException + { + super( context ); + String value = config.getChild( REMOVE_OLDER_THAN_ENTRY).getValue(null); + if ( value != null) + { + Timer timer = new Timer(true); + long period = Long.parseLong( value) * DateTools.MILLISECONDS_PER_DAY; + RemoveTask removeTask = new RemoveTask( period); + // Call it each hour + timer.schedule(removeTask, new Date(), DateTools.MILLISECONDS_PER_HOUR); + } + } + + class RemoveTask extends TimerTask + { + long period; + public RemoveTask( long period ) + { + this.period = period; + } + + public void run() + { + Date endDate = new Date(getClientFacade().today().getTime() - period); + try + { + Reservation[] events = getClientFacade().getReservations((User) null, null, endDate, ClassificationFilter.CLASSIFICATIONFILTER_ARRAY ); + List toRemove = new ArrayList(); + for ( int i=0;i< events.length;i++) + { + Reservation event = events[i]; + if ( isOlderThan( event, endDate)) + { + toRemove.add(event); + } + } + if ( toRemove.size() > 0) + { + getLogger().info("Removing " + toRemove.size() + " old events."); + Reservation[] eventsToRemove = (Reservation[])toRemove.toArray( Reservation.RESERVATION_ARRAY); + int STEP_SIZE = 100; + for ( int i=0;i< eventsToRemove.length;i+=STEP_SIZE) + { + int blockSize = Math.min( eventsToRemove.length- i, STEP_SIZE); + Reservation[] eventBlock = new Reservation[blockSize]; + System.arraycopy( eventsToRemove,i, eventBlock,0, blockSize); + getClientFacade().removeObjects( eventBlock); + } + } + } + catch (RaplaException e) + { + getLogger().error("Could not remove old events ", e); + } + } + } + + private boolean isOlderThan( Reservation event, Date maxAllowedDate ) + { + Appointment[] appointments = event.getAppointments(); + for ( int i=0;i + + + + Name for the view + Name der Ansicht + Nom de la vue + Nombre de la vista + Naam + + + Show navigation bar + Zeige den Navigationsbalken + Affiche la barre de navigation + Indicar la navegación + Navigatie balk + + + Publish calendar to HTML + Veröffentliche Kalender nach HTML + Publier le calendrier en HTML + Publicar el calendario a HTML + + + Web Server + Web-Server + Serveur Web + Servidor Web + Webový Server + Web Server + + + The following views are currently stored: + Folgende Ansichten sind vorhanden: + Les vues suivantes sont disponibles : + Las vistas siguientes están disponibles : + + + Exported calendars + Exportierte Kalender + Calendriers Exportés + calendarios exportados + Gepubliceerde Planningen + + + \ No newline at end of file diff --git a/Rapla/src/org/rapla/plugin/autoexport/CalendarListPageGenerator.java b/Rapla/src/org/rapla/plugin/autoexport/CalendarListPageGenerator.java new file mode 100644 index 0000000..3d19da6 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/autoexport/CalendarListPageGenerator.java @@ -0,0 +1,172 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.autoexport; + +import java.io.IOException; +import java.net.URLEncoder; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.TreeSet; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.rapla.components.util.IOUtil; +import org.rapla.entities.User; +import org.rapla.entities.configuration.CalendarModelConfiguration; +import org.rapla.entities.configuration.Preferences; +import org.rapla.entities.configuration.RaplaMap; +import org.rapla.facade.RaplaComponent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.internal.RaplaStartOption; +import org.rapla.servletpages.RaplaPageGenerator; + + +public class CalendarListPageGenerator extends RaplaComponent implements RaplaPageGenerator +{ + public CalendarListPageGenerator( RaplaContext context ) throws RaplaException + { + super( context ); + setChildBundleName( AutoExportPlugin.RESOURCE_FILE ); + } + + public void generatePage( ServletContext servletContext, HttpServletRequest request, HttpServletResponse response ) + throws IOException, ServletException + { + try + { + String username = request.getParameter( "user" ); + response.setContentType("text/html; charset=" + getRaplaLocale().getCharsetNonUtf() ); + java.io.PrintWriter out = response.getWriter(); + User[] users = getQuery().getUsers(); + Set sortedUsers =new TreeSet( Arrays.asList( users)); + if ( username != null) + { + users = new User[] { getQuery().getUser( username )}; + } + String calendarName = getQuery().getPreferences( null ).getEntryAsString(RaplaStartOption.TITLE, getString("rapla.title")); + out.println( "" ); + out.println( "" ); + out.println( "" + calendarName + "" ); + out.println( "" ); + out.println( "" ); + + out.println( "

    " + getString("webserver") + ": " + calendarName + "

    "); + + for (User user : sortedUsers) + { + Preferences preferences = getQuery().getPreferences( user ); + LinkedHashMap completeMap = new LinkedHashMap(); + CalendarModelConfiguration defaultConf = (CalendarModelConfiguration) preferences.getEntry( CalendarModelConfiguration.CONFIG_ENTRY ); + if ( defaultConf != null) + { + completeMap.put( getString("default"), defaultConf); + } + + final RaplaMap raplaMap = (RaplaMap) preferences.getEntry( "org.rapla.plugin.autoexport" ); + if ( raplaMap != null) + { + completeMap.putAll( raplaMap); + } + SortedMap sortedMap = new TreeMap( new TitleComparator( completeMap)); + sortedMap.putAll( completeMap); + Iterator it = sortedMap.entrySet().iterator(); + + int count =0; + while ( it.hasNext()) + { + Map.Entry entry = (Map.Entry)it.next(); + String key = entry.getKey(); + CalendarModelConfiguration conf = entry.getValue(); + final Object isSet = conf.getOptionMap().get(AutoExportPlugin.HTML_EXPORT); + if( isSet != null && isSet.equals("false")) + { + it.remove(); + continue; + } + if(count == 0) + { + String userName = user.getName(); + if(username == null || userName.trim().length() ==0) + userName = user.getUsername(); + out.println( "

    " + userName + "

    " ); //BJO + out.println( "
      " ); + } + count++; + String title = getTitle(key, conf); + + String filename =URLEncoder.encode( key, "UTF-8" ); + out.print( "
    • " ); + out.print( ""); + out.print( title); + out.print( "" ); + out.println( "
    • " ); + } + if (count > 0) + { + out.println( "
    " ); + } + } + out.println( "" ); + out.println( "" ); + } + catch ( Exception ex ) + { + java.io.PrintWriter out = response.getWriter(); + out.println( IOUtil.getStackTraceAsString( ex ) ); + throw new ServletException( ex ); + } + } + + private String getTitle(String key, CalendarModelConfiguration conf) { + String title = conf.getTitle() ; + if ( title == null || title.trim().length() == 0) + { + title = key; + } + return title; + } + + class TitleComparator implements Comparator { + + Map base; + + public TitleComparator(Map base) { + this.base = base; + } + + public int compare(String a, String b) { + + final String title1 = getTitle(a,base.get(a)); + final String title2 = getTitle(b,base.get(b)); + int result = title1.compareToIgnoreCase( title2); + if ( result != 0) + { + return result; + } + return a.compareToIgnoreCase( b); + + } + } + +} + diff --git a/Rapla/src/org/rapla/plugin/autoexport/CalendarPageGenerator.java b/Rapla/src/org/rapla/plugin/autoexport/CalendarPageGenerator.java new file mode 100644 index 0000000..0681896 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/autoexport/CalendarPageGenerator.java @@ -0,0 +1,119 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.autoexport; + +import java.io.IOException; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.rapla.components.util.IOUtil; +import org.rapla.entities.User; +import org.rapla.facade.RaplaComponent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.ViewFactory; +import org.rapla.gui.internal.common.CalendarModelImpl; +import org.rapla.plugin.RaplaExtensionPoints; +import org.rapla.servletpages.RaplaPageGenerator; + +/******* USAGE: ************ + * ReadOnly calendarview view. + * You will need the autoexport plugin to create a calendarview-view. + * + * Call: + * rapla?page=calendar&user=&file= + * + * Optional Parameters: + * + * &hide_nav: will hide the navigation bar. + * &day=: int-value of the day of month that should be displayed + * &month=: int-value of the month + * &year=: int-value of the year + * &today: will set the view to the current day. Ignores day, month and year + * @throws IOException + * @throws ServletException + * @throws Exception + * @throws RaplaException + */ +public class CalendarPageGenerator extends RaplaComponent implements RaplaPageGenerator +{ + public CalendarPageGenerator( RaplaContext context ) throws RaplaException + { + super( context ); + } + + public void generatePage( ServletContext servletContext, HttpServletRequest request, HttpServletResponse response ) + throws IOException, ServletException + { + try + { + String username = request.getParameter( "user" ); + String filename = request.getParameter( "file" ); + CalendarModelImpl model = null; + User user = getQuery().getUser( username ); + model = new CalendarModelImpl( getContext(),user ); + model.load(filename); + final Object isSet = model.getOption(AutoExportPlugin.HTML_EXPORT); + if( isSet == null || isSet.equals("false")) + { + throw new RaplaException( "Exportfile with name '" + filename + "' not published!" ); + } + + final String viewId = model.getViewId(); + ViewFactory factory = (ViewFactory) getService( RaplaExtensionPoints.CALENDAR_VIEW_EXTENSION + + "/" + + viewId ); + + if ( factory != null ) + { + RaplaPageGenerator currentView = factory.createHTMLView( getContext(), model ); + if ( currentView != null ) + { + currentView.generatePage( servletContext, request, response ); + } + else + { + writeFehler( response, "No view available for exportfile '" + + filename + + "'. Rapla has currently no html support for the view with the id '" + + viewId + + "'." ); + } + } + else + { + writeFehler( response, "No view available for exportfile '" + + filename + + "'. Please install and select the plugin for " + + viewId ); + } + } + catch ( Exception ex ) + { + java.io.PrintWriter out = response.getWriter(); + out.println( IOUtil.getStackTraceAsString( ex ) ); + throw new ServletException( ex ); + } + } + + private void writeFehler( HttpServletResponse response, String message ) throws IOException + { + response.setContentType( "text/html; charset=" + getRaplaLocale().getCharsetNonUtf() ); + java.io.PrintWriter out = response.getWriter(); + out.println( message ); + } + +} diff --git a/Rapla/src/org/rapla/plugin/autoexport/calendar.css b/Rapla/src/org/rapla/plugin/autoexport/calendar.css new file mode 100644 index 0000000..bc3b8f3 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/autoexport/calendar.css @@ -0,0 +1,273 @@ +a, a:visited{ + color: black; + text-decoration: none; +} +a:hover{ + color: black; + background-color: #FFFF88; +} +#calendar a span.tooltip { + display: none; +} + +#calendar a span td { + padding: 0px; + font-size: small; + color: black; +} + +#legend { + align: center; + border-width: 0px; +} + +#legend a span { + display: none; +} + +#legend a span td { + padding: 0px; + font-size: small; + color: black; +} + +#calendar { + width: 100%; +} + +/** calendar tooltip */ +#calendar a:hover span.tooltip { + display: block; + bottom: 0.5em; + right: 0.5em; + padding: .5em; + z-index: 1; + font-size: small; + color: black; + background-color: #FFFF88; +/* Doesn't work in IE */ + position: fixed; + width: 500px; + border: 2px solid #000000; +} + +/* woraround for IE */ +#calendar a:hover span.tooltip { + // position: absolute; +} + +/** legend tooltip */ +#legend a:hover span { + display: block; + bottom: 0.5em; + right: 0.5em; + padding: .5em; + z-index: 1; + font-size: small; + color: black; + background-color: #FFFF88; +/* Doesn't work in IE + position: fixed; + width: auto; + */ + position: absolute; + width: 500px; + border: 2px solid #000000; +} + + +.title { + text-align:center; +} + +.datechooser { + text-align:center; +} + + +.week_table { + width: 100%; + empty-cells: show; + border-spacing:0px; + border-bottom-width:3px; + border-bottom-style:solid; + border-right-width:1px; + border-right-style:solid; + + padding:0px; + border-color:black; +/* workaround for IE */ +// border-collapse:collapse; + background-color: #FFFFFF; +} + +.week_emptycell_black { + font-size:9px; + margin: 0px; + border-top-style:solid; + border-top-width:1px; + border-left-width:0px; + border-right-width:0px; + padding-left: 0px; + padding-right: 0px; +} + +.week_separatorcell_black { + font-size:9px; + border-top-width:1px; + border-top-style:solid; + border-left-width:0px; + border-right-width:1px; + border-right-style:solid; + padding-left: 1px; + border-right-color: #CCCCCC; +} +.week_smallseparatorcell_black { + font-size:9px; + border-left-width:0px; + border-right-width:0px; + border-top-width:1px; + border-top-style:solid; + padding-left: 1px; + padding-right: 1px; + margin-left: 0px; + margin-right: 0px; +} + +.week_emptycell { + font-size:9px; + margin: 0px; + border-color:#CCCCCC; + border-top-style:solid; + border-top-width:1px; + border-left-width:0px; + border-right-width:0px; + padding-left: 0px; + padding-right: 0px; +} + +.week_separatorcell { + font-size:9px; + border-top-width:1px; + border-color:#CCCCCC; + border-top-style:solid; + border-left-width:0px; + border-right-width:1px; + border-right-style:solid; + padding-left: 1px; +} +.week_smallseparatorcell { + font-size:9px; + border-color:#CCCCCC; + border-left-width:0px; + border-right-width:0px; + border-top-width:1px; + border-top-style:solid; + padding-left: 1px; + padding-right: 1px; + margin-left: 0px; + margin-right: 0px; +} + +.week_header { + background-color: #DDDDDD; + text-align: left; + padding-left: 5px; + padding-right: 5px; + border-right-width:1px; + border-right-style:solid; + border-left-width:1px; + border-left-style:solid; + border-top-width:1px; + border-top-style:solid; + border-bottom-width:1px; + border-bottom-style:solid; +} + +.week_number { + font-size: 0.9em; + padding-left: 10px; + padding-right: 10px; + text-align: center; + white-space: nowrap; +} + + +.week_times { + background-color: #DDDDDD; + text-align:right; + padding-right: 7px; + vertical-align: top; + border-left-width:1px; + border-right-width:1px; + border-top-width:1px; + border-left-style:solid; + border-right-style:solid; + border-top-style:solid; +} + +.week_block { + margin: 0px; + padding: 3px; + border-left-width:1px; + border-right-width:1px; + border-top-width:1px; + border-left-style:solid; + border-right-style:solid; + border-top-style:solid; + font-size: 9pt; +} + +.month_table { + width: 100%; + background-color: #FFFFFF; + empty-cells: show; + border-spacing:0px; +/* workaround for IE */ +// border-collapse:collapse; + border: 1px; +} + +.month_header { + background-color: #DDDDDD; + text-align: center; + border:1px; + border-style:solid; +} + +.month_cell { + border:1px; + border-style:solid; +} + +.month_block { + font-size: 8pt; +/* + border:1px; +*/ + margin-bottom: 1px; +} + + +.person { + font-size: 10pt; + font-style: italic; +} + +.resource { + font-size: 10pt; +} + +.link { + text-decoration: underline; +} + +@page { + size: landscape; +} + +@media print { + .datechooser { + display: none; + } +} diff --git a/Rapla/src/org/rapla/plugin/compactweekview/CompactWeekViewFactory.java b/Rapla/src/org/rapla/plugin/compactweekview/CompactWeekViewFactory.java new file mode 100644 index 0000000..cec6f1a --- /dev/null +++ b/Rapla/src/org/rapla/plugin/compactweekview/CompactWeekViewFactory.java @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.compactweekview; + +import javax.swing.Icon; + +import org.rapla.facade.RaplaComponent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.SwingCalendarView; +import org.rapla.gui.ViewFactory; +import org.rapla.gui.images.Images; +import org.rapla.servletpages.RaplaPageGenerator; + +public class CompactWeekViewFactory extends RaplaComponent implements ViewFactory +{ + public final static String COMPACT_WEEK_VIEW = "week_compact"; + + public CompactWeekViewFactory( RaplaContext context ) throws RaplaException + { + super( context ); + } + + public SwingCalendarView createSwingView(RaplaContext context, CalendarModel model, boolean editable) throws RaplaException + { + return new SwingCompactWeekCalendar( context, model, editable); + } + + public RaplaPageGenerator createHTMLView(RaplaContext context, CalendarModel model) throws RaplaException + { + return new HTMLCompactWeekViewPage( context, model); + } + + public String getViewId() + { + return COMPACT_WEEK_VIEW; + } + + public String getName() + { + return getString(COMPACT_WEEK_VIEW); + } + + Icon icon; + public Icon getIcon() + { + if ( icon == null) { + icon = Images.getIcon("/org/rapla/plugin/compactweekview/images/week_compact.png"); + } + return icon; + } + + public String getMenuSortKey() { + return "B1"; + } + + +} + diff --git a/Rapla/src/org/rapla/plugin/compactweekview/CompactWeekViewPlugin.java b/Rapla/src/org/rapla/plugin/compactweekview/CompactWeekViewPlugin.java new file mode 100644 index 0000000..faee742 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/compactweekview/CompactWeekViewPlugin.java @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.compactweekview; + +import org.apache.avalon.framework.configuration.Configuration; +import org.rapla.framework.Container; +import org.rapla.framework.PluginDescriptor; +import org.rapla.plugin.RaplaExtensionPoints; +import org.rapla.plugin.RaplaPluginMetaInfo; + +public class CompactWeekViewPlugin implements PluginDescriptor +{ + static boolean ENABLE_BY_DEFAULT = true; + + public String toString() { + return "Compact Week View"; + } + + public void provideServices(Container container, Configuration config) { + if ( !config.getAttributeAsBoolean("enabled", ENABLE_BY_DEFAULT)) + return; + + container.addContainerProvidedComponent + ( + RaplaExtensionPoints.CALENDAR_VIEW_EXTENSION + ,CompactWeekViewFactory.class.getName() + ,CompactWeekViewFactory.COMPACT_WEEK_VIEW + ,null + ); + } + + + public Object getPluginMetaInfos( String key ) + { + if ( RaplaPluginMetaInfo.METAINFO_PLUGIN_ENABLED_BY_DEFAULT.equals( key )) { + return new Boolean( ENABLE_BY_DEFAULT ); + } + return null; + } + +} + diff --git a/Rapla/src/org/rapla/plugin/compactweekview/HTMLCompactWeekViewPage.java b/Rapla/src/org/rapla/plugin/compactweekview/HTMLCompactWeekViewPage.java new file mode 100644 index 0000000..cdab553 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/compactweekview/HTMLCompactWeekViewPage.java @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.compactweekview; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import org.rapla.components.calendarview.html.AbstractHTMLView; +import org.rapla.components.calendarview.html.HTMLCompactWeekView; +import org.rapla.components.util.xml.XMLWriter; +import org.rapla.entities.domain.Allocatable; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.CalendarOptions; +import org.rapla.plugin.abstractcalendar.AbstractHTMLCalendarPage; +import org.rapla.plugin.abstractcalendar.GroupAllocatablesStrategy; +import org.rapla.plugin.abstractcalendar.RaplaBuilder; + +public class HTMLCompactWeekViewPage extends AbstractHTMLCalendarPage +{ + + public HTMLCompactWeekViewPage( RaplaContext context, CalendarModel calendarModel) throws RaplaException + { + super( context, calendarModel); + } + + protected AbstractHTMLView createCalendarView() { + HTMLCompactWeekView weekView = new HTMLCompactWeekView(); + CalendarOptions opt = getCalendarOptions(); + Set excludeDays = opt.getExcludeDays(); + weekView.setExcludeDays( excludeDays ); + return weekView; + + } + + protected RaplaBuilder createBuilder() throws RaplaException { + RaplaBuilder builder = super.createBuilder(); + + builder.setSmallBlocks( true ); + builder.setSplitByAllocatables( true ); + + GroupAllocatablesStrategy strategy; + if ( builder.getAllocatables().size() > 0) { + strategy = new GroupAllocatablesStrategy( getRaplaLocale().getLocale() ); + strategy.setAllocationGroups( builder.getAllocatables() ) ; + } else { + // put all Allocatables in the same group + strategy = new GroupAllocatablesStrategy( getRaplaLocale().getLocale() ) { + protected Collection group(List blockList) { + ArrayList list = new ArrayList(); + list.add( blockList ); + return list; + } + }; + } + strategy.setFixedSlotsEnabled( true ); + builder.setBuildStrategy( strategy ); + + List allocatables = builder.getAllocatables(); + String[] slotNames = new String[ allocatables.size() ]; + for (int i = 0; i < slotNames.length; i++ ) { + Allocatable allocatable = (Allocatable) allocatables.get( i ); + String slotName = allocatable.getName( getRaplaLocale().getLocale() ); + slotNames[i] = XMLWriter.encode( slotName ); + } + ((HTMLCompactWeekView)view).setSlots( slotNames ); + return builder; + } + + protected int getIncrementSize() { + return Calendar.WEEK_OF_YEAR; + } + +} + diff --git a/Rapla/src/org/rapla/plugin/compactweekview/SwingCompactWeekCalendar.java b/Rapla/src/org/rapla/plugin/compactweekview/SwingCompactWeekCalendar.java new file mode 100644 index 0000000..e7b3b4b --- /dev/null +++ b/Rapla/src/org/rapla/plugin/compactweekview/SwingCompactWeekCalendar.java @@ -0,0 +1,192 @@ + +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.plugin.compactweekview; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import java.awt.Point; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Set; + +import javax.swing.JComponent; +import javax.swing.JLabel; + +import org.rapla.components.calendar.DateRenderer; +import org.rapla.components.calendar.DateRendererAdapter; +import org.rapla.components.calendarview.swing.AbstractSwingCalendar; +import org.rapla.components.calendarview.swing.SwingCompactWeekView; +import org.rapla.components.calendarview.swing.ViewListener; +import org.rapla.components.util.DateTools; +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.entities.NamedComparator; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.CalendarOptions; +import org.rapla.plugin.abstractcalendar.AbstractRaplaSwingCalendar; +import org.rapla.plugin.abstractcalendar.GroupAllocatablesStrategy; +import org.rapla.plugin.abstractcalendar.RaplaBuilder; +import org.rapla.plugin.abstractcalendar.RaplaCalendarViewListener; +import org.rapla.plugin.abstractcalendar.SwingRaplaBuilder; + + +public class SwingCompactWeekCalendar extends AbstractRaplaSwingCalendar +{ + /** + * Default slot size for each column of calendar view + * TODO: Make configurable + */ + private static final int DEFAULTSLOTSIZE = 120; + /** + * Default slot size for resource column preceding day columns + */ + private static final int DEFAULTRESOURCESLOTSIZE = 70; + + public SwingCompactWeekCalendar(RaplaContext sm,CalendarModel settings, boolean editable) throws RaplaException { + super( sm, settings, editable); + } + protected AbstractSwingCalendar createView(boolean showScrollPane) { + final DateRendererAdapter dateRenderer = new DateRendererAdapter((DateRenderer) getService(DateRenderer.class.getName()), getRaplaLocale().getTimeZone(), getRaplaLocale().getLocale()); + return new SwingCompactWeekView( showScrollPane ) { + private static final long serialVersionUID = 1L; + + protected JComponent createSlotHeader(Date date) { + JLabel component = (JLabel) super.createSlotHeader(date); + if ( date != null ) { + boolean today = DateTools.isSameDay(getQuery().today().getTime(), date.getTime()); + if ( today) + { + component.setFont(component.getFont().deriveFont( Font.BOLD)); + } + if (isEditable() && dateRenderer != null ) { + component.setOpaque(true); + Color color = dateRenderer.getBackgroundColor(date); + String toolTip = dateRenderer.getToolTipText(date); + component.setBackground(color); + component.setToolTipText(toolTip); + } + } + else + { + String text = getI18n().getString("resources"); + component.setText(text); + component.setOpaque(true); + component.setToolTipText(text); + } + return component; + } + }; + + } + + protected ViewListener createListener() throws RaplaException { + return new RaplaCalendarViewListener(getContext(), model, view) { + /** override to change the allocatable to the row that is selected */ + protected void showPopupMenu(Component component,Point p,Date start,Date end, int slotNr) + { + Allocatable[] selectedAllocatables; + try { + selectedAllocatables = getModel().getSelectedAllocatables(); + } catch (RaplaException ex) { + showException(ex, view); + return; + } + Allocatable selectedAllocatable = null; + if ( selectedAllocatables.length == 1 ) { + selectedAllocatable = selectedAllocatables[0]; + } + + if ( end.getTime()- start.getTime() == DateTools.MILLISECONDS_PER_DAY ) { + Calendar cal = getRaplaLocale().createCalendar(); + cal.setTime ( start ); + cal.set( Calendar.HOUR_OF_DAY, getCalendarOptions().getWorktimeStart()); + start = cal.getTime(); + end = new Date ( start.getTime() + 30 * DateTools.MILLISECONDS_PER_MINUTE ); + } + if ( selectedAllocatables.length > 0 ) { + int row = slotNr / DateTools.DAYS_PER_WEEK; + List sortedAllocatables = new ArrayList( Arrays.asList( selectedAllocatables)); + Collections.sort(sortedAllocatables, new NamedComparator( getLocale() )); + selectedAllocatable = (Allocatable) sortedAllocatables.get(row); + } + + showPopupMenu( component, p, start,end, slotNr, selectedAllocatables, selectedAllocatable); + } + + /* if the selcted view is a month-view or compact-view, the start-time will not be the selected time, + * but the time of the start-time of the appointment instead. The start-date is taken from the passed date. + * */ + protected Date calcStartDate(Date date, Appointment appointment) { + return getRaplaLocale().toDate( date, appointment.getStart() ); + } + + }; + } + + protected RaplaBuilder createBuilder() throws RaplaException { + RaplaBuilder builder = new SwingRaplaBuilder(getContext()); + builder.setRepeatingVisible( view.isEditable()); + builder.setEditingUser( getUser() ); + builder.setExceptionsExcluded( !getCalendarOptions().isExceptionsVisible() || !view.isEditable()); + builder.setFromModel( model, view.getStartDate(), view.getEndDate() ); + + builder.setSmallBlocks( true ); + builder.setSplitByAllocatables( true ); + + GroupAllocatablesStrategy strategy = new GroupAllocatablesStrategy( getRaplaLocale().getLocale() ); + strategy.setFixedSlotsEnabled( true); + strategy.setResolveConflictsEnabled( false ); + strategy.setAllocationGroups( builder.getAllocatables() ) ; + builder.setBuildStrategy( strategy ); + Allocatable[] allocatables = (Allocatable[])builder.getAllocatables().toArray( Allocatable.ALLOCATABLE_ARRAY); + String[] slotNames = new String[ allocatables.length ]; + for (int i = 0; i 0); + } + + if (zone != null) { + final String timezoneId = zone.getID(); + + try { + TimeZoneRegistry registry = TimeZoneRegistryFactory.getInstance().createRegistry(); + timezone = registry.getTimeZone(timezoneId); + } catch (Exception rc) { + final VTimeZone vTimeZone = new VTimeZone(); + timezone = new net.fortuna.ical4j.model.TimeZone(vTimeZone); + final int rawOffset = zone.getRawOffset(); + timezone.setRawOffset(rawOffset); + } + } + } + + private void initConfiguration() { + try { + ClientFacade facade = getClientFacade(); + Preferences preferences = (Preferences) facade.edit(facade.getPreferences(null)); + RaplaConfiguration raplaConfiguration = (RaplaConfiguration) preferences.getEntry("org.rapla.plugin"); + config = raplaConfiguration.find("class", Export2iCalPlugin.PLUGIN_CLASS); + } catch (RaplaException e) { + getLogger().error("Cannot read plugin configuration"); + } + } + + public void setiCalendarWorktime(CalendarOptions options, RaplaLocale locale) throws RaplaException { + calendar = locale.createCalendar(); + worktimeStartHour = options.getWorktimeStart(); + worktimeEndHour = options.getWorktimeEnd(); + } + + public Calendar createiCalender(Reservation[] reservations) { + + Calendar calendar = initiCalendar(); + addICalMethod(calendar, Method.PUBLISH); + addVTimeZone(calendar); + + if (reservations.length == 0) { + return null; + } + + for (int resIt = 0, resLen = reservations.length; resIt < resLen; resIt++) { + Appointment[] appointments = reservations[resIt].getAppointments(); + + for (int appIt = 0, appLen = appointments.length; appIt < appLen; appIt++) { + calendar.getComponents().add(createVEvent(appointments[appIt])); + } + } + + return calendar; + } + + private void addVTimeZone(Calendar calendar) { + + if (timezone != null) { + VTimeZone tz = timezone.getVTimeZone(); + calendar.getComponents().add(tz); + } + } + + /** + * Initialisiert ein neues leeres iCalendar-Objekt. + * + * @return Ein neues leeres iCalendar-Objekt. + */ + public Calendar initiCalendar() { + Calendar calendar = new Calendar(); + calendar.getProperties().add(new ProdId("-//Rapla//iCal Plugin//EN")); + calendar.getProperties().add(Version.VERSION_2_0); + return calendar; + + } + + public void addICalMethod(Calendar iCalendar, Method method) { + iCalendar.getProperties().add(method); + } + + public void addVEvent(Calendar iCalendar, Appointment appointment) { + iCalendar.getComponents().add(createVEvent(appointment)); + } + + /** + * Erstellt anhand des übergebenen Appointment-Objekts einen + * iCalendar-Event. + * + * @param appointment Ein Rapla Appointment. + * @return Ein iCalendar-Event mit den Daten des Appointments. + */ + private VEvent createVEvent(Appointment appointment) { + + PropertyList properties = new PropertyList(); + + + boolean isAllDayEvent = appointment.isWholeDaysSet() || isAllDayEvent(appointment.getStart(), appointment.getEnd()); + addDateStampToEvent(appointment, properties); + addCreateDateToEvent(appointment, properties); + addStartDateToEvent(appointment, properties, isAllDayEvent); + addLastModifiedDateToEvent(appointment, properties); + addEndDateToEvent(appointment, properties, isAllDayEvent); + addEventNameToEvent(appointment, properties); + addUidToEvent(appointment, properties); + addLocationToEvent(appointment, properties); + addCategories(appointment, properties); + addOrganizer(appointment, properties); + addAttendees(appointment, properties); + addRepeatings(appointment, properties); + + + VEvent event = new VEvent(properties); + + return event; + } + + /** + * add organizer to properties + * + * @param appointment + * @param properties + */ + private void addOrganizer(Appointment appointment, PropertyList properties) { + // means we do not export attendees so we do not have a meeting + if (!doExportAsMeeting) + return; + final User owner = appointment.getReservation().getOwner(); + try { + Organizer organizer = null; + if (owner.getEmail() != null && owner.getEmail().trim().length() > 0) { + try { + final URI uri = new URI("MAILTO:" + owner.getEmail().trim()); + organizer = new Organizer(uri); + } catch (URISyntaxException e) { + } + } + if (organizer == null) { + organizer = new Organizer("MAILTO:" + URLEncoder.encode(owner.getUsername(), "UTF-8")); + } + if (!"".equals(owner.getName())) + organizer.getParameters().add(new Cn(owner.getName())); + properties.add(organizer); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e); + } catch (UnsupportedEncodingException e) { + throw new IllegalArgumentException(e); + } + } + + + /** + * add attenddees if system property ical4j.validation.relaxed is set ony + * + * @param appointment + * @param properties + */ + private void addAttendees(Appointment appointment, PropertyList properties) { + if (!doExportAsMeeting) + return; + + Allocatable[] persons = appointment.getReservation().getPersons(); + + for (Allocatable person : persons) { + String email = null; + Attribute attr = person.getClassification().getAttribute(exportAttendeesAttribute); + if (attr != null && person.getClassification().getValue(attr) != null) + email = person.getClassification().getValue(attr).toString().trim(); + // determine if person has email attribute + if (email != null && email.length() > 0) { + try { + Attendee attendee = new Attendee(new URI(email)); + attendee.getParameters().add(Role.REQ_PARTICIPANT); + attendee.getParameters().add(new Cn(person.getName(Locale.getDefault()))); + attendee.getParameters().add(new PartStat(exportAttendeesParticipationStatus)); + properties.add(attendee); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e); + } + } + } + } + + private boolean isAllDayEvent(Date start, Date end) { + + calendar.setTime(start); + int startHour = calendar.get(java.util.Calendar.HOUR_OF_DAY); + + calendar.setTime(end); + int endHour = calendar.get(java.util.Calendar.HOUR_OF_DAY); + + boolean startsOnWorkTimeStart = startHour == worktimeStartHour; + boolean endsOnWorkTimeEnd = endHour == worktimeEndHour; + + return startsOnWorkTimeStart && endsOnWorkTimeEnd; + } + + /** + * F�gt dem Termin das Modifizierungsdatum hinzu + * + * @param appointment + * @param event + */ + private void addDateStampToEvent(Appointment appointment, PropertyList properties) { + Date lastChange = appointment.getReservation().getLastChangeTime(); + properties.add(new LastModified(convertRaplaLocaleToUTC(lastChange))); + } + + /** + * F�gt dem Termin den DTSTAMP hinzu lt. rfc muss dies dem Erstellungdatum + * des Objektes entsprechen. Da dies jedoch den Import erschwert und sinnlos + * ist, wird es auf das letzte Modifizierungsdatum geschrieben. + * + * @param appointment + * @param event + */ + private void addLastModifiedDateToEvent(Appointment appointment, PropertyList properties) { + Date lastChange = appointment.getReservation().getLastChangeTime(); + properties.add(new DtStamp(convertRaplaLocaleToUTC(lastChange))); + } + + /** + * Fügt die Wiederholungen des übergebenen Appointment-Objekts dem + * übergebenen Event-Objekt hinzu. + * + * @param appointment Ein Rapla Appointment. + * @param event Ein iCalendar Event. + */ + private void addRepeatings(Appointment appointment, PropertyList properties) { + Repeating repeating = appointment.getRepeating(); + + if (repeating == null) { + return; + } + + // This returns the strings DAYLY, WEEKLY, MONTHLY, YEARLY + String type = repeating.getType().toString().toUpperCase(); + + Recur recur; + + // here is evaluated, if a COUNT is set in Rapla, or an enddate is + // specified + if (repeating.getNumber() == -1) { + recur = new Recur(type, -1); + } else if (repeating.isFixedNumber()) { + recur = new Recur(type, repeating.getNumber()); + } else { + net.fortuna.ical4j.model.Date endDate = new net.fortuna.ical4j.model.Date(repeating.getEnd()); + // TODO do we need to translate the enddate in utc? + recur = new Recur(type, endDate); + } + + if (repeating.isDaily()) { + // DAYLY -> settings : intervall + recur.setInterval(repeating.getInterval()); + + } else if (repeating.isWeekly()) { + // WEEKLY -> settings : every nTh Weekday + recur.setInterval(repeating.getInterval()); + + calendar.setTime(appointment.getStart()); + recur.getDayList().add(WeekDay.getWeekDay(calendar)); + } else if (repeating.isMonthly()) { + // MONTHLY -> settings : every nTh Weekday + recur.setInterval(repeating.getInterval()); + calendar.setTime(appointment.getStart()); + int weekofmonth = Math.round(calendar.get(java.util.Calendar.DAY_OF_MONTH) / DateTools.DAYS_PER_WEEK) + 1; + recur.getDayList().add(new WeekDay(WeekDay.getWeekDay(calendar), weekofmonth)); + } else if (repeating.isYearly()) { + // YEARLY -> settings : every nTh day mTh Monthname + calendar.setTime(appointment.getStart()); + calendar.get(java.util.Calendar.DAY_OF_YEAR); + } else { + System.out.println("Invalid data in recurrency rule!"); + } + + properties.add(new RRule(recur)); + + // bugfix - if rapla has no exceptions, an empty EXDATE: element is + // produced. This may bother some iCal tools + if (repeating.getExceptions().length == 0) { + return; + } + + // Add exception dates + //DateList dl = new DateList(Value.DATE); + + // rku: use seperate EXDATE for each exception + for (Iterator itExceptions = Arrays.asList(repeating.getExceptions()).iterator(); itExceptions.hasNext(); ) { + DateList dl = new DateList(Value.DATE); + dl.add(new net.fortuna.ical4j.model.Date((Date) itExceptions.next())); + properties.add(new ExDate(dl)); + } + //properties.add(new ExDate(dl)); + } + + public String getAttendeeString(Appointment appointment) { + + String attendeeString = ""; + + Reservation raplaReservation = appointment.getReservation(); + Allocatable[] raplaPersons = raplaReservation.getPersons(); + + for (int i = 0; i < raplaPersons.length; i++) { + if (!isReserved(raplaPersons[i], appointment)) + continue; + + attendeeString += raplaPersons[i].getName(Locale.getDefault()); + attendeeString = attendeeString.trim(); + + if (i != raplaPersons.length - 1) { + attendeeString += ", "; + } else { + attendeeString = " [" + attendeeString + "]"; + } + } + + return attendeeString; + } + + /** + * Fügt die Kategorien hinzu. + * + * @param appointment Ein Rapla Appointment. + * @param event Ein iCalendar Event. + */ + private void addCategories(Appointment appointment, PropertyList properties) { + Classification cls = appointment.getReservation().getClassification(); + Categories cat = new Categories(); + cat.getCategories().add(cls.getType().getName(Locale.getDefault())); + properties.add(cat); + } + + /** + * Prüfe, ob eine Ressource für ein bestimmten Appointment + * reserviert ist. + * + * @param alloc + * @param when + * @return true, wenn die Ressource reserviert ist. + * false sonst + */ + private boolean isReserved(Allocatable alloc, Appointment when) { + Reservation reservation = when.getReservation(); + Appointment[] restrictions = reservation.getRestriction(alloc); + + for (int restIt = 0, restLen = restrictions.length; restIt < restLen; restIt++) { + if (when.equals(restrictions[restIt])) + return true; + } + + return (restrictions.length == 0); + } + + /** + * Fügt einem iCal-Event den Ort aus dem übergebenen + * Appointment-Objekt hinzu. + * + * @param appointment + * @param event + */ + private void addLocationToEvent(Appointment appointment, PropertyList properties) { + Allocatable[] allocatables = appointment.getReservation().getResources(); + + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < allocatables.length; i++) { + if (!isReserved(allocatables[i], appointment)) + continue; + + if (buffer.length() > 0) { + buffer.append(", "); + } + buffer.append(allocatables[i].getName(Locale.getDefault())); + } + + properties.add(new Location(buffer.toString())); + } + + /** + * Fügt einem iCal-Event die Termin-ID aus dem übergebenen + * Appointment-Objekt hinzu. + * + * @param appointment + * @param event + */ + private void addUidToEvent(Appointment appointment, PropertyList properties) { + String uid = getId(appointment.getReservation()) + getId(appointment); + properties.add(new Uid(uid)); + } + + /** + * Erzeugt eine eindeutige ID für den übergebenen Appointment. + * + * @param entity + * @return + */ + private String getId(RaplaObject entity) { + SimpleIdentifier id = (SimpleIdentifier) ((RefEntity) entity).getId(); + return entity.getRaplaType().getLocalName() + "_" + id.getKey(); + } + + /** + * Fügt einem iCal-Event den Termin-Namen aus dem übergebenen + * Appointment-Objekt hinzu. + * + * @param appointment + * @param event + */ + private void addEventNameToEvent(Appointment appointment, PropertyList properties) { + + String eventDescription = appointment.getReservation().getName(Locale.getDefault()); + if (attendeeToTitle) { + eventDescription += getAttendeeString(appointment); + } + properties.add(new Summary(eventDescription)); + } + + /** + * Fügt einem iCal-Event das Enddatum aus dem übergebenen + * Appointment-Objekt hinzu. + * + * @param appointment + * @param event + */ + private void addEndDateToEvent(Appointment appointment, PropertyList properties, boolean isAllDayEvent) { + + Date endDate = appointment.getEnd(); + java.util.Calendar calendar = java.util.Calendar.getInstance(); + + if (isAllDayEvent) { + DtEnd end = getDtEndFromAllDayEvent(endDate, calendar); + properties.add(end); + } else { + if (appointment.getRepeating() == null) { + DtEnd end = new DtEnd(convertRaplaLocaleToUTC(endDate)); + end.setUtc(true); + properties.add(end); + } else { + DtEnd end = getEndDateProperty(appointment, endDate); + properties.add(end); + } + } + } + + private DtEnd getEndDateProperty(Appointment appointment, Date endDate) { + + DateTime date = convertRaplaLocaleToUTC(endDate); + TimeZoneRegistry registry = TimeZoneRegistryFactory.getInstance().createRegistry(); + net.fortuna.ical4j.model.TimeZone tz = registry.getTimeZone(timezone.getID()); + date.setTimeZone(tz); + return new DtEnd(date); + } + + private DtEnd getDtEndFromAllDayEvent(Date endDate, java.util.Calendar calendar) { + + calendar.clear(); + calendar.setTime(endDate); + int year = calendar.get(java.util.Calendar.YEAR); + calendar.add(java.util.Calendar.DATE, 1); + int month = calendar.get(java.util.Calendar.MONTH); + int date = calendar.get(java.util.Calendar.DAY_OF_MONTH); + calendar.clear(); + calendar.set(year, month, date); + DtEnd end = new DtEnd(new net.fortuna.ical4j.model.Date(calendar.getTime())); + return end; + } + + /** + * Fügt einem iCal-Event das Startdatum aus dem übergebenen + * Appointment-Objekt hinzu. + * + * @param appointment + * @param event + */ + private void addStartDateToEvent(Appointment appointment, PropertyList properties, boolean isAllDayEvent) { + + Date startDate = appointment.getStart(); + + if (isAllDayEvent) { + DtStart start = getDtStartFromAllDayEvent(startDate, calendar); + properties.add(start); + } else { + if (appointment.getRepeating() == null) { + DtStart start = new DtStart(convertRaplaLocaleToUTC(startDate)); + start.setUtc(true); + properties.add(start); + } else { + DtStart start = getStartDateProperty(appointment, startDate); + properties.add(start); + } + } + } + + private DtStart getStartDateProperty(Appointment appointment, Date startDate) { + + DateTime date = convertRaplaLocaleToUTC(startDate); + TimeZoneRegistry registry = TimeZoneRegistryFactory.getInstance().createRegistry(); + net.fortuna.ical4j.model.TimeZone tz = registry.getTimeZone(timezone.getID()); + date.setTimeZone(tz); + return new DtStart(date); + } + + private DtStart getDtStartFromAllDayEvent(Date startDate, java.util.Calendar calendar) { + + calendar.clear(); + calendar.setTime(startDate); + int year = calendar.get(java.util.Calendar.YEAR); + int month = calendar.get(java.util.Calendar.MONTH); + int date = calendar.get(java.util.Calendar.DAY_OF_MONTH); + calendar.clear(); + calendar.set(year, month, date); + DtStart start = new DtStart(new net.fortuna.ical4j.model.Date(calendar.getTime())); + return start; + } + + /** + * Fügt einem iCal-Event das Erstelldatum aus dem übergebenen + * Appointment-Objekt hinzu. + * + * @param appointment + * @param event + */ + private void addCreateDateToEvent(Appointment appointment, PropertyList properties) { + + Date createTime = appointment.getReservation().getCreateTime(); + properties.add(new Created(convertRaplaLocaleToUTC(createTime))); + } + + private DateTime convertRaplaLocaleToUTC(Date date) { + return convertRaplaLocaleToUTC(date, timezone); + } + + public static DateTime convertUtcToRaplaLocale(Date date, TimeZone timezone) { + java.util.Calendar cal = java.util.Calendar.getInstance(); + cal.setTime(date); + cal.setTimeZone(timezone); + int offset = cal.getTimeZone().getOffset(date.getTime()); + cal.add(java.util.Calendar.MILLISECOND, +offset); + return new DateTime(cal.getTime()); + } + + /** + * Diese Methode wandelt das Rapla Datum in UTC um + * + * @param date Das zu konvertierende lokale Datum. + * @return Das nach UTC konvertierte Datum. + */ + public static DateTime convertRaplaLocaleToUTC(Date date, TimeZone timezone) { + + java.util.Calendar cal = java.util.Calendar.getInstance(); + cal.setTime(date); + cal.setTimeZone(timezone); + int offset = cal.getTimeZone().getOffset(date.getTime()); + cal.add(java.util.Calendar.MILLISECOND, -offset); + return new DateTime(cal.getTime()); + } + +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/plugin/export2ical/Export2iCalDialogInitializer.java b/Rapla/src/org/rapla/plugin/export2ical/Export2iCalDialogInitializer.java new file mode 100644 index 0000000..5b6a85e --- /dev/null +++ b/Rapla/src/org/rapla/plugin/export2ical/Export2iCalDialogInitializer.java @@ -0,0 +1,139 @@ +package org.rapla.plugin.export2ical; + +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.IOException; + +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; + +import org.rapla.components.iolayer.IOInterface; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.storage.RefEntity; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.framework.StartupEnvironment; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.MenuExtensionPoint; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.plugin.RaplaExtensionPoints; +import org.rapla.server.RemoteServiceCaller; + +public class Export2iCalDialogInitializer extends RaplaGUIComponent { + + + + public Export2iCalDialogInitializer(RaplaContext sm) throws RaplaException { + super(sm); + + + + if (!isModifyPreferencesAllowed()) { + return; + } + + setChildBundleName(Export2iCalPlugin.RESOURCE_FILE); + + MenuExtensionPoint exportExtension = (MenuExtensionPoint) getService(RaplaExtensionPoints.EXPORT_MENU_EXTENSION_POINT); + + final StartupEnvironment startupEnvironment = (StartupEnvironment)getContext().lookup(StartupEnvironment.ROLE); + final int startupMode = startupEnvironment.getStartupMode(); + boolean hasserver = getUpdateModule().isClientForServer(); + if ( hasserver && startupMode != StartupEnvironment.APPLET) + { + exportExtension.insert(exportiCalToFile()); + } + getLogger().info("Export2iCal plugin added"); + } + + private JMenuItem exportiCalToFile() { + + JMenuItem item = new JMenuItem(getString("export_file_text")); + item.setIcon(getIcon("icon.export")); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + getCalendarOptions(); + try { + CalendarModel raplaCal = ((CalendarModel) getContext().lookup(CalendarModel.ROLE)); + RemoteServiceCaller remote =(RemoteServiceCaller) getContext().lookup( RemoteServiceCaller.ROLE); + StringBuffer buf = new StringBuffer(); + for (Reservation reservation:raplaCal.getReservations()) + { + boolean first = buf.length() == 0; + if ( !first) + buf.append(","); + final String idString = ((RefEntity) reservation).getId().toString().split("_")[1]; + buf.append( idString ); + } + String ids = buf.toString(); + String result = remote.call( ICalExportRemoteServiceFactory.class.getName(),ICalExportRemoteServiceFactory.EXPORT, new String[] {ids}); + if ( result.trim().length() == 0) + { + JOptionPane.showMessageDialog(null, getString("no_dates_text"), "Export2iCal", JOptionPane.INFORMATION_MESSAGE); + } + else + { + String nonEmptyTitle = raplaCal.getNonEmptyTitle(); + if ( nonEmptyTitle.length() == 0) + { + nonEmptyTitle = "rapla_calendar"; + } + String name =nonEmptyTitle +".ics"; + export(result, name); + } + } catch (Exception ex) { + showException(ex, getMainComponent()); + } + } + }); + return item; + } + + private void export(String result, String name) throws RaplaException { + final byte[] bytes = result.getBytes(); + saveFile(bytes, name , new String[] {"ics","ical"}); +// JFileChooser fileChooser = new JFileChooser(); +// fileChooser.setFileFilter(new iCalFileFilter()); +// +// if (fileChooser.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) { +// String path = fileChooser.getSelectedFile().getPath(); +// File file = null; +// if (path.substring(path.length() - 4, path.length()).equals(".ics")) { +// file = new File(path); +// } else { +// file = new File(path + ".ics"); +// } +// try { +// FileWriter fileWriter = new FileWriter(file); +// BufferedWriter writer = new BufferedWriter( fileWriter); +// writer.write( result); +// //calOutputter.output(ical, fileWriter); +// JOptionPane.showMessageDialog(null, getString("export_file_succesfull_text"), "Export2iCal", JOptionPane.INFORMATION_MESSAGE); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } + } + +// private class iCalFileFilter extends FileFilter { +// public boolean accept(File file) { +// return (file.isDirectory() || file.getName().toLowerCase().endsWith(".ics")); +// } +// +// public String getDescription() { +// return "iCal Files"; +// } +// } + + public void saveFile(byte[] content, String filename, String[] extensions) throws RaplaException { + final Frame frame = (Frame) SwingUtilities.getRoot(getMainComponent()); + IOInterface io = (IOInterface) getService(IOInterface.ROLE); + try { + io.saveFile(frame, null, extensions, filename, content); + } catch (IOException e) { + throw new RaplaException("Can't export file!", e); + } + } +} diff --git a/Rapla/src/org/rapla/plugin/export2ical/Export2iCalPlugin.java b/Rapla/src/org/rapla/plugin/export2ical/Export2iCalPlugin.java new file mode 100644 index 0000000..5889943 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/export2ical/Export2iCalPlugin.java @@ -0,0 +1,87 @@ +package org.rapla.plugin.export2ical; + +import org.apache.avalon.framework.configuration.Configuration; +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.components.xmlbundle.impl.I18nBundleImpl; +import org.rapla.framework.Container; +import org.rapla.framework.PluginDescriptor; +import org.rapla.plugin.RaplaExtensionPoints; +import org.rapla.plugin.RaplaPluginMetaInfo; +import org.rapla.server.RaplaRemoteServiceFactory; +import org.rapla.server.ServerService; + + +public class Export2iCalPlugin implements PluginDescriptor { + + public static final String RESOURCE_FILE = Export2iCalPlugin.class.getPackage().getName() + ".Export2iCalResources"; + + public static final String PLUGIN_CLASS = Export2iCalPlugin.class.getName(); + public static final String PLUGIN_ENTRY = "org.rapla.plugin.export2ical"; + public static final String ICAL_EXPORT = PLUGIN_ENTRY+".selected"; + public static final String DAYS_BEFORE = "days_before"; + public static final String DAYS_AFTER = "days_after"; + + public static String PREF_BEFORE_DAYS = "export2iCal_before_days"; + public static String PREF_AFTER_DAYS = "export2iCal_after_days"; + + public static final String GLOBAL_INTERVAL = "global_interval"; + public static final String EXPORT_ATTENDEES = "export_attendees"; + public static final String EXPORT_ATTENDEES_EMAIL_ATTRIBUTE = "export_attendees_email_attribute"; + public static final String TIMEZONE = "timezone"; + public static final String FIRST_PLUGIN_STARTDATE = "first_plugin_startdate"; + public static final String LAST_MODIFIED_INTERVALL = "last_modified_intervall"; + + public static final String DEFAULT_timezone = "Etc/UTC"; + public static final int DEFAULT_daysBefore = 30; + public static final int DEFAULT_daysAfter = 30; + public static final boolean DEFAULT_basedOnAutoExport = true; + public static final boolean DEFAULT_globalIntervall = true; + public static final int DEFAULT_lastModifiedIntervall = 5; + public static String DEFAULT_attendee_resource_attribute = "email"; + public static String DEFAULT_attendee_participation_status; + + + static boolean ENABLE_BY_DEFAULT = true; + public static boolean DEFAULT_exportAttendees = false; + public static final String EXPORT_ATTENDEES_PARTICIPATION_STATUS = "export_attendees_participation_status"; + + public String toString() { + return "Export2iCal Plugin"; + } + + public void provideServices(Container container, Configuration config) { + + if (!config.getAttributeAsBoolean("enabled", ENABLE_BY_DEFAULT)) + return; + + try { + // Class.forName("net.fortuna.ical4j.model.parameter.PartStat"); + DEFAULT_attendee_participation_status = "TENTATIVE"; + } catch (Throwable ex ) { + } + + + container.addContainerProvidedComponentInstance(PLUGIN_CLASS, Boolean.TRUE); + container.addContainerProvidedComponent(I18nBundle.ROLE, I18nBundleImpl.class.getName(), RESOURCE_FILE, I18nBundleImpl.createConfig(RESOURCE_FILE)); + + if ( container.getContext().has( ServerService.ROLE) ){ + container.addContainerProvidedComponent(RaplaExtensionPoints.SERVLET_PAGE_EXTENSION, Export2iCalServlet.class.getName(), "iCal", config); + container.addContainerProvidedComponent( RaplaRemoteServiceFactory.ROLE, ICalExportRemoteServiceFactory.class.getName(), ICalExportRemoteServiceFactory.class.getName(),config); + container.addContainerProvidedComponent(RaplaExtensionPoints.SERVER_EXTENSION, Export2iCalChangeWatcher.class.getName(), PLUGIN_CLASS, config); + } else { + container.addContainerProvidedComponent(RaplaExtensionPoints.CLIENT_EXTENSION, Export2iCalDialogInitializer.class.getName(), PLUGIN_CLASS, config); + container.addContainerProvidedComponent(RaplaExtensionPoints.USER_OPTION_PANEL_EXTENSION, Export2iCalUserOption.class.getName(), PLUGIN_CLASS, config); + container.addContainerProvidedComponent(RaplaExtensionPoints.PLUGIN_OPTION_PANEL_EXTENSION, Export2iCalAdminOption.class.getName(), PLUGIN_CLASS, config); + } + + } + + + + public Object getPluginMetaInfos(String key) { + if ( RaplaPluginMetaInfo.METAINFO_PLUGIN_ENABLED_BY_DEFAULT.equals( key )) { + return new Boolean( ENABLE_BY_DEFAULT ); + } + return null; + } +} diff --git a/Rapla/src/org/rapla/plugin/export2ical/Export2iCalResources.xml b/Rapla/src/org/rapla/plugin/export2ical/Export2iCalResources.xml new file mode 100644 index 0000000..bf91f2d --- /dev/null +++ b/Rapla/src/org/rapla/plugin/export2ical/Export2iCalResources.xml @@ -0,0 +1,90 @@ + + + + + Export view to iCal file + Exportiere Ansicht in iCal File + Exporte la vue en iCal File + + + Publish calendar to iCal + Veröffentliche Kalender nach iCal + Publier le calendrier en iCal + Publicar el calendario en iCal + + + previous days: + vorhergehende Tage: + les jours précédents: + + + subsequent days: + darauffolgende Tage: + les jours suivants: + + + User interval setting: + Benutzerspezifische Intervalleinstellung: + le paramètre d`intervalle utilisateur spécifique: + + + Successfully created. + Erfolgreich angelegt. + Créé avec succès. + + + Save iCal view + Ansicht als iCal speichern + Enregistrer la vue iCal + + + Export + Exportieren + Exporter + + + Cancel + Abbrechen + Annuler + + + iCal view name: + Name der iCal Ansicht: + Nom de la vue iCal: + + + Available iCal views: + Verfügbare iCal Ansichten: + Les vues iCal disponibles: + + + Available views based on html export: + Verfügbare Ansichten basierend auf HTML Export: + Les vues disponibles à la base d`HTML Export: + + + Delete + Löschen + Effacer + + + No dates available in the current view. + In der aktuellen Ansicht sind keine Termine vorhanden. + Pas de dates disponibles dans la vue en cours. + + + No name is given. + Kein Name gesetzt. + Aucun nom n'est donné. + + + The iCal file has been successfully created. + Die iCal Datei wurde erfolgreich erstellt. + Le fichier iCal a été créé avec succès. + + + use user interval setting + benutzerspezifische Intervalleinstellung verwenden + utiliser le paramètre d`intervalle utilisateur spécifique: + + \ No newline at end of file diff --git a/Rapla/src/org/rapla/plugin/export2ical/Export2iCalServlet.java b/Rapla/src/org/rapla/plugin/export2ical/Export2iCalServlet.java new file mode 100644 index 0000000..3d2fc3d --- /dev/null +++ b/Rapla/src/org/rapla/plugin/export2ical/Export2iCalServlet.java @@ -0,0 +1,199 @@ +package org.rapla.plugin.export2ical; + +import java.io.IOException; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.SimpleTimeZone; +import java.util.TimeZone; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import net.fortuna.ical4j.data.CalendarOutputter; +import net.fortuna.ical4j.model.Calendar; +import net.fortuna.ical4j.model.ValidationException; + +import org.apache.avalon.framework.configuration.Configuration; +import org.rapla.entities.User; +import org.rapla.entities.configuration.Preferences; +import org.rapla.entities.domain.Reservation; +import org.rapla.facade.RaplaComponent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.framework.RaplaLocale; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.internal.common.CalendarModelImpl; +import org.rapla.servletpages.RaplaPageGenerator; + +public class Export2iCalServlet extends RaplaComponent implements RaplaPageGenerator { + + private int global_daysBefore; + private int global_daysAfter; + + private String username; + private String filename; + + private boolean global_interval; + private HttpServletResponse response; + private TimeZone timezone; + + private SimpleDateFormat rfc1123DateFormat; + + private SimpleTimeZone gmt = new SimpleTimeZone(0, "GMT"); + + private static java.util.Calendar calendar; + private Preferences preferences; + + public Export2iCalServlet(RaplaContext context, Configuration config) throws RaplaException { + super(context); + + RaplaLocale raplaLocale = getRaplaLocale(); + calendar = raplaLocale.createCalendar(); + + + + global_interval = config.getChild(Export2iCalPlugin.GLOBAL_INTERVAL).getValueAsBoolean(Export2iCalPlugin.DEFAULT_globalIntervall); + + global_daysBefore = config.getChild(Export2iCalPlugin.DAYS_BEFORE).getValueAsInteger(Export2iCalPlugin.DEFAULT_daysBefore); + global_daysAfter = config.getChild(Export2iCalPlugin.DAYS_AFTER).getValueAsInteger(Export2iCalPlugin.DEFAULT_daysAfter); + + timezone = TimeZone.getTimeZone(config.getChild(Export2iCalPlugin.TIMEZONE).getValue(Export2iCalPlugin.DEFAULT_timezone)); + + rfc1123DateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z", Locale.US); + rfc1123DateFormat.setTimeZone(gmt); + } + + public void generatePage(ServletContext servletContext, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + + this.response = response; + + filename = request.getParameter("file"); + username = request.getParameter("user"); + + boolean getAllAppointments = request.getParameter("complete") != null; + // if param COMPLETE is given, retrieve all appointments + + try { + User user = getQuery().getUser(username); + preferences = getQuery().getPreferences(user); + + CalendarModel calModel = getCalendarModel(user, filename); + + if (calModel == null) { + response.getWriter().println("The calendar '" + filename + "' you tried to retrieve is not published or available!"); + return; + } + + response.setHeader("Last-Modified", rfc1123DateFormat.format(Export2iCalChangeWatcher.getLastModified(calModel))); + final Object isSet = calModel.getOption(Export2iCalPlugin.ICAL_EXPORT); + + if((isSet == null || isSet.equals("false"))) + { + response.getWriter().println("The calendar '" + filename + "' you tried to retrieve is not published or available!"); + return; + } + + if (request.getMethod().equals("HEAD")) { + return; + } + + Reservation[] reserv = getAllAppointments ? getAllReservations(calModel) : calModel.getReservations(); + + write(reserv); + } catch (Exception e) { + response.getWriter().println(("An error occured giving you the Calendarview for user " + username + " named " + filename)); + response.getWriter().println(); + e.printStackTrace(response.getWriter()); + } + } + + + /** + * Retrieves CalendarModel by username && filename, sets appropriate before + * and after times (only if global intervall is false) + * + * @param user + * the Rapla-User + * @param filename + * the Filename of the exported view + * @return + */ + private CalendarModel getCalendarModel(User user, String filename) { + try { + CalendarModelImpl calModel; + + calModel = new CalendarModelImpl(getContext(), user); + calModel.load(filename); + + int daysBefore = global_interval ? global_daysBefore : preferences.getEntryAsInteger(Export2iCalPlugin.PREF_BEFORE_DAYS, 11); + int daysAfter = global_interval ? global_daysAfter : preferences.getEntryAsInteger(Export2iCalPlugin.PREF_AFTER_DAYS, global_daysAfter); + + Date now = new Date(); + + //calModel.getReservations(startDate, endDate) + + // set start Date + calendar.setTime(now); + calendar.add(java.util.Calendar.DAY_OF_YEAR, -daysBefore); + calModel.setStartDate(calendar.getTime()); + + // set end Date + calendar.setTime(now); + calendar.add(java.util.Calendar.DAY_OF_YEAR, daysAfter); + calModel.setEndDate(calendar.getTime()); + + //debug sysout + //System.out.println("startdate - before "+ calModel.getStartDate() + " - " + daysBefore); + //System.out.println("enddate - after "+ calModel.getStartDate() + " - " + daysAfter); + + return calModel; + + } catch (RaplaException e) { + getLogger().error("The Calendarmodel " + filename + " could not be read for the user " + username); + return null; + } catch (NullPointerException e) { + getLogger().error("There is no Calendarmodel named " + filename + " for the user: " + username); + return null; + } + } + + private Reservation[] getAllReservations(CalendarModel calModel) throws RaplaException { + + calendar.set(calendar.getMinimum(java.util.Calendar.YEAR), calendar.getMinimum(java.util.Calendar.MONTH), calendar.getMinimum(java.util.Calendar.DAY_OF_MONTH)); + calModel.setStartDate(calendar.getTime()); + + // Calendar.getMaximum doesn't work with iCal4j. Using 9999 + calendar.set(9999, calendar.getMaximum(java.util.Calendar.MONTH), calendar.getMaximum(java.util.Calendar.DAY_OF_MONTH)); + calModel.setEndDate(calendar.getTime()); + + return calModel.getReservations(); + } + + private void write(Reservation[] reserv) throws RaplaException, IOException { + + response.setContentType("text/calendar; charset=" + getRaplaLocale().getCharsetNonUtf()); + response.setHeader("Content-Disposition", "attachment; filename=" + filename + ".ics"); + + if (reserv == null) { + throw new RaplaException("Error with returning '" + filename); + } + final RaplaContext context = getContext(); + Export2iCalConverter converter = new Export2iCalConverter(context,timezone, preferences); + RaplaGUIComponent comp = new RaplaGUIComponent(context); + converter.setiCalendarWorktime(comp.getCalendarOptions(), getRaplaLocale()); + Calendar iCal = converter.createiCalender(reserv); + CalendarOutputter calOutputter = new CalendarOutputter(); + PrintWriter responseWriter = response.getWriter(); + try { + calOutputter.output(iCal, responseWriter); + } catch (ValidationException e) { + getLogger().error("The calendar file is invalid!\n" + e); + } + } + +} diff --git a/Rapla/src/org/rapla/plugin/export2ical/Export2iCalUserOption.java b/Rapla/src/org/rapla/plugin/export2ical/Export2iCalUserOption.java new file mode 100644 index 0000000..59530e8 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/export2ical/Export2iCalUserOption.java @@ -0,0 +1,194 @@ +package org.rapla.plugin.export2ical; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Locale; + +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSpinner; +import javax.swing.SpinnerNumberModel; + +import org.apache.avalon.framework.configuration.Configuration; +import org.rapla.components.layout.TableLayout; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.configuration.Preferences; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.OptionPanel; +import org.rapla.gui.RaplaGUIComponent; + +/*** + * This is the user-option panel + * @author Twardon + * + */ +public class Export2iCalUserOption extends RaplaGUIComponent implements OptionPanel, ActionListener { + + private Preferences preferences; + private Configuration config; + private JPanel panel = new JPanel(); + + private JSpinner spiDaysBefore; + private JSpinner spiDaysAfter; + + private JCheckBox chkUseUserdefinedIntervall; + + public boolean addButtons = true; + private boolean global_interval; + + private int user_days_before; + private int user_days_after; + private int global_days_before; + private int global_days_after; + + private boolean userdefined; + private JCheckBox chkExportAttendees; + private JComboBox cbDefaultParticipationsStatusRessourceAttribute; + private boolean user_export_attendees; + private String user_export_attendees_participants_status; + + public Export2iCalUserOption(RaplaContext sm, Configuration config) throws RaplaException { + super(sm); + setChildBundleName(Export2iCalPlugin.RESOURCE_FILE); + this.config = config; + + } + + public JComponent getComponent() { + return panel; + } + + public String getName(Locale locale) { + return "Export2iCal"; + } + + public void createList() throws RaplaException { + + chkExportAttendees = new JCheckBox("export attendees of vevent"); + chkExportAttendees.addActionListener(this); + cbDefaultParticipationsStatusRessourceAttribute = new JComboBox(new Object [] { + "ACCEPTED", + "TENTATIVE" + + }); + cbDefaultParticipationsStatusRessourceAttribute.setSelectedItem(Export2iCalPlugin.DEFAULT_attendee_participation_status); + cbDefaultParticipationsStatusRessourceAttribute.setToolTipText("Define the default value for participation status"); + + + double[][] sizes = new double[][] { { 5, TableLayout.FILL, 5,TableLayout.FILL, 5 }, + { TableLayout.PREFERRED, 5, + TableLayout.PREFERRED, 5, + TableLayout.PREFERRED, 5, + TableLayout.PREFERRED, 5, + TableLayout.PREFERRED, 5, + TableLayout.PREFERRED, 5, + TableLayout.PREFERRED, 5, + TableLayout.PREFERRED, 5, + TableLayout.FILL, 5, + TableLayout.PREFERRED } }; + + TableLayout tableLayout = new TableLayout(sizes); + panel.setLayout(tableLayout); + + JPanel interval = new JPanel(); + interval.add(new JLabel(getString("previous_days_text"))); + spiDaysBefore = new JSpinner(new SpinnerNumberModel(30, 0, 100, 1)); + interval.add(spiDaysBefore); + interval.add(new JLabel(getString("subsequent_days_text"))); + spiDaysAfter = new JSpinner(new SpinnerNumberModel(30, 0, 100, 1)); + interval.add(spiDaysAfter); + chkUseUserdefinedIntervall = new JCheckBox(getString("use_user_interval_setting_text")); + chkUseUserdefinedIntervall.setSelected(userdefined); + interval.add(chkUseUserdefinedIntervall); + int before = global_interval ? global_days_before : user_days_before; + spiDaysBefore.setValue(new Integer(before)); + + int after = global_interval ? global_days_after : user_days_after; + spiDaysAfter.setValue(new Integer(after)); + + + + + if (addButtons) { + panel.add(new JLabel(getString("user_interval_setting_text")), "1,0"); + panel.add(chkUseUserdefinedIntervall,"1,2"); + panel.add(interval, "1,4"); + } + + + // set values + chkExportAttendees.setSelected(user_export_attendees); + cbDefaultParticipationsStatusRessourceAttribute.setSelectedItem(user_export_attendees_participants_status); + cbDefaultParticipationsStatusRessourceAttribute.setEnabled(user_export_attendees); + + panel.add(chkExportAttendees, "1,6"); + panel.add(new JLabel("participation status:"), "1,8"); + panel.add(cbDefaultParticipationsStatusRessourceAttribute, "3,8"); + + + chkUseUserdefinedIntervall.setEnabled(!global_interval); + spiDaysAfter.setEnabled(userdefined); + spiDaysBefore.setEnabled(userdefined); + + + chkUseUserdefinedIntervall.addActionListener(this); + + } + + public void show() throws RaplaException { + + global_days_before = config.getChild(Export2iCalPlugin.DAYS_BEFORE).getValueAsInteger(Export2iCalPlugin.DEFAULT_daysBefore); + global_days_after = config.getChild(Export2iCalPlugin.DAYS_AFTER).getValueAsInteger(Export2iCalPlugin.DEFAULT_daysAfter); + + userdefined = (preferences.hasEntry(Export2iCalPlugin.PREF_BEFORE_DAYS) || preferences.hasEntry(Export2iCalPlugin.PREF_AFTER_DAYS)); + + user_days_before = preferences.getEntryAsInteger(Export2iCalPlugin.PREF_BEFORE_DAYS, global_days_before); + user_days_after = preferences.getEntryAsInteger(Export2iCalPlugin.PREF_AFTER_DAYS, global_days_after); + + global_interval = config.getChild(Export2iCalPlugin.GLOBAL_INTERVAL).getValueAsBoolean(Export2iCalPlugin.DEFAULT_globalIntervall); + boolean global_export_attendees = config.getChild(Export2iCalPlugin.EXPORT_ATTENDEES).getValueAsBoolean(Export2iCalPlugin.DEFAULT_exportAttendees); + String global_export_attendees_participants_status = config.getChild(Export2iCalPlugin.EXPORT_ATTENDEES_PARTICIPATION_STATUS).getValue(Export2iCalPlugin.DEFAULT_attendee_participation_status); + + user_export_attendees = preferences.getEntryAsBoolean(Export2iCalPlugin.EXPORT_ATTENDEES, global_export_attendees); + user_export_attendees_participants_status = preferences.getEntryAsString(Export2iCalPlugin.EXPORT_ATTENDEES_PARTICIPATION_STATUS, global_export_attendees_participants_status); + + + createList(); + } + + public void setPreferences(Preferences preferences) { + this.preferences = preferences; + } + + public void actionPerformed(ActionEvent e) { + if (e.getSource()==chkUseUserdefinedIntervall){ + spiDaysBefore.setEnabled(chkUseUserdefinedIntervall.isSelected()); + spiDaysAfter.setEnabled(chkUseUserdefinedIntervall.isSelected()); + } + if (e.getSource() == chkExportAttendees) { + cbDefaultParticipationsStatusRessourceAttribute.setEnabled(chkExportAttendees.isSelected()); + } + } + + public void commit() { + + //saving an null object will delete the setting + if(!chkUseUserdefinedIntervall.isSelected()){ + preferences.putEntry(Export2iCalPlugin.PREF_BEFORE_DAYS, (RaplaObject)null); + preferences.putEntry(Export2iCalPlugin.PREF_AFTER_DAYS, (RaplaObject)null); + }else{ + preferences.putEntry(Export2iCalPlugin.PREF_BEFORE_DAYS, this.spiDaysBefore.getValue().toString()); + preferences.putEntry(Export2iCalPlugin.PREF_AFTER_DAYS, this.spiDaysAfter.getValue().toString()); + } + + preferences.putEntry(Export2iCalPlugin.EXPORT_ATTENDEES, ""+chkExportAttendees.isSelected()); + preferences.putEntry(Export2iCalPlugin.EXPORT_ATTENDEES_PARTICIPATION_STATUS, ""+cbDefaultParticipationsStatusRessourceAttribute.getSelectedItem().toString()); + + + } + +} diff --git a/Rapla/src/org/rapla/plugin/export2ical/ICalExportRemoteServiceFactory.java b/Rapla/src/org/rapla/plugin/export2ical/ICalExportRemoteServiceFactory.java new file mode 100644 index 0000000..8e2b71e --- /dev/null +++ b/Rapla/src/org/rapla/plugin/export2ical/ICalExportRemoteServiceFactory.java @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.export2ical; + +import org.apache.avalon.framework.configuration.Configuration; +import org.rapla.facade.RaplaComponent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.server.RaplaRemoteServiceFactory; +import org.rapla.server.RemoteMethod; +import org.rapla.server.RemoteService; +import org.rapla.server.RemoteSession; + +public class ICalExportRemoteServiceFactory extends RaplaComponent implements RaplaRemoteServiceFactory +{ + public static RemoteMethod EXPORT = new RemoteMethod("export",new String[] {"eventids"}); + Configuration config; + public ICalExportRemoteServiceFactory( RaplaContext context, Configuration config) throws RaplaException + { + super( context ); + this.config = config; + } + + public RemoteService createRemoteService(final RemoteSession session) throws RaplaException + { + if ( session == null ) + { + throw new RaplaException("Need to login first"); + } + RaplaICalExport service = new RaplaICalExport( session.getContext(), config); + service.setSession( session ); + return service; + } + +} + diff --git a/Rapla/src/org/rapla/plugin/export2ical/RaplaICalExport.java b/Rapla/src/org/rapla/plugin/export2ical/RaplaICalExport.java new file mode 100644 index 0000000..a2ea5f4 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/export2ical/RaplaICalExport.java @@ -0,0 +1,103 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.export2ical; + +import java.io.IOException; +import java.io.OutputStream; +import java.text.ParseException; +import java.util.Map; +import java.util.TimeZone; + +import net.fortuna.ical4j.data.CalendarOutputter; +import net.fortuna.ical4j.model.Calendar; +import net.fortuna.ical4j.model.ValidationException; + +import org.apache.avalon.framework.configuration.Configuration; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.storage.RefEntity; +import org.rapla.entities.storage.internal.SimpleIdentifier; +import org.rapla.facade.RaplaComponent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaContextException; +import org.rapla.framework.RaplaException; +import org.rapla.server.RemoteService; +import org.rapla.server.RemoteSession; +import org.rapla.storage.impl.AbstractCachableOperator; + +public class RaplaICalExport extends RaplaComponent implements RemoteService +{ + RemoteSession session; + TimeZone timezone; + + public RaplaICalExport( RaplaContext context, Configuration config) throws RaplaException { + super( context ); + timezone = TimeZone.getTimeZone(config.getChild(Export2iCalPlugin.TIMEZONE).getValue(Export2iCalPlugin.DEFAULT_timezone)); + + } + + public void setSession ( RemoteSession session) { + this.session = session; + } + + public void export(String reservationIds, OutputStream out ) throws RaplaException, IOException + { + Export2iCalConverter converter = new Export2iCalConverter(getContext(),timezone, null); + converter.setiCalendarWorktime(getCalendarOptions(), getRaplaLocale()); + if ( reservationIds.length() == 0) + { + return; + } + String[] ids = reservationIds.split(","); + Reservation[] reservations = new Reservation[ids.length]; + for ( int i =0;i + + + + {0} of {1} errors. Consult log file + {0} von {1} Fehler. Consult Protokolldatei. + {0} sur {1} erreurs. Consultez le fichier journal. + {0} de {1} errores. Consulte el archivo de registro. + {0} z {1} chyb. Konzultovat log souboru. + {0} op {1} fouten. Raadpleeg logbestand. + + diff --git a/Rapla/src/org/rapla/plugin/jndi/JNDIAuthenticationStore.java b/Rapla/src/org/rapla/plugin/jndi/JNDIAuthenticationStore.java new file mode 100644 index 0000000..7d584a7 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/jndi/JNDIAuthenticationStore.java @@ -0,0 +1,1059 @@ +/* + * ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999-2003 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + * [Additional notices, if required by prior licensing conditions] + * + */ + + +package org.rapla.plugin.jndi; + + +import java.security.MessageDigest; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.Map; +import java.util.TreeMap; + +import javax.naming.AuthenticationException; +import javax.naming.CommunicationException; +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.NameParser; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttribute; +import javax.naming.directory.BasicAttributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; + +import org.apache.avalon.framework.activity.Startable; +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.logger.AbstractLogEnabled; +import org.apache.avalon.framework.logger.ConsoleLogger; +import org.apache.avalon.framework.logger.Logger; +import org.rapla.components.util.Tools; +import org.rapla.entities.Category; +import org.rapla.framework.RaplaException; +import org.rapla.plugin.jndi.internal.JNDIConf; +import org.rapla.storage.AuthenticationStore; +import org.rapla.storage.RaplaSecurityException; + +/** + * This Plugin is based on the jakarta.apache.org/tomcat JNDI Realm + * and enables the authentication of a rapla user against a JNDI-Directory. + * The most commen usecase is LDAP-Authentication, but ActiveDirectory + * may be possible, too. + + *
  • Each user element has a distinguished name that can be formed by + * substituting the presented username into a pattern configured by the + * userPattern property.
  • + * + * + *
  • The user may be authenticated by binding to the directory with the + * username and password presented. This method is used when the + * userPassword property is not specified.
  • + * + *
  • The user may be authenticated by retrieving the value of an attribute + * from the directory and comparing it explicitly with the value presented + * by the user. This method is used when the userPassword + * property is specified, in which case: + *
      + *
    • The element for this user must contain an attribute named by the + * userPassword property. + *
    • The value of the user password attribute is either a cleartext + * String, or the result of passing a cleartext String through the + * digest() method (using the standard digest + * support included in RealmBase). + *
    • The user is considered to be authenticated if the presented + * credentials (after being passed through + * digest()) are equal to the retrieved value + * for the user password attribute.
    • + *
  • + * + */ + +public class JNDIAuthenticationStore extends AbstractLogEnabled implements AuthenticationStore,Startable,JNDIConf { + // ----------------------------------------------------- Instance Variables + + + /** + * Digest algorithm used in storing passwords in a non-plaintext format. + * Valid values are those accepted for the algorithm name by the + * MessageDigest class, or null if no digesting should + * be performed. + */ + protected String digest = null; + + /** + * The MessageDigest object for digesting user credentials (passwords). + */ + protected MessageDigest md = null; + + /** + * The connection username for the server we will contact. + */ + protected String connectionName = null; + + + /** + * The connection password for the server we will contact. + */ + protected String connectionPassword = null; + + + /** + * The connection URL for the server we will contact. + */ + protected String connectionURL = null; + + + /** + * The directory context linking us to our directory server. + */ + protected DirContext context = null; + + + /** + * The JNDI context factory used to acquire our InitialContext. By + * default, assumes use of an LDAP server using the standard JNDI LDAP + * provider. + */ + protected String contextFactory = "com.sun.jndi.ldap.LdapCtxFactory"; + + /** + * The attribute name used to retrieve the user password. + */ + protected String userPassword = null; + + /** + * The attribute name used to retrieve the user email. + */ + protected String userMail = null; + + + /** + * The attribute name used to retrieve the complete name of the user. + */ + protected String userCn = null; + + + /** + * The base element for user searches. + */ + protected String userBase = ""; + + /** + * The message format used to search for a user, with "{0}" marking + * the spot where the username goes. + */ + protected String userSearch = null; + + + + /** + * The MessageFormat object associated with the current + * userSearch. + */ + protected MessageFormat userSearchFormat = null; + + /** + * The number of connection attempts. If greater than zero we use the + * alternate url. + */ + protected int connectionAttempt = 0; + + public JNDIAuthenticationStore(Configuration config,Logger logger) throws RaplaException { + enableLogging( logger); + Map map = generateMap(config); + initWithMap(map); + /* + setDigest( config.getAttribute( DIGEST, null ) ); + setConnectionName( config.getAttribute( CONNECTION_NAME ) ); + setConnectionPassword( config.getAttribute( CONNECTION_PASSWORD, null) ); + setConnectionURL( config.getAttribute( CONNECTION_URL ) ); + setContextFactory( config.getAttribute( CONTEXT_FACTORY, contextFactory ) ); + setUserPassword( config.getAttribute( USER_PASSWORD, null ) ); + setUserMail( config.getAttribute( USER_MAIL, null ) ); + setUserCn( config.getAttribute( USER_CN, null ) ); + setUserSearch( config.getAttribute( USER_SEARCH) ); + setUserBase( config.getAttribute( USER_BASE) ); + */ + } + + static public Map generateMap(Configuration config) { + String[] attributes = config.getAttributeNames(); + Map map = new TreeMap(); + for (int i=0;inull. + * + * If there are any errors with the JDBC connection, executing + * the query or anything we return null (don't authenticate). This + * event is also logged, and the connection will be closed so that + * a subsequent request will automatically re-open it. + * + * @param username Username of the Principal to look up + * @param credentials Password or other credentials to use in + * authenticating this username + */ + private JNDIUser authenticateUser(String username, String credentials) { + DirContext context = null; + JNDIUser user = null; + try { + + // Ensure that we have a directory context available + context = open(); + + // Occassionally the directory context will timeout. Try one more + // time before giving up. + try { + + // Authenticate the specified username if possible + user = authenticate(context, username, credentials); + + } catch (CommunicationException e) { + + + // If contains the work closed. Then assume socket is closed. + // If message is null, assume the worst and allow the + // connection to be closed. + if (e.getMessage()!=null && + e.getMessage().indexOf("closed") < 0) + throw(e); + + // log the exception so we know it's there. + log("jndiRealm.exception", e); + + // close the connection so we know it will be reopened. + if (context != null) + close(context); + + // open a new directory context. + context = open(); + + // Try the authentication again. + user = authenticate(context, username, credentials); + + } + + // Return the authenticated Principal (if any) + return user; + } catch (NamingException e) { + + // Log the problem for posterity + log("jndiRealm.exception", e); + + // Close the connection so that it gets reopened next time + if (context != null) + close(context); + + // Return "not authenticated" for this request + return null; + } + + } + + + // -------------------------------------------------------- Package Methods + + + // ------------------------------------------------------ Protected Methods + + + /** + * Return the Principal associated with the specified username and + * credentials, if there is one; otherwise return null. + * + * @param context The directory context + * @param username Username of the Principal to look up + * @param credentials Password or other credentials to use in + * authenticating this username + * + * @exception NamingException if a directory server error occurs + */ + protected synchronized JNDIUser authenticate(DirContext context, + String username, + String credentials) + throws NamingException { + + if (username == null || username.equals("") + || credentials == null || credentials.equals("")) + return (null); + + // Retrieve user information + JNDIUser user = getUser(context, username); + if (user != null && checkCredentials(context, user, credentials)) + return user; + + return null; + } + + + /** + * Return a User object containing information about the user + * with the specified username, if found in the directory; + * otherwise return null. + * + * If the userPassword configuration attribute is + * specified, the value of that attribute is retrieved from the + * user's directory entry. If the userRoleName + * configuration attribute is specified, all values of that + * attribute are retrieved from the directory entry. + * + * @param context The directory context + * @param username Username to be looked up + * + * @exception NamingException if a directory server error occurs + */ + protected JNDIUser getUser(DirContext context, String username) + throws NamingException { + JNDIUser user = null; + // Get attributes to retrieve from user entry + ArrayList list = new ArrayList(); + if (userPassword != null) + list.add(userPassword); + if (userMail != null) + list.add(userMail); + if (userCn != null) + list.add(userCn); + + String[] attrIds = new String[list.size()]; + list.toArray(attrIds); + // Use pattern or search for user entry + user = getUserBySearch(context, username, attrIds); + return user; + } + + /** + * Search the directory to return a User object containing + * information about the user with the specified username, if + * found in the directory; otherwise return null. + * + * @param context The directory context + * @param username The username + * @param attrIds String[]containing names of attributes to retrieve. + * + * @exception NamingException if a directory server error occurs + */ + protected JNDIUser getUserBySearch(DirContext context, + String username, + String[] attrIds) + throws NamingException { + + if (userSearchFormat == null) { + getLogger().error("no userSearchFormat specied"); + return null; + } + + // Form the search filter + String filter = userSearchFormat.format(new String[] { username }); + + // Set up the search controls + SearchControls constraints = new SearchControls(); + + constraints.setSearchScope(SearchControls.SUBTREE_SCOPE); + + // Specify the attributes to be retrieved + if (attrIds == null) + attrIds = new String[0]; + constraints.setReturningAttributes(attrIds); + + if (getLogger().isDebugEnabled()) { + log(" Searching for " + username); + log(" base: " + userBase + " filter: " + filter); + } + //filter = ""; + Attributes attributes = new BasicAttributes(true); + attributes.put(new BasicAttribute("uid","admin")); + NamingEnumeration results = + //context.search(userBase,attributes);// + context.search(userBase, filter,constraints); +/* + while ( results.hasMore()) + { + System.out.println( results.next()); + } + */ // Fail if no entries found + if (results == null || !results.hasMore()) { + if (getLogger().isDebugEnabled()) { + log(" username not found"); + } + return (null); + } + + // Get result for the first entry found + SearchResult result = (SearchResult)results.next(); + + // Check no further entries were found + if (results.hasMore()) { + log("username " + username + " has multiple entries"); + return (null); + } + + // Get the entry's distinguished name + NameParser parser = context.getNameParser(""); + Name contextName = parser.parse(context.getNameInNamespace()); + Name baseName = parser.parse(userBase); + Name entryName = parser.parse(result.getName()); + Name name = contextName.addAll(baseName); + name = name.addAll(entryName); + String dn = name.toString(); + + if (getLogger().isDebugEnabled()) + log(" entry found for " + username + " with dn " + dn); + + // Get the entry's attributes + Attributes attrs = result.getAttributes(); + if (attrs == null) + return null; + + return createUser(username, dn, attrs); + } + + + private JNDIUser createUser(String username, String dn, Attributes attrs) throws NamingException { + // Retrieve value of userPassword + String password = null; + if (userPassword != null) + password = getAttributeValue(userPassword, attrs); + + String mail = null; + if ( userMail != null) { + mail = getAttributeValue( userMail, attrs ); + } + + String cn = null; + if ( userCn != null ) { + cn = getAttributeValue( userCn, attrs ); + } + return new JNDIUser(username, dn, password, mail, cn); + } + + + + /** + * Check whether the given User can be authenticated with the + * given credentials. If the userPassword + * configuration attribute is specified, the credentials + * previously retrieved from the directory are compared explicitly + * with those presented by the user. Otherwise the presented + * credentials are checked by binding to the directory as the + * user. + * + * @param context The directory context + * @param user The User to be authenticated + * @param credentials The credentials presented by the user + * + * @exception NamingException if a directory server error occurs + */ + protected boolean checkCredentials(DirContext context, + JNDIUser user, + String credentials) + throws NamingException { + + boolean validated = false; + + if (userPassword == null) { + validated = bindAsUser(context, user, credentials); + } else { + validated = compareCredentials(context, user, credentials); + } + + if ( getLogger().isDebugEnabled() ) { + if (validated) { + log("jndiRealm.authenticateSuccess: " + user.username); + } else { + log("jndiRealm.authenticateFailure: " + user.username); + } + } + return (validated); + } + + + + /** + * Check whether the credentials presented by the user match those + * retrieved from the directory. + * + * @param context The directory context + * @param info The User to be authenticated + * @param credentials Authentication credentials + * + * @exception NamingException if a directory server error occurs + */ + protected boolean compareCredentials(DirContext context, + JNDIUser info, + String credentials) + throws NamingException { + + if (info == null || credentials == null) + return (false); + + String password = info.password; + if (password == null) + return (false); + + // Validate the credentials specified by the user + if ( getLogger().isDebugEnabled() ) + log(" validating credentials"); + + boolean validated = false; + if (hasMessageDigest()) { + // Hex hashes should be compared case-insensitive + validated = (digest(credentials).equalsIgnoreCase(password)); + } else { + validated = (digest(credentials).equals(password)); + } + + return (validated); + + } + + protected boolean hasMessageDigest() { + return !(md == null); + } + + + /** + * Digest the password using the specified algorithm and + * convert the result to a corresponding hexadecimal string. + * If exception, the plain credentials string is returned. + * + * IMPLEMENTATION NOTE - This implementation is + * synchronized because it reuses the MessageDigest instance. + * This should be faster than cloning the instance on every request. + * + * @param credentials Password or other credentials to use in + * authenticating this username + */ + protected String digest(String credentials) { + + // If no MessageDigest instance is specified, return unchanged + if ( hasMessageDigest() == false) + return (credentials); + + // Digest the user credentials and return as hexadecimal + synchronized (this) { + try { + md.reset(); + md.update(credentials.getBytes()); + return (Tools.convert(md.digest())); + } catch (Exception e) { + log("realmBase.digest", e); + return (credentials); + } + } + } + + /** + * Check credentials by binding to the directory as the user + * + * @param context The directory context + * @param user The User to be authenticated + * @param credentials Authentication credentials + * + * @exception NamingException if a directory server error occurs + */ + protected boolean bindAsUser(DirContext context, + JNDIUser user, + String credentials) + throws NamingException { + + if (credentials == null || user == null) + return (false); + + String dn = user.dn; + if (dn == null) + return (false); + + // Validate the credentials specified by the user + if ( getLogger().isDebugEnabled() ) { + log(" validating credentials by binding as the user"); + } + + // Set up security environment to bind as the user + context.addToEnvironment(Context.SECURITY_PRINCIPAL, dn); + context.addToEnvironment(Context.SECURITY_CREDENTIALS, credentials); + + // Elicit an LDAP bind operation + boolean validated = false; + try { + if ( getLogger().isDebugEnabled() ) { + log(" binding as " + dn); + } + //Attributes attr = + context.getAttributes("", null); + validated = true; + } + catch (AuthenticationException e) { + if ( getLogger().isDebugEnabled() ) { + log(" bind attempt failed" + e.getMessage()); + } + } + + // Restore the original security environment + if (connectionName != null) { + context.addToEnvironment(Context.SECURITY_PRINCIPAL, + connectionName); + } else { + context.removeFromEnvironment(Context.SECURITY_PRINCIPAL); + } + + if (connectionPassword != null) { + context.addToEnvironment(Context.SECURITY_CREDENTIALS, + connectionPassword); + } + else { + context.removeFromEnvironment(Context.SECURITY_CREDENTIALS); + } + + return (validated); + } + + + /** + * Return a String representing the value of the specified attribute. + * + * @param attrId Attribute name + * @param attrs Attributes containing the required value + * + * @exception NamingException if a directory server error occurs + */ + private String getAttributeValue(String attrId, Attributes attrs) + throws NamingException { + + if ( getLogger().isDebugEnabled() ) + log(" retrieving attribute " + attrId); + + if (attrId == null || attrs == null) + return null; + + Attribute attr = attrs.get(attrId); + if (attr == null) + return (null); + Object value = attr.get(); + if (value == null) + return (null); + String valueString = null; + if (value instanceof byte[]) + valueString = new String((byte[]) value); + else + valueString = value.toString(); + + return valueString; + } + + + /** + * Close any open connection to the directory server for this Realm. + * + * @param context The directory context to be closed + */ + protected void close(DirContext context) { + + // Do nothing if there is no opened connection + if (context == null) + return; + + // Close our opened connection + try { + if ( getLogger().isDebugEnabled() ) + log("Closing directory context"); + context.close(); + } catch (NamingException e) { + log("jndiRealm.close", e); + } + this.context = null; + + } + + + /** + * Open (if necessary) and return a connection to the configured + * directory server for this Realm. + * + * @exception NamingException if a directory server error occurs + */ + protected DirContext open() throws NamingException { + + // Do nothing if there is a directory server connection already open + if (context != null) + return (context); + + try { + + // Ensure that we have a directory context available + context = new InitialDirContext(getDirectoryContextEnvironment()); +/* + } catch (NamingException e) { + + connectionAttempt = 1; + + // log the first exception. + log("jndiRealm.exception", e); + + // Try connecting to the alternate url. + context = new InitialDirContext(getDirectoryContextEnvironment()); +*/ + } finally { + + // reset it in case the connection times out. + // the primary may come back. + connectionAttempt = 0; + + } + + return (context); + + } + + /** + * Create our directory context configuration. + * + * @return java.util.Hashtable the configuration for the directory context. + */ + protected Hashtable getDirectoryContextEnvironment() { + + Hashtable env = new Hashtable(); + + // Configure our directory context environment. + if ( getLogger().isDebugEnabled() && connectionAttempt == 0) + log("Connecting to URL " + connectionURL); + env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory); + if (connectionName != null) + env.put(Context.SECURITY_PRINCIPAL, connectionName); + if (connectionPassword != null) + env.put(Context.SECURITY_CREDENTIALS, connectionPassword); + if (connectionURL != null && connectionAttempt == 0) + env.put(Context.PROVIDER_URL, connectionURL); + return env; + } + + // ------------------------------------------------------ Lifecycle Methods + + + /** + * Prepare for active use of the public methods of this Component. + * + * @exception LifecycleException if this component detects a fatal error + * that prevents it from being started + */ + public void start() throws Exception { + // Create a MessageDigest instance for credentials, if desired + if (digest != null) { + md = MessageDigest.getInstance(digest); + } + // Validate that we can open our connection + open(); + // Perform normal superclass initialization + } + + + /** + * Gracefully shut down active use of the public methods of this Component. + * + * @exception LifecycleException if this component detects a fatal error + * that needs to be reported + */ + public void stop() throws Exception { + // Close any open directory server connection + close(this.context); + + } + + public static void main(String[] args) { + JNDIAuthenticationStore aut = new JNDIAuthenticationStore(); + aut.enableLogging(new ConsoleLogger()); + aut.setConnectionName( "uid=admin,ou=system" ); + aut.setConnectionPassword( "secret" ); + aut.setConnectionURL( "ldap://localhost:10389" ); + //aut.setUserPassword ( "userPassword" ); + aut.setUserBase ( "dc=example,dc=com" ); + aut.setUserSearch ("(uid={0})" ); + try { + aut.start(); + if ( aut.authenticate ( "admin", "admin" ) ) { + System.out.println( "Authentication succeeded." ); + } else { + System.out.println( "Authentication failed" ); + } + } catch (Exception ex ) { + ex.printStackTrace(); + } + } + + + /** + * A private class representing a User + */ + static class JNDIUser { + String username = null; + String dn = null; + String password = null; + String mail = null; + String cn = null; + + JNDIUser(String username, String dn, String password, String mail, String cn) { + this.username = username; + this.dn = dn; + this.password = password; + this.mail = mail; + this.cn = cn; + } + + } +} + diff --git a/Rapla/src/org/rapla/plugin/jndi/JNDIPlugin.java b/Rapla/src/org/rapla/plugin/jndi/JNDIPlugin.java new file mode 100644 index 0000000..6e68adb --- /dev/null +++ b/Rapla/src/org/rapla/plugin/jndi/JNDIPlugin.java @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.jndi; + +import org.apache.avalon.framework.configuration.Configuration; +import org.rapla.framework.Container; +import org.rapla.framework.PluginDescriptor; +import org.rapla.plugin.RaplaExtensionPoints; +import org.rapla.plugin.jndi.internal.JNDIOption; +import org.rapla.plugin.jndi.internal.JNDITest; +import org.rapla.plugin.jndi.internal.RaplaJNDITestOnLocalhost; +import org.rapla.plugin.jndi.internal.RaplaJNDITestOnServer; +import org.rapla.plugin.jndi.internal.RaplaJNDITestRemoteServiceFactory; +import org.rapla.server.RaplaRemoteServiceFactory; +import org.rapla.server.ServerService; +import org.rapla.storage.AuthenticationStore; + +public class JNDIPlugin implements PluginDescriptor { + public static final String PLUGIN_CLASS = JNDIPlugin.class.getName(); + public static final String PLUGIN_NAME = "Ldap or other JNDI Authentication"; + + public static final String JNDI_ON_SERVER = JNDIPlugin.class.getPackage().getName() + ".JNDIOnServer"; + public static final String JNDI_ON_LOCALHOST = JNDIPlugin.class.getPackage().getName() + ".JNDIOnLocalhost"; + + public String toString() { + return PLUGIN_NAME; + } + + public void provideServices(Container container, Configuration config) + { + if ( container.getContext().has( ServerService.ROLE) ){ + container.addContainerProvidedComponent( RaplaRemoteServiceFactory.ROLE, RaplaJNDITestRemoteServiceFactory.class.getName()); + // only add mail service on localhost + container.addContainerProvidedComponent( JNDITest.ROLE, RaplaJNDITestOnLocalhost.class.getName(), JNDI_ON_LOCALHOST , config); + } else { + // the following order is important for resolving, + // first add the service on the server + // then on localhost + // container.addContainerProvidedComponent( JNDITest.ROLE, RaplaJNDITestOnLocalhost.class.getName(), JNDI_ON_LOCALHOST , config); + container.addContainerProvidedComponent( JNDITest.ROLE, RaplaJNDITestOnServer.class.getName(), JNDI_ON_SERVER , config); + } + container.addContainerProvidedComponent( RaplaExtensionPoints.PLUGIN_OPTION_PANEL_EXTENSION,JNDIOption.class.getName(),JNDIPlugin.class.getName(), config); + + if ( !config.getAttributeAsBoolean("enabled", false) ) + return; + + container.addContainerProvidedComponent( AuthenticationStore.class.getName(), JNDIAuthenticationStore.class.getName(), PLUGIN_CLASS, config); + } + + public Object getPluginMetaInfos( String key ) + { + return null; + } + + + +} + diff --git a/Rapla/src/org/rapla/plugin/jndi/internal/JNDIConf.java b/Rapla/src/org/rapla/plugin/jndi/internal/JNDIConf.java new file mode 100644 index 0000000..a5f5b0d --- /dev/null +++ b/Rapla/src/org/rapla/plugin/jndi/internal/JNDIConf.java @@ -0,0 +1,15 @@ +package org.rapla.plugin.jndi.internal; + +public interface JNDIConf { + public static final String USER_BASE = "userBase"; + public static final String USER_SEARCH = "userSearch"; + public static final String USER_CN = "userCn"; + public static final String USER_MAIL = "userMail"; + public static final String USER_PASSWORD = "userPassword"; + public static final String DIGEST = "digest"; + public static final String CONTEXT_FACTORY = "contextFactory"; + public static final String CONNECTION_URL = "connectionURL"; + public static final String CONNECTION_PASSWORD = "connectionPassword"; + public static final String CONNECTION_NAME = "connectionName"; + +} diff --git a/Rapla/src/org/rapla/plugin/jndi/internal/JNDIOption.java b/Rapla/src/org/rapla/plugin/jndi/internal/JNDIOption.java new file mode 100644 index 0000000..f09165f --- /dev/null +++ b/Rapla/src/org/rapla/plugin/jndi/internal/JNDIOption.java @@ -0,0 +1,219 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.jndi.internal; + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Locale; + +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.DefaultConfiguration; +import org.rapla.components.layout.TableLayout; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.DefaultPluginOption; +import org.rapla.gui.OptionPanel; +import org.rapla.gui.toolkit.DialogUI; +import org.rapla.plugin.jndi.JNDIPlugin; + +public class JNDIOption extends DefaultPluginOption implements OptionPanel,JNDIConf { + TableLayout tableLayout; + JPanel content; + + JTextField digest; + JTextField connectionName; + JTextField connectionPassword; + JTextField connectionURL; + JTextField contextFactory; + JTextField userPassword; + JTextField userMail; + JTextField userCn; + JTextField userSearch; + JTextField userBase; + + + + public JNDIOption(RaplaContext sm) throws RaplaException { + super(sm); + } + + protected JPanel createPanel() throws RaplaException { + digest = newTextField(); + connectionName = newTextField(); + connectionPassword = newTextField(); + connectionURL = newTextField(); + contextFactory= newTextField(); + userPassword = newTextField(); + userMail = newTextField(); + userCn = newTextField(); + userSearch = newTextField(); + userBase = newTextField(); + + JPanel panel = super.createPanel(); + content = new JPanel(); + tableLayout = new TableLayout(); + tableLayout.insertColumn( 0, TableLayout.PREFERRED); + tableLayout.insertColumn( 1, 5); + tableLayout.insertColumn( 2, TableLayout.FILL); + tableLayout.insertColumn( 3, 5); + content.setLayout(tableLayout); + tableLayout.insertRow( 0, TableLayout.PREFERRED); + content.add( new JLabel("WARNING! Only enable plugin, if you have a LDAP Server running!"), "0,0,2,0"); + addRow(CONNECTION_NAME, connectionName); + addRow(CONNECTION_PASSWORD, connectionPassword ); + addRow(CONNECTION_URL, connectionURL ); + addRow(CONTEXT_FACTORY, contextFactory); + addRow(DIGEST, digest); + addRow(USER_PASSWORD, userPassword ); + addRow(USER_MAIL, userMail ); + addRow(USER_CN, userCn ); + addRow(USER_SEARCH, userSearch ); + addRow(USER_BASE, userBase ); + JButton testButton = new JButton("Test access"); + addRow("TestAccess", testButton ); + testButton.addActionListener( new ActionListener() + { + + public void actionPerformed(ActionEvent e) { + try + { + JNDITest test = (JNDITest)getContext().lookup(JNDITest.ROLE); + DefaultConfiguration conf = new DefaultConfiguration("test"); + StringBuffer buf = new StringBuffer(); + addChildren(conf); + String[] attributes = conf.getAttributeNames(); + for (int i=0;i 0) + { + return; + } + } + test.test(list,username,password); + { + DialogUI dialog =DialogUI.create( getContext(), getComponent(), true, "JNDI","JNDI Authentification successfull"); + dialog.start(); + } + } catch (Exception ex) + { + showException(ex, getComponent()); + } + + } + + }); + panel.add( content, BorderLayout.CENTER); + return panel; + } + + private JTextField newTextField() throws RaplaException { + final JTextField jTextField = new JTextField(); + addCopyPaste( jTextField); + return jTextField; + } + + private void addRow(String title, JComponent component) { + int row = tableLayout.getNumRow(); + tableLayout.insertRow( row, TableLayout.PREFERRED); + content.add(new JLabel(title), "0," + row); + content.add( component, "2," + row); + tableLayout.insertRow( row + 1, 5); + } + + protected void addChildren( DefaultConfiguration newConfig) { + setAttribute(newConfig,CONNECTION_NAME, connectionName); + setAttribute(newConfig,CONNECTION_PASSWORD, connectionPassword ); + setAttribute(newConfig,CONNECTION_URL, connectionURL ); + setAttribute(newConfig,CONTEXT_FACTORY, contextFactory); + setAttribute(newConfig,DIGEST, digest); + setAttribute(newConfig,USER_BASE, userBase ); + setAttribute(newConfig,USER_CN, userCn ); + setAttribute(newConfig,USER_MAIL, userMail ); + setAttribute(newConfig,USER_PASSWORD, userPassword ); + setAttribute(newConfig,USER_SEARCH, userSearch ); + } + + public void setAttribute( DefaultConfiguration newConfig, String attributeName, JTextField text) { + String value = text.getText().trim(); + if ( value.length() > 0) + newConfig.setAttribute( attributeName, value); + } + + public void readAttribute( String attributeName, JTextField text) { + readAttribute( attributeName, text, ""); + } + + public void readAttribute( String attributeName, JTextField text, String defaultValue) { + text.setText(config.getAttribute(attributeName, defaultValue)); + } + + protected void readConfig( Configuration config) { + super.readConfig( config ); + readAttribute("digest", digest); + readAttribute("connectionName", connectionName, "cn=Manager,dc=mycompany,dc=com" ); + readAttribute("connectionPassword", connectionPassword, "secret" ); + readAttribute("connectionURL", connectionURL, "ldap://localhost:389"); + readAttribute("contextFactory", contextFactory, "com.sun.jndi.ldap.LdapCtxFactory"); + readAttribute("userPassword", userPassword,"userPassword" ); + readAttribute("userMail", userMail,"mail" ); + readAttribute("userCn", userCn,"cn" ); + readAttribute("userSearch", userSearch,"(uid={0})" ); + readAttribute("userBase", userBase,"ou=people,dc=mycompany,dc=com" ); + } + + public void show() throws RaplaException { + super.show(); + } + + public void commit() throws RaplaException { + super.commit(); + } + + + /** + * @see org.rapla.gui.DefaultPluginOption#getDescriptorClassName() + */ + public String getDescriptorClassName() { + return JNDIPlugin.class.getName(); + } + + public String getName(Locale locale) { + return JNDIPlugin.PLUGIN_NAME; + } + +} diff --git a/Rapla/src/org/rapla/plugin/jndi/internal/JNDITest.java b/Rapla/src/org/rapla/plugin/jndi/internal/JNDITest.java new file mode 100644 index 0000000..420b9eb --- /dev/null +++ b/Rapla/src/org/rapla/plugin/jndi/internal/JNDITest.java @@ -0,0 +1,11 @@ +package org.rapla.plugin.jndi.internal; + +import org.rapla.framework.RaplaException; +import org.rapla.server.RemoteMethod; + +public interface JNDITest { + RemoteMethod TEST = new RemoteMethod("test", new String[] {"config","username","password"}); + String ROLE = JNDITest.class.getName(); + + public void test(String config,String username,String password) throws RaplaException; +} diff --git a/Rapla/src/org/rapla/plugin/jndi/internal/PasswordEnterUI.java b/Rapla/src/org/rapla/plugin/jndi/internal/PasswordEnterUI.java new file mode 100644 index 0000000..dbeec23 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/jndi/internal/PasswordEnterUI.java @@ -0,0 +1,80 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.jndi.internal; + +import java.awt.GridLayout; + +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; + +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.toolkit.RaplaWidget; + + +public class PasswordEnterUI extends RaplaGUIComponent + implements + RaplaWidget +{ + JPanel panel = new JPanel(); + GridLayout gridLayout1 = new GridLayout(); + // The Controller for this Dialog + + JLabel label1 = new JLabel(); + JLabel label2 = new JLabel(); + + JTextField tf1 = new JTextField(10); + JPasswordField tf2 = new JPasswordField(10); + + + public PasswordEnterUI(RaplaContext sm) throws RaplaException{ + super( sm); + panel.setLayout(gridLayout1); + gridLayout1.setRows( 2); + gridLayout1.setColumns(2); + gridLayout1.setHgap(10); + gridLayout1.setVgap(10); + panel.add(label1); + panel.add(tf1); + + panel.add(label2); + panel.add(tf2); + label1.setText(getString("username") + ":"); + label2.setText(getString("password") + ":"); + } + + public JComponent getComponent() { + return panel; + } + + public String getUsername() { + return tf1.getText(); + } + + public char[] getNewPassword() { + return tf2.getPassword(); + } +} + + + + + + + + + diff --git a/Rapla/src/org/rapla/plugin/jndi/internal/RaplaJNDITestOnLocalhost.java b/Rapla/src/org/rapla/plugin/jndi/internal/RaplaJNDITestOnLocalhost.java new file mode 100644 index 0000000..c89ca24 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/jndi/internal/RaplaJNDITestOnLocalhost.java @@ -0,0 +1,84 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.jndi.internal; + +import java.io.IOException; +import java.io.OutputStream; +import java.text.ParseException; +import java.util.Map; +import java.util.TreeMap; + +import org.apache.avalon.framework.logger.Logger; +import org.rapla.facade.RaplaComponent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.plugin.jndi.JNDIAuthenticationStore; +import org.rapla.server.RemoteService; +import org.rapla.server.RemoteSession; + +public class RaplaJNDITestOnLocalhost extends RaplaComponent implements RemoteService, JNDITest +{ + RemoteSession session; + public RaplaJNDITestOnLocalhost( RaplaContext context) throws RaplaException { + super( context ); + } + + public void setSession ( RemoteSession session) { + this.session = session; + } + + public void test(String config,String username,String password ) throws RaplaException + { + String[] test = config.split("RAPLANEXT"); + Map map = new TreeMap(); + for (int i =0;i + * MailToUserInterface mail = (MailToUserInterface) getContext().loopup( MailToUserInterface.ROLE ); + * mail.sendMail( subject, body ); + * + * + * Example 2: + * + * + * MailInterface mail = (MailInterface) getContext().loopup( MailInterface.ROLE ); + * mail.sendMail( senderMail, recipient, subject, body ); + * + + * @see org.rapla.components.mail.MailInterface + * @see org.rapla.plugin.mail.MailToUserInterface + */ +public class MailPlugin implements PluginDescriptor +{ + public static final String MAIL_ON_SERVER = MailPlugin.class.getPackage().getName() + ".MailOnServer"; + public static final String MAIL_ON_LOCALHOST = MailPlugin.class.getPackage().getName() + ".MailOnLocalhost"; + public static final String DEFAULT_SENDER_ENTRY = "org.rapla.plugin.mail.DefaultSender"; + + public static String DEFAULT_MAIL_CLASS = org.rapla.components.mail.SmtpClient.class.getName(); + + public String toString() { + return "Mail Service"; + } + + public void provideServices(Container container, Configuration config) { + container.addContainerProvidedComponent( RaplaExtensionPoints.PLUGIN_OPTION_PANEL_EXTENSION,MailOption.class.getName(),MailPlugin.class.getName(), config); + + if ( !config.getAttributeAsBoolean("enabled", false) ) + return; + + String mailClass =config.getChild("mailinterface").getValue( DEFAULT_MAIL_CLASS); + // Use the mail API + if ( config.getAttributeAsBoolean("usemailapi", false)) + { + mailClass = MailapiClient.class.getName(); + } + + container.addContainerProvidedComponent( MailInterface.ROLE, mailClass, mailClass , config); + + if ( container.getContext().has( ServerService.ROLE) ){ + container.addContainerProvidedComponent( RaplaRemoteServiceFactory.ROLE, MailToUserRemoteServiceFactory.class.getName()); + // only add mail service on localhost + container.addContainerProvidedComponent( MailToUserInterface.ROLE, RaplaMailToUserOnLocalhost.class.getName(), MAIL_ON_LOCALHOST , config); + } else { + // the following order is important for resolving, + // first add the service on the server + // then on localhost + container.addContainerProvidedComponent( MailToUserInterface.ROLE, RaplaMailToUserOnServer.class.getName(),MAIL_ON_SERVER, config); + container.addContainerProvidedComponent( MailToUserInterface.ROLE, RaplaMailToUserOnLocalhost.class.getName(), MAIL_ON_LOCALHOST , config); + } + } + + public Object getPluginMetaInfos( String key ) + { + return null; + } +} + diff --git a/Rapla/src/org/rapla/plugin/mail/MailToUserInterface.java b/Rapla/src/org/rapla/plugin/mail/MailToUserInterface.java new file mode 100644 index 0000000..7589d83 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/mail/MailToUserInterface.java @@ -0,0 +1,13 @@ +package org.rapla.plugin.mail; + +import org.rapla.framework.RaplaException; +import org.rapla.server.RemoteMethod; + +public interface MailToUserInterface +{ + RemoteMethod SEND_MAIL = new RemoteMethod("sendMail", new String[] {"username","subject","mailBody"}); + + String ROLE = MailToUserInterface.class.getName(); + + void sendMail(String username,String subject, String body) throws RaplaException; +} diff --git a/Rapla/src/org/rapla/plugin/mail/internal/MailOption.java b/Rapla/src/org/rapla/plugin/mail/internal/MailOption.java new file mode 100644 index 0000000..9de14ee --- /dev/null +++ b/Rapla/src/org/rapla/plugin/mail/internal/MailOption.java @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.mail.internal; + +import java.awt.BorderLayout; +import java.util.Locale; + +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.DefaultConfiguration; +import org.rapla.components.calendar.RaplaNumber; +import org.rapla.components.layout.TableLayout; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.DefaultPluginOption; +import org.rapla.gui.OptionPanel; +import org.rapla.plugin.mail.MailPlugin; + +public class MailOption extends DefaultPluginOption implements OptionPanel { + + JTextField mailServer = new JTextField(); + RaplaNumber smtpPortField = new RaplaNumber(new Integer(25), new Integer(0),null,false); + JTextField defaultSender = new JTextField(); + JCheckBox booleanField = new JCheckBox(); + + public MailOption(RaplaContext sm) throws RaplaException { + super(sm); + } + + protected JPanel createPanel() throws RaplaException { + JPanel panel = super.createPanel(); + JPanel content = new JPanel(); + addCopyPaste( mailServer); + addCopyPaste( defaultSender); + double[][] sizes = new double[][] { + {5,TableLayout.PREFERRED, 5,TableLayout.FILL,5} + ,{TableLayout.PREFERRED,5,TableLayout.PREFERRED, 5, TableLayout.PREFERRED, 5, TableLayout.PREFERRED, 5, TableLayout.PREFERRED} + }; + TableLayout tableLayout = new TableLayout(sizes); + content.setLayout(tableLayout); + content.add(new JLabel("Mail Server"), "1,0"); + content.add( mailServer, "3,0"); + content.add(new JLabel("Mail Port"), "1,2"); + content.add( smtpPortField, "3,2"); + content.add(new JLabel("Use Java Mail API*"), "1,4"); + content.add(booleanField,"3,4"); + content.add(new JLabel("Default Sender"), "1,6"); + content.add( defaultSender, "3,6"); + content.add( new JLabel("*Require the mailapi-jars on classpath (e.g.WEB-INF/lib)"), "3,8"); + + panel.add( content, BorderLayout.CENTER); + return panel; + } + + + protected void addChildren( DefaultConfiguration newConfig) { + DefaultConfiguration smtpPort = new DefaultConfiguration("smtp-port"); + DefaultConfiguration smtpServer = new DefaultConfiguration("smtp-host"); + smtpPort.setValue(smtpPortField.getNumber().intValue() ); + smtpServer.setValue( mailServer.getText()); + newConfig.setAttribute( "usemailapi", booleanField.isSelected() ); + newConfig.addChild( smtpPort ); + newConfig.addChild( smtpServer ); + } + + protected void readConfig( Configuration config) { + booleanField.setSelected( config.getAttributeAsBoolean("usemailapi", false)); + mailServer.setText( config.getChild("smtp-host").getValue("localhost")); + smtpPortField.setNumber( new Integer(config.getChild("smtp-port").getValueAsInteger(25))); + } + + public void show() throws RaplaException { + super.show(); + defaultSender.setText( preferences.getEntryAsString(MailPlugin.DEFAULT_SENDER_ENTRY,"rapla")); + } + + public void commit() throws RaplaException { + super.commit(); + preferences.putEntry(MailPlugin.DEFAULT_SENDER_ENTRY,String.valueOf( defaultSender.getText() )); + } + + + /** + * @see org.rapla.gui.DefaultPluginOption#getDescriptorClassName() + */ + public String getDescriptorClassName() { + return MailPlugin.class.getName(); + } + + public String getName(Locale locale) { + return "Mail Plugin"; + } + +} diff --git a/Rapla/src/org/rapla/plugin/mail/internal/MailToUserRemoteServiceFactory.java b/Rapla/src/org/rapla/plugin/mail/internal/MailToUserRemoteServiceFactory.java new file mode 100644 index 0000000..2401ca0 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/mail/internal/MailToUserRemoteServiceFactory.java @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.mail.internal; + +import org.rapla.facade.RaplaComponent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.server.RaplaRemoteServiceFactory; +import org.rapla.server.RemoteService; +import org.rapla.server.RemoteSession; + +public class MailToUserRemoteServiceFactory extends RaplaComponent implements RaplaRemoteServiceFactory +{ + public MailToUserRemoteServiceFactory( RaplaContext context ) throws RaplaException + { + super( context ); + } + + public RemoteService createRemoteService(final RemoteSession session) throws RaplaException + { + if ( session == null ) + { + throw new RaplaException("Need to login first"); + } + RaplaMailToUserOnLocalhost service = new RaplaMailToUserOnLocalhost( getContext() ); + service.setSession( session ); + return service; + } + +} + diff --git a/Rapla/src/org/rapla/plugin/mail/internal/RaplaMailToUserOnLocalhost.java b/Rapla/src/org/rapla/plugin/mail/internal/RaplaMailToUserOnLocalhost.java new file mode 100644 index 0000000..d8322d5 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/mail/internal/RaplaMailToUserOnLocalhost.java @@ -0,0 +1,84 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.mail.internal; + +import java.io.IOException; +import java.io.OutputStream; +import java.text.ParseException; +import java.util.Map; + +import org.rapla.components.mail.MailException; +import org.rapla.components.mail.MailInterface; +import org.rapla.entities.User; +import org.rapla.entities.configuration.Preferences; +import org.rapla.facade.ClientFacade; +import org.rapla.facade.RaplaComponent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.plugin.mail.MailPlugin; +import org.rapla.plugin.mail.MailToUserInterface; +import org.rapla.server.RemoteService; +import org.rapla.server.RemoteSession; + +public class RaplaMailToUserOnLocalhost extends RaplaComponent implements MailToUserInterface, RemoteService +{ + RemoteSession session; + + public RaplaMailToUserOnLocalhost( RaplaContext context) throws RaplaException { + super( context ); + } + + public void setSession ( RemoteSession session) { + this.session = session; + } + + public void sendMail(String userName,String subject, String body) throws RaplaException { + User recipientUser = getQuery().getUser( userName ); + // O.K. We need to generate the mail + String recipientEmail = recipientUser.getEmail(); + if (recipientEmail == null || recipientEmail.trim().length() == 0) { + getLogger().warn("No email adress specified for user " + + recipientUser.getUsername() + + " Can't send mail."); + return; + } + + + final MailInterface mail = (MailInterface)getContext().lookup(MailInterface.ROLE); + ClientFacade facade = (ClientFacade) getContext().lookup(ClientFacade.ROLE); + Preferences prefs = facade.getPreferences( null); + final String defaultSender = prefs.getEntryAsString( MailPlugin.DEFAULT_SENDER_ENTRY, "rapla"); + try { + mail.sendMail( defaultSender, recipientEmail,subject, body); + } catch (MailException ex) { + throw new RaplaException( ex ); + } + if ( session != null) { + session.getLogger().info("Email send to user " + userName); + } + + } + + public void remoteMethodCall( String methodName, Map args, OutputStream out ) throws RaplaException, IOException, ParseException + { + if (SEND_MAIL.is( methodName )) + { + String username = SEND_MAIL.value( args, 0); + String subject= SEND_MAIL.value( args, 1); + String body= SEND_MAIL.value( args, 2); + sendMail( username, subject, body); + } + + } +} + diff --git a/Rapla/src/org/rapla/plugin/mail/internal/RaplaMailToUserOnServer.java b/Rapla/src/org/rapla/plugin/mail/internal/RaplaMailToUserOnServer.java new file mode 100644 index 0000000..e0e59ae --- /dev/null +++ b/Rapla/src/org/rapla/plugin/mail/internal/RaplaMailToUserOnServer.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.mail.internal; + +import org.rapla.facade.RaplaComponent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.plugin.mail.MailToUserInterface; +import org.rapla.server.RemoteServiceCaller; + +/** RemoteStub */ +public class RaplaMailToUserOnServer extends RaplaComponent implements MailToUserInterface +{ + + public RaplaMailToUserOnServer( RaplaContext context ) throws RaplaException + { + super( context ); + } + + public void sendMail( String username, String subject, String mailBody ) throws RaplaException + { + RemoteServiceCaller remote =(RemoteServiceCaller) getContext().lookup( RemoteServiceCaller.ROLE); + remote.call( MailToUserRemoteServiceFactory.class.getName(),SEND_MAIL, new String[] {username, subject,mailBody}); + } + +} + diff --git a/Rapla/src/org/rapla/plugin/monthview/HTMLMonthViewPage.java b/Rapla/src/org/rapla/plugin/monthview/HTMLMonthViewPage.java new file mode 100644 index 0000000..d0de627 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/monthview/HTMLMonthViewPage.java @@ -0,0 +1,60 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.monthview; + +import java.util.Calendar; +import java.util.Set; + +import org.rapla.components.calendarview.html.AbstractHTMLView; +import org.rapla.components.calendarview.html.HTMLMonthView; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.CalendarOptions; +import org.rapla.plugin.abstractcalendar.AbstractHTMLCalendarPage; +import org.rapla.plugin.abstractcalendar.GroupAllocatablesStrategy; +import org.rapla.plugin.abstractcalendar.RaplaBuilder; + +public class HTMLMonthViewPage extends AbstractHTMLCalendarPage +{ + public HTMLMonthViewPage( RaplaContext context, CalendarModel calendarModel ) throws RaplaException + { + super( context, calendarModel ); + } + + protected AbstractHTMLView createCalendarView() { + HTMLMonthView monthView = new HTMLMonthView(); + CalendarOptions opt = getCalendarOptions(); + Set excludeDays = opt.getExcludeDays(); + monthView.setExcludeDays( excludeDays ); + return monthView; + } + + protected RaplaBuilder createBuilder() throws RaplaException { + RaplaBuilder builder = super.createBuilder(); + builder.setSmallBlocks( true ); + + GroupAllocatablesStrategy strategy = new GroupAllocatablesStrategy( getRaplaLocale().getLocale() ); + boolean compactColumns = getCalendarOptions().isCompactColumns() || builder.getAllocatables().size() ==0 ; + strategy.setFixedSlotsEnabled( !compactColumns); + builder.setBuildStrategy( strategy ); + + return builder; + } + + protected int getIncrementSize() { + return Calendar.MONTH; + } + +} + diff --git a/Rapla/src/org/rapla/plugin/monthview/MonthViewFactory.java b/Rapla/src/org/rapla/plugin/monthview/MonthViewFactory.java new file mode 100644 index 0000000..eafe9da --- /dev/null +++ b/Rapla/src/org/rapla/plugin/monthview/MonthViewFactory.java @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.monthview; + +import javax.swing.Icon; + +import org.rapla.facade.RaplaComponent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.SwingCalendarView; +import org.rapla.gui.ViewFactory; +import org.rapla.gui.images.Images; +import org.rapla.servletpages.RaplaPageGenerator; + +public class MonthViewFactory extends RaplaComponent implements ViewFactory +{ + public MonthViewFactory( RaplaContext context ) throws RaplaException + { + super( context ); + } + + public final static String MONTH_VIEW = "month"; + + public SwingCalendarView createSwingView(RaplaContext context, CalendarModel model, boolean editable) throws RaplaException + { + return new SwingMonthCalendar( context, model, editable); + } + + public RaplaPageGenerator createHTMLView(RaplaContext context, CalendarModel model) throws RaplaException + { + return new HTMLMonthViewPage( context, model); + } + + public String getViewId() + { + return MONTH_VIEW; + } + + public String getName() + { + return getString(MONTH_VIEW); + } + + Icon icon; + public Icon getIcon() + { + if ( icon == null) { + icon = Images.getIcon("/org/rapla/plugin/monthview/images/month.png"); + } + return icon; + } + + public String getMenuSortKey() { + return "C"; + } + + +} + diff --git a/Rapla/src/org/rapla/plugin/monthview/MonthViewPlugin.java b/Rapla/src/org/rapla/plugin/monthview/MonthViewPlugin.java new file mode 100644 index 0000000..2d9d9c2 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/monthview/MonthViewPlugin.java @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.monthview; + +import org.apache.avalon.framework.configuration.Configuration; +import org.rapla.framework.Container; +import org.rapla.framework.PluginDescriptor; +import org.rapla.plugin.RaplaExtensionPoints; +import org.rapla.plugin.RaplaPluginMetaInfo; + +public class MonthViewPlugin implements PluginDescriptor +{ + public String toString() { + return "Month-View"; + } + + public void provideServices(Container container, Configuration config) { + if ( !config.getAttributeAsBoolean("enabled", true)) + return; + + container.addContainerProvidedComponent + ( + RaplaExtensionPoints.CALENDAR_VIEW_EXTENSION + ,MonthViewFactory.class.getName() + ,MonthViewFactory.MONTH_VIEW + ,null + ); + } + + public Object getPluginMetaInfos( String key ) + { + if ( RaplaPluginMetaInfo.METAINFO_PLUGIN_ENABLED_BY_DEFAULT.equals( key )) { + return Boolean.TRUE; + } + return null; + } + +} + diff --git a/Rapla/src/org/rapla/plugin/monthview/SwingMonthCalendar.java b/Rapla/src/org/rapla/plugin/monthview/SwingMonthCalendar.java new file mode 100644 index 0000000..62c145c --- /dev/null +++ b/Rapla/src/org/rapla/plugin/monthview/SwingMonthCalendar.java @@ -0,0 +1,132 @@ + +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.plugin.monthview; + +import java.awt.Color; +import java.util.Calendar; +import java.util.Date; +import java.util.Set; + +import javax.swing.JComponent; + +import org.rapla.components.calendar.WeekendHighlightRenderer; +import org.rapla.components.calendarview.swing.AbstractSwingCalendar; +import org.rapla.components.calendarview.swing.SwingMonthView; +import org.rapla.components.calendarview.swing.ViewListener; +import org.rapla.components.util.DateTools; +import org.rapla.entities.domain.Appointment; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.CalendarOptions; +import org.rapla.plugin.abstractcalendar.AbstractRaplaSwingCalendar; +import org.rapla.plugin.abstractcalendar.GroupAllocatablesStrategy; +import org.rapla.plugin.abstractcalendar.RaplaBuilder; +import org.rapla.plugin.abstractcalendar.RaplaCalendarViewListener; +import org.rapla.plugin.abstractcalendar.SwingRaplaBuilder; + + +public class SwingMonthCalendar extends AbstractRaplaSwingCalendar +{ + public SwingMonthCalendar(RaplaContext sm,CalendarModel settings, boolean editable) throws RaplaException { + super( sm, settings, editable); + } + + public static Color DATE_NUMBER_COLOR_HIGHLIGHTED = Color.black; + + protected AbstractSwingCalendar createView(boolean editable) { + boolean showScrollPane = editable; + + /** renderer for weekdays in month-view */ + final WeekendHighlightRenderer weekdayRenderer = new WeekendHighlightRenderer(); + return new SwingMonthView( showScrollPane ) { + private static final long serialVersionUID = 1L; + + protected JComponent createSlotHeader(int weekday) { + JComponent component = super.createSlotHeader( weekday ); + if (isEditable()) { + component.setOpaque(true); + Color color = weekdayRenderer.getBackgroundColor( weekday, 1, 1, 1); + component.setBackground(color); + } + return component; + } + + protected Color getNumberColor( Date date ) + { + boolean today = DateTools.isSameDay(getQuery().today().getTime(), date.getTime()); + if ( today) + { + return DATE_NUMBER_COLOR_HIGHLIGHTED; + } + else + { + return super.getNumberColor( date ); + } + } + }; + } + + protected ViewListener createListener() throws RaplaException { + return new RaplaCalendarViewListener(getContext(), model, view) { + /* if the selcted view is a month-view or compact-view, the start-time will not be the selected time, + * but the time of the start-time of the appointment instead. The start-date is taken from the passed date. + * */ + protected Date calcStartDate(Date date, Appointment appointment) { + return getRaplaLocale().toDate( date, appointment.getStart() ); + } + + }; + } + + protected RaplaBuilder createBuilder() throws RaplaException + { + RaplaBuilder builder = new SwingRaplaBuilder(getContext()); + builder.setRepeatingVisible( view.isEditable()); + builder.setEditingUser( getUser() ); + builder.setExceptionsExcluded( !getCalendarOptions().isExceptionsVisible() || !view.isEditable()); + builder.setFromModel( model, view.getStartDate(), view.getEndDate() ); + + builder.setSmallBlocks( true ); + + GroupAllocatablesStrategy strategy = new GroupAllocatablesStrategy( getRaplaLocale().getLocale() ); + boolean compactColumns = getCalendarOptions().isCompactColumns() || builder.getAllocatables().size() ==0 ; + strategy.setFixedSlotsEnabled( !compactColumns); + builder.setBuildStrategy( strategy ); + + return builder; + } + + protected void configureView() throws RaplaException { + CalendarOptions calendarOptions = getCalendarOptions(); + Set excludeDays = calendarOptions.getExcludeDays(); + + view.setExcludeDays( excludeDays ); + if ( !view.isEditable() ) { + view.setSlotSize( model.getSize()); + } else { + view.setSlotSize( 150 ); + } + view.setToDate(model.getSelectedDate()); + } + + public int getIncrementSize() + { + return Calendar.MONTH; + } + + + +} diff --git a/Rapla/src/org/rapla/plugin/monthview/images/month.png b/Rapla/src/org/rapla/plugin/monthview/images/month.png new file mode 100644 index 0000000..205a545 Binary files /dev/null and b/Rapla/src/org/rapla/plugin/monthview/images/month.png differ diff --git a/Rapla/src/org/rapla/plugin/notification/NotificationOption.java b/Rapla/src/org/rapla/plugin/notification/NotificationOption.java new file mode 100644 index 0000000..9daf6e1 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/notification/NotificationOption.java @@ -0,0 +1,255 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.notification; + +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Set; + +import javax.swing.AbstractAction; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.apache.avalon.framework.configuration.ConfigurationException; +import org.rapla.components.layout.TableLayout; +import org.rapla.entities.configuration.Preferences; +import org.rapla.entities.configuration.RaplaMap; +import org.rapla.entities.domain.Allocatable; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.OptionPanel; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.TreeFactory; +import org.rapla.gui.toolkit.DialogUI; +import org.rapla.gui.toolkit.RaplaTree; + +public class NotificationOption extends RaplaGUIComponent implements OptionPanel,ChangeListener { + final static String ALLOCATIONLISTENERS_CONFIG = NotificationService.ALLOCATIONLISTENERS_CONFIG; + final static String NOTIFY_IF_OWNER_CONFIG = NotificationService.NOTIFY_IF_OWNER_CONFIG; + boolean state = false; + JPanel content= new JPanel(); + RaplaTree treeSelection; + JPanel buttonPanel; + JButton previewButton; + JButton deleteButton; + JButton addButton; + JCheckBox notifyIfOwnerCheckBox; + NotificationAction deleteAction; + NotificationAction addAction; + NotificationAction notifyIfOwnerAction; + Preferences preferences; + + public NotificationOption(RaplaContext sm) throws RaplaException { + super( sm); + setChildBundleName( NotificationPlugin.RESOURCE_FILE); + } + + public void create() throws RaplaException { + treeSelection = new RaplaTree(); + buttonPanel = new JPanel(); + buttonPanel.setLayout(new FlowLayout()); + deleteButton = new JButton(); + addButton = new JButton(); + buttonPanel.add(addButton); + buttonPanel.add(deleteButton); + double[][] sizes = new double[][] { + {5,TableLayout.FILL,5} + ,{TableLayout.PREFERRED,5,TableLayout.PREFERRED,5,TableLayout.PREFERRED,5,TableLayout.FILL} + }; + TableLayout tableLayout = new TableLayout(sizes); + content.setLayout(tableLayout); + content.add(new JLabel(getStringAsHTML("notification.option.description")), "1,0"); + notifyIfOwnerCheckBox = new JCheckBox(); + content.add(notifyIfOwnerCheckBox, "1,2"); + content.add(buttonPanel, "1,4"); + content.add(treeSelection,"1,6"); + + deleteAction = new NotificationAction().setDelete(); + addAction = new NotificationAction().setAdd(); + notifyIfOwnerAction = new NotificationAction().setNotifyIfOwner(); + deleteButton.setAction(deleteAction); + addButton.setAction(addAction); + notifyIfOwnerCheckBox.setAction(notifyIfOwnerAction); + treeSelection.addChangeListener(this); + } + + public JComponent getComponent() { + return content; + } + public String getName(Locale locale) { + return getString("notification_options"); + } + + final private TreeFactory getTreeFactory() { + return (TreeFactory) getService(TreeFactory.ROLE); + } + + private void delete(List selection) throws RaplaException,ConfigurationException { + RaplaMap allocatableList = (RaplaMap) preferences.getEntry(ALLOCATIONLISTENERS_CONFIG); + if ( allocatableList == null) + return; + Set set= new HashSet(); + for (Iterator it = allocatableList.values().iterator();it.hasNext();) { + Object allocatable = it.next(); + if ( !selection.contains( allocatable ) ) { + set.add( allocatable); + } + } + preferences.putEntry( ALLOCATIONLISTENERS_CONFIG, getModification().newRaplaMap( set) ); + update(); + } + + + public void show() throws RaplaException { + if (treeSelection== null) + create(); + update(); + } + + private void update() throws RaplaException { + RaplaMap allocatableList = (RaplaMap) preferences.getEntry(ALLOCATIONLISTENERS_CONFIG); + notifyIfOwnerAction.setEnabled(false); + + boolean notify = preferences.getEntryAsBoolean( NOTIFY_IF_OWNER_CONFIG, false); + notifyIfOwnerCheckBox.setSelected(notify); + notifyIfOwnerAction.setEnabled(true); + + treeSelection.getTree().setCellRenderer(getTreeFactory().createRenderer()); + Allocatable[] allocatables = Allocatable.ALLOCATABLE_ARRAY; + if ( allocatableList!= null) { + Set set = new HashSet( allocatableList.values()); + allocatables = (Allocatable[]) set.toArray( Allocatable.ALLOCATABLE_ARRAY); + } + + treeSelection.exchangeTreeModel(getTreeFactory().createClassifiableModel(allocatables)); + } + + + + private void updateNotifyIfOwner() throws RaplaException, ConfigurationException { + preferences.putEntry( NOTIFY_IF_OWNER_CONFIG, String.valueOf(notifyIfOwnerCheckBox.isSelected())); + } + + private void add(Allocatable allocatable) throws ConfigurationException, RaplaException { + RaplaMap raplaEntityList = (RaplaMap)preferences.getEntry( ALLOCATIONLISTENERS_CONFIG ); + Set list; + if ( raplaEntityList != null ){ + list = new HashSet(raplaEntityList.values()); + } else { + list = new HashSet(); + } + list.add( allocatable ); + getLogger().info("Adding notificationEntry " + allocatable); + preferences.putEntry( ALLOCATIONLISTENERS_CONFIG ,getModification().newRaplaMap( list )); + update(); + } + + + public void setPreferences(Preferences preferences) { + this.preferences = preferences; + } + + public void commit() { + } + + + public void stateChanged(ChangeEvent e) { + deleteAction.update(); + //addAction.update(); + } + + class NotificationAction extends AbstractAction { + private static final long serialVersionUID = 1L; + + List selection; + int ADD = 1; + int DELETE = 2; + int NOTIFY_IF_OWNER = 3; + int type; + + NotificationAction setDelete() { + putValue(NAME,getString("delete")); + putValue(SMALL_ICON,getIcon("icon.delete")); + setEnabled(false); + type = DELETE; + return this; + } + + NotificationAction setAdd() { + putValue(NAME,getString("add")); + putValue(SMALL_ICON,getIcon("icon.new")); + type = ADD; + return this; + } + + NotificationAction setNotifyIfOwner() { + putValue(NAME,getStringAsHTML("notify_if_owner")); + type = NOTIFY_IF_OWNER; + return this; + } + + void update() { + selection = treeSelection.getSelectedElements(); + setEnabled(selection.size() > 0); + } + + public void actionPerformed(ActionEvent evt) { + try { + if (type == DELETE) { + delete(selection); + } else if (type == NOTIFY_IF_OWNER) { + updateNotifyIfOwner(); + } else if (type == ADD) { + showAddDialog(); + } + } catch (Exception ex) { + showException(ex,getComponent()); + } + } + + private void showAddDialog() throws RaplaException, ConfigurationException { + DialogUI dialog; + RaplaTree treeSelection = new RaplaTree(); + treeSelection.setMultiSelect(true); + treeSelection.getTree().setCellRenderer(getTreeFactory().createRenderer()); + + treeSelection.exchangeTreeModel(getTreeFactory().createClassifiableModel(getQuery().getAllocatables())); + treeSelection.setMinimumSize(new java.awt.Dimension(300, 200)); + treeSelection.setPreferredSize(new java.awt.Dimension(400, 260)); + dialog = DialogUI.create( + getContext() + ,getComponent() + ,true + ,treeSelection + ,new String[] { getString("add"),getString("cancel")}); + dialog.setTitle(""); + dialog.start(); + if (dialog.getSelectedIndex() == 0) { + Iterator it = treeSelection.getSelectedElements().iterator(); + while (it.hasNext()) { + add((Allocatable) it.next()); + } + } + } + } + +} diff --git a/Rapla/src/org/rapla/plugin/notification/NotificationPlugin.java b/Rapla/src/org/rapla/plugin/notification/NotificationPlugin.java new file mode 100644 index 0000000..e4f7120 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/notification/NotificationPlugin.java @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.notification; + +import org.apache.avalon.framework.configuration.Configuration; +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.components.xmlbundle.impl.I18nBundleImpl; +import org.rapla.framework.Container; +import org.rapla.framework.PluginDescriptor; +import org.rapla.plugin.RaplaExtensionPoints; + +/** Users can subscribe for allocation change notifications for selected resources or persons.*/ + +public class NotificationPlugin implements PluginDescriptor +{ + public static final String RESOURCE_FILE =NotificationPlugin.class.getPackage().getName() + ".NotificationResources"; + public static final String PLUGIN_CLASS = NotificationPlugin.class.getName(); + static boolean ENABLE_BY_DEFAULT = false; + + public String toString() { + return "Notification Service"; + } + + public void provideServices(Container container, Configuration config) { + if ( !config.getAttributeAsBoolean("enabled", ENABLE_BY_DEFAULT) ) + return; + + container.addContainerProvidedComponent( I18nBundle.ROLE, I18nBundleImpl.class.getName(), RESOURCE_FILE,I18nBundleImpl.createConfig( RESOURCE_FILE ) ); + container.addContainerProvidedComponent( RaplaExtensionPoints.USER_OPTION_PANEL_EXTENSION, NotificationOption.class.getName(),PLUGIN_CLASS, config); + container.addContainerProvidedComponent( RaplaExtensionPoints.SERVER_EXTENSION, NotificationService.class.getName(), PLUGIN_CLASS,config); + } + + public Object getPluginMetaInfos( String key ) + { + return null; + } + +} + diff --git a/Rapla/src/org/rapla/plugin/notification/NotificationResources.xml b/Rapla/src/org/rapla/plugin/notification/NotificationResources.xml new file mode 100644 index 0000000..29db987 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/notification/NotificationResources.xml @@ -0,0 +1,80 @@ + + + + + Notification options + Benachrichtigungs Optionen + Options de notification + Notificaciones + + + Rapla: Allocation change of [{0}] + Rapla: Reservierungsbenachrichtigung [{0}] + Rapla : Changement d'allocation pour [{0}] + Rapla : Cambio de asignación para [{0}] + + + + The following allocation changes have been made: + Die folgenden Belegungsänderungen wurden vorgenommen: + Les changement d'allocation suivants ont été faits: + Se han hecho los siguientes cambios de asignación: + + + + New appointment for [{0}]: + Neuer Termin für [{0}]: + Nouvelle réservation pour [{0}] : + Nueva cita para [{0}] + + + + Appointment changed for [{0}] from: + Termin verändert für [{0}] von: + Changement d''horaire pour [{0}] c''était : + Cambio de cita para [{0}] + + + + Removed appointment for [{0}]: + Termin für [{0}] gelöscht: + Libération de [{0}] le : + Se eliminó la cita para [{0}] + + + to + nach + cela devient : + para + + + Modified by {0} + verändert durch {0} + Modifié par {0} + Modificado por {0} + + + Complete event + Komplette Veranstaltung + Description complète de l'évènement + Descriptción completa del evento + + + + Also notify on modification of own events. + Benachrichtige auch bei Änderungen an eigenen Veranstaltungen. + Je veux être notifié pour les changements faits par moi-même + Quiero ser notificado de los cambios hechos por mí mismo + + + Mail for every allocation change of the listed resources and persons + Mail bei Belegungsänderung von folgenden Ressourcen und Personen + Envoit de Mail à chaque changement d'allocation des resources/personnes listées + Envío de correo a cada cambio de asignación de recursos/personas listado + + + + + + + diff --git a/Rapla/src/org/rapla/plugin/notification/NotificationService.java b/Rapla/src/org/rapla/plugin/notification/NotificationService.java new file mode 100644 index 0000000..fbe5c2f --- /dev/null +++ b/Rapla/src/org/rapla/plugin/notification/NotificationService.java @@ -0,0 +1,349 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.notification; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.rapla.components.util.Command; +import org.rapla.components.util.CommandQueue; +import org.rapla.entities.User; +import org.rapla.entities.configuration.Preferences; +import org.rapla.entities.configuration.RaplaMap; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Repeating; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.Classification; +import org.rapla.facade.AllocationChangeEvent; +import org.rapla.facade.AllocationChangeListener; +import org.rapla.facade.ClientFacade; +import org.rapla.facade.RaplaComponent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.plugin.mail.MailPlugin; +import org.rapla.plugin.mail.MailToUserInterface; + +/** Sends Notification Mails on allocation change.*/ + +public class NotificationService extends RaplaComponent + implements + AllocationChangeListener +{ + public final static String ALLOCATIONLISTENERS_CONFIG = "org.rapla.plugin.notification.allocationlisteners"; + public final static String NOTIFY_IF_OWNER_CONFIG = "org.rapla.plugin.notification.notify_if_owner"; + + ClientFacade clientFacade; + MailToUserInterface mailToUserInterface; + protected CommandQueue mailQueue; + + public NotificationService(RaplaContext sm) throws RaplaException + { + super( sm); + + mailToUserInterface = (MailToUserInterface) sm.lookup( MailToUserInterface.ROLE + "/" + MailPlugin.MAIL_ON_LOCALHOST ); + clientFacade = (ClientFacade) sm.lookup(ClientFacade.ROLE); + + setChildBundleName( NotificationPlugin.RESOURCE_FILE ); + + mailQueue = org.rapla.components.util.CommandQueue.createCommandQueue(); + clientFacade.addAllocationChangedListener(this); + getLogger().info("NotificationServer Plugin started"); + } + + public void changed(AllocationChangeEvent[] changeEvents) { + try { + getLogger().debug("TRIGGERD"); + + User[] users = clientFacade.getUsers(); + List mailList = new ArrayList(); + for ( int i=0;i< users.length;i++) { + User user = users[i]; + Preferences preferences = clientFacade.getPreferences(user); + Map allocatableMap ; + if (preferences != null && preferences.getEntry(ALLOCATIONLISTENERS_CONFIG)!= null ) { + allocatableMap = ((RaplaMap)(preferences.getEntry(ALLOCATIONLISTENERS_CONFIG))); + }else { + allocatableMap = new HashMap(); + } + + + if ( allocatableMap.size()> 0) + { + boolean notifyIfOwner = preferences.getEntryAsBoolean(NOTIFY_IF_OWNER_CONFIG, false); + AllocationMail mail = getAllocationMail( new HashSet(allocatableMap.values()), changeEvents, preferences.getOwner(),notifyIfOwner); + if (mail != null) { + mailList.add(mail); + } + + } + } + MailCommand mailCommand = new MailCommand(mailList); + mailQueue.enqueue(mailCommand); + } catch (RaplaException ex) { + getLogger().error("Can't trigger notification service." + ex.getMessage(),ex); + } + } + + public boolean isInvokedOnAWTEventQueue() { + return false; + } + + AllocationMail getAllocationMail(Collection allocatables, AllocationChangeEvent[] changeEvents, User owner,boolean notifyIfOwner) throws RaplaException { + + HashMap reservationMap = null; + HashSet changedAllocatables = null; + for ( int i = 0; i< changeEvents.length; i++) { + if (reservationMap == null) + reservationMap = new HashMap(4); + AllocationChangeEvent event = changeEvents[i]; + Reservation reservation = event.getNewReservation(); + if (!allocatables.contains(event.getAllocatable())) + continue; + if (!notifyIfOwner && owner.equals(reservation.getOwner())) + continue; + List eventList = (List)reservationMap.get(reservation); + if (eventList == null) { + eventList = new ArrayList(3); + reservationMap.put(reservation,eventList); + changedAllocatables = new HashSet(); + } + changedAllocatables.add(event.getAllocatable()); + eventList.add(event); + } + if ( reservationMap == null) { + return null; + } + Set keySet = reservationMap.keySet(); + // Check if we have any notifications. + if (keySet.size() == 0) + return null; + + AllocationMail mail = new AllocationMail(); + StringBuffer buf = new StringBuffer(); + buf.append(getString("mail_body") + "\n"); + Iterator it = keySet.iterator(); + while (it.hasNext()) { + Reservation reservation = (Reservation)it.next(); + List eventList = (List) reservationMap.get(reservation); + printEvents(buf,reservation,eventList); + buf.append("\n\n"); + } + + it = changedAllocatables.iterator(); + StringBuffer allocatableNames = null; + while (it.hasNext()) { + if (allocatableNames == null) { + allocatableNames = new StringBuffer(); + } else { + allocatableNames.append(", "); + } + allocatableNames.append(((Allocatable)it.next()).getName(getLocale())); + } + mail.subject = getI18n().format("mail_subject",allocatableNames.toString()); + mail.body = buf.toString(); + mail.recipient = owner.getUsername(); + return mail; + } + + private void printEvents(StringBuffer buf,Reservation reservation,List eventList) { + buf.append("\n"); + buf.append("-----------"); + buf.append(getString("changes")); + buf.append("-----------"); + buf.append("\n"); + buf.append("\n"); + buf.append(getString("reservation")); + buf.append(": "); + buf.append(reservation.getName(getLocale())); + buf.append("\n"); + buf.append("\n"); + Iterator it = eventList.iterator(); + boolean removed = true; + while (it.hasNext()) { + AllocationChangeEvent event = (AllocationChangeEvent)it.next(); + if (!event.getType().equals( AllocationChangeEvent.REMOVE )) + removed = false; + + buf.append(getI18n().format("appointment." + event.getType() + ,event.getAllocatable().getName(getLocale())) + ); + if (!event.getType().equals(AllocationChangeEvent.ADD )) { + printAppointment (buf, event.getOldAppointment() ); + } + if (event.getType().equals( AllocationChangeEvent.CHANGE )) { + buf.append(getString("moved_to")); + } + if (!event.getType().equals( AllocationChangeEvent.REMOVE )) { + printAppointment (buf, event.getNewAppointment() ); + } + if ( event.getUser() != null) { + buf.append("\n"); + buf.append( getI18n().format("modified_by", event.getUser().getUsername() ) ); + } + buf.append("\n"); + buf.append("\n"); + } + + if (removed) + return; + + buf.append("-----------"); + buf.append(getString("complete_reservation")); + buf.append("-----------"); + buf.append("\n"); + buf.append("\n"); + buf.append(getString("reservation.owner")); + buf.append(": "); + buf.append(reservation.getOwner().getUsername()); + buf.append(" <"); + buf.append(reservation.getOwner().getName()); + buf.append(">"); + buf.append("\n"); + buf.append(getString("reservation_type")); + buf.append(": "); + Classification classification = reservation.getClassification(); + buf.append( classification.getType().getName(getLocale()) ); + Attribute[] attributes = classification.getAttributes(); + for (int i=0; i< attributes.length; i++) { + Object value = classification.getValue(attributes[i]); + if (value == null) + continue; + buf.append("\n"); + buf.append(attributes[i].getName(getLocale())); + buf.append(": "); + buf.append(classification.getValueAsString(attributes[i], getLocale())); + } + + Allocatable[] resources = reservation.getResources(); + if (resources.length>0) { + buf.append("\n"); + buf.append( getString("resources")); + buf.append( ": "); + printAllocatables(buf,reservation,resources); + } + Allocatable[] persons = reservation.getPersons(); + if (persons.length>0) { + buf.append("\n"); + buf.append( getString("persons")); + buf.append( ": "); + printAllocatables(buf,reservation,persons); + } + Appointment[] appointments = reservation.getAppointments(); + if (appointments.length>0) { + buf.append("\n"); + buf.append("\n"); + buf.append( getString("appointments")); + buf.append( ": "); + buf.append("\n"); + } + for (int i = 0;i0) + buf.append(", "); + buf.append( getAppointmentFormater().getShortSummary( restriction[i]) ); + } + buf.append(")"); + } + + + class AllocationMail { + String recipient; + String subject; + String body; + public String toString() { + return "TO Username: " + recipient + "\n" + + "Subject: " + subject + "\n" + + body; + } + } + + final class MailCommand implements Command { + List mailList; + public MailCommand(List mailList) { + this.mailList = mailList; + } + + public void execute() { + Iterator it = mailList.iterator(); + while (it.hasNext()) { + AllocationMail mail = (AllocationMail) it.next(); + if (getLogger().isDebugEnabled()) + getLogger().debug("Sending mail " + mail.toString()); + else if (getLogger().isInfoEnabled()) + getLogger().info("AllocationChange. Sending mail to " + mail.recipient); + try { + mailToUserInterface.sendMail(mail.recipient, mail.subject, mail.body ); + getLogger().info("AllocationChange. Mail sent."); + } catch (RaplaException ex) { + getLogger().error("Could not send mail to " + mail.recipient + " Cause: " + ex.getMessage(), ex); + } + } + } + + }; + + +} + diff --git a/Rapla/src/org/rapla/plugin/occupationview/AllocationCell.java b/Rapla/src/org/rapla/plugin/occupationview/AllocationCell.java new file mode 100644 index 0000000..3ca3dec --- /dev/null +++ b/Rapla/src/org/rapla/plugin/occupationview/AllocationCell.java @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2011 Bob Jordaens | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.plugin.occupationview; + +import org.rapla.entities.domain.Allocatable; + +public class AllocationCell { + + Allocatable allocatable; + + public AllocationCell(Allocatable allocatable) { + this. allocatable = allocatable; + } +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/plugin/occupationview/ColumnGroup.java b/Rapla/src/org/rapla/plugin/occupationview/ColumnGroup.java new file mode 100644 index 0000000..bb8df25 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/occupationview/ColumnGroup.java @@ -0,0 +1,178 @@ +package org.rapla.plugin.occupationview; + +import java.awt.Component; +import java.awt.Dimension; +import java.util.Iterator; +import java.util.Vector; +import javax.swing.JLabel; +import javax.swing.JTable; +import javax.swing.UIManager; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.JTableHeader; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; + + +public class ColumnGroup { + /** + * Cell renderer for group header. + */ + protected TableCellRenderer renderer; + /** + * Holds the TableColumn or ColumnGroup objects contained + * within this ColumnGroup instance. + */ + protected Vector v; + /** + * The ColumnGroup instance name. + */ + protected String text; + /** + * The margin to use for renderering. + */ + protected int margin=0; + + /** + * Standard ColumnGroup constructor. + * @param text Name of the ColumnGroup which will be displayed + * when the ColumnGroup is renderered. + */ + public ColumnGroup(String text) { + this(null,text); + } + + /** + * Standard ColumnGroup constructor. + * @param renderer a TableCellRenderer for the group. + * @param text Name of the ColumnGroup which will be displayed + * when the ColumnGroup is renderered. + */ + public ColumnGroup(TableCellRenderer renderer,String text) { + if (renderer == null) { + this.renderer = new DefaultTableCellRenderer() { + /** + * + */ + private static final long serialVersionUID = 1L; + + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, int row, int column) { + JTableHeader header = table.getTableHeader(); + if (header != null) { + setForeground(header.getForeground()); + setBackground(header.getBackground()); + setFont(header.getFont()); + } + setHorizontalAlignment(JLabel.CENTER); + setText((value == null) ? "" : value.toString()); + setBorder(UIManager.getBorder("TableHeader.cellBorder")); + return this; + } + }; + } else { + this.renderer = renderer; + } + this.text = text; + v = new Vector(); + } + + + /** + * Add a TableColumn or ColumnGroup object to the + * ColumnGroup instance. + * @param obj TableColumn or ColumnGroup + */ + public void add(Object obj) { + if (obj == null) { return; } + v.addElement(obj); + } + + + /** + * Get the ColumnGroup list containing the required table + * column. + * @param g vector to populate with the ColumnGroup/s + * @param c TableColumn + * @return Vector containing the ColumnGroup/s + */ + public Vector getColumnGroups(TableColumn c, Vector g) { + g.addElement(this); + if (v.contains(c)) return g; + Iterator iter = v.iterator(); + while (iter.hasNext()) { + Object obj = iter.next(); + if (obj instanceof ColumnGroup) { + Vector groups = + (Vector)((ColumnGroup)obj).getColumnGroups(c,(Vector)g.clone()); + if (groups != null) return groups; + } + } + return null; + } + + /** + * Returns the TableCellRenderer for the ColumnGroup. + * @return the TableCellRenderer + */ + public TableCellRenderer getHeaderRenderer() { + return renderer; + } + + /** + * Set the TableCellRenderer for this ColumnGroup. + * @param renderer the renderer to use + */ + public void setHeaderRenderer(TableCellRenderer renderer) { + if (renderer != null) { + this.renderer = renderer; + } + } + + /** + * Get the ColumnGroup header value. + * @return the value. + */ + public Object getHeaderValue() { + return text; + } + + /** + * Get the dimension of this ColumnGroup. + * @param table the table the header is being rendered in + * @return the dimension of the ColumnGroup + */ + public Dimension getSize(JTable table) { + Component comp = renderer.getTableCellRendererComponent( + table, getHeaderValue(), false, false,-1, -1); + int height = comp.getPreferredSize().height; + int width = 0; + Iterator iter = v.iterator(); + while (iter.hasNext()) { + Object obj = iter.next(); + if (obj instanceof TableColumn) { + TableColumn aColumn = (TableColumn)obj; + width += aColumn.getWidth(); + } else { + width += ((ColumnGroup)obj).getSize(table).width; + } + } + return new Dimension(width, height); + } + + /** + * Sets the margin that ColumnGroup instance will use and all + * held TableColumns and/or ColumnGroups. + * @param margin the margin + */ + public void setColumnMargin(int margin) { + this.margin = margin; + Iterator iter = v.iterator(); + while (iter.hasNext()) { + Object obj = iter.next(); + if (obj instanceof ColumnGroup) { + ((ColumnGroup)obj).setColumnMargin(margin); + } + } + } +} + diff --git a/Rapla/src/org/rapla/plugin/occupationview/EndOfLifeArchiver.java b/Rapla/src/org/rapla/plugin/occupationview/EndOfLifeArchiver.java new file mode 100644 index 0000000..0a8b764 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/occupationview/EndOfLifeArchiver.java @@ -0,0 +1,24 @@ +package org.rapla.plugin.occupationview; + +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.AttributeType; +import org.rapla.entities.dynamictype.Classification; + +public class EndOfLifeArchiver { + + static public AttributeType getEndOfLifeType(Allocatable alloc) { + Classification classification = alloc.getClassification(); + if ( classification == null) + return null; + final Attribute attribute = classification.getType().getAttribute("_endoflife"); + if(attribute == null) + return null; + AttributeType type = attribute.getType(); + if (type.equals(AttributeType.BOOLEAN) || type.equals(AttributeType.DATE)) + return type; + else + return null; + } + +} diff --git a/Rapla/src/org/rapla/plugin/occupationview/GroupableTableColumnModel.java b/Rapla/src/org/rapla/plugin/occupationview/GroupableTableColumnModel.java new file mode 100644 index 0000000..3ef8343 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/occupationview/GroupableTableColumnModel.java @@ -0,0 +1,78 @@ +package org.rapla.plugin.occupationview; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Vector; +import javax.swing.table.DefaultTableColumnModel; +import javax.swing.table.TableColumn; + +// Steve Webb 16/09/04 swebb99_uk@hotmail.com + +/** + * Class which extends the functionality of DefaultColumnTableModel to + * also provide capabilities to group columns. This can be used for + * instance to aid in the layout of groupable table headers. + */ +public class GroupableTableColumnModel extends DefaultTableColumnModel { + + /** + * + */ + private static final long serialVersionUID = 1L; + /** + * Hold the list of ColumnGroups which define what group each normal + * column is within, if any. + */ + protected ArrayList columnGroups = new ArrayList(); + + + /** + * Add a new columngroup. + * @param columnGroup new ColumnGroup + */ + public void addColumnGroup(ColumnGroup columnGroup) { + columnGroups.add(columnGroup); + } + + /** + * Provides an Iterator to iterate over the + * ColumnGroup list. + * @return Iterator over ColumnGroups + */ + public Iterator columnGroupIterator() { + return columnGroups.iterator(); + } + + /** + * Returns a ColumnGroup specified by an index. + * @param index index of ColumnGroup + * @return ColumnGroup + */ + public ColumnGroup getColumnGroup(int index) { + if(index >= 0 && index < columnGroups.size()) { + return (ColumnGroup)columnGroups.get(index); + } + return null; + } + + /** + * Provides and iterator for accessing the ColumnGroups + * associated with a column. + * @param col Column + * @return ColumnGroup iterator + */ + public Iterator getColumnGroups(TableColumn col) { + if (columnGroups.isEmpty()) return null; + Iterator iter = columnGroups.iterator(); + while (iter.hasNext()) { + ColumnGroup cGroup = (ColumnGroup)iter.next(); + Vector v_ret = (Vector)cGroup.getColumnGroups(col,new Vector()); + if (v_ret != null) { + return v_ret.iterator(); + } + } + return null; + } +} + + diff --git a/Rapla/src/org/rapla/plugin/occupationview/GroupableTableHeader.java b/Rapla/src/org/rapla/plugin/occupationview/GroupableTableHeader.java new file mode 100644 index 0000000..0ee2605 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/occupationview/GroupableTableHeader.java @@ -0,0 +1,42 @@ +package org.rapla.plugin.occupationview; + +import java.util.Iterator; + +import javax.swing.table.JTableHeader; + + +/** + * This is the object which manages the header of the JTable and + * also provides functionality for groupable headers. + */ +public class GroupableTableHeader extends JTableHeader { + + /** + * + */ + private static final long serialVersionUID = 1L; + /** + * Constructs a GroupableTableHeader which is initialized with cm as the + * column model. If cm is null this method will initialize the table header + * with a default TableColumnModel. + * @param model the column model for the table + */ + public GroupableTableHeader(GroupableTableColumnModel model) { + super(model); + setUI(new GroupableTableHeaderUI()); + setReorderingAllowed(false); + } + + /** + * Sets the margins correctly for all groups within + * the header. + */ + public void setColumnMargin() { + int columnMargin = getColumnModel().getColumnMargin(); + Iterator iter = ((GroupableTableColumnModel)columnModel).columnGroupIterator(); + while (iter.hasNext()) { + ColumnGroup cGroup = (ColumnGroup)iter.next(); + cGroup.setColumnMargin(columnMargin); + } + } +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/plugin/occupationview/GroupableTableHeaderUI.java b/Rapla/src/org/rapla/plugin/occupationview/GroupableTableHeaderUI.java new file mode 100644 index 0000000..d246dc9 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/occupationview/GroupableTableHeaderUI.java @@ -0,0 +1,174 @@ +package org.rapla.plugin.occupationview; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Vector; +import javax.swing.JComponent; +import javax.swing.plaf.basic.BasicTableHeaderUI; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; +import javax.swing.table.TableColumnModel; + + +/** + * This class paints groupable header cells. These can be a combination of + * normal header cells and groupable cells. + */ +public class GroupableTableHeaderUI extends BasicTableHeaderUI { + + /** + * Contains a list of ColumnGroups that have already been painted + * in the current paint request. + */ + protected Vector paintedGroups = new Vector(); + + /** + * Paint a representation of the table header. + * @param g the Graphics context in which to paint + * @param c the component being painted; this argument is often ignored + */ + public void paint(Graphics g, JComponent c) { + Rectangle clipBounds = g.getClipBounds(); + GroupableTableColumnModel cm = (GroupableTableColumnModel)header.getColumnModel(); + if (cm == null) return; + ((GroupableTableHeader)header).setColumnMargin(); + int column = 0; + Dimension size = header.getSize(); + Rectangle cellRect = new Rectangle(0, 0, size.width, size.height); + Hashtable h = new Hashtable(); + //int columnMargin = cm.getColumnMargin(); + + Enumeration columns = cm.getColumns(); + while (columns.hasMoreElements()) { + cellRect.height = size.height; + cellRect.y = 0; + TableColumn aColumn = (TableColumn)columns.nextElement(); + Iterator colGrpIter = cm.getColumnGroups(aColumn); + if (colGrpIter != null) { + int groupHeight = 0; + while (colGrpIter.hasNext()) { + ColumnGroup cGroup = (ColumnGroup)colGrpIter.next(); + Rectangle groupRect = (Rectangle)h.get(cGroup); + if (groupRect == null) { + groupRect = new Rectangle(cellRect); + Dimension d = cGroup.getSize(header.getTable()); + groupRect.width = d.width; + groupRect.height = d.height; + h.put(cGroup, groupRect); + } + if(!paintedGroups.contains(cGroup)) { + paintCell(g, groupRect, cGroup); + paintedGroups.add(cGroup); + } + groupHeight += groupRect.height; + cellRect.height = size.height - groupHeight; + cellRect.y = groupHeight; + } + } + cellRect.width = aColumn.getWidth(); + if (cellRect.intersects(clipBounds)) { + paintCell(g, cellRect, column); + } + cellRect.x += cellRect.width; + column++; + } + paintedGroups.clear(); + } + + /** + * Paints a header column cell. + * @param g Graphics context + * @param cellRect The rectangle to contain the cell + * @param columnIndex The header column to be painted + */ + private void paintCell(Graphics g, Rectangle cellRect, int columnIndex) { + TableColumn aColumn = header.getColumnModel().getColumn(columnIndex); + TableCellRenderer renderer = aColumn.getHeaderRenderer(); + if(renderer == null) { + renderer = header.getDefaultRenderer(); + } + Component component = renderer.getTableCellRendererComponent( + header.getTable(), aColumn.getHeaderValue(),false, false, -1, columnIndex); + rendererPane.add(component); + rendererPane.paintComponent(g, component, header, cellRect.x, cellRect.y, + cellRect.width, cellRect.height, true); + } + + /** + * Paint group column cell. + * @param g Graphics context + * @param cellRect Rectangle that the cell with be painted in + * @param cGroup Current column group + */ + private void paintCell(Graphics g, Rectangle cellRect,ColumnGroup cGroup) { + TableCellRenderer renderer = cGroup.getHeaderRenderer(); + Component component = renderer.getTableCellRendererComponent( + header.getTable(), cGroup.getHeaderValue(),false, false, -1, -1); + rendererPane.add(component); + rendererPane.paintComponent(g, component, header, cellRect.x, cellRect.y, + cellRect.width, cellRect.height, true); + } + + /** + * Calculate and return the height of the header. + * @return Header Height + */ + private int getHeaderHeight() { + int height = 0; + GroupableTableColumnModel columnModel = (GroupableTableColumnModel)header.getColumnModel(); + for(int column = 0; column < columnModel.getColumnCount(); column++) { + TableColumn aColumn = columnModel.getColumn(column); + TableCellRenderer renderer = aColumn.getHeaderRenderer(); + if(renderer == null) { + renderer = header.getDefaultRenderer(); + } + Component comp = renderer.getTableCellRendererComponent( + header.getTable(), aColumn.getHeaderValue(), false, false,-1, column); + int cHeight = comp.getPreferredSize().height; + Iterator iter = columnModel.getColumnGroups(aColumn); + if (iter != null) { + while (iter.hasNext()) { + ColumnGroup cGroup = (ColumnGroup)iter.next(); + cHeight += cGroup.getSize(header.getTable()).height; + } + } + height = Math.max(height, cHeight); + } + return height; + } + + /** + * Calculate and return the dimension of the header. + * @param width Starting width to be used. + * @return Dimension of the header + */ + private Dimension createHeaderSize(long width) { + TableColumnModel columnModel = header.getColumnModel(); + width += columnModel.getColumnMargin() * columnModel.getColumnCount(); + if (width > Integer.MAX_VALUE) { + width = Integer.MAX_VALUE; + } + return new Dimension((int)width, getHeaderHeight()); + } + + /** + * Invokes the getPreferredSize method on each UI handled by this object. + * @param c the component whose preferred size is being queried; this argument is ignored. + * @return the dimension of the whole header + */ + public Dimension getPreferredSize(JComponent c) { + long width = 0; + Enumeration columns = header.getColumnModel().getColumns(); + while (columns.hasMoreElements()) { + TableColumn aColumn = (TableColumn)columns.nextElement(); + width = width + aColumn.getPreferredWidth(); + } + return createHeaderSize(width); + } +} + diff --git a/Rapla/src/org/rapla/plugin/occupationview/OccupationCell.java b/Rapla/src/org/rapla/plugin/occupationview/OccupationCell.java new file mode 100644 index 0000000..64a3f9a --- /dev/null +++ b/Rapla/src/org/rapla/plugin/occupationview/OccupationCell.java @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2011 Bob Jordaens | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.plugin.occupationview; + +import org.rapla.entities.domain.Reservation; + +public class OccupationCell { + + char leftBound; + char rightBound; + // 0 : Free Cell + // -1 : Forbidden Cell Resource is not available at all, out of order or in maintenance + // -2 : FirstFit Cell + private int typeId; + Reservation reservation; + public Reservation getReservation() { + return reservation; + } + public OccupationCell(char leftBound, int typeId, char rightBound) + { + this(leftBound, typeId, rightBound, null); + } + public OccupationCell(char leftBound, int typeId, char rightBound, Reservation reservation) { + this.leftBound = leftBound; + this.typeId = typeId; + this.rightBound = rightBound; + this.reservation = reservation; + } + + + public int getTypeId() { + return typeId; + } +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/plugin/occupationview/OccupationFactory.java b/Rapla/src/org/rapla/plugin/occupationview/OccupationFactory.java new file mode 100644 index 0000000..ff523b4 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/occupationview/OccupationFactory.java @@ -0,0 +1,72 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2011 Robert Jordaens | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.occupationview; + +import javax.swing.Icon; + +import org.rapla.facade.RaplaComponent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.SwingCalendarView; +import org.rapla.gui.ViewFactory; +import org.rapla.gui.images.Images; +import org.rapla.servletpages.RaplaPageGenerator; + +public class OccupationFactory extends RaplaComponent implements ViewFactory +{ + public OccupationFactory( RaplaContext context ) throws RaplaException + { + super( context ); + setChildBundleName( OccupationPlugin.RESOURCE_FILE); + } + + public final static String OCCUPATION_VIEW = "occupation"; + + public SwingCalendarView createSwingView(RaplaContext context, CalendarModel model, boolean editable) throws RaplaException + { + return new SwingOccupation( context, model, editable); + } + + public RaplaPageGenerator createHTMLView(RaplaContext context, CalendarModel model) throws RaplaException + { + return null; + } + + public String getViewId() + { + return OCCUPATION_VIEW; + } + + public String getName() + { + return getString(OCCUPATION_VIEW); + } + + Icon icon; + public Icon getIcon() + { + if ( icon == null) { + icon = Images.getIcon("/org/rapla/plugin/occupationview/images/table.png"); + } + return icon; + } + + public String getMenuSortKey() { + return "O"; + } + + public boolean isPrintSupported() { + return false; + } +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/plugin/occupationview/OccupationOption.java b/Rapla/src/org/rapla/plugin/occupationview/OccupationOption.java new file mode 100644 index 0000000..97bf407 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/occupationview/OccupationOption.java @@ -0,0 +1,123 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2011 Bob Jordaens | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.occupationview; + +import java.awt.Component; +import java.util.Locale; + +import javax.swing.DefaultListCellRenderer; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; + +import org.rapla.components.calendar.RaplaNumber; +import org.rapla.components.layout.TableLayout; +import org.rapla.entities.configuration.Preferences; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.OptionPanel; +import org.rapla.gui.RaplaGUIComponent; + +public class OccupationOption extends RaplaGUIComponent implements OptionPanel { + + public final static String MONTHS = "org.rapla.plugin.occupation.Months"; + JPanel panel = new JPanel(); + RaplaNumber months = new RaplaNumber(new Double(1),new Double(0),new Double(12), false); + + Preferences preferences; + +// BJO 00000101 + public final static String START_DAY = "org.rapla.plugin.occupation.StartDay"; + public final static String FIRSTDAY = "FirstDay"; + public final static String TODAY = "ToDay"; + JComboBox startDaySelector = new JComboBox( new String[] { + FIRSTDAY + , TODAY + } + ); +// BJO 00000101 + public OccupationOption(RaplaContext sm) throws RaplaException { + super( sm); + setChildBundleName( OccupationPlugin.RESOURCE_FILE); + } + + public void create() throws RaplaException { + + double pre = TableLayout.PREFERRED; + double fill = TableLayout.FILL; + // rows = 1 columns = 2 + panel.setLayout( new TableLayout(new double[][] { + {pre, 5, pre, 5, pre} + , {pre, 5, pre, 5, fill} + } + )); + + panel.add( new JLabel(getString("horizon")),"0,0" ); + panel.add( months,"2,0"); + +// BJO 00000101 + ListRenderer listRenderer = new ListRenderer(); + panel.add( new JLabel(getString("startday")),"0,2" ); + panel.add(startDaySelector,"2,2"); + startDaySelector.setRenderer( listRenderer ); +// BJO 00000101 + } + + public JComponent getComponent() { + return panel; + } + + public String getName(Locale locale) { + return getString("occupation"); + } + + public void setPreferences( Preferences preferences) { + this.preferences = preferences; + } + + public void show() throws RaplaException { + int times = preferences.getEntryAsInteger( MONTHS,0); + months.setNumber(times); + + String day = preferences.getEntryAsString( START_DAY, TODAY ); + startDaySelector.setSelectedItem( day.equals(FIRSTDAY) ? FIRSTDAY : TODAY ); + + create(); + } + + public void commit() { + int times = months.getNumber().intValue(); + preferences.putEntry( MONTHS,"" + times); + + String day = (String) startDaySelector.getSelectedItem(); + preferences.putEntry( START_DAY, day); + } + + private class ListRenderer extends DefaultListCellRenderer { + + private static final long serialVersionUID = 1L; + + public ListRenderer() { + } + + public Component getListCellRendererComponent(JList list,Object value, int index, boolean isSelected, boolean cellHasFocus) { + if ( value != null) { + setText(getString( (String) value )); + + } + return this; + } + } +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/plugin/occupationview/OccupationPlugin.java b/Rapla/src/org/rapla/plugin/occupationview/OccupationPlugin.java new file mode 100644 index 0000000..1128e61 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/occupationview/OccupationPlugin.java @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2011 Bob Jordaens | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.occupationview; + +import org.apache.avalon.framework.configuration.Configuration; +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.components.xmlbundle.impl.I18nBundleImpl; +import org.rapla.framework.Container; +import org.rapla.framework.PluginDescriptor; +import org.rapla.plugin.RaplaExtensionPoints; +import org.rapla.plugin.RaplaPluginMetaInfo; + +public class OccupationPlugin implements PluginDescriptor +{ + static boolean ENABLE_BY_DEFAULT = false; + public static final String RESOURCE_FILE =OccupationPlugin.class.getPackage().getName() + ".OccupationResources"; + public static final String PLUGIN_CLASS = OccupationPlugin.class.getName(); + + + public String toString() + { + return "Occupation View"; + } + + public void provideServices(Container container, Configuration config) + { + if ( !config.getAttributeAsBoolean("enabled", ENABLE_BY_DEFAULT) ) + return; + + container.addContainerProvidedComponent( I18nBundle.ROLE, I18nBundleImpl.class.getName(), RESOURCE_FILE,I18nBundleImpl.createConfig( RESOURCE_FILE ) ); + container.addContainerProvidedComponent( RaplaExtensionPoints.CALENDAR_VIEW_EXTENSION, OccupationFactory.class.getName(), OccupationFactory.OCCUPATION_VIEW, null); + container.addContainerProvidedComponent( RaplaExtensionPoints.USER_OPTION_PANEL_EXTENSION, OccupationOption.class.getName(),PLUGIN_CLASS, config); + } + + public Object getPluginMetaInfos( String key ) + { + if ( RaplaPluginMetaInfo.METAINFO_PLUGIN_ENABLED_BY_DEFAULT.equals( key )) { + return new Boolean( ENABLE_BY_DEFAULT ); + } + return null; + } +} + diff --git a/Rapla/src/org/rapla/plugin/occupationview/OccupationResources.xml b/Rapla/src/org/rapla/plugin/occupationview/OccupationResources.xml new file mode 100644 index 0000000..228624a --- /dev/null +++ b/Rapla/src/org/rapla/plugin/occupationview/OccupationResources.xml @@ -0,0 +1,222 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + Occupation + Besetzung + l''Occupation + Ocupación + Obsazenost + Bezetting + Zawód + + + Month + + Monat + + Mois + + ...: + ...: + Maand + + + + First fit + ...: + Premier libre + ...: + ...: + Eerst vrij + + + Best fit + ...: + Mellieur libre + ...: + ...: + Beste vrij + + + Free slot: + Frei: + Libre: + ...: + ...: + Vrij: + + + #Gaps + ...: + #Gaps + ...: + ...: + #Gaps + + + Resource not availabe. + ...: + Resource pas disponible." + ...: + ...: + Materiaal niet beschikbaar. + + + -1 Month + -1 Monate + -1 Mois" + -1 + -1 + -1 Maand + + + +1 Month + +1 Monate + +1 Mois" + +1 + +1 + +1 Maand + + + Remarks + ... + ... + ... + Opmerking + + + Resources/persons + Ressourcen/Personen + Ressources/Personnes + Recursos/personas + Prostředky/osoby + Persoon/Materiaal + + + In + Ein + Entrée + Entrada + Vstupní + In + + + In + Aus + Sortie + Salida + Výstupní + Uit + + + Archive {0} >= {1} days inactive + Archiv {0} >= {1} Tage inaktiv + Archive {0} >= {1} jours inactifs + pArchivo {0} >= {1} día inactivos + Archiv {0} >= {1} dnů neaktivní + Archiveer {0} >= {1} dagen inactief + + + Archive {0} < {1} days inactive + Archiv {0} < {1} Tage inaktiv + Archive {0} < {1} jours inactifs + Archivo {0} < {1} día inactivos + Archiv {0} < {1} dnů neaktivní + Archiveer {0} < {1} dagen inactief + + + Archive + Archiv + Archive + pArchivo + Archiv + Archiveer + + + _endoflive not defined. + _endoflive nicht definiert. + _endoflive pas défini. + _endoflive no se define. + _endoflive není definováno. + _endoflife niet gedefinieerd. + + + Confirm. Archive on {0}. + Bestätigen. Archiv auf {0). + Confirmer. Archives le {0}.) + Confirmar. Archivo de {0}. + Potvrdit. Archivu na {0}. + Bevestig: Archiveer op {0}. + + + Deleted + Gelöscht + Supprimé + Eliminado + vypouští + Geschrapt + + + Starting day of the month + Ab Tag des Monats + A partir du jour du mois + Días a partir del mes + První den v měsíci + Start dag van de maand + Począwszy od dnia miesiąca + + + Day 1 of month + Tag 1 des Monats + Jour 1 du mois + El día 1 del mes + Den 1 měsíce + Dag 1 van maand + Dzień 1 miesiąca + + + Today + Heute + Aujourd'hui + Hoy + Dnes + Vandaag + Dzisiaj + + + Options + Einstellungen + Options + Opciones + Volby + Voorkeur + + + Denied + Gesperrt + Interdit + Denegado + Zamítnuto + Geen toegang + + \ No newline at end of file diff --git a/Rapla/src/org/rapla/plugin/occupationview/OccupationTableModel.java b/Rapla/src/org/rapla/plugin/occupationview/OccupationTableModel.java new file mode 100644 index 0000000..7b3596d --- /dev/null +++ b/Rapla/src/org/rapla/plugin/occupationview/OccupationTableModel.java @@ -0,0 +1,252 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2011 Bob Jordaens | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.plugin.occupationview; + +import java.util.Date; +import javax.swing.table.DefaultTableModel; + +import org.rapla.components.util.DateTools; +import org.rapla.components.xmlbundle.I18nBundle; + +public class OccupationTableModel extends DefaultTableModel +{ + private static final long serialVersionUID = 1L; + + static final int CALENDAR_SEQUENCE_NUMBER = 0; + static final int CALENDAR_RESOURCE = 1; + static final int CALENDAR_CHECK = 2; + static final int CALENDAR_IN_DAYS = 3; + static final int CALENDAR_OUT_DAYS = 4; + static final int CALENDAR_EVENTS = 5; + + private I18nBundle i18n; + Object occupationTable[][]; + private int freeSlot= 0; + private Date calendarStartDate; + private int todayColumn = 0; + + public OccupationTableModel(I18nBundle i18n, int rowCount, int columnCount, Date date) { + this.calendarStartDate = date; + this.i18n = i18n; + occupationTable = new Object[rowCount][columnCount]; + setRowCount(rowCount); + setColumnCount(columnCount); + /* + String[]columns = new String[] { + i18n.getString("resources_persons") + ,i18n.getString("status") + ,i18n.getString("free_days") + ,i18n.getString("start_date") + ,i18n.getString("end_date") + ,i18n.getString("reservation") + ,i18n.getString("appointment") + }; + this.setColumnIdentifiers( columns); + */ + } + + /* + public void setOccupations(Object occupationTable[][], int rowCount, int columnCount) { + setRowCount(0); + this.occupationTable = occupationTable; + setRowCount(rowCount); + setColumnCount(columnCount); + firstFit(); + } + */ + + public void firstFit() { + if(freeSlot==0) + return; + int freeCount=0; + Object occupation = null; + OccupationCell occCell = null; + for (int r=0; r=CALENDAR_EVENTS ;c--) + if(!getValueAt(row,c).equals(type)) + break; + return c + 1; + } + + public Date getColumnDate(int c) { + return DateTools.addDays(calendarStartDate, c - CALENDAR_EVENTS); + } + + public Date getcalendarStartDate() { + return calendarStartDate; + } + + public void setTodayColumn(int c) { + this.todayColumn = c; + } + + public int getTodayColumn() { + return todayColumn; + } + + public int getFreeSlot() { + return freeSlot; + } + + public boolean isCellEditable(int row, int column) { + return false; + } + + /* + public int getRowCount() { + return this.rowCount; + } + + public int getColumnCount() + { + return columnCount; + } + */ + + public Object getValueAt( int rowIndex, int columnIndex ) + { + + switch ( columnIndex ) + { + case CALENDAR_CHECK: + return occupationTable[rowIndex][columnIndex]; + case CALENDAR_IN_DAYS: + if(occupationTable[rowIndex][columnIndex] == null) + return new Integer(Integer.MAX_VALUE); + case CALENDAR_OUT_DAYS: + if(occupationTable[rowIndex][columnIndex] == null) + return new Integer(Integer.MAX_VALUE); + } + + return occupationTable[rowIndex][columnIndex]; + } + + public void setValueAt( Object value, int rowIndex, int columnIndex ) + { + + switch ( columnIndex ) + { + case CALENDAR_IN_DAYS: + if(value == null) + occupationTable[rowIndex][columnIndex] = new Integer(Integer.MAX_VALUE); + case CALENDAR_OUT_DAYS: + if(value == null) + occupationTable[rowIndex][columnIndex] = new Integer(Integer.MAX_VALUE); + default: + occupationTable[rowIndex][columnIndex] = value; + } + return; + } + + public String getColumnName( int columnIndex ) + { + switch ( columnIndex ) + { + case CALENDAR_RESOURCE: return i18n.getString("resources_persons"); + case CALENDAR_CHECK: return i18n.getString("event_check"); + case CALENDAR_SEQUENCE_NUMBER: return "#"; + case CALENDAR_IN_DAYS: return i18n.getString("daysin"); + case CALENDAR_OUT_DAYS: return i18n.getString("daysout"); + + default: return "getcolumnName"; + } + } + + public Class getColumnClass(int columnIndex) { + + switch ( columnIndex ) + { + default: return String.class; + } + } + + public void setFreeSlot(int freeSlot) { + this.freeSlot = freeSlot; + } + + public void calcSlotGaps() { + int totalFreeGaps=0; + int lastFreeGap=0; + for (int r=0; r0:=n-times + //update(); + } + + + public void update() throws RaplaException + { + try { + container.getTopLevelAncestor().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + updatetable(); + } finally { + container.getTopLevelAncestor().setCursor(Cursor.getDefaultCursor()); + } + } + + public void updatetable() throws RaplaException + { + timeShift.update(); + int timeShiftTimes = timeShift.timeShiftTimes.getNumber().intValue() + 1; + + ClassificationFilter[] cfilters = model.getReservationFilter(); + if(cfilters.length>1) { + DialogUI dialog = DialogUI.create( + getContext() + ,container.getTopLevelAncestor() + ,true + ,getString("warning") + ,getString("warning.max-one-eventtype") + ,new String[] { getString("continue") } + ); + dialog.setIcon(getIcon("icon.warning")); + dialog.setDefault(0); + dialog.start(); + return; + } + mutableReservation = getClientFacade().newReservation(); + Appointment appointment = getClientFacade().newAppointment( model.getSelectedDate(), model.getSelectedDate()); + mutableReservation.addAppointment(appointment); + appointments = mutableReservation.getAppointments(); + allocatableList = getAllAllocatables(); + Collections.sort(allocatableList, new AllocatableSortByName()); + int rowCount = allocatableList.size(); + Iterator it = allocatableList.iterator(); + calendarDS = timeShift.getSelectedStartTime(); // start midnight + calendarDE = timeShift.getSelectedEndTime(); // end midnight + + + // calculate number of columns required to display from calendar + Calendar calendarTmp = (Calendar) calendarDS.clone(); + int totalDays= -1 * calendarTmp.get(Calendar.DAY_OF_MONTH) + 1; + for ( int i = 0; i< timeShiftTimes; i++) { + totalDays += calendarTmp.getActualMaximum(Calendar.DAY_OF_MONTH); + calendarTmp.add(calendarShift, 1); + } + int columnCount = totalDays + OccupationTableModel.CALENDAR_EVENTS ; // + for fixed columns + //Object occupationTable[][] = new Object[rowCount][columnCount]; + occupationTableModel = new OccupationTableModel(getI18n(), rowCount, columnCount, calendarDS.getTime()); + + +// Sorting block + + sorter = new TableSorter(occupationTableModel,table.getTableHeader()); + sorter.setSortable(0, false); + + sorter.setColumnComparator(1, new Comparator() { + public int compare(AllocationCell o1, AllocationCell o2) { + String s1 = o1.allocatable.getName(locale); + String s2 = o2.allocatable.getName(locale); + return s1.compareTo(s2); + } + }); + + sorter.setSortable(2, false); + + sorter.setColumnComparator(3, new Comparator() { + public int compare(Object o1, Object o2) { + Integer i1 = (Integer) o1; + Integer i2 = (Integer) o2; + return i1.compareTo(i2); + } + }); + + sorter.setColumnComparator(4, new Comparator() { + public int compare(Object o1, Object o2) { + Integer i1 = (Integer) o1; + Integer i2 = (Integer) o2; + return i1.compareTo(i2); + } + }); + + //sorter.setSortable(4, false); + sorter.setSortable(5, false); + + //table.setRowSorter(sorter); + + table.setModel( sorter ); + + table.getColumnModel().getColumn(OccupationTableModel.CALENDAR_CHECK).setCellRenderer(alcRenderer); + int r = 0; + char leftBound = ' '; + char rightBound = ' '; + while (it.hasNext()) { + + // get resource data + Allocatable alloc = it.next(); + AllocationCell alcCell = new AllocationCell(alloc); + occupationTableModel.setValueAt( alcCell, r, OccupationTableModel.CALENDAR_RESOURCE); + + // get reservation data + Calendar calendarTDS = (Calendar) calendarDS.clone(); + Calendar calendarTDE = (Calendar) calendarDE.clone(); + mutableReservation.addAllocatable(alloc); + String occupationType = null; + QueryModule qry = getQuery(); + for ( int c = OccupationTableModel.CALENDAR_EVENTS ; c <= (columnCount - 1); c++) { + sorter.setSortable(c, false); + if(DateTools.cutDate(today).equals(DateTools.cutDate(calendarTDS.getTime()))) + occupationTableModel.setTodayColumn(c); + appointment.move(calendarTDS.getTime(),calendarTDE.getTime()); + updateBindings(appointments); + occupationType = getOccupationType((Allocatable) alloc); + //occupationTableModel.setValueAt( null, r, OccupationTableModel.CALENDAR_IN_DAYS); + if(occupationType.equals("C")) { // Conflict + // Not Free + Reservation [] res = qry.getReservationsForAllocatable(new Allocatable[] { alloc },calendarTDS.getTime(),calendarTDE.getTime(), cfilters); + if(res.length==0) { + OccupationCell occCell = new OccupationCell('N',0,'N', null); + occupationTableModel.setValueAt( occCell, r, c); + } + else { // Not Free + // A from-to will be split is days like [ [, ] [ , ] [, ] ] [ left boundary = startdate, ] right boundary = enddate + // ] and [ used in the middle. + Reservation reservation = res[0]; + Appointment[] apps = reservation.getAppointmentsFor(alloc); + Appointment app = apps[0]; + //System.out.println(alloc.getName(locale)); + //System.out.println("Start= " + app.getStart() + " TDS= " + calendarTDS.getTime()); + Date minStartDate = app.getStart(); + if(DateTools.isSameDay(minStartDate.getTime(),calendarTDS.getTime().getTime())) + if(DateTools.isMidnight(minStartDate)) + leftBound = '['; + else + leftBound = '<'; + else + if(DateTools.isMidnight(minStartDate)) + leftBound = ']'; + else + leftBound = '>'; + //Repeating rep = app[0].getRepeating(); + //if (rep != null) System.out.println("Repating= " + rep); + //System.out.println("MaxEnd = " + app.getMaxEnd() + " End= " + app.getEnd()); + Date maxendDate = app.getMaxEnd(); + rightBound = '['; + if(maxendDate!=null) + if(DateTools.isMidnight(maxendDate)) { // endDate 00:00:00 = previous date + if(DateTools.isSameDay((DateTools.subDay(maxendDate)).getTime(),calendarTDS.getTime().getTime())) + rightBound = ']'; + } + else + if(DateTools.isSameDay(maxendDate.getTime(),calendarTDS.getTime().getTime())) + rightBound = '>'; + OccupationCell occCell = new OccupationCell(leftBound,1,rightBound, reservation); + occupationTableModel.setValueAt( occCell, r, c); + if(c == occupationTableModel.getTodayColumn()) + setDaysInOut(app, r, today); + else + if(c >= OccupationTableModel.CALENDAR_EVENTS) + setDaysInOut(app, r, calendarTDS.getTime()); + + //System.out.println(res[0].toString() + " Length:" + res.length); + } + } + else + if(occupationType.equals(" ")) { // Free + OccupationCell occCell = new OccupationCell('N',0,'N'); + occupationTableModel.setValueAt( occCell, r, c); + + // calculate archive date + Date startDate = DateTools.subDays(today, archiveAge); + Reservation [] res = getQuery().getReservationsForAllocatable(new Allocatable[] { alloc },startDate,today, null); + if(res.length != 0) { + Appointment[] apps = res[res.length - 1].getAppointments(); + Appointment app = apps[0]; + if ( c == occupationTableModel.getTodayColumn()) + setDaysInOut(app, r, today); + } + + } + else + if(occupationType.equals("F")) { // Forbidden Resource is not available at all, out of order or in maintenance + OccupationCell occCell = new OccupationCell('N',-1,'N'); + occupationTableModel.setValueAt( occCell, r, c); + } + /* debug + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String DS = sdf.format(calendarTDS.getTime()); + String DE = sdf.format(calendarTDE.getTime()); + System.out.println("Row="+r+" Column=" + c + " StartDate=" + DS + " EndDate= " + DE + " Type = " + occupationType); + */ + calendarTDS.add(Calendar.DATE, 1); // next startday + calendarTDE.add(Calendar.DATE, 1); // next endday + } + r++; + } + + occupationTableModel.setFreeSlot(timeShift.freeSlot.getNumber().intValue()); + //setLineNumbers(); + + occupationTableModel.firstFit(); + + TableColumnModel cm = table.getColumnModel(); + + // Resource column Header + TableColumn column = cm.getColumn(OccupationTableModel.CALENDAR_SEQUENCE_NUMBER); + column.setPreferredWidth(30); + column.setMaxWidth(50); + + // Check column Header + column = cm.getColumn(OccupationTableModel.CALENDAR_RESOURCE); + column.setPreferredWidth(200); + column.setMaxWidth(300); + + // In column Header + column = cm.getColumn(OccupationTableModel.CALENDAR_CHECK); + column.setPreferredWidth(200); + column.setMaxWidth(300); + + // Out column Header + column = cm.getColumn(OccupationTableModel.CALENDAR_IN_DAYS); + column.setPreferredWidth(40); + column.setMaxWidth(50); + + // Sequence line number + column = cm.getColumn(OccupationTableModel.CALENDAR_OUT_DAYS); + column.setPreferredWidth(40); + column.setMaxWidth(50); + + calendarTmp = (Calendar) calendarDS.clone(); + SimpleDateFormat sdfMM = new SimpleDateFormat("MM",locale); + sdfMM.setTimeZone(DateTools.getTimeZone()); + SimpleDateFormat sdfdd = new SimpleDateFormat("dd",locale); + sdfdd.setTimeZone(DateTools.getTimeZone()); + //SimpleDateFormat sdfEE = new SimpleDateFormat("EE",locale); + //sdfEE.setTimeZone(DateTools.getTimeZone()); + GroupableTableColumnModel gcm = (GroupableTableColumnModel)table.getColumnModel(); + String oldMM=null; // old month groupHeader label + String newMM=null; // new month groupHeader label + ColumnGroup g_MM = null; + for ( int i = OccupationTableModel.CALENDAR_EVENTS ; i <= (columnCount - 1); i++) { + + Date dateTmp = calendarTmp.getTime(); + int day = calendarTmp.get(Calendar.DAY_OF_WEEK); + + newMM = sdfMM.format(dateTmp); + //set columnGroupHeader label == MM (Month) 01, , 12 + if(!newMM.equals(oldMM)) { + if(oldMM!=null) + gcm.addColumnGroup(g_MM); + g_MM = new ColumnGroup(new GroupableTableCellRenderer(),newMM); + oldMM = newMM; + } + //set columnGroupHeader label == dd (Day) 01, ...,31 + ColumnGroup g_dd = new ColumnGroup(new GroupableTableCellRenderer(), sdfdd.format(dateTmp)); + g_MM.add(g_dd); + ColumnGroup g_dw = new ColumnGroup(new DayOfWeekHeaderRenderer(), Integer.toString(day)); + g_dd.add(g_dw); + column = cm.getColumn(i); + g_dw.add(column); + + // set column sizes + column.setMinWidth(19); + column.setMaxWidth(26); + column.setPreferredWidth(26); + //set columnHeader label == Day of the week Mo, .... , Su + int selectedCount = occupationTableModel.getSelectedRows(i); + column.setHeaderValue(selectedCount); + column.setHeaderRenderer(new countRenderer()); + calendarTmp.add(Calendar.DATE,1); + } + gcm.addColumnGroup(g_MM); + } + + public void setDaysInOut(Appointment app, int r, Date referenceDate) { + + if(referenceDate.before(today)) + return; + else + if(referenceDate.after(today)) + if((Integer) occupationTableModel.getValueAt(r,OccupationTableModel.CALENDAR_IN_DAYS) != Integer.MAX_VALUE) + return; + + int days = (int) ((app.getStart().getTime() - today.getTime()) / DateTools.MILLISECONDS_PER_DAY); + occupationTableModel.setValueAt( days, r, OccupationTableModel.CALENDAR_IN_DAYS); + + Repeating rpt = app.getRepeating(); + Date edate = null; + if ( rpt == null ) + edate = app.getEnd(); + else + if ( rpt.getEnd() != null && !rpt.isFixedNumber() ) + edate = rpt.getEnd(); + else + if (rpt.getEnd() != null) + edate = rpt.getEnd(); + if(edate == null) + occupationTableModel.setValueAt(null, r, OccupationTableModel.CALENDAR_OUT_DAYS); + else { + days = (int) ((edate.getTime() - today.getTime()) / DateTools.MILLISECONDS_PER_DAY); + occupationTableModel.setValueAt( days, r, OccupationTableModel.CALENDAR_OUT_DAYS); + } + + return; + } + + public JComponent getDateSelection() + { + return timeShift.getComponent(); + } + + public void scrollToStart() + { + try { + update(); + } catch (RaplaException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + public JComponent getComponent() + { + return container; + } + + private List getAllAllocatables() throws RaplaException { + Allocatable[] allocatables = model.getSelectedAllocatables(); + return getAllocatableList( allocatables); + } + + private List getAllocatableList(Allocatable[] allocatables) { + List result = Arrays.asList( allocatables ); + return result; + } + + private void updateBindings(Appointment[] appointments) { + // System.out.println("getting allocated resources"); + try { + + for (int i=0;i allocatables = new HashSet(Arrays.asList(getQuery().getAllocatableBindings(appointment))); + appointmentMap.put(appointment,allocatables); + } + } + catch (RaplaException ex) { + showException(ex,table); + } + } + + private String getOccupationType(Allocatable allocatable) { + calcConflictingAppointments( allocatable ); + if ( conflictCount == 0 ) { + return " "; + } else + if ( conflictCount == appointments.length ) { + if ( conflictCount == permissionConflictCount ) { + if (!checkRestrictions) { + return "F"; //forbiddenIcon; + } + } else { + return "C"; //conflictIcon; + } + } else + if ( !checkRestrictions ) { + return "X"; + } + for ( int i = 0 ; i < appointments.length; i++ ) { + Appointment appointment = appointments[i]; + if ( mutableReservation.hasAllocated( allocatable, appointment ) && !getQuery().hasPermissionToAllocate( appointment,allocatable )) + return "F"; //forbiddenIcon; + } + + if ( permissionConflictCount - conflictCount == 0 ) { + return " "; + } + + Appointment[] restriction = mutableReservation.getRestriction(allocatable); + if ( restriction.length == 0 ) { + return "C"; //conflictIcon; + } else { + boolean conflict = false; + for (int i = 0 ;i < restriction.length;i++) { + Set allocatables = (Set)appointmentMap.get( restriction[i]); + if (allocatables.contains(allocatable)) { + conflict = true; + break; + } + } + if ( conflict ) + return "C"; // conflictIcon; + else + return "X"; // "X" not allways available + } + } + + // calculates the number of conflicting appointments for this allocatable + private void calcConflictingAppointments(Allocatable allocatable) { + if (conflictingAppointments == null || conflictingAppointments.length!=appointments.length) + conflictingAppointments = new boolean[appointments.length]; + conflictCount = 0; + permissionConflictCount = 0; + for (int i=0;i') + cellBorder.setThickness(10, EAST); + else + cellBorder.setThickness(0, EAST); + + cellBorder.setThickness(2, SOUTH); + + Color color = getColorForClassifiable( reservation ); + if(color==null) + cell.setBackground( Color.WHITE ); + else + cell.setBackground( color); + setBorder(cellBorder); + } + else + if( occCell.getTypeId() == 0) { // Free + cell.setBackground( Color.LIGHT_GRAY); + } + else + if( occCell.getTypeId() == -1) { // Forbidden + cell.setBackground( Color.BLACK ); + } + else + if( occCell.getTypeId() == -2) { // FirstFit + cellBorder.setThickness(1, NORTH); + if(occCell.leftBound=='[') + cellBorder.setThickness(2, WEST); + else + cellBorder.setThickness(0, WEST); + cellBorder.setThickness(1, SOUTH); + if(occCell.rightBound==']') + cellBorder.setThickness(2, EAST); + else + cellBorder.setThickness(0, EAST); + setBorder(cellBorder); + cell.setBackground( Color.GREEN ); + } + setText(""); + if( c == table.getSelectedColumn() && r == table.getSelectedRow()) { // identify selected cell + // Selector context Popup + cellBorder.setThickness(0, NORTH); + cellBorder.setThickness(5, WEST); + cellBorder.setThickness(0, SOUTH); + cellBorder.setThickness(5, EAST); + cell.setBackground( Color.GRAY ); + setBorder(cellBorder); + } + } + + if( value instanceof AllocationCell ) + { + Font font = cell.getFont(); + cell.setFont(font.deriveFont(Font.BOLD)); + AllocationCell allcCell = (AllocationCell) value; + // handle the first column: Resources + Allocatable allc = allcCell.allocatable; + cell.setBackground( Color.WHITE); + setText(allc.getName(locale)); + cell.setBackground( Color.WHITE ); + Color color = getColorForClassifiable( allc ); + if(color!=null) + cell.setBackground( color); + } + + if( value instanceof Integer ) + { + if( c == OccupationTableModel.CALENDAR_IN_DAYS) { + int days = (Integer) value; + if(days == Integer.MAX_VALUE) { + setText(""); + } + else { + cell.setBackground( Color.WHITE); + if(days >= 0) { + formatDaysInOut.setPositivePrefix("+"); + setText(formatDaysInOut.format(days)); + } + else { + formatDaysInOut.setPositivePrefix("-"); + setText(formatDaysInOut.format(days)); + } + } + } + else { + if( c == OccupationTableModel.CALENDAR_OUT_DAYS ) { + cell.setBackground( Color.WHITE); + int days = (Integer) value; + if(days == Integer.MAX_VALUE) { + Object daysIn = occupationTableModel.getValueAt(row,OccupationTableModel.CALENDAR_IN_DAYS); + if((Integer) daysIn == Integer.MAX_VALUE) + setText(""); + else + setText("?"); + } + else { + if(days >= 0) + formatDaysInOut.setPositivePrefix("+"); + else + formatDaysInOut.setPositivePrefix("-"); + setText(formatDaysInOut.format(days)); + } + } + } + } + + if(c == OccupationTableModel.CALENDAR_SEQUENCE_NUMBER) { + Font font = cell.getFont(); + cell.setFont(font.deriveFont(Font.BOLD)); + AllocationCell allcCell = (AllocationCell) occupationTableModel.getValueAt(row,OccupationTableModel.CALENDAR_RESOURCE); + cell.setBackground( Color.WHITE); + Allocatable alloc = allcCell.allocatable; + AttributeType type = EndOfLifeArchiver.getEndOfLifeType(alloc); + if(type != null) { + Classification classification = alloc.getClassification(); + Object endlife = classification.getValue("_endoflife"); + if(endlife==null) { + Object daysOut = occupationTableModel.getValueAt(row, OccupationTableModel.CALENDAR_OUT_DAYS); + if( (Integer) daysOut == Integer.MAX_VALUE) // old enough to be archived + cell.setBackground( Color.GREEN); + else + cell.setBackground( Color.ORANGE); + } + } + setText(Integer.toString(r+1)); + return cell; + } + + if( value instanceof String ) { + if( c == OccupationTableModel.CALENDAR_IN_DAYS || c == OccupationTableModel.CALENDAR_OUT_DAYS) { + cell.setBackground( Color.WHITE); + setText((String) value); + } + } + + if(value==null) { + cell.setBackground( Color.WHITE); + setText((value == null) ? "" : "Unknown"); + } + + if(c == occupationTableModel.getTodayColumn() && c > OccupationTableModel.CALENDAR_EVENTS) { + cell.setBackground( Color.decode("#bb5823")); + } + + if(c >= OccupationTableModel.CALENDAR_EVENTS) { + if (hasFocus) + cell.setForeground( Color.GRAY ); + } + + //setText((value == null) ? "" : value.toString()); + return cell; + } + } + + private int convertRowIndexToModel(JTable table, int r) { + return sorter.modelIndex(r); + } + + Color getColorForClassifiable( Classifiable classifiable ) { + Classification c = classifiable.getClassification(); + Attribute colorAttribute = c.getAttribute("color"); + String color = null; + if ( colorAttribute != null) { + Object hexValue = c.getValue( colorAttribute ); + if ( hexValue != null) { + if ( hexValue instanceof Category) { + hexValue = ((Category) hexValue).getAnnotation( CategoryAnnotations.KEY_NAME_COLOR ); + } + if ( hexValue != null) { + color = hexValue.toString(); + } + } + } + if ( color != null) + { + try + { + return Color.decode(color); + } + catch (NumberFormatException ex) + { + getLogger().warn( "Can't parse Color " + color + " " +ex.getMessage()); + } + } + return null; + } + + class GroupableTableCellRenderer extends DefaultTableCellRenderer { + + private static final long serialVersionUID = 1L; + + public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean focused, int r, int c) { + JTableHeader header = table.getTableHeader(); + if (header != null) { + setBackground(Color.WHITE); + Font font = getFont(); + setFont(font.deriveFont(Font.BOLD)); + } + + setHorizontalAlignment(SwingConstants.CENTER); + setText(value != null ? value.toString() : " "); + setBorder(UIManager.getBorder("TableHeader.cellBorder")); + return this; + } + } + + private final String dayNames[] = new DateFormatSymbols().getShortWeekdays(); + class DayOfWeekHeaderRenderer extends DefaultTableCellRenderer { + + private static final long serialVersionUID = 1L; + public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean focused, int r, int c) { + if(value instanceof String) + { + int dd = Integer.parseInt(((String) value)); + setText(dayNames[dd]); + if( dd == Calendar.SUNDAY || dd == Calendar.SATURDAY) + setBackground(Color.LIGHT_GRAY); + else + setBackground(Color.WHITE); + Font font = getFont(); + setFont(font.deriveFont(Font.BOLD)); + } + else { + setText(value != null ? value.toString() : " "); + } + + //if(c == occupationTableModel.getTodayColumn()) + // setBackground(Color.decode("#bb5823")); + + setHorizontalAlignment(SwingConstants.CENTER); + setBorder(UIManager.getBorder("TableHeader.cellBorder")); + return this; + } + } + + class countRenderer extends DefaultTableCellRenderer { + + private static final long serialVersionUID = 1L; + public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean focused, int r, int c) { + + setText(value != null ? value.toString() : " "); + LinesBorder cellBorder = new LinesBorder(Color.GRAY); + cellBorder.setThickness(2, NORTH); + cellBorder.setThickness(1, WEST); + cellBorder.setThickness(1, EAST); + cellBorder.setThickness(0, SOUTH); + setBorder(cellBorder); + setBackground( Color.WHITE); + setHorizontalAlignment(SwingConstants.CENTER); + return this; + } + } + + public class PopupTableHandler extends MouseAdapter { + + void showPopup(MouseEvent me) throws RaplaException{ + // Conflict " " = Resource is available + // Conflict "X" = Resource is not allways available + // Conflict "C" = Resource is used at requested timestamp + // Forbidden "F" = Resource is not available at the specified timestamp + // First Fit proposal "=" = Resource is available for the request period + Point p = new Point(me.getX(), me.getY()); + int r = table.getSelectedRow(); + int c = table.getSelectedColumn(); + if( r < 0 || c < 0) + return; + r = convertRowIndexToModel(table,r); + + Object obj = occupationTableModel.getValueAt(r, c); + if (obj instanceof OccupationCell ) { + OccupationCell occCell = (OccupationCell) obj; + if(occCell.getTypeId() > 0) + editPopup(occCell, r, c, p); + else + newPopup(occCell, r, c, p); + } + + if (obj instanceof AllocationCell ) { + AllocationCell alcCell = (AllocationCell) obj; + if(alcCell.allocatable !=null ) { + Object daysOut = occupationTableModel.getValueAt(r, OccupationTableModel.CALENDAR_OUT_DAYS); + Object daysIn = occupationTableModel.getValueAt(r, OccupationTableModel.CALENDAR_IN_DAYS); + if((Integer) daysOut == Integer.MAX_VALUE && (Integer) daysIn == Integer.MAX_VALUE) + archivePopup(alcCell, r, c, p, 0); + else + if( ((Integer) daysOut < 0)) + archivePopup(alcCell, r, c, p,(Integer) daysOut); + } + } + return; + } + + public void newPopup(OccupationCell occCell, int r, int c, Point p) throws RaplaException { + Calendar calendarStart = null; + Calendar calendarEnd = null; + + if( occCell.getTypeId() == -2) { + int cs = occupationTableModel.findStartSlot(r, c, -2) - OccupationTableModel.CALENDAR_EVENTS; // corrected start + calendarStart = (Calendar) calendarDS.clone(); + calendarStart.add(Calendar.DATE, cs); + calendarEnd = (Calendar) calendarStart.clone(); + calendarEnd.add(Calendar.DATE, occupationTableModel.getFreeSlot()); + } else + if( occCell.getTypeId() == 0) { + calendarStart = (Calendar) calendarDS.clone(); + calendarStart.add(Calendar.DATE, c - OccupationTableModel.CALENDAR_EVENTS); + calendarEnd = (Calendar) calendarStart.clone(); + calendarEnd.add(Calendar.DATE, 1); + } else + if( occCell.getTypeId() == -1) { + return; + } + + mutableReservation = getClientFacade().newReservation(); + Appointment appointment = null; + appointment = getClientFacade().newAppointment( calendarStart.getTime(), calendarEnd.getTime(), repeatingType, repeatingDuration ); + appointment.setWholeDays(true); + mutableReservation.addAppointment(appointment); + AllocationCell alcCell = (AllocationCell) occupationTableModel.getValueAt(r, OccupationTableModel.CALENDAR_RESOURCE); + Allocatable alloc = alcCell.allocatable; + mutableReservation.addAllocatable(alloc); + JPopupMenu popup = new JPopupMenu(); + newAdapter menuAction = new newAdapter(mutableReservation,0); + JMenuItem newItem = new JMenuItem(getString("new"),getIcon( "icon.new")); + newItem.setActionCommand("new"); + newItem.addActionListener(menuAction); + popup.add(newItem); + popup.show( table, p.x, p.y); + } + + SimpleDateFormat sdfdatetime = new SimpleDateFormat("yyyy-MM-dd"); + public void archivePopup(AllocationCell alcCell, int r, int c, Point p, int days) throws RaplaException { + Allocatable alloc = alcCell.allocatable; + + AttributeType type = EndOfLifeArchiver.getEndOfLifeType(alloc); + if(type == null) + return; + + JPopupMenu popup = new JPopupMenu(); + newAdapter menuAction = new newAdapter(alloc, days); + JMenuItem archiveItem; + if(canModify(alloc)) { + if (type.equals(AttributeType.BOOLEAN)) + archiveItem = new JMenuItem(getI18n().getString("archive_yn"),getIcon( "icon.archive")); + else { + if(days < 0) { + archiveItem = new JMenuItem(getI18n().format("forcearchive_lt", sdfdatetime.format(DateTools.addDays(today, (int) (archiveAge + days))), archiveAge),getIcon( "icon.archive")); + archiveItem.setBackground(Color.ORANGE); + } + else { + archiveItem = new JMenuItem(getI18n().format("archiveda_gt", sdfdatetime.format(today), archiveAge),getIcon( "icon.archive")); + archiveItem.setBackground(Color.GREEN); + } + } + archiveItem.setEnabled(true); + archiveItem.setActionCommand("archive"); + } + else { + archiveItem = new JMenuItem(getString("permission.denied"),getIcon("icon.no_perm")); + archiveItem.setEnabled(true); + } + archiveItem.addActionListener(menuAction); + popup.add(archiveItem); + popup.show( table, p.x, p.y); + } + + public void editPopup(OccupationCell occCell, int r, int c, Point p) throws RaplaException { + + Reservation reservation = occCell.getReservation(); + + JPopupMenu popup = new JPopupMenu(); + newAdapter menuAction = new newAdapter(reservation, 0); + JMenuItem editItem = new JMenuItem(getString("edit"),getIcon( "icon.edit")); + editItem.setActionCommand("edit"); + editItem.addActionListener(menuAction); + editItem.setEnabled(canModify(reservation) || getQuery().canExchangeAllocatables(reservation)); + popup.add(editItem); + + JMenuItem deleteItem = new JMenuItem(getString("delete"),getIcon( "icon.delete")); + deleteItem.setActionCommand("delete"); + deleteItem.addActionListener(menuAction); + deleteItem.setEnabled(canModify(reservation)); + popup.add(deleteItem); + + JMenuItem viewItem = new JMenuItem(getString("info"),getIcon( "icon.help")); + viewItem.setActionCommand("info"); + viewItem.addActionListener(menuAction); + User owner = reservation.getOwner(); + try + { + User user = getUser(); + boolean canView = getQuery().canReadReservationsFromOthers( user) || user.equals( owner); + viewItem.setEnabled( canView); + } + catch (RaplaException ex) + { + getLogger().error( "Can't get user",ex); + } + popup.add(viewItem); + popup.show( table, p.x, p.y); + } + + /** Implementation-specific. Should be private.*/ + public void mousePressed(MouseEvent me) { + if (me.isPopupTrigger()) + try { + showPopup(me); + } catch (RaplaException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + /** Implementation-specific. Should be private.*/ + public void mouseReleased(MouseEvent me) { + if (me.isPopupTrigger()) + try { + showPopup(me); + } catch (RaplaException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + /** double click*/ + public void mouseClicked(MouseEvent me) { + if (me.getClickCount() > 1 ) + { + //Point p = new Point(me.getX(), me.getY()); + int r = table.getSelectedRow(); + int c = table.getSelectedColumn(); + if( r < 0 || c < 0) + return; + r = convertRowIndexToModel(table, r); + if(c { + public int compare(Allocatable o1, Allocatable o2) { + return o1.getName(locale).compareTo(o2.getName(locale)); + } + } + + public class LinesBorder extends AbstractBorder implements SwingConstants { + + private static final long serialVersionUID = 1L; + protected int northThickness; + protected int southThickness; + protected int eastThickness; + protected int westThickness; + protected Color northColor; + protected Color southColor; + protected Color eastColor; + protected Color westColor; + + public LinesBorder(Color color) { + this(color, 1); + } + + public LinesBorder(Color color, int thickness) { + setColor(color); + setThickness(thickness); + } + + public LinesBorder(Color color, Insets insets) { + setColor(color); + setThickness(insets); + } + + public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + Color oldColor = g.getColor(); + + g.setColor(northColor); + for (int i = 0; i < northThickness; i++) { + g.drawLine(x, y+i, x+width-1, y+i); + } + g.setColor(southColor); + for (int i = 0; i < southThickness; i++) { + g.drawLine(x, y+height-i-1, x+width-1, y+height-i-1); + } + g.setColor(eastColor); + for (int i = 0; i < westThickness; i++) { + g.drawLine(x+i, y, x+i, y+height-1); + } + g.setColor(westColor); + for (int i = 0; i < eastThickness; i++) { + g.drawLine(x+width-i-1, y, x+width-i-1, y+height-1); + } + g.setColor(oldColor); + } + + public Insets getBorderInsets(Component c) { + return new Insets(northThickness, westThickness, southThickness, eastThickness); + } + + public Insets getBorderInsets(Component c, Insets insets) { + return new Insets(northThickness, westThickness, southThickness, eastThickness); + } + + public boolean isBorderOpaque() { + return true; + } + + public void setColor(Color c) { + northColor = c; + southColor = c; + eastColor = c; + westColor = c; + } + + public void setColor(Color c, int direction) { + switch (direction) { + case NORTH: northColor = c; break; + case SOUTH: southColor = c; break; + case EAST: eastColor = c; break; + case WEST: westColor = c; break; + default: + } + } + + public void setThickness(int n) { + northThickness = n; + southThickness = n; + eastThickness = n; + westThickness = n; + } + + public void setThickness(Insets insets) { + northThickness = insets.top; + southThickness = insets.bottom; + eastThickness = insets.right; + westThickness = insets.left; + } + + public void setThickness(int n, int direction) { + switch (direction) { + case NORTH: northThickness = n; break; + case SOUTH: southThickness = n; break; + case EAST: eastThickness = n; break; + case WEST: westThickness = n; break; + default: + } + } + + public void append(LinesBorder b, boolean isReplace) { + if (isReplace) { + northThickness = b.northThickness; + southThickness = b.southThickness; + eastThickness = b.eastThickness; + westThickness = b.westThickness; + } else { + northThickness = Math.max(northThickness ,b.northThickness); + southThickness = Math.max(southThickness ,b.southThickness); + eastThickness = Math.max(eastThickness ,b.eastThickness); + westThickness = Math.max(westThickness ,b.westThickness); + } + } + + public void append(Insets insets, boolean isReplace) { + if (isReplace) { + northThickness = insets.top; + southThickness = insets.bottom; + eastThickness = insets.right; + westThickness = insets.left; + } else { + northThickness = Math.max(northThickness ,insets.top); + southThickness = Math.max(southThickness ,insets.bottom); + eastThickness = Math.max(eastThickness ,insets.right); + westThickness = Math.max(westThickness ,insets.left); + } + } + } + + + class AllocatableCellRenderer extends JComponent implements TableCellRenderer { + + private static final long serialVersionUID = 1L; + + public AllocatableCellRenderer() { + super(); + } + + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int r, int c) { + int row =convertRowIndexToModel(table,r); + OccupationCell occCell = null; + if(occupationTableModel.getTodayColumn()!= 0) + occCell = (OccupationCell) occupationTableModel.getValueAt(row,occupationTableModel.getTodayColumn()); + else + occCell = (OccupationCell) occupationTableModel.getValueAt(row,OccupationTableModel.CALENDAR_EVENTS); + + if(occCell!=null) { + final Reservation reservation = occCell.getReservation(); + if( reservation != null ) { + AllocatableColors controlColors = new AllocatableColors(reservation); + return controlColors; + } + } + return this; + } + } + + public class AllocatableColors extends JComponent { + Font textFont = new Font("SanSerif", Font.BOLD, 12); + FontMetrics fontMetrics = getFontMetrics(textFont); + + private static final long serialVersionUID = 1L; + + Reservation reservation; + + AllocatableColors(Reservation reservation) { + this.reservation = reservation; + } + + public void paint(Graphics g) { + + Classification classification = null; + + // Resource check points + List arrayList = Arrays.asList(reservation.getAllocatables()); + + Comparator comp = new Comparator() { + public int compare(Object o1, Object o2) { + if ((((Allocatable) o1).getClassification().getType().getName(locale)).compareTo(((Allocatable) o2).getClassification().getType().getName(locale)) > 0) + return 1; + if ((((Allocatable) o1).getClassification().getType().getName(locale)).compareTo(((Allocatable) o2).getClassification().getType().getName(locale)) < 0) + return -1; + return 0; + } + }; + + Collections.sort(arrayList,comp); + Iterator it = arrayList.iterator(); + int i=0; + Color color = null; + while (it.hasNext()) { + Allocatable allocatable = (Allocatable) it.next(); + classification = allocatable.getClassification(); + g.drawRect (1 + i*22, 0, 19, 14); + color = getColorForClassifiable( allocatable ); + if ( color == null ) + g.setColor(Color.WHITE); //RaplaColorList.getHexForColor( RaplaColorList.getResourceColor(i)); + else + g.setColor(color); + g.fillRect (2 + i*22, 1, 18, 13); + g.setFont(textFont); + g.setColor(Color.BLACK); + + g.drawString(classification.getType().getName(locale).substring(0,1), 5 + i*22, 12); // First character from name + i++; + } + + int width = i * 22 + 2; // start of text comments + + it = arrayList.iterator(); + + while (it.hasNext()) { + Allocatable allocatable = (Allocatable) it.next(); + classification = allocatable.getClassification(); + width = getControlData(classification, g, width); + } + + // Event check points + classification = reservation.getClassification(); + width = getControlData(classification, g, width); + + + } + + private int getControlData(Classification classification, Graphics g, int inWidth) { + Attribute[] attributes = classification.getAttributes(); + String color = null; + String txt = null; + + int outWidth = inWidth; + for (int k=0; k', 28) + { + private static final long serialVersionUID = 1L; + + public String getToolTipText(MouseEvent e) { + + return getString("plus1month"); + } + + public Point getToolTipLocation(MouseEvent event) { + return new Point((event.getX()), (event.getY() + 20)); + } + }; + + nextButton. setToolTipText(""); // needed to activate tooltip + + panel.setLayout(tableLayout); + calendarPanel.add(dateSelection); + calendarPanel.add(todayButton); + calendarPanel.add(prevButton); + calendarPanel.add(nextButton); + panel.add(calendarPanel, "1, 0"); + + startTimeLabel.setText(getString("start_time")); + calendarPanel.add( startTimeLabel ); + startTime = new RaplaTime(); + startTime.setTimeZone(DateTools.getTimeZone()); + startTime.setRowsPerHour(1); + startTime.setTime(00,00); + calendarPanel.add(startTime); + startTime.addDateChangeListener(listener); + + endTimeLabel.setText(getString("end_time")); + calendarPanel.add( endTimeLabel ); + endTime = new RaplaTime(); + endTime.setTimeZone(DateTools.getTimeZone()); + endTime.setRowsPerHour(1); + endTime.setTime(00,00); + calendarPanel.add(endTime); + endTime.addDateChangeListener(listener); + + JPanel optionsPanel = new JPanel(); + TitledBorder optionsBorder = BorderFactory.createTitledBorder(blackline,getString("options.timeshift")); + optionsPanel.setBorder(optionsBorder); + panel.add(optionsPanel,"7,0"); + + // columns = 7, rows = 2 + optionsPanel.setLayout( new TableLayout(new double[][] {{ pre, 5, pre, 5 , pre, 5, pre }, {10, pre }})); + + timeShiftTimesLabel.setText(getString("horizon")); + optionsPanel.add(timeShiftTimesLabel,"0,1,l,f"); + timeShiftTimes = new RaplaNumber(new Double(1),new Double(0),new Double(12), false); + optionsPanel.add(timeShiftTimes,"2,1,f,f"); + timeShiftTimes.addChangeListener(listener); + timeShifts = getQuery().getPreferences(getUser()).getEntryAsInteger( OccupationOption.MONTHS,0); + timeShiftTimes.setNumber(timeShifts); + + String startDay = getQuery().getPreferences(getUser()).getEntryAsString( OccupationOption.START_DAY,OccupationOption.TODAY); + isStartDayFirstDay = startDay.equals(OccupationOption.FIRSTDAY) ? true : false; + + freeSlotLabel.setText(getString("freeSlot")); + optionsPanel.add(freeSlotLabel,"4,1,l,f"); + freeSlot = new RaplaNumber(new Double(0),new Double(0),new Double(99), false); + optionsPanel.add(freeSlot,"6,1,f,f"); + freeSlot.addChangeListener(listener); + nextButton.addActionListener( listener ); + prevButton.addActionListener( listener ); + + dateSelection.addDateChangeListener( listener ); + todayButton.addActionListener(listener); + update(); + } + + boolean listenersEnabled = true; + public void update() throws RaplaException + { + listenersEnabled = false; + try { + if ( model.getSelectedDate() == null) { + model.setSelectedDate( getQuery().today()); + } + Date date = model.getSelectedDate(); + String startDay = getQuery().getPreferences(getUser()).getEntryAsString( OccupationOption.START_DAY,OccupationOption.TODAY); + isStartDayFirstDay = startDay.equals(OccupationOption.FIRSTDAY) ? true : false; + dateSelection.setDate( setStartOfMonth(date)); + } finally { + listenersEnabled = true; + } + } + + public void dispose() { + } + + public void setNavigationVisible( boolean enable) { + nextButton.setVisible( enable); + prevButton.setVisible( enable); + } + + /** possible values are Calendar.DATE, Calendar.WEEK_OF_YEAR, Calendar.MONTH and Calendar.YEAR. + Default is Calendar.WEEK_OF_YEAR. + */ + public void setIncrementSize(int incrementSize) { + this.incrementSize = incrementSize; + } + + /** registers new DateChangeListener for this component. + * An DateChangeEvent will be fired to every registered DateChangeListener + * when the a different date is selected. + * @see DateChangeListener + * @see DateChangeEvent + */ + public void addDateChangeListener(DateChangeListener listener) { + listenerList.add(listener); + } + + /** removes a listener from this component.*/ + public void removeDateChangeListener(DateChangeListener listener) { + listenerList.remove(listener); + } + + public DateChangeListener[] getDateChangeListeners() { + return (DateChangeListener[])listenerList.toArray(new DateChangeListener[]{}); + } + + /** An ActionEvent will be fired to every registered ActionListener + * when the a different date is selected. + */ + protected void fireDateChange(Date date) { + if (listenerList.size() == 0) + return; + DateChangeListener[] listeners = getDateChangeListeners(); + DateChangeEvent evt = new DateChangeEvent(this,date); + for (int i = 0;i= 0) { + updateDates(date); + } + } + else + if( newStart < newEnd) { + updateDates(date); + } + } + else + if (evt.getSource() == endTime) { + Date date = dateSelection.getDate(); + long newEnd = getTimeMs(endTime.getTime()); + long newStart = getTimeMs(startTime.getTime()); + if (newStart < newEnd) { + updateDates(date); + } + } + //System.out.println("Unknown event: " + evt.toString()); + } finally { + listenersEnabled = true; + } + } + + private void updateDates(Date date) { + model.setSelectedDate( date ); + + duration = getTimeMs(endTime.getTime()); // 24:00 duration = 0 + if(duration == 0) + duration += 86400000; + duration -= getTimeMs(startTime.getTime()); + //System.out.println("Start=" + date.toString() + " Duration="+duration); + fireDateChange( date ); + } + + public void stateChanged(ChangeEvent e) { + Calendar calendar = getRaplaLocale().createCalendar(); + calendar.setTime(dateSelection.getDate()); + Date date = calendar.getTime(); + fireDateChange( date); + } + } +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/plugin/occupationview/images/archive.gif b/Rapla/src/org/rapla/plugin/occupationview/images/archive.gif new file mode 100644 index 0000000..51e703b Binary files /dev/null and b/Rapla/src/org/rapla/plugin/occupationview/images/archive.gif differ diff --git a/Rapla/src/org/rapla/plugin/occupationview/images/eclipse-icons/edit.gif b/Rapla/src/org/rapla/plugin/occupationview/images/eclipse-icons/edit.gif new file mode 100644 index 0000000..9312d7e Binary files /dev/null and b/Rapla/src/org/rapla/plugin/occupationview/images/eclipse-icons/edit.gif differ diff --git a/Rapla/src/org/rapla/plugin/occupationview/images/eclipse-icons/help.gif b/Rapla/src/org/rapla/plugin/occupationview/images/eclipse-icons/help.gif new file mode 100644 index 0000000..9d70301 Binary files /dev/null and b/Rapla/src/org/rapla/plugin/occupationview/images/eclipse-icons/help.gif differ diff --git a/Rapla/src/org/rapla/plugin/occupationview/images/eclipse-icons/inout.gif b/Rapla/src/org/rapla/plugin/occupationview/images/eclipse-icons/inout.gif new file mode 100644 index 0000000..02aec9f Binary files /dev/null and b/Rapla/src/org/rapla/plugin/occupationview/images/eclipse-icons/inout.gif differ diff --git a/Rapla/src/org/rapla/plugin/occupationview/images/eclipse-icons/new.gif b/Rapla/src/org/rapla/plugin/occupationview/images/eclipse-icons/new.gif new file mode 100644 index 0000000..d68588b Binary files /dev/null and b/Rapla/src/org/rapla/plugin/occupationview/images/eclipse-icons/new.gif differ diff --git a/Rapla/src/org/rapla/plugin/occupationview/images/eclipse-icons/trash.gif b/Rapla/src/org/rapla/plugin/occupationview/images/eclipse-icons/trash.gif new file mode 100644 index 0000000..f9c8a2b Binary files /dev/null and b/Rapla/src/org/rapla/plugin/occupationview/images/eclipse-icons/trash.gif differ diff --git a/Rapla/src/org/rapla/plugin/occupationview/images/error.png b/Rapla/src/org/rapla/plugin/occupationview/images/error.png new file mode 100644 index 0000000..5ad474b Binary files /dev/null and b/Rapla/src/org/rapla/plugin/occupationview/images/error.png differ diff --git a/Rapla/src/org/rapla/plugin/occupationview/images/table.png b/Rapla/src/org/rapla/plugin/occupationview/images/table.png new file mode 100644 index 0000000..fe33429 Binary files /dev/null and b/Rapla/src/org/rapla/plugin/occupationview/images/table.png differ diff --git a/Rapla/src/org/rapla/plugin/officeprinting/OfficePrintingDialog.java b/Rapla/src/org/rapla/plugin/officeprinting/OfficePrintingDialog.java new file mode 100644 index 0000000..ca8eaf3 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/officeprinting/OfficePrintingDialog.java @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.officeprinting; + +import java.awt.BorderLayout; + +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTree; + +import org.rapla.entities.domain.Reservation; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.TreeFactory; +import org.rapla.gui.toolkit.RaplaWidget; + +/** sample UseCase that only displays the text of the configuration and + all reservations of the user.*/ +class OfficePrintingDialog extends RaplaGUIComponent implements RaplaWidget +{ + + JLabel label = new JLabel(); + JTree tree; + JPanel panel = new JPanel(); + public final static String ROLE = OfficePrintingDialog.class.getName(); + + public OfficePrintingDialog(RaplaContext sm) throws RaplaException { + super(sm); + setChildBundleName( OfficePrintingPlugin.RESOURCE_FILE); + getLogger().info("MyUseCase started"); + Reservation[] reservations = getQuery().getReservations(getUser(),null,null,null); + tree = new JTree(getTreeFactory().createClassifiableModel(reservations)); + tree.setCellRenderer(getTreeFactory().createRenderer()); + tree.setRootVisible(false); + tree.setShowsRootHandles(true); + panel.setBorder(BorderFactory.createEmptyBorder(40,40,40,40)); + panel.setLayout(new BorderLayout()); + panel.add(label,BorderLayout.NORTH); + panel.add(tree,BorderLayout.CENTER); + label.setText( getString("my_reservations")); + } + + private TreeFactory getTreeFactory() + { + return (TreeFactory) getService(TreeFactory.ROLE); + } + + public JComponent getComponent() { + return panel; + } +} + diff --git a/Rapla/src/org/rapla/plugin/officeprinting/OfficePrintingMenuFactory.java b/Rapla/src/org/rapla/plugin/officeprinting/OfficePrintingMenuFactory.java new file mode 100644 index 0000000..359d2f8 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/officeprinting/OfficePrintingMenuFactory.java @@ -0,0 +1,215 @@ +package org.rapla.plugin.officeprinting; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.apache.avalon.framework.configuration.Configuration; +import org.rapla.components.util.IOUtil; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.User; +import org.rapla.entities.configuration.RaplaConfiguration; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.Classifiable; +import org.rapla.entities.dynamictype.Classification; +import org.rapla.entities.storage.RefEntity; +import org.rapla.entities.storage.internal.SimpleIdentifier; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaContextException; +import org.rapla.framework.RaplaException; +import org.rapla.framework.StartupEnvironment; +import org.rapla.gui.MenuContext; +import org.rapla.gui.ObjectMenuFactory; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.toolkit.RaplaMenuItem; + +public class OfficePrintingMenuFactory extends RaplaGUIComponent implements + ObjectMenuFactory { + + private static final String RESERVATIONID = "RESERVATIONID"; + private static final String USERNAME = "USERNAME"; + private static final String RESOURCENAME = "RESOURCENAME"; + private static final String STARTDATE = "STARTDATE"; + private static final String ENDDATE = "ENDDATE"; + private UnoContact unoContact; + final Map documentInfos; + + public OfficePrintingMenuFactory(RaplaContext context, Configuration config) + throws RaplaException { + super(context); + documentInfos = new HashMap(5); + String option1 = config.getChild(OfficePrintingOption.OPTION1) + .getValue(null); + if (option1 != null) { + documentInfos.put(getDocumentName(option1), option1); + } + String option2 = config.getChild(OfficePrintingOption.OPTION2) + .getValue(null); + if (option2 != null) { + documentInfos.put(getDocumentName(option2), option2); + } + String option3 = config.getChild(OfficePrintingOption.OPTION3) + .getValue(null); + if (option3 != null) { + documentInfos.put(getDocumentName(option3), option3); + } + String option4 = config.getChild(OfficePrintingOption.OPTION4) + .getValue(null); + if (option4 != null) { + documentInfos.put(getDocumentName(option4), option4); + } + String option5 = config.getChild(OfficePrintingOption.OPTION5) + .getValue(null); + if (option5 != null) { + documentInfos.put(getDocumentName(option5), option5); + } + StartupEnvironment env = (StartupEnvironment) context + .lookup(StartupEnvironment.ROLE); + URL contextRootURL = env.getContextRootURL(); + int startupEnv = env.getStartupMode(); + for (String documentName : documentInfos.keySet()) { + String documentURL = documentInfos.get(documentName); + if (startupEnv == StartupEnvironment.WEBSTART + || startupEnv == StartupEnvironment.APPLET) { + try { + documentURL = new URL(contextRootURL, documentURL) + .toString(); + documentInfos.put(documentName, documentURL); + } catch (MalformedURLException e) { + getLogger().error( + "Invalid relative URL '" + documentURL + "'."); + } + } else { + try { + File contextRootFile = IOUtil.getFileFrom(contextRootURL); + File file = new File(contextRootFile, documentURL); + documentURL = file.getCanonicalFile().toURI().toURL() + .toString(); + documentInfos.put(documentName, documentURL); + } catch (IOException e) { + getLogger().error(e.getMessage()); + } + } + } + unoContact = new UnoContact(getLogger(), documentInfos); + } + + private String getDocumentName(String path) { + /* tms problem with IOUtil.fileToURL + File file = new File(path); + URL url = IOUtil.fileToURL(file); + URL baseURL = IOUtil.getBase(url); + String urlStr = url.toString(); + String baseUrlStr = baseURL.toString(); + String ret = urlStr.substring(baseUrlStr.length()); + ret = ret.substring(0, ret.lastIndexOf(".")); + return ret; */ + return null; //tms + } + + @Override + public RaplaMenuItem[] create(final MenuContext menuContext, + final RaplaObject focusedObject) { + if (!focusedObject.getRaplaType().equals(Appointment.TYPE)) { + return RaplaMenuItem.EMPTY_ARRAY; + } + if (!menuContext.has("selected_date")) { + return RaplaMenuItem.EMPTY_ARRAY; + } + final Appointment appointment = (Appointment) focusedObject; + final Reservation reservation = appointment.getReservation(); + try { + menuContext.lookup("selected_date"); + } catch (RaplaContextException e1) { + throw new IllegalStateException( + "selected_date has somehow vanished. This exception should not happen."); + } + + int i = 0; + RaplaMenuItem[] menuItems = new RaplaMenuItem[documentInfos.size()]; + for (final String documentName : documentInfos.keySet()) { + // create the menu entry + final RaplaMenuItem printItem = new RaplaMenuItem( + "OFFICEPRINTING_APPOINTMENT"); + printItem.setIcon(getIcon("icon.print")); + printItem.setSelectedIcon(getIcon("icon.checked")); + printItem.setSelected(true); + printItem.setText(getI18n().getString("printdocument") + " " + + documentName); + // Last the action for the marked menu + printItem.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + int id = ((SimpleIdentifier) ((RefEntity) reservation) + .getId()).getKey(); + String reservationId = Integer.valueOf(id).toString(); + User user = appointment.getOwner(); + String username = user.getName(); + if (username == null || username.length() == 0) { + username = user.getUsername(); + } + Allocatable[] resources = reservation.getResources(); + String resourceName = ""; + if(resources.length != 0) { + Allocatable firstResource = resources[0]; + resourceName = firstResource.getName(getLocale()); + } + Date startDate = appointment.getStart(); + String startDateStr = null; + if (startDate != null) { + startDateStr = getRaplaLocale().formatDateLong( + startDate); + } + Date endDate = appointment.getEnd(); + String endDateStr = null; + if (endDate != null) { + endDateStr = getRaplaLocale().formatDateLong(endDate); + } + Map attributes = getAttributes(reservation); + attributes.put(RESERVATIONID, reservationId); + attributes.put(USERNAME, username); + attributes.put(RESOURCENAME, resourceName); + attributes.put(STARTDATE, startDateStr); + attributes.put(ENDDATE, endDateStr); + unoContact.printForm(documentName, attributes); + } + }); + menuItems[i] = printItem; + i++; + } + return menuItems; + } + + private Map getAttributes(final Classifiable classifiable) { + Map attrMap = new HashMap(); + final Classification classification = classifiable.getClassification(); + final Attribute[] attributes = classification.getAttributes(); + final Locale locale = getRaplaLocale().getLocale(); + for (Attribute attribute : attributes) { + final String name = getName(attribute); + String valueString = null; + final Object value = classification.getValue(attribute); + if (value instanceof Boolean) { + valueString = getString(((Boolean) value).booleanValue() ? "yes" + : "no"); + } else { + valueString = classification + .getValueAsString(attribute, locale); + } + attrMap.put(name, valueString); + } + return attrMap; + } + +} diff --git a/Rapla/src/org/rapla/plugin/officeprinting/OfficePrintingOption.java b/Rapla/src/org/rapla/plugin/officeprinting/OfficePrintingOption.java new file mode 100644 index 0000000..b1dd828 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/officeprinting/OfficePrintingOption.java @@ -0,0 +1,157 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.officeprinting; + +import java.awt.BorderLayout; + +import java.util.Locale; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.DefaultConfiguration; + +import org.rapla.components.layout.TableLayout; +import org.rapla.entities.configuration.Preferences; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.DefaultPluginOption; +import org.rapla.gui.OptionPanel; + +public class OfficePrintingOption extends DefaultPluginOption implements + OptionPanel { + + public final static String OPTION1 = "document-path-1"; + public final static String OPTION2 = "document-path-2"; + public final static String OPTION3 = "document-path-3"; + public final static String OPTION4 = "document-path-4"; + public final static String OPTION5 = "document-path-5"; + public final static String OPTION_DEFAULT = ""; + + JTextField documentPath1 = new JTextField(); + JTextField documentPath2 = new JTextField(); + JTextField documentPath3 = new JTextField(); + JTextField documentPath4 = new JTextField(); + JTextField documentPath5 = new JTextField(); + + public OfficePrintingOption(RaplaContext sm) throws RaplaException { + super(sm); + } + + protected JPanel createPanel() throws RaplaException { + JPanel panel = super.createPanel(); + JPanel content = new JPanel(); + double[][] sizes = new double[][] { + { 5, TableLayout.PREFERRED, 5, TableLayout.PREFERRED, + TableLayout.FILL, 5 }, + { TableLayout.PREFERRED, 5, TableLayout.PREFERRED, 5, TableLayout.PREFERRED, 5, TableLayout.PREFERRED, 5, TableLayout.PREFERRED, 5, TableLayout.PREFERRED } }; + TableLayout tableLayout = new TableLayout(sizes); + content.setLayout(tableLayout); + panel.add(content, BorderLayout.CENTER); + + addCopyPaste(documentPath1); + documentPath1.setColumns(35); + content.add(new JLabel("Document path 1:"), "1,2"); + content.add(documentPath1, "3,2"); + + addCopyPaste(documentPath1); + documentPath1.setColumns(35); + content.add(new JLabel("Document path 2:"), "1,4"); + content.add(documentPath2, "3,4"); + + addCopyPaste(documentPath1); + documentPath1.setColumns(35); + content.add(new JLabel("Document path 3:"), "1,6"); + content.add(documentPath3, "3,6"); + + addCopyPaste(documentPath1); + documentPath1.setColumns(35); + content.add(new JLabel("Document path 4:"), "1,8"); + content.add(documentPath4, "3,8"); + + addCopyPaste(documentPath1); + documentPath1.setColumns(35); + content.add(new JLabel("Document path 5:"), "1,10"); + content.add(documentPath5, "3,10"); + + return panel; + } + + public void show() throws RaplaException { + super.show(); + } + + public void commit() throws RaplaException { + super.commit(); + } + + public void setPreferences(Preferences preferences) { + this.preferences = preferences; + } + + protected void addChildren(DefaultConfiguration newConfig) { + DefaultConfiguration markerLabelConf1 = new DefaultConfiguration(OPTION1); + markerLabelConf1.setValue(documentPath1.getText()); + newConfig.addChild(markerLabelConf1); + + DefaultConfiguration markerLabelConf2 = new DefaultConfiguration(OPTION2); + markerLabelConf2.setValue(documentPath2.getText()); + newConfig.addChild(markerLabelConf2); + + DefaultConfiguration markerLabelConf3 = new DefaultConfiguration(OPTION3); + markerLabelConf3.setValue(documentPath3.getText()); + newConfig.addChild(markerLabelConf3); + + DefaultConfiguration markerLabelConf4 = new DefaultConfiguration(OPTION4); + markerLabelConf4.setValue(documentPath4.getText()); + newConfig.addChild(markerLabelConf4); + + DefaultConfiguration markerLabelConf5 = new DefaultConfiguration(OPTION5); + markerLabelConf5.setValue(documentPath5.getText()); + newConfig.addChild(markerLabelConf5); + } + + protected void readConfig(Configuration config) { + String markerLabelText1 = config.getChild(OPTION1).getValue( + OPTION_DEFAULT); + documentPath1.setText(markerLabelText1); + + String markerLabelText2 = config.getChild(OPTION2).getValue( + OPTION_DEFAULT); + documentPath2.setText(markerLabelText2); + + String markerLabelText3 = config.getChild(OPTION3).getValue( + OPTION_DEFAULT); + documentPath3.setText(markerLabelText3); + + String markerLabelText4 = config.getChild(OPTION4).getValue( + OPTION_DEFAULT); + documentPath4.setText(markerLabelText4); + + String markerLabelText5 = config.getChild(OPTION5).getValue( + OPTION_DEFAULT); + documentPath5.setText(markerLabelText5); + } + + @Override + public String getDescriptorClassName() { + return OfficePrintingPlugin.class.getName(); + } + + public String getName(Locale locale) { + return "Office Printing Plugin"; + } + +} diff --git a/Rapla/src/org/rapla/plugin/officeprinting/OfficePrintingPlugin.java b/Rapla/src/org/rapla/plugin/officeprinting/OfficePrintingPlugin.java new file mode 100644 index 0000000..fbb21aa --- /dev/null +++ b/Rapla/src/org/rapla/plugin/officeprinting/OfficePrintingPlugin.java @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.officeprinting; +import org.apache.avalon.framework.configuration.Configuration; +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.components.xmlbundle.impl.I18nBundleImpl; +import org.rapla.framework.Container; +import org.rapla.framework.PluginDescriptor; +import org.rapla.plugin.RaplaExtensionPoints; +import org.rapla.plugin.appointmentmarker.AppointmentMarkerMenuFactory; + +/** + This is a demonstration of a rapla-plugin. It adds a sample usecase and option + to the rapla-system. + */ + +public class OfficePrintingPlugin + implements + PluginDescriptor +{ + public static final String RESOURCE_FILE = OfficePrintingPlugin.class.getPackage().getName() + ".OfficePrintingResources"; + public static final String PLUGIN_CLASS = OfficePrintingPlugin.class.getName(); + + public String toString() { + return "OfficePrinting"; + } + + /** + * @see org.rapla.framework.PluginDescriptor#provideServices(org.rapla.framework.general.Container) + */ + public void provideServices(Container container, Configuration config) { + if ( !config.getAttributeAsBoolean("enabled", false) ) + return; + + container.addContainerProvidedComponent( I18nBundle.ROLE, I18nBundleImpl.class.getName(), RESOURCE_FILE,I18nBundleImpl.createConfig( RESOURCE_FILE ) ); + //container.addContainerProvidedComponent( RaplaExtensionPoints.CLIENT_EXTENSION, OfficePrintingInitializer.class.getName(), PLUGIN_CLASS, config); + container.addContainerProvidedComponent( RaplaExtensionPoints.PLUGIN_OPTION_PANEL_EXTENSION, OfficePrintingOption.class.getName(),PLUGIN_CLASS, config); + container.addContainerProvidedComponent( RaplaExtensionPoints.OBJECT_MENU_EXTENSION, OfficePrintingMenuFactory.class.getName(), PLUGIN_CLASS, config); + + } + + public Object getPluginMetaInfos( String key ) + { + return null; + } + +} + diff --git a/Rapla/src/org/rapla/plugin/officeprinting/OfficePrintingResources.xml b/Rapla/src/org/rapla/plugin/officeprinting/OfficePrintingResources.xml new file mode 100644 index 0000000..266d5a1 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/officeprinting/OfficePrintingResources.xml @@ -0,0 +1,9 @@ + + + + + My reservations + Meine Reservierungen + mes réservations + + diff --git a/Rapla/src/org/rapla/plugin/officeprinting/UnoContact.java b/Rapla/src/org/rapla/plugin/officeprinting/UnoContact.java new file mode 100644 index 0000000..91741f5 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/officeprinting/UnoContact.java @@ -0,0 +1,215 @@ +package org.rapla.plugin.officeprinting; + +import java.io.File; +import java.util.Map; + +import org.apache.avalon.framework.logger.Logger; +import com.sun.star.beans.PropertyValue; +import com.sun.star.beans.UnknownPropertyException; +import com.sun.star.comp.helper.Bootstrap; +import com.sun.star.container.NoSuchElementException; +import com.sun.star.container.XEnumeration; +import com.sun.star.container.XEnumerationAccess; +import com.sun.star.frame.XComponentLoader; +import com.sun.star.frame.XDesktop; +import com.sun.star.lang.IllegalArgumentException; +import com.sun.star.lang.WrappedTargetException; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.text.XDependentTextField; +import com.sun.star.text.XTextDocument; +import com.sun.star.text.XTextFieldsSupplier; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; +import com.sun.star.util.CloseVetoException; +import com.sun.star.util.XCloseable; +import com.sun.star.view.XPrintable; + +public class UnoContact { + + private final static String DOCUMENTTYPE_WRITER = "swriter"; + private Logger logger; + private Map documentInfos; + + public UnoContact( Logger logger, + Map documentInfos) { + this.logger = logger; + this.documentInfos = documentInfos; + } + + public void printForm(String documentName, Map attributes) { + XDesktop xDesktop = getDesktop(); + try { + XComponent document = loadDocument(xDesktop, DOCUMENTTYPE_WRITER, + getOOURL(documentInfos.get(documentName))); + for (String key : attributes.keySet()) { + replaceField(document, key, attributes.get(key)); + } + printDocument(document); + closeDocument(document); + } catch (CloseVetoException e) { + logger.error(e.getMessage(), e); + } catch (NoSuchElementException e) { + logger.error(e.getMessage(), e); + } catch (WrappedTargetException e) { + logger.error(e.getMessage(), e); + } catch (UnknownPropertyException e) { + logger.error(e.getMessage(), e); + } + } + + public XDesktop getDesktop() { + XDesktop xDesktop = null; + XMultiComponentFactory xMCF = null; + + try { + XComponentContext xContext = null; + xContext = Bootstrap.bootstrap(); + xMCF = xContext.getServiceManager(); + if (xMCF != null) { + logger.info("Connected to a running office ..."); + Object oDesktop = xMCF.createInstanceWithContext( + "com.sun.star.frame.Desktop", xContext); + xDesktop = (XDesktop) UnoRuntime.queryInterface(XDesktop.class, + oDesktop); + } else { + logger.error("Can't create a desktop." + + "No connection, no remote office servicemanager available!"); + } + } catch (Exception e) { + e.printStackTrace(System.err); + System.exit(1); + } + return xDesktop; + } + + public XTextDocument createTextdocument(XDesktop xDesktop) { + + XTextDocument aTextDocument = null; + + try { + XComponent xComponent = createNewDocument(xDesktop, + DOCUMENTTYPE_WRITER); + aTextDocument = (XTextDocument) UnoRuntime.queryInterface( + XTextDocument.class, xComponent); + } catch (Exception e) { + e.printStackTrace(System.err); + } + return aTextDocument; + } + + public XComponent createNewDocument(XDesktop xDesktop, String sDocumentType) { + + String sURL = "private:factory/" + sDocumentType; + + XComponent xComponent = null; + XComponentLoader xComponentLoader = null; + PropertyValue xEmptyArgs[] = new PropertyValue[0]; + + try { + xComponentLoader = (XComponentLoader) UnoRuntime.queryInterface( + XComponentLoader.class, xDesktop); + xComponent = xComponentLoader.loadComponentFromURL(sURL, "_blank", + 0, xEmptyArgs); + } catch (Exception e) { + + e.printStackTrace(System.err); + } + return xComponent; + } + + public XComponent loadDocument(XDesktop xDesktop, String sDocumentType, + String documentURL) { + XComponent xComponent = null; + XComponentLoader xComponentLoader = null; + PropertyValue xEmptyArgs[] = new PropertyValue[0]; + + try { + xComponentLoader = (XComponentLoader) UnoRuntime.queryInterface( + XComponentLoader.class, xDesktop); + xComponent = xComponentLoader.loadComponentFromURL(documentURL, + "_blank", 0, xEmptyArgs); + + } catch (Exception e) { + + e.printStackTrace(System.err); + } + return xComponent; + } + + public String getOOURL(String url) { + if (File.separator.equals("/")) { + return url.replaceFirst("file:/", "file:///"); + } else { + return url.replaceFirst("file:/", "file://"); + } + } + + public void closeDocument(XComponent document) throws CloseVetoException { + XCloseable xcloseable = (XCloseable) UnoRuntime.queryInterface( + XCloseable.class, document); + xcloseable.close(false); + } + + public void replaceField(XComponent document, String fieldName, + String fieldValue) throws NoSuchElementException, + WrappedTargetException, UnknownPropertyException { + XTextFieldsSupplier xTextFieldsSupplier = (XTextFieldsSupplier) UnoRuntime + .queryInterface(XTextFieldsSupplier.class, document); + XEnumerationAccess xEnumeratedFields = xTextFieldsSupplier + .getTextFields(); + XEnumeration xEnum = xEnumeratedFields.createEnumeration(); + while (xEnum.hasMoreElements()) { + // get the next element as a UNO Any + Object aField = xEnum.nextElement(); + // get the name of the sheet from its XNamed interface + XDependentTextField xUserField = (XDependentTextField) UnoRuntime + .queryInterface(XDependentTextField.class, aField); + String foundFieldName = (String) xUserField.getTextFieldMaster() + .getPropertyValue("Name"); + if (fieldName.equals(foundFieldName)) { + try { + xUserField.getTextFieldMaster().setPropertyValue("Content", + fieldValue); + } catch (Exception e) { + logger.error("Cannot set field value '" + fieldValue + + "' for '" + fieldName + "': " + e.getMessage()); + } + // break; + } + } + } + + public void printDocument(XComponent document) { + XPrintable xPrintable = (XPrintable) UnoRuntime.queryInterface( + XPrintable.class, document); + PropertyValue[] printerValues = xPrintable.getPrinter(); + String printerName = getPropertyValue(printerValues, "Name"); + PropertyValue[] printerDesc = new PropertyValue[1]; + printerDesc[0] = new PropertyValue(); + printerDesc[0].Name = "Name"; + printerDesc[0].Value = printerName; + try { + xPrintable.setPrinter(printerDesc); + PropertyValue[] printOpts = new PropertyValue[1]; + printOpts[0] = new PropertyValue(); + printOpts[0].Name = "Pages"; + printOpts[0].Value = "1"; + xPrintable.print(printOpts); + } catch (IllegalArgumentException e) { + logger.error("Error printing document: " + e.getMessage()); + } + } + + public String getPropertyValue(PropertyValue[] values, String name) { + String ret = null; + for (PropertyValue value : values) { + if (value.Name.equals(name)) { + ret = (String) value.Value; + break; + } + } + return ret; + } + +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/plugin/package.html b/Rapla/src/org/rapla/plugin/package.html new file mode 100644 index 0000000..f3f474b --- /dev/null +++ b/Rapla/src/org/rapla/plugin/package.html @@ -0,0 +1,9 @@ + +This is the base package of the GUI-client. Communication through the backend +is done through the modules of org.rapla.facade package. +The gui-client is normally started through the RaplaClientService. +You can also plug-in your own components into the gui. + + + + diff --git a/Rapla/src/org/rapla/plugin/periodcopy/CopyDialog.java b/Rapla/src/org/rapla/plugin/periodcopy/CopyDialog.java new file mode 100644 index 0000000..18e29be --- /dev/null +++ b/Rapla/src/org/rapla/plugin/periodcopy/CopyDialog.java @@ -0,0 +1,83 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.periodcopy; + +import javax.swing.BorderFactory; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; + +import org.rapla.components.layout.TableLayout; +import org.rapla.entities.domain.Period; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.toolkit.RaplaWidget; + +/** sample UseCase that only displays the text of the configuration and + all reservations of the user.*/ +class CopyDialog extends RaplaGUIComponent implements RaplaWidget +{ + + JComboBox sourcePeriodChooser = new JComboBox(new String[] {"a", "b"}); + JComboBox destPeriodChooser = new JComboBox(new String[] {"a", "b"}); + JPanel panel = new JPanel(); + JLabel label = new JLabel(); + JList selectedReservations = new JList(); + public final static String ROLE = CopyDialog.class.getName(); + + public CopyDialog(RaplaContext sm) throws RaplaException { + super(sm); + setChildBundleName( PeriodCopyPlugin.RESOURCE_FILE); + Period[] periods = getQuery().getPeriods(); + sourcePeriodChooser.setModel( new DefaultComboBoxModel( periods )); + destPeriodChooser.setModel( new DefaultComboBoxModel( periods )); + label.setText(getString("copy_selected_events_from")); + panel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); + panel.setLayout(new TableLayout(new double[][]{ + {TableLayout.PREFERRED ,5 , TableLayout.FILL } + ,{20, 5, TableLayout.PREFERRED ,5 ,TableLayout.PREFERRED, 5, TableLayout.PREFERRED } + } + )); + selectedReservations.setEnabled( false ); + + panel.add(label, "0,0,2,1"); + panel.add( new JLabel(getString("source")),"0,2" ); + panel.add( sourcePeriodChooser,"2,2" ); + panel.add( new JLabel(getString("destination")),"0,4" ); + panel.add( destPeriodChooser,"2,4" ); + panel.add( new JLabel(getString("reservations")) , "0,6,l,t"); + panel.add( new JScrollPane( selectedReservations ),"2,6" ); + } + + public JComboBox getDestChooser() { + return destPeriodChooser; + } + + public JComboBox getSourceChooser() { + return sourcePeriodChooser; + } + + public JList getReservationList() { + return selectedReservations; + } + + public JComponent getComponent() { + return panel; + } +} + diff --git a/Rapla/src/org/rapla/plugin/periodcopy/CopyPluginInit.java b/Rapla/src/org/rapla/plugin/periodcopy/CopyPluginInit.java new file mode 100644 index 0000000..e5f85e9 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/periodcopy/CopyPluginInit.java @@ -0,0 +1,170 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.periodcopy; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +import javax.swing.DefaultListModel; +import javax.swing.JComboBox; +import javax.swing.ListModel; + +import org.rapla.entities.Entity; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Period; +import org.rapla.entities.domain.Repeating; +import org.rapla.entities.domain.Reservation; +import org.rapla.facade.PeriodModel; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.MenuExtensionPoint; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.toolkit.DialogUI; +import org.rapla.gui.toolkit.RaplaMenuItem; +import org.rapla.gui.toolkit.RaplaSeparator; +import org.rapla.plugin.RaplaExtensionPoints; + +public class CopyPluginInit extends RaplaGUIComponent +{ + public CopyPluginInit(RaplaContext sm) throws RaplaException { + super(sm); + setChildBundleName( PeriodCopyPlugin.RESOURCE_FILE); + MenuExtensionPoint menu = (MenuExtensionPoint) getService( RaplaExtensionPoints.EDIT_MENU_EXTENSION_POINT); + menu.insert( new RaplaSeparator("info_end")); + menu.insert(createInfoMenu()); + } + + private RaplaMenuItem createInfoMenu( ) { + RaplaMenuItem item = new RaplaMenuItem("copy_events"); + +// ResourceBundle bundle = ResourceBundle.getBundle( "org.rapla.plugin.periodcopy.PeriodCopy"); + final String label =getString("copy_events") ; + //bundle.getString("copy_events"); + + item.setText( label ); + item.setIcon( getIcon("icon.copy") ); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + try { + final CopyDialog useCase = new CopyDialog(getContext()); + String[] buttons = new String[]{getString("abort"), getString("copy") }; + final DialogUI dialog = DialogUI.create( getContext(),getMainComponent(),true, useCase.getComponent(), buttons); + dialog.setTitle( label); + dialog.setSize( 400, 400); + dialog.getButton( 0).setIcon( getIcon("icon.abort")); + dialog.getButton( 1).setIcon( getIcon("icon.copy")); + dialog.getButton( 1).setEnabled(false); + final JComboBox sourceBox = useCase.getSourceChooser(); + final JComboBox destBox = useCase.getDestChooser(); + PeriodModel periodModel = getPeriodModel(); + final CalendarModel model = (CalendarModel) getService( CalendarModel.ROLE); + Period period = (Period) periodModel.getNearestPeriodForStartDate( model.getSelectedDate()); + if ( period != null) { + sourceBox.setSelectedItem( period ); + destBox.setSelectedItem( period ); + useCase.getReservationList().setModel( getReservationList( model, period) ); + } + ActionListener listener = new ActionListener() { + public void actionPerformed(ActionEvent arg0) { + int destIndex = destBox.getSelectedIndex(); + int sourceIndex = sourceBox.getSelectedIndex(); + dialog.getButton( 1).setEnabled( destIndex >= 0 && sourceIndex >=0 && destIndex != sourceIndex); + Period sourcePeriod = (Period)sourceBox.getSelectedItem(); + try { + useCase.getReservationList().setModel( getReservationList( model, sourcePeriod) ); + } catch (Exception ex ) { + showException( ex, getMainComponent()); + } + } + }; + sourceBox.addActionListener( listener ); + destBox.addActionListener( listener ); + dialog.startNoPack(); + if ( dialog.getSelectedIndex() == 1) { + Period sourcePeriod = (Period)sourceBox.getSelectedItem(); + Period destPeriod = (Period)destBox.getSelectedItem(); + if (sourcePeriod != null && destPeriod != null) + copy( model, sourcePeriod,destPeriod ); + } + } catch (Exception ex) { + showException( ex, getMainComponent() ); + } + } + }); + return item; + } + + private ListModel getReservationList(CalendarModel model, Period period) throws RaplaException { + DefaultListModel listModel = new DefaultListModel(); + if ( period != null) { + Reservation[] reservations = model.getReservations( period.getStart(), period.getEnd() ); + for ( int i=0; i 0) { + newReservations.add( r ); + } + + } + getModification().storeObjects( (Entity[]) newReservations.toArray( Entity.ENTITY_ARRAY) ); + + } + + +} + diff --git a/Rapla/src/org/rapla/plugin/periodcopy/PeriodCopy.properties b/Rapla/src/org/rapla/plugin/periodcopy/PeriodCopy.properties new file mode 100644 index 0000000..2207c9e --- /dev/null +++ b/Rapla/src/org/rapla/plugin/periodcopy/PeriodCopy.properties @@ -0,0 +1,2 @@ +copy_events=copy events +copy_selected_events_from=copy selected events from \ No newline at end of file diff --git a/Rapla/src/org/rapla/plugin/periodcopy/PeriodCopyPlugin.java b/Rapla/src/org/rapla/plugin/periodcopy/PeriodCopyPlugin.java new file mode 100644 index 0000000..5d5aeed --- /dev/null +++ b/Rapla/src/org/rapla/plugin/periodcopy/PeriodCopyPlugin.java @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.periodcopy; +import org.apache.avalon.framework.configuration.Configuration; +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.components.xmlbundle.impl.I18nBundleImpl; +import org.rapla.framework.Container; +import org.rapla.framework.PluginDescriptor; +import org.rapla.plugin.RaplaExtensionPoints; + +public class PeriodCopyPlugin implements PluginDescriptor +{ + + public static final String PLUGIN_CLASS = PeriodCopyPlugin.class.getName(); + public static final String RESOURCE_FILE = PeriodCopyPlugin.class.getPackage().getName() + ".PeriodCopy"; + public String toString() { + return "Copy Periods"; + } + + public void provideServices(Container container, Configuration config) { + if ( !config.getAttributeAsBoolean("enabled", false) ) + return; + + container.addContainerProvidedComponent( I18nBundle.ROLE, I18nBundleImpl.class.getName(), RESOURCE_FILE,I18nBundleImpl.createConfig( RESOURCE_FILE ) ); + container.addContainerProvidedComponent( RaplaExtensionPoints.CLIENT_EXTENSION, CopyPluginInit.class.getName(), PLUGIN_CLASS, config); + } + + public Object getPluginMetaInfos( String key ) + { + return null; + } + +} + diff --git a/Rapla/src/org/rapla/plugin/periodcopy/PeriodCopy_de.properties b/Rapla/src/org/rapla/plugin/periodcopy/PeriodCopy_de.properties new file mode 100644 index 0000000..744114c --- /dev/null +++ b/Rapla/src/org/rapla/plugin/periodcopy/PeriodCopy_de.properties @@ -0,0 +1,2 @@ +copy_events=Kopiere Veranstaltungen +copy_selected_events_from=Ausgewhlte Veranstaltungen kopieren von: \ No newline at end of file diff --git a/Rapla/src/org/rapla/plugin/periodview/HTMLPeriodViewPage.java b/Rapla/src/org/rapla/plugin/periodview/HTMLPeriodViewPage.java new file mode 100644 index 0000000..83c541b --- /dev/null +++ b/Rapla/src/org/rapla/plugin/periodview/HTMLPeriodViewPage.java @@ -0,0 +1,112 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.periodview; + +import java.util.Calendar; +import java.util.Date; +import java.util.Iterator; +import java.util.Set; + +import org.rapla.components.calendarview.Block; +import org.rapla.components.calendarview.html.AbstractHTMLView; +import org.rapla.components.calendarview.html.HTMLPeriodView; +import org.rapla.components.util.DateTools; +import org.rapla.entities.domain.Period; +import org.rapla.facade.PeriodModel; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.CalendarOptions; +import org.rapla.plugin.abstractcalendar.AbstractHTMLCalendarPage; +import org.rapla.plugin.abstractcalendar.AbstractRaplaBlock; +import org.rapla.plugin.abstractcalendar.GroupAllocatablesStrategy; +import org.rapla.plugin.abstractcalendar.RaplaBuilder; + +public class HTMLPeriodViewPage extends AbstractHTMLCalendarPage +{ + public HTMLPeriodViewPage( RaplaContext context, CalendarModel calendarModel ) throws RaplaException + { + super( context, calendarModel ); + } + + protected AbstractHTMLView createCalendarView() { + HTMLPeriodView periodView = new HTMLPeriodView() { + public Block sameAppointment(int day, Block bl) { + Iterator it2 = slots[day].iterator(); + Block b; + while (it2.hasNext()) { + b = (Block)it2.next(); + if ( ((AbstractRaplaBlock)b).getAppointment() + == ((AbstractRaplaBlock)bl).getAppointment() + ) { + return b; + } + } + return null; + } + }; + CalendarOptions opt = getCalendarOptions(); + Set excludeDays = opt.getExcludeDays(); + + periodView.setExcludeDays( excludeDays ); + periodView.setLocale( getRaplaLocale().getLocale() ); + periodView.setTimeZone(getRaplaLocale().getTimeZone()); + + Date start = model.getStartDate(); + Date end = model.getEndDate(); + Date selectedDate = model.getSelectedDate(); + PeriodModel periodModel =getPeriodModel(); + Period selectedPeriod = periodModel.getNearestPeriodForStartDate( start, end); + + /* + * If the user does not change the start date then + * the choosen period is displayed. + * If the user choose another date, then 7 weeks are displayed. + */ + if ( start == null ) { + start = getQuery().today(); + end = DateTools.addDays(start, 7 * 7); + } else { + if ( selectedPeriod == null + || selectedPeriod.getStart().getTime() + != selectedDate.getTime()) { + end = DateTools.addDays(selectedDate, 7 * 7); + } else { + start = selectedPeriod.getStart(); + end = selectedPeriod.getEnd(); + } + } + + periodView.setToDate( start ); + periodView.setEndDate( end ); + + return periodView; + } + + protected RaplaBuilder createBuilder() throws RaplaException { + RaplaBuilder builder = super.createBuilder(); + + GroupAllocatablesStrategy strategy = new GroupAllocatablesStrategy( getRaplaLocale().getLocale() ); + boolean compactColumns = getCalendarOptions().isCompactColumns() || builder.getAllocatables().size() ==0 ; + strategy.setFixedSlotsEnabled( !compactColumns); + builder.setBuildStrategy( strategy ); + + return builder; + } + + protected int getIncrementSize() { + return Calendar.WEEK_OF_YEAR; + } + +} + diff --git a/Rapla/src/org/rapla/plugin/periodview/PeriodViewFactory.java b/Rapla/src/org/rapla/plugin/periodview/PeriodViewFactory.java new file mode 100644 index 0000000..76895e1 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/periodview/PeriodViewFactory.java @@ -0,0 +1,69 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.periodview; + +import javax.swing.Icon; + +import org.rapla.facade.RaplaComponent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.SwingCalendarView; +import org.rapla.gui.ViewFactory; +import org.rapla.gui.images.Images; +import org.rapla.servletpages.RaplaPageGenerator; + +public class PeriodViewFactory extends RaplaComponent implements ViewFactory +{ + public PeriodViewFactory( RaplaContext context ) throws RaplaException + { + super( context ); + } + + public final static String PERIOD_VIEW = "period"; + + public SwingCalendarView createSwingView(RaplaContext context, CalendarModel model, boolean editable) throws RaplaException + { + return new SwingPeriodCalendar( context, model, editable); + } + + public RaplaPageGenerator createHTMLView(RaplaContext context, CalendarModel model) throws RaplaException + { + return new HTMLPeriodViewPage( context, model); + } + + public String getViewId() + { + return PERIOD_VIEW; + } + + public String getName() + { + return getString(PERIOD_VIEW); + } + + Icon icon; + public Icon getIcon() + { + if ( icon == null) { + icon = Images.getIcon("/org/rapla/plugin/periodview/images/period_view.png"); + } + return icon; + } + + public String getMenuSortKey() { + return "E"; + } + +} + diff --git a/Rapla/src/org/rapla/plugin/periodview/PeriodViewPlugin.java b/Rapla/src/org/rapla/plugin/periodview/PeriodViewPlugin.java new file mode 100644 index 0000000..7265be7 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/periodview/PeriodViewPlugin.java @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.periodview; + +import org.apache.avalon.framework.configuration.Configuration; +import org.rapla.framework.Container; +import org.rapla.framework.PluginDescriptor; +import org.rapla.plugin.RaplaExtensionPoints; +import org.rapla.plugin.RaplaPluginMetaInfo; + +public class PeriodViewPlugin implements PluginDescriptor +{ + static boolean ENABLE_BY_DEFAULT = false; + + public String toString() { + return "Period View"; + } + + public void provideServices(Container container, Configuration config) { + if ( !config.getAttributeAsBoolean("enabled", ENABLE_BY_DEFAULT) ) + return; + + container.addContainerProvidedComponent + ( + RaplaExtensionPoints.CALENDAR_VIEW_EXTENSION + ,PeriodViewFactory.class.getName() + ,PeriodViewFactory.PERIOD_VIEW + ,null + ); + } + + + public Object getPluginMetaInfos( String key ) + { + if ( RaplaPluginMetaInfo.METAINFO_PLUGIN_ENABLED_BY_DEFAULT.equals( key )) { + return new Boolean( ENABLE_BY_DEFAULT ); + } + return null; + } + + +} + diff --git a/Rapla/src/org/rapla/plugin/periodview/SwingPeriodCalendar.java b/Rapla/src/org/rapla/plugin/periodview/SwingPeriodCalendar.java new file mode 100644 index 0000000..66ca03d --- /dev/null +++ b/Rapla/src/org/rapla/plugin/periodview/SwingPeriodCalendar.java @@ -0,0 +1,144 @@ + +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.plugin.periodview; + +import java.awt.Component; +import java.awt.Point; +import java.util.Calendar; +import java.util.Date; +import java.util.Set; + +import org.rapla.components.calendarview.swing.AbstractSwingCalendar; +import org.rapla.components.calendarview.swing.SwingPeriodView; +import org.rapla.components.calendarview.swing.ViewListener; +import org.rapla.components.util.DateTools; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Period; +import org.rapla.facade.PeriodModel; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.CalendarOptions; +import org.rapla.plugin.abstractcalendar.AbstractRaplaSwingCalendar; +import org.rapla.plugin.abstractcalendar.GroupAllocatablesStrategy; +import org.rapla.plugin.abstractcalendar.RaplaBuilder; +import org.rapla.plugin.abstractcalendar.RaplaCalendarViewListener; +import org.rapla.plugin.abstractcalendar.SwingRaplaBuilder; + + +public class SwingPeriodCalendar extends AbstractRaplaSwingCalendar +{ + + public SwingPeriodCalendar(RaplaContext sm,CalendarModel settings, boolean editable) throws RaplaException { + super( sm, settings, editable); + } + + protected AbstractSwingCalendar createView( boolean showScrollPane) + { + return new SwingPeriodView( showScrollPane ); + } + + protected ViewListener createListener() throws RaplaException { + return new RaplaCalendarViewListener(getContext(), model, view) { + /** override to change start- and end-dates and the repeating count */ + protected void showPopupMenu(Component component,Point p,Date start,Date end, int slotNr, Allocatable[] selectedAllocatables, Allocatable selectedAllocatable) { + int repeatings = (int)((end.getTime() - start.getTime())/DateTools.MILLISECONDS_PER_WEEK) + 1; + Calendar cal = getRaplaLocale().createCalendar(); + + cal.setTime(getModel().getSelectedDate()); + int firstDay = cal.get(Calendar.DAY_OF_WEEK) ; + + cal.setTime ( end ); + int hour = cal.get(Calendar.HOUR_OF_DAY); + int minute = cal.get(Calendar.MINUTE); + int day_of_week = cal.get(Calendar.DAY_OF_WEEK); + while ( day_of_week < firstDay ) { + day_of_week += 7; + } + + cal.setTime ( start ); + int day_of_week_end = cal.get(Calendar.DAY_OF_WEEK) ; + while ( day_of_week_end < firstDay ) { + day_of_week_end += 7; + } + + int decal = day_of_week - day_of_week_end; + // System.out.println("start=" + start + "\nend=" + end + "\nfirstday=" + firstDay + " day_of_week=" + day_of_week + " dayofweekend=" + day_of_week_end + " decal=" + decal); + if ( decal < 0 ) { + cal.add(Calendar.DATE, decal); + start = new Date ( cal.getTime().getTime() ); + cal.add(Calendar.DATE, -decal); + repeatings++ ; + } + + cal.set(Calendar.HOUR_OF_DAY, hour); + cal.set(Calendar.MINUTE, minute); + if ( decal > 0 ) + cal.add(Calendar.DATE, decal); + end = new Date ( cal.getTime().getTime() ); + showPopupMenu( component, p, start,end, slotNr, selectedAllocatables, selectedAllocatable, repeatings); + } + }; + } + + protected void configureView() throws RaplaException { + CalendarOptions calendarOptions = getCalendarOptions(); + Set excludeDays = calendarOptions.getExcludeDays(); + view.setExcludeDays( excludeDays ); + { + SwingPeriodView view = (SwingPeriodView)this.view; + view.setRowsPerHour( 1 ); + view.setRowSize( 16); + } + if ( !view.isEditable() ) { + view.setSlotSize( model.getSize()); + } else { + view.setSlotSize( 140 ); + } + + ((SwingPeriodView)view).setWorktime( calendarOptions.getWorktimeStart(), calendarOptions.getWorktimeEnd()); + + PeriodModel periodModel =getPeriodModel(); + Date start = model.getSelectedDate(); + Date end = model.getEndDate(); + Period selectedPeriod = periodModel.getNearestPeriodForStartDate( start, end); + if ( selectedPeriod != null) { + end = selectedPeriod.getEnd(); + } + view.setToDate( start ); + ((SwingPeriodView)view).setPeriodEnd( end ); + } + + protected RaplaBuilder createBuilder() throws RaplaException + { + RaplaBuilder builder = new SwingRaplaBuilder(getContext()); + builder.setRepeatingVisible( view.isEditable()); + builder.setEditingUser( getUser() ); + builder.setExceptionsExcluded( !getCalendarOptions().isExceptionsVisible() || !view.isEditable()); + builder.setFromModel( model, view.getStartDate(), view.getEndDate() ); + + GroupAllocatablesStrategy strategy = new GroupAllocatablesStrategy( getRaplaLocale().getLocale() ); + boolean compactColumns = getCalendarOptions().isCompactColumns() || builder.getAllocatables().size() ==0 ; + strategy.setFixedSlotsEnabled( !compactColumns); + strategy.setResolveConflictsEnabled( true ); + builder.setBuildStrategy( strategy ); + return builder; + } + + public int getIncrementSize() + { + return Calendar.WEEK_OF_YEAR; + } +} diff --git a/Rapla/src/org/rapla/plugin/periodview/images/period_view.png b/Rapla/src/org/rapla/plugin/periodview/images/period_view.png new file mode 100644 index 0000000..30ed961 Binary files /dev/null and b/Rapla/src/org/rapla/plugin/periodview/images/period_view.png differ diff --git a/Rapla/src/org/rapla/plugin/periodwizard/AppointmentSelection.java b/Rapla/src/org/rapla/plugin/periodwizard/AppointmentSelection.java new file mode 100644 index 0000000..d49f56e --- /dev/null +++ b/Rapla/src/org/rapla/plugin/periodwizard/AppointmentSelection.java @@ -0,0 +1,349 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.periodwizard; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.Iterator; +import java.util.Locale; + +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; + +import org.rapla.components.calendar.DateChangeEvent; +import org.rapla.components.calendar.DateChangeListener; +import org.rapla.components.calendar.RaplaTime; +import org.rapla.components.layout.TableLayout; +import org.rapla.components.util.DateTools; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Period; +import org.rapla.entities.domain.Repeating; +import org.rapla.entities.domain.RepeatingType; +import org.rapla.entities.domain.Reservation; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.framework.RaplaLocale; +import org.rapla.gui.internal.edit.reservation.AbstractAppointmentEditor; +import org.rapla.gui.internal.edit.reservation.AppointmentIdentifier; +import org.rapla.gui.toolkit.WeekdayChooser; + +class AppointmentSelection extends AbstractAppointmentEditor +{ + ArrayList appointmentList = new ArrayList(); + JPanel appointmentListPanel = new JPanel(); + Period period; + Reservation reservation; + + public AppointmentSelection(RaplaContext sm) throws RaplaException { + super( sm); + appointmentListPanel.setLayout(new BoxLayout(appointmentListPanel + ,BoxLayout.Y_AXIS)); + + addNewRow(); + addNewRow(); + addNewRow(); + addNewRow(); + addNewRow(); + } + + public void newAppointment(Date start,Date end) throws RaplaException { + Iterator it = appointmentList.iterator(); + while (it.hasNext()) { + AppointmentPanel panel = (AppointmentPanel) it.next(); + if (!panel.isUsed()) { + panel.newAppointment(start,end); + return; + } + } + addNewRow(); + AppointmentPanel panel = (AppointmentPanel) appointmentList.get(appointmentList.size()-1); + panel.newAppointment(start,end); + appointmentListPanel.revalidate(); + appointmentListPanel.repaint(); + } + + public void removeAllAppointments() { + Iterator it = appointmentList.iterator(); + while (it.hasNext()) { + AppointmentPanel panel = (AppointmentPanel) it.next(); + panel.clearAll(); + } + } + + public void addNewRow() { + AppointmentPanel panel = new AppointmentPanel(getRaplaLocale().getLocale()); + appointmentListPanel.add(panel.getComponent()); + appointmentList.add(panel); + } + + public void removeAppointmentPanel(AppointmentPanel panel) { + appointmentListPanel.remove(panel.getComponent()); + appointmentList.remove(panel); + if (appointmentList.size() <5) + addNewRow(); + appointmentListPanel.revalidate(); + appointmentListPanel.repaint(); + } + + public JComponent getComponent() { + return appointmentListPanel; + } + + public void setReservation(Reservation reservation) { + this.reservation = reservation; + + } + public void setPeriod(Period period) { + this.period = period; + } + + protected void fireAppointmentAdded(Appointment appointment) { + super.fireAppointmentAdded(appointment); + Iterator it = appointmentList.iterator(); + while (it.hasNext()) + ((AppointmentPanel) it.next()).updateIndex(); + } + + protected void fireAppointmentRemoved(Appointment appointment) { + super.fireAppointmentRemoved(appointment); + Iterator it = appointmentList.iterator(); + while (it.hasNext()) + ((AppointmentPanel) it.next()).updateIndex(); + } + + public class AppointmentPanel implements ActionListener,DateChangeListener { + AppointmentIdentifier identifier = new AppointmentIdentifier(); + JPanel content = new JPanel(); + private Calendar calendar; + WeekdayChooser weekdayChooser; + JLabel newLabel = new JLabel(); + JButton delete = new JButton(); + RaplaTime startTime; + JLabel startLabel = new JLabel(); + RaplaTime endTime; + JLabel endLabel = new JLabel(); + Appointment appointment; + private boolean listenerEnabled = true; + public AppointmentPanel(Locale locale) { + calendar = Calendar.getInstance(DateTools.getTimeZone(),locale); + + TableLayout tableLayout = new TableLayout(new double[][] { + { + TableLayout.PREFERRED + ,28 + ,TableLayout.PREFERRED + ,5 + ,TableLayout.PREFERRED + ,TableLayout.PREFERRED + ,5 + ,TableLayout.PREFERRED + ,TableLayout.PREFERRED + } , + { + 3 + ,TableLayout.PREFERRED + ,6 + } + }); + content.setLayout(tableLayout); + startTime = new RaplaTime(locale); + startTime.setTimeZone(DateTools.getTimeZone()); + endTime = new RaplaTime(locale); + endTime.setTimeZone(DateTools.getTimeZone()); + setAppointmentVisible(false); + weekdayChooser = new WeekdayChooser(locale); + weekdayChooser.setSelectedItem(null); + weekdayChooser.addActionListener(this); + delete.addActionListener(this); + + content.add("0,1", delete); + content.add("1,1,r,f", identifier); + content.add("0,1,1,1", newLabel); + content.add("2,1", weekdayChooser); + content.add("4,1,c,r", startLabel); + content.add("5,1", startTime); + content.add("7,1,c,r", endLabel); + content.add("8,1", endTime); + + newLabel.setText(getString("new_appointment")+":"); + startLabel.setText(getString("time_at") + ": "); + endLabel.setText(getString("time_until") + ": "); + delete.setText(getString("delete")); + delete.setIcon(getIcon("icon.delete")); + } + + public JComponent getComponent() { + return content; + } + + public void clearAll() { + if (!isUsed()) + return; + + reservation.removeAppointment(appointment); + fireAppointmentRemoved(appointment); + appointment = null; + weekdayChooser.setSelectedItem(null); + setAppointmentVisible(false); + } + + public boolean isUsed() { + return appointment != null; + } + + public void newAppointment(Date startDate,Date endDate) throws RaplaException { + calendar.setTime(startDate); + weekdayChooser.selectWeekday(calendar.get(Calendar.DAY_OF_WEEK)); + startTime.setTime(startDate); + endTime.setTime(endDate); + content.revalidate(); + } + + public void updateIndex() { + if (!isUsed()) + return; + Appointment[] apps = reservation.getAppointments(); + for (int i=0;i0) { + startTime = apps[apps.length-1].getStart(); + endTime = apps[apps.length-1].getEnd(); + } else { + startTime = new Date(DateTools.MILLISECONDS_PER_HOUR * getCalendarOptions().getWorktimeStart()); + endTime = new Date(startTime.getTime() + DateTools.MILLISECONDS_PER_HOUR); + } + + Date startDate = f.toDate(getDate(),startTime); + Date endDate = f.toDate(getDate(),endTime); + createAppointment(startDate,endDate); + } + + public void createAppointment(Date startDate,Date endDate) throws RaplaException { + appointment = getModification().newAppointment(startDate, endDate); + appointment.setRepeatingEnabled(true); + Repeating repeating = appointment.getRepeating(); + repeating.setType( RepeatingType.WEEKLY ); + repeating.setEnd(period.getEnd()); + startTime.setTime(appointment.getStart()); + endTime.setTime(appointment.getEnd()); + reservation.addAppointment(appointment); + startTime.addDateChangeListener(this); + endTime.addDateChangeListener(this); + + fireAppointmentAdded(appointment); + } + + public void actionPerformed(ActionEvent evt) { + if (evt.getSource() == weekdayChooser && weekdayChooser.getSelectedWeekday()>=0) { + selectFirstInPeriod(weekdayChooser.getSelectedWeekday()); + if (appointment == null) { + try { + newAppointment(); + } catch (Exception ex) { + showException(ex,getComponent()); + } + setAppointmentVisible(true); + } + update(); + } if (evt.getSource() == delete) { + removeAppointmentPanel(this); + reservation.removeAppointment(appointment); + fireAppointmentRemoved(appointment); + appointment = null; + } + } + + /** Selects the first appearence of the specified weekday in the period as startdate*/ + private void selectFirstInPeriod(int dayOfWeek) { + calendar.setTime(period.getStart()); + calendar.set(Calendar.DAY_OF_WEEK, dayOfWeek); + if (calendar.getTime().before(period.getStart())) { + calendar.add(Calendar.DAY_OF_WEEK,7); + } + } + + /** returns the selected date (not the time) */ + private Date getDate() { + return calendar.getTime(); + } + + public void dateChanged(DateChangeEvent evt) { + if (!listenerEnabled) + return; + try { + listenerEnabled = false; + RaplaLocale f = getRaplaLocale(); + long duration = appointment.getEnd().getTime() - appointment.getStart().getTime(); + if (evt.getSource() == startTime) { + Date newStart = f.toDate(getDate(),startTime.getTime()); + Date newEnd = new Date(newStart.getTime() + duration); + if (newEnd.getTime() >= getDate().getTime() + DateTools.MILLISECONDS_PER_DAY) { + newEnd = new Date( + getDate().getTime() + + DateTools.MILLISECONDS_PER_DAY + - DateTools.MILLISECONDS_PER_MINUTE + ); + } + endTime.setTime(newEnd); + getLogger().debug("enddate adjusted"); + } + if (evt.getSource() == endTime) { + Date newEnd = f.toDate(getDate(),endTime.getTime()); + if (appointment.getStart().after(newEnd)) { + startTime.setTime(newEnd); + getLogger().debug("startdate adjusted"); + } + } + } finally { + listenerEnabled = true; + } + update(); + } + + private void update() { + RaplaLocale f = getRaplaLocale(); + Date start = f.toDate(getDate(),startTime.getTime()); + Date end = f.toDate(getDate(),endTime.getTime()); + appointment.move(start,end); + fireAppointmentChanged(appointment); + } + + private void setAppointmentVisible(boolean visible) { + newLabel.setVisible(!visible); + delete.setVisible(visible); + startLabel.setVisible(visible); + startTime.setVisible(visible); + endLabel.setVisible(visible); + endTime.setVisible(visible); + identifier.setVisible(visible); + } + + } +} diff --git a/Rapla/src/org/rapla/plugin/periodwizard/PeriodReservationWizard.java b/Rapla/src/org/rapla/plugin/periodwizard/PeriodReservationWizard.java new file mode 100644 index 0000000..aa6f1ad --- /dev/null +++ b/Rapla/src/org/rapla/plugin/periodwizard/PeriodReservationWizard.java @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.periodwizard; + +import java.awt.Component; + +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.ReservationWizard; + +/** The period-wizard-plugin eases the creation of reservations that repeat weekly + in a given period. This is a very common usecase at universities and schools. + */ +public class PeriodReservationWizard extends RaplaGUIComponent implements ReservationWizard { + + public PeriodReservationWizard(RaplaContext sm) throws RaplaException { + super( sm); + setChildBundleName( PeriodWizardPlugin.RESOURCE_FILE); + } + + public void start(Component owner,CalendarModel model,DynamicType type) + throws RaplaException { + WizardSequence sequence = new WizardSequence(getContext()); + sequence.start(owner,model,type); + } + + public String toString() { + return getString("reservation.create_with_default_wizard"); + } +} + + diff --git a/Rapla/src/org/rapla/plugin/periodwizard/PeriodWizardPlugin.java b/Rapla/src/org/rapla/plugin/periodwizard/PeriodWizardPlugin.java new file mode 100644 index 0000000..b12fe74 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/periodwizard/PeriodWizardPlugin.java @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.periodwizard; + +import org.apache.avalon.framework.configuration.Configuration; +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.components.xmlbundle.impl.I18nBundleImpl; +import org.rapla.framework.Container; +import org.rapla.framework.PluginDescriptor; +import org.rapla.plugin.RaplaExtensionPoints; + +public class PeriodWizardPlugin implements PluginDescriptor { + public static final String RESOURCE_FILE =PeriodWizardPlugin.class.getPackage().getName() + ".WizardResources"; + public static final String PLUGIN_CLASS = PeriodWizardPlugin.class.getName(); + static boolean ENABLE_BY_DEFAULT = false; + + public String toString() { + return "Create Reservations in Periods"; + } + + public void provideServices(Container container, Configuration config) { + if ( !config.getAttributeAsBoolean("enabled", ENABLE_BY_DEFAULT) ) + return; + + container.addContainerProvidedComponent( I18nBundle.ROLE, I18nBundleImpl.class.getName(), RESOURCE_FILE,I18nBundleImpl.createConfig( RESOURCE_FILE ) ); + container.addContainerProvidedComponent( RaplaExtensionPoints.RESERVATION_WIZARD_EXTENSION, PeriodReservationWizard.class.getName(), PLUGIN_CLASS, config); + } + + public Object getPluginMetaInfos( String key ) + { + return null; + } + + + +} + diff --git a/Rapla/src/org/rapla/plugin/periodwizard/WizardResources.xml b/Rapla/src/org/rapla/plugin/periodwizard/WizardResources.xml new file mode 100644 index 0000000..9924e06 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/periodwizard/WizardResources.xml @@ -0,0 +1,267 @@ + + + + + + Weekly event in a period + Wöchentliche Veranstaltung in einem Zeitraum + Évènement hebdomadaire pendant toute une période + Evento semanal durante todo un periodo + Activiteit (Periode wekelijks) + + + + Wizard for regular events + Assistent zum Anlegen einer regelmäßigen Veranstaltung + Assistant de création d'évènement hebdomadaire + Asistente de creación de eventos semanales + Assistent: Activiteit (Periode) + + + + No periods found. Please enter the periods which you want to use for planning first. You'll need to login as admin for this. + + + Keine Zeiträume gefunden. Bitte geben Sie zuerst die Zeiträume ein, für die Sie planen möchten. Sie müssen sich hierfür als admin einloggen. + + Pas de période. Commencez par définir des périodes dans l'année. Vous avez besoin de vous connecter comme 'admin' pour le faire + No existen periodos. Comience por definir los periodos del año que se usarán en las planificaciones. Para ello, necesitará conectarse como usuario admin. + Geen periode gevonden. Eerst een periode toevoegen. Contacteer de systeem beheerder. + + + + You have to mark at least one resource or person + to search for a free appointment! + + + Sie müssen mindestens eine Ressource oder Person + für die Sie den freien Termin suchen markieren! + + Vous devez au moins sélectionner une resource ou personne pour laquelle (ou lesquelles) vous voulez rechercher un crénau possible + ¡Debe seleccionar al menos un recurso o persona para el cual (o los cuales) desea buscar una cita disponible! + Tenminste één middel of persoon selecteren! + + + + Create a regular event:
    +
    + 1. Select a period.
    +
    + 2. Choose a event-type.
    +
    + 3. Enter the event information.
    +
    + 4. Proceed to the appointments-menu.
    +
    + + Anlegen einer regelmäßigen Veranstaltung:
    +
    + 1. Zeitraum in dem die Reservierung stattfindet auswählen.
    +
    + 2. Veranstaltungstyp auswählen.
    +
    + 3. Eingabe der Veranstaltungsdaten.
    +
    + 4. Weiter zur Termineingabe.
    +
    + + Création d'évènement hebdomadaire
    +
    + 1. Choisissez la période de l'année.
    +
    + 2. Choisissez le type de l'évènement.
    +
    + 3. Entrez les informations concernant l'évènement.
    +
    + 4. Passez à la fenêtre de définition des crénaux.
    +
    + + Creación de eventos semanales:
    +
    + 1. Seleccione el periodo del año.
    +
    + 2. Escoja el tipo de evento.
    +
    + 3. Introduzca la información del evento.
    +
    + 4. Pase a la ventana de creación de citas.
    +
    + + Plannen van een activiteit:
    +
    + 1. Kies een periode.
    +
    + 2. Kies een type activiteit.
    +
    + 3. Geef de activiteit een label.
    +
    + 4. Volgende.
    +
    +
    + + + Enter known appointments:
    + Fill in the day of the week and the time of the appointments.
    +
    + Search for free appointments:
    + Mark the resource or persons for which you are searching a free appointment in + the left table and press "Search free appointment".
    +
    + Selecting resources and persons:
    + Mark the persons and resources you want in the left table. + Double-click to add them to the right table and to assign them + to all entered appointments. If you want to assign a + resource or person to a particular appointment, click in the + right column next to the corresponding entry in the right table. +
    + + Eingabe von bekannten Terminen:
    + Geben Sie den Wochentag und die Uhrzeit der Termine ein.
    +
    + Suche nach freien Terminen:
    + Markieren Sie die Ressourcen oder Personen für die Sie einen freien Termin suchen + in der linken Tabelle und drücken Sie auf "Freien Termin suchen".
    +
    + Auswählen von Ressourcen und Personen:
    + Markieren Sie die gewünschten Personen und Ressourcen in der linken Tabelle. + Mit einen Doppelklick werden die Einträge in die rechte Tabelle übernommen und + allen eingegebenen Termine zugewiesen. Wenn Sie eine + Ressource oder Person nur einem bestimmten Termin zuweisen wollen, klicken Sie + in die rechte Spalte neben dem entsprechenden Eintrag in der rechten Tabelle. +
    + + Indiquer les crénaux que vous connaissez :
    + Indiquez le jour de la semaine et l'heure de début et de fin du crénau.
    +
    + Chercher des crénaux libres :
    + Sélectionnez dans la table de gauche + les ressources et personnes pour lesquelles vous + voulez rechercher un crénau libre. + Puis cliquez sur "Chercher un crénau libre".
    +
    + Selectionner les ressources et personnes :
    + Sélectionnez dans la table de gauche + les ressources et personnes puis cliquez sur "Insérer" + Elle seront assignées pour tous les crénaux. + Pour assigner une ressource ou personne à des crénaux + particuliers, cliquez sur le bouton à coté de la ressource + ou personne dans la table de droite. +
    + + Introduzca las citas que conoce:
    + Indique el día de la semana y la hora de inicio y de fin de la cita.
    +
    + Búsqueda de citas disponibles:
    + Seleccione en la tabla de la izquierda + los recursos y las personas para los cuales + desee buscar una cita disponible + y pulse sobre "Buscar una cita disponible".
    +
    + Seleccione los recursos y las personas:
    + Seleccione en la tabla de la izquierda + los recursos y las personas y pulse el botón "Añadir". + Se añadirán para todas las citas. + Para asignar un recurso o persona a una cita + particular, pulse sobre el botón que aparece a continuación + del recurso o persona en la tabla de la derecha. +
    + + 1. Plan een activiteit:
    + Geef de dag van de week en tijd.
    + of
    + zoek een vrije plaats; selecteer de middelen in de linkerlijst en Zoek.
    +
    2. Selecteer de middelen:
    + Selecteer de middelen in de linkerlijst en Voeg toe.

    + Als je een middel enkel aan één activiteit + wil toewijzen selecteer deze in de rechterlijst naast de naam.
    +
    3. Sla op. +
    +
    + + + Search for appointments:
    + Select with your mouse a free appointment in the wee kview. + Click "Add Appointment" to create the appointment + and return to the previous menu. +
    +
    + There you can assign the resource or person to the appointment. +
    + + Terminsuche:
    + Wählen Sie mit der Maus ein freies Zeitfenster in der Wochenansicht aus. + Drücken Sie "Termin hinzufügen", um den Termin anzulegen und zum + vorherigen Menü zurückzukehren.
    +
    + Sie können dort die gewünschte Ressource oder Person dem Termin zuweisen. +
    + + Chercher des crénaux :
    + Sélectionnez avec la souris un crénau libre + dans la semaine. + Cliquez sur "Ajouter crénaux" pour + ajouter le crénaux et revenir sur la fenêtre précédente. +
    + Ainsi vous pouvez assigner la ressource ou la personne + aux crénaux en cliquant sur 'insérer' +
    + + Búsqueda de citas :
    + Seleccione con el ratón una cita disponible durante la semana. + Pulse "Añadir cita" para añadir la cita + y volver a la ventana previa. +
    +
    + Allí podrá asignar recursos o personas a la cita. +
    + + Zoek:

    + Selecteer met de muis de tijd in het weekoverzicht.;
    +
    + Toevoegen om te bevestigen. +
    +
    + + Search for a free appointment + Freien Termin suchen + Chercher un crénau libre + Buscar una cita disponible + Zoek + + + Appointment Menu + Termineingabe + Création des crénaux + Creación de citas + Volgende + + + + Add Appointment + Termin hinzufügen + Ajouter un crénau + Añadir cita + Toevoegen + + + Enter appointments: + Eingabe der Termine: + Ajouter des crénaux : + Introducción de citas : + Toevoegen activiteiten: + + + Select resources and persons: + Auswählen von Ressourcen und Personen: + Sélection des ressources et personnes : + Selección de recursos y personas : + Toewijzingen: + + + {0}: {1} + {0}: {1} + {0}: {1} + {0}: {1} + {0}: {1} + +
    diff --git a/Rapla/src/org/rapla/plugin/periodwizard/WizardSequence.java b/Rapla/src/org/rapla/plugin/periodwizard/WizardSequence.java new file mode 100644 index 0000000..2c17c18 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/periodwizard/WizardSequence.java @@ -0,0 +1,445 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.periodwizard; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ActionMap; +import javax.swing.BorderFactory; +import javax.swing.DefaultComboBoxModel; +import javax.swing.Icon; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.Border; + +import org.apache.avalon.framework.activity.Disposable; +import org.rapla.components.calendarview.Block; +import org.rapla.components.calendarview.swing.SwingWeekView; +import org.rapla.components.calendarview.swing.ViewListener; +import org.rapla.components.layout.TableLayout; +import org.rapla.components.util.DateTools; +import org.rapla.entities.User; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Period; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.facade.ModificationEvent; +import org.rapla.facade.ModificationListener; +import org.rapla.facade.PeriodModel; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.CalendarOptions; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.internal.edit.ClassificationField; +import org.rapla.gui.internal.edit.reservation.AllocatableSelection; +import org.rapla.gui.toolkit.DisposingTool; +import org.rapla.gui.toolkit.EmptyLineBorder; +import org.rapla.gui.toolkit.WizardDialog; +import org.rapla.gui.toolkit.WizardPanel; +import org.rapla.plugin.abstractcalendar.RaplaBuilder; +import org.rapla.plugin.abstractcalendar.SwingRaplaBuilder; + + +class WizardSequence extends RaplaGUIComponent + implements + ModificationListener + ,Disposable +{ + + WizardDialog wizardDialog; + Reservation reservation; + Period period; + ReservationInfo panel1; + ReservationAllocation panel2; + ReservationWeekview panel3; + public WizardSequence(RaplaContext sm) throws RaplaException { + super(sm); + this.setChildBundleName( PeriodWizardPlugin.RESOURCE_FILE); + panel1 = new ReservationInfo(); + panel2 = new ReservationAllocation(); + panel3 = new ReservationWeekview(); + + } + + public ActionMap createActionMap() { + ActionMap map = new ActionMap(); + map.put(WizardPanel.ABORT, new Handler(getString("abort"),getIcon("icon.abort"))); + map.put(WizardPanel.PREV, new Handler( getString("back"),getIcon("icon.arrow_left"))); + map.put(WizardPanel.NEXT, new Handler(null,null)); + map.put(WizardPanel.FINISH, new Handler(getString("save"), getIcon("icon.save") )); + return map; + } + + /** the handler encapsulate the state logic of the wizard */ + class Handler extends AbstractAction { + private static final long serialVersionUID = 1L; + + Handler(String name, Icon icon) { + putValue(NAME,name); + putValue(SMALL_ICON, icon); + } + public void actionPerformed(ActionEvent evt) { + try { + String command = evt.getActionCommand(); + WizardPanel panel = wizardDialog.getActivePanel(); + if (command.equals(WizardPanel.ABORT)) { + wizardDialog.close(); + } + if (command.equals(WizardPanel.NEXT)) { + if (panel == panel1 && panel1.checkReservation()) { + panel2.appointmentSelection.setPeriod(period); + wizardDialog.start(panel2); + } + if (panel == panel2) { + Collection allocatables = panel2.getAllocatables(); + if (allocatables.size() == 0) { + showWarning(getString("warning.need_resource_or_person") + ,wizardDialog); + } else { + panel3.setAllocatables(allocatables); + wizardDialog.start(panel3); + panel3.build(); + } + } + if (panel == panel3) { + panel2.newAppointment(panel3.getStart(), panel3.getEnd()); + wizardDialog.start(panel2); + } + + } + if (command.equals(WizardPanel.PREV)) { + if (panel == panel2) { + wizardDialog.start(panel1); + } + if (panel == panel3) { + wizardDialog.start(panel2); + } + } + if (command.equals(WizardPanel.FINISH)) { + if (getReservationController().save(reservation,wizardDialog,false)) + wizardDialog.close(); + } + } catch (Exception ex) { + showException(ex,wizardDialog); + } + } + } + + public void start(Component owner,CalendarModel model,DynamicType dynamicType) throws RaplaException { + wizardDialog = WizardDialog.createWizard(getContext(),owner,false); + wizardDialog.setTitle(getString("reservation_wizard.title")); + getLogger().debug("starting wizard"); + wizardDialog.setSize(800, 565); + + reservation = getModification().newReservation(); + if (dynamicType != null) + reservation.setClassification(dynamicType.newClassification()); + panel1.setStart( model.getSelectedDate()); + panel1.setReservation(reservation); + panel2.setReservation(reservation); + getUpdateModule().addModificationListener(this); + wizardDialog.addWindowListener(new DisposingTool(this)); + wizardDialog.setDefault(2); + wizardDialog.start(panel1); + } + + public void dataChanged(ModificationEvent evt) throws RaplaException { + panel2.refresh(evt); + } + + public void dispose() { + try { + getUpdateModule().removeModificationListener(WizardSequence.this); + } catch (Exception ex) { + } + } + + class ReservationInfo + implements + WizardPanel + { + JPanel content = new JPanel(); + JLabel periodLabel = new JLabel(); + JLabel classificationLabel = new JLabel(); + ClassificationField classificationField; + JComboBox periodSelection = new JComboBox(); + + public ReservationInfo() throws RaplaException { + Period[] periods = getQuery().getPeriods(); + periodSelection.setModel(new DefaultComboBoxModel(periods)); + classificationField = new ClassificationField(getContext()); + periodLabel.setText(getString("period") + ":"); + double pre=TableLayout.PREFERRED; + double fill=TableLayout.FILL; + double[][] sizes = new double[][] { + {pre,5,fill} + ,{pre,fill} + }; + content.setLayout(new TableLayout(sizes)); + content.add(periodLabel,"0,0"); + content.add(periodSelection,"2,0"); + content.add(classificationField.getComponent(),"0,1,2,1"); + } + + public void setStart(Date start) throws RaplaException { + PeriodModel periodModel = getPeriodModel(); + Period period = null; + if (start != null) { + period = periodModel.getNearestPeriodForDate(start); + } + if ( period == null) { + period = periodModel.getNearestPeriodForDate( getQuery().today()); + } + if (period != null) { + periodSelection.setSelectedItem(period); + } + + if ( periodModel.getSize() == 0) { + throw new RaplaException(getString("error.no_period_found")); + } + + } + + public void setReservation(Reservation reservation) throws RaplaException { + classificationField.mapFrom(reservation); + } + + public ActionMap getActionMap() { + ActionMap map = createActionMap(); + map.get(PREV).setEnabled(false); + map.get(NEXT).putValue(Action.NAME, getString("reservation_wizard.appointment_menu")); + map.get(NEXT).putValue(Action.SMALL_ICON, getIcon("icon.arrow_right")); + map.get(FINISH).setEnabled(false); + return map; + } + + public String getHelp() { + return getString("reservation_wizard.panel1"); + } + + public String getDefaultAction() { + return NEXT; + } + + public boolean checkReservation() { + try { + classificationField.mapTo(reservation); + } catch (RaplaException ex) { + showException(ex,getComponent()); + return false; + } + String newName = getName( reservation ); + if (newName.length() ==0) { + showWarning(getString("error.no_reservation_name"), getComponent()); + return false; + } + // We must clear all appointments because the period has been changed); + if (period != null && !period.equals(periodSelection.getSelectedItem())) { + panel2.removeAllAppointments(); + } + + period = (Period) periodSelection.getSelectedItem(); + if (period == null) { + showWarning(getString("error.no_period_found"), getComponent()); + return false; + } + return true; + } + + public JComponent getComponent() { + return content; + } + } + + class ReservationAllocation + implements + WizardPanel + { + JPanel content = new JPanel(); + AppointmentSelection appointmentSelection; + AllocatableSelection allocatableSelection; + + public ReservationAllocation() throws RaplaException { + appointmentSelection = new AppointmentSelection(getContext()); + allocatableSelection = new AllocatableSelection(getContext()); + content.setLayout(new BorderLayout()); + content.add(appointmentSelection.getComponent(),BorderLayout.NORTH); + content.add(allocatableSelection.getComponent(),BorderLayout.CENTER); + Border emptyBorder = new EmptyLineBorder(); + appointmentSelection.getComponent().setBorder(BorderFactory.createTitledBorder(emptyBorder, getString("enter_appointments"))); + allocatableSelection.getComponent().setBorder(BorderFactory.createTitledBorder(emptyBorder, getString("select_persons_and_resources"))); + } + + public void setReservation(Reservation reservation) throws RaplaException { + allocatableSelection.setReservation(reservation); + appointmentSelection.setReservation(reservation); + appointmentSelection.addAppointmentListener(allocatableSelection); + } + + public void refresh(ModificationEvent evt) throws RaplaException { + allocatableSelection.refresh(evt); + } + + public void newAppointment(Date start,Date end) throws RaplaException { + appointmentSelection.newAppointment(start,end); + } + + public void removeAllAppointments() { + appointmentSelection.removeAllAppointments(); + } + + public String getHelp() { + return getString("reservation_wizard.panel2"); + } + + public String getDefaultAction() { + return FINISH; + } + + public ActionMap getActionMap() { + ActionMap map = createActionMap(); + map.get(NEXT).putValue(Action.NAME, getString("reservation_wizard.search_free_appointment")); + map.get(NEXT).putValue(Action.SMALL_ICON,getIcon("icon.calendar")); + return map; + } + + public Collection getAllocatables() { + return allocatableSelection.getMarkedAllocatables(); + } + + public JComponent getComponent() { + return content; + } + } + + class ReservationWeekview + implements + WizardPanel + ,ViewListener + { + JPanel content = new JPanel(); + SwingWeekView wv= new SwingWeekView(); + RaplaBuilder builder; + Date start= null; + Date end = null; + Action nextAction; + + public ReservationWeekview() throws RaplaException { + builder = new SwingRaplaBuilder(getContext()); + content.setLayout(new BorderLayout()); + content.add(wv,BorderLayout.CENTER); + CalendarOptions opt = getCalendarOptions(); + wv.setTimeZone( DateTools.getTimeZone()); + wv.setExcludeDays( opt.getExcludeDays() ); + wv.setWorktime( opt.getWorktimeStart(), opt.getWorktimeEnd()); + wv.addBuilder(builder); + //wv.setDateVisible(false); + wv.addCalendarViewListener(this); + } + + public void setAllocatables(Collection markedAllocatables) throws RaplaException { + Iterator it = markedAllocatables.iterator(); + StringBuffer buf = new StringBuffer(); + int i=0; + while (it.hasNext()) { + if (i>0) + buf.append(", "); + buf.append( getName( (Allocatable)it.next() ) ); + i++; + } + String title = getI18n().format("list.format",period.getName(),buf.toString()); + content.setBorder(BorderFactory.createTitledBorder(title)); + wv.setToDate(period.getStart()); + Collection otherReservations = + Arrays.asList(getQuery().getReservations((User) null + ,wv.getStartDate() + ,wv.getEndDate() + ,null) + ); + Collection reservations = new ArrayList(otherReservations); + reservations.add(reservation); + builder.selectAllocatables(markedAllocatables); + builder.selectReservations(reservations); + } + + public void build() { + wv.rebuild(); + wv.scrollToStart(); + } + + public JComponent getComponent() { + return content; + } + + public String getHelp() { + return getString("reservation_wizard.panel3"); + } + + + public ActionMap getActionMap() { + ActionMap map = createActionMap(); + nextAction = map.get(NEXT); + nextAction.putValue(Action.NAME, getString("reservation_wizard.add_appointment")); + nextAction.putValue(Action.SMALL_ICON, getIcon("icon.new")); + nextAction.setEnabled(false); + map.get(FINISH).setEnabled(false); + return map; + } + + public Date getStart() { + return start; + } + + public String getDefaultAction() { + return FINISH; + } + + public Date getEnd() { + return end; + } + + // Implementation of the weekview listener + + public void selectionChanged(Date start,Date end) { + this.start = start; + this.end = end; + nextAction.setEnabled( start != null && end != null ); + } + public void selectionPopup(Component component,Point p,Date start,Date end, int slotNr) { + } + public void blockPopup(Block block,Point p) { + } + public void blockEdit(Block block,Point p) { + } + public void moved(Block block,Point p,Date newStart) { + } + public void resized(Block block,Point p,Date newStart, Date newEnd) { + } + + } + +} + diff --git a/Rapla/src/org/rapla/plugin/tableview/ReservationTableModel.java b/Rapla/src/org/rapla/plugin/tableview/ReservationTableModel.java new file mode 100644 index 0000000..7d339d8 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/tableview/ReservationTableModel.java @@ -0,0 +1,84 @@ +package org.rapla.plugin.tableview; + +import java.util.Date; +import java.util.Locale; + +import javax.swing.table.DefaultTableModel; + +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Reservation; + +public class ReservationTableModel extends DefaultTableModel +{ + private static final long serialVersionUID = 1L; + + Reservation[] reservations = new Reservation[] {}; + Locale locale; + I18nBundle i18n; + + //String[] columns; + public ReservationTableModel(Locale locale, I18nBundle i18n) { + this.locale = locale; + this.i18n = i18n; + String[]columns = new String[] {i18n.getString("name"),i18n.getString("start_date"),i18n.getString("last_changed")}; + this.setColumnIdentifiers( columns); + } + + public void setReservations(Reservation[] events) { + this.reservations = events; + super.fireTableDataChanged(); + } + + public Reservation getReservationAt(int row) { + return this.reservations[row]; + } + + public boolean isCellEditable(int row, int column) { + return false; + } + + public int getRowCount() { + if ( reservations != null) + return reservations.length; + else + return 0; + } + + static public Date findStartDate(Reservation event) { + Appointment[] apps = event.getAppointments(); + Date minimumDate = null; + for (int i=0;i< apps.length;i++) { + Appointment app = apps[i]; + if ( minimumDate == null || app.getStart().before( minimumDate)) { + minimumDate = app.getStart(); + } + } + return minimumDate; + } + + public Object getValueAt( int rowIndex, int columnIndex ) + { + Reservation event = reservations[rowIndex]; + switch ( columnIndex ) + { + case 0: return event.getName( locale); + case 1: return findStartDate( event ); + case 2: return event.getLastChangeTime(); + } + return event; + } + + public Class getColumnClass(int columnIndex) { + switch ( columnIndex ) + { + case 0: return String.class; + case 1: return Date.class; + case 2: return Date.class; + } + return super.getColumnClass( columnIndex ); + } + + + +} diff --git a/Rapla/src/org/rapla/plugin/tableview/SwingTableView.java b/Rapla/src/org/rapla/plugin/tableview/SwingTableView.java new file mode 100644 index 0000000..705a466 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/tableview/SwingTableView.java @@ -0,0 +1,246 @@ +package org.rapla.plugin.tableview; + +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.print.PageFormat; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.lang.reflect.Method; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JComponent; +import javax.swing.JScrollPane; +import javax.swing.JTable; + +import org.rapla.components.calendar.DateChangeEvent; +import org.rapla.components.calendar.DateChangeListener; +import org.rapla.components.iolayer.ComponentPrinter; +import org.rapla.components.tablesorter.TableSorter; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.domain.ReservationHelper; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.MenuContext; +import org.rapla.gui.MenuFactory; +import org.rapla.gui.RaplaGUIComponent; +import org.rapla.gui.SwingCalendarView; +import org.rapla.gui.internal.common.CalendarAction; +import org.rapla.gui.toolkit.DateCellRenderer; +import org.rapla.gui.toolkit.RaplaMenu; +import org.rapla.gui.toolkit.RaplaMenuItem; +import org.rapla.gui.toolkit.RaplaPopupMenu; +import org.rapla.plugin.abstractcalendar.IntervallChooserPanel; + +public class SwingTableView extends RaplaGUIComponent implements SwingCalendarView, Printable +{ + ReservationTableModel reservationTableModel; + + JTable table; + CalendarModel model; + IntervallChooserPanel dateChooser; + JComponent container; + TableSorter sorter; + + public SwingTableView( RaplaContext context, CalendarModel model, final boolean editable ) throws RaplaException + { + super( context ); + table = new JTable() { + private static final long serialVersionUID = 1L; + + public String getToolTipText(MouseEvent e) { + if (!editable) + return null; + int rowIndex = rowAtPoint( e.getPoint() ); + Reservation reservation = reservationTableModel.getReservationAt( sorter.modelIndex( rowIndex )); + return getInfoFactory().getToolTip( reservation ); + } + }; + if ( editable ) + { + container = new JScrollPane( table); + container.setPreferredSize( new Dimension(600,800)); + } + else + { + container = table; + } + this.model = model; + reservationTableModel = new ReservationTableModel( getLocale(),getI18n() ); + sorter = new TableSorter( reservationTableModel, table.getTableHeader()); + table.setModel( sorter ); + table.getColumnModel().getColumn(1 ).setCellRenderer( new DateCellRenderer( getRaplaLocale())); + table.getColumnModel().getColumn(1 ).setMaxWidth( 130 ); + table.getColumnModel().getColumn(1 ).setPreferredWidth( 130 ); + table.getColumnModel().getColumn(2 ).setCellRenderer( new DateCellRenderer( getRaplaLocale())); + table.getColumnModel().getColumn(2 ).setMaxWidth( 130 ); + table.getColumnModel().getColumn(2 ).setPreferredWidth( 130 ); + if ( editable ) { + PopupTableHandler popupHandler = new PopupTableHandler(); + container.addMouseListener( popupHandler); + table.addMouseListener( popupHandler ); + } + + dateChooser = new IntervallChooserPanel( context, model); + dateChooser.addDateChangeListener( new DateChangeListener() { + public void dateChanged( DateChangeEvent evt ) + { + try { + update( ); + } catch (RaplaException ex ){ + showException( ex, getComponent()); + } + } + }); + + reservationTableModel.setReservations( model.getReservations() ); + + if ( !editable) + { + Dimension size = table.getPreferredSize(); + container.setBounds( 0,0,(int)600, (int)size.getHeight()); + } + + } + + public void update() throws RaplaException + { + reservationTableModel.setReservations( model.getReservations() ); + dateChooser.update(); + } + + public JComponent getDateSelection() + { + return dateChooser.getComponent(); + } + + public void scrollToStart() + { + + } + + public JComponent getComponent() + { + return container; + } + + List getSelectedEvents() { + int[] rows = table.getSelectedRows(); + List selectedEvents = new ArrayList(); + for (int i=0;i 1 && selectedEvents.size() == 1 ) + { + Reservation reservation = (Reservation) selectedEvents.get( 0); + try { + getReservationController().edit( reservation ); + } catch (RaplaException ex) { + showException (ex,getComponent()); + } + } + } + } + + private Object getFitWidthMode() + { + try { + Class printMode = Class.forName("javax.swing.JTable$PrintMode"); + return printMode.getField("FIT_WIDTH").get( null); + } catch (Exception e) { + return null; + } + } + + Printable printable = null; + /** + * @see java.awt.print.Printable#print(java.awt.Graphics, java.awt.print.PageFormat, int) + */ + public int print(Graphics graphics, PageFormat format, int page) throws PrinterException { + if ( page == 0) { + // We use reflection to make the following call + // this maintains 1.4 compatibilty + // printable = table.getPrintable( JTable.PrintMode.FIT_WIDTH,f1, null ); + Object FIT_WIDTH = getFitWidthMode(); + if ( FIT_WIDTH == null) + { + printable = new ComponentPrinter( container, true); + } + else + { + MessageFormat f1 = new MessageFormat( model.getNonEmptyTitle()); + try + { + Method getPrintableMethod = JTable.class.getMethod("getPrintable", new Class[] {FIT_WIDTH.getClass(),MessageFormat.class, MessageFormat.class}); + printable = (Printable)getPrintableMethod.invoke( table, new Object[] {FIT_WIDTH,f1, null }); + } catch (Exception ex) + { + throw new PrinterException("Can't get printable of table " + ex.getMessage()); + } + } + } + return printable.print( graphics, format, page); + } + + + } diff --git a/Rapla/src/org/rapla/plugin/tableview/TableViewFactory.java b/Rapla/src/org/rapla/plugin/tableview/TableViewFactory.java new file mode 100644 index 0000000..1ca3b03 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/tableview/TableViewFactory.java @@ -0,0 +1,69 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.tableview; + +import javax.swing.Icon; + +import org.rapla.facade.RaplaComponent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.SwingCalendarView; +import org.rapla.gui.ViewFactory; +import org.rapla.gui.images.Images; +import org.rapla.servletpages.RaplaPageGenerator; + +public class TableViewFactory extends RaplaComponent implements ViewFactory +{ + public TableViewFactory( RaplaContext context ) throws RaplaException + { + super( context ); + } + + public final static String TABLE_VIEW = "table"; + + public SwingCalendarView createSwingView(RaplaContext context, CalendarModel model, boolean editable) throws RaplaException + { + return new SwingTableView( context, model, editable); + } + + public RaplaPageGenerator createHTMLView(RaplaContext context, CalendarModel model) throws RaplaException + { + return new TableViewPage( context, model); + } + + public String getViewId() + { + return TABLE_VIEW; + } + + public String getName() + { + return getString("table"); + } + + Icon icon; + public Icon getIcon() + { + if ( icon == null) { + icon = Images.getIcon("/org/rapla/plugin/tableview/images/table.png"); + } + return icon; + } + + public String getMenuSortKey() { + return "0"; + } + +} + diff --git a/Rapla/src/org/rapla/plugin/tableview/TableViewPage.java b/Rapla/src/org/rapla/plugin/tableview/TableViewPage.java new file mode 100644 index 0000000..50df75f --- /dev/null +++ b/Rapla/src/org/rapla/plugin/tableview/TableViewPage.java @@ -0,0 +1,166 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.tableview; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.rapla.components.util.xml.XMLWriter; +import org.rapla.entities.domain.Reservation; +import org.rapla.facade.RaplaComponent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.framework.RaplaLocale; +import org.rapla.gui.CalendarModel; +import org.rapla.servletpages.RaplaPageGenerator; + +public class TableViewPage extends RaplaComponent implements RaplaPageGenerator +{ + CalendarModel model; + public TableViewPage( RaplaContext context, CalendarModel calendarModel ) throws RaplaException + { + super( context ); + this.model = calendarModel; + } + public String getTitle() { + return model.getNonEmptyTitle(); + } + + + public void generatePage( ServletContext context,HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + response.setContentType("text/html; charset=" + getRaplaLocale().getCharsetNonUtf() ); + java.io.PrintWriter out = response.getWriter(); + + RaplaLocale raplaLocale= getRaplaLocale(); + + + out.println(""); + out.println(""); + out.println(" " + getTitle() + ""); + out.println(" "); + out.println(" "); + // tell the html page where its favourite icon is stored + out.println(" "); + out.println(" "); + out.println(""); + out.println(""); + out.println("

    "); + out.println(getTitle()); + out.println("

    "); + out.println("
    "); + try { + final String calendarHTML = getCalendarHTML(); + out.println(calendarHTML); + } catch (RaplaException e) { + throw new ServletException( e); + } + out.println("
    "); + + // end weekview + out.println(""); + out.println(""); + + } + + class ReservationRow implements Comparable + { + Reservation r; + ReservationRow(Reservation r) + { + this.r = r; + } + + public String getStart() + { + RaplaLocale raplaLocale = getRaplaLocale(); + final Date startDate = getStartDate(); + String start= raplaLocale.formatDateLong(startDate); + return start; + } + + private Date getStartDate() { + return ReservationTableModel.findStartDate( r ); + } + + public String getLastModified() + { + RaplaLocale raplaLocale = getRaplaLocale(); + final Date lastChangeTime = r.getLastChangeTime(); + String lastChanged= raplaLocale.formatDateLong(lastChangeTime); + return lastChanged; + } + + public int compareTo(ReservationRow o) { + return getStartDate().compareTo(o.getStartDate()); + } + + public String getName() + { + return r.getName( getLocale()); + } + + } + public String getCalendarHTML() throws RaplaException { + final Date startDate = model.getStartDate(); + final Date endDate = model.getEndDate(); + final Reservation[] reservations = model.getReservations(startDate, endDate); + List rows = new ArrayList(); + for (Reservation r :reservations) + { + rows.add( new ReservationRow( r)); + } + Collections.sort( rows); + StringBuffer buf = new StringBuffer(); + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(""); + for (ReservationRow r :rows) + { + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(""); + + buf.append(""); + } + buf.append("
    "); + buf.append(getString("name")); + buf.append(""); + buf.append(getString("start_date")); + buf.append(""); + buf.append(getString("last_changed")); + buf.append("
    "); + final String name = XMLWriter.encode(r.getName()); + buf.append( name ); + buf.append(""); + + buf.append( r.getStart() ); + buf.append(""); + buf.append( r.getLastModified()); + buf.append("
    "); + final String result = buf.toString(); + return result; + } + +} + diff --git a/Rapla/src/org/rapla/plugin/tableview/TableViewPlugin.java b/Rapla/src/org/rapla/plugin/tableview/TableViewPlugin.java new file mode 100644 index 0000000..e367d2b --- /dev/null +++ b/Rapla/src/org/rapla/plugin/tableview/TableViewPlugin.java @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.tableview; + +import org.apache.avalon.framework.configuration.Configuration; +import org.rapla.framework.Container; +import org.rapla.framework.PluginDescriptor; +import org.rapla.plugin.RaplaExtensionPoints; +import org.rapla.plugin.RaplaPluginMetaInfo; + +public class TableViewPlugin implements PluginDescriptor +{ + static boolean ENABLE_BY_DEFAULT = true; + + public String toString() + { + return "Table View"; + } + + public void provideServices(Container container, Configuration config) + { + if ( !config.getAttributeAsBoolean("enabled", ENABLE_BY_DEFAULT) ) + return; + + container.addContainerProvidedComponent + ( + RaplaExtensionPoints.CALENDAR_VIEW_EXTENSION + ,TableViewFactory.class.getName() + ,TableViewFactory.TABLE_VIEW + ,null + ); + } + + public Object getPluginMetaInfos( String key ) + { + if ( RaplaPluginMetaInfo.METAINFO_PLUGIN_ENABLED_BY_DEFAULT.equals( key )) { + return new Boolean( ENABLE_BY_DEFAULT ); + } + return null; + } + + +} + diff --git a/Rapla/src/org/rapla/plugin/tableview/images/table.png b/Rapla/src/org/rapla/plugin/tableview/images/table.png new file mode 100644 index 0000000..fe33429 Binary files /dev/null and b/Rapla/src/org/rapla/plugin/tableview/images/table.png differ diff --git a/Rapla/src/org/rapla/plugin/tableview/report.css b/Rapla/src/org/rapla/plugin/tableview/report.css new file mode 100644 index 0000000..bad7b31 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/tableview/report.css @@ -0,0 +1,59 @@ +/* Page background color */ +body { background-color: #FFFFFF; + text : #000000; + } + +.dayheader { + border: 2px solid #000000; + background-color: #FFFF88; + width: 600px; +} + +.time { + vertical-align:top; + font-weight: bold; + font-size: 8pt; + padding-right: 20px; + padding-top: 0px; + padding-bottom: 0px; + margin: 0px; +} + +.value { + vertical-align:top; + font-size: 8pt; + margin: 0px; + padding: 0px; +} + +.label { + vertical-align:top; + font-weight: bold; + font-size: 8pt; + margin: 0px; + padding: 0px; +} + +.infotable { + font-size:small; + border-spacing:0px; +} + +.separation_row { + height:5px; +} + + +.datechooser { + text-align:center; +} + +@page { + size: portrait; +} + +@media print { + .datechooser { + display: none; + } +} diff --git a/Rapla/src/org/rapla/plugin/weekview/DayViewFactory.java b/Rapla/src/org/rapla/plugin/weekview/DayViewFactory.java new file mode 100644 index 0000000..90f740d --- /dev/null +++ b/Rapla/src/org/rapla/plugin/weekview/DayViewFactory.java @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.weekview; + +import javax.swing.Icon; + +import org.rapla.facade.RaplaComponent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.SwingCalendarView; +import org.rapla.gui.ViewFactory; +import org.rapla.gui.images.Images; +import org.rapla.servletpages.RaplaPageGenerator; + +public class DayViewFactory extends RaplaComponent implements ViewFactory +{ + public DayViewFactory( RaplaContext context ) throws RaplaException + { + super( context ); + } + + public final static String DAY_VIEW = "day"; + + public SwingCalendarView createSwingView(RaplaContext context, CalendarModel model, boolean editable) throws RaplaException + { + return new SwingDayCalendar( context, model, editable); + } + + public RaplaPageGenerator createHTMLView(RaplaContext context, CalendarModel model) throws RaplaException + { + return new HTMLDayViewPage( context, model); + } + + public String getViewId() + { + return DAY_VIEW; + } + + public String getName() + { + return getString(DAY_VIEW); + } + + Icon icon; + public Icon getIcon() + { + if ( icon == null) { + icon = Images.getIcon("/org/rapla/plugin/weekview/images/day.png"); + } + return icon; + } + + public String getMenuSortKey() { + return "A"; + } + + +} + diff --git a/Rapla/src/org/rapla/plugin/weekview/HTMLDayViewPage.java b/Rapla/src/org/rapla/plugin/weekview/HTMLDayViewPage.java new file mode 100644 index 0000000..0dfd77a --- /dev/null +++ b/Rapla/src/org/rapla/plugin/weekview/HTMLDayViewPage.java @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.weekview; + +import java.util.Calendar; +import java.util.HashSet; +import java.util.Set; + +import org.rapla.components.calendarview.html.AbstractHTMLView; +import org.rapla.components.calendarview.html.HTMLWeekView; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.CalendarOptions; + +public class HTMLDayViewPage extends HTMLWeekViewPage +{ + public HTMLDayViewPage( RaplaContext context, CalendarModel calendarModel ) throws RaplaException + { + super( context, calendarModel ); + } + + protected AbstractHTMLView createCalendarView() { + HTMLWeekView weekView = (HTMLWeekView) super.createCalendarView(); + CalendarOptions opt = getCalendarOptions(); + Set excludeDays = opt.getExcludeDays(); + if ( isDayView()) { + excludeDays = new HashSet(); + Calendar cal = getRaplaLocale().createCalendar(); + cal.setTime( model.getSelectedDate() ); + for (int i=0;i<8;i++) { + if ( i != cal.get( Calendar.DAY_OF_WEEK)) { + excludeDays.add( new Integer(i)); + } + } + } + weekView.setExcludeDays( excludeDays ); + weekView.setShowNonEmptyExcludedDays( false ) ; + return weekView; + } + + + public boolean isDayView() { + return true; + } + + public int getIncrementSize() { + return Calendar.DAY_OF_YEAR; + } + +} + diff --git a/Rapla/src/org/rapla/plugin/weekview/HTMLWeekViewPage.java b/Rapla/src/org/rapla/plugin/weekview/HTMLWeekViewPage.java new file mode 100644 index 0000000..eb0b989 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/weekview/HTMLWeekViewPage.java @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.weekview; + +import java.text.MessageFormat; +import java.util.Calendar; +import java.util.Set; + +import org.rapla.components.calendarview.html.AbstractHTMLView; +import org.rapla.components.calendarview.html.HTMLWeekView; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.CalendarOptions; +import org.rapla.plugin.abstractcalendar.AbstractHTMLCalendarPage; +import org.rapla.plugin.abstractcalendar.GroupAllocatablesStrategy; +import org.rapla.plugin.abstractcalendar.RaplaBuilder; + +public class HTMLWeekViewPage extends AbstractHTMLCalendarPage +{ + public HTMLWeekViewPage( RaplaContext context, CalendarModel calendarModel ) throws RaplaException + { + super( context, calendarModel ); + } + + protected AbstractHTMLView createCalendarView() { + HTMLWeekView weekView = new HTMLWeekView(){ + protected String getWeekNumberRow() { + return MessageFormat.format("("+getString("week") + " {0,date,w})", getStartDate()); + } + }; + CalendarOptions opt = getCalendarOptions(); + Set excludeDays = opt.getExcludeDays(); + weekView.setExcludeDays( excludeDays ); + weekView.setRowsPerHour( opt.getRowsPerHour() ); + weekView.setWorktime( opt.getWorktimeStart(), opt.getWorktimeEnd() ); + weekView.setShowNonEmptyExcludedDays( true ) ; + return weekView; + } + + protected RaplaBuilder createBuilder() throws RaplaException { + RaplaBuilder builder = super.createBuilder(); + + GroupAllocatablesStrategy strategy = new GroupAllocatablesStrategy( getRaplaLocale().getLocale() ); + boolean compactColumns = getCalendarOptions().isCompactColumns() || builder.getAllocatables().size() ==0 ; + strategy.setFixedSlotsEnabled( !compactColumns); + strategy.setResolveConflictsEnabled( true ); + builder.setBuildStrategy( strategy ); + + return builder; + } + + public int getIncrementSize() { + return Calendar.WEEK_OF_YEAR; + } + +} + diff --git a/Rapla/src/org/rapla/plugin/weekview/SwingDayCalendar.java b/Rapla/src/org/rapla/plugin/weekview/SwingDayCalendar.java new file mode 100644 index 0000000..827f184 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/weekview/SwingDayCalendar.java @@ -0,0 +1,56 @@ + +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.plugin.weekview; + +import java.util.Calendar; +import java.util.HashSet; +import java.util.Set; + +import org.rapla.components.calendarview.swing.SwingWeekView; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.CalendarOptions; + + +public class SwingDayCalendar extends SwingWeekCalendar +{ + public SwingDayCalendar( RaplaContext sm, CalendarModel model, boolean editable ) throws RaplaException + { + super( sm, model, editable ); + } + + protected void configureView() { + super.configureView(); + SwingWeekView view = (SwingWeekView) this.view; + CalendarOptions calendarOptions = getCalendarOptions(); + Set excludeDays = calendarOptions.getExcludeDays(); + excludeDays = new HashSet(); + Calendar cal = getRaplaLocale().createCalendar(); + cal.setTime( model.getSelectedDate() ); + for (int i=0;i<8;i++) { + if ( i != cal.get( Calendar.DAY_OF_WEEK)) { + excludeDays.add( new Integer(i)); + } + } + view.setExcludeDays( excludeDays ); + view.setShowNonEmptyExcludedDays( false ); + } + + public int getIncrementSize() { + return Calendar.DAY_OF_YEAR; + } + +} diff --git a/Rapla/src/org/rapla/plugin/weekview/SwingWeekCalendar.java b/Rapla/src/org/rapla/plugin/weekview/SwingWeekCalendar.java new file mode 100644 index 0000000..a1bbec8 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/weekview/SwingWeekCalendar.java @@ -0,0 +1,161 @@ + +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.plugin.weekview; + +import java.awt.Color; +import java.awt.Font; +import java.text.MessageFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.Set; + +import javax.swing.JComponent; + +import org.rapla.components.calendar.DateChangeEvent; +import org.rapla.components.calendar.DateRenderer; +import org.rapla.components.calendar.DateRendererAdapter; +import org.rapla.components.calendarview.swing.AbstractSwingCalendar; +import org.rapla.components.calendarview.swing.SwingWeekView; +import org.rapla.components.util.DateTools; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.CalendarOptions; +import org.rapla.plugin.abstractcalendar.AbstractRaplaSwingCalendar; +import org.rapla.plugin.abstractcalendar.GroupAllocatablesStrategy; +import org.rapla.plugin.abstractcalendar.RaplaBuilder; +import org.rapla.plugin.abstractcalendar.SwingRaplaBuilder; + + +public class SwingWeekCalendar extends AbstractRaplaSwingCalendar +{ + public SwingWeekCalendar( RaplaContext sm, CalendarModel model, boolean editable ) throws RaplaException + { + super( sm, model, editable ); + } + + protected AbstractSwingCalendar createView(boolean showScrollPane) { + /** renderer for dates in weekview */ + final DateRendererAdapter dateRenderer = new DateRendererAdapter((DateRenderer) getService(DateRenderer.class.getName()), getRaplaLocale().getTimeZone(), getRaplaLocale().getLocale()); + SwingWeekView wv = new SwingWeekView( showScrollPane ) { + private static final long serialVersionUID = 1L; + + protected JComponent createSlotHeader(Date date) { + JComponent component = super.createSlotHeader(date); + boolean today = DateTools.isSameDay(getQuery().today().getTime(), date.getTime()); + if ( today) + { + component.setFont(component.getFont().deriveFont( Font.BOLD)); + } + if (isEditable() && dateRenderer != null ) { + component.setOpaque(true); + Color color = dateRenderer.getBackgroundColor(date); + String toolTip = dateRenderer.getToolTipText(date); + component.setBackground(color); + component.setToolTipText(toolTip); + } + return component; + } + + @Override + public void rebuild() { + super.rebuild(); + // update week + weekTitle.setText(MessageFormat.format(getString("calendarweek.abbreviation"), getStartDate())); + } + }; + return wv; + } + + public void dateChanged(DateChangeEvent evt) { + super.dateChanged( evt ); + ((SwingWeekView)view).scrollDateVisible( evt.getDate()); + } + + protected RaplaBuilder createBuilder() throws RaplaException + { + RaplaBuilder builder = new SwingRaplaBuilder(getContext()); + builder.setRepeatingVisible( view.isEditable()); + builder.setEditingUser( getUser() ); + builder.setExceptionsExcluded( !getCalendarOptions().isExceptionsVisible() || !view.isEditable()); + builder.setFromModel( model, view.getStartDate(), view.getEndDate() ); + + GroupAllocatablesStrategy strategy = new GroupAllocatablesStrategy( getRaplaLocale().getLocale() ); + boolean compactColumns = getCalendarOptions().isCompactColumns() || builder.getAllocatables().size() ==0 ; + strategy.setFixedSlotsEnabled( !compactColumns); + strategy.setResolveConflictsEnabled( true ); + builder.setBuildStrategy( strategy ); + return builder; + } + + protected void configureView() { + SwingWeekView view = (SwingWeekView) this.view; + CalendarOptions calendarOptions = getCalendarOptions(); + Set excludeDays = calendarOptions.getExcludeDays(); + view.setExcludeDays( excludeDays ); + + int rowsPerHour = calendarOptions.getRowsPerHour(); + + int start = calendarOptions.getWorktimeStart(); + int end = calendarOptions.getWorktimeEnd(); + int hours = Math.max(1, end - start); + view.setRowsPerHour( rowsPerHour ); + if ( rowsPerHour == 1 ) { + if ( hours < 10) + { + view.setRowSize( 80); + } + else if ( hours < 15) + { + view.setRowSize( 60); + } + else + { + view.setRowSize( 30); + } + } else if ( rowsPerHour == 2 ) { + if ( hours < 10) + { + view.setRowSize( 40); + } + else + { + view.setRowSize( 20); + } + } + else if ( rowsPerHour >= 4 ) + { + view.setRowSize( 15); + } + view.setWorktime( calendarOptions.getWorktimeStart(), calendarOptions.getWorktimeEnd()); + view.setShowNonEmptyExcludedDays( true ); + if ( !view.isEditable() ) { + view.setSlotSize( model.getSize()); + } else { + view.setSlotSize( 135 ); + } + view.setToDate(model.getSelectedDate()); + } + + public void scrollToStart() + { + ((SwingWeekView)view).scrollToStart(); + } + + public int getIncrementSize() { + return Calendar.WEEK_OF_YEAR; + } + +} diff --git a/Rapla/src/org/rapla/plugin/weekview/WeekViewFactory.java b/Rapla/src/org/rapla/plugin/weekview/WeekViewFactory.java new file mode 100644 index 0000000..ea9da6e --- /dev/null +++ b/Rapla/src/org/rapla/plugin/weekview/WeekViewFactory.java @@ -0,0 +1,69 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.weekview; + +import javax.swing.Icon; + +import org.rapla.facade.RaplaComponent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.CalendarModel; +import org.rapla.gui.SwingCalendarView; +import org.rapla.gui.ViewFactory; +import org.rapla.gui.images.Images; +import org.rapla.servletpages.RaplaPageGenerator; + +public class WeekViewFactory extends RaplaComponent implements ViewFactory +{ + public WeekViewFactory( RaplaContext context ) throws RaplaException + { + super( context ); + } + + public final static String WEEK_VIEW = "week"; + + public SwingCalendarView createSwingView(RaplaContext context, CalendarModel model, boolean editable) throws RaplaException + { + return new SwingWeekCalendar( context, model, editable); + } + + public RaplaPageGenerator createHTMLView(RaplaContext context, CalendarModel model) throws RaplaException + { + return new HTMLWeekViewPage( context, model); + } + + public String getViewId() + { + return WEEK_VIEW; + } + + public String getName() + { + return getString(WEEK_VIEW); + } + + Icon icon; + public Icon getIcon() + { + if ( icon == null) { + icon = Images.getIcon("/org/rapla/plugin/weekview/images/week.png"); + } + return icon; + } + + public String getMenuSortKey() { + return "B"; + } + +} + diff --git a/Rapla/src/org/rapla/plugin/weekview/WeekViewPlugin.java b/Rapla/src/org/rapla/plugin/weekview/WeekViewPlugin.java new file mode 100644 index 0000000..0202ab3 --- /dev/null +++ b/Rapla/src/org/rapla/plugin/weekview/WeekViewPlugin.java @@ -0,0 +1,60 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.weekview; + +import org.apache.avalon.framework.configuration.Configuration; +import org.rapla.framework.Container; +import org.rapla.framework.PluginDescriptor; +import org.rapla.plugin.RaplaExtensionPoints; +import org.rapla.plugin.RaplaPluginMetaInfo; + +public class WeekViewPlugin implements PluginDescriptor +{ + static boolean ENABLE_BY_DEFAULT = true; + + public String toString() { + return "Week View"; + } + + public void provideServices(Container container, Configuration config) { + if ( !config.getAttributeAsBoolean("enabled", ENABLE_BY_DEFAULT) ) + return; + + container.addContainerProvidedComponent + ( + RaplaExtensionPoints.CALENDAR_VIEW_EXTENSION + ,WeekViewFactory.class.getName() + ,WeekViewFactory.WEEK_VIEW + ,null + ); + + container.addContainerProvidedComponent + ( + RaplaExtensionPoints.CALENDAR_VIEW_EXTENSION + ,DayViewFactory.class.getName() + ,DayViewFactory.DAY_VIEW + ,null + ); + } + + public Object getPluginMetaInfos( String key ) + { + if ( RaplaPluginMetaInfo.METAINFO_PLUGIN_ENABLED_BY_DEFAULT.equals( key )) { + return new Boolean( ENABLE_BY_DEFAULT ); + } + return null; + } + + +} + diff --git a/Rapla/src/org/rapla/plugin/weekview/images/day.png b/Rapla/src/org/rapla/plugin/weekview/images/day.png new file mode 100644 index 0000000..6d6e294 Binary files /dev/null and b/Rapla/src/org/rapla/plugin/weekview/images/day.png differ diff --git a/Rapla/src/org/rapla/plugin/weekview/images/week.png b/Rapla/src/org/rapla/plugin/weekview/images/week.png new file mode 100644 index 0000000..1fc0339 Binary files /dev/null and b/Rapla/src/org/rapla/plugin/weekview/images/week.png differ diff --git a/Rapla/src/org/rapla/server/RaplaRemoteServiceFactory.java b/Rapla/src/org/rapla/server/RaplaRemoteServiceFactory.java new file mode 100644 index 0000000..5003a1a --- /dev/null +++ b/Rapla/src/org/rapla/server/RaplaRemoteServiceFactory.java @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.server; + +import org.rapla.framework.RaplaException; + + +/** this Factory creates the server side for the RemoteServiceCaller + * @see RemoteServiceCaller */ +public interface RaplaRemoteServiceFactory { + String ROLE = RaplaRemoteServiceFactory.class.getName(); + RemoteService createRemoteService( RemoteSession session) throws RaplaException; +} diff --git a/Rapla/src/org/rapla/server/RemoteMethod.java b/Rapla/src/org/rapla/server/RemoteMethod.java new file mode 100644 index 0000000..b4e4f4d --- /dev/null +++ b/Rapla/src/org/rapla/server/RemoteMethod.java @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 ?, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.server; + +import java.util.Map; + + +public class RemoteMethod { + String methodName; + String[] parameterNames; + public RemoteMethod(String methodName) + { + this(methodName,new String[]{} ); + } + public RemoteMethod(String methodName, String[] parameterNames) + { + this.methodName = methodName; + this.parameterNames = parameterNames; + } + + public String method() + { + return methodName; + } + + public String arg(int index) + { + return parameterNames[index]; + } + + public int length() + { + return parameterNames.length; + } + + public String[] getArgumentNames() + { + return parameterNames; + } + + public String value( Map arguments, int i ) + { + String argumentName = parameterNames[i]; + return (String) arguments.get( argumentName); + } + public boolean is( String methodName) + { + + return this.methodName.equals( methodName); + } + + public boolean equals(Object obj) { + if ( obj instanceof String) + { + return is( (String)obj); + } + return super.equals (obj); + } + +} diff --git a/Rapla/src/org/rapla/server/RemoteService.java b/Rapla/src/org/rapla/server/RemoteService.java new file mode 100644 index 0000000..75adf03 --- /dev/null +++ b/Rapla/src/org/rapla/server/RemoteService.java @@ -0,0 +1,17 @@ +package org.rapla.server; + +import java.io.IOException; +import java.io.OutputStream; +import java.text.ParseException; +import java.util.Map; + +import org.rapla.framework.RaplaException; + +public interface RemoteService +{ + void remoteMethodCall( + String methodName, + Map args, + OutputStream out ) throws RaplaException, IOException, ParseException; + +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/server/RemoteServiceCaller.java b/Rapla/src/org/rapla/server/RemoteServiceCaller.java new file mode 100644 index 0000000..2b4181b --- /dev/null +++ b/Rapla/src/org/rapla/server/RemoteServiceCaller.java @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.server; + +import org.rapla.framework.RaplaException; + +/** provides a mechanism to invoke a remote service on the server. + * The server must provide a RemoteService for the specified serviceName. + * The RemoteOperator provides the Service RemoteServiceCaller */ + +public interface RemoteServiceCaller { + String ROLE = RemoteServiceCaller.class.getName(); + String call(String serviceName,RemoteMethod method,String[] args) throws RaplaException; +} + diff --git a/Rapla/src/org/rapla/server/RemoteSession.java b/Rapla/src/org/rapla/server/RemoteSession.java new file mode 100644 index 0000000..3e9b0ca --- /dev/null +++ b/Rapla/src/org/rapla/server/RemoteSession.java @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.server; + +import org.apache.avalon.framework.logger.Logger; +import org.rapla.entities.User; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +/** An interface to access the SessionInformation. An implementation of + * RemoteSession gets passed to the creation RaplaRemoteService.*/ + +public interface RemoteSession { + boolean isAuthentified(); + User getUser() throws RaplaException; + RaplaContext getContext() throws RaplaException; + Logger getLogger(); +} diff --git a/Rapla/src/org/rapla/server/RemoteStorage.java b/Rapla/src/org/rapla/server/RemoteStorage.java new file mode 100644 index 0000000..83a9fc5 --- /dev/null +++ b/Rapla/src/org/rapla/server/RemoteStorage.java @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 ?, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.server; + +import java.util.Date; +import java.util.List; + +import org.rapla.entities.RaplaType; +import org.rapla.framework.RaplaException; +import org.rapla.storage.UpdateEvent; + +public interface RemoteStorage { + String ROLE = RemoteStorage.class.getName(); + RemoteMethod AUTHENTICATE = new RemoteMethod("authenticate",new String[] { "username", "password"}); + RemoteMethod CAN_CHANGE_PASSWORD= new RemoteMethod("canChangePassword"); + RemoteMethod CHANGE_PASSWORD= new RemoteMethod("changePassword",new String[] {"username","oldPassword","newPassword"}); + RemoteMethod GET_RESOURCES = new RemoteMethod("getResources"); + RemoteMethod GET_SERVER_TIME= new RemoteMethod("getServerTime"); + RemoteMethod GET_RESERVATIONS= new RemoteMethod("getReservations", new String[] {"start","end"}); + RemoteMethod GET_ENTITY_RECURSIVE= new RemoteMethod("getEntityRecursive", new String[] {"id"}); + RemoteMethod DISPATCH= new RemoteMethod("dispatch", new String[] {"evt"}); + RemoteMethod CREATE_IDENTIFIER= new RemoteMethod("createIdentifier", new String[] {"raplaType"}); + + // These Methods belong to the server + RemoteMethod LOGIN = new RemoteMethod("login",new String[] { "username", "password"}); + RemoteMethod RESTART_SERVER = new RemoteMethod("restartServer",new String[] { }); + RemoteMethod CHECK_SERVER_VERSION = new RemoteMethod("checkServerVersion",new String[] {"clientVersion" }); + RemoteMethod REFRESH = new RemoteMethod("refresh",new String[] {"clientRepositoryVersion" }); + + void authenticate(String username,String password) throws RaplaException; + boolean canChangePassword() throws RaplaException; + void changePassword(String username,char[] oldPassword,char[] newPassword) throws RaplaException; + List getResources() throws RaplaException; + /** returns the time on the server */ + long getServerTime() throws RaplaException; + /** delegates the corresponding method in the StorageOperator. */ + List getReservations(Date start,Date end) throws RaplaException; + + List getEntityRecursive(Object id) throws RaplaException; + + void dispatch(UpdateEvent evt) throws RaplaException; + Object createIdentifier(RaplaType raplaType) throws RaplaException; +} diff --git a/Rapla/src/org/rapla/server/RemoteStorageCallback.java b/Rapla/src/org/rapla/server/RemoteStorageCallback.java new file mode 100644 index 0000000..82bfde9 --- /dev/null +++ b/Rapla/src/org/rapla/server/RemoteStorageCallback.java @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 ?, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.server; + +import org.rapla.storage.UpdateEvent; + +/** all clients providing this callback service get notified + when the server-storage is modified. + @see org.rapla.components.rpc.MessagingClient#setCallbacks +*/ +public interface RemoteStorageCallback { + String ROLE = RemoteStorageCallback.class.getName(); + void notify(UpdateEvent evt); +} diff --git a/Rapla/src/org/rapla/server/RestartServer.java b/Rapla/src/org/rapla/server/RestartServer.java new file mode 100644 index 0000000..6b6a087 --- /dev/null +++ b/Rapla/src/org/rapla/server/RestartServer.java @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Praktikum Gruppe2?, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.server; + +import org.rapla.framework.RaplaException; + + +public interface RestartServer { + String ROLE = RestartServer.class.getName(); + public void restartServer() throws RaplaException; +} diff --git a/Rapla/src/org/rapla/server/ServerService.java b/Rapla/src/org/rapla/server/ServerService.java new file mode 100644 index 0000000..358fd99 --- /dev/null +++ b/Rapla/src/org/rapla/server/ServerService.java @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 ?, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.server; + +import org.apache.avalon.framework.activity.Startable; +import org.rapla.facade.ClientFacade; +import org.rapla.framework.Container; + +/** Encapsulates a StorageOperator. This service is responsible for +
      +
    • synchronizing update and remove request from clients and passing + them to the storage-operator
    • + +
    • authentification of the clients
    • + +
    • notifing subscribed clients when the stored-data has + changed
    • +
    +*/ +public interface ServerService extends Startable, Container { + String ROLE = ServerService.class.getName(); + ClientFacade getFacade(); + +} diff --git a/Rapla/src/org/rapla/server/ShutdownListener.java b/Rapla/src/org/rapla/server/ShutdownListener.java new file mode 100644 index 0000000..118971c --- /dev/null +++ b/Rapla/src/org/rapla/server/ShutdownListener.java @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.server; + + +public interface ShutdownListener +{ + void shutdownInitiated(); + void shutdownComplete(boolean restart); +} diff --git a/Rapla/src/org/rapla/server/ShutdownService.java b/Rapla/src/org/rapla/server/ShutdownService.java new file mode 100644 index 0000000..2c5dca7 --- /dev/null +++ b/Rapla/src/org/rapla/server/ShutdownService.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.server; + +import org.rapla.framework.RaplaException; +/** You will need this remote-service to shutdown the server from + another process. This is for-example necessary if the server is running in + the background. + + To prevent a shutdown from unprivileged users on the server-host specify + a shutdown-password. + Hint: This is only useful if the configuration can't be + read by unprivileged users ;-). +*/ + +public interface ShutdownService +{ + RemoteMethod SHUTDOWN = new RemoteMethod("shutdown",new String[] {"password", "restart"}); + String ROLE = ShutdownService.class.getName(); + void shutdown(String shutdownPassword, boolean restart) throws RaplaException; + void addShutdownListener(ShutdownListener listener); + void removeShutdownListener(ShutdownListener listener); +} diff --git a/Rapla/src/org/rapla/server/internal/RemoteSessionImpl.java b/Rapla/src/org/rapla/server/internal/RemoteSessionImpl.java new file mode 100644 index 0000000..8f42f95 --- /dev/null +++ b/Rapla/src/org/rapla/server/internal/RemoteSessionImpl.java @@ -0,0 +1,91 @@ +package org.rapla.server.internal; + +import java.util.HashMap; + +import org.apache.avalon.framework.logger.Logger; +import org.rapla.entities.User; +import org.rapla.facade.RaplaComponent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.server.RaplaRemoteServiceFactory; +import org.rapla.server.RemoteSession; +import org.rapla.storage.StorageOperator; + +/** Implementation of RemoteStorage as a RemoteService + * @see org.rapla.server.RemoteStorage + * @see org.rapla.components.rpc.RemoteServiceDispatcher + * + */ +public class RemoteSessionImpl extends RaplaComponent implements RemoteSession { + /** + * + */ + String username; + HashMap remoteServiceMap = new HashMap(); + Logger logger; + StorageOperator operator; + long serverStartTime; + + public RemoteSessionImpl(RaplaContext context, String clientName, long serverStartTime) throws RaplaException { + super( context ); + this.serverStartTime = serverStartTime; + operator = (StorageOperator)context.lookup(StorageOperator.ROLE); + logger = super.getLogger().getChildLogger(clientName); + + } + + public Logger getLogger() { + return logger; + } + + public RaplaContext getContext() { + return super.getContext(); + } + + Object lookupService( String name) throws RaplaException { + Object serviceSession =remoteServiceMap.get( name); + if ( serviceSession != null) { + return serviceSession; + } + RaplaRemoteServiceFactory service = (RaplaRemoteServiceFactory)getContext().lookup( RaplaRemoteServiceFactory.ROLE + "/" + name); + if ( service == null) + throw new RaplaException( "Can't find Remoteservice for Role " + name); + serviceSession = service.createRemoteService( this); + remoteServiceMap.put( name, serviceSession); + return serviceSession; + } + + + String getUsername() { + return username; + } + + public User getUser() throws RaplaException { + String username = getUsername(); + if (username == null) + throw new IllegalStateException("No username found in session."); + User user = this.operator.getUser(username); + if (user == null) + throw new RaplaException("No user found for username: " + username); + return user; + } + + public boolean isAuthentified() { + return getUsername() != null; + } + + public void setUsername( String username ) + { + this.username = username; + } + + public long getServerStartTime() + { + return serverStartTime; + } + + + + + +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/server/internal/RemoteStorageImpl.java b/Rapla/src/org/rapla/server/internal/RemoteStorageImpl.java new file mode 100644 index 0000000..bbfa43c --- /dev/null +++ b/Rapla/src/org/rapla/server/internal/RemoteStorageImpl.java @@ -0,0 +1,447 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.server.internal; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.StringReader; +import java.io.StringWriter; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.avalon.framework.logger.AbstractLogEnabled; +import org.apache.avalon.framework.logger.Logger; +import org.rapla.components.util.SerializableDateTimeFormat; +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.entities.DependencyException; +import org.rapla.entities.RaplaType; +import org.rapla.entities.User; +import org.rapla.entities.storage.EntityResolver; +import org.rapla.entities.storage.Mementable; +import org.rapla.entities.storage.RefEntity; +import org.rapla.entities.storage.internal.SimpleEntity; +import org.rapla.entities.storage.internal.SimpleIdentifier; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaDefaultContext; +import org.rapla.framework.RaplaException; +import org.rapla.server.RemoteService; +import org.rapla.server.RemoteSession; +import org.rapla.server.RemoteStorage; +import org.rapla.server.ShutdownService; +import org.rapla.storage.AuthenticationStore; +import org.rapla.storage.CachableStorageOperator; +import org.rapla.storage.IOContext; +import org.rapla.storage.IdTable; +import org.rapla.storage.LocalCache; +import org.rapla.storage.RaplaSecurityException; +import org.rapla.storage.UpdateEvent; +import org.rapla.storage.impl.EntityStore; +import org.rapla.storage.xml.RaplaInput; +import org.rapla.storage.xml.RaplaMainReader; +import org.rapla.storage.xml.RaplaMainWriter; + +/** Provides an adapter for each client-session to their shared storage operator + * Handles security and synchronizing aspects. + * @author ckohlhaas + * + */ +public class RemoteStorageImpl extends AbstractLogEnabled implements RemoteStorage, RemoteService{ + CachableStorageOperator operator; + RemoteSession session; + + protected SecurityManager security; + boolean authenticationStore; + ServerServiceImpl server; + + RemoteStorageImpl(ServerServiceImpl server, RemoteSession session) throws RaplaException { + this.server = server; + this.session = session; + operator = (CachableStorageOperator)getContext().lookup(CachableStorageOperator.ROLE); + security = (SecurityManager)getContext().lookup( SecurityManager.class.getName()); + authenticationStore = getContext().has(AuthenticationStore.ROLE); + + enableLogging( session.getLogger()); + } + + private void checkAuthentified() throws RaplaSecurityException { + if (!session.isAuthentified()) + throw new RaplaSecurityException("User was not authentified "); + } + + private User getSessionUser() throws RaplaException { + return session.getUser(); + } + + public List getResources() throws RaplaException + { + checkAuthentified(); + User user = null; + this.getLogger().debug ("A RemoteServer wants to get all resource-objects."); + synchronized (operator.getLock()) + { + return makeTransactionSafe(operator.getVisibleEntities(user)); + } + } + + public void remoteMethodCall( String methodName, Map args, OutputStream out ) throws RaplaException, IOException, ParseException + { + if ( !session.isAuthentified()) + { + throw new SessionExpiredException("Session on the server not valid anymore. Maybe the server has been restarted"); + } + + synchronized (operator.getLock()) + { + + BufferedWriter outWriter = new BufferedWriter( new OutputStreamWriter( out,"utf-8")); + + RaplaDefaultContext ioContext = new IOContext().createOutputContext( getContext(), operator.getCache(), true, true); + RaplaMainWriter writer = new RaplaMainWriter(ioContext); + writer.setWriter( outWriter); + if ( RemoteStorage.GET_RESOURCES.is( methodName )) + { + List resources = getResources(); + writer.printList( resources, Collections.EMPTY_LIST, getRepositoryVersion() ); + } + else if ( RemoteStorage.GET_RESERVATIONS.is( methodName)) + { + String startS = RemoteStorage.GET_RESERVATIONS.value(args,0); + String endS = RemoteStorage.GET_RESERVATIONS.value(args,1); + SerializableDateTimeFormat format = new SerializableDateTimeFormat(); + Date start = null; + if ( startS != null) + { + start = format.parseDate( startS, false); + } + Date end = null; + if ( endS != null) + { + end = format.parseDate( endS, true); + } + List resources = getReservations(start, end); + writer.printList( resources, Collections.EMPTY_LIST, getRepositoryVersion() ); + } + else if ( RemoteStorage.DISPATCH.is ( methodName)) + { + String xml = RemoteStorage.DISPATCH.value(args,0); + + LocalCache cache = operator.getCache(); + UpdateEvent event = createUpdateEvent( getContext(),xml, cache ); + + dispatch( event); + } + else if ( RemoteStorage.CREATE_IDENTIFIER.is( methodName)) + { + String typeName = RemoteStorage.CREATE_IDENTIFIER.value(args,0); + RaplaType raplaType = RaplaType.find( typeName ); + SimpleIdentifier id =(SimpleIdentifier) operator.createIdentifier( raplaType); + String idString = raplaType.getLocalName() + "_" + id.getKey(); + outWriter.write( idString ); + } + else if (RemoteStorage.GET_SERVER_TIME.is( methodName)) + { + String timeString = String.valueOf( getServerTime()); + outWriter.write( timeString ); + } + else if (RemoteStorage.GET_ENTITY_RECURSIVE.is(methodName)) + { + String idS = RemoteStorage.GET_ENTITY_RECURSIVE.value(args,0); + int index_ = idS.lastIndexOf( '_'); + String typeName = idS.substring(0, index_ ); + String keyString = idS.substring( index_ + 1); + RaplaType raplaType = RaplaType.find( typeName ); + int key = Integer.parseInt( keyString); + // RaplaType raplaType = (RaplaType); + + SimpleIdentifier id = new SimpleIdentifier( raplaType, key); + List resources = getEntityRecursive( id); + writer.printList( resources, Collections.EMPTY_LIST, getRepositoryVersion() ); + } + else if ( RemoteStorage.CAN_CHANGE_PASSWORD.is( methodName)) + { + String resultString = String.valueOf( canChangePassword()); + outWriter.write( resultString ); + } + else if ( RemoteStorage.CHANGE_PASSWORD.is( methodName)) + { + String username = RemoteStorage.CHANGE_PASSWORD.value( args, 0); + String oldPassword = RemoteStorage.CHANGE_PASSWORD.value( args, 1); + String newPassword = RemoteStorage.CHANGE_PASSWORD.value( args, 2); + changePassword( username, oldPassword.toCharArray(), newPassword.toCharArray()); + } + else if ( RemoteStorage.REFRESH.equals( methodName)) + { + String time = RemoteStorage.REFRESH.value( args, 0); + String xml = server.createUpdateXML(Long.valueOf( time).longValue()); + outWriter.write( xml); + } + else if ( RemoteStorage.RESTART_SERVER.equals( methodName)) + { + restartServer(); + } + else + { + throw new UnsupportedOperationException("Operation " + methodName+ " not available on server."); + } + outWriter.flush(); + } + } + + + private long getRepositoryVersion() + { + return server.repositoryVersion; + } + + public I18nBundle getI18n() throws RaplaException { + return (I18nBundle)getContext().lookup(I18nBundle.ROLE + "/org.rapla.RaplaResources"); + } + + + + public List getEntityRecursive(Object id) throws RaplaException { + checkAuthentified(); + //synchronized (operator.getLock()) + { + RefEntity entity = operator.resolveId(id); + ArrayList completeList = new ArrayList(); + completeList.add( entity ); + Iterator it = entity.getSubEntities(); + while (it.hasNext()) { + completeList.add( it.next() ); + } + List list = makeTransactionSafe( completeList ); + getLogger().debug("Get entity " + entity); + return list; + } + } + + public List getReservations(Date start,Date end) throws RaplaException + { + checkAuthentified(); + User user = null; + this.getLogger().debug ("A RemoteServer wants to reservations from ." + start + " to " + end); + synchronized (operator.getLock()) + { + ArrayList completeList = new ArrayList(); + List reservations = operator.getReservations(user, start, end ); + completeList.addAll(reservations); + Iterator it = reservations.iterator(); + while (it.hasNext()) { + Iterator it2 = ((RefEntity)it.next()).getSubEntities(); + while (it2.hasNext()) { + completeList.add( it2.next() ); + } + } + List list = makeTransactionSafe( completeList ); + getLogger().debug("Get reservations " + start + " " + end + ": " + + reservations.size() + "," + list.size()); + return list; + } + } + + public RaplaContext getContext() throws RaplaException { + return session.getContext(); + } + + public void restartServer() throws RaplaException { + checkAuthentified(); + if (!getSessionUser().isAdmin()) + throw new RaplaSecurityException("Only admins can restart the server"); + + ((ShutdownService) getContext().lookup(ShutdownService.ROLE)).shutdown(null, true); + } + + + public long getServerTime() throws RaplaException { + return System.currentTimeMillis(); + } + + public void dispatch(UpdateEvent evt) + throws RaplaException { + checkAuthentified(); + try { + User user; + if ( evt.getUserId() != null) + { + user = (User) operator.resolveId(evt.getUserId()); + } + else + { + user = session.getUser(); + } + synchronized (operator.getLock()) + { + EntityResolver resolver = operator.createEntityResolver(evt.getStoreObjects() + ,operator.getCache()); + Iterator it = evt.getStoreObjects().iterator(); + while (it.hasNext()) { + SimpleEntity entity = (SimpleEntity) it.next(); + if (getLogger().isDebugEnabled()) + getLogger().debug("Contextualizing " + entity); + entity.resolveEntities( resolver); + } + + it = evt.getRemoveObjects().iterator(); + while (it.hasNext()) { + SimpleEntity entity = (SimpleEntity) it.next(); + entity.resolveEntities( resolver); + } + + it = evt.getStoreObjects().iterator(); + while (it.hasNext()) { + SimpleEntity entity = (SimpleEntity) it.next(); + security.checkWritePermissions(user,entity); + } + + it = evt.getRemoveObjects().iterator(); + while (it.hasNext()) { + SimpleEntity entity = (SimpleEntity) it.next(); + security.checkWritePermissions(user,entity); + } + + if (this.getLogger().isDebugEnabled()) + this.getLogger().debug("Dispatching changes to " + operator.getClass()); + + operator.dispatch(evt); + if (this.getLogger().isDebugEnabled()) + this.getLogger().debug("Changes dispatched returning result."); + } + } catch (DependencyException ex) { + throw ex; + } catch (RaplaException ex) { + this.getLogger().error(ex.getMessage(),ex); + throw ex; + } catch (Exception ex) { + this.getLogger().error(ex.getMessage(),ex); + throw new RaplaException(ex); + } catch (Error ex) { + this.getLogger().error(ex.getMessage(),ex); + throw ex; + } + } + + public Object createIdentifier(RaplaType raplaType) throws RaplaException { + checkAuthentified(); + synchronized (operator.getLock()) + { + //User user = + getSessionUser(); //check if authenified + return operator.createIdentifier(raplaType); + } + } + + public void authenticate(String username, + String password) throws RaplaException + { + synchronized (operator.getLock()) + { + getSessionUser(); //check if authenified + server.authenticate( username, password ); + } + } + + public boolean canChangePassword() throws RaplaException { + checkAuthentified(); + synchronized (operator.getLock()) + { + return !authenticationStore && operator.canChangePassword(); + } + } + + public void changePassword(String username + ,char[] oldPassword + ,char[] newPassword + ) throws RaplaException + { + checkAuthentified(); + if ( authenticationStore ) { + throw new RaplaException("Rapla can't change your password. " + + "Authentication is done via plugin." ); + } + synchronized (operator.getLock()) { + User sessionUser = getSessionUser(); + if (!sessionUser.isAdmin()) { + operator.authenticate(username,new String(oldPassword)); + } + User user = operator.getUser(username); + operator.changePassword(user,oldPassword,newPassword); + } + } + + + static public UpdateEvent createUpdateEvent( RaplaContext context,String xml, LocalCache cache ) throws RaplaException + { + EntityStore store = new EntityStore( cache, cache.getSuperCategory()); + RaplaContext inputContext = new IOContext().createInputContext(context,store,new IdTable()); + Logger logger = (Logger)context.lookup( Logger.class.getName()); + RaplaInput xmlAdapter = new RaplaInput( logger.getChildLogger("reading")); + RaplaMainReader contentHandler = new RaplaMainReader( inputContext); + try + { + xmlAdapter.read(new StringReader( xml), contentHandler, false); + } + catch (IOException e) + { + throw new RaplaException(e); + } + UpdateEvent event = new UpdateEvent(); + event.setRepositoryVersion(store.getRepositoryVersion()); + for (Iterator it = store.getList().iterator();it.hasNext();) + { + RefEntity object = (RefEntity)it.next(); + event.putStore( object); + } + for (Iterator it = store.getRemoveIds().iterator();it.hasNext();) + { + Object id = (Object)it.next(); + RefEntity entity = (RefEntity)cache.get( id ); + if ( entity != null) + { + event.putRemove( entity); + } + } + return event; + } + + static public String createUpdateEvent( RaplaContext context, LocalCache cache,UpdateEvent evt) throws RaplaException, IOException + { + RaplaDefaultContext ioContext = new IOContext().createOutputContext( context, cache, true, true); + StringWriter stringWriter = new StringWriter( ); + RaplaMainWriter writer = new RaplaMainWriter(ioContext); + BufferedWriter buf = new BufferedWriter(stringWriter); + writer.setWriter( buf); + writer.printList( evt.getStoreObjects(),evt.getRemoveObjects(), evt.getRepositoryVersion()); + buf.flush(); + String xml = stringWriter.toString(); + return xml; + } + + private static List makeTransactionSafe(List objectList) { + List saveList = new ArrayList(); + Iterator it = objectList.iterator(); + while (it.hasNext()) { + saveList.add(((Mementable)it.next()).clone()); + } + return saveList; + } + +} + diff --git a/Rapla/src/org/rapla/server/internal/SecurityManager.java b/Rapla/src/org/rapla/server/internal/SecurityManager.java new file mode 100644 index 0000000..e9b91bc --- /dev/null +++ b/Rapla/src/org/rapla/server/internal/SecurityManager.java @@ -0,0 +1,308 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Praktikum Gruppe2?, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.server.internal; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.apache.avalon.framework.logger.AbstractLogEnabled; +import org.apache.avalon.framework.logger.Logger; +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.entities.Category; +import org.rapla.entities.Ownable; +import org.rapla.entities.User; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.AppointmentFormater; +import org.rapla.entities.domain.Permission; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.storage.RefEntity; +import org.rapla.facade.Conflict; +import org.rapla.facade.RaplaComponent; +import org.rapla.facade.internal.ConflictFinder; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaContextException; +import org.rapla.framework.RaplaException; +import org.rapla.storage.CachableStorageOperator; +import org.rapla.storage.RaplaSecurityException; + +/** checks if the client can store or delete an entity */ +public class SecurityManager extends AbstractLogEnabled +{ + protected ConflictFinder conflictFinder; + I18nBundle i18n; + AppointmentFormater appointmentFormater; + CachableStorageOperator operator; + + public SecurityManager(RaplaContext serviceManager) throws RaplaContextException { + enableLogging( (Logger) serviceManager.lookup( Logger.class.getName())); + operator = (CachableStorageOperator)serviceManager.lookup( CachableStorageOperator.ROLE); + conflictFinder = new ConflictFinder( operator ); + i18n = (I18nBundle) serviceManager.lookup(I18nBundle.ROLE + "/org.rapla.RaplaResources"); + appointmentFormater = (AppointmentFormater) serviceManager.lookup(AppointmentFormater.ROLE); + } + + void checkWritePermissions(User user,RefEntity entity) throws RaplaSecurityException { + if (user.isAdmin()) + return; + + Object id = entity.getId(); + if (id == null) + throw new RaplaSecurityException("No id set"); + + boolean permitted = false; + RefEntity original = operator.getCache().get(entity.getId()); + // flag indicates if a user only exchanges allocatables (needs to have admin-access on the allocatable) + boolean canExchange = false; + + if (entity instanceof Ownable) { + User entityOwner = ((Ownable) entity).getOwner(); + if (original == null) { + permitted = entityOwner != null && user.isIdentical(entityOwner); + if (getLogger().isDebugEnabled()) + getLogger().debug("Permissions for new object " + entity + + "\nUser check: " + user + " = " + entityOwner); + } else { + User originalOwner = ((Ownable) original).getOwner(); + if (getLogger().isDebugEnabled()) + getLogger().debug("Permissions for existing object " + entity + + "\nUser check: " + user + " = " + entityOwner + " = " + originalOwner); + permitted = (originalOwner != null) && originalOwner.isIdentical(user) && originalOwner.isIdentical(entityOwner); + if ( !permitted ) { + canExchange = canExchange( user, entity, original ); + permitted = canExchange; + } + } + } + if ( !permitted && entity instanceof Allocatable ){ + if ( original == null ) { + permitted = isRegisterer(user); + } else { + permitted = ((Allocatable)original).canModify( user ); + } + } + if ( !permitted && original != null) + { + permitted = RaplaComponent.checkClassifiablePermissions(original, user); + + } + if (!permitted && entity instanceof Appointment) + { + final RefEntity reservation = (RefEntity)((Appointment)entity).getReservation(); + RefEntity originalReservation = operator.getCache().get(reservation.getId()); + permitted = RaplaComponent.checkClassifiablePermissions(originalReservation, user); + } + + if (!permitted) + throw new RaplaSecurityException("User '" + user + "' is not allowed to modify object. '" + entity + "'") ; + + // Check if the user can change the reservation + if ( Reservation.TYPE.equals( entity.getRaplaType()) ) + { + Reservation reservation = (Reservation) entity ; + Reservation originalReservation = (Reservation)original; + Allocatable[] all = reservation.getAllocatables(); + if ( originalReservation != null && canExchange ) { + List newAllocatabes = new ArrayList( Arrays.asList(reservation.getAllocatables() ) ); + newAllocatabes.removeAll( Arrays.asList( originalReservation.getAllocatables())); + all = (Allocatable[])newAllocatabes.toArray( Allocatable.ALLOCATABLE_ARRAY); + } + checkPermissions( user, reservation, originalReservation , all); + } + } + + protected boolean isRegisterer(User user) throws RaplaSecurityException { + try { + Category registererGroup = getUserGroupsCategory().getCategory(Permission.GROUP_REGISTERER_KEY); + return user.belongsTo(registererGroup); + } catch (RaplaException ex) { + throw new RaplaSecurityException(ex ); + } + } + + public Category getUserGroupsCategory() throws RaplaException { + Category userGroups = operator.getSuperCategory().getCategory(Permission.GROUP_CATEGORY_KEY); + if ( userGroups == null) { + throw new RaplaException("No category '" + Permission.GROUP_CATEGORY_KEY + "' available"); + } + return userGroups; + } + + + /** checks if the user just exchanges one allocatable or removes one. The user needs admin-access on the + * removed allocatable and the newly inserted allocatable */ + private boolean canExchange(User user, RefEntity entity, RefEntity original) { + if ( Appointment.TYPE.equals( entity.getRaplaType() )) { + return ((Appointment) entity).matches( (Appointment) original ); + } if ( Reservation.TYPE.equals( entity.getRaplaType() )) { + Reservation newReservation = (Reservation) entity; + Reservation oldReservation = (Reservation) original; + // We only need to check the length because we compare the appointments above. + if ( newReservation.getAppointments().length != oldReservation.getAppointments().length ) + { + return false; + } + + List oldAllocatables = Arrays.asList(oldReservation.getAllocatables()); + List newAllocatables = Arrays.asList(newReservation.getAllocatables()); + List inserted = new ArrayList(newAllocatables); + List removed = new ArrayList(oldAllocatables); + List overlap = new ArrayList(oldAllocatables); + inserted.removeAll( oldAllocatables ); + removed.removeAll( newAllocatables ); + overlap.retainAll( inserted ); + if ( inserted.size() == 0 && removed.size() == 0) + { + return false; + } + // he must have admin rights on all inserted resources + Iterator it = inserted.iterator(); + while (it.hasNext()) { + if (!canAllocateForOthers((Allocatable)it.next(),user)) + { + return false; + } + } + + // and he must have admin rights on all the removed resources + it = removed.iterator(); + while (it.hasNext()) { + if (!canAllocateForOthers((Allocatable)it.next(),user)) + { + return false; + } + } + + // He can't change appointments, only exchange allocatables he has admin-priviliges for + it = overlap.iterator(); + while (it.hasNext()) { + Allocatable all = (Allocatable)it.next(); + Appointment[] r1 = newReservation.getRestriction( all ); + Appointment[] r2 = oldReservation.getRestriction( all ); + boolean changed = false; + if ( r1.length != r2.length ) { + changed = true; + } else { + for ( int i=0; i< r1.length; i++ ) { + if ( !r1[i].matches(r2[i]) ) { + changed = true; + } + } + } + if ( changed && !canAllocateForOthers( all, user )) { + return false; + } + } + return true; + } + return false; + } + + /** for Thierry, we can make this configurable in the next version */ + private boolean canAllocateForOthers(Allocatable allocatable, User user) { + // only admins, current behaviour + return allocatable.canModify( user); + // everyone who can allocate the resource anytime + //return allocatable.canAllocate( user, null, null, operator.today()); + // everyone + //return true; + } + + private void checkConflictsAllowed(User user, Allocatable allocatable, Conflict[] conflictsBefore, Conflict[] conflictsAfter) throws RaplaSecurityException { + int nConflictsBefore = 0; + int nConflictsAfter = 0; + if ( allocatable.canCreateConflicts( user ) ) { + return; + } + if ( conflictsBefore != null ) { + for ( int i = 0; i < conflictsBefore.length; i++ ) { + if ( conflictsBefore[i].getAllocatable().equals ( allocatable ) ) { + nConflictsBefore ++; + } + } + } + + for ( int i = 0; i < conflictsAfter.length; i++ ) { + if ( conflictsAfter[i].getAllocatable().equals ( allocatable ) ) { + nConflictsAfter ++; + } + } + if ( nConflictsAfter > nConflictsBefore ) { + String all = allocatable.getName( i18n.getLocale() ); + throw new RaplaSecurityException( i18n.format("warning.no_conflict_permission", all ) ); + } + } + + private void checkPermissions( User user, Reservation r, Reservation original, Allocatable[] allocatables ) throws RaplaSecurityException { + Conflict[] conflictsBefore = null; + Conflict[] conflictsAfter = null; + try { + conflictsAfter = conflictFinder.getConflicts( null, r ); + if ( original != null ) { + conflictsBefore = conflictFinder.getConflicts( null, original ); + } + } catch ( RaplaException ex ) { + throw new RaplaSecurityException(" Can't check permissions due to:" + ex.getMessage(), ex ); + } + + Appointment[] appointments = r.getAppointments(); + // ceck if the user has the permisson to add allocations in the given time + for (int i = 0; i < allocatables.length; i++ ) { + Allocatable allocatable = allocatables[i]; + checkConflictsAllowed( user, allocatable, conflictsBefore, conflictsAfter ); + for (int j = 0; j < appointments.length; j++ ) { + Appointment appointment = appointments[j]; + if ( r.hasAllocated( allocatable, appointment ) && + !conflictFinder.hasPermissionToAllocate( user, appointment, allocatable, original ) ) { + String all = allocatable.getName( i18n.getLocale() ); + String app = appointmentFormater.getSummary( appointment ); + String error = i18n.format("warning.no_reserve_permission" + ,all + ,app); + throw new RaplaSecurityException( error ); + } + } + } + if (original == null ) + return; + + Date today = operator.today(); + + // 1. calculate the deleted assignments from allocatable to appointments + // 2. check if they were allowed to change in the specified time + appointments = original.getAppointments(); + allocatables = original.getAllocatables(); + for (int i = 0; i < allocatables.length; i++ ) { + Allocatable allocatable = allocatables[i]; + for (int j = 0; j < appointments.length; j++ ) { + Appointment appointment = appointments[j]; + if ( original.hasAllocated( allocatable, appointment ) + && !r.hasAllocated( allocatable, appointment ) ) { + Date start = appointment.getStart(); + Date end = appointment.getMaxEnd(); + if ( !allocatable.canAllocate( user, start, end, today ) ) { + String all = allocatable.getName( i18n.getLocale() ); + String app = appointmentFormater.getSummary( appointment ); + String error = i18n.format("warning.no_reserve_permission" + ,all + ,app); + throw new RaplaSecurityException( error ); + } + } + } + } + } +} diff --git a/Rapla/src/org/rapla/server/internal/ServerServiceImpl.java b/Rapla/src/org/rapla/server/internal/ServerServiceImpl.java new file mode 100644 index 0000000..ff5e310 --- /dev/null +++ b/Rapla/src/org/rapla/server/internal/ServerServiceImpl.java @@ -0,0 +1,543 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Praktikum Gruppe2?, Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.server.internal; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.DefaultConfiguration; +import org.rapla.components.util.DateTools; +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.entities.Entity; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.User; +import org.rapla.entities.configuration.Preferences; +import org.rapla.entities.configuration.RaplaConfiguration; +import org.rapla.entities.domain.Permission; +import org.rapla.entities.internal.UserImpl; +import org.rapla.entities.storage.RefEntity; +import org.rapla.facade.ClientFacade; +import org.rapla.facade.UpdateModule; +import org.rapla.facade.internal.FacadeImpl; +import org.rapla.framework.PluginDescriptor; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaContextException; +import org.rapla.framework.RaplaException; +import org.rapla.framework.internal.ContainerImpl; +import org.rapla.plugin.RaplaExtensionPoints; +import org.rapla.server.RaplaRemoteServiceFactory; +import org.rapla.server.RemoteService; +import org.rapla.server.RemoteSession; +import org.rapla.server.RemoteStorage; +import org.rapla.server.ServerService; +import org.rapla.servletpages.DefaultHTMLMenuEntry; +import org.rapla.servletpages.DefaultHTMLMenuExtensionPoint; +import org.rapla.servletpages.RaplaAppletPageGenerator; +import org.rapla.servletpages.RaplaIndexPageGenerator; +import org.rapla.servletpages.RaplaJNLPPageGenerator; +import org.rapla.servletpages.RaplaResourcePageGenerator; +import org.rapla.servletpages.RaplaStatusPageGenerator; +import org.rapla.storage.AuthenticationStore; +import org.rapla.storage.CachableStorageOperator; +import org.rapla.storage.RaplaSecurityException; +import org.rapla.storage.StorageOperator; +import org.rapla.storage.StorageUpdateListener; +import org.rapla.storage.UpdateEvent; +import org.rapla.storage.UpdateResult; +import org.rapla.storage.dbrm.RaplaStorePage; + +/** Default implementation of StorageService. + *

    Sample configuration 1: +

    + <storage id="storage" >
    + <store>file</store>
    + </storage>
    + 
    + * The store value contains the id of a storage-component. + * Storage-Components are all components that implement the + * CachableStorageOperator interface. + *

    + *

    Sample configuration 2: +

    + <storage id="storage">
    + <store>remote</store>
    + <login>
    + <username>homer</username>
    + <password>duffs</password>
    + </login>
    + </storage>
    + 
    + * This is configuration where the servers are cascaded. The remote-server + * normally needs an authentication. The provided account should have + * admin privileges. + *

    + + @see ServerService + */ + +public class ServerServiceImpl extends ContainerImpl implements StorageUpdateListener, ServerService +{ + protected CachableStorageOperator operator; + protected I18nBundle i18n; + protected AuthenticationStore authenticationStore; + private Configuration operatorConfig; + List pluginList; + + ClientFacade facade; + + private Map updateMap = new HashMap(); + private Map removeMap = new HashMap(); + + long repositoryVersion = 0; + long cleanupPointVersion = 0; + + public ServerServiceImpl( RaplaContext parentContext, Configuration config ) throws RaplaException + { + super( parentContext, config ); + pluginList = (List) parentContext.lookup( PluginDescriptor.PLUGIN_LIST ); + i18n = (I18nBundle) parentContext.lookup( I18nBundle.ROLE + "/org.rapla.RaplaResources" ); + Configuration login = config.getChild( "login" ); + String username = login.getChild( "username" ).getValue( null ); + String password = login.getChild( "password" ).getValue( null ); + operatorConfig = config.getChild( "store" ); + + try + { + operator = (CachableStorageOperator) getContext().lookup( + CachableStorageOperator.ROLE + + "/" + + operatorConfig.getValue( "*" ) ); + addContainerProvidedComponentInstance( CachableStorageOperator.ROLE, operator ); + addContainerProvidedComponentInstance( StorageOperator.ROLE, operator ); + facade = new FacadeImpl( getContext(), new DefaultConfiguration( "facade" ), getLogger() ); + addContainerProvidedComponentInstance( ClientFacade.ROLE, facade ); + addContainerProvidedComponent( SecurityManager.class.getName() ); + addContainerProvidedComponentInstance( RaplaRemoteServiceFactory.ROLE, RemoteStorage.ROLE, + new RaplaRemoteServiceFactory() + { + public RemoteService createRemoteService( RemoteSession session ) + throws RaplaException + { + return new RemoteStorageImpl( ServerServiceImpl.this, + session ); + } + + } ); + + // adds 5 basic pages to the webapplication + addContainerProvidedComponent( RaplaExtensionPoints.SERVLET_PAGE_EXTENSION, RaplaStatusPageGenerator.class.getName(), "server", null); + addContainerProvidedComponent( RaplaExtensionPoints.SERVLET_PAGE_EXTENSION, RaplaIndexPageGenerator.class.getName(), "index", null); + addContainerProvidedComponent( RaplaExtensionPoints.SERVLET_PAGE_EXTENSION, RaplaJNLPPageGenerator.class.getName(), "raplaclient", null); + addContainerProvidedComponent( RaplaExtensionPoints.SERVLET_PAGE_EXTENSION, RaplaAppletPageGenerator.class.getName(), "raplaapplet", null); + addContainerProvidedComponent( RaplaExtensionPoints.SERVLET_PAGE_EXTENSION, RaplaResourcePageGenerator.class.getName(), "resource", null); + addContainerProvidedComponent( RaplaExtensionPoints.SERVLET_PAGE_EXTENSION, RaplaStorePage.class.getName(), "store", null); + + // Index page menu + DefaultHTMLMenuExtensionPoint indexMenu = new DefaultHTMLMenuExtensionPoint(); + addContainerProvidedComponentInstance( RaplaExtensionPoints.HTML_MAIN_MENU_EXTENSION_POINT, indexMenu); + I18nBundle i18n = (I18nBundle)getContext().lookup(I18nBundle.ROLE + "/org.rapla.RaplaResources"); + + indexMenu.insert( new DefaultHTMLMenuEntry(i18n.getString( "start_rapla_with_webstart" ),"rapla?page=raplaclient") ); + indexMenu.insert( new DefaultHTMLMenuEntry(i18n.getString( "start_rapla_with_applet" ),"rapla?page=raplaapplet") ); + indexMenu.insert( new DefaultHTMLMenuEntry(i18n.getString( "server_status" ),"rapla?page=server") ); + } + catch ( RaplaContextException ex ) + { + throw new RaplaContextException( CachableStorageOperator.ROLE, "Store at " + + operatorConfig.getLocation() + + " is not found (or could not be initialized) ", ex ); + } + + operator.addStorageUpdateListener( this ); + if ( username != null && password != null ) + operator.connect( username, password.toCharArray() ); + else + operator.connect(); + + initializePlugins( pluginList, operator.getPreferences( null ) ); + + if ( getContext().has( AuthenticationStore.ROLE ) ) + { + try + { + authenticationStore = (AuthenticationStore) getContext().lookup( AuthenticationStore.ROLE ); + getLogger().info( " Using AuthenticationStore " + authenticationStore.getName() ); + } + catch ( RaplaException ex) + { + getLogger().error( " Can't initialize configured authentication store. Using default authentication." , ex); + } + } + initEventCleanup(); + } + + /** + * @see org.rapla.server.ServerService#getFacade() + */ + public ClientFacade getFacade() + { + return facade; + } + + protected void initializePlugins( List pluginList, Preferences preferences ) throws RaplaException + { + + RaplaConfiguration raplaConfig = (RaplaConfiguration) preferences.getEntry( "org.rapla.plugin" ); + // Add plugin configs + for ( Iterator it = pluginList.iterator(); it.hasNext(); ) + { + PluginDescriptor pluginDescriptor = (PluginDescriptor) it.next(); + String pluginClassname = pluginDescriptor.getClass().getName(); + Configuration pluginConfig = null; + if ( raplaConfig != null ) + { + pluginConfig = raplaConfig.find( "class", pluginClassname ); + } + if ( pluginConfig == null ) + { + pluginConfig = new DefaultConfiguration( "plugin" ); + } + pluginDescriptor.provideServices( this, pluginConfig ); + } + + Collection clientPlugins = getAllServicesFor( RaplaExtensionPoints.SERVER_EXTENSION ); + // start plugins + for ( Iterator it = clientPlugins.iterator(); it.hasNext(); ) + { + String hint = (String) it.next(); + try + { + getContext().lookup( RaplaExtensionPoints.SERVER_EXTENSION + "/" + hint ); + getLogger().info( "Initialize " + hint ); + } + catch ( RaplaContextException ex ) + { + getLogger().error( "Can't initialize " + hint, ex ); + } + } + } + + public void start() throws Exception + { + getLogger().info( "Storage service started" ); + } + + public void stop() throws Exception + { + operator.removeStorageUpdateListener( this ); + try + { + operator.disconnect(); + } + finally + { + } + getLogger().info( "Storage service stopped" ); + } + + public void dispose() + { + super.dispose(); + } + + public String login( Map args ) throws RaplaException + { + String username = RemoteStorage.LOGIN.value( args, 0 ); + String password = RemoteStorage.LOGIN.value( args, 1 ); + login( username, password ); + return username; + } + + public byte[] dispatch( RemoteSession session, String methodName, Map args ) throws Exception + { + if ( RemoteStorage.CHECK_SERVER_VERSION.is( methodName ) ) + { + String clientVersion = RemoteStorage.CHECK_SERVER_VERSION.value( args, 0 ); + checkServerVersion( clientVersion ); + } + else if ( RemoteStorage.LOGIN.is( methodName ) ) + { + login( args ); + } + else + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int indexRole = methodName.indexOf( "/" ); + String role = RemoteStorage.ROLE; + if ( indexRole > 0 ) + { + role = methodName.substring( 0, indexRole ); + methodName = methodName.substring( indexRole + 1 ); + + } + try + { + RemoteService service = (RemoteService) ( (RemoteSessionImpl) session ).lookupService( role ); + service.remoteMethodCall( methodName, args, out ); + } + catch ( RaplaException ex ) + { + getLogger().error( ex.getMessage(), ex ); + throw ex; + } + out.close(); + return out.toByteArray(); + } + return new byte[] {}; + + } + + public StorageOperator getOperator() + { + return operator; + } + + static UpdateEvent createTransactionSafeUpdateEvent( UpdateResult updateResult ) + { + User user = updateResult.getUser(); + UpdateEvent saveEvent = new UpdateEvent(); + if ( user != null ) + { + saveEvent.setUserId( ( (RefEntity) updateResult.getUser() ).getId() ); + } + Iterator it = updateResult.getOperations( UpdateResult.Add.class ); + while ( it.hasNext() ) + { + saveEvent.putStore( (RefEntity) ( (UpdateResult.Add) it.next() ).getNew() ); + } + it = updateResult.getOperations( UpdateResult.Change.class ); + while ( it.hasNext() ) + { + saveEvent.putStore( (RefEntity) ( (UpdateResult.Change) it.next() ).getNew() ); + } + it = updateResult.getOperations( UpdateResult.Remove.class ); + while ( it.hasNext() ) + { + saveEvent.putRemove( (RefEntity) ( (UpdateResult.Remove) it.next() ).getCurrent() ); + } + return saveEvent; + } + + // Implementation of StorageUpdateListener + synchronized public void objectsUpdated( UpdateResult evt ) + { + // notify the client for changes + repositoryVersion++; + UpdateEvent safeResultEvent = createTransactionSafeUpdateEvent( evt ); + if ( getLogger().isDebugEnabled() ) + getLogger().debug( "Storage was modified. Calling notify." ); + for ( Iterator it = safeResultEvent.getStoreObjects().iterator(); it.hasNext(); ) + { + RaplaObject obj = (RaplaObject) it.next(); + updateMap.remove( obj ); + updateMap.put( obj, new Long( repositoryVersion ) ); + } + for ( Iterator it = safeResultEvent.getRemoveObjects().iterator(); it.hasNext(); ) + { + RaplaObject obj = (RaplaObject) it.next(); + updateMap.remove( obj ); + removeMap.remove( obj ); + removeMap.put( obj, new Long( repositoryVersion ) ); + } + } + + /** regulary removes all old update messages that are older the updateInterval ( factor 10) and at least 1 hour old */ + private final void initEventCleanup() + { + TimerTask cleanupTask = new TimerTask() + { + public void run() + { + initEventCleanup(); + } + }; + synchronized ( operator.getLock() ) + { + Timer timer = new Timer( true ); // Start timer as daemon-thread + int delay = 10000; + + { + RefEntity[] keys = (RefEntity[])updateMap.keySet().toArray(new RefEntity[] {}); + for ( int i=0;i clientRepositoryVersion ) + { + safeResultEvent.putStore( key ); + } + } + for ( Iterator it = removeMap.keySet().iterator(); it.hasNext(); ) + { + RefEntity key = (RefEntity) it.next(); + Long lastVersion = (Long) removeMap.get( key ); + if ( lastVersion.longValue() > clientRepositoryVersion ) + { + safeResultEvent.putRemove( key ); + } + } + String xml = RemoteStorageImpl.createUpdateEvent( getContext(), operator.getCache(), safeResultEvent ); + return xml; + } + // Empty String if nothing is expected + return ""; + } + + public void updateError( RaplaException ex ) + { + if ( getLogger() != null ) + getLogger().error( ex.getMessage(), ex ); + try + { + stop(); + // messagingServer.disconnect(); + } + catch ( Exception e ) + { + if ( getLogger() != null ) + getLogger().error( e.getMessage() ); + } + } + + public void storageDisconnected() + { + try + { + stop(); + } + catch ( Exception e ) + { + if ( getLogger() != null ) + getLogger().error( e.getMessage() ); + } + } + + public void checkServerVersion( String clientVersion ) throws RaplaException + { + String serverVersion = i18n.getString( "rapla.version" ); + if ( !serverVersion.equals( clientVersion ) ) + { + throw new RaplaException( "Incompatible client/server versions. Please change your client to version " + + serverVersion + + ". If you are using java-webstart a simple reload and restart could do that!" ); + } + } + + public void login( String username, String password ) throws RaplaException + { + this.getLogger().debug( "User '" + username + "' is requesting login " ); + authenticate( username, password ); + if ( authenticationStore != null) + { + User user = this.operator.getUser( username ); + if ( user == null ) + { + user = new UserImpl(); + ( (RefEntity) user ).setId( this.operator.createIdentifier( User.TYPE ) ); + } + else + { + user = (User) this.operator.editObject( user, null ); + } + + boolean initUser ; + try + { + initUser = authenticationStore.initUser( user, username, password, + this.operator.getSuperCategory() + .getCategory( Permission.GROUP_CATEGORY_KEY ) ); + } catch (RaplaSecurityException ex){ + throw new RaplaSecurityException(i18n.getString("error.login")); + } + if ( initUser ) + { + this.operator.storeAndRemove( new Entity[] + { user }, Entity.ENTITY_ARRAY, null ); + } + } + } + + + public void authenticate( String username, String password ) throws RaplaException + { + synchronized ( this.operator.getLock() ) + { + if ( authenticationStore != null && authenticationStore.authenticate( username, password ) ) + { + // do nothing + } + else + { + this.operator.authenticate( username, password ); + } + } + } + +} diff --git a/Rapla/src/org/rapla/server/internal/SessionExpiredException.java b/Rapla/src/org/rapla/server/internal/SessionExpiredException.java new file mode 100644 index 0000000..4873350 --- /dev/null +++ b/Rapla/src/org/rapla/server/internal/SessionExpiredException.java @@ -0,0 +1,15 @@ +package org.rapla.server.internal; + +import org.rapla.framework.RaplaException; + + +public class SessionExpiredException extends RaplaException +{ + private static final long serialVersionUID = 1L; + + public SessionExpiredException( String text ) + { + super( text ); + } + +} diff --git a/Rapla/src/org/rapla/server/internal/ShutdownRemoteServiceFactory.java b/Rapla/src/org/rapla/server/internal/ShutdownRemoteServiceFactory.java new file mode 100644 index 0000000..2fedf8f --- /dev/null +++ b/Rapla/src/org/rapla/server/internal/ShutdownRemoteServiceFactory.java @@ -0,0 +1,36 @@ +package org.rapla.server.internal; + +import java.io.IOException; +import java.io.OutputStream; +import java.text.ParseException; +import java.util.Map; + +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaContextException; +import org.rapla.framework.RaplaException; +import org.rapla.server.RaplaRemoteServiceFactory; +import org.rapla.server.RemoteService; +import org.rapla.server.RemoteSession; +import org.rapla.server.ShutdownService; + +public class ShutdownRemoteServiceFactory implements RaplaRemoteServiceFactory +{ + ShutdownService shutdownService; + public ShutdownRemoteServiceFactory(RaplaContext context) throws RaplaContextException + { + shutdownService = (ShutdownService) context.lookup(ShutdownService.ROLE); + } + + public RemoteService createRemoteService( RemoteSession session ) throws RaplaException + { + return new RemoteService() { + public void remoteMethodCall( String methodName, Map args, OutputStream out ) throws RaplaException, IOException, ParseException + { + String shutdownPassword = (String) args.get(ShutdownService.SHUTDOWN.arg(0)); + boolean restart = Boolean.valueOf( (String) args.get(ShutdownService.SHUTDOWN.arg(1))).booleanValue(); + shutdownService.shutdown( shutdownPassword, restart); + } + }; + } + +} diff --git a/Rapla/src/org/rapla/server/internal/ShutdownServiceImpl.java b/Rapla/src/org/rapla/server/internal/ShutdownServiceImpl.java new file mode 100644 index 0000000..5c96d19 --- /dev/null +++ b/Rapla/src/org/rapla/server/internal/ShutdownServiceImpl.java @@ -0,0 +1,120 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.server.internal; + +import java.util.Vector; + +import org.apache.avalon.framework.activity.Disposable; +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.logger.Logger; +import org.rapla.framework.Container; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.server.RaplaRemoteServiceFactory; +import org.rapla.server.ShutdownListener; +import org.rapla.server.ShutdownService; + +/** Default implementation of the shutdown service. On a shutdown call + to shutdown all registered ShutdownListeners will first be notified with + shutdownInitated() and then wit shutdownComplete(). A shutdown-request will only be accepted from + process running on the localhost.

    Sample configuration: +

    + <shutdown-service id="shutdown">
    + <password>xxx</password>
    + </shutdownservice>
    + 
    + @see ShutdownService + */ +public class ShutdownServiceImpl implements ShutdownService, Disposable +{ + Vector listenerList = new Vector(); + protected Logger logger; + RaplaContext context; + + public ShutdownServiceImpl( RaplaContext context, Configuration config ) throws RaplaException + { + this.context = context; + this.logger = (Logger) context.lookup( Logger.class.getName() ); + Container container = (Container) context.lookup( Container.ROLE ); + container.addContainerProvidedComponent( RaplaRemoteServiceFactory.ROLE, ShutdownRemoteServiceFactory.ROLE ); + getLogger().info( "Shutdown service started" ); + } + + public void addShutdownListener( ShutdownListener listener ) + { + listenerList.add( listener ); + } + + public void removeShutdownListener( ShutdownListener listener ) + { + listenerList.remove( listener ); + } + + public ShutdownListener[] getShutdownListeners() + { + return (ShutdownListener[]) listenerList.toArray( new ShutdownListener[] {} ); + } + + protected void fireShutdownInitiated() + { + ShutdownListener[] listeners = getShutdownListeners(); + for ( int i = 0; i < listeners.length; i++ ) + { + try + { + listeners[i].shutdownInitiated(); + } + catch ( Throwable ex ) + { + getLogger().error( "Error calling shutdownInitiated " + listeners[i], ex ); + } + } + } + + protected void fireShutdownComplete( boolean restart ) + { + ShutdownListener[] listeners = getShutdownListeners(); + for ( int i = 0; i < listeners.length; i++ ) + try + { + listeners[i].shutdownComplete( restart ); + } + catch ( Throwable ex ) + { + getLogger().error( "Error calling shutdownComplete " + listeners[i], ex ); + } + } + + public void dispose() + { + + } + + protected Logger getLogger() + { + return logger; + } + + private void checkShutdownPermissions( String shutdownPassword ) throws RaplaException + { + // in process call, that means everything is ok + } + + public synchronized void shutdown( String shutdownPassword, boolean restart ) throws RaplaException + { + checkShutdownPermissions( shutdownPassword ); + fireShutdownInitiated(); + fireShutdownComplete( restart ); + } + +} diff --git a/Rapla/src/org/rapla/server/package.html b/Rapla/src/org/rapla/server/package.html new file mode 100644 index 0000000..9c79ba1 --- /dev/null +++ b/Rapla/src/org/rapla/server/package.html @@ -0,0 +1,15 @@ + +

    +The server synchronizes and bundles the client requests and +maintains a single storage for all its clients. It also +provides the basic services for the server side plugins. +For instance the notification plugin notifies sends email on a reservation change. +

    +

    +The server is also responsible for enforcing +the access policies. +

    + + + + diff --git a/Rapla/src/org/rapla/servletpages/DefaultHTMLMenuEntry.java b/Rapla/src/org/rapla/servletpages/DefaultHTMLMenuEntry.java new file mode 100644 index 0000000..78e46c9 --- /dev/null +++ b/Rapla/src/org/rapla/servletpages/DefaultHTMLMenuEntry.java @@ -0,0 +1,30 @@ +package org.rapla.servletpages; + +import java.io.IOException; +import java.io.PrintWriter; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class DefaultHTMLMenuEntry implements RaplaPageGenerator +{ + String name; + String linkName; + + public DefaultHTMLMenuEntry(String name, String linkName) + { + this.name = name; + this.linkName = linkName; + } + + public void generatePage( ServletContext context, HttpServletRequest request, HttpServletResponse response ) throws IOException, ServletException + { + PrintWriter out = response.getWriter(); + // writing the html code line for a button + // including the link to the appropriate servletpage + out.println("" + name + ""); + } + +} diff --git a/Rapla/src/org/rapla/servletpages/DefaultHTMLMenuExtensionPoint.java b/Rapla/src/org/rapla/servletpages/DefaultHTMLMenuExtensionPoint.java new file mode 100644 index 0000000..6d51d7f --- /dev/null +++ b/Rapla/src/org/rapla/servletpages/DefaultHTMLMenuExtensionPoint.java @@ -0,0 +1,44 @@ +package org.rapla.servletpages; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class DefaultHTMLMenuExtensionPoint implements HTMLMenuExtensionPoint, RaplaPageGenerator +{ + List entries = new ArrayList(); + public void insert( RaplaPageGenerator gen ) + { + entries.add( gen); + } + + public void generatePage( ServletContext context, HttpServletRequest request, HttpServletResponse response ) throws IOException, ServletException + { + if ( entries.size() == 0) + { + return; + } + PrintWriter out = response.getWriter(); +// out.println("
      "); + + // there is an ArraList of entries that wants to be part of the HTML + // menu we go through this ArraList, + + for (Iterator it = entries.iterator();it.hasNext();) + { + RaplaPageGenerator entry = (RaplaPageGenerator)it.next(); + out.println("
      "); + entry.generatePage( context, request, response ); + out.println("
      "); + } +// out.println("
    "); + } + +} diff --git a/Rapla/src/org/rapla/servletpages/HTMLMenuExtensionPoint.java b/Rapla/src/org/rapla/servletpages/HTMLMenuExtensionPoint.java new file mode 100644 index 0000000..d043660 --- /dev/null +++ b/Rapla/src/org/rapla/servletpages/HTMLMenuExtensionPoint.java @@ -0,0 +1,6 @@ +package org.rapla.servletpages; + +public interface HTMLMenuExtensionPoint +{ + public void insert(RaplaPageGenerator gen); +} diff --git a/Rapla/src/org/rapla/servletpages/RaplaAppletPageGenerator.java b/Rapla/src/org/rapla/servletpages/RaplaAppletPageGenerator.java new file mode 100644 index 0000000..ebbfba8 --- /dev/null +++ b/Rapla/src/org/rapla/servletpages/RaplaAppletPageGenerator.java @@ -0,0 +1,55 @@ +/** + * + */ +package org.rapla.servletpages; + +import java.io.IOException; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.rapla.components.util.IOUtil; + +public class RaplaAppletPageGenerator implements RaplaPageGenerator{ + + + private String getLibsApplet(ServletContext context) throws java.io.IOException { + StringBuffer buf = new StringBuffer(); + String base = context.getRealPath("/"); + java.io.File baseFile = new java.io.File(base); + java.io.File[] files = IOUtil.getJarFiles(base,"webclient"); + for (int i=0;i"); + out.println(""); + out.println(" Rapla Applet"); + out.println(" "); + out.println(""); + out.println(""); + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" No Java support for APPLET tags please install java plugin for your browser!!"); + out.println(" "); + out.println(""); + out.println(""); + } + +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/servletpages/RaplaIndexPageGenerator.java b/Rapla/src/org/rapla/servletpages/RaplaIndexPageGenerator.java new file mode 100644 index 0000000..3885d4f --- /dev/null +++ b/Rapla/src/org/rapla/servletpages/RaplaIndexPageGenerator.java @@ -0,0 +1,62 @@ +/** + * + */ +package org.rapla.servletpages; + +import java.io.IOException; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.rapla.facade.RaplaComponent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.internal.RaplaStartOption; +import org.rapla.plugin.RaplaExtensionPoints; + +public class RaplaIndexPageGenerator extends RaplaComponent implements RaplaPageGenerator +{ + RaplaPageGenerator menu; + + public RaplaIndexPageGenerator( RaplaContext context ) throws RaplaException + { + super( context); + menu = (RaplaPageGenerator) context.lookup( RaplaExtensionPoints.HTML_MAIN_MENU_EXTENSION_POINT ); + } + + public void generatePage( ServletContext context, HttpServletRequest request, HttpServletResponse response ) + throws IOException, ServletException + { + response.setContentType("text/html; charset=ISO-8859-1"); + java.io.PrintWriter out = response.getWriter(); + out.println(""); + out.println(" "); + // add the link to the stylesheet for this page within the tag + out.println(" "); + // tell the html page where its favourite icon is stored + out.println(" "); + out.println(" "); + String title; + final String defaultTitle = getI18n().getString("rapla.title"); + try { + title= getQuery().getPreferences( null ).getEntryAsString(RaplaStartOption.TITLE, defaultTitle); + } catch (RaplaException e) { + title = defaultTitle; + } + + out.println(title); + out.println(" "); + out.println(" "); + out.println(" "); + out.println("

    "); + out.println(title); + out.println("

    "); + menu.generatePage(context, request, response); + out.println(getI18n().getString("webinfo.text")); + out.println(" "); + out.println(""); + } + +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/servletpages/RaplaJNLPPageGenerator.java b/Rapla/src/org/rapla/servletpages/RaplaJNLPPageGenerator.java new file mode 100644 index 0000000..30e7996 --- /dev/null +++ b/Rapla/src/org/rapla/servletpages/RaplaJNLPPageGenerator.java @@ -0,0 +1,120 @@ +/** + * + */ +package org.rapla.servletpages; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.rapla.components.util.IOUtil; +import org.rapla.facade.RaplaComponent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.gui.internal.RaplaStartOption; + +public class RaplaJNLPPageGenerator extends RaplaComponent implements RaplaPageGenerator{ + + public RaplaJNLPPageGenerator( RaplaContext context ) throws RaplaException + { + super( context); + } + + private String getCodebase( HttpServletRequest request) { + StringBuffer codebaseBuffer = new StringBuffer(); + codebaseBuffer.append(!request.isSecure() ? "http://" : "https://"); + codebaseBuffer.append(request.getServerName()); + if (request.getServerPort() != (!request.isSecure() ? 80 : 443)) + { + codebaseBuffer.append(':'); + codebaseBuffer.append(request.getServerPort()); + } + codebaseBuffer.append(request.getContextPath()); + codebaseBuffer.append('/'); + return codebaseBuffer.toString(); + } + + private String getLibsJNLP(ServletContext context) throws java.io.IOException { + StringBuffer buf = new StringBuffer(); + String base = context.getRealPath("."); + java.io.File baseFile = new java.io.File(base); + java.io.File[] files = IOUtil.getJarFiles(base,"webclient"); + for (int i=0;i"); + } + return buf.toString(); + } + + protected List getProgramArguments() { + List list = new ArrayList(); +/* list.add("-c"); + list.add("rapla?page=jnlp_http_auth.xconf");*/ + list.add("webstart"); + return list; + } + + public void generatePage( ServletContext context, HttpServletRequest request, HttpServletResponse response ) throws IOException { + java.io.PrintWriter out = response.getWriter(); + response.setContentType("application/x-java-jnlp-file;charset=utf-8"); + out.println(""); + out.println(""); + out.println(""); + out.println(" Rapla"); + out.println(" rapla.sourceforge.net (development started at Uni Bonn)"); + out.println(" "); + out.println(" Resource Scheduling Application"); + // we changed the logo from .gif to .png to make it more sexy + out.println(" "); + // and here aswell + final String defaultTitle = getI18n().getString("rapla.title"); + String menuName; + try + { + menuName= getQuery().getPreferences( null ).getEntryAsString(RaplaStartOption.TITLE, defaultTitle); + } + catch (RaplaException e) { + menuName = defaultTitle; + } + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" "); + out.println(" "); + out.println(""); + boolean allpermissionsAllowed = isSigned(); + final String parameter = request.getParameter("sandbox"); + if (allpermissionsAllowed && (parameter== null || parameter.trim().toLowerCase().equals("false"))) + { + out.println(""); + out.println(" "); + out.println(""); + } + out.println(""); + out.println(" "); + out.println(getLibsJNLP(context)); + out.println(""); + out.println(""); + for (Iterator it = getProgramArguments().iterator(); it.hasNext();) + { + out.println(" " + it.next() + " "); + } + out.println(""); + + + out.println(""); + } + + +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/servletpages/RaplaPageGenerator.java b/Rapla/src/org/rapla/servletpages/RaplaPageGenerator.java new file mode 100644 index 0000000..a59f8ce --- /dev/null +++ b/Rapla/src/org/rapla/servletpages/RaplaPageGenerator.java @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.servletpages; +import java.io.IOException; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + + +/** + * You can add arbitrary serlvet pages to your rapla webapp. + * + */ +public interface RaplaPageGenerator +{ + void generatePage( ServletContext context, HttpServletRequest request, HttpServletResponse response ) throws IOException, ServletException; +} diff --git a/Rapla/src/org/rapla/servletpages/RaplaResourcePageGenerator.java b/Rapla/src/org/rapla/servletpages/RaplaResourcePageGenerator.java new file mode 100644 index 0000000..0ade7d3 --- /dev/null +++ b/Rapla/src/org/rapla/servletpages/RaplaResourcePageGenerator.java @@ -0,0 +1,55 @@ +/** + * + */ +package org.rapla.servletpages; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.rapla.components.util.IOUtil; + +public class RaplaResourcePageGenerator implements RaplaPageGenerator{ + Map resourceMap = new HashMap(); + + public void generatePage( ServletContext context, HttpServletRequest request, HttpServletResponse response ) throws IOException { + String resourcename = request.getParameter("name"); + if ( resourcename == null) + { + response.getWriter().println("No name parameter specified"); + return; + } + Resource res = (Resource) resourceMap.get( resourcename ); + if ( res == null) + { + response.getWriter().println("Can't find resource with the name '" + resourcename + "'"); + return; + } + response.setContentType( res.mimetyp ); + InputStream in = res.resourceURL.openStream(); + IOUtil.copyStreams( in, response.getOutputStream()); + } + + public void registerResource( String resourcename, String mimetype, URL resourceUrl) { + resourceMap.put( resourcename, new Resource( resourcename, mimetype, resourceUrl)); + } + + static class Resource { + String resourcename; + String mimetyp; + URL resourceURL; + + Resource(String resourcename, String mimetyp, URL resourceURL ) + { + this.resourcename = resourcename; + this.mimetyp = mimetyp; + this.resourceURL = resourceURL; + } + } +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/servletpages/RaplaStatusPageGenerator.java b/Rapla/src/org/rapla/servletpages/RaplaStatusPageGenerator.java new file mode 100644 index 0000000..2969686 --- /dev/null +++ b/Rapla/src/org/rapla/servletpages/RaplaStatusPageGenerator.java @@ -0,0 +1,39 @@ +/** + * + */ +package org.rapla.servletpages; + +import java.io.IOException; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaContextException; + +public class RaplaStatusPageGenerator implements RaplaPageGenerator{ + I18nBundle m_i18n; + public RaplaStatusPageGenerator(RaplaContext context) throws RaplaContextException { + m_i18n = (I18nBundle) context.lookup(I18nBundle.ROLE + "/org.rapla.RaplaResources"); + } + + public void generatePage( ServletContext context, HttpServletRequest request, HttpServletResponse response ) throws IOException { + response.setContentType("text/html; charset=ISO-8859-1"); + java.io.PrintWriter out = response.getWriter(); + out.println( "" ); + out.println( "" ); + out.println(" "); + out.println(" Rapla Server status!"); + out.println("" ); + + out.println( "" ); + String javaversion = System.getProperty("java.version"); + out.println( "

    Server running

    " + m_i18n.format("info.text", javaversion)); + out.println( "
    " ); + out.println( "" ); + out.println( "" ); + } + +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/servletpages/package.html b/Rapla/src/org/rapla/servletpages/package.html new file mode 100644 index 0000000..549a292 --- /dev/null +++ b/Rapla/src/org/rapla/servletpages/package.html @@ -0,0 +1,5 @@ + +

    Contains the default pages served by the rapla server servlet

    + + + diff --git a/Rapla/src/org/rapla/storage/AuthenticationStore.java b/Rapla/src/org/rapla/storage/AuthenticationStore.java new file mode 100644 index 0000000..9b0ee5c --- /dev/null +++ b/Rapla/src/org/rapla/storage/AuthenticationStore.java @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.storage; +import org.rapla.entities.Category; +import org.rapla.entities.User; +import org.rapla.framework.RaplaException; + +public interface AuthenticationStore { + String ROLE = AuthenticationStore.class.getName(); + /** returns, if the user can be authenticated. */ + boolean authenticate(String username, String password); + /** returns the name of the store */ + String getName(); + /** Initializes a user entity with the values provided by the authentication store. + * @return true if the new user-object attributes (such as email, name, or groups) differ from the values stored before the method was executed, false otherwise. */ + boolean initUser( User user, String username, String password, Category groupRootCategory) throws RaplaException; +} + + + + diff --git a/Rapla/src/org/rapla/storage/CachableStorageOperator.java b/Rapla/src/org/rapla/storage/CachableStorageOperator.java new file mode 100644 index 0000000..3454f3e --- /dev/null +++ b/Rapla/src/org/rapla/storage/CachableStorageOperator.java @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +/** A StorageOperator that operates on a LocalCache-Object. + */ +package org.rapla.storage; + +import java.util.Collection; + +import org.rapla.entities.EntityNotFoundException; +import org.rapla.entities.storage.EntityResolver; +import org.rapla.entities.storage.RefEntity; +import org.rapla.framework.RaplaException; + +public interface CachableStorageOperator extends StorageOperator { + String ROLE = CachableStorageOperator.class.getName(); + + LocalCache getCache(); + void dispatch(UpdateEvent evt) throws RaplaException; + void authenticate(String username,String password) throws RaplaException; + void setCache(LocalCache cache) throws RaplaException; + void saveData() throws RaplaException; + RefEntity resolveId(Object id) throws EntityNotFoundException; + EntityResolver createEntityResolver(Collection entities,LocalCache parent); +} + + + + + + + + + + + diff --git a/Rapla/src/org/rapla/storage/IOContext.java b/Rapla/src/org/rapla/storage/IOContext.java new file mode 100644 index 0000000..ca36ba3 --- /dev/null +++ b/Rapla/src/org/rapla/storage/IOContext.java @@ -0,0 +1,123 @@ +package org.rapla.storage; + +import java.util.HashMap; +import java.util.Map; + +import org.rapla.entities.Category; +import org.rapla.entities.User; +import org.rapla.entities.configuration.CalendarModelConfiguration; +import org.rapla.entities.configuration.Preferences; +import org.rapla.entities.configuration.RaplaConfiguration; +import org.rapla.entities.configuration.RaplaMap; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Period; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaDefaultContext; +import org.rapla.framework.RaplaException; +import org.rapla.storage.impl.EntityStore; +import org.rapla.storage.xml.AllocatableReader; +import org.rapla.storage.xml.AllocatableWriter; +import org.rapla.storage.xml.CategoryReader; +import org.rapla.storage.xml.CategoryWriter; +import org.rapla.storage.xml.DynamicTypeReader; +import org.rapla.storage.xml.DynamicTypeWriter; +import org.rapla.storage.xml.PeriodReader; +import org.rapla.storage.xml.PeriodWriter; +import org.rapla.storage.xml.PreferenceReader; +import org.rapla.storage.xml.PreferenceWriter; +import org.rapla.storage.xml.RaplaCalendarSettingsReader; +import org.rapla.storage.xml.RaplaCalendarSettingsWriter; +import org.rapla.storage.xml.RaplaConfigurationReader; +import org.rapla.storage.xml.RaplaConfigurationWriter; +import org.rapla.storage.xml.RaplaMapReader; +import org.rapla.storage.xml.RaplaMapWriter; +import org.rapla.storage.xml.RemoveReader; +import org.rapla.storage.xml.ReservationReader; +import org.rapla.storage.xml.ReservationWriter; +import org.rapla.storage.xml.UserReader; +import org.rapla.storage.xml.UserWriter; + +public class IOContext +{ + protected Map getLocalnameMap() { + //WARNING We can't use RaplaType.getRegisteredTypes() because the class could not be registered on load time + Map localnameMap = new HashMap(); + localnameMap.put( Reservation.TYPE.getLocalName(), Reservation.TYPE); + localnameMap.put( Appointment.TYPE.getLocalName(), Appointment.TYPE); + localnameMap.put( Allocatable.TYPE.getLocalName(), Allocatable.TYPE); + localnameMap.put( User.TYPE.getLocalName(), User.TYPE); + localnameMap.put( Preferences.TYPE.getLocalName(), Preferences.TYPE); + localnameMap.put( Period.TYPE.getLocalName(), Period.TYPE); + localnameMap.put( Category.TYPE.getLocalName(), Category.TYPE); + localnameMap.put( DynamicType.TYPE.getLocalName(), DynamicType.TYPE); + localnameMap.put( Attribute.TYPE.getLocalName(), Attribute.TYPE); + localnameMap.put( RaplaConfiguration.TYPE.getLocalName(), RaplaConfiguration.TYPE); + localnameMap.put( RaplaMap.TYPE.getLocalName(), RaplaMap.TYPE); + localnameMap.put( CalendarModelConfiguration.TYPE.getLocalName(), CalendarModelConfiguration.TYPE); + return localnameMap; + } + + protected void addReaders(Map readerMap,RaplaContext context) throws RaplaException { + readerMap.put( "remove",new RemoveReader( context)); + readerMap.put( Category.TYPE,new CategoryReader( context)); + readerMap.put( Preferences.TYPE, new PreferenceReader(context) ); + readerMap.put( DynamicType.TYPE, new DynamicTypeReader(context) ); + readerMap.put( User.TYPE, new UserReader(context)); + readerMap.put( Allocatable.TYPE, new AllocatableReader(context) ); + readerMap.put( Period.TYPE, new PeriodReader(context) ); + readerMap.put( Reservation.TYPE,new ReservationReader(context)); + readerMap.put( RaplaConfiguration.TYPE, new RaplaConfigurationReader(context)); + readerMap.put( RaplaMap.TYPE, new RaplaMapReader(context)); + readerMap.put( CalendarModelConfiguration.TYPE, new RaplaCalendarSettingsReader(context) ); + } + + protected void addWriters(Map writerMap,RaplaContext context) throws RaplaException { + writerMap.put( Category.TYPE,new CategoryWriter(context)); + writerMap.put( Preferences.TYPE,new PreferenceWriter(context) ); + writerMap.put( DynamicType.TYPE,new DynamicTypeWriter(context)); + writerMap.put( User.TYPE, new UserWriter(context) ); + writerMap.put( Allocatable.TYPE, new AllocatableWriter(context) ); + writerMap.put( Period.TYPE, new PeriodWriter(context) ); + writerMap.put( Reservation.TYPE,new ReservationWriter(context)); + writerMap.put( RaplaConfiguration.TYPE,new RaplaConfigurationWriter(context) ); + writerMap.put( RaplaMap.TYPE, new RaplaMapWriter(context) ); + writerMap.put( Preferences.TYPE, new PreferenceWriter(context) ); + writerMap.put( CalendarModelConfiguration.TYPE, new RaplaCalendarSettingsWriter(context) ); + } + + public RaplaDefaultContext createInputContext(RaplaContext parentContext, EntityStore store, IdTable idTable) throws RaplaException { + + RaplaDefaultContext ioContext = new RaplaDefaultContext( parentContext); + ioContext.put(EntityStore.class.getName(), store); + ioContext.put(IdTable.class.getName(),idTable); + ioContext.put(PreferenceReader.LOCALNAMEMAPENTRY, getLocalnameMap()); + Map readerMap = new HashMap(); + ioContext.put(PreferenceReader.READERMAP, readerMap); + addReaders( readerMap, ioContext); + return ioContext; + } + + public RaplaDefaultContext createOutputContext(RaplaContext parentContext, LocalCache cache, boolean includeIds, boolean includeVersions) throws RaplaException { + + RaplaDefaultContext ioContext = new RaplaDefaultContext( parentContext); + if ( includeIds) + { + ioContext.put("idonly", Boolean.TRUE); + } + if ( includeVersions) + { + ioContext.put("printversion", Boolean.TRUE); + } + ioContext.put(LocalCache.class.getName(), cache); + ioContext.put(PreferenceReader.LOCALNAMEMAPENTRY, getLocalnameMap()); + Map writerMap = new HashMap(); + ioContext.put(PreferenceWriter.WRITERMAP, writerMap); + addWriters( writerMap, ioContext ); + return ioContext; + } + +} diff --git a/Rapla/src/org/rapla/storage/IdTable.java b/Rapla/src/org/rapla/storage/IdTable.java new file mode 100644 index 0000000..f4657a5 --- /dev/null +++ b/Rapla/src/org/rapla/storage/IdTable.java @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org . | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.storage; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.rapla.entities.RaplaType; +import org.rapla.entities.storage.RefEntity; +import org.rapla.entities.storage.internal.SimpleIdentifier; +import org.rapla.framework.RaplaException; + +/** Maintains the highest ids for every RaplaType in the LocalCache.*/ +public class IdTable { + Map idTable = new HashMap(); + LocalCache cache; + /** increment and return the highest id for the selected RaplaType */ + public Object createId(RaplaType raplaType) throws RaplaException { + SimpleIdentifier oldId = (SimpleIdentifier) idTable.get(raplaType); + if ( oldId == null) { + oldId = calc(cache, raplaType); + idTable.put(raplaType, oldId); + } + if (oldId == null) + throw new RaplaException("Error in Program: RaplaType '" + raplaType + + "' not found in idtable. Have you called recalc?"); + Object newId = new SimpleIdentifier(raplaType,oldId.getKey() + 1); + idTable.put(raplaType,newId); + return newId; + } + + /** Finds the highest id in an entity-collection */ + protected SimpleIdentifier calc(LocalCache cache,RaplaType raplaType) throws RaplaException { + int max = 0; + Iterator it = cache.getIterator(raplaType); + while (it.hasNext()) { + SimpleIdentifier id =(SimpleIdentifier) ((RefEntity)it.next()).getId(); + if (id != null && id.getKey() > max) + max = id.getKey(); + } + return new SimpleIdentifier(raplaType,max); + } + + public void setCache(LocalCache cache) throws RaplaException { + this.cache = cache; + idTable.clear(); + } +} diff --git a/Rapla/src/org/rapla/storage/ImportExportManager.java b/Rapla/src/org/rapla/storage/ImportExportManager.java new file mode 100644 index 0000000..6fb727f --- /dev/null +++ b/Rapla/src/org/rapla/storage/ImportExportManager.java @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.storage; +import org.rapla.framework.RaplaException; +/** Imports the content of on store into another. + Export does an import with source and destination exchanged. + */ +public interface ImportExportManager { + String ROLE = ImportExportManager.class.getName(); + void doImport() throws RaplaException; + void doExport() throws RaplaException; +} diff --git a/Rapla/src/org/rapla/storage/LocalCache.java b/Rapla/src/org/rapla/storage/LocalCache.java new file mode 100644 index 0000000..80ba569 --- /dev/null +++ b/Rapla/src/org/rapla/storage/LocalCache.java @@ -0,0 +1,424 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.storage; + +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.rapla.components.util.Assert; +import org.rapla.components.util.iterator.NestedIterator; +import org.rapla.entities.Category; +import org.rapla.entities.EntityNotFoundException; +import org.rapla.entities.RaplaType; +import org.rapla.entities.User; +import org.rapla.entities.configuration.Preferences; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.AppointmentStartComparator; +import org.rapla.entities.domain.Period; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.internal.CategoryImpl; +import org.rapla.entities.storage.EntityResolver; +import org.rapla.entities.storage.RefEntity; +import org.rapla.entities.storage.internal.SimpleIdentifier; +import org.rapla.framework.RaplaException; + +public class LocalCache implements EntityResolver +{ + Map passwords = new HashMap(); + + Map entities; + Set dynamicTypes; + Set users; + Set resources; + Set reservations; + Set periods; + + Set categories; + Set appointments; + Set attributes; + + Set preferences; + + Map entityMap; + Map enities; + + Locale locale; + + // Index for start and end dates + TreeSet appointmentsStart; + + + class IdComparator implements Comparator { + public int compare(Object o1,Object o2) { + SimpleIdentifier id1 = (SimpleIdentifier)((RefEntity)o1).getId(); + SimpleIdentifier id2 = (SimpleIdentifier)((RefEntity)o2).getId(); + if ( id1.getKey() == id2.getKey()) + return 0; + return (id1.getKey() < id2.getKey()) ? -1 : 1; + } + } + + private CategoryImpl superCategory = new CategoryImpl(); + + public LocalCache(Locale locale) { + this.locale = locale; + superCategory.setId(LocalCache.SUPER_CATEGORY_ID); + superCategory.setKey("supercategory"); + + Comparator comp = new IdComparator(); + + entityMap = new HashMap(); + entities = new HashMap(); + // top-level-entities + reservations = new TreeSet(comp); + periods = new TreeSet(comp); + users = new TreeSet(comp); + resources = new TreeSet(comp); + dynamicTypes = new TreeSet(comp); + + // non-top-level-entities with exception of one super-category + categories = new HashSet(); + appointments = new HashSet(); + preferences = new HashSet(); + attributes = new HashSet(); + + entityMap.put(DynamicType.TYPE,dynamicTypes); + entityMap.put(Attribute.TYPE, attributes); + entityMap.put(Category.TYPE, categories); + entityMap.put(Allocatable.TYPE,resources); + entityMap.put(User.TYPE,users); + entityMap.put(Period.TYPE,periods); + entityMap.put(Reservation.TYPE,reservations); + entityMap.put(Appointment.TYPE,appointments); + entityMap.put(Preferences.TYPE, preferences); + + + appointmentsStart = new TreeSet(new AppointmentStartComparator()); + initSuperCategory(); + } + + /** @return true if the entity has been removed and false if the entity was not found*/ + public boolean remove(RefEntity entity) { + RaplaType raplaType = entity.getRaplaType(); + Set entitySet = (Set)entityMap.get(raplaType); + boolean bResult = true; + if (entitySet != null) { + if (entities.get(entity.getId()) != null) + bResult = false; + if (entity.getId() == null) + return false; + + + if ( Appointment.TYPE.equals( raplaType )) { + removeAppointment(entity); + } + + entities.remove(entity.getId()); + entitySet.remove( entity ); + } else { + throw new RuntimeException("UNKNOWN TYPE. Can't remove object:" + entity.getRaplaType()); + } + return bResult; + } + + public void put(RefEntity entity) { + Assert.notNull(entity); + RaplaType raplaType = entity.getRaplaType(); + Object id = entity.getId(); + if (id == null) + throw new IllegalStateException("ID can't be null"); + + Set entitySet = (Set) entityMap.get(raplaType); + if (entitySet != null) { + + if (Appointment.TYPE.equals( raplaType )) { + removeAppointment(entity); + appointmentsStart.add(entity); + } + entities.put(id,entity); + entitySet.remove( entity ); + entitySet.add( entity ); + } else { + throw new RuntimeException("UNKNOWN TYPE. Can't store object in cache: " + entity.getRaplaType()); + } + } + + + public RefEntity get(Object id) { + if (id == null) + throw new RuntimeException("id is null"); + return (RefEntity)entities.get(id); + } + + private void removeAppointment(RefEntity entity) { + if (appointments.remove(entity)) { + // start date could have been changed, so we probably won't find it with a binary search + if (!appointmentsStart.remove(entity)) { + Iterator it = appointmentsStart.iterator(); + while (it.hasNext()) + if (entity.equals(it.next())) { + it.remove(); + break; + } + } + } + } + + public SortedSet getAppointments(User user,Date start,Date end) { + SortedSet appointmentSet = new TreeSet(new AppointmentStartComparator()); + Iterator it; + if (end != null) { + // all appointments that start before the enddate + it = appointmentsStart.headSet(end).iterator(); + //it = appointments.values().iterator(); + } else { + it = appointmentsStart.iterator(); + } + + while (it.hasNext()) { + Appointment appointment = (Appointment) it.next(); + // test if appointment end before the start-date + if (end != null && appointment.getStart().after(end)) + break; + + // Ignore appointments without a reservation + if ( appointment.getReservation() == null) + continue; + + if ( !appointment.overlaps(start,end, false)) + continue; + if (user == null || user.equals(appointment.getOwner()) ) { + appointmentSet.add(appointment); + } + } + return appointmentSet; + } + + public List getReservations(User user, Date start, Date end) { + HashSet reservationSet = new HashSet(); + Iterator it = getAppointments(user,start,end).iterator(); + while (it.hasNext()) { + Appointment appointment = it.next(); + reservationSet.add( appointment.getReservation() ); + } + return new ArrayList(reservationSet); + } + + + public Collection getCollection(RaplaType type) { + Set entities = (Set) entityMap.get(type); + + if ( Period.TYPE.equals( type)) { + entities = new TreeSet( entities); + } + + if (entities != null) { + return entities; + } else { + throw new RuntimeException("UNKNOWN TYPE. Can't get collection: " + + type); + } + } + + public Iterator getIterator(RaplaType type) throws RaplaException { + Set entities = (Set) entityMap.get(type); + if (entities != null) { + return entities.iterator(); + } + throw new RaplaException("Can't get iterator for " + type); + } + + public void clearAll() { + passwords.clear(); + Iterator it = entityMap.values().iterator(); + while (it.hasNext()) { + ((Set)it.next()).clear(); + } + appointmentsStart.clear(); + entities.clear(); + initSuperCategory(); + + } + private void initSuperCategory() { + entities.put (LocalCache.SUPER_CATEGORY_ID, superCategory); + superCategory.setReadOnly( false ); + categories.add( superCategory ); + Category[] childs = superCategory.getCategories(); + for (int i=0;istr.length()) + throw new ParseException("invalid rapla-id '" + str + "'", index); + try { + return new SimpleIdentifier(type,Integer.parseInt(str.substring(index))); + } catch (NumberFormatException ex) { + throw new ParseException("invalid rapla-id '" + str + "'", index); + } + } + + public DynamicType getDynamicType(String elementKey) { + Iterator it = dynamicTypes.iterator(); + while (it.hasNext()) { + DynamicType dt = (DynamicType) it.next(); + if (dt.getElementKey().equals(elementKey)) + return dt; + } + return null; + } + + public Iterator getVisibleEntities() { + return new NestedIterator(entityMap.keySet().iterator()) { + public Iterator getNestedIterator(Object key) { + RaplaType raplaType = (RaplaType)key; + if ( Reservation.TYPE.equals( raplaType ) || + Appointment.TYPE.equals( raplaType ) ) + return null; + Set set = (Set) entityMap.get( key); + return set.iterator(); + } + }; + } + + public Iterator getAllEntities() { + return new NestedIterator(entityMap.keySet().iterator()) { + public Iterator getNestedIterator(Object raplaType) { + Set set = (Set) entityMap.get(raplaType); + return set.iterator(); + } + /* + public Object next() { + Object obj = super.next(); + System.out.println(obj); + return obj; + } + */ + }; + } + + // Implementation of EntityResolver + public RefEntity resolve(Object id) throws EntityNotFoundException { + if (!(id instanceof SimpleIdentifier)) + new EntityNotFoundException("Unknown identifier class: " + id.getClass() + + ". Only the SimpleIdentier class is supported."); + RefEntity entity = (RefEntity) get(id); + + if (entity == null) + throw new EntityNotFoundException("Object for id [" + id.toString() + "] not found"); + return entity; + } + + public String getPassword(Object userId) { + return (String) passwords.get(userId); + } + + public void putPassword(Object userId, String password) { + passwords.put(userId,password); + } + + public void putAll( Collection list ) + { + Iterator it = list.iterator(); + while (it.hasNext()) { + put((RefEntity)it.next()); + } + + } + +} diff --git a/Rapla/src/org/rapla/storage/RaplaSecurityException.java b/Rapla/src/org/rapla/storage/RaplaSecurityException.java new file mode 100644 index 0000000..223dcc5 --- /dev/null +++ b/Rapla/src/org/rapla/storage/RaplaSecurityException.java @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.storage; + +import org.rapla.framework.RaplaException; + +/** + * This exception is thrown on an invalid login, + * or when a client tries to access data without + * the proper permissions. + */ +public class RaplaSecurityException extends RaplaException { + private static final long serialVersionUID = 1L; + + public RaplaSecurityException(String text) { + super(text); + } + + public RaplaSecurityException(Throwable throwable) { + super(throwable); + } + + public RaplaSecurityException(String text,Throwable ex) { + super(text,ex); + } +} + + + + + + + diff --git a/Rapla/src/org/rapla/storage/ReferenceNotFoundException.java b/Rapla/src/org/rapla/storage/ReferenceNotFoundException.java new file mode 100644 index 0000000..16eb5cc --- /dev/null +++ b/Rapla/src/org/rapla/storage/ReferenceNotFoundException.java @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.storage; + +import org.rapla.framework.RaplaException; + +/** This exception is thrown when you want to store an reference to an entity that was + * is not stored or was removed + */ +public class ReferenceNotFoundException extends RaplaException { + private static final long serialVersionUID = 1L; + + public ReferenceNotFoundException(String text) { + super(text); + } + + public ReferenceNotFoundException(Throwable throwable) { + super(throwable); + } + + public ReferenceNotFoundException(String text,Throwable ex) { + super(text,ex); + } +} + + + + + + + diff --git a/Rapla/src/org/rapla/storage/StorageOperator.java b/Rapla/src/org/rapla/storage/StorageOperator.java new file mode 100644 index 0000000..0635a48 --- /dev/null +++ b/Rapla/src/org/rapla/storage/StorageOperator.java @@ -0,0 +1,97 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +/** A Facade Interface for manipulating the stored data. + * This abstraction allows Rapla to store the data + * in many ways.
    + * Currently implemented are the storage in an XML-File + * ,the storage in an SQL-DBMS and storage over a + * network connection. + * @see org.rapla.storage.dbsql.DBOperator + * @see org.rapla.storage.dbfile.XMLOperator + * @see org.rapla.storage.dbrm.RemoteOperator + * @author Christopher Kohlhaas + */ +package org.rapla.storage; + +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.SortedSet; + +import org.rapla.entities.Category; +import org.rapla.entities.Entity; +import org.rapla.entities.EntityNotFoundException; +import org.rapla.entities.RaplaType; +import org.rapla.entities.User; +import org.rapla.entities.configuration.Preferences; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Reservation; +import org.rapla.framework.RaplaException; + +public interface StorageOperator { + String ROLE = StorageOperator.class.getName(); + + void connect() throws RaplaException; + void connect(String username,char[] password) throws RaplaException; + boolean isConnected(); + /** Refreshes the data. This could be helpfull if the storage + * operator uses a cache and does not support "Active Monitoring" + * of the original data */ + void refresh() throws RaplaException; + void disconnect() throws RaplaException; + + /** should return a clone of the object. Never edit the + original, always edit the object returned by editObject.*/ + Entity editObject(Entity obj, User user) throws RaplaException; + + Entity getPersistant(Entity entity) throws EntityNotFoundException; + /** Stores and/or removes entities and specifies a user that is responsible for the changes. + * Notifies all registered StorageUpdateListeners after a successful + storage.*/ + void storeAndRemove(Entity[] storeObjects,Entity[] removeObjects,User user) throws RaplaException; + + Object createIdentifier(RaplaType raplaType) throws RaplaException; + + Collection getObjects(RaplaType raplaType) throws RaplaException; + + /** returns all the objects (except reservations)that are visible for the current user */ + List getVisibleEntities(User user) throws RaplaException; + + /** returns the user or null if a user with the given username was not found. */ + User getUser(String username) throws RaplaException; + Preferences getPreferences(User user) throws RaplaException; + + + /** returns the reservations of the specified user, sorted by name.*/ + List getReservations(User user,Date start,Date end) throws RaplaException; + + /** returns the appointments of the specified user in the specified period, sorted by start-date */ + SortedSet getAppointments(User user,Date start,Date end) throws RaplaException; + + Category getSuperCategory(); + + /** changes the password and calls storeObjects(new Object[] {user}) */ + void changePassword(User user,char[] oldPassword,char[] newPassword) throws RaplaException; + + boolean canChangePassword(); + + void addStorageUpdateListener(StorageUpdateListener updateListener); + void removeStorageUpdateListener(StorageUpdateListener updateListener); + + Object getLock(); + + Date today(); + + boolean supportsActiveMonitoring(); + +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/storage/StorageUpdateListener.java b/Rapla/src/org/rapla/storage/StorageUpdateListener.java new file mode 100644 index 0000000..55b2e1a --- /dev/null +++ b/Rapla/src/org/rapla/storage/StorageUpdateListener.java @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.storage; + +import org.rapla.framework.RaplaException; + +public interface StorageUpdateListener +{ + public void objectsUpdated(UpdateResult evt); + public void updateError(RaplaException ex); + public void storageDisconnected(); +} diff --git a/Rapla/src/org/rapla/storage/UpdateEvent.java b/Rapla/src/org/rapla/storage/UpdateEvent.java new file mode 100644 index 0000000..395b5f7 --- /dev/null +++ b/Rapla/src/org/rapla/storage/UpdateEvent.java @@ -0,0 +1,104 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.storage; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.rapla.entities.storage.RefEntity; + +public class UpdateEvent implements java.io.Serializable,Cloneable +{ + private static final long serialVersionUID = 1L; + + HashMap removeSet = new HashMap(); + HashMap storeSet = new HashMap(); + + ArrayList removeObjects = new ArrayList(); + ArrayList storeObjects = new ArrayList(); + Object userId; + long repositoryVersion; + + public UpdateEvent() { + } + + public void setUserId( Object userId) { + this.userId = userId; + } + public Object getUserId() { + return userId; + } + + private void addRemove(RefEntity entity) { + removeObjects.add(entity); + removeSet.put( entity.getId(),entity); + } + private void addStore(RefEntity entity) { + storeObjects.add(entity); + storeSet.put( entity.getId(), entity); + } + + public List getRemoveObjects() { + return removeObjects; + } + + public List getStoreObjects() { + return storeObjects; + } + + /** use this method if you want to avoid adding the same Entity twice.*/ + public void putStore(RefEntity entity) { + + if (storeSet.get(entity.getId()) == null) + addStore(entity); + } + + /** use this method if you want to avoid adding the same Entity twice.*/ + public void putRemove(RefEntity entity) { + if (removeSet.get(entity.getId()) == null) + addRemove(entity); + } + + /** find an entity in the update-event that matches the passed original. Returns null + * if no such entity is found. */ + public RefEntity findEntity(RefEntity original) { + RefEntity entity = storeSet.get( original.getId()); + if ( entity != null) + return entity; + entity = removeSet.get( original.getId()); + if ( entity != null) + return entity; + return null; + } + + public UpdateEvent clone() { + UpdateEvent clone = new UpdateEvent( ); + clone.userId = userId; + clone.removeObjects = (ArrayList) removeObjects.clone(); + clone.storeObjects = (ArrayList) storeObjects.clone(); + clone.removeSet = (HashMap) removeSet.clone(); + clone.storeSet = (HashMap) storeSet.clone(); + return clone; + } + + public long getRepositoryVersion() + { + return repositoryVersion; + } + + public void setRepositoryVersion( long repositoryVersion ) + { + this.repositoryVersion = repositoryVersion; + } +} diff --git a/Rapla/src/org/rapla/storage/UpdateResult.java b/Rapla/src/org/rapla/storage/UpdateResult.java new file mode 100644 index 0000000..870d792 --- /dev/null +++ b/Rapla/src/org/rapla/storage/UpdateResult.java @@ -0,0 +1,131 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.storage; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.rapla.entities.User; + +public class UpdateResult +{ + User user; + List operations = new ArrayList(); + + public UpdateResult(User user) { + this.user = user; + } + + public void addOperation(final Object operation) { + if ( operation == null) + throw new IllegalStateException( "Operation can't be null" ); + operations.add(operation); + } + + public User getUser() { + return user; + } + + public Set getRemoveObjects() { + return getObject( Remove.class); + } + + public Set getChangeObjects() { + return getObject( Change.class); + } + + public Set getAddObjects() { + return getObject( Add.class); + } + + public Iterator getOperations( final Class operationClass) { + Iterator operationsIt = operations.iterator(); + if ( operationClass == null) + throw new IllegalStateException( "OperationClass can't be null" ); + + List list = new ArrayList(); + while ( operationsIt.hasNext() ) { + Object obj = operationsIt.next(); + if ( operationClass.isInstance( obj )) + list.add( obj ); + } + + return list.iterator(); + } + + protected Set getObject( final Class operationClass ) { + Set set = new HashSet(); + if ( operationClass == null) + throw new IllegalStateException( "OperationClass can't be null" ); + Iterator it= getOperations( operationClass); + while (it.hasNext() ) { + set.add( ((UpdateOperation)it.next() ).getCurrent()); + } + return set; + } + + + static public class Add implements UpdateOperation { + Object currentObj; // the actual represantation of the object + Object newObj; // the object in the state when it was addes + public Add(Object currentObj, Object newObj) { + this.currentObj = currentObj; + this.newObj = newObj; + } + public Object getCurrent() { + return currentObj; + } + public Object getNew() { + return newObj; + } + } + + static public class Remove implements UpdateOperation { + Object currentObj; // the actual represantation of the object + public Remove(Object currentObj) { + this.currentObj = currentObj; + } + public Object getCurrent() { + return currentObj; + } + } + + static public class Change implements UpdateOperation{ + Object currentObj; // the actual representation of the object + Object newObj; // the object in the state when it was changed + Object oldObj; // the object in the state before it was changed + public Change(Object currentObj, Object newObj, Object oldObj) { + this.currentObj = currentObj; + this.newObj = newObj; + this.oldObj = oldObj; + } + public Object getCurrent() { + return currentObj; + } + public Object getNew() { + return newObj; + } + public Object getOld() { + return oldObj; + } + } +} + +interface UpdateOperation { + public Object getCurrent(); +} + + diff --git a/Rapla/src/org/rapla/storage/dbfile/FileOperator.java b/Rapla/src/org/rapla/storage/dbfile/FileOperator.java new file mode 100644 index 0000000..4b0cfdf --- /dev/null +++ b/Rapla/src/org/rapla/storage/dbfile/FileOperator.java @@ -0,0 +1,309 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org . | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.storage.dbfile; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Iterator; + +import org.apache.avalon.framework.configuration.Configuration; +import org.rapla.components.util.IOUtil; +import org.rapla.entities.RaplaType; +import org.rapla.entities.User; +import org.rapla.entities.storage.RefEntity; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.framework.StartupEnvironment; +import org.rapla.storage.IOContext; +import org.rapla.storage.LocalCache; +import org.rapla.storage.UpdateEvent; +import org.rapla.storage.UpdateResult; +import org.rapla.storage.impl.AbstractCachableOperator; +import org.rapla.storage.impl.EntityStore; +import org.rapla.storage.xml.RaplaInput; +import org.rapla.storage.xml.RaplaMainReader; +import org.rapla.storage.xml.RaplaMainWriter; + +/** Use this Operator to keep the data stored in an XML-File. + *

    Sample configuration: +

    + <file-storage id="file">
    + <file>data.xml</file>
    + <encoding>utf-8</encoding>
    + <validate>no</validate>
    + </facade>
    + 
    + *
      + *
    • The file entry contains the path of the data file. + * If the path is not an absolute path it will be resolved + * relative to the location of the configuration file + *
    • + *
    • The encoding entry specifies the encoding of the xml-file. + * Currently only UTF-8 is tested. + *
    • + *
    • The validate entry specifies if the xml-file should be checked + * against a schema-file that is located under org/rapla/storage/xml/rapla.rng + * (Default is no) + *
    • + *
    + *

    + *

    Note: The xmloperator doesn't check passwords.

    + + @see AbstractCachableOperator + @see org.rapla.storage.StorageOperator + */ +final public class FileOperator extends AbstractCachableOperator +{ + private File storageFile; + private URL loadingURL; + + private final String encoding; + protected boolean isConnected = false; + final boolean includeIds ; + + private final boolean validate; + + public FileOperator( RaplaContext context, Configuration config ) throws RaplaException + { + super( context ); + StartupEnvironment env = (StartupEnvironment) context.lookup( StartupEnvironment.ROLE ); + + URL contextRootURL = env.getContextRootURL(); + + String fileName = config.getChild( "file" ).getValue( "data.xml" ); + try + { + File file = new File( fileName ); + if ( file.isAbsolute() ) + { + storageFile = file; + loadingURL = storageFile.getCanonicalFile().toURI().toURL(); + } + else + { + int startupEnv = env.getStartupMode(); + if ( startupEnv == StartupEnvironment.WEBSTART || startupEnv == StartupEnvironment.APPLET ) + { + loadingURL = new URL( contextRootURL, fileName ); + } + else + { + File contextRootFile = IOUtil.getFileFrom( contextRootURL ); + storageFile = new File( contextRootFile, fileName ); + loadingURL = storageFile.getCanonicalFile().toURI().toURL(); + } + } + getLogger().info("Data:" + loadingURL); + } + catch ( MalformedURLException ex ) + { + throw new RaplaException( fileName + " is not an valid path " ); + } + catch ( IOException ex ) + { + throw new RaplaException( "Can't read " + storageFile + " " + ex.getMessage() ); + } + encoding = config.getChild( "encoding" ).getValue( "utf-8" ); + validate = config.getChild( "validate" ).getValueAsBoolean( false ); + includeIds = config.getChild( "includeIds" ).getValueAsBoolean( false ); + } + + public boolean supportsActiveMonitoring() + { + return false; + } + + /** just calls connect. Username and password will be ignored.*/ + final public void connect( String username, char[] password ) throws RaplaException + { + connect(); + } + + /** Sets the isConnected-flag and calls loadData.*/ + final public void connect() throws RaplaException + { + if ( isConnected ) + return; + loadData(); + isConnected = true; + } + + final public boolean isConnected() + { + return isConnected; + } + + /** this implementation does nothing. Once connected isConnected will always return true.*/ + final public void disconnect() throws RaplaException + { + isConnected = false; + fireStorageDisconnected(); + } + + final public void refresh() throws RaplaException + { + getLogger().warn( "Incremental refreshs are not supported" ); + } + + final public void refreshFull() throws RaplaException + { + try + { + loadData(); + isConnected = true; + } + catch ( Exception ex ) + { + cache.clearAll(); + isConnected = false; + if ( ex instanceof RaplaException ) + throw (RaplaException) ex; + else + throw new RaplaException( ex ); + } + } + + final protected void loadData() throws RaplaException + { + try + { + cache.clearAll(); + idTable.setCache( cache ); + if ( getLogger().isDebugEnabled() ) + getLogger().debug( "Reading data from file:" + loadingURL ); + + RaplaInput xmlAdapter = new RaplaInput( getLogger().getChildLogger( "reading" ) ); + + EntityStore entityStore = new EntityStore( null, cache.getSuperCategory() ); + RaplaContext context = new IOContext().createInputContext( serviceManager, entityStore, idTable ); + RaplaMainReader contentHandler = new RaplaMainReader( context ); + xmlAdapter.read( loadingURL, contentHandler, validate ); + resolveEntities( entityStore.getList().iterator(), entityStore ); + cache.putAll( entityStore.getList() ); + cache.getSuperCategory().setReadOnly(true); + for ( Iterator it = cache.getIterator( User.TYPE ); it.hasNext(); ) + { + RefEntity user = ( (RefEntity) it.next() ); + String password = entityStore.getPassword( user.getId() ); + //System.out.println("Storing password in cache" + password); + cache.putPassword( user.getId(), password ); + } + // contextualize all Entities + if ( getLogger().isDebugEnabled() ) + getLogger().debug( "Entities contextualized" ); + + // save the converted file; + if ( xmlAdapter.wasConverted() ) + { + getLogger().info( "Storing the converted file" ); + saveData(); + } + } + catch ( IOException ex ) + { + getLogger().info( "Loading error: " + loadingURL); + throw new RaplaException( "Can't load file at " + loadingURL + ": " + ex.getMessage() ); + } + catch ( RaplaException ex ) + { + throw ex; + } + catch ( Exception ex ) + { + throw new RaplaException( ex ); + } + } + + + public void dispatch( UpdateEvent evt ) throws RaplaException + { + evt = createClosure( evt ); + check( evt ); + // call of update must be first to update the cache. + // then saveData() saves all the data in the cache + UpdateResult result = update( evt, true ); + saveData(); + fireStorageUpdated( result ); + } + + final public Object createIdentifier( RaplaType raplaType ) throws RaplaException + { + return idTable.createId( raplaType ); + } + + + final public void setCache( LocalCache cache ) throws RaplaException + { + super.setCache( cache ); + idTable.setCache( cache ); + } + + private boolean bWarningDisplayed = false; + + private void showReadOnlyWarning() throws RaplaException + { + if ( bWarningDisplayed ) + return; + javax.swing.JOptionPane.showMessageDialog( null, getI18n().getString( "warning.readonly_storage" ), + getI18n().getString( "warning" ), + javax.swing.JOptionPane.WARNING_MESSAGE ); + bWarningDisplayed = true; + } + + final public void saveData() throws RaplaException + { + try + { + if ( storageFile == null ) + { + showReadOnlyWarning(); + return; + } + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + writeData( buffer ); + byte[] data = buffer.toByteArray(); + buffer.close(); + + moveFile( storageFile, storageFile.getPath() + ".bak" ); + OutputStream out = new FileOutputStream( storageFile ); + out.write( data ); + out.close(); + } + catch ( IOException e ) + { + throw new RaplaException( e.getMessage() ); + } + } + + private void writeData( OutputStream out ) throws IOException, RaplaException + { + final boolean includeVersions = false; + RaplaContext outputContext = new IOContext().createOutputContext( serviceManager, cache, includeIds, includeVersions ); + RaplaMainWriter writer = new RaplaMainWriter( outputContext ); + writer.setEncoding( encoding ); + writer.write( out ); + } + + private void moveFile( File file, String newPath ) throws IOException + { + File backupFile = new File( newPath ); + backupFile.delete(); + file.renameTo( backupFile ); + } + +} diff --git a/Rapla/src/org/rapla/storage/dbfile/package.html b/Rapla/src/org/rapla/storage/dbfile/package.html new file mode 100644 index 0000000..2052c9f --- /dev/null +++ b/Rapla/src/org/rapla/storage/dbfile/package.html @@ -0,0 +1,5 @@ + +Serialization of the reservation-data in an XML file + + + diff --git a/Rapla/src/org/rapla/storage/dbrm/Connector.java b/Rapla/src/org/rapla/storage/dbrm/Connector.java new file mode 100644 index 0000000..d55ba3c --- /dev/null +++ b/Rapla/src/org/rapla/storage/dbrm/Connector.java @@ -0,0 +1,20 @@ +package org.rapla.storage.dbrm; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; + +import org.rapla.framework.RaplaException; + +public interface Connector +{ + + String getInfo(); + + void start() throws Exception; + + InputStream call( String methodName, Map args) throws IOException, RaplaException; + + void stop(); + +} diff --git a/Rapla/src/org/rapla/storage/dbrm/HTTPConnector.java b/Rapla/src/org/rapla/storage/dbrm/HTTPConnector.java new file mode 100644 index 0000000..86e1c07 --- /dev/null +++ b/Rapla/src/org/rapla/storage/dbrm/HTTPConnector.java @@ -0,0 +1,210 @@ +package org.rapla.storage.dbrm; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.net.ConnectException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.ConfigurationException; +import org.rapla.components.util.Tools; +import org.rapla.facade.RaplaComponent; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.framework.internal.ConfigTools; + +public class HTTPConnector extends RaplaComponent implements Connector +{ + String sessionId; + URL server; + + public HTTPConnector(RaplaContext context, Configuration config) throws RaplaException{ + super(context); + try + { + String configEntry = config.getChild("server").getValue(); + String serverURL = ConfigTools.resolveContext(configEntry, context ); + server = new URL(serverURL); + } + catch (MalformedURLException e) + { + throw new RaplaException("Malformed url. Could not parse " + server); + } + catch (ConfigurationException e) + { + throw new RaplaException(e); + } + } + + public InputStream call(String methodName, Map args) throws IOException, RaplaException + { + URL methodURL = new URL(server,"rapla/rpc/" + methodName ); + //URL server = new URL("http://127.0.0.1:8080/vorsorge-optimierer/start"); + + //System.err.println("Calling " + methodURL.toExternalForm() ); + + HttpURLConnection conn = (HttpURLConnection)methodURL.openConnection(); + conn.setRequestMethod("POST"); + conn.setUseCaches( false ); + conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); + conn.setRequestProperty("Cookie","JSESSIONID=" + sessionId); + setSessionForRequest( conn ); + conn.setDoOutput(true); + + try + { + conn.connect(); + } + catch (ConnectException ex) + { + throw new RaplaException(getI18n().format("error.connect", server)); + } + + Writer wr = new OutputStreamWriter(conn.getOutputStream(),"UTF-8"); + addParams( wr, args); + wr.flush(); + + try + { + updateSession ( conn ); + return new BufferedInputStream(conn.getInputStream()); + } + catch (ConnectException ex) + { + throw new RaplaException(getI18n().format("error.connect", server)); + } + catch (IOException ex) + { + String entry = conn.getHeaderField("X-Error-Stacktrace"); + if ( entry != null) + { + Throwable e = getServerException( server); + if ( e instanceof RaplaException) + { + throw (RaplaException) e; + } + throw new RaplaException( e); + } + else + { + throw ex; + } + } + } + + private void setSessionForRequest( HttpURLConnection connection ) + { + if ( sessionId != null) + { + connection.addRequestProperty("Cookie","JSESSIONID=" + sessionId); + } + } + + private Throwable getServerException( URL server ) throws IOException, RaplaException + { + URL methodURL = new URL(server,"rapla/rpc/getException"); + HttpURLConnection connection = (HttpURLConnection)methodURL.openConnection(); + setSessionForRequest( connection ); + + //ByteArrayOutputStream output = new ByteArrayOutputStream(); + + ObjectInputStream in = new ObjectInputStream( connection.getInputStream()); + Throwable e; + try + { + e = (Throwable)in.readObject(); + } + catch (Exception e1) + { + throw new RaplaException( e1); + } + return e; + } + + private void addParams(Writer writer, Map args ) throws IOException + { + boolean appendAdd = false; + for (Iterator it = args.keySet().iterator();it.hasNext();) + { + if ( appendAdd) + { + writer.write( "&"); + } + String key = (String)it.next(); + String value= (String)args.get( key); + if ( value != null) + { + String pair = key+"=" + URLEncoder.encode(value,"utf-8"); + writer.write( pair); + appendAdd = true; + } + else + { + appendAdd = false; + } + + } + } + + private void updateSession( URLConnection connection ) throws IOException + { + + Map cookies = new HashMap(); + String entry = connection.getHeaderField("Set-Cookie"); + if ( entry != null) + { + String[] splitted = Tools.split(entry,';'); + if ( splitted.length > 0) + { + String[] first = Tools.split(splitted[0],'='); + cookies.put(first[0], first[1]); + } + } + String sessionId = (String)cookies.get("JSESSIONID"); + if ( sessionId != null) + { + this.sessionId = sessionId; + } + } + + public boolean hasSession() + { + + return sessionId != null; + } + + public String getInfo() + { + return sessionId; + } + + public void start() throws Exception + { + // TODO Auto-generated method stub + + } + + + public void stop() + { + // TODO Auto-generated method stub + + } + + public long getUpdateIntervall() + { + return 30000; + } + +} diff --git a/Rapla/src/org/rapla/storage/dbrm/RaplaStorePage.java b/Rapla/src/org/rapla/storage/dbrm/RaplaStorePage.java new file mode 100644 index 0000000..7a7d9c5 --- /dev/null +++ b/Rapla/src/org/rapla/storage/dbrm/RaplaStorePage.java @@ -0,0 +1,29 @@ +package org.rapla.storage.dbrm; + +import java.io.IOException; +import java.io.PrintWriter; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.rapla.servletpages.RaplaPageGenerator; + +public class RaplaStorePage implements RaplaPageGenerator +{ + + public void generatePage( + ServletContext context, + HttpServletRequest request, + HttpServletResponse response ) throws IOException, ServletException + { + String storeString = request.getParameter("storeString"); + PrintWriter writer = response.getWriter(); + writer.println("
    "); + writer.println(""); + writer.println(""); + writer.println("
    "); + } + +} diff --git a/Rapla/src/org/rapla/storage/dbrm/RemoteOperator.java b/Rapla/src/org/rapla/storage/dbrm/RemoteOperator.java new file mode 100644 index 0000000..1e28ef5 --- /dev/null +++ b/Rapla/src/org/rapla/storage/dbrm/RemoteOperator.java @@ -0,0 +1,653 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.storage.dbrm; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; + +import org.apache.avalon.framework.configuration.Configuration; +import org.rapla.components.util.Command; +import org.rapla.components.util.CommandQueue; +import org.rapla.components.util.SerializableDateTimeFormat; +import org.rapla.entities.EntityNotFoundException; +import org.rapla.entities.RaplaType; +import org.rapla.entities.User; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.storage.EntityResolver; +import org.rapla.entities.storage.RefEntity; +import org.rapla.entities.storage.internal.SimpleEntity; +import org.rapla.entities.storage.internal.SimpleIdentifier; +import org.rapla.framework.Container; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.plugin.RaplaExtensionPoints; +import org.rapla.server.RemoteMethod; +import org.rapla.server.RemoteServiceCaller; +import org.rapla.server.RemoteStorage; +import org.rapla.server.RestartServer; +import org.rapla.server.internal.RemoteStorageImpl; +import org.rapla.server.internal.SessionExpiredException; +import org.rapla.storage.IOContext; +import org.rapla.storage.LocalCache; +import org.rapla.storage.RaplaSecurityException; +import org.rapla.storage.UpdateEvent; +import org.rapla.storage.UpdateResult; +import org.rapla.storage.impl.AbstractCachableOperator; +import org.rapla.storage.impl.EntityStore; +import org.rapla.storage.xml.RaplaInput; +import org.rapla.storage.xml.RaplaMainReader; + +/** This operator can be used to modify and access data over the + * network. It needs an server-process providing the StorageService + * (usually this is the default rapla-server). + *

    Sample configuration: +

    +   <remote-storage id="web">
    +   </remote-storate>
    +  
    + * The messaging-client value contains the id of a + * messaging-client-component which handles the + * communication with the server. + * The RemoteOperator provides also the Service {@link RemoteServiceCaller} + * @see org.rapla.server.RemoteStorageCallback + * @see org.rapla.components.rpc.MessagingClient +*/ +public class RemoteOperator + extends + AbstractCachableOperator + implements + RemoteServiceCaller + ,RestartServer +{ + ServerStub serv = new ServerStub(); + String username; + String password; + protected CommandQueue notifyQueue; + private boolean bSessionActive = false; + Connector connector; + private boolean bReservationsFetched; + private Date firstCachedDate = null; + private Date lastCachedDate = null; + private boolean isRestarting; + public RemoteOperator(RaplaContext context, Configuration config) throws RaplaException { + super( context ); + + Container raplaMainContainer = ((Container)context.lookup( Container.ROLE)); + raplaMainContainer.addContainerProvidedComponent( RaplaExtensionPoints.SERVLET_PAGE_EXTENSION, RaplaStorePage.class.getName(), "store", null); + + ((Container)context.lookup( Container.ROLE)).addContainerProvidedComponentInstance(RestartServer.ROLE, this); + ((Container)context.lookup( Container.ROLE)).addContainerProvidedComponentInstance(RemoteServiceCaller.ROLE, this); + + connector = new HTTPConnector(context,config); + } + + public void connect() throws RaplaException { + throw new RaplaException("RemoteOperator doesn't support anonymous connect"); + } + + public void connect(String username,char[] password) throws RaplaException { + this.username = username; + this.password = new String(password); + if (isConnected()) + return; + getLogger().info("Connecting to server and starting login.."); + doConnect(); + try { + String clientVersion= i18n.getString("rapla.version") ; + serv.checkServerVersion( clientVersion); + serv.login(this.username,this.password); + bSessionActive = true; + updateToday(); + getLogger().info("login successfull"); + } catch (RaplaException ex){ + disconnect(); + throw ex; + } + loadData(); + notifyQueue = org.rapla.components.util.CommandQueue.createCommandQueue(); + } + + public void saveData() throws RaplaException { + throw new RaplaException("RemoteOperator doesn't support storing complete cache, yet!"); + } + + /** implementation specific. Should be private */ + public void serverHangup() { + getLogger().warn("Server hangup"); + if (!isRestarting) { + getLogger().error(getI18n().format("error.connection_closed",getConnectionName())); + } + isRestarting = false; + new Thread() { + public void run() { + fireStorageDisconnected(); + } + }.start(); + } + + + public String getConnectionName() { + return connector.getInfo(); + } + + private void doConnect() throws RaplaException { + boolean bFailed = true; + try { + connector.start(); + bFailed = false; + } catch (Exception e) { + throw new RaplaException(i18n.format("error.connect",getConnectionName()),e); + } finally { + if (bFailed) + disconnect(); + } + } + + public boolean isConnected() { + // return connector.hasSession();//messagingClient != null && messagingClient.isRunning(); + return bSessionActive; + } + + public boolean supportsActiveMonitoring() { + return true; + } + + public void refresh() throws RaplaException { + serv.refresh(); + } + public void restartServer() throws RaplaException { + isRestarting = true; + serv.restartServer(); + fireStorageDisconnected(); + + } + + /** disconnect from the server */ + public void disconnect() throws RaplaException { + getLogger().info("Disconnecting from server"); + try { + bSessionActive = false; + if ( notifyQueue != null) + { + notifyQueue.dequeueAll(); // Execute all update Commands. + } + firstCachedDate = null; + lastCachedDate = null; + bReservationsFetched = false; + connector.stop(); + cache.clearAll(); + } catch (Exception e) { + throw new RaplaException("Could not disconnect", e); + } + fireStorageDisconnected(); + } + + private void addToCache(List list, boolean useCache) throws RaplaException { + EntityResolver entityResolver = createEntityResolver( list, useCache ? cache : null ); + synchronized (cache) { + resolveEntities( list.iterator(), entityResolver ); + for( Iterator it = list.iterator();it.hasNext();) { + SimpleEntity entity = (SimpleEntity) it.next(); + cache.put(entity); + } + } + } + + private void loadData() throws RaplaException { + checkConnected(); + cache.clearAll(); + getLogger().debug("Getting Data.."); + // recontextualize Entities + addToCache(serv.getResources(), false ); + getLogger().debug("Data flushed"); + } + + protected void checkConnected() throws RaplaException { + if ( !bSessionActive ) { + if (username == null) { + throw new RaplaException("Need to login first!"); + } else { + throw new RaplaException(i18n.format("error.connection_closed", getConnectionName())); + } + } + } + + // This checks should be done on the server + @Override + protected void addChangedDynamicTypeDependant(UpdateEvent evt,DynamicType type, boolean toRemove) throws RaplaException { + } + + + @Override + protected void checkNoDependencies(Collection entities, Set storeObjects) + { + + } + + protected long getCurrentTime() throws RaplaException { + if ( bSessionActive ) + return serv.getServerTime(); + else + return super.getCurrentTime(); + } + + + public void dispatch(UpdateEvent evt) throws RaplaException { + checkConnected(); + // Create closure + UpdateEvent closure = createClosure(evt ); + check( closure ); + // Store on server + if (getLogger().isDebugEnabled()) { + Iterator it =closure.getStoreObjects().iterator(); + while (it.hasNext()) { + RefEntity entity = (RefEntity)it.next(); + getLogger().debug("dispatching store for: " + entity); + } + it =closure.getRemoveObjects().iterator(); + while (it.hasNext()) { + RefEntity entity = (RefEntity)it.next(); + getLogger().debug("dispatching remove for: " + entity); + } + } + serv.dispatch( closure ); + // Store in cache + UpdateResult result = update( closure, true ); + fireStorageUpdated(result); + } + + public Object createIdentifier(RaplaType raplaType) throws RaplaException { + return serv.createIdentifier(raplaType); + } + + /** we must override this method because we can't store the passwords on the client*/ + public void authenticate(String username,String password) throws RaplaException { + serv.authenticate(username,password); + } + + public boolean canChangePassword() { + try { + return serv.canChangePassword(); + } catch (RaplaException ex) { + return false; + } + } + + public void changePassword(User user,char[] oldPassword,char[] newPassword) throws RaplaException { + try { + serv.changePassword(user.getUsername(),oldPassword,newPassword); + } catch (RaplaSecurityException ex) { + throw new RaplaSecurityException(i18n.getString("error.wrong_password")); + } + } + + private void updateReservations(User user,Date start,Date end) throws RaplaException { + if ( !bReservationsFetched ) { + bReservationsFetched = true; + firstCachedDate = start; + lastCachedDate = end; + addToCache(serv.getReservations(firstCachedDate, lastCachedDate), true ); + return; + } + + if ( firstCachedDate != null) { + if (start == null || start.before(firstCachedDate)) { + addToCache(serv.getReservations( start, firstCachedDate), true ); + firstCachedDate = start; + } + } + if ( lastCachedDate != null) { + if (end == null || end.after(lastCachedDate)) { + addToCache(serv.getReservations( lastCachedDate, end), true ); + lastCachedDate = end; + } + } + } + + public RefEntity resolveId(Object id) throws EntityNotFoundException { + try { + return super.resolveId(id); + } catch (EntityNotFoundException ex) { + try { + List resolved = serv.getEntityRecursive( id ); + addToCache(resolved, true ); + } catch (RaplaException rex) { + throw new EntityNotFoundException("Object for id " + id.toString() + " not found due to " + ex.getMessage()); + } + return super.resolveId(id); + } + } + + public SortedSet getAppointments(User user,Date start,Date end) throws RaplaException { + checkConnected(); + updateReservations( user, start, end ); + return super.getAppointments( user, start, end ); + } + + public List getReservations(User user,Date start,Date end) throws RaplaException { + checkConnected(); + updateReservations( user, start, end ); + return super.getReservations( user, start, end); + } + + private String readResultToString( InputStream input) throws IOException + { + InputStreamReader in = new InputStreamReader( input,"utf-8"); + char[] buf = new char[4096]; + StringBuffer buffer = new StringBuffer(); + while ( true ) + { + int len = in.read(buf); + if ( len == -1) + { + break; + } + buffer.append( buf, 0,len ); + //buf. + } + String result = buffer.toString(); + return result; + } + + public String call( String serviceName, RemoteMethod method, String[] args ) throws RaplaException + { + return serv.call(serviceName,method, (String[])args); + } + + long clientRepositoryVerion = 0; + + public class ServerStub implements RemoteStorage { + + String call( RemoteMethod method,String[] args) throws RaplaException { + return call( null, method, args); + } + + InputStream callInput( RemoteMethod method,String[] args) throws RaplaException { + return callInput( null, method, args); + } + + String call( String service, RemoteMethod method,String[] args) throws RaplaException { + try { + InputStream stream = callInput( service,method,args); + String result = readResultToString( stream); + // System.out.println( result ); + return result; + } catch (IOException ex) { + throw new RaplaException(ex); + } + + } + + InputStream callInput( String service,RemoteMethod method,String[] args) throws RaplaException { + try { + String methodName = method.method(); + Map argMap = createArgumentMap( method, args); + if ( service != null) + { + methodName = service +"/" + methodName; + } + return connector.call( methodName, argMap ); + } catch (SessionExpiredException ex) { + disconnect(); + throw ex; + } catch (IOException ex) { + throw new RaplaException(ex); + } + } + + private Map createArgumentMap( RemoteMethod method, String[] args ) throws RaplaException + { + Map argMap = new HashMap(); + if ( args.length != method.length()) + { + throw new RaplaException("Paramter list don't match Expected " + method.length() +" but was " + args.length); + } + for ( int i=0;i")>=0) + { + } + else + { + RemoteOperator.this.refresh( xml); + } + } + public void notifyUpdate() { + if ( isRestarting) + return; + notifyQueue.enqueue(new UpdateCommand()); + } + + + } + + private void refresh(String xml) throws RaplaException + { + synchronized (getLock()) + { + UpdateEvent evt = RemoteStorageImpl.createUpdateEvent( serviceManager,xml, cache ); + Iterator it = evt.getStoreObjects().iterator(); + while (it.hasNext()) { + SimpleEntity entity = (SimpleEntity) it.next(); + + RefEntity cachedVersion = (RefEntity) cache.get(entity.getId()); + // Ignore object if its not newer than the one in cache. + if (cachedVersion != null && cachedVersion.getVersion() >= entity.getVersion()) { + //getLogger().debug("already on client " + entity + " version " + cachedVersion.getVersion()); + it.remove(); + continue; + } + + if (getLogger().isDebugEnabled()) + getLogger().debug(" storing " + entity.getId() + + " version: " + entity.getVersion()); + } + + RemoteOperator.super.resolveEntities + ( + evt.getStoreObjects().iterator() + ,createEntityResolver(evt.getStoreObjects(),cache) + ); + + it = evt.getRemoveObjects().iterator(); + while (it.hasNext()) { + SimpleEntity entity = (SimpleEntity) it.next(); + + RefEntity cachedVersion = (RefEntity) cache.get(entity.getId()); + // Ignore object, if its not in cache. + if (cachedVersion == null) { + it.remove(); + continue; + } + if (getLogger().isDebugEnabled()) + getLogger().debug(" removing " + entity.getId() + + " version: " + entity.getVersion()); + } + + RemoteOperator.super.resolveEntities + ( + evt.getRemoveObjects().iterator() + ,createEntityResolver(evt.getStoreObjects(),cache) + ); + + if ( bSessionActive && + ( evt.getRemoveObjects().size() > 0 + || evt.getStoreObjects().size() > 0 ) ) { + getLogger().info("Objects updated!"); + UpdateResult result = update(evt, false); + clientRepositoryVerion = evt.getRepositoryVersion(); + // now we can set the cache as updated + fireStorageUpdated(result); + } + clientRepositoryVerion = evt.getRepositoryVersion(); + } + + } + public void serverDisconnected() { + bSessionActive = false; + } + + //******* End ClientInterface ************* + class UpdateCommand implements Command { + public void execute() { + if ( !bSessionActive ) + return; // We can ignore the update! + try { + serv.refresh(); + } catch (Exception ex) { + getLogger().error(ex.getMessage(),ex); + /* + // #TODO. Do we need do disconnect on every notify error? + try { + disconnect(); + } catch (RaplaException rex) { + getLogger().error(rex.getMessage(),rex); + } + */ + } + } + } + + + + +} + diff --git a/Rapla/src/org/rapla/storage/dbsql/DBOperator.java b/Rapla/src/org/rapla/storage/dbsql/DBOperator.java new file mode 100644 index 0000000..4640a0a --- /dev/null +++ b/Rapla/src/org/rapla/storage/dbsql/DBOperator.java @@ -0,0 +1,504 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.storage.dbsql; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.Driver; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Iterator; +import java.util.Properties; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.sql.DataSource; + +import org.apache.avalon.framework.activity.Disposable; +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.ConfigurationException; +import org.rapla.entities.RaplaType; +import org.rapla.entities.User; +import org.rapla.entities.storage.RefEntity; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaDefaultContext; +import org.rapla.framework.RaplaException; +import org.rapla.framework.internal.ConfigTools; +import org.rapla.storage.CachableStorageOperator; +import org.rapla.storage.IOContext; +import org.rapla.storage.UpdateEvent; +import org.rapla.storage.UpdateResult; +import org.rapla.storage.impl.AbstractCachableOperator; +import org.rapla.storage.impl.EntityStore; +import org.rapla.storage.xml.RaplaInput; + +/** This Operator is used to store the data in a SQL-DBMS.*/ +public class DBOperator extends AbstractCachableOperator +implements +Disposable +{ + private String driverClassname; + protected String datasourceName; + protected String user; + protected String password; + protected String dbURL; + protected Driver dbDriver; + protected boolean isConnected; + Properties dbProperties = new Properties(); + boolean bSupportsTransactions = false; + boolean hsqldb = false; + private boolean oldResourceTableName; + private boolean newResourceSchema =false; + + public DBOperator(RaplaContext context, Configuration config) throws RaplaException { + super( context ); + datasourceName = config.getChild("datasource").getValue(null); + // dont use datasource (we have to configure a driver ) + if ( datasourceName == null) + { + try + { + driverClassname = config.getChild("driver").getValue(); + dbURL = ConfigTools.resolveContext( config.getChild("url").getValue(), serviceManager); + getLogger().info("Data:" + dbURL); + } + catch (ConfigurationException e) + { + throw new RaplaException( e ); + } + dbProperties.setProperty("user", config.getChild("user").getValue("") ); + dbProperties.setProperty("password", config.getChild("password").getValue("") ); + hsqldb = config.getChild("hsqldb-shutdown").getValueAsBoolean( false ); + try + { + dbDriver = (Driver) getClass().getClassLoader().loadClass(driverClassname).newInstance(); + } + catch (ClassNotFoundException e) + { + throw new RaplaException("DB-Driver not found: " + driverClassname + + "\nCheck classpath!"); + } + catch (Exception e) + { + throw new RaplaException("Could not instantiate DB-Driver: " + driverClassname, e); + } + } + } + + public boolean supportsActiveMonitoring() { + return false; + } + + public Connection createConnection() throws RaplaException { + try { + Connection connection; + //datasource lookup + if ( datasourceName != null) + { + Context cxt = new InitialContext(); + DataSource ds = (DataSource) cxt.lookup("java:/comp/env/jdbc/" + datasourceName ); + if ( ds == null ) + { + throw new RaplaDBException("Datasource not found"); + } + connection = ds.getConnection(); + } + // or driver initialization + else + { + connection = dbDriver.connect(dbURL, dbProperties); + if (connection == null) + { + throw new RaplaDBException("No driver found for: " + dbURL + "\nCheck url!"); + } + } + + bSupportsTransactions = connection.getMetaData().supportsTransactions(); + if (bSupportsTransactions) + { + connection.setAutoCommit( false ); + } + else + { + getLogger().warn("No Transaction support"); + } + return connection; + } catch (Throwable ex) { + if ( ex instanceof RaplaDBException) + { + throw (RaplaDBException) ex; + } + ex.printStackTrace(); + throw new RaplaDBException("DB-Connection aborted",ex); + } + } + + public void connect() throws RaplaException { + if (isConnected()) + { + return; + } + loadData(); + isConnected = true; + } + + public void connect(String username,char[] password) throws RaplaException { + connect(); + } + + public boolean isConnected() { + return isConnected; + } + + final public void refresh() throws RaplaException { + getLogger().warn("Incremental refreshs are not supported"); + } + + public void refreshFull() throws RaplaException { + try + { + loadData(); + } + catch (Exception ex) + { + cache.clearAll(); + disconnect(); + if (ex instanceof RaplaException) + { + throw (RaplaException)ex; + } + else + { + throw new RaplaException(ex); + } + } + } + + public void forceDisconnect() { + try + { + disconnect(); + } + catch (Exception ex) + { + getLogger().error("Error during disconnect ", ex); + } + } + + public void disconnect() throws RaplaException + { + cache.clearAll(); + idTable.setCache( cache ); + // HSQLDB Special + if ( hsqldb ) + { + String sql ="SHUTDOWN COMPACT"; + try + { + Connection connection = createConnection(); + Statement statement = connection.createStatement(); + statement.executeQuery(sql); + } + catch (SQLException ex) + { + throw new RaplaException( ex); + } + } + isConnected = false; + fireStorageDisconnected(); + } + + public void dispose() + { + forceDisconnect(); + } + + final protected void loadData() throws RaplaException { + Connection connection = createConnection(); + try { + // Upgrade db if neccessary + { + ResultSet categoryTable = connection.prepareStatement("select * from CATEGORY" ).executeQuery(); + if ( categoryTable.getMetaData().getColumnCount() == 4) + { + getLogger().warn("Patching Database for table CATEGORY"); + try { + connection.prepareStatement("ALTER TABLE CATEGORY ADD COLUMN DEFINITION TEXT").execute(); + connection.commit(); + } catch (SQLException ex ) { + getLogger().warn("Category patch failed. Trying HDBSQL Syntax"); + connection.prepareStatement("ALTER TABLE CATEGORY ADD COLUMN DEFINITION VARCHAR").execute(); + connection.commit(); + } + getLogger().warn("CATEGORY patched!"); + } + if ( categoryTable.getMetaData().getColumnCount() == 5) + { + getLogger().warn("Patching Database for table CATEGORY (Category Order)"); + connection.prepareStatement("ALTER TABLE CATEGORY ADD COLUMN PARENT_ORDER INTEGER").execute(); + getLogger().warn("CATEGORY patched!"); + } + ResultSet eventTable = connection.prepareStatement("select * from EVENT" ).executeQuery(); + if ( eventTable.getMetaData().getColumnCount() == 5) { + getLogger().warn("Patching Database for table EVENT"); + connection.prepareStatement("ALTER TABLE EVENT ADD COLUMN LAST_CHANGED_BY INTEGER").execute(); + connection.commit(); + getLogger().warn("EVENT patched"); + } + checkForOldResourceTable( connection ); + + } + ResultSet set = connection.prepareStatement("select * from DYNAMIC_TYPE").executeQuery(); + if ( !set.next() ) { + getLogger().warn("No content in database! Creating new database"); + CachableStorageOperator sourceOperator = ( CachableStorageOperator) serviceManager.lookup(CachableStorageOperator.ROLE + "/file"); + sourceOperator.connect(); + setCache( sourceOperator.getCache()); + saveData(); + getLogger().warn("Database created!"); + } else { + cache.clearAll(); + idTable.setCache(cache); + readEverythingIntoCache( connection ); + idTable.setCache(cache); + + if ( getLogger().isDebugEnabled()) + getLogger().debug("Entities contextualized"); + + if ( getLogger().isDebugEnabled()) + getLogger().debug("All ConfigurationReferences resolved"); + } + } + catch (RaplaException ex) + { + throw ex; + } + catch (Exception ex) + { + throw new RaplaException( ex); + } + finally + { + close ( connection ); + } + } + + private void checkForOldResourceTable( Connection connection ) throws SQLException + { + try + { + ResultSet oldResourceTable = connection.getMetaData().getTables(null, null,"resource" , null); + while ( oldResourceTable.next()) + { + oldResourceTableName = true; + } + // If there is also a new Table use the new Table + ResultSet newResourceTable = connection.getMetaData().getTables(null, null,"rapla_resource" , null); + while ( newResourceTable.next()) + { + oldResourceTableName = false; + } + } + catch (SQLException ex) + { + oldResourceTableName = false; + getLogger().warn("Can't determine table schema for table, assuming new schema. Please upgrade database schema if neccessary."); + } + if ( oldResourceTableName ) + { + getLogger().warn("Using old resource table name: resource. Please rename to: rapla_resource"); + } + newResourceSchema = false; + if ( !oldResourceTableName) + { + try + { + ResultSet lastChangedColumn = connection.getMetaData().getColumns(null, null,"rapla_resource" , "creation_time"); + while ( true) + { + final boolean next = lastChangedColumn.next(); + if(!next) + { + break; + } + newResourceSchema = true; + } + } + catch (SQLException ex) + { + newResourceSchema = true; + getLogger().warn("Can't determine table schema for table, assuming new schema. Please upgrade database schema if neccessary."); + } + if ( !newResourceSchema ) + { + getLogger().warn("Using old resource table schema without timestamp. Please upgrade to new schema."); + } + } + } + public Object createIdentifier(RaplaType raplaType) throws RaplaException { + return idTable.createId(raplaType); + } + + public void dispatch(UpdateEvent evt) throws RaplaException { + evt = createClosure( evt ); + check(evt); + Connection connection = createConnection(); + try { + executeEvent(connection,evt); + if (bSupportsTransactions) { + getLogger().debug("Commiting"); + connection.commit(); + } + } catch (Exception ex) { + try { + if (bSupportsTransactions) { + connection.rollback(); + getLogger().error("Doing rollback"); + throw new RaplaDBException(getI18n().getString("error.rollback"),ex); + } else { + String message = getI18n().getString("error.no_rollback"); + getLogger().fatalError(message); + forceDisconnect(); + throw new RaplaDBException(message,ex); + } + } catch (SQLException sqlEx) { + String message = "Unrecoverable error while storing"; + getLogger().fatalError(message, sqlEx); + forceDisconnect(); + throw new RaplaDBException(message,sqlEx); + } + } finally { + close( connection ); + } + UpdateResult result = super.update(evt, true); + fireStorageUpdated(result); + + } + + /** + * @param evt + * @throws RaplaException + */ + protected void executeEvent(Connection connection,UpdateEvent evt) throws RaplaException, SQLException { + // create the writer + RaplaSQL raplaSQL = new RaplaSQL(createOutputContext(), oldResourceTableName, newResourceSchema); + // execute updates + Iterator it = evt.getStoreObjects().iterator(); + while (it.hasNext()) { + RefEntity entity = (RefEntity) it.next(); + raplaSQL.store( connection, entity); + } + + // execute removes + it = evt.getRemoveObjects().iterator(); + while (it.hasNext()) { + Object id = ((RefEntity) it.next()).getId(); + RefEntity entity = (RefEntity)cache.get(id); + if (entity != null) + raplaSQL.remove( connection, entity); + } + + } + + public void removeAll() throws RaplaException { + Connection connection = createConnection(); + try { + checkForOldResourceTable( connection ); + RaplaSQL raplaSQL = new RaplaSQL(createOutputContext(), oldResourceTableName, newResourceSchema); + if (!isConnected()) + createConnection(); + + raplaSQL.removeAll( connection ); + connection.commit(); + // do something here + getLogger().info("DB cleared"); + } + catch (SQLException ex) + { + throw new RaplaException(ex); + } + finally + { + close( connection ); + } + + } + public void saveData() throws RaplaException { + Connection connection = createConnection(); + try { + checkForOldResourceTable( connection ); + RaplaSQL raplaSQL = new RaplaSQL(createOutputContext(), oldResourceTableName, newResourceSchema); + getLogger().info("Creation of DB started"); + if (!isConnected()) + createConnection(); + + raplaSQL.removeAll( connection ); + raplaSQL.createAll( connection ); + connection.commit(); + // do something here + getLogger().info("DB Creation complete"); + } + catch (SQLException ex) + { + throw new RaplaException(ex); + } + finally + { + close( connection ); + } + } + + static private void close(Connection connection) throws RaplaException + { + try + { + connection.close(); + } + catch (SQLException e) + { + throw new RaplaException("Can't close connection to database ", e); + } + } + + protected boolean readEverythingIntoCache(Connection connection) throws RaplaException, IOException, SQLException { + EntityStore entityStore = new EntityStore(null, cache.getSuperCategory()); + + RaplaSQL raplaSQL = new RaplaSQL(createInputContext(entityStore), oldResourceTableName, newResourceSchema); + raplaSQL.loadAll( connection ); + resolveEntities( entityStore.getList().iterator(), entityStore ); + cache.putAll( entityStore.getList()); + for (Iterator it = cache.getIterator(User.TYPE);it.hasNext();) + { + RefEntity user = ((RefEntity)it.next()); + String password = entityStore.getPassword( user.getId()); + cache.putPassword(user.getId(), password); + } + return false; + } + + protected RaplaDefaultContext createInputContext( EntityStore store) throws RaplaException { + RaplaDefaultContext inputContext = new IOContext().createInputContext(serviceManager, store,idTable); + RaplaInput xmlAdapter = new RaplaInput(getLogger().getChildLogger("reading")); + inputContext.put(RaplaInput.class.getName(),xmlAdapter); + return inputContext; + + } + + protected RaplaDefaultContext createOutputContext() throws RaplaException { + RaplaDefaultContext outputContext = new IOContext().createOutputContext(serviceManager, cache,true,false); + return outputContext; + + } + +} diff --git a/Rapla/src/org/rapla/storage/dbsql/EntityStorage.java b/Rapla/src/org/rapla/storage/dbsql/EntityStorage.java new file mode 100644 index 0000000..f23530a --- /dev/null +++ b/Rapla/src/org/rapla/storage/dbsql/EntityStorage.java @@ -0,0 +1,366 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.storage.dbsql; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.Random; +import java.util.StringTokenizer; + +import org.apache.avalon.framework.logger.Logger; +import org.rapla.entities.Category; +import org.rapla.entities.EntityNotFoundException; +import org.rapla.entities.RaplaType; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.storage.EntityResolver; +import org.rapla.entities.storage.RefEntity; +import org.rapla.entities.storage.internal.SimpleIdentifier; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaContextException; +import org.rapla.framework.RaplaException; +import org.rapla.framework.RaplaLocale; +import org.rapla.storage.LocalCache; +import org.rapla.storage.impl.EntityStore; +import org.rapla.storage.xml.PreferenceReader; +import org.rapla.storage.xml.PreferenceWriter; +import org.rapla.storage.xml.RaplaInput; +import org.rapla.storage.xml.RaplaXMLReader; +import org.rapla.storage.xml.RaplaXMLWriter; + +abstract class EntityStorage implements Storage { + Random random = new Random(); + + String insertSql; + String updateSql; + String deleteSql; + String selectSql; + String deleteAllSql; + String searchForIdSql; + + RaplaContext sm; + LocalCache cache; + private EntityStore entityStore; + private RaplaLocale raplaLocale; + + Collection subStores = new ArrayList(); + Connection con; + int lastParameterIndex; /** first paramter is 1 */ + final String tableName; + + Logger logger; + + protected EntityStorage( RaplaContext context, String table,String[] entries) throws RaplaException { + this.sm = context; + if ( context.has( EntityStore.class.getName())) + { + this.entityStore = (EntityStore) context.lookup( EntityStore.class.getName()); + } + if ( context.has( LocalCache.class.getName())) + { + this.cache = (LocalCache) context.lookup( LocalCache.class.getName()); + } + this.raplaLocale = (RaplaLocale) sm.lookup(RaplaLocale.ROLE); + logger = (Logger) context.lookup( Logger.class.getName()); + lastParameterIndex = entries.length; + tableName = table; + createSQL(table,entries); + if (getLogger().isDebugEnabled()) { + getLogger().debug(insertSql); + getLogger().debug(updateSql); + getLogger().debug(deleteSql); + getLogger().debug(selectSql); + getLogger().debug(deleteAllSql); + } + } + + protected Logger getLogger() { + return logger; + } + + + protected void createSQL(String table,String[] entries) { + String idString = entries[0]; + selectSql = "select " + getEntryList(entries) + " from " + table ; + deleteSql = "delete from " + table + " where " + idString + "= ?"; + insertSql = "insert into " + table + " (" + getEntryList(entries) + ") values (" + getMarkerList(entries.length) + ")"; + updateSql = "update " + table + " set " + getUpdateList(entries) + " where " + idString + "= ?"; + deleteAllSql = "delete from " + table; + searchForIdSql = "select id from " + table + " where id = ?"; + } + + protected void addSubStorage(Storage subStore) { + subStores.add(subStore); + } + + public void setConnection(Connection con) { + this.con= con; + Iterator it = subStores.iterator(); + while (it.hasNext()) { + ((Storage) it.next()).setConnection(con); + } + } + + public Locale getLocale() { + return raplaLocale.getLocale(); + } + public java.sql.Date getSQLDate(Calendar cal) { + return new java.sql.Date(cal.getTime().getTime()); + } + + public java.sql.Time getSQLTime(Calendar cal) { + return new java.sql.Time(cal.getTime().getTime()); + } + + private String getEntryList(String[] entries) { + StringBuffer buf = new StringBuffer(); + for (int i=0;i ordering2) + { + return 1; + } + RefEntity e1 = (RefEntity) o1; + RefEntity e2 = (RefEntity) o2; + if (e1.hashCode() > e2.hashCode()) + { + return -1; + } + else + { + return 1; + } + } + + } + ); + + public CategoryStorage(RaplaContext context) throws RaplaException { + super(context,Category.TYPE, "CATEGORY",new String[] {"ID","PARENT_ID","CATEGORY_KEY","LABEL","DEFINITION", "PARENT_ORDER"}); + } + + protected void write(PreparedStatement stmt,RefEntity entity) throws SQLException, RaplaException { + Category root = getSuperCategory(); + if ( entity.equals( root )) + return; + Category category = (Category) entity; + String name = category.getName( getLocale() ); + int id = getId(entity); + int parentId = getId((RefEntity)category.getParent()); + if ( getLogger().isDebugEnabled()) + getLogger().debug("Inserting Category " + name); + stmt.setInt(1, id); + if ( root.equals( category.getParent())) + { + stmt.setObject(2, null, Types.INTEGER); + } + else + { + stmt.setInt(2, parentId); + } + int order = getOrder( category); + String xml = getXML( category ); + stmt.setString(3, category.getKey()); + stmt.setString(4, name ); + stmt.setString(5, xml); + stmt.setInt( 6, order); + stmt.executeUpdate(); + } + + + + private int getOrder( Category category ) + { + Category parent = category.getParent(); + if ( parent == null) + { + return 0; + } + Category[] childs = parent.getCategories();; + for ( int i=0;i 10 ) + { + category = ((CategoryReader)processXML( Category.TYPE, xml )).getCurrentCategory(); + //cache.remove( category ); + category.setId( id ); + } + else + { + // for compatibility with version prior to 1.0rc1 + category = new CategoryImpl(); + category.setId( id); + category.setKey( key ); + category.getName().setName( getLocale().getLanguage(), name); + } + + put( category ); + + int parentIdInt = rset.getInt(2); + orderMap.put( category, new Integer( order)); + if ( !rset.wasNull() ) + { + categoriesWithoutParent.put( category, new SimpleIdentifier(Category.TYPE, parentIdInt)); + } + else + { + categoriesWithoutParent.put( category, null ); + } + } + + public void loadAll() throws RaplaException, SQLException { + categoriesWithoutParent.clear(); + super.loadAll(); + // then we rebuild the hirarchy + Iterator it = categoriesWithoutParent.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = (Map.Entry)it.next(); + Object parentId = entry.getValue(); + Category category = (Category) entry.getKey(); + Category parent; + Assert.notNull( category ); + if ( parentId != null) { + parent = (Category) resolve( parentId ); + Assert.notNull( parent ); + } else { + parent = getSuperCategory(); + Assert.notNull( parent ); + } + + parent.addCategory( category ); + } + } +} + +class AllocatableStorage extends RaplaTypeStorage { + Map classificationMap = new HashMap(); + Map allocatableMap = new HashMap(); + AttributeValueStorage resourceAttributeStorage; + PermissionStorage permissionStorage; + boolean newResourceSchema; + + public AllocatableStorage(RaplaContext context, boolean oldResourceTable, boolean newResourceSchema ) throws RaplaException { + super(context,Allocatable.TYPE + , oldResourceTable ? + "RESOURCE" + : "RAPLA_RESOURCE" + , newResourceSchema ? + new String [] + {"ID","TYPE_KEY","IGNORE_CONFLICTS","OWNER_ID","CREATION_TIME","LAST_CHANGED","LAST_CHANGED_BY"} + : new String [] + {"ID","TYPE_KEY","IS_PERSON","IGNORE_CONFLICTS"}); + resourceAttributeStorage = new AttributeValueStorage(context,"RESOURCE_ATTRIBUTE_VALUE", "RESOURCE_ID",classificationMap); + permissionStorage = new PermissionStorage( context, allocatableMap); + this.newResourceSchema = newResourceSchema; + addSubStorage(resourceAttributeStorage); + addSubStorage(permissionStorage ); + } + + protected void write(PreparedStatement stmt,RefEntity entity) throws SQLException,RaplaException { + AllocatableImpl allocatable = (AllocatableImpl) entity; + if ( getLogger().isDebugEnabled()) + getLogger().debug("Inserting Allocatable " + allocatable.getName(getLocale())); + int id = getId( entity ); + String typeKey = allocatable.getClassification().getType().getElementKey(); + stmt.setInt(1, id ); + stmt.setString(2, typeKey ); + if (newResourceSchema) + { + stmt.setInt(3, allocatable.isHoldBackConflicts()? 1:0); + org.rapla.entities.Timestamp timestamp = allocatable; + Date creationTime = timestamp.getCreateTime(); + Date lastModified = timestamp.getLastChangeTime(); + final RefEntity owner = (RefEntity) allocatable.getOwner(); + if ( owner != null) + { + int userId = getId( owner ); + stmt.setInt(4, userId ); + } + else + { + stmt.setObject(4, null, Types.INTEGER); + } + if ( creationTime != null) + { + stmt.setTimestamp( 5, new java.sql.Timestamp( creationTime.getTime() )); + } + else + { + stmt.setObject(5, null, Types.TIMESTAMP); + } + if ( lastModified != null) + { + stmt.setTimestamp( 6, new java.sql.Timestamp( lastModified.getTime() )); + } + else + { + stmt.setObject(6, null, Types.TIMESTAMP); + } + User lastChangedBy = timestamp.getLastChangedBy(); + if ( lastChangedBy != null) { + int lastChangedById = getId( (RefEntity) lastChangedBy ); + stmt.setInt( 7, lastChangedById); + } else { + stmt.setObject(7, null, Types.INTEGER); + } + } + else + { + stmt.setInt(3, allocatable.isPerson()? 1:0); + stmt.setInt(4, allocatable.isHoldBackConflicts()? 1:0); + + } + + stmt.executeUpdate(); + } + + protected void load(ResultSet rset) throws SQLException { + int idInt = rset.getInt(1); + String typeKey = rset.getString(2); + boolean ignoreConflicts = rset.getInt( 3 ) == 1; + + final Date createDate; + final Date lastChanged; + + if ( newResourceSchema) + { + java.sql.Timestamp creationTime = rset.getTimestamp( 5 ); + java.sql.Timestamp lastModified = rset.getTimestamp( 6 ); + createDate = creationTime != null ? new Date( creationTime.getTime()) : null; + lastChanged = lastModified != null ? new Date( lastModified.getTime()) : null; + } + else + { + createDate = null; + lastChanged = null; + } + AllocatableImpl allocatable = new AllocatableImpl(createDate, lastChanged); + allocatable.setId( new SimpleIdentifier( allocatable.getRaplaType() , idInt)); + allocatable.setHoldBackConflicts( ignoreConflicts ); + DynamicType type = getDynamicType(typeKey ); + if ( type == null) + { + getLogger().error("Allocatable with id " + idInt + " has an unknown type " + typeKey + ". Try ignoring it"); + return; + } + if ( newResourceSchema) + { + int userInt = rset.getInt( 4 ); + if ( !rset.wasNull()) { + User user = (User)get( new SimpleIdentifier(User.TYPE, userInt)); + allocatable.setOwner( user ); + } + int lastModfiedByIdInt = rset.getInt( 7); + if ( !rset.wasNull()) { + User lastModifiedBy = (User)get( new SimpleIdentifier(User.TYPE, lastModfiedByIdInt)); + if ( lastModifiedBy != null) + { + allocatable.setLastChangedBy( lastModifiedBy ); + } + + } + } + Classification classification = type.newClassification(); + allocatable.setClassification( classification ); + classificationMap.put( new Integer(idInt), classification ); + allocatableMap.put( new Integer(idInt), allocatable); + put( allocatable ); + } + + + + public void loadAll() throws RaplaException, SQLException { + classificationMap.clear(); + super.loadAll(); + } +} + +class ReservationStorage extends RaplaTypeStorage { + Map classificationMap = new HashMap(); + Map reservationMap = new HashMap(); + AttributeValueStorage attributeValueStorage; + public ReservationStorage(RaplaContext context) throws RaplaException { + super(context,Reservation.TYPE, "EVENT",new String [] {"ID","TYPE_KEY","OWNER_ID","CREATION_TIME","LAST_CHANGED","LAST_CHANGED_BY"}); + attributeValueStorage = new AttributeValueStorage(context,"EVENT_ATTRIBUTE_VALUE","EVENT_ID", classificationMap); + addSubStorage(attributeValueStorage); + } + + protected void write(PreparedStatement stmt,RefEntity entity) throws SQLException,RaplaException { + Reservation event = (Reservation) entity; + if ( getLogger().isDebugEnabled()) + getLogger().debug("Storing Reservation " + event.getName(getLocale())); + int id = getId( entity ); + String typeKey = event.getClassification().getType().getElementKey(); + int userId = getId( (RefEntity) event.getOwner() ); + stmt.setInt(1, id ); + stmt.setString(2, typeKey ); + stmt.setInt(3, userId ); + org.rapla.entities.Timestamp timestamp = event; + Date creationTime = timestamp.getCreateTime(); + Date lastModified = timestamp.getLastChangeTime(); + if ( creationTime != null) + { + stmt.setTimestamp( 4, new java.sql.Timestamp( creationTime.getTime() )); + } + else + { + stmt.setObject(4, null, Types.TIMESTAMP); + } + if ( lastModified != null) + { + stmt.setTimestamp( 5, new java.sql.Timestamp( lastModified.getTime() )); + } + else + { + stmt.setObject(5, null, Types.TIMESTAMP); + } + User lastChangedBy = timestamp.getLastChangedBy(); + if ( lastChangedBy != null) { + int lastChangedById = getId( (RefEntity) lastChangedBy ); + stmt.setInt( 6, lastChangedById); + } else { + stmt.setObject(6, null, Types.INTEGER); + } + stmt.executeUpdate(); + } + + protected void load(ResultSet rset) throws SQLException { + int idInt = rset.getInt(1); + String typeKey = rset.getString(2); + int userInt = rset.getInt(3); + java.sql.Timestamp creationTime = rset.getTimestamp( 4 ); + java.sql.Timestamp lastModified = rset.getTimestamp( 5 ); + final Date createDate = creationTime != null ? new Date( creationTime.getTime()) : null; + final Date lastChanged = lastModified != null ? new Date( lastModified.getTime()) : null; + ReservationImpl event = new ReservationImpl(createDate, lastChanged); + event.setId( new SimpleIdentifier(Reservation.TYPE, idInt)); + DynamicType type = getDynamicType(typeKey ); + User user = (User)get( new SimpleIdentifier(User.TYPE, userInt)); + if ( user == null || type == null) + { + getLogger().warn("Reservation with id " + idInt + " has no type or owner. It will be ignored"); + return; + } + event.setOwner( user ); + int lastModfiedByIdInt = rset.getInt( 6); + if ( !rset.wasNull()) { + User lastModifiedBy = (User)get( new SimpleIdentifier(User.TYPE, lastModfiedByIdInt)); + if ( lastModifiedBy != null) + { + event.setLastChangedBy( lastModifiedBy ); + } + } + + Classification classification = type.newClassification(); + event.setClassification( classification ); + classificationMap.put( new Integer(idInt), classification ); + reservationMap.put( new Integer(idInt), event ); + put( event ); + } + + public void loadAll() throws RaplaException, SQLException { + classificationMap.clear(); + super.loadAll(); + } +} + +/** This class should only be used within the ResourceStorage class*/ +class AttributeValueStorage extends EntityStorage { + Map classificationMap; + final String foreignKeyName; + public AttributeValueStorage(RaplaContext context,String tablename, String foreignKeyName, Map classificationMap) throws RaplaException { + // FIXME: DB field with name 'VALUE' is not allowed in MS-Access. + // But rename of field makes old versions incompatible + super(context, tablename, new String[]{foreignKeyName, "ATTRIBUTE_KEY", "VALUE"}); + this.foreignKeyName = foreignKeyName; + this.classificationMap = classificationMap; + } + + protected void write(PreparedStatement stmt,RefEntity classifiable) throws EntityNotFoundException, SQLException { + int id = getId(classifiable); + Classification classification = ((Classifiable) classifiable).getClassification();; + Attribute[] attributes = classification.getAttributes(); + for (int i=0;i= 0) { + stmt.setInt(6, number); + } else { + stmt.setObject(6, null, Types.INTEGER); + } + Date repeatingEnd = repeating.getEnd(); + if ( repeatingEnd != null) { + stmt.setObject(7, new Timestamp( repeatingEnd.getTime())); + } else + { + stmt.setObject(7, null, Types.TIMESTAMP); + } + int interval = repeating.getInterval(); + stmt.setInt(8, interval); + } + stmt.executeUpdate(); + } + + protected void load(ResultSet rset) throws SQLException, EntityNotFoundException { + int idInt = rset.getInt(1); + int parentId = rset.getInt( 2 ); + Reservation event; + try { + event = (Reservation) resolve( new SimpleIdentifier( Reservation.TYPE, parentId)); + } + catch ( EntityNotFoundException ex) + { + getLogger().warn("Could not find reservation object with id "+ parentId + " for appointment with id " + idInt ); + return; + } + Date start = new Date(rset.getTimestamp(3).getTime()); + Date end = new Date(rset.getTimestamp(4).getTime()); + boolean wholeDayAppointment = start.getTime() == DateTools.cutDate( start.getTime()) && end.getTime() == DateTools.cutDate( end.getTime()); + AppointmentImpl appointment = new AppointmentImpl(start, end); + appointment.setId( new SimpleIdentifier(Appointment.TYPE, idInt)); + appointment.setWholeDays( wholeDayAppointment); + event.addAppointment( appointment ); + String repeatingType = rset.getString( 5 ); + if ( !rset.wasNull() ) { + appointment.setRepeatingEnabled( true ); + Repeating repeating = appointment.getRepeating(); + repeating.setType( RepeatingType.findForString( repeatingType ) ); + + java.sql.Timestamp repeatingEnd = rset.getTimestamp( 7 ); + if ( !rset.wasNull() ) { + repeating.setEnd( new Date(repeatingEnd.getTime())); + } else { + int number = rset.getInt( 6); + if ( !rset.wasNull()) { + repeating.setNumber( number); + } else { + repeating.setEnd( null ); + } + } + + int interval = rset.getInt( 8); + if ( !rset.wasNull()) + repeating.setInterval( interval); + } + put( appointment ); + } +} + + +class AllocationStorage extends EntityStorage { + + public AllocationStorage(RaplaContext context) throws RaplaException { + super(context,"ALLOCATION",new String [] {"APPOINTMENT_ID", "RESOURCE_ID"}); + } + + protected void write(PreparedStatement stmt, RefEntity entity) throws SQLException, RaplaException { + int appointmentId = getId( entity); + Appointment appointment = (Appointment) entity; + Reservation event = appointment.getReservation(); + Allocatable[] allocatables = event.getAllocatables(); + for (int j=0;j newAppointments.length ) { + event.setRestriction( allocatable, newAppointments ); + } else { + event.setRestriction( allocatable, new Appointment[] {} ); + } + } + + public void delete( RefEntity entity) throws SQLException { + int appointmentId = getId( entity ) ; + executeBatchedStatement(con, "DELETE FROM " + tableName + " WHERE APPOINTMENT_ID = " + appointmentId); + } + + + } + +class AppointmentExceptionStorage extends EntityStorage { + public AppointmentExceptionStorage(RaplaContext context) throws RaplaException { + super(context,"APPOINTMENT_EXCEPTION",new String [] {"APPOINTMENT_ID","EXCEPTION_DATE"}); + } + + + protected void write(PreparedStatement stmt, RefEntity entity) throws SQLException, RaplaException { + int appointmentId = getId( entity); + Appointment appointment = (Appointment) entity; + Repeating repeating = appointment.getRepeating(); + if ( repeating == null) { + return; + } + Date[] exceptions = repeating.getExceptions(); + for ( int i=0;i< exceptions.length;i++) { + java.sql.Timestamp exception = new java.sql.Timestamp( exceptions[i].getTime()); + stmt.setInt( 1, appointmentId ); + stmt.setTimestamp( 2, exception ); + stmt.executeUpdate(); + } + } + + protected void load(ResultSet rset) throws SQLException, RaplaException { + int appointmentId = rset.getInt( 1); + Appointment appointment; + try { + appointment = (Appointment)resolve( new SimpleIdentifier( Appointment.TYPE,appointmentId)); + } + catch ( EntityNotFoundException ex) + { + getLogger().warn("Could not find appointment with id "+ appointmentId + " for the specified exception. Ignoring." ); + return; + } + + Repeating repeating = appointment.getRepeating(); + if ( repeating != null) { + Date date = new Date( rset.getTimestamp( 2 ).getTime()); + repeating.addException( date ); + } + } + + + + public void delete( RefEntity entity) throws SQLException { + int appointmentId = getId( entity); + executeBatchedStatement(con, "DELETE FROM " + tableName + " WHERE APPOINTMENT_ID = " + appointmentId); + } + +} + +class DynamicTypeStorage extends RaplaTypeStorage { + + public DynamicTypeStorage(RaplaContext context) throws RaplaException { + super(context, DynamicType.TYPE,"DYNAMIC_TYPE", + new String [] {"ID","TYPE_KEY","DEFINITION"}); + } + + protected void write(PreparedStatement stmt,RefEntity entity) throws SQLException, RaplaException { + stmt.setInt(1,getId(entity)); + DynamicType type = (DynamicType) entity; + stmt.setString(2, type.getElementKey()); + stmt.setString(3, getXML( type) ); + stmt.executeUpdate(); + } + + protected void load(ResultSet rset) throws SQLException,RaplaException { + String xml = getString(rset,3); + processXML( DynamicType.TYPE, xml ); + } + +} + + +class PreferenceStorage extends RaplaTypeStorage { + + public PreferenceStorage(RaplaContext context) throws RaplaException { + super(context,Preferences.TYPE,"PREFERENCE", + new String [] {"USER_ID","ROLE","STRING_VALUE","XML_VALUE"}); + } + + protected void write(PreparedStatement stmt, RefEntity entity) throws SQLException, RaplaException { + PreferencesImpl preferences = (PreferencesImpl) entity; + User user = preferences.getOwner(); + if ( user == null) { + stmt.setObject(1, null, Types.INTEGER); + } else { + stmt.setInt(1,getId( (RefEntity) user)); + } + Iterator it = preferences.getPreferenceEntries(); + while (it.hasNext()) { + String role = (String) it.next(); + Object entry = preferences.getEntry(role); + stmt.setString( 2, role); + if ( entry instanceof String) { + stmt.setString( 3, (String) entry); + stmt.setString( 4, null); + } else { + //System.out.println("Role " + role + " CHILDREN " + conf.getChildren().length); + String xml = getXML( (RaplaObject)entry); + stmt.setString( 3, null); + stmt.setString( 4, xml); + } + stmt.executeUpdate(); + } + } + + public void save(RefEntity entity) throws SQLException,RaplaException { + delete( entity ); + insert( entity ); + } + + protected void load(ResultSet rset) throws SQLException, RaplaException { + //findPreferences + //check if value set + // yes read value + // no read xml + + int userIdAsInt = rset.getInt(1); + User owner = null; + Object preferenceId; + if ( !rset.wasNull() ){ + Object userId = new SimpleIdentifier( User.TYPE, userIdAsInt ); + owner = (User) get( userId ); + preferenceId = new SimpleIdentifier( Preferences.TYPE, userIdAsInt ); + } else { + preferenceId = new SimpleIdentifier( Preferences.TYPE, 0 ); + } + PreferencesImpl preferences = (PreferencesImpl) get( preferenceId ); + if ( preferences == null) { + preferences = new PreferencesImpl(); + preferences.setId(preferenceId); + preferences.setOwner(owner); + put( preferences ); + } + String configRole = getString( rset, 2); + String value = rset.getString( 3 ); + if ( !rset.wasNull()) { + preferences.putEntry(configRole, value); + } else { + String xml = rset.getString( 4 ); + + PreferenceReader contentHandler = (PreferenceReader) processXML( Preferences.TYPE, xml ); + try { + RaplaObject type = contentHandler.getChildType(); + preferences.putEntry(configRole, type); + } catch (SAXException ex) { + throw new RaplaException (ex); + } + } + } + + + + public void delete( RefEntity entity) throws SQLException { + PreferencesImpl preferences = (PreferencesImpl) entity; + User user = preferences.getOwner(); + if ( user != null) { + int userId = getId( (RefEntity) user ) ; + executeBatchedStatement(con, "DELETE FROM " + tableName + " WHERE USER_ID = " + userId); + } else { + executeBatchedStatement(con, "DELETE FROM " + tableName + " WHERE USER_ID = null"); + } + } + + } + +class UserStorage extends RaplaTypeStorage { + UserGroupStorage groupStorage; + public UserStorage(RaplaContext context) throws RaplaException { + super( context,User.TYPE, "RAPLA_USER", + new String [] {"ID","USERNAME","PASSWORD","NAME","EMAIL","ISADMIN"}); + groupStorage = new UserGroupStorage( context ); + addSubStorage( groupStorage ); + } + + protected void write(PreparedStatement stmt,RefEntity entity) throws SQLException, RaplaException { + User user = (User) entity; + if ( getLogger().isDebugEnabled()) + getLogger().debug("Inserting User " + user.getUsername()); + stmt.setInt(1,getId(entity)); + stmt.setString(2,user.getUsername()); + String password = cache.getPassword(entity.getId()); + stmt.setString(3,password); + stmt.setString(4,user.getName()); + stmt.setString(5,user.getEmail()); + stmt.setInt(6,user.isAdmin()?1:0); + stmt.executeUpdate(); + } + + protected void load(ResultSet rset) throws SQLException, RaplaException { + int idAsInt = rset.getInt(1); + String username = getString(rset,2); + String name = getString(rset,4); + String email = getString(rset,5); + boolean isAdmin = rset.getInt(6) == 1; + UserImpl user = new UserImpl(); + Object userId = new SimpleIdentifier(User.TYPE, idAsInt ); + user.setId( userId ); + user.setUsername( username ); + user.setName( name ); + user.setEmail( email ); + user.setAdmin( isAdmin ); + String password = getString(rset,3); + if ( !rset.wasNull()) { + putPassword(userId,password); + } + put(user); + } + + +} + +class UserGroupStorage extends EntityStorage { + public UserGroupStorage(RaplaContext context) throws RaplaException { + super(context,"RAPLA_USER_GROUP", new String [] {"USER_ID","CATEGORY_ID"}); + } + + public void save(RefEntity entity) throws SQLException,RaplaException { + delete( entity ); + insert( entity ); + } + + protected void write(PreparedStatement stmt, RefEntity entity) throws SQLException, RaplaException { + int userId = getId( entity); + User user = (User) entity; + stmt.setInt(1, userId); + Category[] categories = user.getGroups(); + for (int i=0;i getAppointments(final User user,final Date start,final Date end) throws RaplaException { + checkConnected(); + return cache.getAppointments(user,start,end); + } + + public List getReservations(final User user,final Date start,final Date end) throws RaplaException { + return cache.getReservations(user,start,end); + } + + public Category getSuperCategory() { + return cache.getSuperCategory(); + } + + public void addStorageUpdateListener(StorageUpdateListener listener) { + storageUpdateListeners.add(listener); + } + + public void removeStorageUpdateListener(StorageUpdateListener listener) { + storageUpdateListeners.remove(listener); + } + + public StorageUpdateListener[] getStorageUpdateListeners() { + return (StorageUpdateListener[])storageUpdateListeners.toArray(new StorageUpdateListener[]{}); + } + + protected void fireStorageUpdated(final UpdateResult evt) { + StorageUpdateListener[] listeners = getStorageUpdateListeners(); + for (int i = 0;icheck() +
  • update()
  • +
  • fireStorageUpdate()
  • +
  • fireTriggerEvents()
  • + You should not call dispatch directly from the client. + Use storeObjects and removeObjects instead. + */ + public void dispatch(final UpdateEvent evt) throws RaplaException { + UpdateEvent closure = createClosure( evt); + check( closure ); + UpdateResult result = update( closure, true); + fireStorageUpdated( result ); + } + + /** performs Integrety constraints check */ + protected void check(final UpdateEvent evt) throws RaplaException { + Set storeObjects = new HashSet(evt.getStoreObjects()); + Set removeObjects = new HashSet(evt.getRemoveObjects()); + checkUnique( storeObjects ); + checkConsistency( storeObjects ); + checkReferences( storeObjects ); + checkNoDependencies( removeObjects, storeObjects ); + checkVersions( storeObjects ); + } + + + /** Writes the UpdateEvent in the cache */ + protected UpdateResult update(final UpdateEvent evt, final boolean increaseVersion) throws RaplaException { + User user = null; + if ( evt.getUserId() != null) { + user = (User) resolveId( evt.getUserId() ); + } + UpdateResult result = new UpdateResult( user ); + + List resolvableEntities = new ArrayList(); + HashMap oldEntities = new HashMap(); + // First make a copy of the old entities + for (RefEntity entity: evt.getStoreObjects()) + { + RefEntity persistantEntity = findPersistantVersion( entity ); + + // do nothing, because the persitantVersion is the same as the stored + if ( persistantEntity == entity ) { + continue; + } + + if (persistantEntity != null ) { + if (getLogger().isDebugEnabled()) + getLogger().debug("Storing old: " + entity); + + RefEntity oldEntity = (RefEntity)persistantEntity.deepClone(); + oldEntities.put( persistantEntity, oldEntity ); + } + } + // Then update the new entities + for (RefEntity entity : evt.getStoreObjects()) + { + if ( increaseVersion ) + increaseVersion( entity ); + RefEntity persistantEntity = findPersistantVersion( entity ); + + // do nothing, because the persitantVersion is always ReadOnly + if ( persistantEntity == entity ) { + continue; + } + + if (persistantEntity != null ) { + if (getLogger().isDebugEnabled()) + getLogger().debug("Changing: " + entity); + + ((Mementable)persistantEntity).copy(entity); + } else { + if (getLogger().isDebugEnabled()) + getLogger().debug("Adding entity: " + entity); + + persistantEntity = (RefEntity)entity.deepClone(); + } + ((SimpleEntity)persistantEntity).setReadOnly( true ); + cache.put(persistantEntity); + resolvableEntities.add( persistantEntity ); + } + + /** we need to update every reference in the stored entity. + * So that the references in the persistant entities always point to persistant entities and never + * to local working copies*/ + for (Iterator it = resolvableEntities.iterator(); it.hasNext();) + { + SimpleEntity persistantEntity = (SimpleEntity) it.next(); + persistantEntity.resolveEntities( getCache()); + + RefEntity newEntity = (RefEntity)persistantEntity.deepClone(); + RefEntity oldEntity = (RefEntity)oldEntities.get( persistantEntity ); + if ( oldEntity != null) { + result.addOperation( new UpdateResult.Change( persistantEntity, newEntity, oldEntity)); + } else { + result.addOperation( new UpdateResult.Add( persistantEntity, newEntity)); + } + } + + for (RefEntity entity : evt.getRemoveObjects()) + { + if ( increaseVersion ) + increaseVersion( entity ); + RefEntity persistantVersion = findPersistantVersion( entity ); + + if (persistantVersion != null) { + cache.remove( persistantVersion ); + ((SimpleEntity)persistantVersion).setReadOnly( true ); + result.addOperation( new UpdateResult.Remove( persistantVersion)); + } + } + return result; + } + + /** Create a closure for all objects that should be updated. The closure + contains all objects that are sub-entities of the entities and all objects + and all other objects that are affected by the update: e.g. + Classifiables when the DynamicType changes. + The method will recursivly proceed with all discovered objects. + */ + protected UpdateEvent createClosure(final UpdateEvent evt) throws RaplaException { + UpdateEvent closure = evt.clone(); + Iterator it = evt.getStoreObjects().iterator(); + while (it.hasNext()) + { + addStoreOperationsToClosure(closure, it.next()); + } + + it = evt.getRemoveObjects().iterator(); + while (it.hasNext()) + { + addRemoveOperationsToClosure(closure, it.next() ); + } + return closure; + } + + private void increaseVersion(RefEntity e) throws RaplaException { + e.setVersion (e.getVersion () + 1); + if (getLogger().isDebugEnabled()) + getLogger().debug("Increasing Version for " + e + " to " + e.getVersion()); + } + + private void addStoreOperationsToClosure(UpdateEvent evt,RefEntity entity) throws RaplaException { + if (getLogger().isDebugEnabled() && !evt.getStoreObjects().contains(entity)) + getLogger().debug("Adding " + entity + " to store closure"); + evt.putStore(entity); + if (DynamicType.TYPE.equals( entity.getRaplaType())) { + DynamicType dynamicType = (DynamicType) entity; + addChangedDynamicTypeDependant(evt, dynamicType, false); + } + + Iterator it = entity.getSubEntities(); + + while (it.hasNext()) { + RefEntity subEntity = (RefEntity)it.next(); + addStoreOperationsToClosure(evt,subEntity); + } + + it = getRemovedEntities(entity).iterator(); + while (it.hasNext()) { + addRemoveOperationsToClosure(evt,(RefEntity)it.next()); + } + } + + protected void addChangedDynamicTypeDependant(UpdateEvent evt,DynamicType type, boolean toRemove) throws RaplaException { + Iterator it = getReferencingEntities((RefEntity)type).iterator(); + while (it.hasNext()) { + Object next = it.next(); + if ( !(next instanceof DynamicTypeDependant)) + { + continue; + } + DynamicTypeDependant dependant = (DynamicTypeDependant) next; + // Classifiables need update? + if ( ! dependant.needsChange(type) &&!toRemove ) + continue; + if (getLogger().isDebugEnabled()) + getLogger().debug("Classifiable " + dependant + " needs change!"); + // Classifiables are allready on the store list + if ( evt.getStoreObjects().contains( dependant ) ) + { + dependant = (DynamicTypeDependant)evt.findEntity( (RefEntity) dependant ); + } + // no, then create a clone of the classfiable object and add to list + else + { + User user = null; + if ( evt.getUserId() != null) + { + user = (User) resolveId( evt.getUserId()); + } + dependant = (DynamicTypeDependant)editObject( (Entity) dependant, user); + addStoreOperationsToClosure(evt,(RefEntity) dependant); + } + if ( toRemove) + { + try + { + dependant.commitRemove(type); + } + catch (CannotExistWithoutTypeException ex) + { + // getLogger().warn(ex.getMessage(),ex); + } + } + else + { + dependant.commitChange(type); + } + } + } + + private void addRemoveOperationsToClosure(UpdateEvent evt,RefEntity entity) throws RaplaException { + if (getLogger().isDebugEnabled() && !evt.getRemoveObjects().contains(entity)) + getLogger().debug("Adding " + entity + " to remove closure"); + evt.putRemove(entity); + + if (DynamicType.TYPE.equals( entity.getRaplaType())) { + DynamicType dynamicType = (DynamicType) entity; + addChangedDynamicTypeDependant(evt, dynamicType, true); + } + + // add the subentities + Iterator it = entity.getSubEntities(); + while (it.hasNext()) { + addRemoveOperationsToClosure(evt,(RefEntity)it.next()); + } + + // And also add the SubEntities that have been removed, before storing + it = getRemovedEntities(entity).iterator(); + while (it.hasNext()) { + addRemoveOperationsToClosure(evt,(RefEntity)it.next()); + } + + // If entity is a user, remove the preference object + if (User.TYPE.equals( entity.getRaplaType() )) { + Preferences preferences = cache.getPreferences((User) entity); + if (preferences != null) + addRemoveOperationsToClosure(evt,(RefEntity)preferences); + } + + + } + + private Collection getRemovedEntities( RefEntity entity ) { + RefEntity original = findPersistantVersion( entity ); + List result = null; + if ( original != null ) { + Iterator it = original.getSubEntities(); + while ( it.hasNext()) { + RefEntity subEntity = (RefEntity) it.next(); + if ( !entity.isParentEntity( subEntity) ) { + // SubEntity not found in the new entity add it to remove List + if ( result == null) { + result = new ArrayList(); + } + result.add(subEntity); + //System.out.println( "Removed " + subEntity); + } + } + } + if ( result != null) { + return result; + } else { + return Tools.EMPTY_SET; + } + } + + private void throwNotUnique(RefEntity entity,String name) throws UniqueKeyException { + throw new UniqueKeyException(i18n.format("error.not_unique" + ,name) + ); + } + + public Entity getPersistant(Entity entity) throws EntityNotFoundException { + RefEntity persistant = findPersistantVersion( (RefEntity)entity ); + if ( persistant == null) { + throw new EntityNotFoundException("There is no persistant version of " + entity); + } + return persistant; + } + + protected void resolveEntities(Iterator entities, EntityResolver resolver) throws RaplaException { + List readOnlyList = new ArrayList(); + for (Iterator it = entities;it.hasNext();) { + Object obj = it.next(); + if ( obj instanceof RefEntity) { + RefEntity entity = (RefEntity)obj; + entity.resolveEntities( resolver); + readOnlyList.add( obj ); + } + } + for (Iterator it = readOnlyList.iterator();it.hasNext();) + { + ((SimpleEntity)it.next()).setReadOnly( true ); + } + + } + + + /** check if we find an object with the same name. + * If a different object (different id) with the same unique + * attributes is found a UniqueKeyException will be thrown. + */ + final protected void checkUnique(Collection entities) throws RaplaException { + for (RefEntity entity:entities) + { + String name = ""; + RefEntity entity2 = null; + if ( DynamicType.TYPE.equals( entity.getRaplaType() )) { + DynamicType type =(DynamicType) entity; + name = type.getElementKey(); + + entity2 = (RefEntity) cache.getDynamicType(name); + if (entity2 != null && !entity2.equals(entity)) + throwNotUnique(entity,name); + } + + if ( Category.TYPE.equals( entity.getRaplaType()) ) { + Category category = (Category) entity; + Category[] categories = category.getCategories(); + for ( int i= 0; i entities) throws RaplaException { + for (RefEntity entity:entities) + { + // Check if the user group is missing + if ( Category.TYPE.equals( entity.getRaplaType()) ) { + if (entity.equals( cache.getSuperCategory() )) { + Category userGroups = ((Category) entity).getCategory( Permission.GROUP_CATEGORY_KEY ); + if ( userGroups == null) { + throw new RaplaException("The category with the key '" + Permission.GROUP_CATEGORY_KEY + "' is missing."); + } + } else { + Category category = (Category) entity; + if (category.getParent() == null) { + throw new RaplaException("The category " + category + " needs a parent."); + } + } + } + } + } + + /** Check if the references of each entity refers to an object in cache or in the passed collection.*/ + final protected void checkReferences(Collection entities) throws RaplaException { + Iterator it = entities.iterator(); + while (it.hasNext()) { + SimpleEntity entity = (SimpleEntity) it.next(); + Iterator it2 = entity.getReferences(); + while (it2.hasNext()) { + RefEntity reference = (RefEntity) it2.next(); + if ( reference instanceof Preferences) { + throw new RaplaException("The current version of Rapla doesnt allow references to preferences."); + } + if ( reference instanceof Reservation) { + if (!( entity instanceof Appointment)) { + throw new RaplaException("The current version of Rapla doesnt allow references to events"); + } + } + if ( reference instanceof Appointment) { + if (!( entity instanceof Reservation)) { + throw new RaplaException("The current version of Rapla doesnt allow references to appointment"); + } + } + + // Reference in cache ? + if ( findPersistantVersion( reference ) != null) + continue; + // References in collection. + if (entities.contains(reference)) + continue; + throw new ReferenceNotFoundException(i18n.format("error.reference_not_stored", getName(reference))); + } + } + } + + private String getName(Object object) { + if (object == null) + return (String) null; + if (object instanceof Named) + return (((Named)object).getName(i18n.getLocale())); + return object.toString(); + } + + + /** returns all entities that depend one the passed entities. In most cases + one object depends on an other object if it has a reference to it. + * @param entities */ + final protected Set getDependencies(RefEntity entity) throws RaplaException { + HashSet dependencyList = new HashSet(); + RaplaType type = entity.getRaplaType(); + final Collection referencingEntities; + if ( Category.TYPE.equals( type ) || DynamicType.TYPE.equals( type ) || Allocatable.TYPE.equals( type ) || User.TYPE.equals( type )) { + referencingEntities = getReferencingEntities(entity); + } else { + referencingEntities = cache.getReferers(Preferences.TYPE, entity); + } + dependencyList.addAll(referencingEntities); + return dependencyList; + } + + protected List getReferencingReservations( RefEntity entity ) throws RaplaException { + ArrayList result = new ArrayList(); + Iterator it = this.getReservations(null,null,null).iterator(); + while (it.hasNext()) + { + RefEntity referer = (RefEntity)it.next(); + if (referer != null && referer.isRefering(entity)) { + result.add(referer); + } + } + return result; + } + + protected List getReferencingEntities(RefEntity entity) throws RaplaException{ + ArrayList list = new ArrayList(); + // Important to use getReferncingReservations here, because the method getReservations could be overidden in the subclass, + // to avoid loading unneccessary Reservations in client/server mode. + + list.addAll(getReferencingReservations( entity )); + list.addAll(cache.getReferers(Allocatable.TYPE, entity)); + list.addAll(cache.getReferers(Preferences.TYPE, entity)); + list.addAll(cache.getReferers(User.TYPE, entity)); + list.addAll(cache.getReferers(DynamicType.TYPE, entity)); + return list; + } + + private int countDynamicTypes( Collection entities, String classificationType) { + Iterator it = entities.iterator(); + int count = 0; + while (it.hasNext()) { + RaplaObject entity = (RaplaObject) it.next(); + if ( !DynamicType.TYPE.equals( entity.getRaplaType()) ) + continue; + DynamicType type = (DynamicType) entity; + if ( type.getAnnotation(DynamicTypeAnnotations.KEY_CLASSIFICATION_TYPE).equals( classificationType ) ) { + count ++; + } + } + return count; + } + + + // Count dynamic-types to ensure that there is least one dynamic type left + private void checkDynamicType( Collection entities, String classificationType) throws RaplaException { + Collection allTypes = cache.getCollection( DynamicType.TYPE ); + int count = countDynamicTypes( entities, classificationType ); + if ( count >= 0 && count >= countDynamicTypes( allTypes, classificationType ) ) { + throw new RaplaException(i18n.getString("error.one_type_requiered")); + } + } + + protected void checkNoDependencies(Collection entities, Set storeObjects) throws RaplaException { + HashSet dep = new HashSet(); + + for (RefEntity entity:entities) { + // Add dependencies for the entity + + // First we add the dependencies from the stored object list + for ( RefEntity obj:storeObjects) + { + if (obj.isRefering( entity)) + { + dep.add(obj); + } + } + + // Than we add the dependencies from the cache. It is important that we don't add the dependencies from the stored object list here, because a dependency could be removed in a stored object + Set dependencies = getDependencies( entity ); + for (Object dependency:dependencies) + { + if (!storeObjects.contains( dependency)) + { + dep.add( dependency); + } + } + + + } + if (dep.size()>0) { + Collection names = new ArrayList(); + Iterator entityIt = dep.iterator(); + while (entityIt.hasNext()) { + RaplaObject obj = (RaplaObject)entityIt.next(); + StringBuffer buf = new StringBuffer(); + if(obj instanceof Reservation) + { + buf.append( getString("reservation")); + } + else if(obj instanceof Preferences) + { + buf.append( getString("preferences")); + } + else if(obj instanceof Category) + { + buf.append( getString("categorie")); + } + else if(obj instanceof Allocatable) + { + buf.append( getString("resources_persons")); + } + else if(obj instanceof User) + { + buf.append( getString("user")); + } + if ( obj instanceof Named) { + Locale locale = i18n.getLocale(); + final String string = ((Named)obj).getName( locale ); + buf.append(": " + string); + } + else + { + buf.append( obj.toString()); + } + if ( obj instanceof RefEntity) + { + final Object idFull = ((RefEntity)obj).getId(); + if ( idFull != null) + { + String idShort = idFull.toString(); + int dot = idShort.lastIndexOf('.'); + buf.append(" (" + idShort.substring(dot + 1) + ")"); + } + } + names.add( buf.toString()); + } + throw new DependencyException( names ); + } + // Count dynamic-types to ensure that there is least one dynamic type + // for resources, for persons and for reservations + checkDynamicType( entities, DynamicTypeAnnotations.VALUE_RESERVATION_CLASSIFICATION); + checkDynamicType( entities, DynamicTypeAnnotations.VALUE_RESOURCE_CLASSIFICATION); + checkDynamicType( entities, DynamicTypeAnnotations.VALUE_PERSON_CLASSIFICATION); + } + + private String getString(String key) + { + return getI18n().getString(key); + } + + /** Entities will be resolved against resolveableEntities. + If not found the ParentResolver will be used. + */ + public EntityResolver createEntityResolver(Collection resolveableEntities,LocalCache parent) { + EntityStore resolver = new EntityStore( parent, cache.getSuperCategory() ); + resolver.addAll( resolveableEntities); + return resolver; + } + + + public RefEntity resolveId(Object id) throws EntityNotFoundException { + return cache.resolve(id); + } + + + + + /** compares the version of the cached entities with the versions of the new entities. + * Throws an Exception if the newVersion != cachedVersion + */ + protected void checkVersions( Collection entities ) throws RaplaException { + Iterator it = entities.iterator(); + while (it.hasNext()) { + // Check Versions + RefEntity entity = (RefEntity) it.next(); + RefEntity persistantVersion = findPersistantVersion( entity ); + // If the entities are newer, everything is o.k. + if (persistantVersion != null && persistantVersion != entity && entity.getVersion () < persistantVersion.getVersion()) { + getLogger().warn("There is a newer version for: " + + entity.getId() + + " stored version :" + persistantVersion.getVersion() + + " version to store :" + entity.getVersion()); + throw new RaplaException(getI18n().format("error.new_version", entity.toString())); + } + } + } + + private RefEntity findPersistantVersion(RefEntity entity) { + return (RefEntity) cache.get( entity.getId() ); + } + + + public void authenticate(String username,String password) + throws RaplaException { + if (getLogger().isInfoEnabled()) + getLogger().info ("Check password for User " + username ); + User user= cache.getUser(username); + if (user != null && checkPassword(((RefEntity)user).getId(), password ) ) { + return; + } + throw new RaplaSecurityException(i18n.getString("error.login")); + } + + public boolean canChangePassword() { + return true; + } + + public void changePassword(User user,char[] oldPassword,char[] newPassword) throws RaplaException { + if (getLogger().isInfoEnabled()) + getLogger().info ("Change password for User " + user.getUsername() ); + + Object userId = ((RefEntity)user).getId(); + String password = new String(newPassword); + if ( encryption != null ) + password = encrypt( password ); + cache.putPassword( userId, password ); + storeAndRemove( new Entity[] { editObject(user, null) }, Entity.ENTITY_ARRAY, user); + } + + public Object getLock() { + return lock; + } + + protected String encrypt(String password) { + synchronized ( md ) { + md.reset(); + md.update( password.getBytes()); + return encryption + ":" + Tools.convert( md.digest() ); + } + } + + private boolean checkPassword(Object userId, String password) { + if (userId == null) + return false; + + String correct_pw = (String)cache.getPassword(userId); + if ( correct_pw == null) { + return false; + } + if ( encryption != null && correct_pw.indexOf( encryption + ":" ) >= 0) { + password = encrypt( password ); + } + + if ( correct_pw.equals(password)) { + return true; + } + return false; + } + +} + diff --git a/Rapla/src/org/rapla/storage/impl/EntityStore.java b/Rapla/src/org/rapla/storage/impl/EntityStore.java new file mode 100644 index 0000000..7a294c2 --- /dev/null +++ b/Rapla/src/org/rapla/storage/impl/EntityStore.java @@ -0,0 +1,167 @@ +/** + * + */ +package org.rapla.storage.impl; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; + +import org.rapla.components.util.Assert; +import org.rapla.entities.EntityNotFoundException; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.RaplaType; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.Classification; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.internal.CategoryImpl; +import org.rapla.entities.storage.EntityResolver; +import org.rapla.entities.storage.RefEntity; +import org.rapla.storage.LocalCache; + +public class EntityStore implements EntityResolver { + HashMap entities = new HashMap(); + HashSet idsToRemove = new HashSet(); + LocalCache parent; + HashMap dynamicTypes = new HashMap(); + CategoryImpl superCategory; + HashMap passwordList = new HashMap(); + long repositoryVersion; + + public EntityStore(LocalCache parent,CategoryImpl superCategory) { + this.parent = parent; + this.superCategory = superCategory; + // put( superCategory); + } + + void addAll(Collection collection) { + Iterator it = collection.iterator(); + while (it.hasNext()) + { + put((RefEntity)it.next()); + } + } + + public void put(RefEntity entity) { + Object id = entity.getId(); + Assert.notNull(id); + if ( entity.getRaplaType().equals( DynamicType.TYPE)) + { + DynamicType dynamicType = (DynamicType) entity; + dynamicTypes.put ( dynamicType.getElementKey(), entity); + } + if ( entity.getRaplaType().equals( Allocatable.TYPE)) + { + final Classification classification = ((Allocatable) entity).getClassification(); + final Attribute attribute = classification.getAttribute("email"); + if ( attribute != null) + { + final String email = (String)classification.getValue(attribute); + if ( email != null) + { + entities.put ( email, entity); + } + } + } + entities.put(id,entity); + } + + public void addRemoveId(Object id) + { + idsToRemove.add(id); + } + + public DynamicType getDynamicType(String key) + { + // todo super + DynamicType type = (DynamicType) dynamicTypes.get( key); + if ( type == null && parent != null) + { + type = parent.getDynamicType( key); + } + return type; + } + + public Collection getList() { + return entities.values(); + } + + public Collection getRemoveIds() { + return idsToRemove; + } + + // Implementation of EntityResolver + public RefEntity resolve(Object id) throws EntityNotFoundException { + RefEntity result = get (id ); + if ( result == null) + { + throw new EntityNotFoundException("Object for id " + id.toString() + " not found"); + } + return result; + } + + public CategoryImpl getSuperCategory() + { + return superCategory; + } + + public void putPassword( Object userid, String password ) + { + passwordList.put(userid, password); + } + + public String getPassword( Object userid) + { + return (String)passwordList.get(userid); + } + + public RefEntity get( Object id ) + { + if ( id.equals( superCategory.getId())) + { + return superCategory; + } + RefEntity entity = (RefEntity) entities.get(id); + if (entity != null) + return entity; + + if (parent != null) + { + return parent.get(id); + + } + return null; + } + + public Collection getCollection( RaplaType raplaType ) + { + List collection = new ArrayList(); + Iterator it = entities.values().iterator(); + while (it.hasNext()) + { + RaplaObject obj = (RaplaObject)it.next(); + if ( obj.getRaplaType().equals( raplaType)) + { + collection.add( obj); + } + } + return collection; + } + + public long getRepositoryVersion() + { + return repositoryVersion; + } + + public void setRepositoryVersion( long repositoryVersion ) + { + this.repositoryVersion = repositoryVersion; + } + + + +} \ No newline at end of file diff --git a/Rapla/src/org/rapla/storage/impl/ImportExportManagerImpl.java b/Rapla/src/org/rapla/storage/impl/ImportExportManagerImpl.java new file mode 100644 index 0000000..22bb60a --- /dev/null +++ b/Rapla/src/org/rapla/storage/impl/ImportExportManagerImpl.java @@ -0,0 +1,87 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.storage.impl; +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.ConfigurationException; +import org.apache.avalon.framework.logger.Logger; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaContextException; +import org.rapla.framework.RaplaException; +import org.rapla.storage.CachableStorageOperator; +import org.rapla.storage.ImportExportManager; +/** Imports the content of on store into another. + Export does an import with source and destination exchanged. +

    Configuration:

    +
    +  
    +    file
    +    sql
    +  
    +
    +*/ +public class ImportExportManagerImpl implements ImportExportManager { + + RaplaContext serviceManager; + String sourceString; + String destString; + Logger logger; + + public ImportExportManagerImpl(RaplaContext serviceManager,Configuration configuration) throws RaplaException + { + this.logger = (Logger) serviceManager.lookup( Logger.class.getName()); + this.serviceManager = serviceManager; + try { + sourceString = configuration.getChild("source").getValue(); + destString = configuration.getChild("dest").getValue(); + } catch (ConfigurationException e) { + throw new RaplaException( e); + } + } + + protected Logger getLogger() { + return logger; + } + + /* Import the source into dest. */ + public void doImport() throws RaplaException { + getLogger().info("Import started"); + doConvert(sourceString,destString); + getLogger().info("Import completed"); + } + + /* Export the dest into source. */ + public void doExport() throws RaplaException { + getLogger().info("Export started"); + doConvert(destString,sourceString); + getLogger().info("Export completed"); + } + + private void doConvert(String source,String dest) throws RaplaException { + CachableStorageOperator sourceOperator; + CachableStorageOperator destOperator; + getLogger().info("Converting from " + source + " to " + dest); + try { + sourceOperator = ( CachableStorageOperator) + serviceManager.lookup(CachableStorageOperator.ROLE + "/" + source); + destOperator = ( CachableStorageOperator) + serviceManager.lookup(CachableStorageOperator.ROLE + "/" + dest); + } catch (RaplaContextException ex) { + throw new RaplaException(ex); + } + sourceOperator.connect(); + destOperator.setCache(sourceOperator.getCache()); + destOperator.saveData(); + sourceOperator.disconnect(); + destOperator.disconnect(); + } +} diff --git a/Rapla/src/org/rapla/storage/package.html b/Rapla/src/org/rapla/storage/package.html new file mode 100644 index 0000000..6ee640a --- /dev/null +++ b/Rapla/src/org/rapla/storage/package.html @@ -0,0 +1,5 @@ + +Provides classes and interfaces for serialization and deserialization of the entities. + + + diff --git a/Rapla/src/org/rapla/storage/xml/AllocatableReader.java b/Rapla/src/org/rapla/storage/xml/AllocatableReader.java new file mode 100644 index 0000000..60fd59e --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/AllocatableReader.java @@ -0,0 +1,177 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org . | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.storage.xml; + +import java.util.Date; + +import org.rapla.entities.Category; +import org.rapla.entities.User; +import org.rapla.entities.domain.Permission; +import org.rapla.entities.domain.internal.AllocatableImpl; +import org.rapla.entities.domain.internal.PermissionImpl; +import org.rapla.entities.storage.RefEntity; +import org.rapla.entities.storage.internal.SimpleEntity; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + +public class AllocatableReader extends RaplaXMLReader +{ + DynAttReader dynAttHandler; + AllocatableImpl allocatable; + + public AllocatableReader( RaplaContext context ) throws RaplaException + { + super( context ); + dynAttHandler = new DynAttReader( context ); + addChildHandler( dynAttHandler ); + } + + public void processElement( + String namespaceURI, + String localName, + String qName, + Attributes atts ) throws SAXException + { + if (namespaceURI.equals( DYNATT_NS )) + { + dynAttHandler.setClassifiable( allocatable ); + delegateElement( + dynAttHandler, + namespaceURI, + localName, + qName, + atts ); + return; + } + + if (!namespaceURI.equals( RAPLA_NS )) + return; + + String holdBackString = getString( atts, "holdbackconflicts", "false" ); + boolean holdBackConflicts = Boolean.valueOf( holdBackString ).booleanValue(); + if (localName.equals( "resource" ) || localName.equals( "person" )) + { + String createdAt = atts.getValue( "", "created-at"); + String lastChanged = atts.getValue( "", "last-changed"); + String lastChangedBy = atts.getValue( "", "last-changed-by"); + + Date createTime = null; + Date changeTime = createTime; + if (createdAt != null) + createTime = parseDate( createdAt, false ); + if (lastChanged != null) + changeTime = parseDate( lastChanged, false ); + + allocatable = new AllocatableImpl(createTime, changeTime); + if ( lastChangedBy != null) + { + try + { + User user = (User)resolve(User.TYPE,lastChangedBy ); + allocatable.setLastChangedBy( user ); + } + catch (SAXParseException ex) + { + getLogger().warn("Can't find user " + lastChangedBy + " at line " + ex.getLineNumber()); + } + } + allocatable.setHoldBackConflicts( holdBackConflicts ); + setId( (SimpleEntity) allocatable, atts ); + setVersionIfThere( allocatable, atts); + setOwner(allocatable, atts); + } + + + if (localName.equals( "permission" )) + { + PermissionImpl permission = new PermissionImpl(); + + // process user + String userString = atts.getValue( "user" ); + if (userString != null) + permission.setUser( (User) resolve( User.TYPE, userString ) ); + + // process group + String groupId = atts.getValue( "groupidref" ); + if (groupId != null) + { + permission.setGroup( (Category) resolve( + Category.TYPE, + groupId ) ); + } + else + { + String groupName = atts.getValue( "group" ); + if (groupName != null) + { + Category group= getGroup( groupName); + permission.setGroup( group); + } + } + + String startDate = getString( atts, "start-date", null ); + if (startDate != null) + { + permission.setStart( parseDate( startDate, false ) ); + } + + String endDate = getString( atts, "end-date", null ); + if (endDate != null) + { + permission.setEnd( parseDate( endDate, false ) ); + } + + String minAdvance = getString( atts, "min-advance", null ); + if (minAdvance != null) + { + permission.setMinAdvance( parseLong( minAdvance ) ); + } + + String maxAdvance = getString( atts, "max-advance", null ); + if (maxAdvance != null) + { + permission.setMaxAdvance( parseLong( maxAdvance ) ); + } + + String accessLevel = getString( + atts, + "access", + (String) Permission.ACCESS_LEVEL_NAMEMAP.get( Permission.ALLOCATE_CONFLICTS ) ); + int[] matchingLevel = Permission.ACCESS_LEVEL_NAMEMAP.findMatchingKeys( accessLevel ); + if (matchingLevel.length == 0) + { + throw createSAXParseException( "Unknown access level '" + accessLevel + "'" ); + } + permission.setAccessLevel( matchingLevel[0] ); + allocatable.addPermission( permission ); + } + } + + public void processEnd( String namespaceURI, String localName, String qName ) + throws SAXException + { + if (!namespaceURI.equals( RAPLA_NS )) + return; + + if (localName.equals( "resource" ) || localName.equals( "person" )) + { + if (allocatable.getPermissions().length == 0) + allocatable.addPermission( new PermissionImpl() ); + add( (RefEntity) allocatable ); + } + } +} diff --git a/Rapla/src/org/rapla/storage/xml/AllocatableWriter.java b/Rapla/src/org/rapla/storage/xml/AllocatableWriter.java new file mode 100644 index 0000000..d92adc2 --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/AllocatableWriter.java @@ -0,0 +1,122 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org . | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.storage.xml; + +import java.io.IOException; +import java.util.Iterator; + +import org.rapla.entities.Category; +import org.rapla.entities.EntityNotFoundException; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Permission; +import org.rapla.entities.internal.CategoryImpl; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + + +public class AllocatableWriter extends ClassifiableWriter { + public AllocatableWriter(RaplaContext sm) throws RaplaException { + super(sm); + } + + public void printAllocatable(Allocatable allocatable) throws IOException,RaplaException { + String tagName = allocatable.isPerson()? + "rapla:person" : "rapla:resource"; + openTag(tagName); + printId(allocatable); + printVersion( allocatable); + printOwner(allocatable); + printTimestamp(allocatable ); + if (allocatable.isHoldBackConflicts()) { + att("holdbackconflicts","true"); + } + closeTag(); + printClassification(allocatable.getClassification()); + + Permission[] permissions = allocatable.getPermissions(); + for ( int i = 0; i < permissions.length; i++ ){ + printPermission(permissions[i]); + } + closeElement(tagName); + } + + public void writeObject(RaplaObject object) throws IOException, RaplaException { + printAllocatable( (Allocatable) object); + } + + + protected void printPermission(Permission p) throws IOException,RaplaException { + openTag("rapla:permission"); + if ( p.getUser() != null ) { + att("user", getId( p.getUser() )); + } else if ( p.getGroup() != null ) { + if ( isIdOnly() ) { + att( "groupidref", getId( p.getGroup() ) ); + } else { + att( "group", getGroupPath( p.getGroup() ) ); + } + } + if ( p.getMinAdvance() != null ) { + att ( "min-advance", p.getMinAdvance().toString() ); + } + if ( p.getMaxAdvance() != null ) { + att ( "max-advance", p.getMaxAdvance().toString() ); + } + if ( p.getStart() != null ) { + att ( "start-date", dateTimeFormat.formatDate( p.getStart() ) ); + } + if ( p.getEnd() != null ) { + att ( "end-date", dateTimeFormat.formatDate( p.getEnd() ) ); + } + if ( p.getAccessLevel() != Permission.ALLOCATE_CONFLICTS ) { + att("access", (String) Permission.ACCESS_LEVEL_NAMEMAP.get( p.getAccessLevel() ) ); + } + closeElementTag(); + } + + private String getGroupPath( Category category) throws EntityNotFoundException { + Category rootCategory = cache.getSuperCategory().getCategory(Permission.GROUP_CATEGORY_KEY); + return ((CategoryImpl) rootCategory ).getPathForCategory(category); + } + + public void printAllocatables() throws IOException,RaplaException { + openElement("rapla:resources"); + println(""); + // Print all resources that are not persons + Iterator it = cache.getIterator(Allocatable.TYPE); + while (it.hasNext()) { + Allocatable allocatable = (Allocatable)it.next(); + if ( allocatable.isPerson() ) + continue; + printAllocatable(allocatable); + } + // Print all Persons + it = cache.getIterator(Allocatable.TYPE); + while (it.hasNext()) { + Allocatable allocatable = (Allocatable)it.next(); + if ( !allocatable.isPerson() ) + continue; + printAllocatable(allocatable); + } + println(); + closeElement("rapla:resources"); + + } + + +} + + + diff --git a/Rapla/src/org/rapla/storage/xml/CategoryReader.java b/Rapla/src/org/rapla/storage/xml/CategoryReader.java new file mode 100644 index 0000000..4969ff1 --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/CategoryReader.java @@ -0,0 +1,184 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org . | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.storage.xml; + +import java.util.Stack; + +import org.rapla.components.util.Assert; +import org.rapla.entities.Annotatable; +import org.rapla.entities.Category; +import org.rapla.entities.IllegalAnnotationException; +import org.rapla.entities.MultiLanguageName; +import org.rapla.entities.internal.CategoryImpl; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; + +public class CategoryReader extends RaplaXMLReader +{ + MultiLanguageName currentName = null; + Annotatable currentAnnotatable = null; + String currentLang = null; + Stack categoryStack = new Stack(); + CategoryImpl superCategory; + String annotationKey = null; + CategoryImpl lastProcessedCategory = null; + boolean readOnlyThisCategory; + + public CategoryReader( RaplaContext context ) throws RaplaException + { + super( context ); + superCategory = getSuperCategory(); + currentName = superCategory.getName(); + + } + + public void setReadOnlyThisCategory( boolean enable ) + { + readOnlyThisCategory = enable; + } + + public void processElement( + String namespaceURI, + String localName, + String qName, + Attributes atts ) throws SAXException + { + if (localName.equals( "category" ) && namespaceURI.equals( RAPLA_NS )) + { + String key = atts.getValue( "key" ); + Assert.notNull( key ); + CategoryImpl category = new CategoryImpl(); + category.setKey( key ); + currentName = category.getName(); + currentAnnotatable = category; + setVersionIfThere( category, atts); + if (atts.getValue( "id" )!=null) + { + setId( category, atts ); + } + else + { + setNewId( category ); + } + + + if (!readOnlyThisCategory) + { + if ( !categoryStack.empty() ) + { + Category parent = (Category) categoryStack.peek(); + parent.addCategory( category); + } + else + { + String parentId = atts.getValue( "parentid"); + if ( parentId!= null) + { + if (parentId.equals("category_0")) { + if ( !superCategory.isReadOnly()) + { + superCategory.addCategory( category); + } + category.getReferenceHandler().put("parent", superCategory); + } else { + Object parentIdN = getId( Category.TYPE, parentId); + category.getReferenceHandler().putId("parent", parentIdN); + } + } + else + { + if (atts.getValue( "id" )==null) + { + superCategory.addCategory( category); + } + else + { + // It is the super categorycategory.getReferenceHandler().put("parent", superCategory); + } + } + } + + } + categoryStack.push( category ); + + /* + Category test = category; + String output = ""; + while (test != null) + { + output = "/" + test.getKey() + output; + test = test.getParent(); + } + // System.out.println("Storing category " + output ); + */ + } + + if (localName.equals( "name" ) && namespaceURI.equals( ANNOTATION_NS )) + { + startContent(); + currentLang = atts.getValue( "lang" ); + Assert.notNull( currentLang ); + } + + if (localName.equals( "annotation" ) && namespaceURI.equals( RAPLA_NS )) + { + annotationKey = atts.getValue( "key" ); + Assert.notNull( annotationKey, "key attribute cannot be null" ); + startContent(); + } + } + + public void processEnd( String namespaceURI, String localName, String qName ) + throws SAXException + { + if (localName.equals( "category" )) + { + // Test Namespace uris here for possible xerces bug + if (namespaceURI.equals( "" )) + { + throw createSAXParseException( " category namespace empty. Possible Xerces Bug. Download a newer version of xerces." ); + } + + CategoryImpl category = (CategoryImpl) categoryStack.pop(); + if (!readOnlyThisCategory) + { + add( category ); + } + lastProcessedCategory = category; + } + else if (localName.equals( "name" ) && namespaceURI.equals( ANNOTATION_NS )) + { + String translation = readContent(); + currentName.setName( currentLang, translation ); + } + else if (localName.equals( "annotation" ) && namespaceURI.equals( RAPLA_NS )) + { + try + { + currentAnnotatable.setAnnotation( annotationKey, readContent() ); + } + catch (IllegalAnnotationException ex) + { + } + } + } + + public CategoryImpl getCurrentCategory() + { + return lastProcessedCategory; + } + +} diff --git a/Rapla/src/org/rapla/storage/xml/CategoryWriter.java b/Rapla/src/org/rapla/storage/xml/CategoryWriter.java new file mode 100644 index 0000000..dc533cf --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/CategoryWriter.java @@ -0,0 +1,78 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org . | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.storage.xml; + +import java.io.IOException; + +import org.rapla.entities.Category; +import org.rapla.entities.RaplaObject; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +public class CategoryWriter extends RaplaXMLWriter { + public CategoryWriter(RaplaContext sm) throws RaplaException { + super(sm); + } + + public void printRaplaType(RaplaObject type) throws RaplaException, IOException { + printCategory( (Category) type); + } + + public void printCategory(Category category) throws IOException,RaplaException { + printCategory( category, true); + } + + public void printCategory(Category category,boolean printSubcategories) throws IOException,RaplaException { + openTag("rapla:category"); + printVersion( category); + if (isIdOnly()) + { + printId(category); + Category parent = category.getParent(); + if ( parent != null) + { + att("parentid", getId( parent)); + } + } + att("key",category.getKey()); + closeTag(); + printTranslation(category.getName()); + printAnnotations( category ); + if ( printSubcategories ) + { + Category[] categories = category.getCategories(); + for (int i=0;i entryLevel) { + String id = atts.getValue("idref"); + if ( id != null) { + attribute = (Attribute) resolve(Attribute.TYPE,id); + } else { + attribute = classification.getAttribute(localName); + } + + if (attribute == null) //ignore attributes not found in the classification + return; + + startContent(); + } + } + + public void processEnd(String namespaceURI,String localName,String qName) + throws SAXException + { + if (level > entryLevel) { + String content = readContent(); + if (content != null) { + Object value = parseAttributeValue(attribute, content); + classification.setValue(attribute, value); + } + } + } + +} + + + + diff --git a/Rapla/src/org/rapla/storage/xml/DynamicTypeReader.java b/Rapla/src/org/rapla/storage/xml/DynamicTypeReader.java new file mode 100644 index 0000000..420041e --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/DynamicTypeReader.java @@ -0,0 +1,272 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org . | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.storage.xml; + +import java.text.ParseException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.rapla.components.util.Assert; +import org.rapla.entities.Annotatable; +import org.rapla.entities.Category; +import org.rapla.entities.IllegalAnnotationException; +import org.rapla.entities.MultiLanguageName; +import org.rapla.entities.dynamictype.AttributeType; +import org.rapla.entities.dynamictype.internal.AttributeImpl; +import org.rapla.entities.dynamictype.internal.DynamicTypeImpl; +import org.rapla.entities.storage.RefEntity; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + +public class DynamicTypeReader extends RaplaXMLReader +{ + DynamicTypeImpl dynamicType; + MultiLanguageName currentName = null; + String currentLang = null; + String constraintKey = null; + AttributeImpl attribute = null; + String annotationKey = null; + boolean isAttributeActive = false; + boolean isDynamictypeActive = false; + HashMap typeAnnotations = new HashMap(); + HashMap attributeAnnotations = new HashMap(); + + public DynamicTypeReader( RaplaContext context ) throws RaplaException + { + super( context ); + } + + public void processElement( + String namespaceURI, + String localName, + String qName, + Attributes atts ) throws SAXException + { + + if (localName.equals( "element" )) + { + String qname = getString( atts, "name" ); + String name = qname.substring( qname.indexOf( ":" ) + 1 ); + Assert.notNull( name ); + //System.out.println("NAME: " + qname + " Level " + level + " Entry " + entryLevel); + + if (!isDynamictypeActive) + { + isDynamictypeActive = true; + typeAnnotations.clear(); + dynamicType = new DynamicTypeImpl(); + if (atts.getValue( "id" )!=null) + { + setId( dynamicType, atts ); + } + else + { + setNewId( dynamicType ); + } + + currentName = dynamicType.getName(); + dynamicType.setElementKey( name ); + setVersionIfThere( dynamicType, atts); + + } + else + { + isAttributeActive = true; + attribute = new AttributeImpl(); + currentName = attribute.getName(); + attribute.setKey( name ); + Assert.notNull( name, "key attribute cannot be null" ); + if (atts.getValue("id") != null) + { + setId( attribute, atts ); + } + else + { + setNewId( attribute ); + } + setVersionIfThere( attribute, atts); + attributeAnnotations.clear(); + } + } + + if (localName.equals( "constraint" ) && namespaceURI.equals( RAPLA_NS )) + { + constraintKey = atts.getValue( "name" ); + startContent(); + } + + if (localName.equals( "default" )) + { + startContent(); + } + + // if no attribute type is set + if (localName.equals( "data" ) && namespaceURI.equals( RELAXNG_NS ) && attribute.getType().equals( + AttributeImpl.DEFAULT_TYPE )) + { + String typeName = atts.getValue( "type" ); + if (typeName == null) + throw createSAXParseException( "element relax:data is requiered!" ); + AttributeType type = AttributeType.findForString( typeName ); + if (type == null) + throw createSAXParseException( "AttributeType '" + type + "' not found." ); + attribute.setType( type ); + } + + if (localName.equals( "annotation" ) && namespaceURI.equals( RAPLA_NS )) + { + annotationKey = atts.getValue( "key" ); + Assert.notNull( annotationKey, "key attribute cannot be null" ); + startContent(); + } + + if (localName.equals( "name" ) && namespaceURI.equals( ANNOTATION_NS )) + { + startContent(); + currentLang = atts.getValue( "lang" ); + Assert.notNull( currentLang ); + } + } + + private void addAnnotations( Annotatable annotatable, Map annotations ) + throws SAXParseException + { + for (Iterator it = annotations.entrySet().iterator(); it.hasNext();) + { + Map.Entry entry = (Map.Entry) it.next(); + String key = (String) entry.getKey(); + String annotation = (String) entry.getValue(); + try + { + annotatable.setAnnotation( key, annotation ); + } + catch (IllegalAnnotationException e) + { + throw createSAXParseException( e.getMessage() ); + } + } + + } + + public void processEnd( String namespaceURI, String localName, String qName ) + throws SAXException + { + if (localName.equals( "element" )) + { + if (!isAttributeActive) + { + addAnnotations( dynamicType, typeAnnotations ); + add( dynamicType ); + // We ensure the dynamic type is not modified anymore + dynamicType.setReadOnly( true ); + isDynamictypeActive = false; + } + else + { + addAnnotations( attribute, attributeAnnotations ); + //System.out.println("Adding attribute " + attribute + " to " + dynamicType); + dynamicType.addAttribute( attribute ); + add( (RefEntity) attribute ); + isAttributeActive = false; + } + } + else if (localName.equals( "annotation" ) && namespaceURI.equals( RAPLA_NS )) + { + if (isAttributeActive) + { + attributeAnnotations.put( annotationKey, readContent() ); + } + else + { + typeAnnotations.put( annotationKey, readContent() ); + } + } + else if (localName.equals( "optional" ) && namespaceURI.equals( RELAXNG_NS )) + { + attribute.setOptional( true ); + } + else if (localName.equals( "name" ) && namespaceURI.equals( ANNOTATION_NS )) + { + Assert.notNull( currentName ); + currentName.setName( currentLang, readContent() ); + } + else if (localName.equals( "constraint" ) && namespaceURI.equals( RAPLA_NS )) + { + String content = readContent().trim(); + Object constraint = null; + if (attribute.getConstraintClass( constraintKey ) == Category.class) + { + boolean idContent = isContentCategoryId( content ); + if (idContent) + { + constraint = resolve( Category.TYPE, content ); + } + else + { + constraint = getCategoryFromPath( content ); + } + } + else if (attribute.getConstraintClass( constraintKey ) == Integer.class) + { + constraint = parseLong( content ); + } + else + { + constraint = content; + } + attribute.setConstraint( constraintKey, constraint ); + } + + if (localName.equals( "default" ) && namespaceURI.equals( RAPLA_NS )) + { + String content = readContent().trim(); + final Object defaultValue; + final AttributeType type = attribute.getType(); + if (type == AttributeType.CATEGORY) + { + boolean idContent = isContentCategoryId( content ); + if (idContent) + { + defaultValue = resolve( Category.TYPE, content ); + } + else + { + defaultValue = getCategoryFromPath( content ); + } + } + else + { + Object value; + try + { + value = AttributeImpl.parseAttributeValue(attribute, content, resolver); + } + catch (ParseException e) + { + value = null; + } + defaultValue = value; + } + attribute.setDefaultValue(defaultValue ); + } + } + + + + +} diff --git a/Rapla/src/org/rapla/storage/xml/DynamicTypeWriter.java b/Rapla/src/org/rapla/storage/xml/DynamicTypeWriter.java new file mode 100644 index 0000000..f0bda32 --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/DynamicTypeWriter.java @@ -0,0 +1,184 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.storage.xml; + +import java.io.IOException; +import java.util.Date; +import java.util.Iterator; + +import org.rapla.entities.Category; +import org.rapla.entities.EntityNotFoundException; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.AttributeType; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.dynamictype.internal.DynamicTypeImpl; +import org.rapla.entities.internal.CategoryImpl; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +/** Stores the data from the local cache in XML-format to a print-writer.*/ +public class DynamicTypeWriter extends RaplaXMLWriter +{ + public DynamicTypeWriter(RaplaContext sm) throws RaplaException { + super(sm); + } + + private void printStartPattern() throws IOException { + openElement("relax:start"); + openElement("relax:choice"); + Iterator it = cache.getCollection(DynamicType.TYPE).iterator(); + while (it.hasNext()) { + DynamicTypeImpl type = (DynamicTypeImpl) it.next(); + openTag("relax:ref"); + att("name",type.getElementKey()); + closeElementTag(); + } + closeElement("relax:choice"); + closeElement("relax:start"); + } + + public void printDynamicType(DynamicType type) throws IOException,RaplaException { + openTag("relax:define"); + att("name",type.getElementKey()); + closeTag(); + + openTag("relax:element"); + att("name","dynatt:" + type.getElementKey()); + if (isIdOnly()) + { + att("id",getId(type)); + } + printVersion( type); + + closeTag(); + + printTranslation(type.getName()); + printAnnotations(type); + + Attribute att[] = type.getAttributes(); + for (int i = 0; i< att.length; i ++) { + printAttribute(att[i]); + } + + closeElement("relax:element"); + closeElement("relax:define"); + } + + public void writeObject(RaplaObject type) throws IOException, RaplaException { + printDynamicType( (DynamicType) type ); + } + + + + private String getCategoryPath( Category category) throws EntityNotFoundException { + Category rootCategory = cache.getSuperCategory(); + if ( category != null && rootCategory.equals( category) ) + { + return ""; + } + return ((CategoryImpl) rootCategory ).getPathForCategory(category); + } + + protected void printAttribute(Attribute attribute) throws IOException,RaplaException { + if (attribute.isOptional()) + openElement("relax:optional"); + openTag("relax:element"); + att("name",attribute.getKey()); + if (isIdOnly()) + att("id",getId(attribute)); + + printVersion( attribute); + + AttributeType type = attribute.getType(); + closeTag(); + printTranslation( attribute.getName() ); + printAnnotations( attribute ); + String[] constraintKeys = attribute.getConstraintKeys(); + openTag("relax:data"); + + att("type", type.toString()); + closeElementTag(); + for (int i = 0; i 0) + { + openElement("selected"); + RaplaMapWriter writer = (RaplaMapWriter)getWriterFor( RaplaMap.TYPE); + writer.writeMap( selectedObjects); + closeElement("selected"); + } + RaplaMap extensionMap = calendar.getOptionMap(); + if (extensionMap != null && extensionMap.size() > 0) + { + openElement("options"); + RaplaMapWriter writer = (RaplaMapWriter)getWriterFor( RaplaMap.TYPE); + writer.writeMap( extensionMap); + closeElement("options"); + } + ClassificationFilter[] filter =calendar.getFilter() ; + if ( filter.length> 0) + { + openElement("filter"); + for (ClassificationFilter f:filter) + { + final DynamicType dynamicType = f.getType(); + final String annotation = dynamicType.getAnnotation(DynamicTypeAnnotations.KEY_CLASSIFICATION_TYPE); + boolean eventType = annotation != null && annotation.equals( DynamicTypeAnnotations.VALUE_RESERVATION_CLASSIFICATION); + if (( eventType && !calendar.isDefaultEventTypes()) + || (!eventType && !calendar.isDefaultResourceTypes()) + ) + { + printClassificationFilter( f ); + } + } + closeElement("filter"); + } + closeElement("rapla:" + CalendarModelConfiguration.TYPE.getLocalName()); + } + + + + } + + + diff --git a/Rapla/src/org/rapla/storage/xml/RaplaConfigurationReader.java b/Rapla/src/org/rapla/storage/xml/RaplaConfigurationReader.java new file mode 100644 index 0000000..9295728 --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/RaplaConfigurationReader.java @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org . | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.storage.xml; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.NamespacedSAXConfigurationHandler; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.configuration.RaplaConfiguration; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.xml.sax.Attributes; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; + +public class RaplaConfigurationReader extends RaplaXMLReader { + boolean delegating = false; + + public RaplaConfigurationReader(RaplaContext context) throws RaplaException { + super(context); + } + + NamespacedSAXConfigurationHandler configurationHandler = new NamespacedSAXConfigurationHandler() { + boolean aelfredFailed; + + protected String getLocationString() + { + if ( aelfredFailed ) + return "Unknown"; + try { + // Aelfred causes an error after xslt processing. + return super.getLocationString(); + } catch (Exception ex) { + aelfredFailed = true; + getLogger().warn("Can't get location string. Probably an aelfread sax error."); + return "Unknown"; + } + } + }; + + public void clear() { + configurationHandler.clear(); + // The locator will be reseted after a clear + configurationHandler.setDocumentLocator(getLocator()); + } + + public void processElement(String namespaceURI,String localName,String qName,Attributes atts) + throws SAXException + { + if ( RAPLA_NS.equals(namespaceURI) && localName.equals("config")) + return; + delegating = true; + configurationHandler.startElement(namespaceURI, localName, qName, atts); + } + + public void processEnd(String namespaceURI,String localName,String qName) + throws SAXException + { + if ( RAPLA_NS.equals(namespaceURI) && localName.equals("config")) + return; + + configurationHandler.endElement(namespaceURI, localName, qName); + delegating = false; + + } + + public void startPrefixMapping(String prefix, + String uri) throws SAXException + { + // configurationHandler.startPrefixMapping(prefix, uri); + } + + public void setDocumentLocator(Locator locator) { + configurationHandler.setDocumentLocator(locator); + super.setDocumentLocator(locator); + } + + public void processCharacters(char[] ch,int start,int length) + throws SAXException + { + + if ( delegating ){ + configurationHandler.characters(ch,start,length); + } + } + + + public RaplaObject getType() { + return new RaplaConfiguration(getConfiguration()); + } + + private Configuration getConfiguration() { + Configuration conf = configurationHandler.getConfiguration(); + return conf; + } +} + diff --git a/Rapla/src/org/rapla/storage/xml/RaplaConfigurationWriter.java b/Rapla/src/org/rapla/storage/xml/RaplaConfigurationWriter.java new file mode 100644 index 0000000..2e5d38a --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/RaplaConfigurationWriter.java @@ -0,0 +1,148 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org . | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.storage.xml; + +import java.io.IOException; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.ConfigurationException; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.configuration.RaplaConfiguration; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.xml.sax.helpers.AttributesImpl; +import org.xml.sax.helpers.NamespaceSupport; + + +public class RaplaConfigurationWriter extends RaplaXMLWriter { + + public RaplaConfigurationWriter(RaplaContext sm) throws RaplaException { + super(sm); + } + + public void writeObject(RaplaObject type) throws IOException, RaplaException { + RaplaConfiguration raplaConfig = (RaplaConfiguration) type ; + openElement("rapla:" + RaplaConfiguration.TYPE.getLocalName()); + try { + printConfiguration(raplaConfig.getConfig() ); + } catch (ConfigurationException ex) { + throw new RaplaException( ex ); + } + closeElement("rapla:" + RaplaConfiguration.TYPE.getLocalName()); + } + + private void printConfiguration(final Configuration conf ) throws ConfigurationException, RaplaException, IOException { + printConfiguration(namespaceSupport,conf); + } + + /** + * Serialize each Configuration element. This method is called recursively. + * Original code for this method is taken from the org.apache.framework.configuration.DefaultConfigurationSerializer class + * @param namespaceSupport a NamespaceSupport to use + * @throws ConfigurationException if an error occurs + * @throws IOException if an error occurs + */ + protected void printConfiguration( final NamespaceSupport namespaceSupport, + final Configuration element ) + throws IOException, ConfigurationException, RaplaException + { + namespaceSupport.pushContext(); + + AttributesImpl attr = new AttributesImpl(); + String[] attrNames = element.getAttributeNames(); + + if( null != attrNames ) + { + for( int i = 0; i < attrNames.length; i++ ) + { + attr.addAttribute( "", // namespace URI + attrNames[ i ], // local name + attrNames[ i ], // qName + "CDATA", // type + element.getAttribute( attrNames[ i ], "" ) // value + ); + } + } + + final String nsURI = element.getNamespace(); + String nsPrefix = namespaceSupport.getPrefix(nsURI); + if (nsPrefix == null) + nsPrefix = ""; + + + final String existingURI = namespaceSupport.getURI( nsPrefix ); + + // ie, there is no existing URI declared for this prefix or we're + // remapping the prefix to a different URI + if( existingURI == null || !existingURI.equals( nsURI ) ) + { + if( nsPrefix.equals( "" ) ) { + } else { + // (re)declare a mapping from nsPrefix to nsURI + attr.addAttribute( "", "xmlns:" + nsPrefix, "xmlns:" + nsPrefix, "CDATA", nsURI ); + } + //handler.startPrefixMapping( nsPrefix, nsURI ); + namespaceSupport.declarePrefix( nsPrefix, nsURI ); + } + + String localName = element.getName(); + String qName = element.getName(); + if( nsPrefix == null || nsPrefix.length() == 0 ) + { + qName = localName; + } + else + { + qName = nsPrefix + ":" + localName; + } + + openTag(qName); + att(attr); + + String value = element.getValue( null ); + + if( null == value ) + { + Configuration[] children = element.getChildren(); + if (children.length > 0) + { + closeTag(); + for( int i = 0; i < children.length; i++ ) + { + printConfiguration( namespaceSupport, children[ i ] ); + } + closeElement(qName); + } + else + { + closeElementTag(); + } + } + else + { + closeTagOnLine(); + print(value); + closeElementOnLine(qName); + println(); + } + + + namespaceSupport.popContext(); + } + + + } + + + diff --git a/Rapla/src/org/rapla/storage/xml/RaplaEntityComparator.java b/Rapla/src/org/rapla/storage/xml/RaplaEntityComparator.java new file mode 100644 index 0000000..4071921 --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/RaplaEntityComparator.java @@ -0,0 +1,75 @@ +package org.rapla.storage.xml; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; + +import org.rapla.entities.Category; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.RaplaType; +import org.rapla.entities.User; +import org.rapla.entities.configuration.Preferences; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Period; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.DynamicType; + +public class RaplaEntityComparator implements Comparator +{ + Map ordering = new HashMap(); + public RaplaEntityComparator() + { + int i=0; + ordering.put( Category.TYPE,new Integer(i++)); + ordering.put( DynamicType.TYPE, new Integer(i++)); + ordering.put( User.TYPE,new Integer(i++)); + ordering.put( Allocatable.TYPE, new Integer(i++)); + ordering.put( Preferences.TYPE,new Integer(i++) ); + ordering.put( Period.TYPE, new Integer(i++) ); + ordering.put( Reservation.TYPE,new Integer(i++)); + } + + public int compare( Object o1, Object o2) + { + RaplaObject r1 = (RaplaObject) o1; + RaplaObject r2 = (RaplaObject) o2; + RaplaType t1 = r1.getRaplaType(); + RaplaType t2 = r2.getRaplaType(); + Integer ord1 = (Integer) ordering.get( t1); + Integer ord2 = (Integer) ordering.get( t2); + if ( o1 == o2) + { + return 0; + } + + if ( ord1 != null && ord2 != null) + { + if (ord1.intValue()>ord2.intValue()) + { + return 1; + } + if (ord1.intValue() o2.hashCode()) + { + return 1; + } + else + { + return -1; + } + } + +} diff --git a/Rapla/src/org/rapla/storage/xml/RaplaInput.java b/Rapla/src/org/rapla/storage/xml/RaplaInput.java new file mode 100644 index 0000000..939703a --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/RaplaInput.java @@ -0,0 +1,262 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org . | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.storage.xml; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URL; + +import org.apache.avalon.framework.logger.Logger; +import org.rapla.framework.RaplaException; +import org.xml.sax.ContentHandler; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + +/** Reads the data in xml format from an InputSource into the + LocalCache and converts it to a newer version if necessary. + */ +public final class RaplaInput { + private Logger logger; + private URL fileSource; + private Reader reader; + + private boolean wasConverted; + + public RaplaInput(Logger logger) throws RaplaException { + this.logger = logger; + } + + protected Logger getLogger() { + return logger; + } + + /** returns if the data was converted during read.*/ + public boolean wasConverted() { + return wasConverted; + } + + public void read(URL file, ContentHandler handler, boolean validate) throws RaplaException,IOException { + getLogger().debug("Parsing " + file.toString()); + fileSource = file; + reader = null; + parseData( handler , validate); + } + + public boolean read(Reader xml, ContentHandler handler, boolean validate) throws RaplaException,IOException { + fileSource = null; + reader = xml; + parseData( handler, validate); + return wasConverted; + } + + public boolean read(Reader xml, ContentHandler handler) throws RaplaException,IOException { + fileSource = null; + reader = xml; + parseData( handler, false); + return wasConverted; + } + + public boolean readWithNamespaces(String xml, ContentHandler handler) throws RaplaException,IOException { + StringBuffer dataElement = new StringBuffer(); + dataElement.append(""); + String xmlWithNamespaces = dataElement.toString() + xml + ""; + return read(new StringReader(xmlWithNamespaces), handler); + } + + + + private InputSource getNewSource() { + if ( fileSource != null ) { + return new InputSource( fileSource.toString() ); + } else if ( reader != null ) { + return new InputSource( reader ); + } else { + throw new IllegalStateException("fileSource or reader can't be null"); + } + } + + private void parseData( ContentHandler contentHandler,boolean validate) + throws RaplaException + ,IOException { + try { + RaplaSAXPipeline pipeline = new RaplaSAXPipeline(); + pipeline.enableLogging(getLogger()); + if (validate) { + validate( getNewSource(), "org/rapla/storage/xml/rapla.rng"); + } + pipeline.parse( contentHandler, getNewSource() ); + } catch (SAXException ex) { + Throwable cause = ex.getException(); + if (cause instanceof SAXParseException) { + ex = (SAXParseException) cause; + cause = ex.getException(); + } + if (ex instanceof SAXParseException) { + throw new RaplaException("Line: " + ((SAXParseException)ex).getLineNumber() + + " Column: "+ ((SAXParseException)ex).getColumnNumber() + " " + + ((cause != null) ? cause.getMessage() : ex.getMessage()) + ,(cause != null) ? cause : ex ); + } + if (cause == null) { + throw new RaplaException( ex); + } + if (cause instanceof WrongVersionException) { + convertData( getNewSource(),contentHandler,((WrongVersionException)cause).getVersion()); + return; + } + if (cause instanceof RaplaException) + throw (RaplaException) cause; + else + throw new RaplaException( cause); + } + /* End of Exception Handling */ + } + + /** uses the jing validator to validate a document against an relaxng schema. + * This method uses reflection API, to avoid compile-time dependencies on + * the jing.jar + * @param in + * @param schema + * @throws RaplaException + */ + private void validate(InputSource in, String schema) throws RaplaException { + try { + ErrorHandler errorHandler = new RaplaErrorHandler(); + /* // short version + * propMapBuilder = new com.thaiopensource.util.PropertyMapBuilder(); + * propMapBuilder.put(com.thaiopensource.validate.ValidateProperty.ERROR_HANDLER, errorHandler); + * Object propMap = propMapBuilder.toPropertyMap(); + * Object o =new com.thaiopensource.validate.ValidationDriver(propMap); + * o.loadSchema(schema); + * o.validate(in); + */ + // full reflection syntax + Class validatorC = Class.forName("com.thaiopensource.validate.ValidationDriver"); + Class propIdC = Class.forName("com.thaiopensource.util.PropertyId"); + Class validatepropC = Class.forName("com.thaiopensource.validate.ValidateProperty"); + Object errorHandlerId = validatepropC.getDeclaredField("ERROR_HANDLER").get( null ); + Class propMapC = Class.forName("com.thaiopensource.util.PropertyMap"); + Class propMapBuilderC = Class.forName("com.thaiopensource.util.PropertyMapBuilder"); + Object propMapBuilder = propMapBuilderC.newInstance(); + Method put = propMapBuilderC.getMethod("put", new Class[] {propIdC, Object.class} ); + put.invoke( propMapBuilder, new Object[] {errorHandlerId, errorHandler}); + Method topropMap = propMapBuilderC.getMethod("toPropertyMap", new Class[] {} ); + Object propMap = topropMap.invoke( propMapBuilder, new Object[] {}); + Constructor validatorConst = validatorC.getConstructor( new Class[] { propMapC }); + Object validator = validatorConst.newInstance( new Object[] {propMap}); + Method loadSchema = validatorC.getMethod( "loadSchema", new Class[] {InputSource.class}); + Method validate = validatorC.getMethod("validate", new Class[] {InputSource.class}); + InputSource schemaSource = new InputSource( getResource( schema ).toString() ); + loadSchema.invoke( validator, new Object[] {schemaSource} ); + validate.invoke( validator, new Object[] {in}); + } catch (ClassNotFoundException ex) { + throw new RaplaException( ex.getMessage() + ". Latest jing.jar is missing on the classpath. Please download from http://www.thaiopensource.com/relaxng/jing.html"); + } catch (InvocationTargetException e) { + throw new RaplaException("Can't validate data due to the following error: " + e.getTargetException().getMessage(), e.getTargetException()); + } catch (Exception ex) { + throw new RaplaException("Error invoking JING", ex); + } + } + private URL getResource(String name) throws RaplaException { + URL url = getClass().getClassLoader().getResource( name ); + if ( url == null ) + throw new RaplaException("Resource " + name + " not found"); + return url; + } + private void convertData(InputSource inputSource,ContentHandler handler,String versionString) + throws RaplaException,IOException + { + double version; + try { + version = new Double(versionString).doubleValue(); + } catch (NumberFormatException ex) { + throw new RaplaException("Invalid version tag (double-value expected)!"); + } + // get the version number of the data-schema + if (version > new Double(RaplaMainReader.INPUT_FILE_VERSION).doubleValue()) + throw new RaplaException("This version of Rapla cannot read files with a version-number" + + " greater than " + RaplaMainReader.INPUT_FILE_VERSION + + ", try out the latest version."); + + try { + RaplaSAXPipeline pipeline = new RaplaSAXPipeline(); + pipeline.enableLogging(getLogger()); + if (version < 0.4) { + throw new RaplaException("Rapla 0.7, 0.6 or rapla 0.5 files are not supported in this version\n" + + " Please use rapla version 0.8.2 to convert this file: Load file, edit and save something!"); + } + if (version < 0.5) { + URL stylesheet = getResource( "org/rapla/storage/xml/convert0_4to0_5.xsl" ); + pipeline.addTransformer(stylesheet,new String[][] {}); + } + if (version < 0.6) { + URL stylesheet = getResource( "org/rapla/storage/xml/convert0_5to0_6.xsl" ); + pipeline.addTransformer(stylesheet,new String[][] {}); + } + if (version < 0.7) { + URL stylesheet = getResource( "org/rapla/storage/xml/convert0_6to0_7.xsl" ); + pipeline.addTransformer(stylesheet,new String[][] {}); + } + if (version < 0.8) { + URL stylesheet = getResource( "org/rapla/storage/xml/convert0_7to0_8.xsl" ); + pipeline.addTransformer(stylesheet,new String[][] {}); + } + if (version < 0.9) { + URL stylesheet = getResource( "org/rapla/storage/xml/convert0_8to0_9.xsl" ); + pipeline.addTransformer(stylesheet,new String[][] {}); + } + + if (version < 1.0) { + URL stylesheet = getResource( "org/rapla/storage/xml/convert0_9to1_0.xsl" ); + pipeline.addTransformer(stylesheet,new String[][] {}); + } + + getLogger().info("Start conversion"); + //pipeline.parse(new DefaultHandler(), inputSource); + + pipeline.parse(handler, inputSource); + getLogger().info("Conversion successful"); + wasConverted = true; + } catch (SAXException ex) { + Throwable cause = ex.getException(); + if (cause == null) + throw new RaplaException( ex); + + if (cause instanceof RaplaException) { + throw (RaplaException)cause; + } else { + throw new RaplaException( cause ); + } + } + } + +} diff --git a/Rapla/src/org/rapla/storage/xml/RaplaMainReader.java b/Rapla/src/org/rapla/storage/xml/RaplaMainReader.java new file mode 100644 index 0000000..0745e7c --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/RaplaMainReader.java @@ -0,0 +1,154 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org . | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.storage.xml; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.rapla.entities.Category; +import org.rapla.entities.User; +import org.rapla.entities.configuration.Preferences; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Period; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.xml.sax.Attributes; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; + +public class RaplaMainReader extends RaplaXMLReader +{ + Map localnameTable = new HashMap(); + final static String INPUT_FILE_VERSION = RaplaXMLWriter.OUTPUT_FILE_VERSION; + boolean parseOnlyHeader; + + public RaplaMainReader( RaplaContext context ) throws RaplaException + { + super( context ); + + // Setup the delegation classes + localnameTable.put( "grammar", readerMap.get( DynamicType.TYPE ) ); + localnameTable.put( "element", readerMap.get( DynamicType.TYPE ) ); + + localnameTable.put( "user", readerMap.get( User.TYPE ) ); + localnameTable.put( "category", readerMap.get( Category.TYPE ) ); + localnameTable.put( "preferences", readerMap.get( Preferences.TYPE ) ); + localnameTable.put( "resource", readerMap.get( Allocatable.TYPE ) ); + localnameTable.put( "person", readerMap.get( Allocatable.TYPE ) ); + localnameTable.put( "period", readerMap.get( Period.TYPE ) ); + localnameTable.put( "reservation", readerMap.get( Reservation.TYPE ) ); + localnameTable.put( "remove", readerMap.get( "remove") ); + if (!parseOnlyHeader) + { + addChildHandler( readerMap.values() ); + } + } + + public void setDocumentLocator( Locator locator ) + { + super.setDocumentLocator( locator ); + } + + private void addChildHandler( Collection collection ) + { + Iterator it = collection.iterator(); + while (it.hasNext()) + addChildHandler( (DelegationHandler) it.next() ); + } + + /** checks the version of the input-file. throws + WrongVersionException if the file-version is not supported by + the reader.*/ + public void processHead( + String uri, + String name, + String qName, + Attributes atts ) throws SAXException + { + try + { + String version = null; + getLogger().debug( "Getting version." ); + if (name.equals( "data" ) && uri.equals( RAPLA_NS )) + { + version = atts.getValue( "version" ); + if (version == null) + throw createSAXParseException( "Could not get Version" ); + } + String repositoryVersion = atts.getValue("repositoryVersion"); + if ( repositoryVersion != null) + { + resolver.setRepositoryVersion( Long.parseLong( repositoryVersion)); + } + if (name.equals( "DATA" )) + { + version = atts.getValue( "version" ); + if (version == null) + { + version = "0.1"; + } + } + if (version == null) + throw createSAXParseException( "Invalid Format. Could not read data." ); + + if (!version.equals( INPUT_FILE_VERSION )) + { + getLogger().warn( "Warning: Different version detected" ); + throw new WrongVersionException( version ); + } + getLogger().debug( "Found compatible version-number." ); + // We've got the right version. We can proceed. + } + catch (Exception ex) + { + throw new SAXException( ex ); + } + } + + + public void processElement( + String namespaceURI, + String localName, + String qName, + Attributes atts ) throws SAXException + { + if (level == 1) + { + processHead( namespaceURI, localName, qName, atts ); + return; + } + + if (parseOnlyHeader ) + return; + if ( !namespaceURI.equals(RAPLA_NS) && !namespaceURI.equals(RELAXNG_NS)) + { + // Ignore unknown namespace + return; + } + + // lookup delegation-handler for the localName + DelegationHandler handler = (DelegationHandler) localnameTable.get( localName ); + // Ignore unknown elements + if (handler != null) + { + delegateElement( handler, namespaceURI, localName, qName, atts ); + } + + } + +} diff --git a/Rapla/src/org/rapla/storage/xml/RaplaMainWriter.java b/Rapla/src/org/rapla/storage/xml/RaplaMainWriter.java new file mode 100644 index 0000000..07e1547 --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/RaplaMainWriter.java @@ -0,0 +1,191 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.storage.xml; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.rapla.entities.Category; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.RaplaType; +import org.rapla.entities.User; +import org.rapla.entities.configuration.Preferences; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Period; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + +/** Stores the data from the local cache in XML-format to a print-writer.*/ +public class RaplaMainWriter extends RaplaXMLWriter +{ + /** + * @param sm + * @throws RaplaException + */ + public RaplaMainWriter(RaplaContext sm) throws RaplaException { + super(sm); + } + + public void setWriter( BufferedWriter writer ) { + super.setWriter( writer ); + for ( Iterator it = writerMap.values().iterator();it.hasNext();) { + ((RaplaXMLWriter)it.next()).setWriter( writer ); + } + } + + public void setEncoding( String encoding ) { + super.setEncoding( encoding ); + for ( Iterator it = writerMap.values().iterator();it.hasNext();) { + ((RaplaXMLWriter)it.next()).setEncoding( encoding ); + } + } + + protected void printContent() throws IOException,RaplaException { + printHeader( 0 ); + + ((CategoryWriter)getWriterFor(Category.TYPE)).printCategories(); + println(); + ((DynamicTypeWriter)getWriterFor(DynamicType.TYPE)).printDynamicTypes(); + println(); + ((PreferenceWriter)getWriterFor(Preferences.TYPE)).printPreferences( cache.getPreferences( null )); + println(); + ((UserWriter)getWriterFor(User.TYPE)).printUsers(); + println(); + ((AllocatableWriter)getWriterFor(Allocatable.TYPE)).printAllocatables(); + println(); + ((PeriodWriter)getWriterFor(Period.TYPE)).printPeriods(); + println(); + ((ReservationWriter)getWriterFor(Reservation.TYPE)).printReservations(); + println(); + + closeElement("rapla:data"); + } + + private void printHeader(long repositoryVersion) throws IOException + { + println(""); + openTag("rapla:data"); + for (int i=0;i 0) + { + att("repositoryVersion", String.valueOf(repositoryVersion)); + } + closeTag(); + } + + public void write(OutputStream out ) throws IOException,RaplaException { + BufferedWriter w = new BufferedWriter(new OutputStreamWriter(out,encoding)); + setWriter(w); + printContent(); + w.flush(); + } + + public void printList( List resources,List remove, long repositoryVersion ) throws RaplaException, IOException + { + printHeader( repositoryVersion); + printListPrivate( resources); + openElement("rapla:remove"); + for ( Iterator it = remove.iterator();it.hasNext();) + { + RaplaObject object = (RaplaObject) it.next(); + openTag("rapla:" + object.getRaplaType().getLocalName()); + att("idref", getId( object)); + closeElementTag(); + } + closeElement("rapla:remove"); + closeElement("rapla:data"); + } + + + protected void printListPrivate( List resources ) throws RaplaException, IOException + { + SortedSet set = new TreeSet(new RaplaEntityComparator()); + HashSet hashSet = new HashSet(resources); + set.addAll(resources ); + for ( Iterator it = set.iterator();it.hasNext();) + { + RaplaObject object = (RaplaObject) it.next(); + + RaplaType type = object.getRaplaType(); + RaplaXMLWriter writer; + if ( type == Attribute.TYPE || type == Appointment.TYPE) + { + continue; + } + try + { + writer = getWriterFor(type); + } + catch (RaplaException e) + { + System.err.println( e.getMessage()); + continue; + } + if ( type == Preferences.TYPE) + { + Preferences preferences= (Preferences)object; + if ( preferences.getOwner() != null && hashSet.contains( preferences.getOwner())) + { + continue; + } + writer.writeObject(object); + } + if ( type == Category.TYPE) + { + Category category = (Category)object; + if (category.getParent() != null && hashSet.contains(category.getParent())) + { + continue; + } + ((CategoryWriter) writer).printCategory( category, true); + } + else + { + writer.writeObject(object); + } + + } + + } + + + + + + + +} + + + diff --git a/Rapla/src/org/rapla/storage/xml/RaplaMapReader.java b/Rapla/src/org/rapla/storage/xml/RaplaMapReader.java new file mode 100644 index 0000000..de75b12 --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/RaplaMapReader.java @@ -0,0 +1,105 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org . | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.storage.xml; + +import org.rapla.entities.RaplaObject; +import org.rapla.entities.RaplaType; +import org.rapla.entities.configuration.RaplaMap; +import org.rapla.entities.configuration.internal.RaplaMapImpl; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.storage.RefEntity; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; + +public class RaplaMapReader extends RaplaXMLReader { + + String key; + RaplaMapImpl entityMap; + RaplaXMLReader childReader; + + public RaplaMapReader(RaplaContext sm) throws RaplaException { + super(sm); + } + + public void processElement(String namespaceURI,String localName,String qName,Attributes atts) + throws SAXException + { + if ( !RAPLA_NS.equals(namespaceURI)) + return; + if (localName.equals(RaplaMap.TYPE.getLocalName())) { + entityMap = new RaplaMapImpl(); + return; + } + if (localName.equals("mapentry")) { + key= getString(atts, "key"); + String value = getString( atts, "value", null); + if ( value != null) + { + entityMap.putString( key, value ); + } + return; + } + + String refid = getString( atts, "idref", null); + String keyref = getString( atts, "keyref", null); + RaplaType raplaType = getTypeForLocalName( localName ); + if ( refid != null) { + childReader = null; + // TODO We ignore the old references + if ( raplaType.equals( Appointment.TYPE) || raplaType.equals( Reservation.TYPE)) { + return; + } + Object id = getId( raplaType, refid); + entityMap.getReferenceHandler().putId( key, id); + } else if ( keyref != null) { + childReader = null; + DynamicType type = getDynamicType( keyref ); + if ( type != null) { + Object id = ((RefEntity) type).getId(); + entityMap.getReferenceHandler().putId( key, id); + } + } else { + childReader = getChildHandlerForType( raplaType ); + delegateElement( childReader, namespaceURI, localName, qName, atts); + } + + } + + public void processEnd(String namespaceURI,String localName,String qName) + throws SAXException + { + if ( !RAPLA_NS.equals(namespaceURI) ) + return; + + if ( childReader != null ) { + entityMap.getChildMap().put( key, childReader.getType()); + } + childReader = null; + } + + public RaplaMap getEntityMap() { + return entityMap; + } + public RaplaObject getType() { + //reservation.getReferenceHandler().put() + return getEntityMap(); + } + + +} + diff --git a/Rapla/src/org/rapla/storage/xml/RaplaMapWriter.java b/Rapla/src/org/rapla/storage/xml/RaplaMapWriter.java new file mode 100644 index 0000000..6f581dd --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/RaplaMapWriter.java @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org . | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.storage.xml; + +import java.io.IOException; +import java.util.Iterator; + +import org.rapla.entities.RaplaObject; +import org.rapla.entities.configuration.RaplaMap; +import org.rapla.entities.storage.RefEntity; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + + +public class RaplaMapWriter extends RaplaXMLWriter { + + public RaplaMapWriter(RaplaContext sm) throws RaplaException { + super(sm); + } + + public void writeObject(RaplaObject type) throws IOException, RaplaException { + writeMap((RaplaMap) type ); + } + + public void writeMap(RaplaMap map ) throws IOException, RaplaException { + openElement("rapla:" + RaplaMap.TYPE.getLocalName()); + for (Iterator it = map.keySet().iterator();it.hasNext();) { + Object key = it.next(); + Object obj = (Object)map.get( key); + printEntityReference( key, obj); + } + closeElement("rapla:" + RaplaMap.TYPE.getLocalName()); + } + + private void printEntityReference(Object key,Object obj) throws RaplaException, IOException { + if (obj == null) + { + getLogger().warn( "Map contains empty value under key " + key ); + return; + } + int start = getIndentLevel(); + openTag("rapla:mapentry"); + att("key", key.toString()); + if ( obj instanceof String) + { + String value = (String) obj; + att("value", value); + closeElementTag(); + return; + } + closeTag(); + if ( obj instanceof RefEntity ) { + printReference( (RefEntity) obj); + } else { + RaplaObject raplaObj = (RaplaObject) obj; + getWriterFor( raplaObj.getRaplaType()).writeObject( raplaObj ); + } + setIndentLevel( start+1 ); + closeElement("rapla:mapentry"); + setIndentLevel( start ); + } + + } + + + diff --git a/Rapla/src/org/rapla/storage/xml/RaplaSAXPipeline.java b/Rapla/src/org/rapla/storage/xml/RaplaSAXPipeline.java new file mode 100644 index 0000000..e7850f2 --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/RaplaSAXPipeline.java @@ -0,0 +1,265 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org . | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.storage.xml; + +import java.util.*; +import java.io.*; +import java.net.URL; + +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.sax.SAXTransformerFactory; +import javax.xml.transform.sax.SAXSource; +import javax.xml.transform.sax.SAXResult; +import javax.xml.transform.TransformerException; + +import org.xml.sax.SAXParseException; +import org.xml.sax.ErrorHandler; +import org.xml.sax.XMLReader; +import org.xml.sax.XMLFilter; +import org.xml.sax.ContentHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.XMLFilterImpl; + +import org.apache.avalon.framework.logger.Logger; +import org.apache.avalon.framework.logger.LogEnabled; + +import org.rapla.components.util.Assert; +import org.rapla.components.util.xml.XMLReaderAdapter; +import org.rapla.components.util.xml.XMLTransformerAdapter; +import org.rapla.framework.RaplaException; + +class RaplaSAXPipeline implements LogEnabled { + Vector filters = new Vector(); + String factoryName; + XMLFilter mainFilter; + RaplaErrorHandler errorHandler; + String xmlParser; + + SAXTransformerFactory stf; + XMLReader stylesheetReader; + + Logger logger = null; + + RaplaSAXPipeline() { + mainFilter = new XMLFilterImpl(); + errorHandler = new RaplaErrorHandler(); + } + + public void enableLogging(Logger logger) { + this.logger = logger; + errorHandler.enableLogging(logger); + } + + private Transformer createTransformer(InputSource in) throws RaplaException,SAXException { + SAXTransformerFactory stf = XMLTransformerAdapter.getTransformerFactory(); + try { + if (stylesheetReader == null) { + stylesheetReader = XMLReaderAdapter.createXMLReader(false); + } + return stf.newTransformer(new SAXSource(stylesheetReader,in)); + } catch (TransformerConfigurationException ex) { + throw new RaplaException(ex); + } + } + + public void addTransformer(URL file) + throws RaplaException + ,IOException + ,SAXException + { + addTransformer(file,new String[0][2]); + } + + public void addTransformer(URL file,String[][] parameter) + throws RaplaException + ,IOException + ,SAXException + { + if (logger != null && logger.isInfoEnabled()) + logger.info("Creating new transformer with stylesheet '" + file + "'"); + Transformer transformer = createTransformer(new InputSource(file.toString())); + for (int i=0;i"); + ident = ident + " "; + super.startElement(uri,localName,qName,atts); + copy.startElement(uri,localName,qName,atts); + } + + public void endElement(String uri,String localName,String qName) throws SAXException { + ident = ident.substring(1); + System.out.print( ident + "" ); + super.endElement(uri,localName,qName); + copy.endElement(uri,localName,qName); + } + + public void startDocument() throws SAXException { + System.out.println("Start document"); + super.startDocument(); + copy.startDocument(); + } + public void endDocument() throws SAXException { + super.endDocument(); + copy.endDocument(); + } + public void startPrefixMapping(String prefix,String uri) throws SAXException { + super.startPrefixMapping(prefix,uri); + copy. startPrefixMapping(prefix,uri); + } + public void endPrefixMapping(String prefix) throws SAXException { + super.endPrefixMapping(prefix); + copy.endPrefixMapping(prefix); + } + public void characters(char[] ch,int start,int length) throws SAXException { + super.characters(ch,start,length); + copy.characters(ch,start,length); + } + public void ignorableWhitespace(char[] ch,int start,int length) throws SAXException { + super.ignorableWhitespace(ch,start,length); + copy.ignorableWhitespace(ch,start,length); + } + public void processingInstruction(String target,String data) throws SAXException { + super.processingInstruction(target,data); + copy.processingInstruction(target,data); + } + public void skippedEntity(String name) throws SAXException { + super.skippedEntity(name); + copy.skippedEntity(name); + } + public void setDocumentLocator(Locator locator) { + super.setDocumentLocator(locator); + copy.setDocumentLocator(locator); + } + + public void setErrorHandler(ErrorHandler handler) { + super.setErrorHandler(handler); + copy.setErrorHandler(handler); + } + + public XMLFilter getCopy() { + return copy; + } +} +*/ +class RaplaErrorHandler implements ErrorHandler,LogEnabled { + Logger logger = null; + public void enableLogging(Logger logger) { + this.logger = logger; + } + + public void error(SAXParseException exception) throws SAXException { + throw exception; + } + + public void fatalError(SAXParseException exception) throws SAXException { + throw exception; + } + + public void warning(SAXParseException exception) throws SAXException { + if (logger != null) + logger.error("Warning: " + getString(exception)); + } + + public String getString(SAXParseException exception) { + // return "Line " + exception.getLineNumber() + // + "\t Col " + exception.getColumnNumber() + // + "\t " + + return exception.getMessage(); + } +} diff --git a/Rapla/src/org/rapla/storage/xml/RaplaXMLReader.java b/Rapla/src/org/rapla/storage/xml/RaplaXMLReader.java new file mode 100644 index 0000000..edce6aa --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/RaplaXMLReader.java @@ -0,0 +1,329 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org . | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.storage.xml; + +import java.text.ParseException; +import java.util.Date; +import java.util.Map; + +import org.apache.avalon.framework.logger.Logger; +import org.rapla.components.util.SerializableDateTimeFormat; +import org.rapla.entities.Category; +import org.rapla.entities.EntityNotFoundException; +import org.rapla.entities.Ownable; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.RaplaType; +import org.rapla.entities.User; +import org.rapla.entities.domain.Permission; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.dynamictype.internal.AttributeImpl; +import org.rapla.entities.internal.CategoryImpl; +import org.rapla.entities.storage.EntityResolver; +import org.rapla.entities.storage.RefEntity; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.framework.RaplaLocale; +import org.rapla.storage.IdTable; +import org.rapla.storage.LocalCache; +import org.rapla.storage.impl.EntityStore; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + +public class RaplaXMLReader extends DelegationHandler implements Namespaces +{ + EntityStore resolver; + Logger logger; + IdTable idTable; + RaplaContext sm; + Map localnameMap; + Map readerMap; + SerializableDateTimeFormat dateTimeFormat; + + public RaplaXMLReader( RaplaContext context ) throws RaplaException + { + logger = (Logger) context.lookup( Logger.class.getName() ); + this.sm = context; + this.resolver = (EntityStore) context.lookup( EntityStore.class.getName()); + this.idTable = (IdTable) context.lookup( IdTable.class.getName() ); + RaplaLocale raplaLocale = (RaplaLocale) context.lookup( RaplaLocale.ROLE ); + dateTimeFormat = new SerializableDateTimeFormat( raplaLocale.createCalendar() ); + this.localnameMap = (Map) context.lookup( PreferenceReader.LOCALNAMEMAPENTRY ); + this.readerMap = (Map) context.lookup( PreferenceReader.READERMAP ); + } + + public RaplaType getTypeForLocalName( String localName ) + throws SAXParseException + { + RaplaType type = (RaplaType) localnameMap.get( localName ); + if (type == null) + throw createSAXParseException( "No type declared for localname " + localName ); + return type; + } + + /** + * @param raplaType + * @throws SAXParseException + */ + protected RaplaXMLReader getChildHandlerForType( RaplaType raplaType ) + throws SAXParseException + { + RaplaXMLReader childReader = (RaplaXMLReader) readerMap.get( raplaType ); + if (childReader == null) + { + throw createSAXParseException( "No Reader declared for type " + raplaType ); + } + childReader.setDocumentLocator( getLocator() ); + addChildHandler( childReader ); + return childReader; + } + + protected Logger getLogger() + { + return logger; + } + + + + protected boolean isContentCategoryId( String content ) + { + if ( content == null) + { + return false; + } + content = content.trim(); + String KEY_START = Category.TYPE.getLocalName() + "_"; + boolean idContent = (content.indexOf( KEY_START )< 2 && content.length() < KEY_START.length() + 5 && content.length() > 0); + return idContent; + } + + + public Long parseLong( String text ) throws SAXException + { + try + { + return new Long( text ); + } + catch (NumberFormatException ex) + { + throw createSAXParseException( "No valid number format: " + text ); + } + } + + public Date parseDate( String date, boolean fillDate ) throws SAXException + { + try + { + return dateTimeFormat.parseDate( date, fillDate ); + } + catch (ParseException ex) + { + throw createSAXParseException( ex.getMessage() ); + } + } + + public Date parseDateTime( String date, String time ) throws SAXException + { + try + { + return dateTimeFormat.parseDateTime( date, time ); + } + catch (ParseException ex) + { + throw createSAXParseException( ex.getMessage() ); + } + } + + + + protected String getString( + Attributes atts, + String key, + String defaultString ) + { + String str = atts.getValue( "", key ); + return (str != null) ? str : defaultString; + } + + protected String getString( Attributes atts, String key ) + throws SAXParseException + { + String str = atts.getValue( "", key ); + if (str == null) + throw createSAXParseException( "Attribute " + key + " not found!" ); + return str; + } + + /** return the new id */ + protected Object setId( RefEntity entity, Attributes atts ) + throws SAXException + { + String idString = atts.getValue( "id" ); + Object id = getId( entity.getRaplaType(), idString ); + entity.setId( id ); + return id; + } + + protected void setVersionIfThere( RefEntity entity, Attributes atts ) + throws SAXException + { + String version= atts.getValue( "version" ); + if ( version != null) + { + try { + entity.setVersion( Long.parseLong( version)); + } + catch (NumberFormatException ex) + { + createSAXParseException( "Error parsing version-string '" + version + "'"); + } + } + } + + /** return the new id */ + protected Object setNewId( RefEntity entity ) throws SAXException + { + try + { + Object id = idTable.createId( entity.getRaplaType() ); + entity.setId( id ); + return id; + } + catch (RaplaException ex) + { + throw createSAXParseException( ex.getMessage() ); + } + } + + protected void setOwner( Ownable ownable, Attributes atts ) + throws SAXException + { + String ownerString = atts.getValue( "owner" ); + if (ownerString != null) + { + ownable.setOwner( (User) resolve( User.TYPE, ownerString ) ); + } + } + + + protected Object getId( RaplaType type, String str ) throws SAXException + { + try + { + Object id = LocalCache.getId( type, str ); + return id; + } + catch (ParseException ex) + { + ex.printStackTrace(); + throw createSAXParseException( ex.getMessage() ); + } + } + + void throwEntityNotFound( String type, Integer id ) throws SAXException + { + throw createSAXParseException( type + " with id '" + id + "' not found." ); + } + + public RaplaObject getType() throws SAXException + { + throw createSAXParseException( "Method getType() not implemented by subclass " + this.getClass().getName() ); + } + + protected CategoryImpl getSuperCategory() + { + return resolver.getSuperCategory(); + } + + public DynamicType getDynamicType( String keyref ) + { + return resolver.getDynamicType( keyref); + } + + protected Object resolve( RaplaType type, String str ) throws SAXException + { + try + { + return resolver.resolve( getId( type, str ) ); + } + catch (EntityNotFoundException ex) + { + throw createSAXParseException( ex.getMessage() ); + } + } + + protected Object parseAttributeValue( Attribute attribute, String text ) throws SAXException + { + try + { + EntityResolver resolver = null; + if (isContentCategoryId( text)) + resolver = this.resolver; + return AttributeImpl.parseAttributeValue( attribute, text, resolver ); + } + catch (ParseException ex) + { + throw createSAXParseException( ex.getMessage() ); + } + } + + public void add(RefEntity entity){ + resolver.put(entity); + } + + public void remove(String localname, String id) throws SAXException + { + RaplaType type = getTypeForLocalName( localname); + Object idObject = getId( type, id ); + resolver.addRemoveId( idObject ); + } + + protected Category getCategoryFromPath( String path ) throws SAXParseException + { + try + { + return getSuperCategory().getCategoryFromPath( path ); + } + catch (Exception ex) + { + throw createSAXParseException( ex.getMessage() ); + } + } + + protected Category getGroup(String groupKey) throws SAXParseException{ + CategoryImpl groupCategory = (CategoryImpl) getSuperCategory().getCategory( + Permission.GROUP_CATEGORY_KEY ); + if (groupCategory == null) + { + throw createSAXParseException( Permission.GROUP_CATEGORY_KEY + " category not found" ); + } + try + { + return groupCategory.getCategoryFromPath( groupKey ); + } + catch (Exception ex) + { + throw createSAXParseException( ex ); + } + } + + + protected void putPassword( Object userid, String password ) + { + resolver.putPassword( userid, password); + } + + +} diff --git a/Rapla/src/org/rapla/storage/xml/RaplaXMLWriter.java b/Rapla/src/org/rapla/storage/xml/RaplaXMLWriter.java new file mode 100644 index 0000000..4328afb --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/RaplaXMLWriter.java @@ -0,0 +1,258 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.storage.xml; + +import java.io.IOException; +import java.util.Date; +import java.util.Iterator; +import java.util.Map; + +import org.apache.avalon.framework.logger.Logger; +import org.rapla.components.util.Assert; +import org.rapla.components.util.SerializableDateTimeFormat; +import org.rapla.components.util.xml.XMLWriter; +import org.rapla.entities.Annotatable; +import org.rapla.entities.Category; +import org.rapla.entities.MultiLanguageName; +import org.rapla.entities.Ownable; +import org.rapla.entities.RaplaObject; +import org.rapla.entities.RaplaType; +import org.rapla.entities.Timestamp; +import org.rapla.entities.User; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.AttributeType; +import org.rapla.entities.dynamictype.ConstraintIds; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.internal.CategoryImpl; +import org.rapla.entities.storage.RefEntity; +import org.rapla.entities.storage.internal.SimpleIdentifier; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.framework.RaplaLocale; +import org.rapla.storage.LocalCache; +import org.xml.sax.helpers.NamespaceSupport; + +/** Stores the data from the local cache in XML-format to a print-writer.*/ +abstract public class RaplaXMLWriter extends XMLWriter + implements Namespaces +{ + protected LocalCache cache; + + String encoding = "utf-8"; + final static String OUTPUT_FILE_VERSION="1.0"; + protected NamespaceSupport namespaceSupport = new NamespaceSupport(); + private boolean isIdOnly; + private boolean printVersion; + private Map localnameMap; + Logger logger; + Map writerMap; + protected RaplaContext sm; + public SerializableDateTimeFormat dateTimeFormat; + public RaplaXMLWriter( RaplaContext sm) throws RaplaException { + this.sm = sm; + enableLogging( (Logger) sm.lookup( Logger.class.getName())); + this.writerMap = (Map) sm.lookup( PreferenceWriter.WRITERMAP ); + RaplaLocale raplaLocale = (RaplaLocale) sm.lookup(RaplaLocale.ROLE); + dateTimeFormat = new SerializableDateTimeFormat(raplaLocale.createCalendar()); + this.localnameMap = (Map) sm.lookup(PreferenceReader.LOCALNAMEMAPENTRY); + this.cache = (LocalCache) sm.lookup(LocalCache.class.getName()); + this.isIdOnly = sm.has("idonly"); + this.printVersion = sm.has("printversion"); + Assert.notNull(cache); + + namespaceSupport.pushContext(); + for (int i=0;i 0) + { + Appointment[] apps = (Appointment[]) + restrictionsOldSchema.toArray( Appointment.EMPTY_ARRAY ); + reservation.setRestrictionForId( allocatableId, apps ); + } + } + } +} + + + + diff --git a/Rapla/src/org/rapla/storage/xml/ReservationWriter.java b/Rapla/src/org/rapla/storage/xml/ReservationWriter.java new file mode 100644 index 0000000..36923f1 --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/ReservationWriter.java @@ -0,0 +1,152 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org . | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ + +package org.rapla.storage.xml; + +import java.io.IOException; +import java.util.Date; +import java.util.Iterator; + +import org.rapla.entities.RaplaObject; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Repeating; +import org.rapla.entities.domain.Reservation; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; + + +public class ReservationWriter extends ClassifiableWriter { + public ReservationWriter(RaplaContext sm) throws RaplaException { + super(sm); + } + + protected void printReservation(Reservation r) throws IOException,RaplaException { + openTag("rapla:reservation"); + printId(r); + printVersion( r); + printOwner(r); + printTimestamp(r); + closeTag(); + // System.out.println(((Entity)r).getId() + " Name: " + r.getName() +" User: " + r.getUser()); + printClassification(r.getClassification()); + { + Appointment[] appointments = r.getAppointments(); + for (int i = 0; i< appointments.length; i ++) { + printAppointment(appointments[i]); + } + } + + Allocatable[] allocatables = r.getAllocatables(); + // Print allocatables that dont have a restriction + for (int i=0; i< allocatables.length; i ++) { + Allocatable allocatable = allocatables[i]; + if (r.getRestriction( allocatable ).length > 0 ) + { + continue; + } + openTag("rapla:allocate"); + printIdRef( allocatable ); + closeElementTag(); + } + + closeElement("rapla:reservation"); + } + + public void writeObject( RaplaObject object ) throws IOException, RaplaException + { + printReservation( (Reservation) object); + } + + protected void printAppointment(Appointment appointment) throws IOException,RaplaException { + openTag("rapla:appointment"); + if (isIdOnly()) { + printId( appointment ); + } + printVersion( appointment); + + att("start-date",dateTimeFormat.formatDate( appointment.getStart())); + + if (appointment.isWholeDaysSet()) { + boolean bCut = appointment.getEnd().after(appointment.getStart()); + att("end-date",dateTimeFormat.formatDate(appointment.getEnd(),bCut)); + } else { + att("start-time",dateTimeFormat.formatTime( appointment.getStart())); + att("end-date",dateTimeFormat.formatDate( appointment.getEnd())); + att("end-time",dateTimeFormat.formatTime( appointment.getEnd())); + } + + Allocatable[] allocatables = appointment.getReservation().getRestrictedAllocatables(appointment); + if (appointment.getRepeating() == null && allocatables.length == 0) + { + closeElementTag(); + } + else + { + closeTag(); + if (appointment.getRepeating() != null) { + printRepeating(appointment.getRepeating()); + } + for (int i=0; i< allocatables.length; i ++) { + Allocatable allocatable = allocatables[i]; + openTag("rapla:allocate"); + printIdRef( allocatable ); + closeElementTag(); + } + closeElement("rapla:appointment"); + } + } + + private void printRepeating(Repeating r) throws IOException { + openTag("rapla:repeating"); + if (r.getInterval()!=1) + att("interval",String.valueOf(r.getInterval())); + att("type",r.getType().toString()); + if (r.isFixedNumber()) { + att("number",String.valueOf(r.getNumber())); + } else { + if (r.getEnd() != null) + att("end-date" + ,dateTimeFormat.formatDate(r.getEnd(),true)); + } + Date[] exceptions = r.getExceptions(); + if (exceptions.length==0) { + closeElementTag(); + return; + } + closeTag(); + for (int i=0;i"); + Iterator it = cache.getIterator(User.TYPE); + while (it.hasNext()) { + printUser((User) it.next(), true); + } + closeElement("rapla:users"); + } + + + +} + + diff --git a/Rapla/src/org/rapla/storage/xml/WrongVersionException.java b/Rapla/src/org/rapla/storage/xml/WrongVersionException.java new file mode 100644 index 0000000..4e4c4d7 --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/WrongVersionException.java @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org . | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.storage.xml; + +class WrongVersionException extends Exception { + private static final long serialVersionUID = 1L; + + String version; + public WrongVersionException(String version) { + super("Wrong Version Exception " + version); + this.version = version; + } + + public String getVersion() { + return version; + } + +} diff --git a/Rapla/src/org/rapla/storage/xml/convert0_4to0_5.xsl b/Rapla/src/org/rapla/storage/xml/convert0_4to0_5.xsl new file mode 100644 index 0000000..4fde9fb --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/convert0_4to0_5.xsl @@ -0,0 +1,232 @@ + + + + + + + 0.5 + + + + + + + + default resource + Standard Ressource + + {name} + + + name + Name + + + + + + + person + Person + + {surname} {forename} + + + + title + Titel + + + + + surname + Nachname + + + + forename + Vorname + + + + + + + default reservation + Standard Reservierung + + {name} + + + eventname + Veranstaltungsname + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {surname} {forename} + + + title + Titel + + + + surname + Nachname + + + + forename + Vorname + + + + + + + {name} + + + name + Name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Rapla/src/org/rapla/storage/xml/convert0_5to0_6.xsl b/Rapla/src/org/rapla/storage/xml/convert0_5to0_6.xsl new file mode 100644 index 0000000..a9cffd6 --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/convert0_5to0_6.xsl @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + allocate + + + + + \ No newline at end of file diff --git a/Rapla/src/org/rapla/storage/xml/convert0_6to0_7.xsl b/Rapla/src/org/rapla/storage/xml/convert0_6to0_7.xsl new file mode 100644 index 0000000..6f3d546 --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/convert0_6to0_7.xsl @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Benutzergruppen + user-groups + + + + + + + + + + + + + + + + + diff --git a/Rapla/src/org/rapla/storage/xml/convert0_7to0_8.xsl b/Rapla/src/org/rapla/storage/xml/convert0_7to0_8.xsl new file mode 100644 index 0000000..0979610 --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/convert0_7to0_8.xsl @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Benutzergruppen + user-groups + + + + + + + + + diff --git a/Rapla/src/org/rapla/storage/xml/convert0_8to0_9.xsl b/Rapla/src/org/rapla/storage/xml/convert0_8to0_9.xsl new file mode 100644 index 0000000..9f44d1d --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/convert0_8to0_9.xsl @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + register resources + Ressourcen eintragen + Enregistrer des ressources + + + + + modify preferences + Einstellungen bearbeiten + Modifier la préférence + + + + + + + + diff --git a/Rapla/src/org/rapla/storage/xml/convert0_9to1_0.xsl b/Rapla/src/org/rapla/storage/xml/convert0_9to1_0.xsl new file mode 100644 index 0000000..140d13a --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/convert0_9to1_0.xsl @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Rapla/src/org/rapla/storage/xml/rapla.rng b/Rapla/src/org/rapla/storage/xml/rapla.rng new file mode 100644 index 0000000..5be910f --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/rapla.rng @@ -0,0 +1,418 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Rapla/src/org/rapla/storage/xml/relax.rng b/Rapla/src/org/rapla/storage/xml/relax.rng new file mode 100644 index 0000000..165ba2f --- /dev/null +++ b/Rapla/src/org/rapla/storage/xml/relax.rng @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +choice +interleave + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Rapla/templates/javadoc.css b/Rapla/templates/javadoc.css new file mode 100644 index 0000000..631319d --- /dev/null +++ b/Rapla/templates/javadoc.css @@ -0,0 +1,23 @@ +/* Rapla Javadoc style sheet */ + +/* Page background color */ +body { background-color: #FFFFFF } + +/* Table colors */ +.TableHeadingColor { background: #D0D0D0 } +.TableSubHeadingColor { background: #E0E0E0 } + +/* Navigation bar fonts and colors */ +.NavBarCell1 { background-color:#4c9b4c;} +.NavBarCell1Rev { background-color:#A0A0A0;} +.NavBarFont1 { font-family: Verdana, sans-serif; color:#000000;} +.NavBarFont1Rev { font-family: Verdana, sans-serif; color:#FFFFFF;} +.NavBarCell2 { font-family: Verdana, sans-serif; background-color:#D0D0D0;} +.NavBarCell3 { font-family: Verdana, sans-serif; background-color:#F0F0F0;} + +/* Font used in left-hand frame lists */ +.FrameTitleFont { font-size: normal; font-family: Verdana, sans-serif } +.FrameHeadingFont { font-size: normal; font-family: Verdana, sans-serif } +.FrameItemFont { font-size: 80%; font-family: Verdana, sans-serif } + + diff --git a/Rapla/templates/rapla.exe b/Rapla/templates/rapla.exe new file mode 100644 index 0000000..9b28000 Binary files /dev/null and b/Rapla/templates/rapla.exe differ diff --git a/Rapla/templates/scripts/rapla_bat b/Rapla/templates/scripts/rapla_bat new file mode 100644 index 0000000..9025f34 --- /dev/null +++ b/Rapla/templates/scripts/rapla_bat @@ -0,0 +1,20 @@ +@echo off +:: ------------------------------------------------------------------------- +:: start.bat +:: Script for starting @doc.name@ Version @doc.version@ under Windows +:: Set either JAVA_HOME to point at your Java Development Kit installation. +:: or PATH to point at the java command +:: +set _PROG_DIR=%~dp0 +echo PROG_DIR %_PROG_DIR% +set RAPLA_JAVA=%JAVA_HOME%\bin\ +if not "%JAVA_HOME%" == "" goto gotJavaHome + set RAPLA_JAVA="" +:gotJavaHome +set RAPLA_JAVA_OPTIONS=%JAVA_OPTIONS% +if not "%JAVA_OPTIONS%" == "" goto gotJavaOptions + set RAPLA_JAVA_OPTIONS="-Xmx128M" +:gotJavaOptions +cd %_PROG_DIR% +"%RAPLA_JAVA%java" %RAPLA_JAVA_OPTIONS% -jar raplabootstrap.jar %1 %2 %3 %4 + diff --git a/Rapla/templates/scripts/rapla_sh b/Rapla/templates/scripts/rapla_sh new file mode 100644 index 0000000..d5d2e35 --- /dev/null +++ b/Rapla/templates/scripts/rapla_sh @@ -0,0 +1,47 @@ +#!/bin/sh +# +# Script for starting @doc.name@ version @doc.version@ under Unix +# Set either JAVA_HOME to point at your Java Development Kit installation. +# or PATH to point at the java command + +# resolve links - $0 may be a softlink +PRG="$0" +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '.*/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`/"$link" + fi +done +PRGDIR=`dirname "$PRG"` + +if [ -z "$JAVA_HOME" ] ; then + JAVA=`which java` + if [ -z "$JAVA" ] ; then + echo "Cannot find JAVA. You must set JAVA_HOME to point at your Java Development Kit installation." + exit 1 + fi + JAVA_BINDIR=`dirname $JAVA` + JAVA_HOME=$JAVA_BINDIR/.. + echo "Guessing JAVA_HOME:" $JAVA_HOME +fi +if [ -z "$JAVA_OPTIONS" ] ; then + JAVA_OPTIONS="-Xmx128M" + echo "Guessing JAVA_OPTIONS:" $JAVA_HOME +fi + +if [ ! -x $PRGDIR/webapp ] ; then + chmod +x $PRGDIR/webapp +fi +if [ ! -x $PRGDIR/webapp/WEB-INF ] ; then + chmod +x $PRGDIR/webapp/WEB-INF +fi +if [ ! -x $PRGDIR/webapp/WEB-INF/lib ] ; then + chmod +x $PRGDIR/webapp/WEB-INF/lib +fi + +echo "PROGDIR" $PRGDIR +cd $PRGDIR +$JAVA_HOME/bin/java $JAVA_OPTIONS -jar raplabootstrap.jar $1 $2 $3 $4 diff --git a/Rapla/templates/scripts/raplaclient_bat b/Rapla/templates/scripts/raplaclient_bat new file mode 100644 index 0000000..0806241 --- /dev/null +++ b/Rapla/templates/scripts/raplaclient_bat @@ -0,0 +1,3 @@ +cls +echo webapp/webclient/raplaclient.xconf is used check remote server settings +call rapla.bat client %1 %2 %3 %4 \ No newline at end of file diff --git a/Rapla/templates/scripts/raplaexport_bat b/Rapla/templates/scripts/raplaexport_bat new file mode 100644 index 0000000..352f0b8 --- /dev/null +++ b/Rapla/templates/scripts/raplaexport_bat @@ -0,0 +1,5 @@ +cls +echo raplaserver.xconf is used check file and sql settings +pause +call rapla.bat -c .\webapp\WEB-INF\raplaserver.xconf export +pause \ No newline at end of file diff --git a/Rapla/templates/scripts/raplaimport_bat b/Rapla/templates/scripts/raplaimport_bat new file mode 100644 index 0000000..8905f3c --- /dev/null +++ b/Rapla/templates/scripts/raplaimport_bat @@ -0,0 +1,5 @@ +cls +echo raplaserver.xconf is used check file and sql settings +pause +call rapla.bat -c .\webapp\WEB-INF\raplaserver.xconf import +pause \ No newline at end of file diff --git a/Rapla/templates/scripts/raplaserver_bat b/Rapla/templates/scripts/raplaserver_bat new file mode 100644 index 0000000..8fc42ac --- /dev/null +++ b/Rapla/templates/scripts/raplaserver_bat @@ -0,0 +1,33 @@ +@echo off +:: ------------------------------------------------------------------------- +:: startserver.bat +:: Script for starting @doc.name@ Version @doc.version@ server under Windows +:: Set either JAVA_HOME to point at your Java Development Kit installation. +:: or PATH to point at the java command +:: +set _PROG_DIR=%~dp0 +echo PROG_DIR %_PROG_DIR% +set RAPLA_JAVA=%JAVA_HOME%\bin\ +if not "%JAVA_HOME%" == "" goto gotJavaHome + set RAPLA_JAVA="" +:gotJavaHome +set RAPLA_JAVA_OPTIONS=%JAVA_OPTIONS% +if not "%JAVA_OPTIONS%" == "" goto gotJavaOptions + set RAPLA_JAVA_OPTIONS="-Xmx512M" +:gotJavaOptions + +:: Backward compatibility for old versions +if not "%1" == "import" goto conti1 + call rapla.bat import +goto finish +:conti1 +if not "%1" == "export" goto conti2 + call rapla.bat export +goto finish +:conti2 + +echo USING OPTIONS %RAPLA_JAVA_OPTIONS% +cd %_PROG_DIR% +"%RAPLA_JAVA%java" %RAPLA_JAVA_OPTIONS% -cp raplabootstrap.jar org.rapla.bootstrap.RaplaServerLoader %1 %2 %3 %4 +:finish + diff --git a/Rapla/templates/scripts/raplaserver_sh b/Rapla/templates/scripts/raplaserver_sh new file mode 100644 index 0000000..ba61876 --- /dev/null +++ b/Rapla/templates/scripts/raplaserver_sh @@ -0,0 +1,156 @@ +#!/bin/sh +# +# Script for starting @doc.name@ version @doc.version@ with jetty webserver under Unix +# Set either JAVA_HOME to point at your Java Development Kit installation. +# or PATH to point at the java command +usage() +{ + echo "Usage: $0 {run|start|stop|restart|supervise} " + echo "run : starts rapla-server and wait for Control-c " + echo "start : starts rapla-server in the background (use only when run works)" + echo "stop : stops rapla-server in the background " + echo "restart : calls stop then start " + echo "import : imports the data from xml-file into the database " + echo "export : exports the data from the database into the xml-file" + exit 1 +} + +################################################## +# Find directory function +################################################## +findDirectory() +{ + OP=$1 + shift + for L in $* ; do + [ $OP $L ] || continue + echo $L + break + done +} + + +# ----- Verify and Set Required Environment Variables ------------------------- + +PRG=$0 +ACTION=$1 + + +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '.*/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`/"$link" + fi +done +PRGDIR=`dirname "$PRG"` + +if [ -z "$JAVA_HOME" ] ; then + JAVA=`which java` + if [ -z "$JAVA" ] ; then + echo "Cannot find JAVA. You must set JAVA_HOME to point at your Java Development Kit installation." + exit 1 + fi + JAVA_BINDIR=`dirname $JAVA` + JAVA_HOME=$JAVA_BINDIR/.. + echo "Guessing JAVA_HOME:" $JAVA_HOME +fi +if [ -z "$JAVA_OPTIONS" ] ; then + JAVA_OPTIONS="-Xmx512M" + echo "Guessing JAVA_OPTIONS:" $JAVA_OPTIONS +fi + +JAVA="$JAVA_HOME/bin/java" +echo "PROGDIR" $PRGDIR +cd $PRGDIR +RUN_CMD="$JAVA $JAVA_OPTIONS -cp raplabootstrap.jar -Djava.awt.headless=true org.rapla.bootstrap.RaplaServerLoader" + +##################################################### +# Find a location for the pid file +##################################################### +if [ -z "$JETTY_RUN" ] +then + JETTY_RUN=`findDirectory -w /var/run /usr/var/run .` +fi + +##################################################### +# Find a PID for the pid file +##################################################### +if [ -z "$JETTY_PID" ] +then + JETTY_PID="$JETTY_RUN/raplajetty.pid" +fi + +##################################################### +# Find a location for the jetty console +##################################################### +if [ -z "$JETTY_CONSOLE" ] +then + if [ -w /dev/console ] + then + JETTY_CONSOLE=/dev/console + else + JETTY_CONSOLE=/dev/tty + fi +fi + + +# ----- Do the action ---------------------------------------------------------- + +################################################## +# Do the action +################################################## +case "$ACTION" in + import) + $PRGDIR/rapla.sh import + ;; + export) + $PRGDIR/rapla.sh export + ;; + start) + if [ -f $JETTY_PID ] + then + echo "WARNING $JETTY_PID found. Jetty is probably running!!" + fi + echo "Running Rapla in Jetty: " $RUN_CMD + exec $RUN_CMD >>$JETTY_CONSOLE 2>&1 1>/dev/null & + echo $! > $JETTY_PID + echo "Jetty running pid="`cat $JETTY_PID` + ;; + stop) + PID=`cat $JETTY_PID 2>/dev/null` + echo "Shutting down Jetty: $PID" + kill $PID 2>/dev/null + sleep 2 + kill -9 $PID 2>/dev/null + rm -f $JETTY_PID + echo "STOPPED `date`" >>$JETTY_CONSOLE + ;; + + restart) + $0 stop $* + sleep 5 + $0 start $* + ;; + + supervise) + # + # Under control of daemontools supervise monitor which + # handles restarts and shutdowns via the svc program. + # + exec $RUN_CMD + ;; + + run) + echo "Running Rapla in Jetty: " $RUN_CMD + exec $RUN_CMD + ;; + +*) + usage + ;; +esac + +exit 0 diff --git a/Rapla/test-src/org/rapla/CommunicatorTest.java b/Rapla/test-src/org/rapla/CommunicatorTest.java new file mode 100644 index 0000000..e7c98c6 --- /dev/null +++ b/Rapla/test-src/org/rapla/CommunicatorTest.java @@ -0,0 +1,128 @@ +package org.rapla; + +import java.util.Date; + +import org.apache.avalon.framework.configuration.DefaultConfiguration; +import org.rapla.entities.configuration.Preferences; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.ClassificationFilter; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.facade.ClientFacade; +import org.rapla.framework.RaplaContext; +import org.rapla.storage.dbrm.RemoteOperator; + +public class CommunicatorTest extends ServletTestBase +{ + + public CommunicatorTest( String name ) + { + super( name ); + } + + + public void testLargeform() throws Exception + { + ClientFacade facade = (ClientFacade)getContext().lookup(ClientFacade.ROLE + "/remote-facade-3"); + facade.login("homer","duffs".toCharArray()); + Allocatable alloc = facade.newResource(); + StringBuffer buf = new StringBuffer(); + int stringsize = 100000; + for (int i=0;i< stringsize;i++) + { + buf.append( "xxxxxxxxxx"); + } + String verylongname = buf.toString(); + alloc.getClassification().setValue("name", verylongname); + facade.store( alloc); + } + + + public void testClient() throws Exception + { + ClientFacade facade = (ClientFacade)getContext().lookup(ClientFacade.ROLE + "/remote-facade-3"); + boolean success = facade.login("admin","test".toCharArray()); + assertFalse( "Login should fail",success ); + facade.login("homer","duffs".toCharArray()); + try + { + Preferences preferences = (Preferences)facade.edit( facade.getPreferences( null)); + preferences.putEntry("test-entry", "test-value"); + + facade.store( preferences); + preferences = (Preferences)facade.edit( facade.getPreferences( null)); + preferences.putEntry("test-entry", "test-value"); + facade.store( preferences); + + Allocatable[] allocatables = facade.getAllocatables(); + assertTrue( allocatables.length > 0); + Reservation[] events = facade.getReservations( new Allocatable[] {allocatables[0]}, null,null); + assertTrue( events.length > 0); + + Reservation r = events[0]; + Reservation editable = (Reservation)facade.edit( r); + facade.store( editable ); + + Reservation newEvent = facade.newReservation(); + Appointment newApp = facade.newAppointment( new Date(), new Date()); + newEvent.addAppointment( newApp ); + newEvent.getClassification().setValue("name","Test Reservation"); + newEvent.addAllocatable( allocatables[0]); + + facade.store( newEvent ); + facade.remove( newEvent); + } + finally + { + facade.logout(); + } + } + + public void testUmlaute() throws Exception + { + ClientFacade facade = (ClientFacade)getContext().lookup(ClientFacade.ROLE + "/remote-facade-3"); + facade.login("homer","duffs".toCharArray()); + Allocatable alloc = facade.newResource(); + String typeName = alloc.getClassification().getType().getElementKey(); + String nameWithUmlaute = ""; + alloc.getClassification().setValue("name", nameWithUmlaute); + int allocSizeBefore = facade.getAllocatables().length; + facade.store( alloc); + + facade.logout(); + facade.login("homer","duffs".toCharArray()); + DynamicType type = facade.getDynamicType( typeName); + ClassificationFilter filter = type.newClassificationFilter(); + filter.addEqualsRule("name", nameWithUmlaute); + Allocatable[] allAllocs = facade.getAllocatables(); + assertEquals( allocSizeBefore + 1, allAllocs.length); + Allocatable[] allocs = facade.getAllocatables( new ClassificationFilter[] {filter}); + assertEquals( 1, allocs.length); + + } + public void testManyClients() throws Exception + { + RaplaContext context = getContext(); + int clientNum = 50; + RemoteOperator [] opts = new RemoteOperator[ clientNum]; + DefaultConfiguration remoteConfig = new DefaultConfiguration("element"); + DefaultConfiguration serverParam = new DefaultConfiguration("server"); + serverParam.setValue("http://localhost:8051/"); + remoteConfig.addChild( serverParam ); + for ( int i=0;i 0); + } + +} + + + + + diff --git a/Rapla/test-src/org/rapla/RaplaLocaleTest.java b/Rapla/test-src/org/rapla/RaplaLocaleTest.java new file mode 100644 index 0000000..e2e6ddd --- /dev/null +++ b/Rapla/test-src/org/rapla/RaplaLocaleTest.java @@ -0,0 +1,78 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla; +import java.text.ParseException; +import java.util.Calendar; +import java.util.Locale; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import org.apache.avalon.framework.configuration.DefaultConfiguration; +import org.rapla.components.util.SerializableDateTimeFormat; +import org.rapla.framework.RaplaException; +import org.rapla.framework.RaplaLocale; + +public class RaplaLocaleTest extends TestCase { + DefaultConfiguration config; + + public RaplaLocaleTest(String name) { + super(name); + } + + public static Test suite() { + return new TestSuite(RaplaLocaleTest.class); + } + + private DefaultConfiguration createConfig(String defaultLanguage,String countryString) { + DefaultConfiguration config = new DefaultConfiguration("locale",this.toString()); + DefaultConfiguration country = new DefaultConfiguration("country",this.toString()); + country.setValue(countryString); + config.addChild(country); + DefaultConfiguration languages = new DefaultConfiguration("languages",this.toString()); + config.addChild(languages); + languages.setAttribute("default",defaultLanguage); + DefaultConfiguration language1 = new DefaultConfiguration("language",this.toString()); + language1.setValue("de"); + DefaultConfiguration language2 = new DefaultConfiguration("language",this.toString()); + language2.setValue("en"); + languages.addChild(language1); + languages.addChild(language2); + return config; + } + + public void testDateFormat3() throws ParseException, RaplaException { + RaplaLocale raplaLocale = new RaplaLocaleImpl(createConfig("de","DE")); + String s = raplaLocale.formatDate(new SerializableDateTimeFormat().parseDate("2001-01-12",false)); + assertEquals( "12.01.01", s); + } + + public void testTimeFormat4() throws ParseException, RaplaException { + RaplaLocale raplaLocale= new RaplaLocaleImpl(createConfig("en","US")); + Calendar cal = Calendar.getInstance(raplaLocale.getTimeZone() + ,Locale.US); + cal.set(Calendar.HOUR_OF_DAY,21); + cal.set(Calendar.MINUTE,0); + String s = raplaLocale.formatTime(cal.getTime()); + assertEquals("9:00 PM", s); + } + + + +} + + + + + diff --git a/Rapla/test-src/org/rapla/RaplaTestCase.java b/Rapla/test-src/org/rapla/RaplaTestCase.java new file mode 100644 index 0000000..b028d88 --- /dev/null +++ b/Rapla/test-src/org/rapla/RaplaTestCase.java @@ -0,0 +1,106 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla; + +import java.io.File; +import java.io.IOException; +import java.net.URL; + +import junit.framework.TestCase; + +import org.apache.avalon.framework.container.ContainerUtil; +import org.apache.avalon.framework.logger.ConsoleLogger; +import org.apache.avalon.framework.logger.Logger; +import org.rapla.client.ClientService; +import org.rapla.components.util.IOUtil; +import org.rapla.components.util.SerializableDateTimeFormat; +import org.rapla.facade.ClientFacade; +import org.rapla.framework.Container; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaLocale; +import org.rapla.gui.toolkit.ErrorDialog; + +public abstract class RaplaTestCase extends TestCase { + protected Container raplaContainer; + private Logger logger = new ConsoleLogger(ConsoleLogger.LEVEL_WARN).getChildLogger("test"); + public static String TEST_FOLDER_NAME="temp/test"; + RaplaStartupEnvironment env = new RaplaStartupEnvironment(); + + public RaplaTestCase(String name) { + super(name); + try { + new File("temp").mkdir(); + File testFolder =new File(TEST_FOLDER_NAME); + testFolder.mkdir(); + IOUtil.copy( "test-src/test.xconf", TEST_FOLDER_NAME + "/test.xconf" ); + IOUtil.copy( "test-src/test.xlog", TEST_FOLDER_NAME + "/test.xlog" ); + } catch (IOException ex) { + throw new RuntimeException("Can't initialize config-files: " + ex.getMessage()); + } + } + + public void copyDataFile(String testFile) throws IOException { + try { + IOUtil.copy( testFile, TEST_FOLDER_NAME + "/test.xml" ); + } catch (IOException ex) { + throw new IOException("Failed to copy TestFile '" + testFile + "': " + ex.getMessage()); + } + } + + protected RaplaContext getContext() { + return raplaContainer.getContext(); + } + + protected SerializableDateTimeFormat formater() { + return new SerializableDateTimeFormat(); + } + + protected Logger getLogger() { + return logger; + } + + protected void setUp(String testFile) throws Exception { + ErrorDialog.THROW_ERROR_DIALOG_EXCEPTION = true; + + URL configURL = new URL("file:./" + TEST_FOLDER_NAME + "/test.xconf"); + URL logConfigURL = new URL(IOUtil.getBase(configURL), "test.xlog"); + env.setConfigURL( configURL); + env.setLogConfigURL( logConfigURL ); + copyDataFile("test-src/" + testFile); + raplaContainer = new RaplaMainContainer( env ); + assertNotNull("Container not initialized.",raplaContainer); + } + + protected void setUp() throws Exception { + setUp("testdefault.xml"); + } + + protected ClientService getClientService() throws Exception { + return (ClientService) getContext().lookup(ClientService.ROLE); + } + + protected ClientFacade getFacade() throws Exception { + return (ClientFacade) getContext().lookup(ClientFacade.ROLE); + } + + protected RaplaLocale getRaplaLocale() throws Exception { + return (RaplaLocale) getContext().lookup(RaplaLocale.ROLE); + } + + protected void tearDown() throws Exception { + if (raplaContainer != null) + ContainerUtil.dispose( raplaContainer ); + } + + +} diff --git a/Rapla/test-src/org/rapla/ServerTest.java b/Rapla/test-src/org/rapla/ServerTest.java new file mode 100644 index 0000000..8c29fcd --- /dev/null +++ b/Rapla/test-src/org/rapla/ServerTest.java @@ -0,0 +1,486 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Locale; + +import org.rapla.components.util.DateTools; +import org.rapla.entities.Category; +import org.rapla.entities.DependencyException; +import org.rapla.entities.Entity; +import org.rapla.entities.User; +import org.rapla.entities.configuration.CalendarModelConfiguration; +import org.rapla.entities.configuration.Preferences; +import org.rapla.entities.configuration.RaplaMap; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Permission; +import org.rapla.entities.domain.Repeating; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.AttributeType; +import org.rapla.entities.dynamictype.ClassificationFilter; +import org.rapla.entities.dynamictype.ConstraintIds; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.dynamictype.DynamicTypeAnnotations; +import org.rapla.facade.ClientFacade; +import org.rapla.framework.RaplaException; +import org.rapla.plugin.weekview.WeekViewFactory; +import org.rapla.server.ServerService; + +public class ServerTest extends ServletTestBase { + ServerService raplaServer; + + protected ClientFacade facade1; + Locale locale; + + public ServerTest(String name) { + super(name); + } + + static public void main(String[] args) { + String method = + "testLoad" + ; + ServerTest test = new ServerTest( method ); + try { + test.run(); + } catch (Throwable ex) { + ex.printStackTrace(); + } + } + + protected void setUp() throws Exception { + super.setUp(); + initTestData(); + // start the server + raplaServer = (ServerService) + getContext().lookup(ServerService.ROLE + "/" + getStorageName()); + // start the client service + facade1 = (ClientFacade) + getContext().lookup(ClientFacade.ROLE + "/remote-facade"); + facade1.login("homer","duffs".toCharArray()); + locale = Locale.getDefault(); + } + + protected void initTestData() throws Exception{ + + } + + protected String getStorageName() { + return "storage-file"; + } + + protected void tearDown() throws Exception { + facade1.logout(); + super.tearDown(); + } + + public void testLoad() throws Exception { + facade1.getAllocatables(); + } + + public void testChangeReservation() throws Exception { + ClientFacade facade2 = (ClientFacade) + getContext().lookup(ClientFacade.ROLE + "/remote-facade-2"); + facade2.login("homer","duffs".toCharArray()); + Reservation r1 = facade1.newReservation(); + String typeKey = r1.getClassification().getType().getElementKey(); + r1.getClassification().setValue("name","test-reservation"); + r1.addAppointment( facade1.newAppointment(facade1.today(), new Date())); + facade1.store( r1 ); + // Wait for the update + facade2.refresh(); + + Reservation r2 = findReservation(facade2, typeKey, "test-reservation"); + assertEquals(1, r2.getAppointments().length); + assertEquals(0, r2.getAllocatables().length); + + // Modify Reservation in first facade + Reservation r1clone = (Reservation) facade1.edit( r2); + r1clone.addAllocatable( facade1.getAllocatables()[0]); + facade1.store( r1clone ); + // Wait for the update + facade2.refresh(); + // test for modify in second facade + assertEquals(1, r2.getAllocatables().length); + facade2.logout(); + } + + public void testChangeDynamicType() throws Exception { + Allocatable allocatable = (Allocatable) facade1.getAllocatables()[0]; + assertEquals(3, allocatable.getClassification().getAttributes().length); + DynamicType type = (DynamicType) facade1.getDynamicType("room"); + Attribute newAttribute; + { + newAttribute = (Attribute) facade1.newAttribute(AttributeType.CATEGORY); + DynamicType typeEdit1 = (DynamicType) facade1.edit(type); + newAttribute.setConstraint(ConstraintIds.KEY_ROOT_CATEGORY, facade1.getUserGroupsCategory()); + newAttribute.setKey("test"); + newAttribute.getName().setName("en","test"); + typeEdit1.addAttribute(newAttribute); + facade1.store(typeEdit1); + } + + + { + Allocatable newResource = facade1.newResource(); + newResource.setClassification( type.newClassification()); + newResource.getClassification().setValue("name", "test-resource"); + newResource.getClassification().setValue("test", facade1.getUserGroupsCategory().getCategories()[0]); + facade1.store(newResource); + } + + { + ClientFacade facade2 = (ClientFacade) + getContext().lookup(ClientFacade.ROLE + "/remote-facade-2"); + facade2.login("homer","duffs".toCharArray()); + //Dyn + DynamicType typeInSecondFacade = (DynamicType) facade2.getDynamicType("room"); + Attribute att = (Attribute)typeInSecondFacade.getAttribute("test"); + assertEquals( "test",att.getKey()); + assertEquals( AttributeType.CATEGORY,att.getType()); + assertEquals( facade2.getUserGroupsCategory(),att.getConstraint(ConstraintIds.KEY_ROOT_CATEGORY)); + + ClassificationFilter filter = typeInSecondFacade.newClassificationFilter(); + filter.addEqualsRule("name", "test-resource"); + Allocatable newResource =facade2.getAllocatables( filter.toArray())[0]; + Category userGroup = (Category)newResource.getClassification().getValue("test"); + assertEquals( "Category attribute value is not stored",facade2.getUserGroupsCategory().getCategories()[0].getKey(), userGroup.getKey()); + facade2.logout(); + } + + + assertEquals(4, allocatable.getClassification().getAttributes().length); + DynamicType typeEdit2 = (DynamicType) facade1.edit(type); + Attribute attributeLater = typeEdit2.getAttribute("test"); + assertTrue("Attributes identy changed after storing ", attributeLater.equals(newAttribute)); + typeEdit2.removeAttribute(attributeLater); + facade1.store(typeEdit2); + assertEquals( facade1.getAllocatables().length, 5); + assertEquals(3, allocatable.getClassification().getAttributes().length); + + User user = facade1.newUser(); + user.setUsername("test-user"); + facade1.store( user ); + + removeAnAttribute(); + // Wait for the update + { + ClientFacade facade2 = (ClientFacade) + getContext().lookup(ClientFacade.ROLE + "/remote-facade-2"); + facade2.login("homer","duffs".toCharArray()); + facade2.getUser("test-user"); + facade2.logout(); + } + } + + public void removeAnAttribute() throws Exception { + DynamicType typeEdit3 = (DynamicType) facade1.edit(facade1.getDynamicType("room")); + typeEdit3.removeAttribute( typeEdit3.getAttribute("belongsto")); + Allocatable allocatable = (Allocatable) facade1.getAllocatables()[0]; + assertEquals("erwin", allocatable.getName( locale ) ); + + Entity allocatableClone = facade1.edit(allocatable); + assertEquals(3, allocatable.getClassification().getAttributes().length); + facade1.storeObjects(new Entity[] {allocatableClone, typeEdit3}); + assertEquals( 5, facade1.getAllocatables().length); + assertEquals(2, allocatable.getClassification().getAttributes().length); + + + ClientFacade facade2 = (ClientFacade) + getContext().lookup(ClientFacade.ROLE + "/remote-facade-2"); + facade2.login("homer","duffs".toCharArray()); + // we check if the store affectes the second client. + assertEquals( 5, facade2.getAllocatables().length); + + ClassificationFilter filter = facade2.getDynamicType("room").newClassificationFilter(); + filter.addIsRule( "name", "erwin"); + { + Allocatable rAfter = facade2.getAllocatables( filter.toArray())[0]; + assertEquals(2, rAfter.getClassification().getAttributes().length); + } + //facade2.getUser("test-user"); + // Wait for the update + facade2.refresh(); + facade2.logout(); + } + + private Reservation findReservation(ClientFacade facade,String typeKey,String name) throws RaplaException { + DynamicType reservationType = facade.getDynamicType( typeKey ); + ClassificationFilter filter = reservationType.newClassificationFilter(); + filter.addRule("name", new Object[][] {{"contains","test-reservation"}}); + Reservation[] reservations = facade.getReservationsForAllocatable( null, null, null, new ClassificationFilter[] {filter}); + if (reservations.length >0 ) + return reservations[0]; + else + return null; + } + + + public void testChangeDynamicType2() throws Exception { + DynamicType type = (DynamicType) facade1.getDynamicTypes(DynamicTypeAnnotations.VALUE_RESOURCE_CLASSIFICATION)[0]; + DynamicType typeEdit3 = (DynamicType) facade1.edit(type); + typeEdit3.removeAttribute( typeEdit3.getAttribute("belongsto")); + + Allocatable resource1 = (Allocatable) facade1.getAllocatables()[0]; + assertEquals("erwin", resource1.getName( locale ) ); + facade1.store( typeEdit3 ); + assertEquals(2, resource1.getClassification().getAttributes().length); + } + + public void testRemoveCategory() throws Exception { + Category superCategoryClone = (Category) facade1.edit(facade1.getSuperCategory()); + Category department = superCategoryClone.getCategory("department"); + Category powerplant = department.getCategory("springfield-powerplant"); + powerplant.getParent().removeCategory(powerplant); + try { + facade1.store(superCategoryClone); + fail("Dependency Exception should have been thrown"); + } catch (DependencyException ex) { + } + } + + public void testRemoveCategoryBug5() throws Exception { + DynamicType type = facade1.newDynamicType( DynamicTypeAnnotations.VALUE_RESOURCE_CLASSIFICATION); + String testTypeName = "TestType"; + type.getName().setName("en", testTypeName); + Attribute att = facade1.newAttribute( AttributeType.CATEGORY); + att.setKey("testdep"); + { + Category superCategoryClone = facade1.getSuperCategory(); + Category department = superCategoryClone.getCategory("department"); + att.setConstraint(ConstraintIds.KEY_ROOT_CATEGORY, department); + } + type.addAttribute( att); + facade1.store( type ); + Category superCategoryClone = (Category) facade1.edit(facade1.getSuperCategory()); + Category department = superCategoryClone.getCategory("department"); + superCategoryClone.removeCategory( department); + try { + facade1.store(superCategoryClone); + fail("Dependency Exception should have been thrown"); + } catch (DependencyException ex) { + Collection dependencies = ex.getDependencies(); + System.out.println( dependencies); + assertTrue("Dependencies doesnt contains " + testTypeName, contains(dependencies,testTypeName)); + + } + + + } + + + private boolean contains(Collection dependencies, String testTypeName) { + for (String dep: dependencies) + { + if ( dep.contains( testTypeName)) + { + return true; + } + } + return false; + } + + public void testStoreFilter() throws Exception { + // select from event where name contains 'planting' or name contains 'test'; + DynamicType dynamicType = facade1.getDynamicType("room"); + ClassificationFilter classificationFilter = dynamicType.newClassificationFilter(); + Category channel6 = facade1.getSuperCategory().getCategory("department").getCategory("channel-6"); + Category testdepartment = facade1.getSuperCategory().getCategory("department").getCategory("testdepartment"); + classificationFilter.setRule(0 + ,dynamicType.getAttribute("belongsto") + ,new Object[][] { + {"is",channel6} + ,{"is",testdepartment} + } + ); + boolean thrown = false; + ClassificationFilter[] filter = new ClassificationFilter[] { classificationFilter}; + RaplaMap selected = facade1.newRaplaMap( Collections.EMPTY_MAP ); + CalendarModelConfiguration conf = facade1.newRaplaCalendarModel(selected, filter,null,null,null, null, facade1.today(), "week", null ); + Preferences prefs = (Preferences) facade1.edit( facade1.getPreferences()); + prefs.putEntry( "org.rapla.TestEntry", conf); + facade1.store( prefs ); + User user = raplaServer.getFacade().getUser("homer"); + Preferences storedPrefs = raplaServer.getFacade().getPreferences(user); + assertNotNull( storedPrefs ); + CalendarModelConfiguration storedConf = (CalendarModelConfiguration )storedPrefs.getEntry("org.rapla.TestEntry"); + assertNotNull( storedConf ); + + ClassificationFilter[] storedFilter = storedConf.getFilter(); + assertEquals( 1, storedFilter.length); + ClassificationFilter storedClassFilter = storedFilter[0]; + + assertEquals( 1, storedClassFilter.ruleSize()); + + try { + Category parent = (Category) facade1.edit( testdepartment.getParent() ); + parent.removeCategory( testdepartment ); + facade1.store( parent ); + } catch (DependencyException ex) { + assertTrue(contains(ex.getDependencies(),prefs.getName(locale))); + thrown = true; + } + assertTrue("Dependency Exception should have been thrown!",thrown); + + } + + public void testReservationInTheFutureStoredInCalendar() throws Exception{ + Date futureDate = new Date(facade1.today().getTime() + DateTools.MILLISECONDS_PER_WEEK * 10); + Reservation r = facade1.newReservation(); + r.addAppointment( facade1.newAppointment( futureDate, futureDate)); + r.getClassification().setValue("name","Test"); + + facade1.store( r ); + + RaplaMap map = facade1.newRaplaMap( Collections.singletonList( r )); + CalendarModelConfiguration conf = facade1.newRaplaCalendarModel( map, null,null,"test", null, null,facade1.today(), WeekViewFactory.WEEK_VIEW, null); + + Preferences prefs = ( Preferences ) facade1.edit( facade1.getPreferences() ); + prefs.putEntry( "org.rapla.test", conf); + + try { + facade1.store( prefs ); + fail("Should throw an exception in the current version, because we can't store references to reservations"); + } catch (RaplaException ex) { + } + /* + Thread.sleep( 1000); + + ClientFacade facade2 = (ClientFacade) + getContext().lookup(ClientFacade.ROLE + "/remote-facade-2"); + facade2.login("homer","duffs".toCharArray());*/ + + + } + + + public void testReservationWithExceptionDoesntShow() throws Exception { + { + facade1.removeObjects( facade1.getReservationsForAllocatable( null, null, null, null)); + } + Date start = new Date(); + Date end = new Date( start.getTime() + DateTools.MILLISECONDS_PER_HOUR * 2); + { + Reservation r = facade1.newReservation( ); + r.getClassification().setValue("name","test-reservation"); + Appointment a = facade1.newAppointment( start, end); + a.setRepeatingEnabled( true ); + a.getRepeating().setType( Repeating.WEEKLY); + a.getRepeating().setInterval( 2 ); + a.getRepeating().setNumber( 10 ); + r.addAllocatable( facade1.getAllocatables()[0]); + r.addAppointment( a ); + a.getRepeating().addException( start ); + a.getRepeating().addException( new Date(start.getTime() + DateTools.MILLISECONDS_PER_WEEK)); + facade1.store( r ); + facade1.logout(); + } + { + ClientFacade facade2 = (ClientFacade) getContext().lookup(ClientFacade.ROLE + "/remote-facade-2"); + facade2.login("homer","duffs".toCharArray()); + + Reservation[] res = facade2.getReservationsForAllocatable( null,start, new Date( start.getTime()+ 8* DateTools.MILLISECONDS_PER_WEEK ), null ); + assertEquals( 1, res.length); + Thread.sleep( 100); + facade2.logout(); + } + + + } + + public void testChangeGroup() throws Exception { + User user = (User)facade1.edit( facade1.getUser("monty")); + Category[] groups = user.getGroups(); + assertTrue("No groups found!", groups.length>0); + Category myGroup = facade1.getUserGroupsCategory().getCategory("my-group"); + assertTrue( Arrays.asList(groups).contains(myGroup)); + user.removeGroup( myGroup ); + ClientFacade facade2 = (ClientFacade) + getContext().lookup(ClientFacade.ROLE + "/remote-facade-2"); + facade2.login("homer","duffs".toCharArray()); + Allocatable testResource = (Allocatable) facade2.edit( facade2.getAllocatables()[0]); + assertTrue( testResource.canAllocate( facade2.getUser("monty") ,null, null,null)); + testResource.removePermission( testResource.getPermissions()[0]); + Permission newPermission = testResource.newPermission(); + newPermission.setGroup( facade1.getUserGroupsCategory().getCategory("my-group") ); + newPermission.setAccessLevel( Permission.READ ); + testResource.addPermission( newPermission ); + assertFalse( testResource.canAllocate( facade2.getUser("monty") ,null, null,null)); + assertTrue( testResource.canRead( facade2.getUser("monty"))); + facade1.store( user ); + facade2.refresh(); + assertFalse( testResource.canRead( facade2.getUser("monty") )); + } + + public void testRemoveAppointment() throws Exception + { + Date start = getRaplaLocale().toDate( 2005, 10, 10); + Date end = getRaplaLocale().toDate( 2005, 10, 15); + Reservation r = facade1.newReservation(); + r.getClassification().setValue("name", "newReservation"); + r.addAppointment( facade1.newAppointment( start, end )); + r.addAllocatable( facade1.getAllocatables()[0]); + ClassificationFilter f = r.getClassification().getType().newClassificationFilter(); + f.addEqualsRule("name","newReservation"); + facade1.store( r ); + r = (Reservation) facade1.getPersistant( r ); + facade1.remove( r ); + Reservation[] allRes = facade1.getReservationsForAllocatable( null, null, null,new ClassificationFilter[] {f} ); + assertEquals( 0 , allRes.length); + } + + public void testRestrictionsBug7() throws Exception + { + Reservation r = facade1.newReservation(); + r.getClassification().setValue("name", "newReservation"); + Appointment app1; + { + Date start = getRaplaLocale().toDate( 2005, 10, 10); + Date end = getRaplaLocale().toDate( 2005, 10, 15); + app1 = facade1.newAppointment( start, end ); + r.addAppointment( app1); + } + Appointment app2; + { + Date start = getRaplaLocale().toDate( 2008, 10, 10); + Date end = getRaplaLocale().toDate( 2008, 10, 15); + app2 = facade1.newAppointment( start, end ); + r.addAppointment( app2); + } + Allocatable allocatable = facade1.getAllocatables()[0]; + r.addAllocatable( allocatable); + r.setRestriction( allocatable, new Appointment[] {app1,app2}); + facade1.store( r ); + facade1.logout(); + facade1.login("homer","duffs".toCharArray()); + ClassificationFilter f = r.getClassification().getType().newClassificationFilter(); + f.addEqualsRule("name","newReservation"); + Reservation[] allRes = facade1.getReservationsForAllocatable( null, null, null,new ClassificationFilter[] {f} ); + Reservation test = allRes[0]; + allocatable = facade1.getAllocatables()[0]; + Appointment[] restrictions = test.getRestriction( allocatable); + assertEquals( "Restrictions needs to be saved!",2,restrictions.length); + + } + +} + + + + + diff --git a/Rapla/test-src/org/rapla/ServletTestBase.java b/Rapla/test-src/org/rapla/ServletTestBase.java new file mode 100644 index 0000000..85117c8 --- /dev/null +++ b/Rapla/test-src/org/rapla/ServletTestBase.java @@ -0,0 +1,87 @@ +package org.rapla; + +import java.io.File; +import java.net.ConnectException; +import java.net.HttpURLConnection; +import java.net.URL; + +import junit.framework.TestCase; + +import org.mortbay.jetty.Server; +import org.mortbay.jetty.servlet.Context; +import org.mortbay.jetty.servlet.ServletHolder; +import org.rapla.components.util.IOUtil; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaLocale; + +public abstract class ServletTestBase extends TestCase +{ + MainServlet mainServlet = new MainServlet(); + Server jettyServer; + final public static String WEBAPP_FOLDER_NAME = RaplaTestCase.TEST_FOLDER_NAME + "/webapp"; + final public static String WEBAPP_INF_FOLDER_NAME = WEBAPP_FOLDER_NAME + "/WEB-INF"; + + public ServletTestBase( String name ) + { + super( name ); + new File("temp").mkdir(); + File testFolder =new File(RaplaTestCase.TEST_FOLDER_NAME); + testFolder.mkdir(); + } + + protected void setUp() throws Exception + { + super.setUp(); + mainServlet.setServerId( getStorageName() ); + new File(WEBAPP_FOLDER_NAME).mkdir(); + new File(WEBAPP_INF_FOLDER_NAME).mkdir(); + + IOUtil.copy( "test-src/test.xconf", WEBAPP_INF_FOLDER_NAME + "/raplaserver.xconf" ); + IOUtil.copy( "test-src/test.xlog", WEBAPP_INF_FOLDER_NAME + "/raplaserver.xlog" ); + IOUtil.copy( "test-src/testdefault.xml", WEBAPP_INF_FOLDER_NAME + "/test.xml" ); + IOUtil.copy( "webapp/WEB-INF/web.xml", WEBAPP_INF_FOLDER_NAME + "/web.xml" ); + + jettyServer =new Server(8051); + //jettyServer.setAttribute("org.mortbay.jetty.Request.maxFormContentSize", new Integer(64000000)); + System.setProperty("org.mortbay.jetty.Request.maxFormContentSize",new Integer(64000000).toString() ); + Context context = new Context( jettyServer,"/",Context.SESSIONS ); + context.setResourceBase( WEBAPP_FOLDER_NAME ); + context.addServlet( new ServletHolder(mainServlet), "/*" ); + jettyServer.start(); + + URL server = new URL("http://127.0.0.1:8051/rapla/ping"); + HttpURLConnection connection = (HttpURLConnection)server.openConnection(); + int timeout = 10000; + int interval = 200; + for ( int i=0;i should be replaced with
    " + ,getI18n().getString("error.invalid_key").indexOf("
    ")>=0 + ); + } +} + + + + + diff --git a/Rapla/test-src/org/rapla/components/xmlbundle/tests/I18nBundleImplTest.java b/Rapla/test-src/org/rapla/components/xmlbundle/tests/I18nBundleImplTest.java new file mode 100644 index 0000000..7baf1ef --- /dev/null +++ b/Rapla/test-src/org/rapla/components/xmlbundle/tests/I18nBundleImplTest.java @@ -0,0 +1,122 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.components.xmlbundle.tests; +import java.util.Locale; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.DefaultConfiguration; +import org.apache.avalon.framework.logger.ConsoleLogger; +import org.apache.avalon.framework.logger.Logger; +import org.rapla.components.xmlbundle.I18nBundle; +import org.rapla.components.xmlbundle.LocaleSelector; +import org.rapla.components.xmlbundle.impl.I18nBundleImpl; +import org.rapla.components.xmlbundle.impl.LocaleSelectorImpl; +import org.rapla.framework.RaplaDefaultContext; + + +public class I18nBundleImplTest extends AbstractI18nTest { + I18nBundleImpl i18n; + boolean useFile; + LocaleSelector localeSelector; + Logger logger = new ConsoleLogger(ConsoleLogger.LEVEL_WARN); + + public I18nBundleImplTest(String name) { + this(name, true); + } + + public I18nBundleImplTest(String name,boolean useFile) { + super(name); + this.useFile = useFile; + } + + protected void setUp() throws Exception { + DefaultConfiguration config = new DefaultConfiguration("i18n",this.toString()); + if (this.useFile) { + DefaultConfiguration child = new DefaultConfiguration("file",this.toString()); + child.setValue("src/org/rapla/RaplaResources.xml"); + config.addChild(child); + } else { + config.setAttribute("id","org.rapla.RaplaResources"); + } + i18n = create(config); + } + + private I18nBundleImpl create(Configuration config) throws Exception { + I18nBundleImpl i18n; + RaplaDefaultContext context = new RaplaDefaultContext(); + localeSelector = new LocaleSelectorImpl(); + context.put(LocaleSelector.ROLE,localeSelector); + i18n = new I18nBundleImpl(context,config,new ConsoleLogger()); + return i18n; + } + + protected void tearDown() { + i18n.dispose(); + } + public I18nBundle getI18n() { + return i18n; + } + + public static Test suite() { + TestSuite suite = new TestSuite(); + // The first four test only succeed if the Resource Bundles are build. + suite.addTest(new I18nBundleImplTest("testLocaleChanged",false)); + suite.addTest(new I18nBundleImplTest("testGetIcon",false)); + suite.addTest(new I18nBundleImplTest("testGetString",false)); + suite.addTest(new I18nBundleImplTest("testLocale",false)); + /* + */ + suite.addTest(new I18nBundleImplTest("testInvalidConfig",true)); + suite.addTest(new I18nBundleImplTest("testLocaleChanged",true)); + suite.addTest(new I18nBundleImplTest("testGetIcon",true)); + suite.addTest(new I18nBundleImplTest("testGetString",true)); + suite.addTest(new I18nBundleImplTest("testLocale",true)); + + return suite; + } + + public void testLocaleChanged() { + localeSelector.setLocale(new Locale("de","DE")); + assertEquals(getI18n().getString("cancel"),"Abbrechen"); + localeSelector.setLocale(new Locale("en","DE")); + assertEquals(getI18n().getString("cancel"),"Cancel"); + } + + public void testInvalidConfig() throws Exception { + if (!this.useFile) + return; + DefaultConfiguration config = new DefaultConfiguration("i18n",this.toString()); + try { + create(config); + assertTrue("id is missing should be reported", true); + } catch (Exception ex) { + } + config.setAttribute("id","org.rapla.RaplaResources"); + DefaultConfiguration child = new DefaultConfiguration("file",this.toString()); + child.setValue("./src/org/rapla/RaplaResou"); + config.addChild( child ); + try { + create(config); + assertTrue("file ./src/org/rapla/RaplaResou should fail", true); + } catch (Exception ex) { + } + } +} + + + + + diff --git a/Rapla/test-src/org/rapla/entities/tests/AppointmentTest.java b/Rapla/test-src/org/rapla/entities/tests/AppointmentTest.java new file mode 100644 index 0000000..acf312e --- /dev/null +++ b/Rapla/test-src/org/rapla/entities/tests/AppointmentTest.java @@ -0,0 +1,468 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.tests; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.TimeZone; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import org.rapla.components.util.DateTools; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.AppointmentBlock; +import org.rapla.entities.domain.Repeating; +import org.rapla.entities.domain.RepeatingType; +import org.rapla.entities.domain.internal.AppointmentImpl; +import org.rapla.framework.RaplaException; + +import edu.emory.mathcs.backport.java.util.Collections; + +public class AppointmentTest extends TestCase { + TimeZone zone = DateTools.getTimeZone(); //this is GMT + Locale locale = Locale.getDefault(); + + public AppointmentTest(String name) { + super(name); + } + + public static Test suite() { + return new TestSuite(AppointmentTest.class); + } + + public void setUp() throws Exception { + } + + Appointment createAppointment(Day day,Time start,Time end) { + Calendar cal = Calendar.getInstance(zone,locale); + cal.set(Calendar.YEAR,day.getYear()); + cal.set(Calendar.MONTH,day.getMonth() - 1); + cal.set(Calendar.DATE,day.getDate()); + cal.set(Calendar.HOUR_OF_DAY,start.getHour()); + cal.set(Calendar.MINUTE,start.getMinute()); + cal.set(Calendar.SECOND,0); + cal.set(Calendar.MILLISECOND,0); + Date startTime = cal.getTime(); + cal.set(Calendar.HOUR_OF_DAY,end.getHour()); + cal.set(Calendar.MINUTE,end.getMinute()); + Date endTime = cal.getTime(); + return new AppointmentImpl(startTime,endTime); + } + + public void testOverlapAndCompareTo() throws RaplaException { + Appointment a1 = createAppointment(new Day(2002,5,25),new Time(12,15),new Time(14,15)); + Appointment a2 = createAppointment(new Day(2002,5,26),new Time(13,0),new Time(15,0)); + Appointment a3 = createAppointment(new Day(2002,5,25),new Time(13,0),new Time(15,0)); + + // First the single Appointments + assertTrue(a1.compareTo(a2) == -1); + assertTrue(a2.compareTo(a1) == 1); + assertTrue(a1.compareTo(a3) == -1); + assertTrue(a3.compareTo(a1) == 1); + assertTrue(a1.overlaps(a3)); + assertTrue(a3.overlaps(a1)); + assertTrue(!a2.overlaps(a3)); + assertTrue(!a3.overlaps(a2)); + assertTrue(!a1.overlaps(a2)); + assertTrue(!a2.overlaps(a1)); + + + // Now we test repeatings + a1.setRepeatingEnabled(true); + a2.setRepeatingEnabled(true); + a3.setRepeatingEnabled(true); + + // Weekly repeating until 2002-6-2 + Repeating repeating1 = a1.getRepeating(); + repeating1.setEnd(new Day(2002,6,2).toDate(zone)); + + // daily repeating 8x + Repeating repeating2 = a2.getRepeating(); + repeating2.setType(Repeating.DAILY); + repeating2.setNumber(8); + + // weekly repeating 1x + Repeating repeating3 = a3.getRepeating(); + repeating3.setNumber(1); + + assertTrue(a1.overlaps( + new Day(2002,5,26).toDate(zone) + ,new Day(2002,6,2).toDate(zone) + ) + ); + + assertTrue(a1.overlaps(a2)); + assertTrue("overlap is not symetric",a2.overlaps(a1)); + + assertTrue(a1.overlaps(a3)); + assertTrue("overlap is not symetric",a3.overlaps(a1)); + + assertTrue(!a2.overlaps(a3)); + assertTrue("overlap is not symetric",!a3.overlaps(a2)); + + // No appointment in first week of repeating + repeating1.addException(new Day(2002,5,25).toDate(zone)); + + assertTrue(!a1.overlaps(a3)); + assertTrue("overlap is not symetric",!a3.overlaps(a1)); + } + + public void testOverlap1() throws RaplaException { + Appointment a1 = createAppointment(new Day(2002,4,15),new Time(10,0),new Time(12,0)); + Appointment a2 = createAppointment(new Day(2002,4,15),new Time(9,0),new Time(11,0)); + + a1.setRepeatingEnabled(true); + Repeating repeating1 = a1.getRepeating(); + repeating1.setEnd(new Day(2002,7,11).toDate(zone)); + + a2.setRepeatingEnabled(true); + Repeating repeating2 = a2.getRepeating(); + repeating2.setType(Repeating.DAILY); + repeating2.setNumber(5); + + assertTrue(a1.overlaps(a2)); + assertTrue("overlap is not symetric",a2.overlaps(a1)); + } + + public void testOverlap2() throws RaplaException { + Appointment a1 = createAppointment(new Day(2002,4,12),new Time(12,0),new Time(14,0)); + a1.setRepeatingEnabled(true); + Repeating repeating1 = a1.getRepeating(); + repeating1.setEnd(new Day(2002,7,11).toDate(zone)); + assertTrue(a1.overlaps(new Day(2002,4,15).toDate(zone) + , new Day(2002,4,22).toDate(zone))); + } + + public void testBlocks() { + Appointment a1 = createAppointment(new Day(2002,4,12),new Time(12,0),new Time(14,0)); + a1.setRepeatingEnabled(true); + Repeating repeating1 = a1.getRepeating(); + repeating1.setEnd(new Day(2002,2,11).toDate(zone)); + List blocks = new ArrayList(); + a1.createBlocks( new Day(2002,4,5).toDate(zone) + , new Day(2002,5,5).toDate(zone) + , blocks ); + assertEquals( "Repeating end is in the past: createBlocks should only return one block", 1, blocks.size() ); + } + + public void testMatchNeverEnding() throws RaplaException { + Appointment a1 = createAppointment(new Day(2002,5,25),new Time(11,15),new Time(13,15)); + Appointment a2 = createAppointment(new Day(2002,5,25),new Time(11,15),new Time(13,15)); + + a1.setRepeatingEnabled(true); + Repeating repeating1 = a1.getRepeating(); + repeating1.setType(Repeating.WEEKLY); + + a2.setRepeatingEnabled(true); + Repeating repeating2 = a2.getRepeating(); + repeating2.setType(Repeating.WEEKLY); + + repeating1.addException(new Day(2002,4,10).toDate( zone )); + assertTrue( !a1.matches( a2 ) ); + assertTrue( !a2.matches( a1 ) ); + } + + public void testMonthly() + { + Appointment a1 = createAppointment(new Day(2006,8,17),new Time(10,30),new Time(12,0)); + Date start = a1.getStart(); + a1.setRepeatingEnabled(true); + Repeating repeating1 = a1.getRepeating(); + repeating1.setType( RepeatingType.MONTHLY); + repeating1.setNumber( 4); + List blocks = new ArrayList(); + a1.createBlocks( new Day(2006,8,17).toGMTDate(), new Day( 2007, 3, 30).toGMTDate(), blocks); + assertEquals( 4, blocks.size()); + Collections.sort(blocks); + assertEquals( start, new Date(blocks.get( 0).getStart())); + Calendar cal = DateTools.createGMTCalendar(); + cal.setTime( start ); + int weekday = cal.get( Calendar.DAY_OF_WEEK); + int dayofweekinmonth = cal.get( Calendar.DAY_OF_WEEK_IN_MONTH); + assertEquals( Calendar.THURSDAY,weekday ); + assertEquals( 3, dayofweekinmonth ); + assertEquals( Calendar.AUGUST, cal.get( Calendar.MONTH)); + // we expect the second wednesday in april + cal.add( Calendar.MONTH, 1 ); + cal.set( Calendar.DAY_OF_WEEK , weekday ); + cal.set( Calendar.DAY_OF_WEEK_IN_MONTH, dayofweekinmonth); + start = cal.getTime(); + assertEquals( start, new Date(blocks.get( 1).getStart())); + cal.add( Calendar.MONTH, 1 ); + cal.set( Calendar.DAY_OF_WEEK , weekday ); + cal.set( Calendar.DAY_OF_WEEK_IN_MONTH, dayofweekinmonth); + assertEquals(10, cal.get( Calendar.HOUR_OF_DAY)); + start = cal.getTime(); + assertEquals( start, new Date(blocks.get( 2).getStart())); + cal.add( Calendar.MONTH, 1 ); + cal.set( Calendar.DAY_OF_WEEK , weekday ); + cal.set( Calendar.DAY_OF_WEEK_IN_MONTH, dayofweekinmonth); + start = cal.getTime(); + assertEquals( start, new Date(blocks.get( 3).getStart())); + + assertEquals( start, repeating1.getEnd() ); + assertEquals( start, a1.getMaxEnd() ); + + blocks.clear(); + a1.createBlocks( new Day(2006,1,1).toGMTDate(), new Day( 2007, 10, 20).toGMTDate(), blocks); + assertEquals( 4, blocks.size()); + + blocks.clear(); + a1.createBlocks( new Day(2006,10,19).toGMTDate(), new Day( 2006, 10, 20).toGMTDate(), blocks); + assertEquals( 1, blocks.size()); + } + + public void testMonthly5ft() + { + Appointment a1 = createAppointment(new Day(2006,8,31),new Time(10,30),new Time(12,0)); + Date start = a1.getStart(); + a1.setRepeatingEnabled(true); + Repeating repeating1 = a1.getRepeating(); + repeating1.setType( RepeatingType.MONTHLY); + repeating1.setNumber( 4); + List blocks = new ArrayList(); + a1.createBlocks( new Day(2006,8,1).toGMTDate(), new Day( 2008, 8, 1).toGMTDate(), blocks); + assertEquals( 4, blocks.size()); + Collections.sort( blocks); + assertEquals( start, new Date(blocks.get(0).getStart())); + Calendar cal = DateTools.createGMTCalendar(); + cal.setTime( start ); + int weekday = cal.get( Calendar.DAY_OF_WEEK); + int dayofweekinmonth = cal.get( Calendar.DAY_OF_WEEK_IN_MONTH); + assertEquals( Calendar.THURSDAY,weekday ); + assertEquals( 5, dayofweekinmonth ); + assertEquals( Calendar.AUGUST, cal.get( Calendar.MONTH)); + + cal.set( Calendar.MONTH, Calendar.NOVEMBER ); + cal.set( Calendar.DAY_OF_WEEK , weekday ); + cal.set( Calendar.DAY_OF_WEEK_IN_MONTH, dayofweekinmonth); + start = cal.getTime(); + assertEquals( start, new Date(blocks.get(1).getStart())); + + cal.add( Calendar.YEAR,1); + cal.set( Calendar.MONTH, Calendar.MARCH ); + cal.set( Calendar.DAY_OF_WEEK , weekday ); + cal.set( Calendar.DAY_OF_WEEK_IN_MONTH, dayofweekinmonth); + assertEquals(10, cal.get( Calendar.HOUR_OF_DAY)); + start = cal.getTime(); + + assertEquals( start, new Date(blocks.get(2).getStart())); + cal.set( Calendar.MONTH, Calendar.MAY ); + cal.set( Calendar.DAY_OF_WEEK , weekday ); + cal.set( Calendar.DAY_OF_WEEK_IN_MONTH, dayofweekinmonth); + start = cal.getTime(); + assertEquals( start, new Date(blocks.get(3).getStart())); + + assertEquals( start, repeating1.getEnd() ); + assertEquals( start, a1.getMaxEnd() ); + + blocks.clear(); + a1.createBlocks( new Day(2006,1,1).toGMTDate(), new Day( 2007, 10, 20).toGMTDate(), blocks); + assertEquals( 4, blocks.size()); + + blocks.clear(); + a1.createBlocks( new Day(2006,10,19).toGMTDate(), new Day( 2006, 11, 31).toGMTDate(), blocks); + assertEquals( 1, blocks.size()); + } + + public void testMonthlyNeverending() + { + Appointment a1 = createAppointment(new Day(2006,8,31),new Time(10,30),new Time(12,0)); + Date start = a1.getStart(); + a1.setRepeatingEnabled(true); + Repeating repeating1 = a1.getRepeating(); + repeating1.setType( RepeatingType.MONTHLY); + repeating1.setEnd( null ); + List blocks = new ArrayList(); + a1.createBlocks( new Day(2006,8,1).toGMTDate(), new Day( 2008, 8, 1).toGMTDate(), blocks); + assertEquals( 9, blocks.size()); + assertEquals( start, new Date(blocks.get(0).getStart())); + Calendar cal = DateTools.createGMTCalendar(); + cal.setTime( start ); + int weekday = cal.get( Calendar.DAY_OF_WEEK); + int dayofweekinmonth = cal.get( Calendar.DAY_OF_WEEK_IN_MONTH); + assertEquals( Calendar.THURSDAY,weekday ); + assertEquals( 5, dayofweekinmonth ); + assertEquals( Calendar.AUGUST, cal.get( Calendar.MONTH)); + + cal.set( Calendar.MONTH, Calendar.NOVEMBER ); + cal.set( Calendar.DAY_OF_WEEK , weekday ); + cal.set( Calendar.DAY_OF_WEEK_IN_MONTH, dayofweekinmonth); + start = cal.getTime(); + assertEquals( start, new Date(blocks.get(1).getStart())); + + cal.add( Calendar.YEAR,1); + cal.set( Calendar.MONTH, Calendar.MARCH ); + cal.set( Calendar.DAY_OF_WEEK , weekday ); + cal.set( Calendar.DAY_OF_WEEK_IN_MONTH, dayofweekinmonth); + assertEquals(10, cal.get( Calendar.HOUR_OF_DAY)); + start = cal.getTime(); + + assertEquals( start, new Date(blocks.get(2).getStart())); + cal.set( Calendar.MONTH, Calendar.MAY ); + cal.set( Calendar.DAY_OF_WEEK , weekday ); + cal.set( Calendar.DAY_OF_WEEK_IN_MONTH, dayofweekinmonth); + start = cal.getTime(); + assertEquals( start, new Date(blocks.get(3).getStart())); + + + blocks.clear(); + a1.createBlocks( new Day(2006,1,1).toGMTDate(), new Day( 2007, 10, 20).toGMTDate(), blocks); + assertEquals( 5, blocks.size()); + } + + public void testYearly29February() + { + Appointment a1 = createAppointment(new Day(2004,2,29),new Time(10,30),new Time(12,0)); + Date start = a1.getStart(); + a1.setRepeatingEnabled(true); + Repeating repeating1 = a1.getRepeating(); + repeating1.setType( RepeatingType.YEARLY); + repeating1.setNumber( 4); + List blocks = new ArrayList(); + a1.createBlocks( new Day(2004,1,1).toGMTDate(), new Day( 2020, 1, 1).toGMTDate(), blocks); + assertEquals( 4, blocks.size()); + assertEquals( start, new Date(blocks.get(0).getStart())); + Calendar cal = DateTools.createGMTCalendar(); + cal.setTime( start ); + int weekday = cal.get( Calendar.DAY_OF_WEEK); + int dayofweekinmonth = cal.get( Calendar.DAY_OF_WEEK_IN_MONTH); + assertEquals( Calendar.SUNDAY,weekday ); + assertEquals( 5, dayofweekinmonth ); + assertEquals( Calendar.FEBRUARY, cal.get( Calendar.MONTH)); + + cal.add( Calendar.YEAR,4); + cal.set( Calendar.MONTH, Calendar.FEBRUARY ); + cal.set( Calendar.DAY_OF_MONTH , 29 ); + start = cal.getTime(); + assertEquals( start, new Date(blocks.get(1).getStart())); + + cal.add( Calendar.YEAR,4); + cal.set( Calendar.MONTH, Calendar.FEBRUARY ); + cal.set( Calendar.DAY_OF_MONTH , 29 ); + assertEquals(10, cal.get( Calendar.HOUR_OF_DAY)); + start = cal.getTime(); + + assertEquals( start, new Date(blocks.get(2).getStart())); + cal.add( Calendar.YEAR,4); + cal.set( Calendar.MONTH, Calendar.FEBRUARY ); + cal.set( Calendar.DAY_OF_MONTH , 29 ); + start = cal.getTime(); + assertEquals( start, new Date(blocks.get(3).getStart())); + + assertEquals( start, repeating1.getEnd() ); + assertEquals( start, a1.getMaxEnd() ); + + blocks.clear(); + a1.createBlocks( new Day(2006,1,1).toGMTDate(), new Day( 2012, 10, 20).toGMTDate(), blocks); + assertEquals( 2, blocks.size()); + + blocks.clear(); + a1.createBlocks( new Day(2008,1,1).toGMTDate(), new Day( 2008, 11, 31).toGMTDate(), blocks); + assertEquals( 1, blocks.size()); + } + + public void testYearly() + { + Appointment a1 = createAppointment(new Day(2006,8,17),new Time(10,30),new Time(12,0)); + Date start = a1.getStart(); + a1.setRepeatingEnabled(true); + Repeating repeating1 = a1.getRepeating(); + repeating1.setType( RepeatingType.YEARLY ); + repeating1.setNumber( 4); + Calendar cal = DateTools.createGMTCalendar(); + cal.setTime( start ); + int dayInMonth = cal.get( Calendar.DAY_OF_MONTH); + int month = cal.get( Calendar.MONTH); + List blocks = new ArrayList(); + a1.createBlocks( new Day(2006,8,17).toGMTDate(), new Day( 2010, 3, 30).toGMTDate(), blocks); + assertEquals( 4, blocks.size()); + assertEquals( start, new Date(blocks.get(0).getStart())); + cal.add( Calendar.YEAR, 1 ); + start = cal.getTime(); + assertEquals( start, new Date(blocks.get(1).getStart())); + cal.add( Calendar.YEAR, 1 ); + start = cal.getTime(); + assertEquals( start, new Date(blocks.get(2).getStart())); + cal.add( Calendar.YEAR, 1 ); + start = cal.getTime(); + assertEquals( dayInMonth,cal.get(Calendar.DAY_OF_MONTH)); + assertEquals( month,cal.get(Calendar.MONTH)); + assertEquals( start, new Date(blocks.get(3).getStart())); + assertEquals( start, repeating1.getEnd() ); + assertEquals( start, a1.getMaxEnd() ); + } + + public void testMonthlySetEnd() + { + Appointment a1 = createAppointment(new Day(2006,8,17),new Time(10,30),new Time(12,0)); + Date start = a1.getStart(); + a1.setRepeatingEnabled(true); + Repeating repeating1 = a1.getRepeating(); + repeating1.setType( RepeatingType.MONTHLY); + repeating1.setEnd( new Day(2006,12,1).toGMTDate()); + List blocks = new ArrayList(); + { + Date s = new Day(2006,8,17).toGMTDate(); + Date e = new Day( 2007, 3, 30).toGMTDate(); + a1.createBlocks( s,e , blocks); + } + assertEquals( 4, blocks.size()); + assertEquals( start, new Date(blocks.get(0).getStart())); + Calendar cal = DateTools.createGMTCalendar(); + cal.setTime( start ); + int weekday = cal.get( Calendar.DAY_OF_WEEK); + int dayofweekinmonth = cal.get( Calendar.DAY_OF_WEEK_IN_MONTH); + assertEquals( Calendar.THURSDAY,weekday ); + assertEquals( 3, dayofweekinmonth ); + assertEquals( Calendar.AUGUST, cal.get( Calendar.MONTH)); + cal.add( Calendar.MONTH, 1 ); + cal.set( Calendar.DAY_OF_WEEK , weekday ); + cal.set( Calendar.DAY_OF_WEEK_IN_MONTH, dayofweekinmonth); + start = cal.getTime(); + assertEquals( start, new Date(blocks.get(1).getStart())); + cal.add( Calendar.MONTH, 1 ); + cal.set( Calendar.DAY_OF_WEEK , weekday ); + cal.set( Calendar.DAY_OF_WEEK_IN_MONTH, dayofweekinmonth); + assertEquals(10, cal.get( Calendar.HOUR_OF_DAY)); + start = cal.getTime(); + assertEquals( start, new Date(blocks.get(2).getStart())); + cal.add( Calendar.MONTH, 1 ); + cal.set( Calendar.DAY_OF_WEEK , weekday ); + cal.set( Calendar.DAY_OF_WEEK_IN_MONTH, dayofweekinmonth); + start = cal.getTime(); + assertEquals( start, new Date(blocks.get(3).getStart())); + + assertEquals( new Day(2006,12,1).toGMTDate(), repeating1.getEnd() ); + assertEquals( new Day(2006,12,1).toGMTDate(), a1.getMaxEnd() ); + + blocks.clear(); + a1.createBlocks( new Day(2006,1,1).toGMTDate(), new Day( 2007, 10, 20).toGMTDate(), blocks); + assertEquals( 4, blocks.size()); + + blocks.clear(); + a1.createBlocks( new Day(2006,10,19).toGMTDate(), new Day( 2006, 10, 20).toGMTDate(), blocks); + assertEquals( 1, blocks.size()); + } + +} + + + + + diff --git a/Rapla/test-src/org/rapla/entities/tests/AttributeTest.java b/Rapla/test-src/org/rapla/entities/tests/AttributeTest.java new file mode 100644 index 0000000..a7a32e1 --- /dev/null +++ b/Rapla/test-src/org/rapla/entities/tests/AttributeTest.java @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.tests; +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.rapla.RaplaTestCase; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.AttributeType; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.dynamictype.DynamicTypeAnnotations; +import org.rapla.facade.ClientFacade; +import org.rapla.facade.ModificationModule; +import org.rapla.facade.QueryModule; +import org.rapla.facade.UpdateModule; + +public class AttributeTest extends RaplaTestCase { + ModificationModule modificationMod; + QueryModule queryMod; + UpdateModule updateMod; + DynamicType type; + + public AttributeTest(String name) { + super(name); + } + + public static Test suite() { + return new TestSuite(AttributeTest.class); + } + + protected void setUp() throws Exception { + super.setUp(); + ClientFacade facade= getFacade(); + queryMod = facade; + modificationMod = facade; + updateMod = facade; + + } + + public void testAnnotations() throws Exception { + type = modificationMod.newDynamicType(DynamicTypeAnnotations.VALUE_RESOURCE_CLASSIFICATION); + type.setElementKey("test-type"); + Attribute a1 = modificationMod.newAttribute(AttributeType.STRING); + a1.setKey("test-attribute"); + a1.setAnnotation("expected-rows", "5"); + type.addAttribute( a1 ); + modificationMod.store( type ); + + DynamicType type2 = queryMod.getDynamicType("test-type"); + Attribute a2 = type2.getAttribute("test-attribute"); + + assertEquals(a1, a2); + assertEquals( "default-annotation", a2.getAnnotation("not-defined-ann","default-annotation" )); + assertEquals( "expected-rows", a2.getAnnotationKeys()[0]); + assertEquals( "5", a2.getAnnotation("expected-rows")); + + } +} + + + + + diff --git a/Rapla/test-src/org/rapla/entities/tests/CategoryTest.java b/Rapla/test-src/org/rapla/entities/tests/CategoryTest.java new file mode 100644 index 0000000..3fad013 --- /dev/null +++ b/Rapla/test-src/org/rapla/entities/tests/CategoryTest.java @@ -0,0 +1,154 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.tests; +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.rapla.RaplaTestCase; +import org.rapla.entities.Category; +import org.rapla.entities.DependencyException; +import org.rapla.entities.internal.CategoryImpl; +import org.rapla.facade.ClientFacade; +import org.rapla.facade.ModificationModule; +import org.rapla.facade.QueryModule; +import org.rapla.facade.UpdateModule; +import org.rapla.framework.RaplaException; + +public class CategoryTest extends RaplaTestCase { + CategoryImpl areas; + ModificationModule modificationMod; + QueryModule queryMod; + UpdateModule updateMod; + + public CategoryTest(String name) { + super(name); + } + + public static Test suite() { + return new TestSuite(CategoryTest.class); + } + + protected void setUp() throws Exception { + super.setUp(); + ClientFacade facade = getFacade(); + queryMod = facade; + modificationMod = facade; + updateMod = facade; + + areas = (CategoryImpl) modificationMod.newCategory(); + areas.setKey("areas"); + areas.getName().setName("en","areas"); + Category area51 = modificationMod.newCategory(); + area51.setKey("51"); + area51.getName().setName("en","area 51"); + Category buildingA = modificationMod.newCategory(); + buildingA.setKey("A"); + buildingA.getName().setName("en","building A"); + Category floor1 = modificationMod.newCategory(); + floor1.setKey("1"); + floor1.getName().setName("en","floor 1"); + + buildingA.addCategory(floor1); + area51.addCategory(buildingA); + areas.addCategory(area51); + + } + + public void testStore2() throws Exception { + Category superCategory = (Category)modificationMod.edit(queryMod.getSuperCategory()); + + superCategory.addCategory(areas); + modificationMod.store(superCategory); + assertTrue(areas.getId() != null); + Category editObject = (Category) modificationMod.edit(superCategory); + modificationMod.store(editObject); + assertTrue("reference to subcategory has changed" + ,areas == (CategoryImpl) superCategory.getCategory("areas") + ); + } + + + public void testStore() throws Exception { + Category superCategory = (Category)modificationMod.edit(queryMod.getSuperCategory()); + superCategory.addCategory(areas); + modificationMod.store(superCategory); + assertTrue(areas.getId() != null); + updateMod.refresh(); + Category[] categories = queryMod.getSuperCategory().getCategories(); + for (int i=0;i day.getYear()) + return 1; + if (getMonth() < day.getMonth()) + return -1; + if (getMonth() > day.getMonth()) + return 1; + if (getDate() < day.getDate()) + return -1; + if (getDate() > day.getDate()) + return 1; + return 0; + } + + public boolean equals(Object obj) { + if (!(obj instanceof Day)) + return false; + else + return (compareTo(obj) == 0); + } +} + + + + + + + + diff --git a/Rapla/test-src/org/rapla/entities/tests/PreferencesTest.java b/Rapla/test-src/org/rapla/entities/tests/PreferencesTest.java new file mode 100644 index 0000000..642f7bd --- /dev/null +++ b/Rapla/test-src/org/rapla/entities/tests/PreferencesTest.java @@ -0,0 +1,72 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.tests; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.apache.avalon.framework.configuration.Configuration; +import org.rapla.RaplaTestCase; +import org.rapla.entities.configuration.Preferences; +import org.rapla.entities.configuration.RaplaConfiguration; +import org.rapla.entities.internal.CategoryImpl; +import org.rapla.facade.ClientFacade; +import org.rapla.facade.ModificationModule; +import org.rapla.facade.QueryModule; +import org.rapla.facade.UpdateModule; + +public class PreferencesTest extends RaplaTestCase { + CategoryImpl areas; + ModificationModule modificationMod; + QueryModule queryMod; + UpdateModule updateMod; + + public PreferencesTest(String name) { + super(name); + } + + public static Test suite() { + return new TestSuite(PreferencesTest.class); + } + + protected void setUp() throws Exception { + super.setUp(); + ClientFacade facade = getFacade(); + queryMod = facade; + modificationMod = facade; + updateMod = facade; + } + + public void testLoad() throws Exception { + Preferences preferences = queryMod.getPreferences(); + Configuration config = ((RaplaConfiguration)preferences.getEntry("org.rapla.SessionTest")).getConfig(); + assertEquals("testvalue",config.getAttribute("test")); + } + + public void testStore() throws Exception { + Preferences preferences = queryMod.getPreferences(); + Preferences clone = (Preferences) modificationMod.edit(preferences); + //Allocatable allocatable = queryMod.getAllocatables()[0]; + //Configuration config = queryMod.createReference((RaplaType)allocatable); + //clone.putEntry("org.rapla.gui.weekview", config); + modificationMod.store(clone); + //assertEquals(allocatable, queryMod.resolve(config)); + updateMod.refresh(); + } + +} + + + + + diff --git a/Rapla/test-src/org/rapla/entities/tests/ReservationFilterTest.java b/Rapla/test-src/org/rapla/entities/tests/ReservationFilterTest.java new file mode 100644 index 0000000..5bea869 --- /dev/null +++ b/Rapla/test-src/org/rapla/entities/tests/ReservationFilterTest.java @@ -0,0 +1,97 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.tests; +import java.util.Collections; +import java.util.Iterator; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.rapla.RaplaTestCase; +import org.rapla.entities.configuration.CalendarModelConfiguration; +import org.rapla.entities.configuration.Preferences; +import org.rapla.entities.configuration.RaplaMap; +import org.rapla.entities.dynamictype.ClassificationFilter; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.facade.ClientFacade; +import org.rapla.facade.ModificationModule; +import org.rapla.facade.QueryModule; +import org.rapla.facade.UpdateModule; + +public class ReservationFilterTest extends RaplaTestCase { + ModificationModule modificationMod; + QueryModule queryMod; + UpdateModule updateMod; + + public ReservationFilterTest(String name) { + super(name); + } + + public static Test suite() { + return new TestSuite(ReservationFilterTest.class); + } + + public void setUp() throws Exception { + super.setUp(); + ClientFacade facade = getFacade(); + queryMod = facade; + modificationMod = facade; + updateMod = facade; + } + + public void testStore() throws Exception { + // select from event where (name contains 'planting' or name contains 'owl') or (description contains 'friends'); + DynamicType dynamicType = queryMod.getDynamicType("event"); + ClassificationFilter classificationFilter = dynamicType.newClassificationFilter(); + classificationFilter.setRule(0 + ,dynamicType.getAttribute("name") + ,new Object[][] { + {"contains","planting"} + ,{"contains","owl"} + } + ); + classificationFilter.setRule(1 + ,dynamicType.getAttribute("description") + ,new Object[][] { + {"contains","friends"} + } + ); + /* + modificationMod.newRaplaCalendarModel( ) + ReservationFilter filter = modificationMod.newReservationFilter(, null, ReservationFilter.PARTICULAR_PERIOD, queryMod.getPeriods()[1], null, null ); + // filter.setPeriod(); + //assertEquals("bowling",queryMod.getReservations(filter)[0].getClassification().getValue("name")); + assertTrue(((EntityReferencer)filter).isRefering((RefEntity)dynamicType)); +*/ + ClassificationFilter[] filter = new ClassificationFilter[] {classificationFilter}; + + RaplaMap selected = modificationMod.newRaplaMap( Collections.EMPTY_MAP ); + CalendarModelConfiguration conf = modificationMod.newRaplaCalendarModel(selected, null,filter,null, null, null, queryMod.today(), "week", null ); + Preferences prefs = (Preferences) modificationMod.edit( queryMod.getPreferences()); + prefs.putEntry( "org.rapla.TestConf", conf); + modificationMod.store( prefs ); + + DynamicType newDynamicType = (DynamicType) modificationMod.edit( dynamicType ); + newDynamicType.removeAttribute(dynamicType.getAttribute("description")); + modificationMod.store( newDynamicType ); + + CalendarModelConfiguration configuration = (CalendarModelConfiguration)queryMod.getPreferences().getEntry("org.rapla.TestConf"); + filter = configuration.getFilter(); + Iterator it = filter[0].ruleIterator(); + it.next(); + assertTrue("second rule should be removed." , !it.hasNext()); + + } + + +} diff --git a/Rapla/test-src/org/rapla/entities/tests/ReservationTest.java b/Rapla/test-src/org/rapla/entities/tests/ReservationTest.java new file mode 100644 index 0000000..1f93f81 --- /dev/null +++ b/Rapla/test-src/org/rapla/entities/tests/ReservationTest.java @@ -0,0 +1,145 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.tests; +import java.util.Calendar; +import java.util.Date; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.rapla.RaplaTestCase; +import org.rapla.components.util.DateTools; +import org.rapla.entities.Entity; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Reservation; +import org.rapla.facade.ClientFacade; +import org.rapla.facade.ModificationModule; +import org.rapla.facade.QueryModule; +import org.rapla.framework.RaplaException; + +public class ReservationTest extends RaplaTestCase { + Reservation reserv1; + Reservation reserv2; + Allocatable allocatable1; + Allocatable allocatable2; + Calendar cal; + + ModificationModule modificationMod; + QueryModule queryMod; + + public ReservationTest(String name) { + super(name); + } + + public static Test suite() { + return new TestSuite(ReservationTest.class); + } + + public void setUp() throws Exception { + super.setUp(); + + ClientFacade facade = getFacade(); + queryMod = facade; + modificationMod = facade; + + cal = Calendar.getInstance(DateTools.getTimeZone()); + + + reserv1 = modificationMod.newReservation(); + reserv1.getClassification().setValue("name","Test Reservation 1"); + + reserv2 = modificationMod.newReservation(); + reserv2.getClassification().setValue("name","Test Reservation 2"); + + allocatable1 = modificationMod.newResource(); + allocatable1.getClassification().setValue("name","Test Resource 1"); + + allocatable2 = modificationMod.newResource(); + allocatable2.getClassification().setValue("name","Test Resource 2"); + + cal.set(Calendar.DAY_OF_WEEK,Calendar.TUESDAY); + cal.set(Calendar.HOUR_OF_DAY,13); + cal.set(Calendar.MINUTE,0); + Date startDate = cal.getTime(); + cal.set(Calendar.HOUR_OF_DAY,16); + Date endDate = cal.getTime(); + Appointment appointment = modificationMod.newAppointment(startDate, endDate); + reserv1.addAppointment(appointment); + reserv1.addAllocatable(allocatable1); + } + + public void testHasAllocated() throws RaplaException { + assertTrue(reserv1.hasAllocated(allocatable1)); + assertTrue( ! reserv1.hasAllocated(allocatable2)); + } + + public void testEqual() { + assertTrue( ! reserv1.equals (reserv2)); + assertTrue(reserv1.equals (reserv1)); + } + + public void testEdit() throws RaplaException { + // store the reservation to create the id's + modificationMod.storeObjects(new Entity[] {allocatable1,allocatable2, reserv1}); + Reservation persistantReservation = (Reservation) modificationMod.getPersistant( reserv1); + Appointment oldAppointment= persistantReservation.getAppointments()[0]; + + // Clone the reservation + Reservation clone = (Reservation) modificationMod.edit(persistantReservation); + assertTrue(persistantReservation.equals(clone)); + assertTrue(clone.hasAllocated(allocatable1)); + + // Modify the cloned appointment + Appointment clonedAppointment= clone.getAppointments()[0]; + cal = Calendar.getInstance(DateTools.getTimeZone()); + cal.setTime(clonedAppointment.getStart()); + cal.set(Calendar.HOUR_OF_DAY,12); + clonedAppointment.move(cal.getTime()); + + // Add a new appointment + cal.setTime(clonedAppointment.getStart()); + cal.set(Calendar.DAY_OF_WEEK,Calendar.MONDAY); + cal.set(Calendar.HOUR_OF_DAY,15); + Date startDate2 = cal.getTime(); + cal.set(Calendar.HOUR_OF_DAY,17); + Date endDate2 = cal.getTime(); + Appointment newAppointment = modificationMod.newAppointment(startDate2, endDate2); + clone.addAppointment(newAppointment); + + // Copy clone back to original reservation + modificationMod.storeObjects(new Entity[] {clone}); + + assertTrue(persistantReservation.hasAllocated(allocatable1)); + // Check if oldAppointment has been modified + cal.setTime(oldAppointment.getStart()); + assertTrue(cal.get(Calendar.DAY_OF_WEEK) == Calendar.TUESDAY); + assertTrue(cal.get(Calendar.HOUR_OF_DAY) == 12); + + // Check if newAppointment has been added + Appointment[] appointments = persistantReservation.getAppointments(); + assertTrue(appointments.length == 2); + cal.setTime(appointments[1].getEnd()); + assertEquals(17,cal.get(Calendar.HOUR_OF_DAY)); + assertEquals(Calendar.MONDAY,cal.get(Calendar.DAY_OF_WEEK)); + cal.setTime(appointments[1].getStart()); + assertEquals(15,cal.get(Calendar.HOUR_OF_DAY)); + assertEquals(Calendar.MONDAY,cal.get(Calendar.DAY_OF_WEEK)); + } + +} + + + + + diff --git a/Rapla/test-src/org/rapla/entities/tests/Time.java b/Rapla/test-src/org/rapla/entities/tests/Time.java new file mode 100644 index 0000000..7b5adeb --- /dev/null +++ b/Rapla/test-src/org/rapla/entities/tests/Time.java @@ -0,0 +1,129 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.tests; + +import java.util.*; +/** + For storing time-information (without dates). + This is only used in the test-cases. +*/ +public final class Time implements Comparable +{ + public final static int MILLISECONDS_PER_SECOND = 1000; + public final static int MILLISECONDS_PER_MINUTE = 60 * 1000; + public final static int MILLISECONDS_PER_HOUR = 60 * 60 * 1000 ; + public final static int MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000 ; + int millis = 0; + + public Time(int millis) { + this.millis = millis; + } + + public Time(Calendar cal) { + this.millis = calculateMillis(cal.get(Calendar.HOUR_OF_DAY) + ,cal.get(Calendar.MINUTE) + ,cal.get(Calendar.SECOND) + ,cal.get(Calendar.MILLISECOND)); + } + + public Time(int hour,int minute) { + int second = 0; + int millis = 0; + this.millis = calculateMillis(hour,minute,second,millis); + } + + public static Calendar time2calendar(long time,TimeZone zone) + { + Calendar c = Calendar.getInstance(zone); + c.setTime(new Date(time)); + return c; + } + + + public static int date2daytime(Date d,TimeZone zone) + { + Calendar c = Calendar.getInstance(zone); + c.setTime(d); + return calendar2daytime(c); + } + + + //* @return Time in milliseconds + public static int calendar2daytime(Calendar cal) + { + return Time.calculateMillis(cal.get(Calendar.HOUR_OF_DAY) + ,cal.get(Calendar.MINUTE) + ,cal.get(Calendar.SECOND) + ,cal.get(Calendar.MILLISECOND)); + } + + private static int calculateMillis(int hour,int minute,int second,int millis) { + return ((hour * 60 + minute) * 60 + second) * 1000 + millis; + } + + public int getHour() { + return millis / MILLISECONDS_PER_HOUR; + } + + public int getMillis() { + return millis; + } + + public int getMinute() { + return (millis % MILLISECONDS_PER_HOUR) / MILLISECONDS_PER_MINUTE; + } + + public int getSecond() { + return (millis % MILLISECONDS_PER_MINUTE) / MILLISECONDS_PER_SECOND; + } + + public int getMillisecond() { + return (millis % MILLISECONDS_PER_SECOND); + } + + public String toString() { + return getHour() + ":" + getMinute(); + } + + public Date toDate(TimeZone zone) { + Calendar cal = Calendar.getInstance(zone); + cal.setTime(new Date(0)); + cal.set(Calendar.HOUR_OF_DAY,getHour()); + cal.set(Calendar.MINUTE,getMinute()); + cal.set(Calendar.SECOND,getSecond()); + return cal.getTime(); + } + + public int compareTo(Object obj) { + Time time2 = (Time) obj; + if (getMillis() < time2.getMillis()) + return -1; + if (getMillis() > time2.getMillis()) + return 1; + return 0; + } + public boolean equals(Object obj) { + if (!(obj instanceof Time)) + return false; + else + return (compareTo(obj) == 0); + } +} + + + + + + + + diff --git a/Rapla/test-src/org/rapla/entities/tests/TimeTest.java b/Rapla/test-src/org/rapla/entities/tests/TimeTest.java new file mode 100644 index 0000000..0969bc1 --- /dev/null +++ b/Rapla/test-src/org/rapla/entities/tests/TimeTest.java @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.tests; + +import junit.framework.*; + +public class TimeTest extends TestCase { + + public TimeTest(String name) { + super(name); + } + + public static Test suite() { + return new TestSuite(TimeTest.class); + } + + protected void setUp() { + } + + public void testTime() { + Time time = new Time(0,1); + assertTrue(time.getMillis() == Time.MILLISECONDS_PER_MINUTE); + Time time1 = new Time( 2 * Time.MILLISECONDS_PER_HOUR + 15 * Time.MILLISECONDS_PER_MINUTE); + assertTrue(time1.getHour() == 2); + assertTrue(time1.getMinute() == 15); + Time time2 = new Time(23,15); + Time time3 = new Time(2,15); + assertTrue(time1.compareTo(time2) == -1); + assertTrue(time2.compareTo(time1) == 1); + assertTrue(time1.compareTo(time3) == 0); + } +} + + + + + diff --git a/Rapla/test-src/org/rapla/entities/tests/UserTest.java b/Rapla/test-src/org/rapla/entities/tests/UserTest.java new file mode 100644 index 0000000..012bd52 --- /dev/null +++ b/Rapla/test-src/org/rapla/entities/tests/UserTest.java @@ -0,0 +1,88 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.entities.tests; +import java.util.Locale; + +import org.rapla.ServletTestBase; +import org.rapla.entities.Category; +import org.rapla.entities.User; +import org.rapla.facade.ClientFacade; +import org.rapla.framework.RaplaException; +import org.rapla.server.ServerService; + + +public class UserTest extends ServletTestBase { + ServerService raplaServer; + + ClientFacade adminFacade; + ClientFacade testFacade; + Locale locale; + + public UserTest(String name) { + super(name); + } + + protected void setUp() throws Exception { + super.setUp(); + // start the server + raplaServer = (ServerService) + getContext().lookup(ServerService.ROLE + "/storage-file"); + // start the client service + adminFacade = (ClientFacade) + getContext().lookup(ClientFacade.ROLE + "/remote-facade"); + adminFacade.login("homer","duffs".toCharArray()); + locale = Locale.getDefault(); + + try + { + Category groups = (Category) adminFacade.edit( adminFacade.getUserGroupsCategory() ); + Category testGroup = adminFacade.newCategory(); + testGroup.setKey("test-group"); + groups.addCategory( testGroup ); + adminFacade.store( groups ); + } catch (RaplaException ex) { + adminFacade.logout(); + super.tearDown(); + throw ex; + + } + testFacade = (ClientFacade) + getContext().lookup(ClientFacade.ROLE + "/remote-facade-2"); + boolean canLogin = testFacade.login("homer","duffs".toCharArray()); + assertTrue( "Can't login", canLogin ); + } + + protected void tearDown() throws Exception { + adminFacade.logout(); + testFacade.logout(); + super.tearDown(); + } + + public void testCreateAndRemoveUser() throws Exception { + User user = adminFacade.newUser(); + user.setUsername("test"); + user.setName("Test User"); + adminFacade.store( user ); + testFacade.refresh(); + User newUser = testFacade.getUser("test"); + testFacade.remove( newUser ); + // first create a new resource and set the permissions + } + + +} + + + + + diff --git a/Rapla/test-src/org/rapla/facade/tests/ClientFacadeTest.java b/Rapla/test-src/org/rapla/facade/tests/ClientFacadeTest.java new file mode 100644 index 0000000..98975e0 --- /dev/null +++ b/Rapla/test-src/org/rapla/facade/tests/ClientFacadeTest.java @@ -0,0 +1,313 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.facade.tests; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.Locale; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.rapla.RaplaTestCase; +import org.rapla.components.util.DateTools; +import org.rapla.components.util.SerializableDateTimeFormat; +import org.rapla.entities.DependencyException; +import org.rapla.entities.Entity; +import org.rapla.entities.EntityNotFoundException; +import org.rapla.entities.ReadOnlyException; +import org.rapla.entities.User; +import org.rapla.entities.configuration.CalendarModelConfiguration; +import org.rapla.entities.configuration.Preferences; +import org.rapla.entities.configuration.RaplaMap; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.ClassificationFilter; +import org.rapla.facade.ClientFacade; +import org.rapla.facade.Conflict; +import org.rapla.facade.ModificationModule; +import org.rapla.facade.QueryModule; +import org.rapla.facade.UserModule; +import org.rapla.framework.RaplaException; +import org.rapla.framework.RaplaLocale; +import org.rapla.plugin.weekview.WeekViewFactory; + +public class ClientFacadeTest extends RaplaTestCase { + ClientFacade facade; + Locale locale; + + public ClientFacadeTest(String name) { + super(name); + } + + public static Test suite() { + return new TestSuite(ClientFacadeTest.class); + } + + protected void setUp() throws Exception { + super.setUp(); + facade = (ClientFacade) + getContext().lookup(ClientFacade.ROLE + "/local-facade"); + facade.login("homer","duffs".toCharArray()); + locale = Locale.getDefault(); + } + + protected void tearDown() throws Exception { + facade.logout(); + super.tearDown(); + } + + private Reservation findReservation(QueryModule queryMod,String name) throws RaplaException { + Reservation[] reservations = queryMod.getReservationsForAllocatable(null,null,null,null); + for (int i=0;i 0); + } + + void printConflicts(Conflict[] c) { + System.out.println(c.length + " Conflicts:"); + for (int i=0;i blocks = new ArrayList(); + Reservation reservation = getFacade().newReservation(); + Appointment appointment = getFacade().newAppointment( + formater().parseDateTime("2004-01-01","18:30:00") + ,formater().parseDateTime("2004-01-02","12:00:00") + ); + reservation.addAppointment( appointment ); + appointment.createBlocks(start,end, blocks ); + blocks = RaplaBuilder.splitBlocks( blocks + ,start + ,end + ); + + assertEquals("Blocks are not split in two", 2, blocks.size()); + assertEquals( formater().parseDateTime("2004-01-01","23:59:59").getTime()/1000, blocks.get(0).getEnd()/1000); + assertEquals( formater().parseDateTime("2004-01-02","00:00:00").getTime(), blocks.get(1).getStart()); + assertEquals( formater().parseDateTime("2004-01-02","12:00:00").getTime(), blocks.get(1).getEnd()); + + // test 3 Blocks + blocks.clear(); + reservation = getFacade().newReservation(); + appointment = getFacade().newAppointment( + formater().parseDateTime("2004-01-01","18:30:00") + ,formater().parseDateTime("2004-01-04","00:00:00") + ); + reservation.addAppointment( appointment ); + appointment.createBlocks(start,end, blocks ); + blocks = RaplaBuilder.splitBlocks( blocks + ,start + ,end + ); + assertEquals("Blocks are not split in three", 3, blocks.size()); + assertEquals(formater().parseDateTime("2004-01-03","23:59:59").getTime()/1000, blocks.get(2).getEnd()/1000); + + // test 3 Blocks, but only the first two should show + blocks.clear(); + reservation = getFacade().newReservation(); + appointment = getFacade().newAppointment( + formater().parseDateTime("2004-01-01","18:30:00") + ,formater().parseDateTime("2004-01-04","00:00:00") + ); + reservation.addAppointment( appointment ); + appointment.createBlocks(start,end, blocks ); + blocks = RaplaBuilder.splitBlocks( blocks + ,start + ,formater().parseDateTime("2004-01-03","00:00:00") + + ); + assertEquals("Blocks are not split in two", 2, blocks.size()); + assertEquals(formater().parseDateTime("2004-01-02","23:59:59").getTime()/1000, blocks.get(1).getEnd()/1000); + } +} + diff --git a/Rapla/test-src/org/rapla/gui/toolkit/tests/ErrorDialogTest.java b/Rapla/test-src/org/rapla/gui/toolkit/tests/ErrorDialogTest.java new file mode 100644 index 0000000..d81539d --- /dev/null +++ b/Rapla/test-src/org/rapla/gui/toolkit/tests/ErrorDialogTest.java @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.gui.toolkit.tests; + +import org.rapla.gui.tests.GUITestCase; +import org.rapla.gui.toolkit.ErrorDialog; + +import junit.framework.*; + +public class ErrorDialogTest extends GUITestCase { + + public ErrorDialogTest(String name) { + super(name); + } + + public static Test suite() { + return new TestSuite(ErrorDialogTest.class); + } + + public void testError() throws Exception { + ErrorDialog.THROW_ERROR_DIALOG_EXCEPTION = false; + ErrorDialog dialog = new ErrorDialog(getContext()); + dialog.show("This is a very long sample error-text for our error-message-displaying-test" + + " it should be wrapped so that the whole text is diplayed."); + } + + public static void main(String[] args) { + new ErrorDialogTest("ErrorDialogTest").interactiveTest("testError"); + } + + +} + + + + + + + + + + diff --git a/Rapla/test-src/org/rapla/plugin/tests/CopyPeriodPluginTest.java b/Rapla/test-src/org/rapla/plugin/tests/CopyPeriodPluginTest.java new file mode 100644 index 0000000..f3c629f --- /dev/null +++ b/Rapla/test-src/org/rapla/plugin/tests/CopyPeriodPluginTest.java @@ -0,0 +1,83 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.plugin.tests; + +import java.util.Collections; +import java.util.Locale; + +import org.rapla.RaplaTestCase; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Period; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.ClassificationFilter; +import org.rapla.facade.ClientFacade; +import org.rapla.gui.internal.common.CalendarModelImpl; +import org.rapla.gui.internal.common.CalendarSelectionModel; +import org.rapla.plugin.periodcopy.CopyPluginInit; + +/** listens for allocation changes */ +public class CopyPeriodPluginTest extends RaplaTestCase { + ClientFacade facade; + Locale locale; + + public CopyPeriodPluginTest(String name) { + super(name); + } + + protected void setUp() throws Exception { + super.setUp(); + facade = (ClientFacade) + getContext().lookup(ClientFacade.ROLE + "/local-facade"); + facade.login("homer","duffs".toCharArray()); + locale = Locale.getDefault(); + } + + private Reservation findReservationWithName(Reservation[] reservations, String name) { + for (int i=0;i= 0 ); + + reservationName = "Another name"; + r.getClassification().setValue( "name", reservationName ); + r.getAppointments()[0].move( new Date( new Date().getTime() + DateTools.MILLISECONDS_PER_HOUR ) ); + facade1.store( r ); + + System.out.println( ( (RefEntity) r ).getVersion() ); + + Thread.sleep( 1000 ); + assertEquals( 2, mailMock.getCallCount() ); + + assertNotNull( mailMock.getMailBody() ); + assertTrue( mailMock.getMailBody().indexOf( reservationName ) >= 0 ); + + + + } + + public void testRemove() throws Exception + { + Allocatable allocatable = facade1.getAllocatables()[0]; + + add( allocatable ); + + Reservation r = facade1.newReservation(); + String reservationName = "New Reservation"; + r.getClassification().setValue( "name", reservationName ); + Appointment appointment = facade1.newAppointment( new Date(), new Date( new Date().getTime() + + DateTools.MILLISECONDS_PER_HOUR ) ); + r.addAppointment( appointment ); + r.addAllocatable( allocatable ); + + + facade1.store( r ); + facade1.remove( r ); + + Thread.sleep( 1000 ); + MockMailer mailMock = (MockMailer) raplaServer.getContext().lookup( MailInterface.ROLE ); + assertEquals( 2, mailMock.getCallCount() ); + String body = mailMock.getMailBody(); + assertTrue( "Body doesnt contain delete text\n" + body, body.indexOf( "gel\u00f6scht" ) >= 0 ); + + } + +} \ No newline at end of file diff --git a/Rapla/test-src/org/rapla/server/SecurityManagerTest.java b/Rapla/test-src/org/rapla/server/SecurityManagerTest.java new file mode 100644 index 0000000..9da8d88 --- /dev/null +++ b/Rapla/test-src/org/rapla/server/SecurityManagerTest.java @@ -0,0 +1,144 @@ +package org.rapla.server; + +import java.util.Date; +import java.util.Locale; + +import org.rapla.ServletTestBase; +import org.rapla.components.util.DateTools; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.ClassificationFilter; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.facade.ClientFacade; +import org.rapla.storage.RaplaSecurityException; + +public class SecurityManagerTest extends ServletTestBase { + + ServerService raplaServer; + + protected ClientFacade facade1; + + Locale locale; + + public SecurityManagerTest(String name) + { + super(name); + } + + protected void setUp() throws Exception + { + super.setUp(); + // start the server + raplaServer = (ServerService) getContext().lookup( + ServerService.ROLE + "/" + getStorageName()); + // start the client service + facade1 = (ClientFacade) getContext().lookup( + ClientFacade.ROLE + "/remote-facade"); +/* facade2 = (ClientFacade) getContext().lookup( + ClientFacade.ROLE + "/remote-facade-2");*/ + locale = Locale.getDefault(); + } + + protected String getStorageName() + { + return "storage-file"; + } + + public void testConflictForbidden() throws Exception + { + // We test conflict prevention for an appointment that is in the future + Date start = new Date(facade1.today().getTime() + DateTools.MILLISECONDS_PER_DAY + 10 * DateTools.MILLISECONDS_PER_HOUR); + Date end = new Date( start.getTime() + 2 * DateTools.MILLISECONDS_PER_HOUR); + + facade1.login("homer", "duffs".toCharArray()); + DynamicType roomType = facade1.getDynamicType("room"); + ClassificationFilter filter = roomType.newClassificationFilter(); + filter.addEqualsRule("name", "erwin"); + Allocatable resource = facade1.getAllocatables( filter.toArray())[0]; + { + // First we create a reservation for the resource + Reservation event = facade1.newReservation(); + event.getClassification().setValue("name", "taken"); + event.addAppointment( facade1.newAppointment( start, end ) ); + event.addAllocatable( resource ); + facade1.store( event ); + } + facade1.logout(); + // Now we login as a non admin user, who isnt allowed to create conflicts on the resource erwin + facade1.login("monty", "burns".toCharArray()); + { + Reservation event = facade1.newReservation(); + // A new event with the same time for the same resource should fail. + event.getClassification().setValue("name", "conflicting event"); + Appointment app = facade1.newAppointment( start, end ) ; + event.addAppointment( app); + event.addAllocatable( resource ); + try + { + facade1.store( event ); + fail("Security Exception expected"); + } + catch ( Exception ex) + { + Throwable ex1 = ex; + boolean secExceptionFound = false; + while ( ex1 != null) + { + if ( ex1 instanceof RaplaSecurityException) + { + secExceptionFound = true; + } + ex1 = ex1.getCause(); + } + if ( !secExceptionFound) + { + ex.printStackTrace(); + fail("Exception expected but was not security exception"); + } + } + // moving the start of the second appointment to the end of the first one should work + app.move( end ); + facade1.store( event ); + } + { + // We have to reget the event + DynamicType eventType = facade1.getDynamicType("event"); + ClassificationFilter eventFilter = eventType.newClassificationFilter(); + eventFilter.addEqualsRule("name", "conflicting event"); + + Reservation event = facade1.getReservationsForAllocatable( null, null, null, eventFilter.toArray())[0]; + // But moving back the appointment to today should fail + event = (Reservation) facade1.edit( event ); + Date startPlus1 = new Date( start.getTime() + DateTools.MILLISECONDS_PER_HOUR) ; + event.getAppointments()[0].move( startPlus1,end); + try + { + facade1.store( event ); + fail("Security Exception expected"); + } + catch ( Exception ex) + { + Throwable ex1 = ex; + boolean secExceptionFound = false; + while ( ex1 != null) + { + if ( ex1 instanceof RaplaSecurityException) + { + secExceptionFound = true; + } + ex1 = ex1.getCause(); + } + if ( !secExceptionFound) + { + ex.printStackTrace(); + fail("Exception expected but was not security exception"); + } + } + facade1.logout(); + Thread.sleep(100); + } + + } + +} diff --git a/Rapla/test-src/org/rapla/storage/dbfile/tests/FileOperatorDiffTest.java b/Rapla/test-src/org/rapla/storage/dbfile/tests/FileOperatorDiffTest.java new file mode 100644 index 0000000..1730a8f --- /dev/null +++ b/Rapla/test-src/org/rapla/storage/dbfile/tests/FileOperatorDiffTest.java @@ -0,0 +1,88 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.storage.dbfile.tests; + +import java.io.FileInputStream; +import java.io.IOException; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.rapla.RaplaTestCase; +import org.rapla.entities.Entity; +import org.rapla.entities.domain.Allocatable; +import org.rapla.framework.RaplaException; +import org.rapla.storage.CachableStorageOperator; + +public class FileOperatorDiffTest extends RaplaTestCase { + CachableStorageOperator operator; + + public FileOperatorDiffTest(String name) { + super(name); + } + + public boolean differ(String file1, String file2) throws IOException { + FileInputStream in1 = null; + FileInputStream in2 = null; + boolean bDiffer = false; + try { + in1 = new FileInputStream(file1); + in2 = new FileInputStream(file2); + while (true) { + int b1 = in1.read(); + int b2 = in2.read(); + if (b1 != b2) { + bDiffer = true; + break; + } + if (b1 == -1) { + break; + } + } + return bDiffer; + } + finally { + if (in1 != null) + in1.close(); + if (in2 != null) + in2.close(); + } + } + + public static Test suite() { + return new TestSuite(FileOperatorDiffTest.class); + } + + public void setUp() throws Exception { + super.setUp(); + operator = (CachableStorageOperator) + getContext().lookup(CachableStorageOperator.ROLE +"/file"); + } + + public void testSave() throws RaplaException,IOException { + String testFile = "test-src/testdefault.xml"; + assertTrue(differ(TEST_FOLDER_NAME + "/test.xml",testFile) == false); + operator.connect(); + Entity obj = (Entity)operator.getObjects( Allocatable.TYPE).iterator().next(); + operator.editObject(obj, null); + operator.storeAndRemove(new Entity[] { obj }, Entity.ENTITY_ARRAY, null); + assertTrue("stored version differs from orginal " + testFile + , differ(TEST_FOLDER_NAME + "/test.xml",testFile) == false ); + } + +} + + + + + diff --git a/Rapla/test-src/org/rapla/storage/dbfile/tests/FileOperatorRemoteTest.java b/Rapla/test-src/org/rapla/storage/dbfile/tests/FileOperatorRemoteTest.java new file mode 100644 index 0000000..424a743 --- /dev/null +++ b/Rapla/test-src/org/rapla/storage/dbfile/tests/FileOperatorRemoteTest.java @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.storage.dbfile.tests; + +import org.rapla.ServerTest; +import org.rapla.storage.CachableStorageOperator; + +public class FileOperatorRemoteTest extends ServerTest { + CachableStorageOperator operator; + + public FileOperatorRemoteTest(String name) { + super(name); + } + + protected String getStorageName() { + return "storage-file"; + } + + +} + + + + + diff --git a/Rapla/test-src/org/rapla/storage/dbfile/tests/FileOperatorTest.java b/Rapla/test-src/org/rapla/storage/dbfile/tests/FileOperatorTest.java new file mode 100644 index 0000000..34467d5 --- /dev/null +++ b/Rapla/test-src/org/rapla/storage/dbfile/tests/FileOperatorTest.java @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.storage.dbfile.tests; +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.rapla.storage.tests.AbstractOperatorTest; + +public class FileOperatorTest extends AbstractOperatorTest { + + public FileOperatorTest(String name) { + super(name); + } + + public static Test suite() { + return new TestSuite(FileOperatorTest.class); + } + + + protected String getStorageName() { + return "file"; + } + + protected String getFacadeName() { + return "local-facade"; + } + + +} + + + + + diff --git a/Rapla/test-src/org/rapla/storage/dbsql/tests/SQLOperatorRemoteTest.java b/Rapla/test-src/org/rapla/storage/dbsql/tests/SQLOperatorRemoteTest.java new file mode 100644 index 0000000..d5359e0 --- /dev/null +++ b/Rapla/test-src/org/rapla/storage/dbsql/tests/SQLOperatorRemoteTest.java @@ -0,0 +1,213 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.storage.dbsql.tests; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.rapla.ServerTest; +import org.rapla.components.util.IOUtil; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.AttributeAnnotations; +import org.rapla.entities.dynamictype.AttributeType; +import org.rapla.entities.dynamictype.ClassificationFilter; +import org.rapla.entities.dynamictype.ConstraintIds; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.facade.ClientFacade; +import org.rapla.framework.RaplaContext; +import org.rapla.framework.RaplaException; +import org.rapla.storage.CachableStorageOperator; +import org.rapla.storage.ImportExportManager; +import org.rapla.storage.dbsql.DBOperator; + +public class SQLOperatorRemoteTest extends ServerTest { + + public SQLOperatorRemoteTest(String name) { + super(name); + } + + protected String getStorageName() { + return "storage-sql"; + } + + public static Test suite() throws Exception { + TestSuite suite = new TestSuite("SQLOperatorRemoteTest"); + suite.addTest( new SQLOperatorRemoteTest("testExport")); + suite.addTest( new SQLOperatorRemoteTest("testNewAttribute")); + suite.addTest( new SQLOperatorRemoteTest("testAttributeChange")); + suite.addTest( new SQLOperatorRemoteTest("testChangeDynamicType")); + //suite.addTest( new SQLOperatorRemoteTest("testChangeDynamicTypeRemoveAttribute")); + suite.addTest( new SQLOperatorRemoteTest("testChangeGroup")); + suite.addTest( new SQLOperatorRemoteTest("testCreateResourceAndRemoveAttribute")); + return suite; + } + + protected void setUp() throws Exception + { + IOUtil.copy( "webapp/WEB-INF/rapla-hsqldb.properties", WEBAPP_INF_FOLDER_NAME+ "/rapla-hsqldb.properties" ); + IOUtil.copy( "webapp/WEB-INF/rapla-hsqldb.script", WEBAPP_INF_FOLDER_NAME+"/rapla-hsqldb.script" ); + super.setUp(); + } + + public void testExport() throws Exception { + RaplaContext sm = getContext(); + ImportExportManager conv = (ImportExportManager) sm.lookup(ImportExportManager.ROLE); + conv.doExport(); + CachableStorageOperator operator = (CachableStorageOperator) + getContext().lookup(CachableStorageOperator.ROLE +"/sql"); + operator.disconnect(); + Thread.sleep( 1000 ); + operator.connect(); + operator.getVisibleEntities( null ); + operator.disconnect(); + } + + /** exposes a bug in the 0.12.1 Version of Rapla */ + public void testAttributeChange() throws Exception { + ClientFacade facade = (ClientFacade) + getContext().lookup(ClientFacade.ROLE +"/sql-facade"); + facade.login("admin","".toCharArray()); + // change Type + changeEventType( facade ); + facade.logout(); + + // We need to disconnect the operator + CachableStorageOperator operator = (CachableStorageOperator) + getContext().lookup(CachableStorageOperator.ROLE +"/sql"); + operator.disconnect(); + testTypeIds(); + // The error shows when connect again + operator.connect(); + changeEventType( facade ); + testTypeIds(); + + } + + private void changeEventType( ClientFacade facade ) throws RaplaException + { + DynamicType eventType = (DynamicType) facade.edit( facade.getDynamicType("event") ); + Attribute attribute = eventType.getAttribute("description"); + attribute.setType( AttributeType.CATEGORY ); + attribute.setConstraint( ConstraintIds.KEY_ROOT_CATEGORY, facade.getSuperCategory().getCategory("department") ); + facade.store( eventType ); + } + + private void testTypeIds() throws RaplaException, SQLException + { + CachableStorageOperator operator = (CachableStorageOperator) + getContext().lookup(CachableStorageOperator.ROLE +"/sql"); + Connection connection = ((DBOperator)operator).createConnection(); + String sql ="SELECT * from DYNAMIC_TYPE"; + try + { + Statement statement = connection.createStatement(); + ResultSet set = statement.executeQuery(sql); + while ( !set.isLast()) + { + set.next(); + String idString = set.getString("ID"); + String key = set.getString("TYPE_KEY"); + System.out.println( "id " + idString + " key " + key); + } + } + catch (SQLException ex) + { + throw new RaplaException( ex); + } + finally + { + connection.close(); + } + } + + + public void testNewAttribute() throws Exception { + ClientFacade facade = (ClientFacade) + getContext().lookup(ClientFacade.ROLE +"/sql-facade"); + facade.login("admin","".toCharArray()); + // change Type + DynamicType roomType = (DynamicType) facade.edit( facade.getDynamicType("room") ); + Attribute attribute = facade.newAttribute( AttributeType.STRING ); + attribute.setKey("color"); + attribute.setAnnotation( AttributeAnnotations.KEY_EDIT_VIEW, AttributeAnnotations.VALUE_NO_VIEW); + roomType.addAttribute( attribute ); + facade.store( roomType ); + + roomType = (DynamicType) facade.getPersistant( roomType ); + + Allocatable[] allocatables = facade.getAllocatables( new ClassificationFilter[] {roomType.newClassificationFilter() }); + Allocatable allocatable = (Allocatable) facade.edit( allocatables[0]); + allocatable.getClassification().setValue("color", "665532"); + + String name = (String) allocatable.getClassification().getValue("name"); + facade.store( allocatable ); + + facade.logout(); + + // We need to disconnect the operator + CachableStorageOperator operator = (CachableStorageOperator) + getContext().lookup(CachableStorageOperator.ROLE +"/sql"); + operator.disconnect(); + // The error shows when connect again + operator.connect(); + + facade.login("admin","".toCharArray()); + allocatables = facade.getAllocatables( new ClassificationFilter[] {roomType.newClassificationFilter() }); + allocatable = (Allocatable) facade.edit( allocatables[0]); + assertEquals( name, allocatable.getClassification().getValue("name") ); + } + + public void testCreateResourceAndRemoveAttribute() throws RaplaException + { + Allocatable newResource = facade1.newResource(); + newResource.setClassification( facade1.getDynamicType("room").newClassification()); + newResource.getClassification().setValue("name", "test-resource"); + //If commented in it works + //newResource.getClassification().setValue("belongsto", facade1.getSuperCategory().getCategory("department").getCategories()[0]); + facade1.store(newResource); + + DynamicType typeEdit3 = (DynamicType) facade1.edit(facade1.getDynamicType("room")); + typeEdit3.removeAttribute( typeEdit3.getAttribute("belongsto")); + facade1.store(typeEdit3); + + } + + + public void tearDown() throws Exception { + // nochmal ueberpruefen ob die Daten auch wirklich eingelesen werden koennen. This could not be the case + CachableStorageOperator operator = (CachableStorageOperator) + getContext().lookup(CachableStorageOperator.ROLE +"/sql"); + operator.disconnect(); + Thread.sleep( 200 ); + operator.connect(); + operator.getVisibleEntities( null ); + operator.disconnect(); + Thread.sleep( 100 ); + super.tearDown(); + Thread.sleep(500); + } + + + +} + + + + + diff --git a/Rapla/test-src/org/rapla/storage/dbsql/tests/SQLOperatorTest.java b/Rapla/test-src/org/rapla/storage/dbsql/tests/SQLOperatorTest.java new file mode 100644 index 0000000..b436f75 --- /dev/null +++ b/Rapla/test-src/org/rapla/storage/dbsql/tests/SQLOperatorTest.java @@ -0,0 +1,175 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.storage.dbsql.tests; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Date; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.rapla.components.util.DateTools; +import org.rapla.components.util.IOUtil; +import org.rapla.entities.Category; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Period; +import org.rapla.entities.domain.Repeating; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.AttributeType; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.storage.RefEntity; +import org.rapla.framework.RaplaException; +import org.rapla.storage.dbsql.DBOperator; +import org.rapla.storage.tests.AbstractOperatorTest; + +public class SQLOperatorTest extends AbstractOperatorTest { + + public SQLOperatorTest(String name) { + super(name); + } + + public void setUp() throws Exception { + IOUtil.copy( "webapp/WEB-INF/rapla-hsqldb.properties", TEST_FOLDER_NAME + "/rapla-hsqldb.properties" ); + IOUtil.copy( "webapp/WEB-INF/rapla-hsqldb.script", TEST_FOLDER_NAME + "/rapla-hsqldb.script" ); + super.setUp(); + ((DBOperator) operator).removeAll(); + operator.disconnect(); + operator.connect(); + } + + public static Test suite() { + return new TestSuite(SQLOperatorTest.class); + } + + /** exposes a bug in 1.1 + * @throws RaplaException */ + public void testPeriodInfitiveEnd() throws RaplaException { + facade.login("homer", "duffs".toCharArray() ); + Reservation event = facade.newReservation(); + Appointment appointment = facade.newAppointment( new Date(), new Date()); + event.getClassification().setValue("name","test"); + appointment.setRepeatingEnabled( true ); + appointment.getRepeating().setEnd( null ); + event.addAppointment( appointment ); + facade.store(event); + operator.refresh(); + + Reservation event1 = (Reservation)operator.getPersistant( event ); + Repeating repeating = event1.getAppointments()[0].getRepeating(); + assertNotNull( repeating ); + assertNull( repeating.getEnd()); + assertEquals( -1, repeating.getNumber()); + } + + public void testPeriodStorage() throws RaplaException { + facade.login("homer", "duffs".toCharArray() ); + Date start = DateTools.cutDate( new Date()); + Date end = new Date( start.getTime() + DateTools.MILLISECONDS_PER_WEEK); + Period period = facade.newPeriod(); + period.setName( "TEST PERIOD"); + period.setStart( start ); + period.setEnd( end ); + facade.store( period); + operator.refresh(); + + Period period1 = (Period)operator.getPersistant( period); + assertEquals( period1.getStart(), period.getStart()); + assertEquals( period1.getEnd(), period1.getEnd()); + } + + public void testCategoryChange() throws RaplaException { + facade.login("homer", "duffs".toCharArray() ); + { + Category category1 = facade.newCategory(); + Category category2 = facade.newCategory(); + category1.setKey("users1"); + category2.setKey("users2"); + Category groups = (Category) facade.edit(facade.getUserGroupsCategory()); + groups.addCategory( category1 ); + groups.addCategory( category2 ); + facade.store( groups); + assertEquals("users1",facade.getUserGroupsCategory().getCategories()[3].getKey()); + assertEquals("users2",facade.getUserGroupsCategory().getCategories()[4].getKey()); + operator.disconnect(); + operator.connect(); + facade.refresh(); + } + assertEquals("users1",facade.getUserGroupsCategory().getCategories()[3].getKey()); + assertEquals("users2",facade.getUserGroupsCategory().getCategories()[4].getKey()); + + + } + + + + public void testDynamicTypeChange() throws Exception + { + facade.login("homer", "duffs".toCharArray() ); + DynamicType type = (DynamicType)facade.edit(facade.getDynamicType("event")); + Object id = ((RefEntity)type).getId(); + Attribute att = facade.newAttribute( AttributeType.STRING); + att.setKey("test-att"); + type.addAttribute( att ); + facade.store( type); + facade.logout(); + printTypeIds(); + operator.disconnect(); + facade.login("homer", "duffs".toCharArray() ); + DynamicType typeAfterEdit = facade.getDynamicType("event"); + Object idAfterEdit = ((RefEntity)typeAfterEdit).getId(); + assertEquals( id, idAfterEdit); + } + + private void printTypeIds() throws RaplaException, SQLException + { + Connection connection = ((DBOperator)operator).createConnection(); + String sql ="SELECT * from DYNAMIC_TYPE"; + try + { + Statement statement = connection.createStatement(); + ResultSet set = statement.executeQuery(sql); + while ( !set.isLast()) + { + set.next(); + String idString = set.getString("ID"); + String key = set.getString("TYPE_KEY"); + System.out.println( "id " + idString + " key " + key); + } + } + catch (SQLException ex) + { + throw new RaplaException( ex); + } + finally + { + connection.close(); + } + } + + protected String getStorageName() { + return "sql"; + } + + protected String getFacadeName() { + return "sql-facade"; + } + +} + + + + + diff --git a/Rapla/test-src/org/rapla/storage/tests/AbstractOperatorTest.java b/Rapla/test-src/org/rapla/storage/tests/AbstractOperatorTest.java new file mode 100644 index 0000000..ca72136 --- /dev/null +++ b/Rapla/test-src/org/rapla/storage/tests/AbstractOperatorTest.java @@ -0,0 +1,189 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.storage.tests; +import java.util.Date; + +import org.rapla.RaplaTestCase; +import org.rapla.entities.Category; +import org.rapla.entities.Entity; +import org.rapla.entities.User; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Appointment; +import org.rapla.entities.domain.Repeating; +import org.rapla.entities.domain.Reservation; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.AttributeType; +import org.rapla.entities.dynamictype.ClassificationFilter; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.facade.ClientFacade; +import org.rapla.framework.RaplaException; +import org.rapla.storage.CachableStorageOperator; + +public abstract class AbstractOperatorTest extends RaplaTestCase { + + protected CachableStorageOperator operator; + protected ClientFacade facade; + public AbstractOperatorTest(String name) { + super(name); + } + + protected void setUp() throws Exception { + super.setUp(); + operator = (CachableStorageOperator)getContext().lookup(CachableStorageOperator.ROLE + "/" + getStorageName()); + facade = (ClientFacade) getContext().lookup(ClientFacade.ROLE + "/" + getFacadeName()); + } + + abstract protected String getStorageName(); + + abstract protected String getFacadeName(); + + public void testReservationStore() throws RaplaException { + // abspeichern + facade.login("homer", "duffs".toCharArray() ); + { + Reservation r = facade.newReservation(); + r.getClassification().setValue("name","test"); + Appointment app = facade.newAppointment( new Date(), new Date()); + Appointment app2 = facade.newAppointment( new Date(), new Date()); + Allocatable resource = facade.newResource(); + r.addAppointment( app); + r.addAppointment( app2); + r.addAllocatable(resource ); + r.setRestriction( resource, new Appointment[] {app}); + app.setRepeatingEnabled( true ); + app.getRepeating().setType(Repeating.DAILY); + app.getRepeating().setNumber( 10); + app.getRepeating().addException( new Date()); + facade.storeObjects( new Entity[] { r,app, app2, resource }); + } + operator.disconnect(); + operator.connect(); + facade.login("homer", "duffs".toCharArray() ); + // einlesen + { + String defaultReservation = "event"; + ClassificationFilter filter = facade.getDynamicType( defaultReservation ).newClassificationFilter(); + filter.addRule("name",new Object[][] { {"contains","test"}}); + Reservation reservation = facade.getReservationsForAllocatable( null, null, null, new ClassificationFilter[] {filter} )[0]; + Appointment[] apps = reservation.getAppointments(); + Allocatable resource = reservation.getAllocatables()[0]; + assertEquals( 2, apps.length); + assertEquals( 1, reservation.getAppointmentsFor( resource ).length); + Appointment app = reservation.getAppointmentsFor( resource )[0]; + assertEquals( 1, app.getRepeating().getExceptions().length); + assertEquals( Repeating.DAILY, app.getRepeating().getType()); + assertEquals( 10, app.getRepeating().getNumber()); + } + } + + public void testUserStore() throws RaplaException { + facade.login("homer", "duffs".toCharArray() ); + { + User u = facade.newUser(); + u.setUsername("kohlhaas"); + u.setAdmin( false); + u.addGroup( facade.getUserGroupsCategory().getCategory("my-group")); + facade.store( u ); + } + operator.disconnect(); + operator.connect(); + facade.login("homer", "duffs".toCharArray() ); + { + User u = facade.getUser("kohlhaas"); + Category[] groups = u.getGroups(); + assertEquals( groups.length, 2 ); + assertEquals( facade.getUserGroupsCategory().getCategory("my-group"), groups[1]); + assertFalse( u.isAdmin() ); + } + } + + public void testCategoryAnnotation() throws RaplaException { + String sampleDoc = "This is the category for user-groups"; + String sampleAnnotationValue = "documentation"; + facade.login("homer", "duffs".toCharArray() ); + { + Category userGroups = (Category) facade.edit( facade.getUserGroupsCategory()); + userGroups.setAnnotation( sampleAnnotationValue, sampleDoc ); + facade.store( userGroups ); + } + operator.disconnect(); + operator.connect(); + facade.login("homer", "duffs".toCharArray() ); + { + Category userGroups = facade.getUserGroupsCategory(); + assertEquals( sampleDoc, userGroups.getAnnotation( sampleAnnotationValue )); + } + } + + public void testAttributeStore() throws RaplaException { + facade.login("homer", "duffs".toCharArray() ); + // abspeichern + { + DynamicType type = (DynamicType) facade.edit( facade.getDynamicType("event")); + + Attribute att = facade.newAttribute( AttributeType.STRING ); + att.setKey("test-att"); + type.addAttribute( att ); + + Reservation r = facade.newReservation(); + try { + r.setClassification( type.newClassification() ); + fail("Should have thrown an IllegalStateException"); + } catch (IllegalStateException ex) { + } + + facade.store( type ); + + r.setClassification( ((DynamicType)facade.getPersistant(type)).newClassification() ); + + r.getClassification().setValue("name","test"); + r.getClassification().setValue("test-att","test-att-value"); + Appointment app = facade.newAppointment( new Date(), new Date()); + Appointment app2 = facade.newAppointment( new Date(), new Date()); + Allocatable resource = facade.newResource(); + r.addAppointment( app); + r.addAppointment( app2); + r.addAllocatable(resource ); + r.setRestriction( resource, new Appointment[] {app}); + app.setRepeatingEnabled( true ); + app.getRepeating().setType(Repeating.DAILY); + app.getRepeating().setNumber( 10); + app.getRepeating().addException( new Date()); + facade.storeObjects( new Entity[] { r,app, app2, resource }); + operator.disconnect(); + } + // einlesen + { + operator.connect(); + facade.login("homer", "duffs".toCharArray() ); + String defaultReservation = "event"; + ClassificationFilter filter = facade.getDynamicType( defaultReservation ).newClassificationFilter(); + filter.addRule("name",new Object[][] { {"contains","test"}}); + Reservation reservation = facade.getReservationsForAllocatable( null, null, null, new ClassificationFilter[] {filter} )[0]; + Appointment[] apps = reservation.getAppointments(); + Allocatable resource = reservation.getAllocatables()[0]; + assertEquals( "test-att-value", reservation.getClassification().getValue("test-att")); + assertEquals( 2, apps.length); + assertEquals( 1, reservation.getAppointmentsFor( resource ).length); + Appointment app = reservation.getAppointmentsFor( resource )[0]; + assertEquals( 1, app.getRepeating().getExceptions().length); + assertEquals( Repeating.DAILY, app.getRepeating().getType()); + assertEquals( 10, app.getRepeating().getNumber()); + } + } +} + + + + + diff --git a/Rapla/test-src/org/rapla/storage/tests/LocalCacheTest.java b/Rapla/test-src/org/rapla/storage/tests/LocalCacheTest.java new file mode 100644 index 0000000..7c0fcb1 --- /dev/null +++ b/Rapla/test-src/org/rapla/storage/tests/LocalCacheTest.java @@ -0,0 +1,127 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.storage.tests; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.Locale; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.rapla.RaplaTestCase; +import org.rapla.entities.User; +import org.rapla.entities.domain.Allocatable; +import org.rapla.entities.domain.Period; +import org.rapla.entities.domain.internal.AllocatableImpl; +import org.rapla.entities.dynamictype.Attribute; +import org.rapla.entities.dynamictype.AttributeType; +import org.rapla.entities.dynamictype.Classification; +import org.rapla.entities.dynamictype.DynamicType; +import org.rapla.entities.dynamictype.DynamicTypeAnnotations; +import org.rapla.entities.dynamictype.internal.AttributeImpl; +import org.rapla.entities.dynamictype.internal.DynamicTypeImpl; +import org.rapla.entities.storage.internal.SimpleIdentifier; +import org.rapla.storage.CachableStorageOperator; +import org.rapla.storage.LocalCache; + +public class LocalCacheTest extends RaplaTestCase { + Locale locale; + + public LocalCacheTest(String name) { + super(name); + } + + public static Test suite() { + return new TestSuite(LocalCacheTest.class); + } + + protected void setUp() throws Exception { + super.setUp(); + } + + public DynamicTypeImpl createDynamicType() throws Exception { + AttributeImpl attribute = new AttributeImpl(AttributeType.STRING); + attribute.setKey("name"); + attribute.setId(new SimpleIdentifier(Attribute.TYPE,1)); + DynamicTypeImpl dynamicType = new DynamicTypeImpl(); + dynamicType.setElementKey("defaultResource"); + dynamicType.setId(new SimpleIdentifier(DynamicType.TYPE,1)); + dynamicType.addAttribute(attribute); + dynamicType.setAnnotation(DynamicTypeAnnotations.KEY_NAME_FORMAT,"{name}"); + return dynamicType; + } + + + public AllocatableImpl createResource(int id,DynamicType type,String name) { + Date today = new Date(); + AllocatableImpl resource = new AllocatableImpl(today, today); + resource.setId(new SimpleIdentifier(Allocatable.TYPE,id)); + Classification classification = type.newClassification(); + classification.setValue("name",name); + resource.setClassification(classification); + return resource; + } + + public void testAllocatable() throws Exception { + LocalCache cache = new LocalCache( Locale.GERMANY ); + + DynamicTypeImpl type = createDynamicType(); + type.setReadOnly( true ); + cache.put( type ); + AllocatableImpl resource1 = createResource(1,type,"Adrian"); + cache.put(resource1); + AllocatableImpl resource2 = createResource(2,type,"Beta"); + cache.put(resource2); + AllocatableImpl resource3 = createResource(3,type,"Ceta"); + cache.put(resource3); + + resource1.getClassification().setValue("name","Zeta"); + cache.put(resource1); + Allocatable[] resources = (Allocatable[]) + cache.getCollection( Allocatable.TYPE).toArray(Allocatable.ALLOCATABLE_ARRAY); + assertEquals(3, resources.length); + assertTrue(resources[1].getName(locale).equals("Beta")); + } + + public void test2() throws Exception { + CachableStorageOperator storage = (CachableStorageOperator) + getContext().lookup(CachableStorageOperator.ROLE + "/file"); + storage.connect(); + LocalCache cache = storage.getCache(); + { + Iterator it = cache.getCollection(Period.TYPE).iterator(); + Period period = (Period) it.next(); + Collection reservations = cache.getReservations(null,period.getStart(),period.getEnd()); + assertEquals(0,reservations.size()); + period = (Period) it.next(); + reservations = cache.getReservations(null,period.getStart(),period.getEnd()); + assertEquals(2, reservations.size()); + User user = cache.getUser("homer"); + reservations = cache.getReservations(user,null,null); + assertEquals(3, reservations.size()); + reservations = cache.getReservations(user,period.getStart(),period.getEnd()); + assertEquals(2, reservations.size()); + } + { + Iterator it = cache.getIterator(Allocatable.TYPE); + assertEquals("erwin",((Allocatable)it.next()).getName(locale)); + assertEquals("Room A66",((Allocatable)it.next()).getName(locale)); + } + } +} + + + + + diff --git a/Rapla/test-src/org/rapla/storage/xml/tests/ConverterTest.java b/Rapla/test-src/org/rapla/storage/xml/tests/ConverterTest.java new file mode 100644 index 0000000..e6b6c30 --- /dev/null +++ b/Rapla/test-src/org/rapla/storage/xml/tests/ConverterTest.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------* + | Copyright (C) 2006 Christopher Kohlhaas | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License as published by the | + | Free Software Foundation. A copy of the license has been included with | + | these distribution in the COPYING file, if not go to www.fsf.org | + | | + | As a special exception, you are granted the permissions to link this | + | program with every library, which license fulfills the Open Source | + | Definition as published by the Open Source Initiative (OSI). | + *--------------------------------------------------------------------------*/ +package org.rapla.storage.xml.tests; + +import org.rapla.*; + +public class ConverterTest extends RaplaTestCase { + public ConverterTest(String name) { + super(name); + } + + protected void setUp() throws Exception { + // we do it in convert + } + + + public void testConvert() throws Exception { + super.setUp( "org/rapla/storage/xml/tests/version-0.5.xml" ); + assertEquals(2,getFacade().getAllocatables().length); + } + +} + + + + + diff --git a/Rapla/test-src/org/rapla/storage/xml/tests/version-0.5.xml b/Rapla/test-src/org/rapla/storage/xml/tests/version-0.5.xml new file mode 100644 index 0000000..ef03a05 --- /dev/null +++ b/Rapla/test-src/org/rapla/storage/xml/tests/version-0.5.xml @@ -0,0 +1,348 @@ + + + + Kategorien + categories + + Verwaltungseinheit + Administrative entity + + Paedagogik + + + MathNat + + Informatik + + Abteilung 3 + + + + + + Veranstaltungen + + Studienabschnitt + + Grundstudium + + + Hauptstudium + + + + Bereich + + Theoretisch + + + Praktisch + + + Theo/Prak + + + + + Raum + + Raumtyp + + Hörsaal + + + Seminarraum + + + + + Gebäude + Building + + Komplex + + AVZ III + + + Hauptgebäude + + + + + + + + + Raum + room + + {name} + + + Name + name + + + + Plätze + seats + + + + Sonstiges + misc. + + + + Tafel + blackboard + + + + + gehört zu + belongs to + + + + + + + + Raumtyp + + + + + + + + + + + + Dozent + lecturer + + {surname}, {forename} + + + Titel + title + + + + Nachname + surname + + + + Vorname + forename + + + + email + email + + + + + arbeitet für + works for + + + + + + + + + + + + + Vorlesungen + lecture + + {name} + + + Name + name + + + + Studiengang/Institut + + + + + + + Studienabschnitt + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + category[key='hoersaal'] + + + + + + + + + + + + + + + + + category[key='grundstudium'] + + + + + + + + + + + + + + + + + A207 + 80 + Baemer + Doppeltafel + category[key='MathNat']/category[key='Informatik']/category[key='a3'] + category[key='hoersaal'] + + + + + H A + 220 + Overheadprojektor + Doppeltafel + category[key='MathNat'] + category[key='hoersaal'] + + + + + + + + + + + + + + + + + Dipl. IDB (*) + category[key='MathNat']/category[key='Informatik']/category[key='a3'] + category[key='haupstudium'] + + + + + + + + + + + + + + + + + + Online-Algorithmen + category[key='MathNat']/category[key='Informatik']/category[key='a3'] + + + + + + + + + + + + + + + + + diff --git a/Rapla/test-src/test.xconf b/Rapla/test-src/test.xconf new file mode 100644 index 0000000..f9a1142 --- /dev/null +++ b/Rapla/test-src/test.xconf @@ -0,0 +1,109 @@ + + + + + DE + + de + en + + 8-18 + 1,7 + + + + + + + local-facade + + + + file + + + + sql + + + + xxx + + + + file + homer + duffs + + + + file + + + + sql + + + + remote + + + + remote-2 + + + + web + + + + utf-8 + test.xml + + + + + org.hsqldb.jdbcDriver + jdbc:hsqldb:${context-root}/rapla-hsqldb + sa + + true + + + + + + + + + file + sql + + + + http://localhost:8051/ + + + + http://localhost:8051/ + + + + http://localhost:8051/ + + + + + + diff --git a/Rapla/test-src/test.xlog b/Rapla/test-src/test.xlog new file mode 100644 index 0000000..bee375b --- /dev/null +++ b/Rapla/test-src/test.xlog @@ -0,0 +1,74 @@ + + + + + + + + + + + + System.out + + %7.7{priority} %1.13{time:HH:mm:ss:S} [%5.25{category}]: %{message}\n%{throwable} + + + + ${context-root}/testing.log + + %5.5{priority} %17.17{time:dd/MM HH:mm:ss:S} [%5.25{category}]: %{message}\n%{throwable} + + true + + 1m + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Rapla/test-src/testdefault.xml b/Rapla/test-src/testdefault.xml new file mode 100644 index 0000000..95f3eb9 --- /dev/null +++ b/Rapla/test-src/testdefault.xml @@ -0,0 +1,256 @@ + + + + + Abteilung + department + + channel-6 + + + elementary-springfield + + + springfield powerplant + + + test department + + + + Benutzergruppen + user-groups + + my-group + + + Ressourcen eintragen + register resources + Enregistrer des ressources + + + Einstellungen bearbeiten + modify preferences + Modifier la préférence + + + + + + + + Raum + room + + resource + {name} + + + Name + name + + + + Anzahl der Plätze + number of seats + + + + gehört zu + belongs to + + category[key='department'] + + + + + + + Dozent + lecturer + + person + {surname} {forename} + + + Titel + title + + + + Nachname + surname + + + + Vorname + forename + + + + + + + + Veranstaltung + event + + reservation + {name} + + + Name + name + + + + gehört zu + belongs to + + category[key='department'] + + + Beschreibung + description + + 5 + additional-view + + + + + + + + + + + + + + + + + + + + + + + + org.rapla.MockMailer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + erwin + 10 + category[key='elementary-springfield'] + + + + + + Room A66 + 30 + category[key='springfield-powerplant'] + + + + + + Mr. + Burns + Monty + + + + + + Simpson + Homer + + + + + + + + + + + + + + + + bowling + with all my friends + + + + + + + + + + + + + Reservation 2 + + + + + + + + + + + + power planting + new demands for energy + + + + + + + + + + + + + + diff --git a/Rapla/updatejavadocs.sh b/Rapla/updatejavadocs.sh new file mode 100644 index 0000000..914efa0 --- /dev/null +++ b/Rapla/updatejavadocs.sh @@ -0,0 +1,7 @@ +#! /bin/sh +./build.sh javadocs +cd build +tar -czf javadocs.tar.gz javadocs/ +scp javadocs.tar.gz $rapladocs +ssh $sf +#rsync --size-only -r -u -v build/javadocs website/doc/javadocs diff --git a/Rapla/webapp/WEB-INF/.cvsignore b/Rapla/webapp/WEB-INF/.cvsignore new file mode 100644 index 0000000..0ad8b78 --- /dev/null +++ b/Rapla/webapp/WEB-INF/.cvsignore @@ -0,0 +1 @@ +logs diff --git a/Rapla/webapp/WEB-INF/User.csv b/Rapla/webapp/WEB-INF/User.csv new file mode 100644 index 0000000..ac87e58 --- /dev/null +++ b/Rapla/webapp/WEB-INF/User.csv @@ -0,0 +1 @@ +RaplaUser;passw;Rapla Username;User@rapla.org;registrer diff --git a/Rapla/webapp/WEB-INF/data.xml b/Rapla/webapp/WEB-INF/data.xml new file mode 100644 index 0000000..b3e6ae6 --- /dev/null +++ b/Rapla/webapp/WEB-INF/data.xml @@ -0,0 +1,143 @@ + + + + + Benutzergruppen + user-groups + Groupes de utilisateurs + + Ressourcen eintragen + register resources + Enregistrer des ressources + + + Einstellungen bearbeiten + modify preferences + Modifier la préférence + + + Die Veranstaltungen anderer sehen + See events of other users + Voir les événements d'autres utilisateurs + + + + + + + + Standard Ressource + default resource + Ressource défaut + + resource + {name} + + + Name + name + nom + + + + + + + + Person + person + personne + + person + {surname} {forename} + + + Nachname + surname + nom de famille + + + + Vorname + forename + prénom + + + + Email + email + email + + + + + + + + Veranstaltung + event + évènement + + reservation + {name} + + + Veranstaltungsname + eventname + Nom de l'évènement + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Test + + + + + + + + + + + + + dsfdsfsfdsdf + + + + + + diff --git a/Rapla/webapp/WEB-INF/logs/rapla-client.log.000001 b/Rapla/webapp/WEB-INF/logs/rapla-client.log.000001 new file mode 100644 index 0000000..24a007d --- /dev/null +++ b/Rapla/webapp/WEB-INF/logs/rapla-client.log.000001 @@ -0,0 +1,65 @@ +INFO 20120628 20:20:20:518 [rapla]: ----------- Rapla startup mode = 1 +INFO 20120628 20:20:20:519 [rapla]: Default Locale= en_US +INFO 20120628 20:20:20:519 [rapla]: Configured Locale= en_US +INFO 20120628 20:20:20:543 [rapla]: Config=file:/E:/2012Fall/Prjs/Rapla2/Rapla/webapp/WEB-INF/rapla.xconf +INFO 20120628 20:20:20:543 [rapla]: Config=file:/E:/2012Fall/Prjs/Rapla2/Rapla/webapp/WEB-INF/raplaclient.xlog +INFO 20120628 20:20:20:543 [rapla]: Rapla.Version=1.4.3-beta +INFO 20120628 20:20:20:543 [rapla]: Rapla.Build=2011-10-05 13:41 +INFO 20120628 20:20:20:543 [rapla]: Java.Version=1.7.0_04 +INFO 20120628 20:20:20:882 [rapla]: Data:file:/E:/2012Fall/Prjs/Rapla2/Rapla/webapp/WEB-INF/data.xml +INFO 20120628 20:20:20:947 [rapla.facade]: Welcome admin +INFO 20120628 20:20:20:967 [rapla]: Export2iCal plugin added +INFO 20120628 20:20:20:968 [rapla]: Initialize org.rapla.plugin.export2ical.Export2iCalPlugin +INFO 20120628 20:21:52:377 [rapla]: Shutting down rapla-container +INFO 20120628 20:22:40:194 [rapla]: ----------- Rapla startup mode = 1 +INFO 20120628 20:22:40:195 [rapla]: Default Locale= en_US +INFO 20120628 20:22:40:195 [rapla]: Configured Locale= en_US +INFO 20120628 20:22:40:211 [rapla]: Config=file:/E:/2012Fall/Prjs/Rapla2/Rapla/webapp/WEB-INF/rapla.xconf +INFO 20120628 20:22:40:211 [rapla]: Config=file:/E:/2012Fall/Prjs/Rapla2/Rapla/webapp/WEB-INF/raplaclient.xlog +INFO 20120628 20:22:40:211 [rapla]: Rapla.Version=1.4.3-beta +INFO 20120628 20:22:40:211 [rapla]: Rapla.Build=2011-10-05 13:41 +INFO 20120628 20:22:40:211 [rapla]: Java.Version=1.7.0_04 +INFO 20120628 20:22:40:431 [rapla]: Data:file:/E:/2012Fall/Prjs/Rapla2/Rapla/webapp/WEB-INF/data.xml +INFO 20120628 20:22:40:478 [rapla.facade]: Welcome admin +INFO 20120628 20:22:40:492 [rapla]: Export2iCal plugin added +INFO 20120628 20:22:40:492 [rapla]: Initialize org.rapla.plugin.export2ical.Export2iCalPlugin +INFO 20120628 20:22:53:301 [rapla]: Shutting down rapla-container +INFO 20120628 20:24:54:726 [rapla]: ----------- Rapla startup mode = 1 +INFO 20120628 20:24:54:727 [rapla]: Default Locale= en_US +INFO 20120628 20:24:54:727 [rapla]: Configured Locale= en_US +INFO 20120628 20:24:54:742 [rapla]: Config=file:/E:/2012Fall/Prjs/Rapla2/Rapla/webapp/WEB-INF/rapla.xconf +INFO 20120628 20:24:54:742 [rapla]: Config=file:/E:/2012Fall/Prjs/Rapla2/Rapla/webapp/WEB-INF/raplaclient.xlog +INFO 20120628 20:24:54:742 [rapla]: Rapla.Version=1.4.3-beta +INFO 20120628 20:24:54:743 [rapla]: Rapla.Build=2011-10-05 13:41 +INFO 20120628 20:24:54:743 [rapla]: Java.Version=1.7.0_04 +INFO 20120628 20:24:54:977 [rapla]: Data:file:/E:/2012Fall/Prjs/Rapla2/Rapla/webapp/WEB-INF/data.xml +INFO 20120628 20:24:55:14 [rapla.facade]: Welcome admin +INFO 20120628 20:24:55:25 [rapla]: Export2iCal plugin added +INFO 20120628 20:24:55:25 [rapla]: Initialize org.rapla.plugin.export2ical.Export2iCalPlugin +INFO 20120628 20:26:14:467 [rapla]: Shutting down rapla-container +INFO 20120628 20:31:16:518 [rapla]: ----------- Rapla startup mode = 1 +INFO 20120628 20:31:16:521 [rapla]: Default Locale= en_US +INFO 20120628 20:31:16:521 [rapla]: Configured Locale= en_US +INFO 20120628 20:31:16:538 [rapla]: Config=file:/E:/2012Fall/Prjs/Rapla2/Rapla/webapp/WEB-INF/rapla.xconf +INFO 20120628 20:31:16:539 [rapla]: Config=file:/E:/2012Fall/Prjs/Rapla2/Rapla/webapp/WEB-INF/raplaclient.xlog +INFO 20120628 20:31:16:539 [rapla]: Rapla.Version=1.4.3-beta +INFO 20120628 20:31:16:539 [rapla]: Rapla.Build=2011-10-05 13:41 +INFO 20120628 20:31:16:539 [rapla]: Java.Version=1.7.0_04 +INFO 20120628 20:31:16:805 [rapla]: Data:file:/E:/2012Fall/Prjs/Rapla2/Rapla/webapp/WEB-INF/data.xml +INFO 20120628 20:31:16:845 [rapla.facade]: Welcome admin +INFO 20120628 20:31:16:856 [rapla]: Export2iCal plugin added +INFO 20120628 20:31:16:856 [rapla]: Initialize org.rapla.plugin.export2ical.Export2iCalPlugin +INFO 20120628 20:31:27:239 [rapla]: Shutting down rapla-container +INFO 20120821 16:56:18:12 [rapla]: ----------- Rapla startup mode = 1 +INFO 20120821 16:56:18:12 [rapla]: Default Locale= en_US +INFO 20120821 16:56:18:13 [rapla]: Configured Locale= en_US +INFO 20120821 16:56:18:40 [rapla]: Config=file:/C:/Users/tms08012/git/Project3again/Rapla/webapp/WEB-INF/rapla.xconf +INFO 20120821 16:56:18:40 [rapla]: Config=file:/C:/Users/tms08012/git/Project3again/Rapla/webapp/WEB-INF/raplaclient.xlog +INFO 20120821 16:56:18:41 [rapla]: Rapla.Version=1.4.3-beta +INFO 20120821 16:56:18:41 [rapla]: Rapla.Build=2011-10-05 13:41 +INFO 20120821 16:56:18:41 [rapla]: Java.Version=1.7.0_04 +INFO 20120821 16:56:18:381 [rapla]: Data:file:/C:/Users/tms08012/git/Project3again/Rapla/webapp/WEB-INF/data.xml +INFO 20120821 16:56:18:421 [rapla.facade]: Welcome admin +INFO 20120821 16:56:18:434 [rapla]: Export2iCal plugin added +INFO 20120821 16:56:18:434 [rapla]: Initialize org.rapla.plugin.export2ical.Export2iCalPlugin +INFO 20120821 16:56:30:288 [rapla]: Shutting down rapla-container diff --git a/Rapla/webapp/WEB-INF/rapla-access.sql b/Rapla/webapp/WEB-INF/rapla-access.sql new file mode 100644 index 0000000..aaa476a --- /dev/null +++ b/Rapla/webapp/WEB-INF/rapla-access.sql @@ -0,0 +1,16 @@ +-- drop TABLE EVENT_ATTRIBUTE_VALUE; +CREATE TABLE ALLOCATION ( APPOINTMENT_ID INTEGER NOT NULL, RESOURCE_ID INTEGER NOT NULL, OPTIONAL INTEGER ); +CREATE TABLE APPOINTMENT ( ID INTEGER NOT NULL, EVENT_ID INTEGER NOT NULL , APPOINTMENT_START DATETIME NOT NULL , APPOINTMENT_END DATETIME NOT NULL , REPETITION_TYPE VARCHAR(15), REPETITION_NUMBER INTEGER ,REPETITION_END DATETIME , REPETITION_INTERVAL INTEGER , PRIMARY KEY (ID)); +CREATE TABLE APPOINTMENT_EXCEPTION ( APPOINTMENT_ID INTEGER NOT NULL , EXCEPTION_DATE DATETIME NOT NULL ); +CREATE TABLE CATEGORY ( ID INTEGER NOT NULL , PARENT_ID INTEGER , CATEGORY_KEY VARCHAR(50) NOT NULL , LABEL VARCHAR(250) , DEFINITION LONGTEXT, PARENT_ORDER INTEGER,PRIMARY KEY (ID)); +CREATE TABLE DYNAMIC_TYPE ( ID INTEGER NOT NULL , TYPE_KEY VARCHAR(20) NOT NULL , DEFINITION LONGTEXT NOT NULL, PRIMARY KEY (ID)); +CREATE TABLE EVENT ( ID INTEGER NOT NULL , TYPE_KEY VARCHAR(20) NOT NULL , OWNER_ID INTEGER NOT NULL , CREATION_TIME DATETIME, LAST_CHANGED DATETIME ,LAST_CHANGED_BY INTEGER, PRIMARY KEY (ID)); +CREATE TABLE EVENT_ATTRIBUTE_VALUE ( EVENT_ID INTEGER NOT NULL , ATTRIBUTE_KEY VARCHAR(20) NOT NULL , ATTRIBUTE_VALUE LONGTEXT); +CREATE TABLE PERIOD ( ID INTEGER NOT NULL , NAME VARCHAR(255) NOT NULL , PERIOD_START DATETIME NOT NULL , PERIOD_END DATETIME NOT NULL , PRIMARY KEY (ID)); +CREATE TABLE PERMISSION ( RESOURCE_ID INTEGER NOT NULL , USER_ID INTEGER , GROUP_ID INTEGER , ACCESS_LEVEL INTEGER NOT NULL , MIN_ADVANCE INTEGER , MAX_ADVANCE INTEGER , START_DATE DATETIME , END_DATE DATETIME ); +CREATE TABLE PREFERENCE ( USER_ID INTEGER, ROLE VARCHAR(200) NOT NULL , STRING_VALUE LONGTEXT, XML_VALUE LONGTEXT); +CREATE TABLE RAPLA_USER ( ID INTEGER NOT NULL , USERNAME VARCHAR(30) NOT NULL , PASSWORD VARCHAR(130) , NAME VARCHAR(200) NOT NULL , EMAIL VARCHAR(150) NOT NULL , ISADMIN INTEGER NOT NULL , PRIMARY KEY (ID)); +CREATE TABLE RAPLA_USER_GROUP ( USER_ID INTEGER NOT NULL, CATEGORY_ID INTEGER NOT NULL ); +CREATE TABLE RAPLA_RESOURCE ( ID INTEGER NOT NULL , TYPE_KEY VARCHAR(100) NOT NULL, IGNORE_CONFLICTS INTEGER NOT NULL, OWNER_ID INTEGER, CREATION_TIME DATETIME, LAST_CHANGED DATETIME ,LAST_CHANGED_BY INTEGER, PRIMARY KEY (ID)); +CREATE TABLE RESOURCE_GROUP ( ID INTEGER NOT NULL , GROUP_ID INTEGER NOT NULL); +CREATE TABLE RESOURCE_ATTRIBUTE_VALUE ( RESOURCE_ID INTEGER NOT NULL , ATTRIBUTE_KEY VARCHAR(25), ATTRIBUTE_VALUE LONGTEXT); diff --git a/Rapla/webapp/WEB-INF/rapla-hsqldb.properties b/Rapla/webapp/WEB-INF/rapla-hsqldb.properties new file mode 100644 index 0000000..e2d1cd9 --- /dev/null +++ b/Rapla/webapp/WEB-INF/rapla-hsqldb.properties @@ -0,0 +1,14 @@ +#HSQL database +#Sun Dec 14 05:42:44 CET 2003 +hsqldb.script_format=0 +runtime.gc_interval=0 +sql.enforce_strict_size=false +readonly=false +hsqldb.first_identity=0 +hsqldb.cache_file_scale=1 +sql.compare_in_locale=false +sql.month=true +hsqldb.log_size=8 +modified=no +sql.enforce_size=false + diff --git a/Rapla/webapp/WEB-INF/rapla-hsqldb.script b/Rapla/webapp/WEB-INF/rapla-hsqldb.script new file mode 100644 index 0000000..c989445 --- /dev/null +++ b/Rapla/webapp/WEB-INF/rapla-hsqldb.script @@ -0,0 +1,17 @@ +CREATE TABLE ALLOCATION ( APPOINTMENT_ID INTEGER NOT NULL, RESOURCE_ID INTEGER NOT NULL, OPTIONAL INTEGER ); +CREATE TABLE APPOINTMENT ( ID INTEGER NOT NULL, EVENT_ID INTEGER NOT NULL , APPOINTMENT_START DATETIME NOT NULL , APPOINTMENT_END DATETIME NOT NULL , REPETITION_TYPE VARCHAR(15), REPETITION_NUMBER INTEGER ,REPETITION_END DATETIME , REPETITION_INTERVAL INTEGER , PRIMARY KEY (ID)); +CREATE TABLE APPOINTMENT_EXCEPTION ( APPOINTMENT_ID INTEGER NOT NULL , EXCEPTION_DATE DATETIME NOT NULL ); +CREATE TABLE CATEGORY ( ID INTEGER NOT NULL , PARENT_ID INTEGER , CATEGORY_KEY VARCHAR(50) NOT NULL , LABEL VARCHAR(250) , DEFINITION VARCHAR, PARENT_ORDER INTEGER, PRIMARY KEY (ID)); +CREATE TABLE DYNAMIC_TYPE ( ID INTEGER NOT NULL , TYPE_KEY VARCHAR(20) NOT NULL , DEFINITION VARCHAR NOT NULL, PRIMARY KEY (ID)); +CREATE TABLE EVENT ( ID INTEGER NOT NULL , TYPE_KEY VARCHAR(20) NOT NULL , OWNER_ID INTEGER NOT NULL , CREATION_TIME DATETIME , LAST_CHANGED DATETIME, LAST_CHANGED_BY INTEGER, PRIMARY KEY (ID)); +CREATE TABLE EVENT_ATTRIBUTE_VALUE ( EVENT_ID INTEGER NOT NULL , ATTRIBUTE_KEY VARCHAR(20) NOT NULL , VALUE VARCHAR); +CREATE TABLE PERIOD ( ID INTEGER NOT NULL , NAME VARCHAR(255) NOT NULL , PERIOD_START DATETIME NOT NULL , PERIOD_END DATETIME NOT NULL , PRIMARY KEY (ID)); +CREATE TABLE PERMISSION ( RESOURCE_ID INTEGER NOT NULL , USER_ID INTEGER , GROUP_ID INTEGER , ACCESS_LEVEL INTEGER NOT NULL , MIN_ADVANCE INTEGER , MAX_ADVANCE INTEGER , START_DATE DATETIME , END_DATE DATETIME ); +CREATE TABLE PREFERENCE ( USER_ID INTEGER, ROLE VARCHAR(200) NOT NULL , STRING_VALUE VARCHAR, XML_VALUE VARCHAR); +CREATE TABLE RAPLA_USER ( ID INTEGER NOT NULL , USERNAME VARCHAR(30) NOT NULL , PASSWORD VARCHAR(130) , NAME VARCHAR(200) NOT NULL , EMAIL VARCHAR(150) NOT NULL , ISADMIN INTEGER NOT NULL , PRIMARY KEY (ID)); +CREATE TABLE RAPLA_USER_GROUP ( USER_ID INTEGER NOT NULL, CATEGORY_ID INTEGER NOT NULL ); +CREATE TABLE RAPLA_RESOURCE ( ID INTEGER NOT NULL , TYPE_KEY VARCHAR(100) NOT NULL, IGNORE_CONFLICTS INTEGER NOT NULL, OWNER_ID INTEGER, CREATION_TIME DATETIME, LAST_CHANGED DATETIME , LAST_CHANGED_BY INTEGER, PRIMARY KEY (ID) ); +CREATE TABLE RESOURCE_GROUP ( ID INTEGER NOT NULL , GROUP_ID INTEGER NOT NULL); +CREATE TABLE RESOURCE_ATTRIBUTE_VALUE ( RESOURCE_ID INTEGER NOT NULL , ATTRIBUTE_KEY VARCHAR(25), VALUE VARCHAR); + +CREATE USER SA PASSWORD "" ADMIN diff --git a/Rapla/webapp/WEB-INF/rapla-mysql.sql b/Rapla/webapp/WEB-INF/rapla-mysql.sql new file mode 100644 index 0000000..bac54cb --- /dev/null +++ b/Rapla/webapp/WEB-INF/rapla-mysql.sql @@ -0,0 +1,214 @@ +-- Mysql -h -uroot -p < thisscript + +CREATE SCHEMA IF NOT EXISTS `RAPLA_DB` DEFAULT CHARACTER SET latin1; +USE `RAPLA_DB`; + +-- ----------------------------------------------------- +-- Table `RAPLA_DB`.`ALLOCATION` +-- ----------------------------------------------------- +DROP TABLE IF EXISTS `RAPLA_DB`.`ALLOCATION` ; + +CREATE TABLE IF NOT EXISTS `RAPLA_DB`.`ALLOCATION` ( + `APPOINTMENT_ID` INT NOT NULL , + `RESOURCE_ID` INT NOT NULL, + `OPTIONAL` INT + ); + +CREATE INDEX `INDEX_1` ON `RAPLA_DB`.`ALLOCATION` (`APPOINTMENT_ID` ASC) ; + + +-- ----------------------------------------------------- +-- Table `RAPLA_DB`.`APPOINTMENT` +-- ----------------------------------------------------- +DROP TABLE IF EXISTS `RAPLA_DB`.`APPOINTMENT` ; + +CREATE TABLE IF NOT EXISTS `RAPLA_DB`.`APPOINTMENT` ( + `ID` INT NOT NULL + ,`EVENT_ID` INT NOT NULL + ,`APPOINTMENT_START` DATETIME NOT NULL + ,`APPOINTMENT_END` DATETIME NOT NULL + ,`REPETITION_TYPE` VARCHAR(15) NULL DEFAULT NULL + ,`REPETITION_NUMBER` INT NULL DEFAULT NULL + ,`REPETITION_END` DATETIME NULL DEFAULT NULL + ,`REPETITION_INTERVAL` INT NULL DEFAULT NULL + ,PRIMARY KEY (`ID`) ); + + +-- ----------------------------------------------------- +-- Table `RAPLA_DB`.`APPOINTMENT_EXCEPTION` +-- ----------------------------------------------------- +DROP TABLE IF EXISTS `RAPLA_DB`.`APPOINTMENT_EXCEPTION` ; + +CREATE TABLE IF NOT EXISTS `RAPLA_DB`.`APPOINTMENT_EXCEPTION` ( + `APPOINTMENT_ID` INT NOT NULL , + `EXCEPTION_DATE` DATETIME NOT NULL ); + +CREATE INDEX `Index_1` ON `RAPLA_DB`.`APPOINTMENT_EXCEPTION` (`APPOINTMENT_ID` ASC) ; + + +-- ----------------------------------------------------- +-- Table `RAPLA_DB`.`CATEGORY` +-- ----------------------------------------------------- +DROP TABLE IF EXISTS `RAPLA_DB`.`CATEGORY` ; + +CREATE TABLE IF NOT EXISTS `RAPLA_DB`.`CATEGORY` ( + `ID` INT NOT NULL , + `PARENT_ID` INT NULL DEFAULT NULL , + `CATEGORY_KEY` VARCHAR(50) NOT NULL , + `LABEL` VARCHAR(250) NULL DEFAULT NULL , + `DEFINITION` TEXT NULL DEFAULT NULL , + `PARENT_ORDER` INT NULL DEFAULT NULL , + PRIMARY KEY (`ID`) ); + +CREATE INDEX `INDEX_2` ON `RAPLA_DB`.`CATEGORY` (`PARENT_ID` ASC) ; + + +-- ----------------------------------------------------- +-- Table `RAPLA_DB`.`DYNAMIC_TYPE` +-- ----------------------------------------------------- +DROP TABLE IF EXISTS `RAPLA_DB`.`DYNAMIC_TYPE` ; + +CREATE TABLE IF NOT EXISTS `RAPLA_DB`.`DYNAMIC_TYPE` ( + `ID` INT NOT NULL , + `TYPE_KEY` VARCHAR(50) NOT NULL , + `DEFINITION` TEXT NOT NULL , + PRIMARY KEY (`ID`) ); + + +-- ----------------------------------------------------- +-- Table `RAPLA_DB`.`EVENT` +-- ----------------------------------------------------- +DROP TABLE IF EXISTS `RAPLA_DB`.`EVENT` ; + +CREATE TABLE IF NOT EXISTS `RAPLA_DB`.`EVENT` ( + `ID` INT NOT NULL , + `TYPE_KEY` VARCHAR(50) NOT NULL , + `OWNER_ID` INT NOT NULL , + `CREATION_TIME` DATETIME , + `LAST_CHANGED` DATETIME , + `LAST_CHANGED_BY` INT NULL DEFAULT NULL , + PRIMARY KEY (`ID`) ); + + +-- ----------------------------------------------------- +-- Table `RAPLA_DB`.`EVENT_ATTRIBUTE_VALUE` +-- ----------------------------------------------------- +DROP TABLE IF EXISTS `RAPLA_DB`.`EVENT_ATTRIBUTE_VALUE` ; + +CREATE TABLE IF NOT EXISTS `RAPLA_DB`.`EVENT_ATTRIBUTE_VALUE` ( + `EVENT_ID` INT NOT NULL , + `ATTRIBUTE_KEY` VARCHAR(20) NOT NULL , + `VALUE` VARCHAR(1000) NULL DEFAULT NULL ); + +CREATE INDEX `INDEX_1` ON `RAPLA_DB`.`EVENT_ATTRIBUTE_VALUE` (`EVENT_ID` ASC) ; + + +-- ----------------------------------------------------- +-- Table `RAPLA_DB`.`PERIOD` +-- ----------------------------------------------------- +DROP TABLE IF EXISTS `RAPLA_DB`.`PERIOD` ; + +CREATE TABLE IF NOT EXISTS `RAPLA_DB`.`PERIOD` ( + `ID` INT NOT NULL , + `NAME` VARCHAR(255) NOT NULL , + `PERIOD_START` DATETIME NOT NULL , + `PERIOD_END` DATETIME NOT NULL , + PRIMARY KEY (`ID`) ); + + +-- ----------------------------------------------------- +-- Table `RAPLA_DB`.`PERMISSION` +-- ----------------------------------------------------- +DROP TABLE IF EXISTS `RAPLA_DB`.`PERMISSION` ; + +CREATE TABLE IF NOT EXISTS `RAPLA_DB`.`PERMISSION` ( + `RESOURCE_ID` INT NOT NULL , + `USER_ID` INT NULL DEFAULT NULL , + `GROUP_ID` INT NULL DEFAULT NULL , + `ACCESS_LEVEL` INT NOT NULL , + `MIN_ADVANCE` INT NULL DEFAULT NULL , + `MAX_ADVANCE` INT NULL DEFAULT NULL , + `START_DATE` DATETIME NULL DEFAULT NULL , + `END_DATE` DATETIME NULL DEFAULT NULL ); + +CREATE INDEX `INDEX_1` ON `RAPLA_DB`.`PERMISSION` (`RESOURCE_ID` ASC) ; + + +-- ----------------------------------------------------- +-- Table `RAPLA_DB`.`PREFERENCE` +-- ----------------------------------------------------- +DROP TABLE IF EXISTS `RAPLA_DB`.`PREFERENCE` ; + +CREATE TABLE IF NOT EXISTS `RAPLA_DB`.`PREFERENCE` ( + `USER_ID` INT NULL DEFAULT NULL , + `ROLE` VARCHAR(200) NOT NULL , + `STRING_VALUE` VARCHAR(1000) NULL DEFAULT NULL , + `XML_VALUE` MEDIUMTEXT NULL DEFAULT NULL ); + +CREATE INDEX `INDEX_1` ON `RAPLA_DB`.`PREFERENCE` (`USER_ID` ASC) ; + + +-- ----------------------------------------------------- +-- Table `RAPLA_DB`.`RAPLA_USER` +-- ----------------------------------------------------- +DROP TABLE IF EXISTS `RAPLA_DB`.`RAPLA_USER` ; + +CREATE TABLE IF NOT EXISTS `RAPLA_DB`.`RAPLA_USER` ( + `ID` INT NOT NULL , + `USERNAME` VARCHAR(30) NOT NULL , + `PASSWORD` VARCHAR(130) NULL DEFAULT NULL , + `NAME` VARCHAR(200) NOT NULL , + `EMAIL` VARCHAR(150) NOT NULL , + `ISADMIN` INT NOT NULL , + PRIMARY KEY (`ID`) ); + + +-- ----------------------------------------------------- +-- Table `RAPLA_DB`.`RAPLA_USER_GROUP` +-- ----------------------------------------------------- +DROP TABLE IF EXISTS `RAPLA_DB`.`RAPLA_USER_GROUP` ; + +CREATE TABLE IF NOT EXISTS `RAPLA_DB`.`RAPLA_USER_GROUP` ( + `USER_ID` INT NOT NULL , + `CATEGORY_ID` INT NOT NULL ); + + +-- ----------------------------------------------------- +-- Table `RAPLA_DB`.`RAPLA_RESOURCE` +-- ----------------------------------------------------- +DROP TABLE IF EXISTS `RAPLA_DB`.`RAPLA_RESOURCE` ; + +CREATE TABLE IF NOT EXISTS `RAPLA_DB`.`RAPLA_RESOURCE` ( + `ID` INT NOT NULL , + `TYPE_KEY` VARCHAR(100) NOT NULL , + `IGNORE_CONFLICTS` INT NOT NULL , + `OWNER_ID` INT, + `CREATION_TIME` DATETIME , + `LAST_CHANGED` DATETIME, + `LAST_CHANGED_BY` INT NULL DEFAULT NULL , + PRIMARY KEY (`ID`) ); + + +-- ----------------------------------------------------- +-- Table `RAPLA_DB`.`RESOURCE_GROUP` +-- ----------------------------------------------------- +DROP TABLE IF EXISTS `RAPLA_DB`.`RESOURCE_GROUP` ; + +CREATE TABLE IF NOT EXISTS `RAPLA_DB`.`RESOURCE_GROUP` ( + `ID` INT NOT NULL , + `GROUP_ID` INT NOT NULL ); + + +-- ----------------------------------------------------- +-- Table `RAPLA_DB`.`RESOURCE_ATTRIBUTE_VALUE` +-- ----------------------------------------------------- +DROP TABLE IF EXISTS `RAPLA_DB`.`RESOURCE_ATTRIBUTE_VALUE` ; + +CREATE TABLE IF NOT EXISTS `RAPLA_DB`.`RESOURCE_ATTRIBUTE_VALUE` ( + `RESOURCE_ID` INT NOT NULL , + `ATTRIBUTE_KEY` VARCHAR(25) NULL DEFAULT NULL , + `VALUE` VARCHAR(1000) NULL DEFAULT NULL ); + +DROP USER 'rapla'@'%'; +CREATE USER 'rapla'@'%' IDENTIFIED BY 'raplapw'; +GRANT ALL PRIVILEGES ON `RAPLA_DB`.* TO 'rapla'@'%'; \ No newline at end of file diff --git a/Rapla/webapp/WEB-INF/rapla-oracle.sql b/Rapla/webapp/WEB-INF/rapla-oracle.sql new file mode 100644 index 0000000..689d076 --- /dev/null +++ b/Rapla/webapp/WEB-INF/rapla-oracle.sql @@ -0,0 +1,222 @@ + +ALTER TABLE RAPLA_RESOURCE DROP PRIMARY KEY CASCADE; +DROP TABLE RAPLA_RESOURCE CASCADE CONSTRAINTS; + +CREATE TABLE RAPLA_RESOURCE +( + ID INTEGER NOT NULL, + TYPE_KEY VARCHAR2(100 BYTE) NOT NULL, + IGNORE_CONFLICTS INTEGER NOT NULL, + OWNER_ID INTEGER , + CREATION_TIME TIMESTAMP(6) , + LAST_CHANGED TIMESTAMP(6) , + LAST_CHANGED_BY INTEGER +) ; + + + +DROP TABLE ALLOCATION CASCADE CONSTRAINTS; + +CREATE TABLE ALLOCATION +( + APPOINTMENT_ID INTEGER NOT NULL, + RESOURCE_ID INTEGER NOT NULL, + OPTIONAL INTEGER + +) ; + + +ALTER TABLE APPOINTMENT DROP PRIMARY KEY CASCADE; +DROP TABLE APPOINTMENT CASCADE CONSTRAINTS; + +CREATE TABLE APPOINTMENT +( + ID INTEGER NOT NULL, + EVENT_ID INTEGER NOT NULL, + APPOINTMENT_START TIMESTAMP(6) NOT NULL, + APPOINTMENT_END TIMESTAMP(6) NOT NULL, + REPETITION_TYPE VARCHAR2(15 BYTE), + REPETITION_NUMBER INTEGER, + REPETITION_END TIMESTAMP(6), + REPETITION_INTERVAL INTEGER +) ; + + +DROP TABLE APPOINTMENT_EXCEPTION CASCADE CONSTRAINTS; + +CREATE TABLE APPOINTMENT_EXCEPTION +( + APPOINTMENT_ID INTEGER NOT NULL, + EXCEPTION_DATE TIMESTAMP(6) NOT NULL +) ; + + +ALTER TABLE CATEGORY DROP PRIMARY KEY CASCADE; +DROP TABLE CATEGORY CASCADE CONSTRAINTS; + +CREATE TABLE CATEGORY +( + ID INTEGER NOT NULL, + PARENT_ID INTEGER, + CATEGORY_KEY VARCHAR2(50 BYTE) NOT NULL, + LABEL VARCHAR2(200 BYTE), + DEFINITION CLOB, + PARENT_ORDER INTEGER +) ; + + +ALTER TABLE DYNAMIC_TYPE DROP PRIMARY KEY CASCADE; +DROP TABLE DYNAMIC_TYPE CASCADE CONSTRAINTS; + +CREATE TABLE DYNAMIC_TYPE +( + ID INTEGER NOT NULL, + TYPE_KEY VARCHAR2(20 BYTE) NOT NULL, + DEFINITION CLOB NOT NULL +) ; + + +ALTER TABLE EVENT DROP PRIMARY KEY CASCADE; +DROP TABLE EVENT CASCADE CONSTRAINTS; + +CREATE TABLE EVENT +( + ID INTEGER NOT NULL, + TYPE_KEY VARCHAR2(20 BYTE) NOT NULL, + OWNER_ID INTEGER NOT NULL, + CREATION_TIME TIMESTAMP(6) , + LAST_CHANGED TIMESTAMP(6) , + LAST_CHANGED_BY INTEGER +) ; + + +DROP TABLE EVENT_ATTRIBUTE_VALUE CASCADE CONSTRAINTS; + +CREATE TABLE EVENT_ATTRIBUTE_VALUE +( + EVENT_ID INTEGER NOT NULL, + ATTRIBUTE_KEY VARCHAR2(20 BYTE) NOT NULL, + VALUE VARCHAR2(500 BYTE) +) ; + + +ALTER TABLE PERIOD DROP PRIMARY KEY CASCADE; +DROP TABLE PERIOD CASCADE CONSTRAINTS; + +CREATE TABLE PERIOD +( + ID INTEGER NOT NULL, + NAME VARCHAR2(255 BYTE) NOT NULL, + PERIOD_START TIMESTAMP(6) NOT NULL, + PERIOD_END TIMESTAMP(6) NOT NULL +) ; + + +DROP TABLE PERMISSION CASCADE CONSTRAINTS; + +CREATE TABLE PERMISSION +( + RESOURCE_ID INTEGER NOT NULL, + USER_ID INTEGER, + GROUP_ID INTEGER, + ACCESS_LEVEL INTEGER NOT NULL, + MIN_ADVANCE INTEGER, + MAX_ADVANCE INTEGER, + START_DATE TIMESTAMP(6), + END_DATE TIMESTAMP(6) +) ; + + +ALTER TABLE RAPLA_USER DROP PRIMARY KEY CASCADE; +DROP TABLE RAPLA_USER CASCADE CONSTRAINTS; + +CREATE TABLE RAPLA_USER +( + ID INTEGER NOT NULL, + USERNAME VARCHAR2(30 BYTE) NOT NULL, + PASSWORD VARCHAR2(130 BYTE), + NAME VARCHAR2(100 BYTE), + EMAIL VARCHAR2(150 BYTE), + ISADMIN INTEGER +) ; + + +DROP TABLE RAPLA_USER_GROUP CASCADE CONSTRAINTS; + +CREATE TABLE RAPLA_USER_GROUP +( + USER_ID INTEGER NOT NULL, + CATEGORY_ID INTEGER NOT NULL +) ; + + +DROP TABLE RESOURCE_ATTRIBUTE_VALUE CASCADE CONSTRAINTS; + +CREATE TABLE RESOURCE_ATTRIBUTE_VALUE +( + RESOURCE_ID INTEGER NOT NULL, + ATTRIBUTE_KEY VARCHAR2(25 BYTE), + VALUE VARCHAR2(200 BYTE) +) ; + + +DROP TABLE RESOURCE_GROUP CASCADE CONSTRAINTS; + +CREATE TABLE RESOURCE_GROUP +( + ID INTEGER NOT NULL, + GROUP_ID INTEGER NOT NULL +) ; + + +DROP TABLE PREF CASCADE CONSTRAINTS; + +CREATE TABLE PREF +( + USER_ID INTEGER, + ROLE VARCHAR2(200 BYTE) NOT NULL, + STRING_VALUE VARCHAR2(2000 BYTE), + XML_VALUE VARCHAR2(2000 BYTE) +) ; + + +DROP TABLE PREFERENCE CASCADE CONSTRAINTS; + +CREATE TABLE PREFERENCE +( + USER_ID INTEGER, + ROLE VARCHAR2(200 BYTE) NOT NULL, + STRING_VALUE VARCHAR2(2000 BYTE), + XML_VALUE CLOB +) ; + + +ALTER TABLE RAPLA_RESOURCE ADD ( + PRIMARY KEY (ID)); + + +ALTER TABLE APPOINTMENT ADD ( + PRIMARY KEY (ID)); + + +ALTER TABLE CATEGORY ADD ( + PRIMARY KEY (ID)); + + +ALTER TABLE DYNAMIC_TYPE ADD ( + PRIMARY KEY (ID)); + + +ALTER TABLE EVENT ADD ( + PRIMARY KEY (ID)); + + +ALTER TABLE PERIOD ADD ( + PRIMARY KEY (ID)); + + +ALTER TABLE RAPLA_USER ADD ( + PRIMARY KEY (ID)); + + + diff --git a/Rapla/webapp/WEB-INF/rapla-postgres.sql b/Rapla/webapp/WEB-INF/rapla-postgres.sql new file mode 100644 index 0000000..e624f02 --- /dev/null +++ b/Rapla/webapp/WEB-INF/rapla-postgres.sql @@ -0,0 +1,15 @@ +CREATE TABLE ALLOCATION ( APPOINTMENT_ID INTEGER NOT NULL, RESOURCE_ID INTEGER NOT NULL, OPTIONAL INTEGER ); +CREATE TABLE APPOINTMENT ( ID INTEGER NOT NULL, EVENT_ID INTEGER NOT NULL , APPOINTMENT_START TIMESTAMP NOT NULL , APPOINTMENT_END TIMESTAMP NOT NULL , REPETITION_TYPE VARCHAR(15), REPETITION_NUMBER INTEGER ,REPETITION_END TIMESTAMP , REPETITION_INTERVAL INTEGER , PRIMARY KEY (ID)); +CREATE TABLE APPOINTMENT_EXCEPTION ( APPOINTMENT_ID INTEGER NOT NULL , EXCEPTION_DATE TIMESTAMP NOT NULL ); +CREATE TABLE CATEGORY ( ID INTEGER NOT NULL , PARENT_ID INTEGER , CATEGORY_KEY VARCHAR(50) NOT NULL , LABEL VARCHAR(255) , DEFINITION TEXT, PARENT_ORDER INTEGER, PRIMARY KEY (ID)); +CREATE TABLE DYNAMIC_TYPE ( ID INTEGER NOT NULL , TYPE_KEY VARCHAR(20) NOT NULL , DEFINITION TEXT NOT NULL, PRIMARY KEY (ID)); +CREATE TABLE EVENT ( ID INTEGER NOT NULL , TYPE_KEY VARCHAR(20) NOT NULL , OWNER_ID INTEGER NOT NULL , CREATION_TIME TIMESTAMP , LAST_CHANGED TIMESTAMP, LAST_CHANGED_BY INTEGER, PRIMARY KEY (ID)); +CREATE TABLE EVENT_ATTRIBUTE_VALUE ( EVENT_ID INTEGER NOT NULL , ATTRIBUTE_KEY VARCHAR(20) NOT NULL , VALUE VARCHAR(1000)); +CREATE TABLE PERIOD ( ID INTEGER NOT NULL , NAME VARCHAR(255) NOT NULL , PERIOD_START TIMESTAMP NOT NULL , PERIOD_END TIMESTAMP NOT NULL , PRIMARY KEY (ID)); +CREATE TABLE PERMISSION ( RESOURCE_ID INTEGER NOT NULL , USER_ID INTEGER , GROUP_ID INTEGER , ACCESS_LEVEL INTEGER NOT NULL , MIN_ADVANCE INTEGER , MAX_ADVANCE INTEGER , START_DATE TIMESTAMP , END_DATE TIMESTAMP ); +CREATE TABLE PREFERENCE ( USER_ID INTEGER, ROLE VARCHAR(200) NOT NULL , STRING_VALUE VARCHAR(1000), XML_VALUE TEXT); +CREATE TABLE RAPLA_USER ( ID INTEGER NOT NULL , USERNAME VARCHAR(30) NOT NULL , PASSWORD VARCHAR(130) , NAME VARCHAR(200) NOT NULL , EMAIL VARCHAR(150) NOT NULL , ISADMIN INTEGER NOT NULL , PRIMARY KEY (ID)); +CREATE TABLE RAPLA_USER_GROUP ( USER_ID INTEGER NOT NULL, CATEGORY_ID INTEGER NOT NULL ); +CREATE TABLE RAPLA_RESOURCE ( ID INTEGER NOT NULL , TYPE_KEY VARCHAR(100) NOT NULL, IGNORE_CONFLICTS INTEGER NOT NULL,OWNER_ID INTEGER , CREATION_TIME TIMESTAMP , LAST_CHANGED TIMESTAMP, LAST_CHANGED_BY INTEGER, PRIMARY KEY (ID)); +CREATE TABLE RESOURCE_GROUP ( ID INTEGER NOT NULL , GROUP_ID INTEGER NOT NULL); +CREATE TABLE RESOURCE_ATTRIBUTE_VALUE ( RESOURCE_ID INTEGER NOT NULL , ATTRIBUTE_KEY VARCHAR(25), VALUE VARCHAR(1000)); diff --git a/Rapla/webapp/WEB-INF/rapla.xconf b/Rapla/webapp/WEB-INF/rapla.xconf new file mode 100644 index 0000000..42a8884 --- /dev/null +++ b/Rapla/webapp/WEB-INF/rapla.xconf @@ -0,0 +1,74 @@ + + + + + + + + de + en + fr + es + zh + cs + nl + pl + + + + + + + + admin + + + + file + + + + + + + utf-8 + + data.xml + no + no + + + + + + + + + + + + db_user + your_pwd + + + diff --git a/Rapla/webapp/WEB-INF/raplaclient.xlog b/Rapla/webapp/WEB-INF/raplaclient.xlog new file mode 100644 index 0000000..3241ce4 --- /dev/null +++ b/Rapla/webapp/WEB-INF/raplaclient.xlog @@ -0,0 +1,39 @@ + + + + + + + + + + + System.out + + %5.5{priority} %21.21{time:yyyyMMdd HH:mm:ss:S} [%5.25{category}]: %{message}\n%{throwable} + + + + + + ${context-root}/logs/rapla-client.log + + %5.5{priority} %21.21{time:yyyyMMdd HH:mm:ss:S} [%5.25{category}]: %{message}\n%{throwable} + + true + + 1m + + + + + + + + + + + + diff --git a/Rapla/webapp/WEB-INF/raplaserver.xconf b/Rapla/webapp/WEB-INF/raplaserver.xconf new file mode 100644 index 0000000..31d626e --- /dev/null +++ b/Rapla/webapp/WEB-INF/raplaserver.xconf @@ -0,0 +1,78 @@ + + + + + + + + de + en + fr + es + zh + cs + nl + pl + + + + + utf-8 + + + + + + file + + + + + utf-8 + + data.xml + no + no + + + + + + + + + + + + db_user + your_pwd + + + + + file + sql + + + + diff --git a/Rapla/webapp/WEB-INF/raplaserver.xlog b/Rapla/webapp/WEB-INF/raplaserver.xlog new file mode 100644 index 0000000..3325f05 --- /dev/null +++ b/Rapla/webapp/WEB-INF/raplaserver.xlog @@ -0,0 +1,74 @@ + + + + + + + + + + + + ${context-root}/logs/rapla-server.log + + %5.5{priority} %21.21{time:yyyyMMdd HH:mm:ss:S} [%5.25{category}]: %{message}\n%{throwable} + + true + + 1m + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Rapla/webapp/WEB-INF/simpsons-data.xml b/Rapla/webapp/WEB-INF/simpsons-data.xml new file mode 100644 index 0000000..432eea7 --- /dev/null +++ b/Rapla/webapp/WEB-INF/simpsons-data.xml @@ -0,0 +1,630 @@ + + + + + Verwaltungseinheit + Administrative Entity + + channel-6 + + + elementary-springfield + + + springfield powerplant + + Sicherheitabteilung + securitydepartment + + + Abfallbeseitigung + waste disposal + + + + + Ressourcen + resources + + Fahrzeugtypen + vehicle-types + + Bus + bus + + + PKW + car + + + + Raumtypen + room-types + + Klassenraum + classroom + + + Studio + studio + + + Bar + bar + + + Konferenzraum + conference room + + + + + Vorlesung + lecture + + Schwierigkeitsgrade + difficulty levels + + viel zu einfach + too simple + + + viel zu schwierig + too hard + + + zu langweilig + too boring + + + + + Benutzergruppen + user-groups + + Ressourcen eintragen + register resources + Enregistrer des ressources + + + Einstellungen bearbeiten + modify preferences + Modifier la préférence + + + + + + + + Fahrzeug + vehicle + + resource + {name} + + + Name + name + + + + PS + hp + + + + Anzahl Plätze + number of seats + + + + + gehört zu + belongs to + + category[key='department'] + + + + Fahrzeugtyp + type of vehicle + + category[key='resources']/category[key='vehicle-types'] + + + + + + + Raum + room + + resource + {name} + + + Name + name + + + + Tafel + blackboard + + + + Anzahl der Plätze + number of seats + + + + Raumtyp + type of room + + category[key='resources']/category[key='room-types'] + + + + gehört zu + belongs to + + category[key='department'] + + + + + + + + Dozent + lecturer + + person + {surname} {forename} + + + Titel + title + + + + Nachname + surname + + + + Vorname + forename + + + + email + email + + + + + arbeitet für + works for + + category[key='department'] + + + + + + + + Meeting + meeting + + reservation + {name} + + + Name + name + + + + Gäste erlaubt + guests permitted + + + + + + + + Vorlesung + lecture + + reservation + {name} + + + Name + name + + + + Schwierigkeitsgrad + difficulty level + + category[key='lecture']/category[key='level'] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + room A66 + true + 30 + category[key='conferenceroom'] + category[key='springfield-powerplant']/category[key='security-department'] + + + + + + classroom 3a + true + 24 + category[key='classroom'] + category[key='elementary-springfield'] + + + + + + studio 1 + false + 150 + category[key='studio'] + category[key='channel-6'] + + + + + + Ottos bus + 250 + 50 + category[key='elementary-springfield'] + category[key='bus'] + + + + + + Moes bar + false + 50 + category[key='bar'] + + + + + + Principal + Skinner + Seymour + scinner@elementary-springfield.edu + category[key='elementary-springfield'] + + + + + + Simpson + Homer + simpson@security.power-plant.sim + category[key='springfield-powerplant'] + + + + + + Mr. + Burns + Monty + chief@power-plant.sim + category[key='springfield-powerplant'] + + + + + + Gumble + Barney + gumble@duff.beer + + + + + + Miss + Bouvier + Selma + selma@simgles.com + + + + + + Brockman + Kent + kent@channel6.com + category[key='channel-6'] + + + + + + Krustofski + Herschel + krusty@channel6.com + category[key='channel-6'] + + + + + + Simpson + Abraham + abe@retirement-castle.old + + + + + + Simpson + Lisa + lisa@mensa.org + + + + + + Mann + Otto + otto@elementary-springfield.edu + category[key='elementary-springfield'] + + + + + + Szyslak + Moe + lisa@moes-tavern.com + + + + + + + + + + + + + + + Duffs seminar + category[key='too-easy'] + + + + + + + + + + + + + + drinking at Moes + true + + + + + + + + + + + + + + lesson for veterans + category[key='too-boring'] + + + + + + + + + + + + + + + + + storys about the past + category[key='too-boring'] + + + + + + + + + + + + + brewing techniques + category[key='too-hard'] + + + + + + + + + + + + + + + + Itchy and Scratchy + true + + + + + + + + + + + + + bowling + false + + + + + + + + + + + + + jam session + true + + + + + + + + + enviromental protection + category[key='too-boring'] + + + + + + + + + + + + + + power planting + category[key='too-easy'] + + + + + + + + + + + + + Han Tan Bantan + category[key='too-boring'] + + + + + + + + + + + + + diff --git a/Rapla/webapp/WEB-INF/web.xml b/Rapla/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..43297de --- /dev/null +++ b/Rapla/webapp/WEB-INF/web.xml @@ -0,0 +1,80 @@ + + + + + + + + RaplaServer + RaplaServer + Rapla + + org.rapla.MainServlet + + + config-file + raplaserver.xconf + + + + log-config-file + raplaserver.xlog + + 3 + + + + RaplaServer + /rapla/* + + + + RaplaServer + /rapla + + + + + RaplaServer + /server + + + + RaplaServer + /index.jsp + + + + RaplaServer + /raplaclient.jnlp + + + + RaplaServer + /raplaapplet.jsp + + + + RaplaServer + /calendar.jsp + + + + RaplaServer + /report.jsp + + + + RaplaServer + /export/* + + + + + + redirect.html + + + diff --git a/Rapla/webapp/default.css b/Rapla/webapp/default.css new file mode 100644 index 0000000..d74ce31 --- /dev/null +++ b/Rapla/webapp/default.css @@ -0,0 +1,58 @@ +/* Use this file to customize the Rapla HTML Pages to your style */ +body { + font:12px/18px "Lucida Grande","Lucida Sans Unicode",Arial,Verdana,sans-serif; + color: #333333; +} + +/* Button Appearance */ + +.button { + border:none; + cursor:pointer; + display:inline-block; + overflow:visible; + padding:0; + margin:0; + height:29px; + background:url("images/button.gif") right top; +} + +.button a, .button input { + font-size: 13px; + font-weight: bold; + font-family: Arial,Helvetica,Helvetica Neue,Verdana,sans-serif; + text-decoration:none; + color: #FFFFFF !important; + cursor:pointer; + outline:none; + overflow:visible; + white-space:nowrap; + + border:none; + display:inline-block; + padding: 0 18px; + margin:0 !important; + + line-height:29px; + height:29px; + width:auto; + + background:url("images/button.gif") no-repeat left top; +} + +/* Browser fixes */ +.button input::-moz-focus-inner { + padding: 0; + border: none; +} + +.menuEntry +{ + padding-top:5px; + padding-bottom:5px; +} +/* You can also customize the calendar +.month_table { + background-color: #FFFFAA; +} +*/ \ No newline at end of file diff --git a/Rapla/webapp/images/button.gif b/Rapla/webapp/images/button.gif new file mode 100644 index 0000000..06295bf Binary files /dev/null and b/Rapla/webapp/images/button.gif differ diff --git a/Rapla/webapp/images/favicon.ico b/Rapla/webapp/images/favicon.ico new file mode 100644 index 0000000..0c6439d Binary files /dev/null and b/Rapla/webapp/images/favicon.ico differ diff --git a/Rapla/webapp/redirect.html b/Rapla/webapp/redirect.html new file mode 100644 index 0000000..4f53bc7 --- /dev/null +++ b/Rapla/webapp/redirect.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Rapla/webapp/webclient/logo.gif b/Rapla/webapp/webclient/logo.gif new file mode 100644 index 0000000..0032e22 Binary files /dev/null and b/Rapla/webapp/webclient/logo.gif differ diff --git a/Rapla/webapp/webclient/raplaclient.xconf b/Rapla/webapp/webclient/raplaclient.xconf new file mode 100644 index 0000000..132d0de --- /dev/null +++ b/Rapla/webapp/webclient/raplaclient.xconf @@ -0,0 +1,35 @@ + + + + + + + + de + en + fr + es + zh + cs + nl + + + + + + + + + remote + + + + + ${download-url} + + + diff --git a/Rapla/webapp/webclient/raplaclient.xlog b/Rapla/webapp/webclient/raplaclient.xlog new file mode 100644 index 0000000..7659ef0 --- /dev/null +++ b/Rapla/webapp/webclient/raplaclient.xlog @@ -0,0 +1,39 @@ + + + + + + + + + + + System.out + + %5.5{priority} %21.21{time:yyyyMMdd HH:mm:ss:S} [%5.25{category}]: %{message}\n%{throwable} + + + + + + + + + + + + +