Index: applications/editors/josm/plugins/OpeningHoursEditor/.classpath
===================================================================
--- applications/editors/josm/plugins/OpeningHoursEditor/.classpath	(revision 22751)
+++ applications/editors/josm/plugins/OpeningHoursEditor/.classpath	(revision 22751)
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/JOSM"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
Index: applications/editors/josm/plugins/OpeningHoursEditor/.project
===================================================================
--- applications/editors/josm/plugins/OpeningHoursEditor/.project	(revision 22751)
+++ applications/editors/josm/plugins/OpeningHoursEditor/.project	(revision 22751)
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>OpeningHoursEditor</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
Index: applications/editors/josm/plugins/OpeningHoursEditor/LICENSE.txt
===================================================================
--- applications/editors/josm/plugins/OpeningHoursEditor/LICENSE.txt	(revision 22751)
+++ applications/editors/josm/plugins/OpeningHoursEditor/LICENSE.txt	(revision 22751)
@@ -0,0 +1,345 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+	51 Franklin St, 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
+
+
+	    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.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 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) year  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.
+
+  <signature of Ty Coon>, 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.
Index: applications/editors/josm/plugins/OpeningHoursEditor/README.txt
===================================================================
--- applications/editors/josm/plugins/OpeningHoursEditor/README.txt	(revision 22751)
+++ applications/editors/josm/plugins/OpeningHoursEditor/README.txt	(revision 22751)
@@ -0,0 +1,9 @@
+README 
+======
+
+for a complete Description see http://wiki.openstreetmap.org/wiki/JOSM/Plugins/OpeningHoursEditor
+
+The Parser was generated with javacc.
+There is also a nice javacc-Plugin for Eclipse see http://eclipse-javacc.sourceforge.net/
+
+For questions or reports send me an email: f.thomale@googlemail.com
Index: applications/editors/josm/plugins/OpeningHoursEditor/build.xml
===================================================================
--- applications/editors/josm/plugins/OpeningHoursEditor/build.xml	(revision 22751)
+++ applications/editors/josm/plugins/OpeningHoursEditor/build.xml	(revision 22751)
@@ -0,0 +1,277 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+** This is a template build file for a JOSM  plugin.
+**
+** Maintaining versions
+** ====================
+** see README.template
+**
+** Usage
+** =====
+** To build it run
+**
+**    > ant  dist
+**
+** To install the generated plugin locally (in you default plugin directory) run
+**
+**    > ant  install
+**
+** The generated plugin jar is not automatically available in JOSMs plugin configuration
+** dialog. You have to check it in first.
+**
+** Use the ant target 'publish' to check in the plugin and make it available to other
+** JOSM users:
+**    set the properties commit.message and plugin.main.version
+** and run
+**    > ant  publish
+**
+**
+-->
+<project name="OpeningHoursEditor" default="dist" basedir=".">
+
+	<!-- enter the SVN commit message -->
+	<property name="commit.message" value="Commit message" />
+	<!-- enter the *lowest* JOSM version this plugin is currently compatible with -->
+	<property name="plugin.main.version" value="2830" />
+
+
+	<!--
+      ************************************************
+      ** should not be necessary to change the following properties
+     -->
+	<property name="josm" location="../../core/dist/josm-custom.jar" />
+	<property name="plugin.dist.dir" value="../../dist" />
+	<property name="plugin.build.dir" value="build" />
+	<property name="plugin.src.dir" value="src" />
+	<property name="ant.build.javac.target" value="1.5" />
+	<property name="plugin.jar" value="${plugin.dist.dir}/${ant.project.name}.jar" />
+
+	<!--
+    **********************************************************
+    ** javacc-otc - compiles OpeningTimeCompiler
+    **
+	** can be changed see http://www.engr.mun.ca/~theo/JavaCC-FAQ/javacc-faq-moz.htm#tth_sEc2.5
+    **********************************************************
+    -->
+	<target name="javacc-otc">
+		<echo message="compiling OpeningTimeCompiler with javacc ... " />
+		<exec executable="javacc" dir="src/org/openstreetmap/josm/plugins/ohe/parser/">
+			<arg value="OpeningTimeCompiler.jj" />
+		</exec>
+	</target>
+
+	<!--
+    **********************************************************
+    ** init - initializes the build
+    **********************************************************
+    -->
+	<target name="init">
+		<mkdir dir="${plugin.build.dir}" />
+	</target>
+
+	<!--
+    **********************************************************
+    ** compile - complies the source tree
+    **********************************************************
+    -->
+	<target name="compile" depends="init">
+		<echo message="compiling sources for  ${plugin.jar} ... " />
+		<javac srcdir="src" classpath="${josm}" debug="true" destdir="${plugin.build.dir}">
+			<compilerarg value="-Xlint:deprecation" />
+			<compilerarg value="-Xlint:unchecked" />
+		</javac>
+	</target>
+
+	<!--
+    **********************************************************
+    ** dist - creates the plugin jar
+    **********************************************************
+    -->
+	<target name="dist" depends="compile,revision">
+		<echo message="creating ${ant.project.name}.jar ... " />
+		<copy todir="${plugin.build.dir}/images">
+			<fileset dir="images" />
+		</copy>
+		<copy todir="${plugin.build.dir}">
+			<fileset dir=".">
+				<include name="README" />
+				<include name="LICENSE" />
+			</fileset>
+		</copy>
+		<jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
+			<!--
+        ************************************************
+        ** configure these properties. Most of them will be copied to the plugins
+        ** manifest file. Property values will also show up in the list available
+        ** plugins: http://josm.openstreetmap.de/wiki/Plugins.
+        **
+        ************************************************
+    -->
+			<manifest>
+				<attribute name="Author" value="Falko Thomale" />
+				<attribute name="Plugin-Class" value="org.openstreetmap.josm.plugins.ohe.OhePlugin" />
+				<attribute name="Plugin-Date" value="${version.entry.commit.date}" />
+				<attribute name="Plugin-Description" value="extended options for editing opening_hours" />
+				<attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/wiki/JOSM/Plugins/OpeningHoursEditor" />
+				<attribute name="Plugin-Mainversion" value="${plugin.main.version}" />
+				<attribute name="Plugin-Version" value="${version.entry.commit.revision}" />
+			</manifest>
+		</jar>
+	</target>
+
+	<!--
+    **********************************************************
+    ** revision - extracts the current revision number for the
+    **    file build.number and stores it in the XML property
+    **    version.*
+    **********************************************************
+    -->
+	<target name="test" depends="dist">
+	</target>
+
+	<!--
+    **********************************************************
+    ** revision - extracts the current revision number for the
+    **    file build.number and stores it in the XML property
+    **    version.*
+    **********************************************************
+    -->
+	<target name="revision">
+		<!--
+		<exec append="false" output="REVISION" executable="svn" failifexecutionfails="false">
+			<env key="LANG" value="C"/>
+			<arg value="info"/>
+			<arg value="-
+-xml"/>
+			<arg value="."/>
+		</exec>
+		<xmlproperty file="REVISION" prefix="version" keepRoot="false" collapseAttributes="true"/>
+		<delete file="REVISION"/>
+-->
+	</target>
+
+	<!--
+    **********************************************************
+    ** clean - clean up the build environment
+    **********************************************************
+    -->
+	<target name="clean">
+		<delete dir="${plugin.build.dir}" />
+		<delete file="${plugin.jar}" />
+	</target>
+
+	<!--
+    **********************************************************
+    ** install - install the plugin in your local JOSM installation
+    **********************************************************
+    -->
+	<target name="install" depends="dist">
+		<property environment="env" />
+		<condition property="josm.plugins.dir" value="${env.APPDATA}/JOSM/plugins" else="${user.home}/.josm/plugins">
+			<and>
+				<os family="windows" />
+			</and>
+		</condition>
+		<copy file="${plugin.jar}" todir="${josm.plugins.dir}" />
+	</target>
+
+
+	<!--
+	************************** Publishing the plugin *********************************** 
+	-->
+	<!--
+		** extracts the JOSM release for the JOSM version in ../core and saves it in the 
+		** property ${coreversion.info.entry.revision}
+		**
+		-->
+	<target name="core-info">
+		<exec append="false" output="core.info.xml" executable="svn" failifexecutionfails="false">
+			<env key="LANG" value="C" />
+			<arg value="info" />
+			<arg value="--xml" />
+			<arg value="../../core" />
+		</exec>
+		<xmlproperty file="core.info.xml" prefix="coreversion" keepRoot="true" collapseAttributes="true" />
+		<echo>Building against core revision ${coreversion.info.entry.revision}.</echo>
+		<echo>Plugin-Mainversion is set to ${plugin.main.version}.</echo>
+		<delete file="core.info.xml" />
+	</target>
+
+	<!--
+		** commits the source tree for this plugin
+		-->
+	<target name="commit-current">
+		<echo>Commiting the plugin source with message '${commit.message}' ...</echo>
+		<exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
+			<env key="LANG" value="C" />
+			<arg value="commit" />
+			<arg value="-m '${commit.message}'" />
+			<arg value="." />
+		</exec>
+	</target>
+
+	<!--
+		** updates (svn up) the source tree for this plugin
+		-->
+	<target name="update-current">
+		<echo>Updating plugin source ...</echo>
+		<exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
+			<env key="LANG" value="C" />
+			<arg value="up" />
+			<arg value="." />
+		</exec>
+		<echo>Updating ${plugin.jar} ...</echo>
+		<exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
+			<env key="LANG" value="C" />
+			<arg value="up" />
+			<arg value="../dist/${plugin.jar}" />
+		</exec>
+	</target>
+
+	<!--
+		** commits the plugin.jar 
+		-->
+	<target name="commit-dist">
+		<echo>
+	***** Properties of published ${plugin.jar} *****
+	Commit message    : '${commit.message}'					
+	Plugin-Mainversion: ${plugin.main.version}
+	JOSM build version: ${coreversion.info.entry.revision}
+	Plugin-Version    : ${version.entry.commit.revision}
+	***** / Properties of published ${plugin.jar} *****					
+						
+	Now commiting ${plugin.jar} ...
+	</echo>
+		<exec append="true" output="svn.log" executable="svn" failifexecutionfails="false">
+			<env key="LANG" value="C" />
+			<arg value="-m '${commit.message}'" />
+			<arg value="commit" />
+			<arg value="${plugin.jar}" />
+		</exec>
+	</target>
+
+	<!-- ** make sure svn is present as a command line tool ** -->
+	<target name="ensure-svn-present">
+		<exec append="true" output="svn.log" executable="svn" failifexecutionfails="false" failonerror="false" resultproperty="svn.exit.code">
+			<env key="LANG" value="C" />
+			<arg value="--version" />
+		</exec>
+		<fail message="Fatal: command 'svn --version' failed. Please make sure svn is installed on your system.">
+			<!-- return code not set at all? Most likely svn isn't installed -->
+			<condition>
+				<not>
+					<isset property="svn.exit.code" />
+				</not>
+			</condition>
+		</fail>
+		<fail message="Fatal: command 'svn --version' failed. Please make sure a working copy of svn is installed on your system.">
+			<!-- error code from SVN? Most likely svn is not what we are looking on this system -->
+			<condition>
+				<isfailure code="${svn.exit.code}" />
+			</condition>
+		</fail>
+	</target>
+
+	<target name="publish" depends="ensure-svn-present,core-info,commit-current,update-current,clean,dist,commit-dist">
+	</target>
+</project>
Index: applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/OhePlugin.java
===================================================================
--- applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/OhePlugin.java	(revision 22751)
+++ applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/OhePlugin.java	(revision 22751)
@@ -0,0 +1,277 @@
+package org.openstreetmap.josm.plugins.ohe;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Vector;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JDialog;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.ListSelectionModel;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.command.ChangePropertyCommand;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.command.SequenceCommand;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.plugins.Plugin;
+import org.openstreetmap.josm.plugins.PluginInformation;
+import org.openstreetmap.josm.plugins.ohe.gui.OheDialogPanel;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.Shortcut;
+
+public class OhePlugin extends Plugin {
+
+	// Strings for choosing which key of an object with given tags should be
+	// edited
+	// the order is referencing the preference of the keys
+	// String[] -> {key, value, to-editing-key} key and value can contain regexp
+	private final String[][] TAG_EDIT_STRINGS = new String[][] {
+			{ "opening_hours", ".*", "opening_hours" },
+			{ "collection_times", ".*", "collection_times" },
+			{ "collection_times:local", ".*", "collection_times:local" },
+			{ "lit", ".*", "lit" },
+			{ "amenity", "post_box", "collection_times" },
+			{ "amenity", ".*", "opening_hours" },
+			{ "shop", ".*", "opening_hours" }, { "highway", ".*", "lit" } };
+
+	/**
+	 * Will be invoked by JOSM to bootstrap the plugin
+	 * 
+	 * @param info
+	 *            information about the plugin and its local installation
+	 */
+	public OhePlugin(PluginInformation info) {
+		super(info);
+		Main.main.menu.toolsMenu.add(new OheMenuAction());
+	}
+
+	class OheMenuAction extends JosmAction {
+		public OheMenuAction() {
+			super(
+					tr("edit opening_hours"),
+					"opening_hours.png",
+					tr("edit time-tag of selcted Element in a graphical interface"),
+					Shortcut.registerShortcut("tools:opening_hourseditor", tr(
+							"Tool: {0}", tr("edit opening_hours")),
+							KeyEvent.VK_T, Shortcut.GROUP_MENU), false);
+		}
+
+		@Override
+		protected void updateEnabledState() {
+			if (getCurrentDataSet() == null) {
+				setEnabled(false);
+			} else {
+				updateEnabledState(getCurrentDataSet().getSelected());
+			}
+		}
+
+		@Override
+		protected void updateEnabledState(
+				Collection<? extends OsmPrimitive> selection) {
+			setEnabled(selection != null && !selection.isEmpty());
+		}
+
+		public void actionPerformed(ActionEvent evt) {
+			// fetch active Layer
+			OsmDataLayer osmlayer = Main.main.getEditLayer();
+			if (osmlayer != null) {
+				Collection<OsmPrimitive> selection = osmlayer.data
+						.getSelected();
+				if (selection.size() == 1) { // one object selected
+					OsmPrimitive object = selection.iterator().next();
+					String[] keyValuePair = editTimeTags(object.getKeys());
+					if (keyValuePair != null) {
+						String key = keyValuePair[0].trim();
+						String newkey = keyValuePair[1].trim();
+						String value = keyValuePair[2].trim();
+
+						if (value.equals("")) {
+							value = null; // delete the key
+						}
+						if (newkey.equals("")) {
+							newkey = key;
+							value = null; // delete the key instead
+						}
+						if (key.equals(newkey)
+								&& tr("<different>").equals(value))
+							return;
+						if (key.equals(newkey) || value == null) {
+							Main.main.undoRedo.add(new ChangePropertyCommand(
+									object, newkey, value));
+						} else {
+							Collection<Command> commands = new Vector<Command>();
+							commands.add(new ChangePropertyCommand(object, key,
+									null));
+							commands.add(new ChangePropertyCommand(object,
+									newkey, value));
+							Main.main.undoRedo.add(new SequenceCommand(
+									tr("Change properties of 1 object"),
+									commands));
+						}
+					}
+				} else { // Not possible to edit 0, 2 or more objects
+					JOptionPane
+							.showMessageDialog(
+									Main.parent,
+									tr(
+											"You have {0} Elements selected. But you can edit only one element!",
+											selection.size()),
+									"openingHoursEditor Warning",
+									JOptionPane.ERROR_MESSAGE);
+				}
+			}
+		}
+	}
+
+	// opens up dialogs to change one of the key-value-pairs and returns the
+	// changed pair
+	private String[] editTimeTags(Map<String, String> keyValueMap) {
+		String selectedKey = "";
+
+		if ((selectedKey = tagChooseDialog(keyValueMap)) == null)
+			return null;
+
+		final String value = (keyValueMap.containsKey(selectedKey)) ? keyValueMap
+				.get(selectedKey)
+				: "";
+		OheDialogPanel panel = new OheDialogPanel(this, selectedKey, value);
+
+		final JOptionPane optionPane = new JOptionPane(panel,
+				JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION);
+		final JDialog dlg = optionPane.createDialog(Main.parent, tr("Edit"));
+
+		dlg.setResizable(true);
+		dlg.setVisible(true);
+
+		Object answer = optionPane.getValue();
+		if (!(answer == null || answer == JOptionPane.UNINITIALIZED_VALUE || (answer instanceof Integer && (Integer) answer != JOptionPane.OK_OPTION)))
+			return panel.getChangedKeyValuePair();
+
+		return null;
+	}
+
+	// opens a dialog for choosing from a set of tags which can be edited
+	// the chosen one is returned
+	private String tagChooseDialog(Map<String, String> keyValueMap) {
+		String preSelectedKey = getPreSelectedKey(keyValueMap);
+		int preSelectedRow = -1;
+
+		String[][] rowData = new String[keyValueMap.size()][2];
+		int cnt = 0;
+		for (Object key : keyValueMap.keySet().toArray()) {
+			rowData[cnt][0] = key.toString();
+			rowData[cnt][1] = keyValueMap.get(key);
+			if (key.toString().equals(preSelectedKey))
+				preSelectedRow = cnt;
+			cnt++;
+		}
+
+		final JTable table = new JTable(rowData,
+				new String[] { "key", "value" }) {
+			public boolean isCellEditable(int rowIndex, int colIndex) {
+				return false; // Disallow the editing of any cell
+			}
+		};
+		table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+		JScrollPane sp = new JScrollPane(
+				JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
+				JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+		sp.setViewportView(table);
+
+		final JTextField tf = new JTextField();
+
+		ActionListener al = new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				if (e.getActionCommand().equals("edit")) {
+					table.setEnabled(true);
+					tf.setEnabled(false);
+				} else if (e.getActionCommand().equals("new")) {
+					table.setEnabled(false);
+					tf.setEnabled(true);
+				}
+			}
+		};
+
+		JRadioButton editButton = new JRadioButton("edit existing tag");
+		editButton.setActionCommand("edit");
+		editButton.addActionListener(al);
+		JRadioButton newButton = new JRadioButton("edit new tag");
+		newButton.setActionCommand("new");
+		newButton.addActionListener(al);
+		ButtonGroup group = new ButtonGroup();
+		group.add(newButton);
+		group.add(editButton);
+
+		if (preSelectedRow != -1) {
+			table.setEnabled(true);
+			tf.setEnabled(false);
+			table.setRowSelectionInterval(preSelectedRow, preSelectedRow);
+			editButton.setSelected(true);
+		} else {
+			table.setEnabled(false);
+			tf.setEnabled(true);
+			tf.setText(preSelectedKey);
+			newButton.setSelected(true);
+		}
+
+		JPanel dlgPanel = new JPanel(new GridBagLayout());
+		dlgPanel.add(editButton, GBC.std().anchor(GBC.CENTER));
+		dlgPanel.add(sp, GBC.eol().fill(GBC.BOTH));
+		dlgPanel.add(newButton, GBC.std().anchor(GBC.CENTER));
+		dlgPanel.add(tf, GBC.eol().fill(GBC.HORIZONTAL));
+
+		JOptionPane optionPane = new JOptionPane(dlgPanel,
+				JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION);
+		JDialog dlg = optionPane.createDialog(Main.parent, tr("Choose key"));
+
+		dlg.pack();
+		dlg.setResizable(true);
+		dlg.setVisible(true);
+
+		Object answer = optionPane.getValue();
+		if (answer != null
+				&& answer != JOptionPane.UNINITIALIZED_VALUE
+				&& (answer instanceof Integer && (Integer) answer == JOptionPane.OK_OPTION))
+			if (editButton.isSelected() && table.getSelectedRow() != -1)
+				return rowData[table.getSelectedRow()][0];
+			else if (newButton.isSelected())
+				return tf.getText();
+
+		return null;
+	}
+
+	private String getPreSelectedKey(Map<String, String> keyValueMap) {
+		for (String[] pattern : TAG_EDIT_STRINGS) {
+			Pattern keyPattern = Pattern.compile(pattern[0]);
+			Pattern valuePattern = Pattern.compile(pattern[1]);
+			for (Object key : keyValueMap.keySet().toArray()) {
+				Matcher keyMatcher = keyPattern.matcher(key.toString());
+				if (keyMatcher.matches()) {
+					Matcher valueMatcher = valuePattern.matcher(keyValueMap
+							.get(key));
+					if (valueMatcher.matches()) {
+						return pattern[2];
+					}
+				}
+			}
+		}
+		return "";
+	}
+}
Index: applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/OpeningTimeUtils.java
===================================================================
--- applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/OpeningTimeUtils.java	(revision 22751)
+++ applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/OpeningTimeUtils.java	(revision 22751)
@@ -0,0 +1,224 @@
+package org.openstreetmap.josm.plugins.ohe;
+
+import java.util.ArrayList;
+
+import org.openstreetmap.josm.plugins.ohe.gui.TimeRect;
+import org.openstreetmap.josm.plugins.ohe.parser.OpeningTimeCompiler;
+
+public class OpeningTimeUtils {
+	// implements the subtraction of daytimes in spans of days when a day in
+	// the list occurs direct afterwards
+	public static ArrayList<int[]> convert(ArrayList<DateTime> dateTimes) {
+		ArrayList<int[]> ret = new ArrayList<int[]>(); // the list which is
+		// returned
+		for (int i = 0; i < dateTimes.size(); ++i) { // iterate over every entry
+			DateTime dateTime = dateTimes.get(i);
+			ArrayList<DateTime> newDateTimes = new ArrayList<DateTime>();
+
+			// test if the given entry is a single dayspan
+			if (dateTime.daySpans.size() == 1
+					&& dateTime.daySpans.get(0).isSpan()) {
+				ArrayList<DaySpan> partDaySpans = new ArrayList<DaySpan>();
+				int start_day = dateTime.daySpans.get(0).startDay;
+
+				// look in every entry behind
+				while (i + 1 < dateTimes.size()) {
+					ArrayList<DaySpan> following = dateTimes.get(i + 1).daySpans;
+					if (following.size() == 1
+							&& following.get(0).startDay > dateTime.daySpans
+									.get(0).startDay
+							&& following.get(0).endDay < dateTime.daySpans
+									.get(0).endDay) {
+						partDaySpans.add(new DaySpan(start_day, following
+								.get(0).startDay - 1));
+						start_day = following.get(0).endDay + 1;
+						newDateTimes.add(dateTimes.get(i + 1));
+						i++;
+					} else
+						break;
+				}
+
+				partDaySpans.add(new DaySpan(start_day, dateTime.daySpans
+						.get(0).endDay));
+				newDateTimes.add(new DateTime(partDaySpans,
+						dateTime.daytimeSpans));
+			}
+			if (newDateTimes.isEmpty())
+				newDateTimes.add(dateTime);
+
+			// create the int-array
+			for (int j = 0; j < newDateTimes.size(); ++j) {
+				DateTime dateTime2 = newDateTimes.get(j);
+				for (DaySpan dayspan : dateTime2.daySpans) {
+					for (DaytimeSpan timespan : dateTime2.daytimeSpans) {
+						if (!timespan.isOff())
+							ret.add(new int[] { dayspan.startDay,
+									dayspan.endDay, timespan.startMinute,
+									timespan.endMinute });
+					}
+				}
+			}
+		}
+		return ret;
+	}
+
+	public static class DaySpan {
+		public int startDay;
+		public int endDay;
+
+		public DaySpan(int startDay, int endDay) {
+			this.startDay = startDay;
+			this.endDay = endDay;
+		}
+
+		public boolean isSpan() {
+			return endDay > startDay;
+		}
+
+		public boolean isSingleDay() {
+			return startDay == endDay;
+		}
+	}
+
+	public static class DaytimeSpan {
+		public int startMinute;
+		public int endMinute;
+
+		public DaytimeSpan(int startMinute, int endMinute) {
+			this.startMinute = startMinute;
+			this.endMinute = endMinute;
+		}
+
+		public boolean isOff() {
+			return startMinute == -1;
+		}
+
+		public boolean isSpan() {
+			return endMinute > startMinute;
+		}
+	}
+
+	public static class DateTime {
+		public ArrayList<DaySpan> daySpans;
+		public ArrayList<DaytimeSpan> daytimeSpans;
+
+		public DateTime(ArrayList<DaySpan> daySpans,
+				ArrayList<DaytimeSpan> daytimeSpans) {
+			this.daySpans = daySpans;
+			this.daytimeSpans = daytimeSpans;
+		}
+	}
+
+	// returns a String (e.g "Mo-Sa 10:00-20:00; Tu off") representing the
+	// TimeRects
+	public static String makeStringFromRects(ArrayList<TimeRect> givenTimeRects) {
+		// create an array of booleans representing every minute on all the days
+		// in a week
+		boolean[][] minuteArray = new boolean[7][24 * 60 + 2];
+		for (int day = 0; day < 7; ++day)
+			for (int minute = 0; minute < 24 * 60 + 2; ++minute)
+				minuteArray[day][minute] = false;
+		for (TimeRect timeRect : givenTimeRects)
+			for (int day = timeRect.getDayStart(); day <= timeRect.getDayEnd(); ++day)
+				for (int minute = timeRect.getMinuteStart(); minute <= timeRect
+						.getMinuteEnd(); ++minute)
+					minuteArray[day][minute] = true;
+
+		String ret = "";
+		int[] days = new int[7]; // an array representing the status of the days
+		// 0 means nothing done with this day yet
+		// 8 means the day is off
+		// 0<x<8 means the day have the openinghours of day x
+		// -8<x<0 means nothing done with this day yet, but it intersects a
+		// range of days with same opening_hours
+		for (int i = 0; i < 7; ++i) {
+			String add = "";
+
+			if (isArrayEmpty(minuteArray[i]) && days[i] == 0) {
+				days[i] = 8;
+			} else if (isArrayEmpty(minuteArray[i]) && days[i] < 0) {
+				add = OpeningTimeCompiler.WEEKDAYS[i] + " off";
+				days[i] = -8;
+			} else if (days[i] <= 0) {
+				days[i] = i + 1;
+				int lastSameDay = i;
+				int sameDayCount = 1;
+				for (int j = i + 1; j < 7; ++j) {
+					if (arraysEqual(minuteArray[i], minuteArray[j])) {
+						days[j] = i + 1;
+						lastSameDay = j;
+						sameDayCount++;
+					}
+				}
+				if (sameDayCount == 1) {
+					// a single Day with this special opening_hours
+					add = OpeningTimeCompiler.WEEKDAYS[i] + " "
+							+ makeStringFromMinuteArray(minuteArray[i]);
+				} else if (sameDayCount == 2) {
+					// exactly two Days with this special opening_hours
+					add = OpeningTimeCompiler.WEEKDAYS[i] + ","
+							+ OpeningTimeCompiler.WEEKDAYS[lastSameDay] + " "
+							+ makeStringFromMinuteArray(minuteArray[i]);
+				} else if (sameDayCount > 2) {
+					// more than two Days with this special opening_hours
+					add = OpeningTimeCompiler.WEEKDAYS[i] + "-"
+							+ OpeningTimeCompiler.WEEKDAYS[lastSameDay] + " "
+							+ makeStringFromMinuteArray(minuteArray[i]);
+					for (int j = i + 1; j < lastSameDay; ++j) {
+						if (days[j] == 0)
+							days[j] = -i - 1;
+					}
+				}
+			}
+
+			if (!add.isEmpty()) {
+				if (!ret.isEmpty())
+					ret += "; ";
+				ret += add;
+			}
+		}
+		return ret;
+	}
+
+	// returns a String representing the openinghours on one special day (e.g.
+	// "10:00-20:00")
+	private static String makeStringFromMinuteArray(boolean[] minutes) {
+		String ret = "";
+		for (int i = 0; i < minutes.length; ++i) {
+			if (minutes[i]) {
+				int start = i;
+				while (i < minutes.length && minutes[i])
+					++i;
+				String addString = timeString(start);
+				if (i - 1 == 24 * 60 + 1) // open end
+					addString += "+";
+				else if (start != i - 1) // closing time
+					addString += "-" + timeString(i - 1);
+				if (!ret.isEmpty())
+					ret += ",";
+				ret += addString;
+			}
+		}
+		return ret;
+	}
+
+	public static String timeString(int minutes) {
+		int h = minutes / 60;
+		int m = minutes % 60;
+		return (h < 10 ? "0" : "") + h + ":" + (m < 10 ? "0" : "") + m;
+	}
+
+	private static boolean isArrayEmpty(boolean[] bs) {
+		for (int i = 0; i < bs.length; i++)
+			if (bs[i])
+				return false;
+		return true;
+	}
+
+	private static boolean arraysEqual(boolean[] bs, boolean[] bs2) {
+		boolean ret = true;
+		for (int i = 0; i < bs.length; i++)
+			ret &= bs[i] == bs2[i];
+		return ret;
+	}
+}
Index: applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/gui/OheDialogPanel.java
===================================================================
--- applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/gui/OheDialogPanel.java	(revision 22751)
+++ applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/gui/OheDialogPanel.java	(revision 22751)
@@ -0,0 +1,158 @@
+package org.openstreetmap.josm.plugins.ohe.gui;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Dimension;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+
+import javax.swing.Box;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+import org.openstreetmap.josm.plugins.ohe.OhePlugin;
+import org.openstreetmap.josm.plugins.ohe.OpeningTimeUtils;
+import org.openstreetmap.josm.plugins.ohe.parser.OpeningTimeCompiler;
+import org.openstreetmap.josm.plugins.ohe.parser.ParseException;
+import org.openstreetmap.josm.plugins.ohe.parser.SyntaxException;
+import org.openstreetmap.josm.plugins.ohe.parser.TokenMgrError;
+import org.openstreetmap.josm.tools.GBC;
+
+public class OheDialogPanel extends JPanel {
+
+	private final JTextField keyField;
+
+	// The Component for showing the Time as a Text
+	private final JTextField valueField;
+
+	private final JButton twentyfourSevenButton;
+	private final JLabel actualPostionLabel;
+
+	// The important Panel for showing/editing the Time graphical
+	private final OheEditor editorPanel;
+
+	private final String oldkey;
+
+	public OheDialogPanel(OhePlugin plugin, String key, String value) {
+		oldkey = key;
+		keyField = new JTextField(key);
+
+		valueField = new JTextField(value);
+		valueField.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				// on every action in the textfield the timeRects are reloaded
+				editorPanel.initTimeRects();
+			}
+		});
+
+		twentyfourSevenButton = new JButton(tr("apply {0}", "24/7"));
+		twentyfourSevenButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				valueField.setText("24/7");
+				editorPanel.initTimeRects();
+			}
+		});
+
+		actualPostionLabel = new JLabel("Mo 00:00");
+		JPanel toolsPanel = new JPanel(new GridBagLayout());
+		toolsPanel.add(twentyfourSevenButton, GBC.std());
+		toolsPanel.add(Box.createGlue(), GBC.std().fill(GBC.HORIZONTAL));
+		toolsPanel.add(actualPostionLabel, GBC.eop());
+
+		editorPanel = new OheEditor(this);
+
+		// adding all Components in a Gridbaglayout
+		setLayout(new GridBagLayout());
+		add(new JLabel(tr("Key")), GBC.std());
+		add(Box.createHorizontalStrut(10), GBC.std());
+		add(keyField, GBC.eol().fill(GBC.HORIZONTAL));
+		add(new JLabel(tr("Value")), GBC.std());
+		add(Box.createHorizontalStrut(10), GBC.std());
+		add(valueField, GBC.eop().fill(GBC.HORIZONTAL));
+		add(toolsPanel, GBC.eol().fill(GBC.HORIZONTAL));
+		add(editorPanel, GBC.eol().fill());
+
+		valueField.requestFocus();
+		setPreferredSize(new Dimension(480, 520));
+	}
+
+	public String[] getChangedKeyValuePair() {
+		return new String[] { oldkey, keyField.getText(), valueField.getText() };
+	}
+
+	// returns the compiled Time from the valueField
+	public ArrayList<int[]> getTime() throws Exception {
+		String value = valueField.getText();
+		ArrayList<int[]> time = null;
+		if (value.length() > 0) {
+			OpeningTimeCompiler compiler = new OpeningTimeCompiler(value);
+			try {
+				time = OpeningTimeUtils.convert(compiler.startCompile());
+			} catch (Throwable t) {
+				int tColumns[] = null;
+				String info = null;
+
+				if (t instanceof ParseException) {
+					ParseException parserExc = (ParseException) t;
+					tColumns = new int[] {
+							parserExc.currentToken.beginColumn - 1,
+							parserExc.currentToken.endColumn + 1 };
+				} else if (t instanceof SyntaxException) {
+					SyntaxException syntaxError = (SyntaxException) t;
+					tColumns = new int[] { syntaxError.getStartColumn(),
+							syntaxError.getEndColumn() };
+					info = syntaxError.getInfo();
+				} else if (t instanceof TokenMgrError) {
+					TokenMgrError tokenMgrError = (TokenMgrError) t;
+					tColumns = new int[] { tokenMgrError.errorColumn - 1,
+							tokenMgrError.errorColumn + 1 };
+				} else {
+					t.printStackTrace();
+				}
+
+				// shows a Information Dialog, where the Error occurred
+				if (tColumns != null) {
+					int first = Math.max(0, tColumns[0]);
+					int last = Math.min(value.length(), tColumns[1]);
+					String begin = value.substring(0, first);
+					String middle = value.substring(first, last);
+					String end = value.substring(last);
+					String message = "<html>"
+							+ tr("There is something wrong in the value near:")
+							+ "<br>" + begin
+							+ "<span style='background-color:red;'>" + middle
+							+ "</span>" + end;
+					if (info != null)
+						message += "<br>" + tr("Info: {0}", tr(info));
+					message += "<br>"
+							+ tr("Correct the value manually and than press Enter.");
+					message += "</html>";
+					JOptionPane.showMessageDialog(this, message,
+							tr("Error in timeformat"),
+							JOptionPane.INFORMATION_MESSAGE);
+				}
+
+				throw new Exception("Error in the TimeValue");
+			}
+		}
+
+		return time;
+	}
+
+	// updates the valueField with the given timeRects
+	public void updateValueField(ArrayList<TimeRect> timeRects) {
+		if (valueField != null && timeRects != null)
+			valueField.setText(OpeningTimeUtils.makeStringFromRects(timeRects));
+	}
+
+	public void setMousePositionText(String positionText) {
+		actualPostionLabel.setText(positionText);
+	}
+}
Index: applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/gui/OheEditor.java
===================================================================
--- applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/gui/OheEditor.java	(revision 22751)
+++ applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/gui/OheEditor.java	(revision 22751)
@@ -0,0 +1,327 @@
+package org.openstreetmap.josm.plugins.ohe.gui;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.util.ArrayList;
+
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.plugins.ohe.OpeningTimeUtils;
+import org.openstreetmap.josm.plugins.ohe.parser.OpeningTimeCompiler;
+
+public class OheEditor extends JPanel implements MouseListener,
+		MouseMotionListener {
+	final OheDialogPanel dialog;
+
+	final private JScrollPane scrollPane;
+	final JPanel contentPanel;
+
+	ArrayList<TimeRect> timeRects;
+
+	final private int dayAxisHeight = 20;
+	final private int timeAxisWidth = 45;
+
+	public OheEditor(OheDialogPanel oheDialogPanel) {
+		dialog = oheDialogPanel;
+
+		// the MainPanel for showing the TimeRects
+		contentPanel = new JPanel() {
+			@Override
+			public void setSize(Dimension d) {
+				super.setSize(d);
+				repositionTimeRects();
+			}
+
+			@Override
+			public void paintComponent(Graphics g) {
+				if (OheEditor.this.isEnabled()) {
+					g.setColor(Color.WHITE);
+					g.fillRect(0, 0, getWidth(), getHeight());
+
+					// horizontal Lines
+					for (int i = 1; i < 24; ++i) {
+						if (i % 3 == 0)
+							g.setColor(Color.BLACK);
+						else
+							g.setColor(Color.LIGHT_GRAY);
+
+						g.drawLine(0, getMinutePosition(i * 60), getWidth(),
+								getMinutePosition(i * 60));
+					}
+
+					// vertical Lines
+					g.setColor(Color.BLACK);
+					for (int i = 1; i < 7; ++i)
+						g.drawLine(getDayPosition(i), 0, getDayPosition(i),
+								getHeight());
+
+					// if a new Rect is dragged draw it
+					if (day0 >= 0) {
+						Graphics2D g2D = (Graphics2D) g;
+
+						int day2 = Math.min(day0, day1);
+						int day3 = Math.max(day0, day1);
+						int minute2 = Math.min(minute0, minute1);
+						int minute3 = Math.max(minute0, minute1);
+						Rectangle bounds = getPanelBoundsForTimeinterval(day2,
+								day3 + 1, minute2, minute3);
+
+						TimeRect.drawTimeRect(g2D, bounds, minute2 == minute3, false);
+					}
+				} else {
+					g.setColor(Color.LIGHT_GRAY);
+					g.fillRect(0, 0, getWidth(), getHeight());
+				}
+			}
+		};
+		contentPanel.addMouseListener(this);
+		contentPanel.addMouseMotionListener(this);
+		contentPanel.setLayout(null);
+		contentPanel.setPreferredSize(new Dimension(180, 384));
+
+		initTimeRects();
+
+		scrollPane = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
+				JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+		scrollPane.setViewportView(contentPanel);
+
+		// the upper Panel for showing Weekdays
+		scrollPane.setColumnHeaderView(new JPanel() {
+			@Override
+			public Dimension getPreferredSize() {
+				return new Dimension(contentPanel.getWidth(), dayAxisHeight);
+			}
+
+			@Override
+			public void paintComponent(Graphics g) {
+				g.setColor(Color.WHITE);
+				g.fillRect(0, 0, getWidth(), getHeight());
+
+				g.setColor(Color.BLACK);
+				for (int i = 0; i < 7; ++i) {
+					if (i > 0)
+						g.drawLine(getDayPosition(i) + 1, 0,
+								getDayPosition(i) + 1, getHeight());
+
+					String text = OpeningTimeCompiler.WEEKDAYS[i];
+					g.drawString(text, (int) (getDayPosition(i + 0.5) - g
+							.getFontMetrics().stringWidth(text) * 0.5),
+							(int) (dayAxisHeight * 0.5 + g.getFontMetrics()
+									.getHeight() * 0.35));
+				}
+			}
+		});
+
+		// the left Panel for showing the hours
+		scrollPane.setRowHeaderView(new JPanel() {
+			@Override
+			public Dimension getPreferredSize() {
+				return new Dimension(timeAxisWidth, contentPanel.getHeight());
+			}
+
+			@Override
+			public void paintComponent(Graphics g) {
+				g.setColor(Color.WHITE);
+				g.fillRect(0, 0, getWidth(), getHeight());
+
+				for (int i = 1; i < 24; ++i) {
+					if (i % 3 == 0) {
+						g.setColor(Color.BLACK);
+						String text = ((i < 10) ? "0" + i : i) + ":00";
+						g
+								.drawString(text, timeAxisWidth - 10
+										- g.getFontMetrics().stringWidth(text),
+										getMinutePosition(i * 60)
+												+ (int) (g.getFontMetrics()
+														.getHeight() * 0.35));
+					} else
+						g.setColor(Color.LIGHT_GRAY);
+
+					g.drawLine(getWidth() - 4, getMinutePosition(i * 60) + 1,
+							getWidth(), getMinutePosition(i * 60) + 1);
+				}
+			}
+		});
+
+		setLayout(new BorderLayout());
+		add(scrollPane, BorderLayout.CENTER);
+	}
+
+	// update all the TimeRects with new Data
+	public void initTimeRects() {
+		contentPanel.removeAll();
+
+		ArrayList<int[]> time;
+		try {
+			time = dialog.getTime();
+		} catch (Exception exc) {
+			setEnabled(false);
+			return;
+		}
+
+		setEnabled(true);
+		timeRects = new ArrayList<TimeRect>();
+		if (time != null) {
+			for (int[] timeRectValues : time) {
+				int day0 = timeRectValues[0];
+				int day1 = timeRectValues[1];
+				int minute0 = timeRectValues[2];
+				int minute1 = timeRectValues[3];
+				TimeRect timeRect = new TimeRect(OheEditor.this, day0, day1,
+						minute0, minute1);
+				timeRects.add(timeRect);
+				contentPanel.add(timeRect);
+			}
+		}
+
+		repositionTimeRects();
+		repaint();
+	}
+
+	protected void repositionTimeRects() {
+		if (timeRects != null)
+			for (TimeRect timeRect : timeRects)
+				timeRect.reposition();
+	}
+
+	// returns the physical Borders of the TimeRect on the mainPanel
+	public Rectangle getPanelBoundsForTimeinterval(int dayStart, int dayEnd,
+			int minutesStart, int minutesEnd) {
+		int x = getDayPosition(dayStart);
+		int y = getMinutePosition(minutesStart);
+		int width = getDayPosition(dayEnd) - getDayPosition(dayStart);
+		int height = getMinutePosition(minutesEnd)
+				- getMinutePosition(minutesStart);
+
+		// work around openjdk bug
+		if (Main.isOpenjdk) {
+			x++;
+			y++;
+		}
+
+		if (minutesStart == minutesEnd)
+			return new Rectangle(x, y - 2 - TimeRect.verticalNonDrawedPixels,
+					width, height + 5 + 2 * TimeRect.verticalNonDrawedPixels);
+
+		return new Rectangle(x, y, width, height + 1);
+	}
+
+	public double getDayWidth() {
+		return (contentPanel.getWidth() - 1) / 7.0;
+	}
+
+	public int getDayPosition(double d) {
+		return (int) (d * getDayWidth());
+	}
+
+	public double getMinuteHeight() {
+		return (contentPanel.getHeight() - 1) / (24.0 * 60);
+	}
+
+	public int getMinutePosition(int minute) {
+		return (int) (minute * getMinuteHeight());
+	}
+
+	// removes the given timerect from the panel and from the arraylist
+	public void removeTimeRect(TimeRect timeRectToRemove) {
+		timeRects.remove(timeRectToRemove);
+		contentPanel.remove(timeRectToRemove);
+		dialog.updateValueField(timeRects);
+		repaint();
+	}
+
+	// drawing a new Rect
+	private int day0 = -1;
+	private int minute0;
+	private int day1;
+	private int minute1;
+	private int xDragStart;
+	private int yDragStart;
+
+	@Override
+	public void mouseClicked(MouseEvent evt) {
+	}
+
+	@Override
+	public void mouseEntered(MouseEvent evt) {
+	}
+
+	@Override
+	public void mouseExited(MouseEvent evt) {
+	}
+
+	@Override
+	public void mousePressed(MouseEvent evt) {
+		day0 = (int) Math.floor(evt.getX() / getDayWidth());
+		minute0 = (int) Math.floor(evt.getY()
+				/ (getMinuteHeight() * TimeRect.minuteResterize))
+				* TimeRect.minuteResterize;
+		day1 = day0;
+		minute1 = minute0;
+		xDragStart = evt.getX();
+		yDragStart = evt.getY();
+	}
+
+	@Override
+	public void mouseReleased(MouseEvent evt) {
+		// mouse must be moved 5px before creating a rect
+		if (xDragStart == -1
+				|| Math.abs(evt.getX() - xDragStart)
+						+ Math.abs(evt.getY() - yDragStart) > 5) {
+			int day2 = Math.min(day0, day1);
+			int day3 = Math.max(day0, day1);
+			int minute2 = Math.min(minute0, minute1);
+			int minute3 = Math.max(minute0, minute1);
+
+			TimeRect timeRect = new TimeRect(OheEditor.this, day2, day3,
+					minute2, minute3);
+			timeRects.add(timeRect);
+			contentPanel.add(timeRect);
+			timeRect.reposition();
+			dialog.updateValueField(timeRects);
+
+			day0 = -1;
+			repaint();
+		}
+	}
+
+	@Override
+	public void mouseDragged(MouseEvent evt) {
+		// mouse must be moved 5px before drawing a rect
+		if (xDragStart == -1
+				|| Math.abs(evt.getX() - xDragStart)
+						+ Math.abs(evt.getY() - yDragStart) > 5) {
+			xDragStart = -1;
+			day1 = (int) Math.floor(evt.getX() / getDayWidth());
+			minute1 = (int) Math.floor(evt.getY()
+					/ (getMinuteHeight() * TimeRect.minuteResterize))
+					* TimeRect.minuteResterize;
+			repaint();
+		}
+	}
+
+	@Override
+	public void mouseMoved(MouseEvent evt) {
+		mousePositionChanged(evt.getX(), evt.getY());
+	}
+
+	public void mousePositionChanged(int x, int y) {
+		int actualDay = (int) Math.floor(x / getDayWidth());
+		int minutes = (int) Math.floor(y
+				/ (getMinuteHeight() * TimeRect.minuteResterize))
+				* TimeRect.minuteResterize;
+		actualDay = Math.max(0, Math.min(6, actualDay));
+		minutes = Math.max(0, Math.min(24 * 60, minutes));
+		dialog.setMousePositionText(OpeningTimeCompiler.WEEKDAYS[actualDay]
+				+ " " + OpeningTimeUtils.timeString(minutes));
+	}
+}
Index: applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/gui/TimeRect.java
===================================================================
--- applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/gui/TimeRect.java	(revision 22751)
+++ applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/gui/TimeRect.java	(revision 22751)
@@ -0,0 +1,296 @@
+package org.openstreetmap.josm.plugins.ohe.gui;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Cursor;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+
+public class TimeRect extends JPanel implements MouseListener,
+		MouseMotionListener {
+	public static final int[] transformCursorTypes = new int[] {
+			Cursor.MOVE_CURSOR, Cursor.N_RESIZE_CURSOR,
+			Cursor.NE_RESIZE_CURSOR, Cursor.E_RESIZE_CURSOR,
+			Cursor.SE_RESIZE_CURSOR, Cursor.S_RESIZE_CURSOR,
+			Cursor.SW_RESIZE_CURSOR, Cursor.W_RESIZE_CURSOR,
+			Cursor.NW_RESIZE_CURSOR };
+
+	public static final int minuteResterize = 15;
+	public static final int verticalNonDrawedPixels = 5;
+
+	public static final boolean[][] transformDirections = new boolean[][] {
+			{ true, true, true, true }, // Drag
+			{ true, false, false, false }, // N
+			{ true, true, false, false }, // NE
+			{ false, true, false, false }, // E
+			{ false, true, true, false }, // SE
+			{ false, false, true, false }, // S
+			{ false, false, true, true }, // SW
+			{ false, false, false, true }, // W
+			{ true, false, false, true }, // NW
+	};
+
+	public static final int roundCornerSize = 8;
+	private final int clickAreaSize = 16;
+
+	private OheEditor editor;
+
+	private int dayStart;
+	private int dayEnd;
+	private int minuteStart;
+	private int minuteEnd;
+
+	public TimeRect(OheEditor editor, int dayStart, int dayEnd,
+			int minutesStart, int minutesEnd) {
+		this.editor = editor;
+
+		this.dayStart = dayStart;
+		this.dayEnd = dayEnd;
+		this.minuteStart = minutesStart;
+		this.minuteEnd = minutesEnd;
+
+		transformType = -1;
+
+		setOpaque(true);
+
+		addMouseListener(this);
+		addMouseMotionListener(this);
+	}
+
+	public int getDayStart() {
+		return dayStart;
+	}
+
+	public int getDayEnd() {
+		return dayEnd;
+	}
+
+	public int getMinuteStart() {
+		return minuteStart;
+	}
+
+	public int getMinuteEnd() {
+		return minuteEnd;
+	}
+
+	public void reposition() {
+		setBounds(editor.getPanelBoundsForTimeinterval(dayStart, dayEnd + 1,
+				minuteStart, minuteEnd));
+		editor.contentPanel.repaint();
+	}
+
+	private boolean isZeroMinuteInterval() {
+		return minuteStart == minuteEnd;
+	}
+
+	private boolean isOpenEndInterval() {
+		return minuteEnd == 24 * 60 + 1;
+	}
+
+	private void updateTimeInterval(int newDayStart, int newDayEnd,
+			int newMinuteStart, int newMinuteEnd) {
+		dayStart = newDayStart;
+		dayEnd = newDayEnd;
+		minuteStart = newMinuteStart;
+		minuteEnd = newMinuteEnd;
+
+		editor.dialog.updateValueField(editor.timeRects);
+		reposition();
+	}
+
+	@Override
+	public void paintComponent(Graphics g) {
+		drawTimeRect((Graphics2D) g, new Rectangle(0, 0, getWidth(),
+				getHeight()), isZeroMinuteInterval(), isOpenEndInterval());
+	}
+
+	public static void drawTimeRect(Graphics2D g2D, Rectangle bounds,
+			boolean isZeroMinuteInterval, boolean isOpenEndInterval) {
+
+		Color innerColor = new Color(135, 135, 234);
+		if (isOpenEndInterval)
+			innerColor = new Color(234, 135, 135);
+
+		int tmpRoundCornerSize = TimeRect.roundCornerSize;
+		int verticalNonFilledBorder = 1;
+		if (isZeroMinuteInterval) {
+			innerColor = new Color(135, 234, 135);
+			tmpRoundCornerSize = 0;
+			verticalNonFilledBorder = verticalNonDrawedPixels;
+		}
+
+		g2D.setColor(innerColor);
+		g2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
+				.6f));
+		g2D.fillRoundRect(bounds.x + 1, bounds.y + verticalNonFilledBorder,
+				bounds.width - 2, bounds.height - 1 - 2
+						* verticalNonFilledBorder, tmpRoundCornerSize,
+				tmpRoundCornerSize);
+
+		g2D.setColor(new Color(255, 0, 0));
+		g2D.setComposite(AlphaComposite
+				.getInstance(AlphaComposite.SRC_OVER, 1f));
+		g2D.drawRoundRect(bounds.x + 1, bounds.y + verticalNonFilledBorder,
+				bounds.width - 2, bounds.height - 1 - 2
+						* verticalNonFilledBorder, tmpRoundCornerSize,
+				tmpRoundCornerSize);
+
+	}
+
+	private int actualDayDrag;
+	private int actualMinuteDrag;
+	private int dragX;
+	private int dragY;
+	private int transformType;
+
+	// Calculate where the Component was clicked and returns the
+	// transformtype
+	private int getTransformType(MouseEvent evt) {
+		int tmpClickAreaWidth = Math.min(clickAreaSize, getWidth() / 3);
+		int tmpClickAreaHeight = Math.min(clickAreaSize, getHeight() / 3);
+
+		boolean isInNorthernTransformClickArea = evt.getY() < tmpClickAreaHeight;
+		boolean isInEasternTransformClickArea = evt.getX() > getWidth()
+				- tmpClickAreaWidth;
+		boolean isInSouthernTransformClickArea = evt.getY() > getHeight()
+				- tmpClickAreaHeight;
+		boolean isInWesternTransformClickArea = evt.getX() < tmpClickAreaWidth;
+
+		if (isZeroMinuteInterval()) {
+			isInNorthernTransformClickArea = false;
+			isInSouthernTransformClickArea = false;
+		}
+
+		int tType = 0;
+		for (int i = 1; i < transformDirections.length && tType == 0; i++) {
+			if (isInNorthernTransformClickArea == transformDirections[i][0]
+					&& isInEasternTransformClickArea == transformDirections[i][1]
+					&& isInSouthernTransformClickArea == transformDirections[i][2]
+					&& isInWesternTransformClickArea == transformDirections[i][3])
+				tType = i;
+		}
+
+		return tType;
+	}
+
+	public void showMenu(MouseEvent evt) {
+		JPopupMenu menu = new JPopupMenu();
+		final JCheckBoxMenuItem cbMenuItem = new JCheckBoxMenuItem(
+				tr("open end"), isOpenEndInterval());
+		menu.add(cbMenuItem);
+		cbMenuItem.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				if (cbMenuItem.isSelected())
+					updateTimeInterval(dayStart, dayEnd, minuteStart,
+							24 * 60 + 1);
+				else
+					updateTimeInterval(dayStart, dayEnd, minuteStart, 24 * 60);
+			}
+		});
+		menu.show(this, evt.getX(), evt.getY());
+	}
+
+	@Override
+	public void mouseClicked(MouseEvent evt) {
+	}
+
+	@Override
+	public void mouseEntered(MouseEvent evt) {
+	}
+
+	@Override
+	public void mouseExited(MouseEvent evt) {
+		if (transformType < 0)
+			setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
+	}
+
+	@Override
+	public void mousePressed(MouseEvent evt) {
+		if (evt.isPopupTrigger()) {
+			showMenu(evt);
+		} else {
+			actualDayDrag = 0;
+			actualMinuteDrag = 0;
+			dragX = evt.getXOnScreen();
+			dragY = evt.getYOnScreen();
+			transformType = getTransformType(evt);
+		}
+	}
+
+	@Override
+	public void mouseReleased(MouseEvent evt) {
+		transformType = -1;
+	}
+
+	@Override
+	public void mouseDragged(MouseEvent evt) {
+		if (transformType >= 0) {
+			int xDiff = evt.getXOnScreen() - dragX;
+			int yDiff = evt.getYOnScreen() - dragY;
+
+			xDiff = (int) Math.round(xDiff / editor.getDayWidth())
+					- actualDayDrag;
+			yDiff = (int) Math.round(yDiff
+					/ (editor.getMinuteHeight() * minuteResterize))
+					* minuteResterize - actualMinuteDrag;
+
+			if (xDiff != 0) {
+				int newDayStart = dayStart;
+				int newDayEnd = dayEnd;
+
+				if (transformDirections[transformType][3])
+					newDayStart += xDiff;
+				if (transformDirections[transformType][1])
+					newDayEnd += xDiff;
+
+				if (newDayStart > newDayEnd) {
+					editor.removeTimeRect(this);
+					transformType = -1;
+					setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
+				} else if (newDayStart >= 0 && newDayEnd <= 6) {
+					actualDayDrag += xDiff;
+					updateTimeInterval(newDayStart, newDayEnd, minuteStart,
+							minuteEnd);
+				}
+			}
+			if (yDiff != 0 && transformType >= 0) {
+				int newMinutesStart = minuteStart;
+				int newMinutesEnd = minuteEnd;
+
+				if (transformDirections[transformType][0])
+					newMinutesStart = newMinutesStart + yDiff;
+				if (transformDirections[transformType][2]
+						&& !isOpenEndInterval())
+					newMinutesEnd = newMinutesEnd + yDiff;
+
+				if (newMinutesStart >= 0
+						&& (newMinutesEnd <= 24 * 60 || isOpenEndInterval())) {
+					actualMinuteDrag += yDiff;
+					updateTimeInterval(dayStart, dayEnd, newMinutesStart,
+							newMinutesEnd);
+				}
+			}
+		}
+		editor.mousePositionChanged(evt.getX() + getX(), evt.getY() + getY());
+	}
+
+	@Override
+	public void mouseMoved(MouseEvent evt) {
+		if (transformType < 0)
+			setCursor(new Cursor(transformCursorTypes[getTransformType(evt)]));
+		editor.mousePositionChanged(evt.getX() + getX(), evt.getY() + getY());
+	}
+}
Index: applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/OpeningTimeCompiler.java
===================================================================
--- applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/OpeningTimeCompiler.java	(revision 22751)
+++ applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/OpeningTimeCompiler.java	(revision 22751)
@@ -0,0 +1,435 @@
+/* Generated By:JavaCC: Do not edit this line. OpeningTimeCompiler.java */
+package org.openstreetmap.josm.plugins.ohe.parser;
+
+import java.io.ByteArrayInputStream;
+import java.util.ArrayList;
+import java.util.Vector;
+import org.openstreetmap.josm.plugins.ohe.OpeningTimeUtils.DaySpan;
+import org.openstreetmap.josm.plugins.ohe.OpeningTimeUtils.DaytimeSpan;
+import org.openstreetmap.josm.plugins.ohe.OpeningTimeUtils.DateTime;
+
+public class OpeningTimeCompiler implements OpeningTimeCompilerConstants {
+  public static final String[] WEEKDAYS = new String[] {"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"};
+
+  public OpeningTimeCompiler(String time)
+  {
+    this(new ByteArrayInputStream(time.getBytes()), null);
+  }
+
+// returns a list of times
+  final public ArrayList<DateTime> startCompile() throws ParseException, ParseException, SyntaxException {
+  Vector<Token> usedTokens = new Vector<Token>();
+  ArrayList<DateTime> time = new ArrayList<DateTime>();
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case DIGIT:
+    case WEEKDAY:
+    case OFF:
+      time = timespanlist(usedTokens);
+      break;
+    case 5:
+      jj_consume_token(5);
+      break;
+    default:
+      jj_la1[0] = jj_gen;
+      jj_consume_token(-1);
+      throw new ParseException();
+    }
+    jj_consume_token(0);
+    if (time.size() == 0) {
+                ArrayList<DaySpan> daySpans = new ArrayList<DaySpan>();
+        daySpans.add(new DaySpan(0,6));
+                ArrayList<DaytimeSpan> timeSpans = new ArrayList<DaytimeSpan>();
+        timeSpans.add(new DaytimeSpan(0, 24 * 60));
+        time.add(new DateTime(daySpans, timeSpans));
+        }
+
+    {if (true) return time;}
+    throw new Error("Missing return statement in function");
+  }
+
+//
+  final public ArrayList<DateTime> timespanlist(Vector<Token> usedTokens) throws ParseException, ParseException, SyntaxException {
+  ArrayList<DateTime> list = new ArrayList<DateTime>();
+  DateTime first = null;
+  ArrayList<DateTime> second = null;
+    first = timespan(usedTokens);
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case 6:
+      jj_consume_token(6);
+      second = timespanlist(usedTokens);
+      break;
+    default:
+      jj_la1[1] = jj_gen;
+      ;
+    }
+    list.add(first);
+    if(second != null)
+        list.addAll(second);
+    {if (true) return list;}
+    throw new Error("Missing return statement in function");
+  }
+
+//
+  final public DateTime timespan(Vector<Token> usedTokens) throws ParseException, ParseException, SyntaxException {
+  ArrayList<DaySpan> dayspanlist = new ArrayList<DaySpan>();
+  dayspanlist.add(new DaySpan(0, 6)); // if no days are given the timespan is applied to day 0-6
+  ArrayList<DaytimeSpan> daytimespanlist = null;
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case WEEKDAY:
+      dayspanlist = dayspanlist(usedTokens);
+      jj_consume_token(7);
+      break;
+    default:
+      jj_la1[2] = jj_gen;
+      ;
+    }
+    daytimespanlist = daytimespanlist(usedTokens);
+    {if (true) return new DateTime(dayspanlist, daytimespanlist);}
+    throw new Error("Missing return statement in function");
+  }
+
+// returns a list of daytimes as ArrayList<int[2]>
+  final public ArrayList<DaytimeSpan> daytimespanlist(Vector<Token> usedTokens) throws ParseException, ParseException, SyntaxException {
+  ArrayList<DaytimeSpan> list = new ArrayList<DaytimeSpan>();
+  DaytimeSpan first;
+  ArrayList<DaytimeSpan> second = new ArrayList<DaytimeSpan>();
+    first = daytimespan(usedTokens);
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case 8:
+      jj_consume_token(8);
+      second = daytimespanlist(usedTokens);
+      break;
+    default:
+      jj_la1[3] = jj_gen;
+      ;
+    }
+    list.add(first);
+    if(second != null)
+        list.addAll(second);
+    {if (true) return list;}
+    throw new Error("Missing return statement in function");
+  }
+
+// returns the minutes from 00:00 of a daytimespan as integer[2]
+// int[0] == int[1] means it is just one daytime
+// int[0] == int[1] == -1 means off
+//           int[1] == 24*60+1 means openend
+  final public DaytimeSpan daytimespan(Vector<Token> usedTokens) throws ParseException, ParseException, SyntaxException {
+  int start, end =-1;
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case OFF:
+      start = off(usedTokens);
+      break;
+    case DIGIT:
+      start = daytime(usedTokens);
+      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+      case PLUS:
+        end = openend(usedTokens);
+        break;
+      default:
+        jj_la1[5] = jj_gen;
+        switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+        case 9:
+          jj_consume_token(9);
+          end = daytime(usedTokens);
+          break;
+        default:
+          jj_la1[4] = jj_gen;
+          ;
+        }
+      }
+      break;
+    default:
+      jj_la1[6] = jj_gen;
+      jj_consume_token(-1);
+      throw new ParseException();
+    }
+    if (end < 0)
+        end = start;
+    else if(end < start)
+        {if (true) throw new SyntaxException(null, usedTokens.lastElement().beginColumn - 1, usedTokens.lastElement().endColumn + 1);}
+
+    {if (true) return new DaytimeSpan(start, end);}
+    throw new Error("Missing return statement in function");
+  }
+
+// returns the minutes from 00:00 as integer
+  final public int daytime(Vector<Token> usedTokens) throws ParseException, ParseException, SyntaxException {
+  Token th0, th1, tm0, tm1;
+    th1 = jj_consume_token(DIGIT);
+    th0 = jj_consume_token(DIGIT);
+    jj_consume_token(10);
+    tm1 = jj_consume_token(DIGIT);
+    tm0 = jj_consume_token(DIGIT);
+    usedTokens.add(th1);
+    usedTokens.add(th0);
+    usedTokens.add(tm1);
+    usedTokens.add(tm0);
+    int hour = Integer.parseInt(th1.image) * 10 + Integer.parseInt(th0.image);
+    int minute = Integer.parseInt(tm1.image) * 10 + Integer.parseInt(tm0.image);
+
+    if (hour > 24)
+        {if (true) throw new SyntaxException(null, th1.beginColumn - 1, th0.endColumn + 1);}
+    if (minute >= 60 || (hour == 24 && minute != 0))
+        {if (true) throw new SyntaxException(null, tm1.beginColumn - 1, tm0.endColumn + 1);}
+
+    {if (true) return hour * 60 + minute;}
+    throw new Error("Missing return statement in function");
+  }
+
+//
+  final public int openend(Vector<Token> usedTokens) throws ParseException, ParseException, SyntaxException {
+  Token plus;
+    plus = jj_consume_token(PLUS);
+    usedTokens.add(plus);
+    {if (true) return 24 * 60 + 1;}
+    throw new Error("Missing return statement in function");
+  }
+
+//
+  final public int off(Vector<Token> usedTokens) throws ParseException, ParseException, SyntaxException {
+  Token off;
+    off = jj_consume_token(OFF);
+    usedTokens.add(off);
+    {if (true) return -1;}
+    throw new Error("Missing return statement in function");
+  }
+
+// returns a list of days as ArrayList<int[2]>
+  final public ArrayList<DaySpan> dayspanlist(Vector<Token> usedTokens) throws ParseException, ParseException, SyntaxException {
+  ArrayList<DaySpan> list = new ArrayList<DaySpan>();
+  DaySpan first;
+  ArrayList<DaySpan> second = null;
+    first = dayspan(usedTokens);
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case 8:
+      jj_consume_token(8);
+      second = dayspanlist(usedTokens);
+      break;
+    default:
+      jj_la1[7] = jj_gen;
+      ;
+    }
+    if (second == null) {
+      //second dayspanlist is empty, only the first is returned
+      list.add(first);
+    } else {
+      if (first.endDay + 1 == second.get(0).startDay) {
+        //the second dayspanlist and the first are coherent, both are joined and returned
+        list.addAll(second);
+        list.set(0, new DaySpan(first.startDay, second.get(0).endDay));
+      } else {
+        //the first and the second dayspans are not coherent, both are added to the returnStatement
+        list.add(first);
+        list.addAll(second);
+      }
+    }
+    {if (true) return list;}
+    throw new Error("Missing return statement in function");
+  }
+
+// returns the span of two days as int[2]
+// int[0] == int[1] means it is just one day
+  final public DaySpan dayspan(Vector<Token> usedTokens) throws ParseException, ParseException, SyntaxException {
+  int start, end = -1;
+    start = day(usedTokens);
+    switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+    case 9:
+      jj_consume_token(9);
+      end = day(usedTokens);
+      break;
+    default:
+      jj_la1[8] = jj_gen;
+      ;
+    }
+    if(end < 0)
+        end = start;
+    else if(end < start)
+        {if (true) throw new SyntaxException(null, usedTokens.lastElement().beginColumn - 1, usedTokens.lastElement().endColumn + 1);}
+
+    {if (true) return new DaySpan(start, end);}
+    throw new Error("Missing return statement in function");
+  }
+
+// returns the weekday as integer
+  final public int day(Vector<Token> usedTokens) throws ParseException, ParseException, SyntaxException {
+  Token t;
+    t = jj_consume_token(WEEKDAY);
+    usedTokens.add(t);
+    for (int i = 0; i < WEEKDAYS.length; ++i)
+      if (WEEKDAYS[i].equals(t.image))
+        {if (true) return i;}
+
+    {if (true) throw new SyntaxException(null, t.beginColumn - 1, t.endColumn + 1);}
+    throw new Error("Missing return statement in function");
+  }
+
+  /** Generated Token Manager. */
+  public OpeningTimeCompilerTokenManager token_source;
+  SimpleCharStream jj_input_stream;
+  /** Current token. */
+  public Token token;
+  /** Next token. */
+  public Token jj_nt;
+  private int jj_ntk;
+  private int jj_gen;
+  final private int[] jj_la1 = new int[9];
+  static private int[] jj_la1_0;
+  static {
+      jj_la1_init_0();
+   }
+   private static void jj_la1_init_0() {
+      jj_la1_0 = new int[] {0x36,0x40,0x4,0x100,0x200,0x8,0x12,0x100,0x200,};
+   }
+
+  /** Constructor with InputStream. */
+  public OpeningTimeCompiler(java.io.InputStream stream) {
+     this(stream, null);
+  }
+  /** Constructor with InputStream and supplied encoding */
+  public OpeningTimeCompiler(java.io.InputStream stream, String encoding) {
+    try { jj_input_stream = new SimpleCharStream(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
+    token_source = new OpeningTimeCompilerTokenManager(jj_input_stream);
+    token = new Token();
+    jj_ntk = -1;
+    jj_gen = 0;
+    for (int i = 0; i < 9; i++) jj_la1[i] = -1;
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream stream) {
+     ReInit(stream, null);
+  }
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream stream, String encoding) {
+    try { jj_input_stream.ReInit(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
+    token_source.ReInit(jj_input_stream);
+    token = new Token();
+    jj_ntk = -1;
+    jj_gen = 0;
+    for (int i = 0; i < 9; i++) jj_la1[i] = -1;
+  }
+
+  /** Constructor. */
+  public OpeningTimeCompiler(java.io.Reader stream) {
+    jj_input_stream = new SimpleCharStream(stream, 1, 1);
+    token_source = new OpeningTimeCompilerTokenManager(jj_input_stream);
+    token = new Token();
+    jj_ntk = -1;
+    jj_gen = 0;
+    for (int i = 0; i < 9; i++) jj_la1[i] = -1;
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.Reader stream) {
+    jj_input_stream.ReInit(stream, 1, 1);
+    token_source.ReInit(jj_input_stream);
+    token = new Token();
+    jj_ntk = -1;
+    jj_gen = 0;
+    for (int i = 0; i < 9; i++) jj_la1[i] = -1;
+  }
+
+  /** Constructor with generated Token Manager. */
+  public OpeningTimeCompiler(OpeningTimeCompilerTokenManager tm) {
+    token_source = tm;
+    token = new Token();
+    jj_ntk = -1;
+    jj_gen = 0;
+    for (int i = 0; i < 9; i++) jj_la1[i] = -1;
+  }
+
+  /** Reinitialise. */
+  public void ReInit(OpeningTimeCompilerTokenManager tm) {
+    token_source = tm;
+    token = new Token();
+    jj_ntk = -1;
+    jj_gen = 0;
+    for (int i = 0; i < 9; i++) jj_la1[i] = -1;
+  }
+
+  private Token jj_consume_token(int kind) throws ParseException {
+    Token oldToken;
+    if ((oldToken = token).next != null) token = token.next;
+    else token = token.next = token_source.getNextToken();
+    jj_ntk = -1;
+    if (token.kind == kind) {
+      jj_gen++;
+      return token;
+    }
+    token = oldToken;
+    jj_kind = kind;
+    throw generateParseException();
+  }
+
+
+/** Get the next Token. */
+  final public Token getNextToken() {
+    if (token.next != null) token = token.next;
+    else token = token.next = token_source.getNextToken();
+    jj_ntk = -1;
+    jj_gen++;
+    return token;
+  }
+
+/** Get the specific Token. */
+  final public Token getToken(int index) {
+    Token t = token;
+    for (int i = 0; i < index; i++) {
+      if (t.next != null) t = t.next;
+      else t = t.next = token_source.getNextToken();
+    }
+    return t;
+  }
+
+  private int jj_ntk() {
+    if ((jj_nt=token.next) == null)
+      return (jj_ntk = (token.next=token_source.getNextToken()).kind);
+    else
+      return (jj_ntk = jj_nt.kind);
+  }
+
+  private java.util.List<int[]> jj_expentries = new java.util.ArrayList<int[]>();
+  private int[] jj_expentry;
+  private int jj_kind = -1;
+
+  /** Generate ParseException. */
+  public ParseException generateParseException() {
+    jj_expentries.clear();
+    boolean[] la1tokens = new boolean[11];
+    if (jj_kind >= 0) {
+      la1tokens[jj_kind] = true;
+      jj_kind = -1;
+    }
+    for (int i = 0; i < 9; i++) {
+      if (jj_la1[i] == jj_gen) {
+        for (int j = 0; j < 32; j++) {
+          if ((jj_la1_0[i] & (1<<j)) != 0) {
+            la1tokens[j] = true;
+          }
+        }
+      }
+    }
+    for (int i = 0; i < 11; i++) {
+      if (la1tokens[i]) {
+        jj_expentry = new int[1];
+        jj_expentry[0] = i;
+        jj_expentries.add(jj_expentry);
+      }
+    }
+    int[][] exptokseq = new int[jj_expentries.size()][];
+    for (int i = 0; i < jj_expentries.size(); i++) {
+      exptokseq[i] = jj_expentries.get(i);
+    }
+    return new ParseException(token, exptokseq, tokenImage);
+  }
+
+  /** Enable tracing. */
+  final public void enable_tracing() {
+  }
+
+  /** Disable tracing. */
+  final public void disable_tracing() {
+  }
+
+}
Index: applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/OpeningTimeCompiler.jj
===================================================================
--- applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/OpeningTimeCompiler.jj	(revision 22751)
+++ applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/OpeningTimeCompiler.jj	(revision 22751)
@@ -0,0 +1,259 @@
+/*
+DIGIT ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
+WEEKDAY ::= "Mo" | "Tu" | "We" | "Th" | "Fr" | "Sa" | "Su"
+PLUS ::= "+"
+OFF ::= "off"
+
+startCompile ::= "24/7" | timespanlist
+timespanlist ::= timespan [ "; " timespanlist ]
+timespan ::= [ dayspanlist " " ] daytimespanlist
+
+daytimespanlist ::=	daytimespan [ "," daytimespanlist ]
+daytimespan ::= off | ( daytime ( openend |  [ "-" daytime ]) )
+daytime ::=	(DIGIT DIGIT ":" DIGIT DIGIT )
+openend ::= PLUS
+off ::= OFF
+
+dayspanlist ::=	dayspan [ "," dayspanlist ]
+dayspan ::=	WEEKDAY [ "-" WEEKDAY ]
+*/
+
+options
+{
+  LOOKAHEAD= 1;
+  CHOICE_AMBIGUITY_CHECK = 2;
+  OTHER_AMBIGUITY_CHECK = 1;
+  STATIC = false;
+  DEBUG_PARSER = false;
+  DEBUG_LOOKAHEAD = false;
+  DEBUG_TOKEN_MANAGER = false;
+  ERROR_REPORTING = true;
+  JAVA_UNICODE_ESCAPE = false;
+  UNICODE_INPUT = false;
+  IGNORE_CASE = false;
+  USER_TOKEN_MANAGER = false;
+  USER_CHAR_STREAM = false;
+  BUILD_PARSER = true;
+  BUILD_TOKEN_MANAGER = true;
+  SANITY_CHECK = true;
+  FORCE_LA_CHECK = false;
+}
+
+PARSER_BEGIN(OpeningTimeCompiler)
+package org.openstreetmap.josm.plugins.ohe.parser;
+
+import java.io.ByteArrayInputStream;
+import java.util.ArrayList;
+import java.util.Vector;
+import org.openstreetmap.josm.plugins.ohe.OpeningTimeUtils.DaySpan;
+import org.openstreetmap.josm.plugins.ohe.OpeningTimeUtils.DaytimeSpan;
+import org.openstreetmap.josm.plugins.ohe.OpeningTimeUtils.DateTime;
+
+public class OpeningTimeCompiler
+{
+  public static final String[] WEEKDAYS = new String[] {"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"};
+
+  public OpeningTimeCompiler(String time)
+  {
+    this(new ByteArrayInputStream(time.getBytes()), null);
+  }
+}
+
+PARSER_END(OpeningTimeCompiler)
+
+TOKEN : { < DIGIT : [ "0"-"9" ] > }
+TOKEN : { < WEEKDAY : "Mo" | "Tu" | "We" | "Th" | "Fr" | "Sa" | "Su" > }
+TOKEN : { < PLUS : "+" > }
+TOKEN : { < OFF : "off" > }
+
+// returns a list of times
+ArrayList<DateTime> startCompile() throws ParseException, SyntaxException :
+{
+  Vector<Token> usedTokens = new Vector<Token>();
+  ArrayList<DateTime> time = new ArrayList<DateTime>();
+}
+{
+  ( time = timespanlist(usedTokens) | "24/7" ) <EOF>
+  {
+    if (time.size() == 0) {
+  		ArrayList<DaySpan> daySpans = new ArrayList<DaySpan>();
+    	daySpans.add(new DaySpan(0,6));
+  		ArrayList<DaytimeSpan> timeSpans = new ArrayList<DaytimeSpan>();
+    	timeSpans.add(new DaytimeSpan(0, 24 * 60));
+    	time.add(new DateTime(daySpans, timeSpans));
+	}
+    	
+    return time;
+  }
+}
+//
+ArrayList<DateTime> timespanlist(Vector<Token> usedTokens) throws ParseException, SyntaxException :
+{
+  ArrayList<DateTime> list = new ArrayList<DateTime>();
+  DateTime first = null;
+  ArrayList<DateTime> second = null;
+}
+{
+  first = timespan(usedTokens) [ "; " second = timespanlist(usedTokens) ]
+  {
+    list.add(first);
+    if(second != null)
+    	list.addAll(second);
+    return list;
+  }
+}
+//
+DateTime timespan(Vector<Token> usedTokens) throws ParseException, SyntaxException :
+{
+  ArrayList<DaySpan> dayspanlist = new ArrayList<DaySpan>();
+  dayspanlist.add(new DaySpan(0, 6)); // if no days are given the timespan is applied to day 0-6
+  ArrayList<DaytimeSpan> daytimespanlist = null;
+}
+{
+  [ dayspanlist = dayspanlist(usedTokens) " " ] daytimespanlist = daytimespanlist(usedTokens)
+  {
+    return new DateTime(dayspanlist, daytimespanlist);
+  }
+}
+// returns a list of daytimes as ArrayList<int[2]>
+ArrayList<DaytimeSpan> daytimespanlist(Vector<Token> usedTokens) throws ParseException, SyntaxException :
+{
+  ArrayList<DaytimeSpan> list = new ArrayList<DaytimeSpan>();
+  DaytimeSpan first;
+  ArrayList<DaytimeSpan> second = new ArrayList<DaytimeSpan>();
+}
+{
+  first = daytimespan(usedTokens) [ "," second = daytimespanlist(usedTokens) ]
+  {
+    list.add(first);
+    if(second != null)
+    	list.addAll(second);
+    return list;
+  }
+}
+// returns the minutes from 00:00 of a daytimespan as integer[2]
+// int[0] == int[1] means it is just one daytime
+// int[0] == int[1] == -1 means off
+//           int[1] == 24*60+1 means openend
+DaytimeSpan daytimespan(Vector<Token> usedTokens) throws ParseException, SyntaxException :
+{
+  int start, end =-1;
+}
+{
+  ( start = off(usedTokens) | ( start = daytime(usedTokens) ( end = openend(usedTokens) | [ "-" end = daytime(usedTokens) ] ) ) )
+  {
+    if (end < 0)
+    	end = start;
+    else if(end < start)
+    	throw new SyntaxException(null, usedTokens.lastElement().beginColumn - 1, usedTokens.lastElement().endColumn + 1);
+    
+    return new DaytimeSpan(start, end);
+  }
+}
+// returns the minutes from 00:00 as integer
+int daytime(Vector<Token> usedTokens) throws ParseException, SyntaxException :
+{
+  Token th0, th1, tm0, tm1;
+}
+{
+  th1 = <DIGIT> th0 = <DIGIT> ":" tm1 = <DIGIT> tm0 = <DIGIT>
+  {
+    usedTokens.add(th1);
+    usedTokens.add(th0);
+    usedTokens.add(tm1);
+    usedTokens.add(tm0);
+    int hour = Integer.parseInt(th1.image) * 10 + Integer.parseInt(th0.image);
+    int minute = Integer.parseInt(tm1.image) * 10 + Integer.parseInt(tm0.image);
+
+    if (hour > 24)
+    	throw new SyntaxException(null, th1.beginColumn - 1, th0.endColumn + 1);
+    if (minute >= 60 || (hour == 24 && minute != 0))
+    	throw new SyntaxException(null, tm1.beginColumn - 1, tm0.endColumn + 1);
+
+    return hour * 60 + minute;
+  }
+}
+//
+int openend(Vector<Token> usedTokens) throws ParseException, SyntaxException :
+{
+  Token plus;
+}
+{
+  plus = <PLUS>
+  {
+    usedTokens.add(plus);
+    return 24 * 60 + 1;
+  }
+}
+//
+int off(Vector<Token> usedTokens) throws ParseException, SyntaxException :
+{
+  Token off;
+}
+{
+  off = <OFF>
+  {
+    usedTokens.add(off);
+    return -1;
+  }
+}
+// returns a list of days as ArrayList<int[2]>
+ArrayList<DaySpan> dayspanlist(Vector<Token> usedTokens) throws ParseException, SyntaxException :
+{
+  ArrayList<DaySpan> list = new ArrayList<DaySpan>();
+  DaySpan first;
+  ArrayList<DaySpan> second = null;
+}
+{
+  first = dayspan(usedTokens) [ "," second = dayspanlist(usedTokens) ]
+  {
+    if (second == null) {
+      //second dayspanlist is empty, only the first is returned
+      list.add(first);
+    } else {
+      if (first.endDay + 1 == second.get(0).startDay) {
+        //the second dayspanlist and the first are coherent, both are joined and returned
+        list.addAll(second);
+        list.set(0, new DaySpan(first.startDay, second.get(0).endDay));
+      } else {
+        //the first and the second dayspans are not coherent, both are added to the returnStatement
+        list.add(first);
+        list.addAll(second);
+      }
+    }
+    return list;
+  }
+}
+// returns the span of two days as int[2]
+// int[0] == int[1] means it is just one day
+DaySpan dayspan(Vector<Token> usedTokens) throws ParseException, SyntaxException :
+{
+  int start, end = -1;
+}
+{
+  start = day(usedTokens) [ "-" end = day(usedTokens) ]
+  {
+    if(end < 0)
+    	end = start;
+    else if(end < start)
+    	throw new SyntaxException(null, usedTokens.lastElement().beginColumn - 1, usedTokens.lastElement().endColumn + 1);
+    
+    return new DaySpan(start, end);
+  }
+}
+// returns the weekday as integer
+int day(Vector<Token> usedTokens) throws ParseException, SyntaxException :
+{
+  Token t;
+}
+{
+  t = <WEEKDAY>
+  {
+    usedTokens.add(t);
+    for (int i = 0; i < WEEKDAYS.length; ++i)
+      if (WEEKDAYS[i].equals(t.image))
+    	return i;
+    	
+    throw new SyntaxException(null, t.beginColumn - 1, t.endColumn + 1);
+  }
+}
Index: applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/OpeningTimeCompilerConstants.java
===================================================================
--- applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/OpeningTimeCompilerConstants.java	(revision 22751)
+++ applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/OpeningTimeCompilerConstants.java	(revision 22751)
@@ -0,0 +1,40 @@
+/* Generated By:JavaCC: Do not edit this line. OpeningTimeCompilerConstants.java */
+package org.openstreetmap.josm.plugins.ohe.parser;
+
+
+/**
+ * Token literal values and constants.
+ * Generated by org.javacc.parser.OtherFilesGen#start()
+ */
+public interface OpeningTimeCompilerConstants {
+
+  /** End of File. */
+  int EOF = 0;
+  /** RegularExpression Id. */
+  int DIGIT = 1;
+  /** RegularExpression Id. */
+  int WEEKDAY = 2;
+  /** RegularExpression Id. */
+  int PLUS = 3;
+  /** RegularExpression Id. */
+  int OFF = 4;
+
+  /** Lexical state. */
+  int DEFAULT = 0;
+
+  /** Literal token values. */
+  String[] tokenImage = {
+    "<EOF>",
+    "<DIGIT>",
+    "<WEEKDAY>",
+    "\"+\"",
+    "\"off\"",
+    "\"24/7\"",
+    "\"; \"",
+    "\" \"",
+    "\",\"",
+    "\"-\"",
+    "\":\"",
+  };
+
+}
Index: applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/OpeningTimeCompilerTokenManager.java
===================================================================
--- applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/OpeningTimeCompilerTokenManager.java	(revision 22751)
+++ applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/OpeningTimeCompilerTokenManager.java	(revision 22751)
@@ -0,0 +1,436 @@
+/* Generated By:JavaCC: Do not edit this line. OpeningTimeCompilerTokenManager.java */
+package org.openstreetmap.josm.plugins.ohe.parser;
+import java.io.ByteArrayInputStream;
+import java.util.ArrayList;
+import java.util.Vector;
+import org.openstreetmap.josm.plugins.ohe.OpeningTimeUtils.DaySpan;
+import org.openstreetmap.josm.plugins.ohe.OpeningTimeUtils.DaytimeSpan;
+import org.openstreetmap.josm.plugins.ohe.OpeningTimeUtils.DateTime;
+
+/** Token Manager. */
+public class OpeningTimeCompilerTokenManager implements OpeningTimeCompilerConstants
+{
+
+  /** Debug output. */
+  public  java.io.PrintStream debugStream = System.out;
+  /** Set debug output. */
+  public  void setDebugStream(java.io.PrintStream ds) { debugStream = ds; }
+private final int jjStopStringLiteralDfa_0(int pos, long active0)
+{
+   switch (pos)
+   {
+      case 0:
+         if ((active0 & 0x20L) != 0L)
+         {
+            jjmatchedKind = 1;
+            return -1;
+         }
+         return -1;
+      case 1:
+         if ((active0 & 0x20L) != 0L)
+         {
+            if (jjmatchedPos == 0)
+            {
+               jjmatchedKind = 1;
+               jjmatchedPos = 0;
+            }
+            return -1;
+         }
+         return -1;
+      case 2:
+         if ((active0 & 0x20L) != 0L)
+         {
+            if (jjmatchedPos == 0)
+            {
+               jjmatchedKind = 1;
+               jjmatchedPos = 0;
+            }
+            return -1;
+         }
+         return -1;
+      default :
+         return -1;
+   }
+}
+private final int jjStartNfa_0(int pos, long active0)
+{
+   return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0), pos + 1);
+}
+private int jjStopAtPos(int pos, int kind)
+{
+   jjmatchedKind = kind;
+   jjmatchedPos = pos;
+   return pos + 1;
+}
+private int jjMoveStringLiteralDfa0_0()
+{
+   switch(curChar)
+   {
+      case 32:
+         return jjStopAtPos(0, 7);
+      case 43:
+         return jjStopAtPos(0, 3);
+      case 44:
+         return jjStopAtPos(0, 8);
+      case 45:
+         return jjStopAtPos(0, 9);
+      case 50:
+         return jjMoveStringLiteralDfa1_0(0x20L);
+      case 58:
+         return jjStopAtPos(0, 10);
+      case 59:
+         return jjMoveStringLiteralDfa1_0(0x40L);
+      case 111:
+         return jjMoveStringLiteralDfa1_0(0x10L);
+      default :
+         return jjMoveNfa_0(0, 0);
+   }
+}
+private int jjMoveStringLiteralDfa1_0(long active0)
+{
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(0, active0);
+      return 1;
+   }
+   switch(curChar)
+   {
+      case 32:
+         if ((active0 & 0x40L) != 0L)
+            return jjStopAtPos(1, 6);
+         break;
+      case 52:
+         return jjMoveStringLiteralDfa2_0(active0, 0x20L);
+      case 102:
+         return jjMoveStringLiteralDfa2_0(active0, 0x10L);
+      default :
+         break;
+   }
+   return jjStartNfa_0(0, active0);
+}
+private int jjMoveStringLiteralDfa2_0(long old0, long active0)
+{
+   if (((active0 &= old0)) == 0L)
+      return jjStartNfa_0(0, old0);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(1, active0);
+      return 2;
+   }
+   switch(curChar)
+   {
+      case 47:
+         return jjMoveStringLiteralDfa3_0(active0, 0x20L);
+      case 102:
+         if ((active0 & 0x10L) != 0L)
+            return jjStopAtPos(2, 4);
+         break;
+      default :
+         break;
+   }
+   return jjStartNfa_0(1, active0);
+}
+private int jjMoveStringLiteralDfa3_0(long old0, long active0)
+{
+   if (((active0 &= old0)) == 0L)
+      return jjStartNfa_0(1, old0);
+   try { curChar = input_stream.readChar(); }
+   catch(java.io.IOException e) {
+      jjStopStringLiteralDfa_0(2, active0);
+      return 3;
+   }
+   switch(curChar)
+   {
+      case 55:
+         if ((active0 & 0x20L) != 0L)
+            return jjStopAtPos(3, 5);
+         break;
+      default :
+         break;
+   }
+   return jjStartNfa_0(2, active0);
+}
+private int jjMoveNfa_0(int startState, int curPos)
+{
+   int startsAt = 0;
+   jjnewStateCnt = 12;
+   int i = 1;
+   jjstateSet[0] = startState;
+   int kind = 0x7fffffff;
+   for (;;)
+   {
+      if (++jjround == 0x7fffffff)
+         ReInitRounds();
+      if (curChar < 64)
+      {
+         long l = 1L << curChar;
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 0:
+                  if ((0x3ff000000000000L & l) != 0L)
+                     kind = 1;
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else if (curChar < 128)
+      {
+         long l = 1L << (curChar & 077);
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               case 0:
+                  if (curChar == 83)
+                     jjCheckNAddTwoStates(11, 8);
+                  else if (curChar == 84)
+                     jjCheckNAddTwoStates(8, 9);
+                  else if (curChar == 70)
+                     jjstateSet[jjnewStateCnt++] = 5;
+                  else if (curChar == 87)
+                     jjstateSet[jjnewStateCnt++] = 3;
+                  else if (curChar == 77)
+                     jjstateSet[jjnewStateCnt++] = 1;
+                  break;
+               case 1:
+                  if (curChar == 111)
+                     kind = 2;
+                  break;
+               case 2:
+                  if (curChar == 77)
+                     jjstateSet[jjnewStateCnt++] = 1;
+                  break;
+               case 3:
+                  if (curChar == 101)
+                     kind = 2;
+                  break;
+               case 4:
+                  if (curChar == 87)
+                     jjstateSet[jjnewStateCnt++] = 3;
+                  break;
+               case 5:
+                  if (curChar == 114)
+                     kind = 2;
+                  break;
+               case 6:
+                  if (curChar == 70)
+                     jjstateSet[jjnewStateCnt++] = 5;
+                  break;
+               case 7:
+                  if (curChar == 84)
+                     jjCheckNAddTwoStates(8, 9);
+                  break;
+               case 8:
+                  if (curChar == 117)
+                     kind = 2;
+                  break;
+               case 9:
+                  if (curChar == 104)
+                     kind = 2;
+                  break;
+               case 10:
+                  if (curChar == 83)
+                     jjCheckNAddTwoStates(11, 8);
+                  break;
+               case 11:
+                  if (curChar == 97)
+                     kind = 2;
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else
+      {
+         int i2 = (curChar & 0xff) >> 6;
+         long l2 = 1L << (curChar & 077);
+         do
+         {
+            switch(jjstateSet[--i])
+            {
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      if (kind != 0x7fffffff)
+      {
+         jjmatchedKind = kind;
+         jjmatchedPos = curPos;
+         kind = 0x7fffffff;
+      }
+      ++curPos;
+      if ((i = jjnewStateCnt) == (startsAt = 12 - (jjnewStateCnt = startsAt)))
+         return curPos;
+      try { curChar = input_stream.readChar(); }
+      catch(java.io.IOException e) { return curPos; }
+   }
+}
+static final int[] jjnextStates = {
+};
+
+/** Token literal values. */
+public static final String[] jjstrLiteralImages = {
+"", null, null, "\53", "\157\146\146", "\62\64\57\67", "\73\40", "\40", "\54", 
+"\55", "\72", };
+
+/** Lexer state names. */
+public static final String[] lexStateNames = {
+   "DEFAULT",
+};
+protected SimpleCharStream input_stream;
+private final int[] jjrounds = new int[12];
+private final int[] jjstateSet = new int[24];
+protected char curChar;
+/** Constructor. */
+public OpeningTimeCompilerTokenManager(SimpleCharStream stream){
+   if (SimpleCharStream.staticFlag)
+      throw new Error("ERROR: Cannot use a static CharStream class with a non-static lexical analyzer.");
+   input_stream = stream;
+}
+
+/** Constructor. */
+public OpeningTimeCompilerTokenManager(SimpleCharStream stream, int lexState){
+   this(stream);
+   SwitchTo(lexState);
+}
+
+/** Reinitialise parser. */
+public void ReInit(SimpleCharStream stream)
+{
+   jjmatchedPos = jjnewStateCnt = 0;
+   curLexState = defaultLexState;
+   input_stream = stream;
+   ReInitRounds();
+}
+private void ReInitRounds()
+{
+   int i;
+   jjround = 0x80000001;
+   for (i = 12; i-- > 0;)
+      jjrounds[i] = 0x80000000;
+}
+
+/** Reinitialise parser. */
+public void ReInit(SimpleCharStream stream, int lexState)
+{
+   ReInit(stream);
+   SwitchTo(lexState);
+}
+
+/** Switch to specified lex state. */
+public void SwitchTo(int lexState)
+{
+   if (lexState >= 1 || lexState < 0)
+      throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", TokenMgrError.INVALID_LEXICAL_STATE);
+   else
+      curLexState = lexState;
+}
+
+protected Token jjFillToken()
+{
+   final Token t;
+   final String curTokenImage;
+   final int beginLine;
+   final int endLine;
+   final int beginColumn;
+   final int endColumn;
+   String im = jjstrLiteralImages[jjmatchedKind];
+   curTokenImage = (im == null) ? input_stream.GetImage() : im;
+   beginLine = input_stream.getBeginLine();
+   beginColumn = input_stream.getBeginColumn();
+   endLine = input_stream.getEndLine();
+   endColumn = input_stream.getEndColumn();
+   t = Token.newToken(jjmatchedKind, curTokenImage);
+
+   t.beginLine = beginLine;
+   t.endLine = endLine;
+   t.beginColumn = beginColumn;
+   t.endColumn = endColumn;
+
+   return t;
+}
+
+int curLexState = 0;
+int defaultLexState = 0;
+int jjnewStateCnt;
+int jjround;
+int jjmatchedPos;
+int jjmatchedKind;
+
+/** Get the next Token. */
+public Token getNextToken() 
+{
+  Token matchedToken;
+  int curPos = 0;
+
+  EOFLoop :
+  for (;;)
+  {
+   try
+   {
+      curChar = input_stream.BeginToken();
+   }
+   catch(java.io.IOException e)
+   {
+      jjmatchedKind = 0;
+      matchedToken = jjFillToken();
+      return matchedToken;
+   }
+
+   jjmatchedKind = 0x7fffffff;
+   jjmatchedPos = 0;
+   curPos = jjMoveStringLiteralDfa0_0();
+   if (jjmatchedKind != 0x7fffffff)
+   {
+      if (jjmatchedPos + 1 < curPos)
+         input_stream.backup(curPos - jjmatchedPos - 1);
+         matchedToken = jjFillToken();
+         return matchedToken;
+   }
+   int error_line = input_stream.getEndLine();
+   int error_column = input_stream.getEndColumn();
+   String error_after = null;
+   boolean EOFSeen = false;
+   try { input_stream.readChar(); input_stream.backup(1); }
+   catch (java.io.IOException e1) {
+      EOFSeen = true;
+      error_after = curPos <= 1 ? "" : input_stream.GetImage();
+      if (curChar == '\n' || curChar == '\r') {
+         error_line++;
+         error_column = 0;
+      }
+      else
+         error_column++;
+   }
+   if (!EOFSeen) {
+      input_stream.backup(1);
+      error_after = curPos <= 1 ? "" : input_stream.GetImage();
+   }
+   throw new TokenMgrError(EOFSeen, curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LEXICAL_ERROR);
+  }
+}
+
+private void jjCheckNAdd(int state)
+{
+   if (jjrounds[state] != jjround)
+   {
+      jjstateSet[jjnewStateCnt++] = state;
+      jjrounds[state] = jjround;
+   }
+}
+private void jjAddStates(int start, int end)
+{
+   do {
+      jjstateSet[jjnewStateCnt++] = jjnextStates[start];
+   } while (start++ != end);
+}
+private void jjCheckNAddTwoStates(int state1, int state2)
+{
+   jjCheckNAdd(state1);
+   jjCheckNAdd(state2);
+}
+
+}
Index: applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/ParseException.java
===================================================================
--- applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/ParseException.java	(revision 22751)
+++ applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/ParseException.java	(revision 22751)
@@ -0,0 +1,187 @@
+/* Generated By:JavaCC: Do not edit this line. ParseException.java Version 5.0 */
+/* JavaCCOptions:KEEP_LINE_COL=null */
+package org.openstreetmap.josm.plugins.ohe.parser;
+
+/**
+ * This exception is thrown when parse errors are encountered.
+ * You can explicitly create objects of this exception type by
+ * calling the method generateParseException in the generated
+ * parser.
+ *
+ * You can modify this class to customize your error reporting
+ * mechanisms so long as you retain the public fields.
+ */
+public class ParseException extends Exception {
+
+  /**
+   * The version identifier for this Serializable class.
+   * Increment only if the <i>serialized</i> form of the
+   * class changes.
+   */
+  private static final long serialVersionUID = 1L;
+
+  /**
+   * This constructor is used by the method "generateParseException"
+   * in the generated parser.  Calling this constructor generates
+   * a new object of this type with the fields "currentToken",
+   * "expectedTokenSequences", and "tokenImage" set.
+   */
+  public ParseException(Token currentTokenVal,
+                        int[][] expectedTokenSequencesVal,
+                        String[] tokenImageVal
+                       )
+  {
+    super(initialise(currentTokenVal, expectedTokenSequencesVal, tokenImageVal));
+    currentToken = currentTokenVal;
+    expectedTokenSequences = expectedTokenSequencesVal;
+    tokenImage = tokenImageVal;
+  }
+
+  /**
+   * The following constructors are for use by you for whatever
+   * purpose you can think of.  Constructing the exception in this
+   * manner makes the exception behave in the normal way - i.e., as
+   * documented in the class "Throwable".  The fields "errorToken",
+   * "expectedTokenSequences", and "tokenImage" do not contain
+   * relevant information.  The JavaCC generated code does not use
+   * these constructors.
+   */
+
+  public ParseException() {
+    super();
+  }
+
+  /** Constructor with message. */
+  public ParseException(String message) {
+    super(message);
+  }
+
+
+  /**
+   * This is the last token that has been consumed successfully.  If
+   * this object has been created due to a parse error, the token
+   * followng this token will (therefore) be the first error token.
+   */
+  public Token currentToken;
+
+  /**
+   * Each entry in this array is an array of integers.  Each array
+   * of integers represents a sequence of tokens (by their ordinal
+   * values) that is expected at this point of the parse.
+   */
+  public int[][] expectedTokenSequences;
+
+  /**
+   * This is a reference to the "tokenImage" array of the generated
+   * parser within which the parse error occurred.  This array is
+   * defined in the generated ...Constants interface.
+   */
+  public String[] tokenImage;
+
+  /**
+   * It uses "currentToken" and "expectedTokenSequences" to generate a parse
+   * error message and returns it.  If this object has been created
+   * due to a parse error, and you do not catch it (it gets thrown
+   * from the parser) the correct error message
+   * gets displayed.
+   */
+  private static String initialise(Token currentToken,
+                           int[][] expectedTokenSequences,
+                           String[] tokenImage) {
+    String eol = System.getProperty("line.separator", "\n");
+    StringBuffer expected = new StringBuffer();
+    int maxSize = 0;
+    for (int i = 0; i < expectedTokenSequences.length; i++) {
+      if (maxSize < expectedTokenSequences[i].length) {
+        maxSize = expectedTokenSequences[i].length;
+      }
+      for (int j = 0; j < expectedTokenSequences[i].length; j++) {
+        expected.append(tokenImage[expectedTokenSequences[i][j]]).append(' ');
+      }
+      if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) {
+        expected.append("...");
+      }
+      expected.append(eol).append("    ");
+    }
+    String retval = "Encountered \"";
+    Token tok = currentToken.next;
+    for (int i = 0; i < maxSize; i++) {
+      if (i != 0) retval += " ";
+      if (tok.kind == 0) {
+        retval += tokenImage[0];
+        break;
+      }
+      retval += " " + tokenImage[tok.kind];
+      retval += " \"";
+      retval += add_escapes(tok.image);
+      retval += " \"";
+      tok = tok.next;
+    }
+    retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn;
+    retval += "." + eol;
+    if (expectedTokenSequences.length == 1) {
+      retval += "Was expecting:" + eol + "    ";
+    } else {
+      retval += "Was expecting one of:" + eol + "    ";
+    }
+    retval += expected.toString();
+    return retval;
+  }
+
+  /**
+   * The end of line string for this machine.
+   */
+  protected String eol = System.getProperty("line.separator", "\n");
+
+  /**
+   * Used to convert raw characters to their escaped version
+   * when these raw version cannot be used as part of an ASCII
+   * string literal.
+   */
+  static String add_escapes(String str) {
+      StringBuffer retval = new StringBuffer();
+      char ch;
+      for (int i = 0; i < str.length(); i++) {
+        switch (str.charAt(i))
+        {
+           case 0 :
+              continue;
+           case '\b':
+              retval.append("\\b");
+              continue;
+           case '\t':
+              retval.append("\\t");
+              continue;
+           case '\n':
+              retval.append("\\n");
+              continue;
+           case '\f':
+              retval.append("\\f");
+              continue;
+           case '\r':
+              retval.append("\\r");
+              continue;
+           case '\"':
+              retval.append("\\\"");
+              continue;
+           case '\'':
+              retval.append("\\\'");
+              continue;
+           case '\\':
+              retval.append("\\\\");
+              continue;
+           default:
+              if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+                 String s = "0000" + Integer.toString(ch, 16);
+                 retval.append("\\u" + s.substring(s.length() - 4, s.length()));
+              } else {
+                 retval.append(ch);
+              }
+              continue;
+        }
+      }
+      return retval.toString();
+   }
+
+}
+/* JavaCC - OriginalChecksum=2f50c922ea4e67dd4db32c2a385c2943 (do not edit this line) */
Index: applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/SimpleCharStream.java
===================================================================
--- applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/SimpleCharStream.java	(revision 22751)
+++ applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/SimpleCharStream.java	(revision 22751)
@@ -0,0 +1,471 @@
+/* Generated By:JavaCC: Do not edit this line. SimpleCharStream.java Version 5.0 */
+/* JavaCCOptions:STATIC=false,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
+package org.openstreetmap.josm.plugins.ohe.parser;
+
+/**
+ * An implementation of interface CharStream, where the stream is assumed to
+ * contain only ASCII characters (without unicode processing).
+ */
+
+public class SimpleCharStream
+{
+/** Whether parser is static. */
+  public static final boolean staticFlag = false;
+  int bufsize;
+  int available;
+  int tokenBegin;
+/** Position in buffer. */
+  public int bufpos = -1;
+  protected int bufline[];
+  protected int bufcolumn[];
+
+  protected int column = 0;
+  protected int line = 1;
+
+  protected boolean prevCharIsCR = false;
+  protected boolean prevCharIsLF = false;
+
+  protected java.io.Reader inputStream;
+
+  protected char[] buffer;
+  protected int maxNextCharInd = 0;
+  protected int inBuf = 0;
+  protected int tabSize = 8;
+
+  protected void setTabSize(int i) { tabSize = i; }
+  protected int getTabSize(int i) { return tabSize; }
+
+
+  protected void ExpandBuff(boolean wrapAround)
+  {
+    char[] newbuffer = new char[bufsize + 2048];
+    int newbufline[] = new int[bufsize + 2048];
+    int newbufcolumn[] = new int[bufsize + 2048];
+
+    try
+    {
+      if (wrapAround)
+      {
+        System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+        System.arraycopy(buffer, 0, newbuffer, bufsize - tokenBegin, bufpos);
+        buffer = newbuffer;
+
+        System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+        System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos);
+        bufline = newbufline;
+
+        System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+        System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos);
+        bufcolumn = newbufcolumn;
+
+        maxNextCharInd = (bufpos += (bufsize - tokenBegin));
+      }
+      else
+      {
+        System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+        buffer = newbuffer;
+
+        System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+        bufline = newbufline;
+
+        System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+        bufcolumn = newbufcolumn;
+
+        maxNextCharInd = (bufpos -= tokenBegin);
+      }
+    }
+    catch (Throwable t)
+    {
+      throw new Error(t.getMessage());
+    }
+
+
+    bufsize += 2048;
+    available = bufsize;
+    tokenBegin = 0;
+  }
+
+  protected void FillBuff() throws java.io.IOException
+  {
+    if (maxNextCharInd == available)
+    {
+      if (available == bufsize)
+      {
+        if (tokenBegin > 2048)
+        {
+          bufpos = maxNextCharInd = 0;
+          available = tokenBegin;
+        }
+        else if (tokenBegin < 0)
+          bufpos = maxNextCharInd = 0;
+        else
+          ExpandBuff(false);
+      }
+      else if (available > tokenBegin)
+        available = bufsize;
+      else if ((tokenBegin - available) < 2048)
+        ExpandBuff(true);
+      else
+        available = tokenBegin;
+    }
+
+    int i;
+    try {
+      if ((i = inputStream.read(buffer, maxNextCharInd, available - maxNextCharInd)) == -1)
+      {
+        inputStream.close();
+        throw new java.io.IOException();
+      }
+      else
+        maxNextCharInd += i;
+      return;
+    }
+    catch(java.io.IOException e) {
+      --bufpos;
+      backup(0);
+      if (tokenBegin == -1)
+        tokenBegin = bufpos;
+      throw e;
+    }
+  }
+
+/** Start. */
+  public char BeginToken() throws java.io.IOException
+  {
+    tokenBegin = -1;
+    char c = readChar();
+    tokenBegin = bufpos;
+
+    return c;
+  }
+
+  protected void UpdateLineColumn(char c)
+  {
+    column++;
+
+    if (prevCharIsLF)
+    {
+      prevCharIsLF = false;
+      line += (column = 1);
+    }
+    else if (prevCharIsCR)
+    {
+      prevCharIsCR = false;
+      if (c == '\n')
+      {
+        prevCharIsLF = true;
+      }
+      else
+        line += (column = 1);
+    }
+
+    switch (c)
+    {
+      case '\r' :
+        prevCharIsCR = true;
+        break;
+      case '\n' :
+        prevCharIsLF = true;
+        break;
+      case '\t' :
+        column--;
+        column += (tabSize - (column % tabSize));
+        break;
+      default :
+        break;
+    }
+
+    bufline[bufpos] = line;
+    bufcolumn[bufpos] = column;
+  }
+
+/** Read a character. */
+  public char readChar() throws java.io.IOException
+  {
+    if (inBuf > 0)
+    {
+      --inBuf;
+
+      if (++bufpos == bufsize)
+        bufpos = 0;
+
+      return buffer[bufpos];
+    }
+
+    if (++bufpos >= maxNextCharInd)
+      FillBuff();
+
+    char c = buffer[bufpos];
+
+    UpdateLineColumn(c);
+    return c;
+  }
+
+  @Deprecated
+  /**
+   * @deprecated
+   * @see #getEndColumn
+   */
+
+  public int getColumn() {
+    return bufcolumn[bufpos];
+  }
+
+  @Deprecated
+  /**
+   * @deprecated
+   * @see #getEndLine
+   */
+
+  public int getLine() {
+    return bufline[bufpos];
+  }
+
+  /** Get token end column number. */
+  public int getEndColumn() {
+    return bufcolumn[bufpos];
+  }
+
+  /** Get token end line number. */
+  public int getEndLine() {
+     return bufline[bufpos];
+  }
+
+  /** Get token beginning column number. */
+  public int getBeginColumn() {
+    return bufcolumn[tokenBegin];
+  }
+
+  /** Get token beginning line number. */
+  public int getBeginLine() {
+    return bufline[tokenBegin];
+  }
+
+/** Backup a number of characters. */
+  public void backup(int amount) {
+
+    inBuf += amount;
+    if ((bufpos -= amount) < 0)
+      bufpos += bufsize;
+  }
+
+  /** Constructor. */
+  public SimpleCharStream(java.io.Reader dstream, int startline,
+  int startcolumn, int buffersize)
+  {
+    inputStream = dstream;
+    line = startline;
+    column = startcolumn - 1;
+
+    available = bufsize = buffersize;
+    buffer = new char[buffersize];
+    bufline = new int[buffersize];
+    bufcolumn = new int[buffersize];
+  }
+
+  /** Constructor. */
+  public SimpleCharStream(java.io.Reader dstream, int startline,
+                          int startcolumn)
+  {
+    this(dstream, startline, startcolumn, 4096);
+  }
+
+  /** Constructor. */
+  public SimpleCharStream(java.io.Reader dstream)
+  {
+    this(dstream, 1, 1, 4096);
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.Reader dstream, int startline,
+  int startcolumn, int buffersize)
+  {
+    inputStream = dstream;
+    line = startline;
+    column = startcolumn - 1;
+
+    if (buffer == null || buffersize != buffer.length)
+    {
+      available = bufsize = buffersize;
+      buffer = new char[buffersize];
+      bufline = new int[buffersize];
+      bufcolumn = new int[buffersize];
+    }
+    prevCharIsLF = prevCharIsCR = false;
+    tokenBegin = inBuf = maxNextCharInd = 0;
+    bufpos = -1;
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.Reader dstream, int startline,
+                     int startcolumn)
+  {
+    ReInit(dstream, startline, startcolumn, 4096);
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.Reader dstream)
+  {
+    ReInit(dstream, 1, 1, 4096);
+  }
+  /** Constructor. */
+  public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline,
+  int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException
+  {
+    this(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize);
+  }
+
+  /** Constructor. */
+  public SimpleCharStream(java.io.InputStream dstream, int startline,
+  int startcolumn, int buffersize)
+  {
+    this(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
+  }
+
+  /** Constructor. */
+  public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline,
+                          int startcolumn) throws java.io.UnsupportedEncodingException
+  {
+    this(dstream, encoding, startline, startcolumn, 4096);
+  }
+
+  /** Constructor. */
+  public SimpleCharStream(java.io.InputStream dstream, int startline,
+                          int startcolumn)
+  {
+    this(dstream, startline, startcolumn, 4096);
+  }
+
+  /** Constructor. */
+  public SimpleCharStream(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException
+  {
+    this(dstream, encoding, 1, 1, 4096);
+  }
+
+  /** Constructor. */
+  public SimpleCharStream(java.io.InputStream dstream)
+  {
+    this(dstream, 1, 1, 4096);
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream dstream, String encoding, int startline,
+                          int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException
+  {
+    ReInit(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize);
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream dstream, int startline,
+                          int startcolumn, int buffersize)
+  {
+    ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException
+  {
+    ReInit(dstream, encoding, 1, 1, 4096);
+  }
+
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream dstream)
+  {
+    ReInit(dstream, 1, 1, 4096);
+  }
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream dstream, String encoding, int startline,
+                     int startcolumn) throws java.io.UnsupportedEncodingException
+  {
+    ReInit(dstream, encoding, startline, startcolumn, 4096);
+  }
+  /** Reinitialise. */
+  public void ReInit(java.io.InputStream dstream, int startline,
+                     int startcolumn)
+  {
+    ReInit(dstream, startline, startcolumn, 4096);
+  }
+  /** Get token literal value. */
+  public String GetImage()
+  {
+    if (bufpos >= tokenBegin)
+      return new String(buffer, tokenBegin, bufpos - tokenBegin + 1);
+    else
+      return new String(buffer, tokenBegin, bufsize - tokenBegin) +
+                            new String(buffer, 0, bufpos + 1);
+  }
+
+  /** Get the suffix. */
+  public char[] GetSuffix(int len)
+  {
+    char[] ret = new char[len];
+
+    if ((bufpos + 1) >= len)
+      System.arraycopy(buffer, bufpos - len + 1, ret, 0, len);
+    else
+    {
+      System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0,
+                                                        len - bufpos - 1);
+      System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1);
+    }
+
+    return ret;
+  }
+
+  /** Reset buffer when finished. */
+  public void Done()
+  {
+    buffer = null;
+    bufline = null;
+    bufcolumn = null;
+  }
+
+  /**
+   * Method to adjust line and column numbers for the start of a token.
+   */
+  public void adjustBeginLineColumn(int newLine, int newCol)
+  {
+    int start = tokenBegin;
+    int len;
+
+    if (bufpos >= tokenBegin)
+    {
+      len = bufpos - tokenBegin + inBuf + 1;
+    }
+    else
+    {
+      len = bufsize - tokenBegin + bufpos + 1 + inBuf;
+    }
+
+    int i = 0, j = 0, k = 0;
+    int nextColDiff = 0, columnDiff = 0;
+
+    while (i < len && bufline[j = start % bufsize] == bufline[k = ++start % bufsize])
+    {
+      bufline[j] = newLine;
+      nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j];
+      bufcolumn[j] = newCol + columnDiff;
+      columnDiff = nextColDiff;
+      i++;
+    }
+
+    if (i < len)
+    {
+      bufline[j] = newLine++;
+      bufcolumn[j] = newCol + columnDiff;
+
+      while (i++ < len)
+      {
+        if (bufline[j = start % bufsize] != bufline[++start % bufsize])
+          bufline[j] = newLine++;
+        else
+          bufline[j] = newLine;
+      }
+    }
+
+    line = bufline[j];
+    column = bufcolumn[j];
+  }
+
+}
+/* JavaCC - OriginalChecksum=2b00b69ffbba5b2196b1ed036fcc5142 (do not edit this line) */
Index: applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/SyntaxException.java
===================================================================
--- applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/SyntaxException.java	(revision 22751)
+++ applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/SyntaxException.java	(revision 22751)
@@ -0,0 +1,26 @@
+package org.openstreetmap.josm.plugins.ohe.parser;
+
+public class SyntaxException extends Exception {
+
+	private int startColumn;
+	private int endColumn;
+	private String info;
+
+	public int getStartColumn() {
+		return startColumn;
+	}
+
+	public int getEndColumn() {
+		return endColumn;
+	}
+
+	public String getInfo() {
+		return info;
+	}
+
+	public SyntaxException(String info, int startColumn, int endColumn) {
+		this.startColumn = startColumn;
+		this.endColumn = endColumn;
+		this.info = info;
+	}
+}
Index: applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/Token.java
===================================================================
--- applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/Token.java	(revision 22751)
+++ applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/Token.java	(revision 22751)
@@ -0,0 +1,131 @@
+/* Generated By:JavaCC: Do not edit this line. Token.java Version 5.0 */
+/* JavaCCOptions:TOKEN_EXTENDS=,KEEP_LINE_COL=null,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
+package org.openstreetmap.josm.plugins.ohe.parser;
+
+/**
+ * Describes the input token stream.
+ */
+
+public class Token implements java.io.Serializable {
+
+  /**
+   * The version identifier for this Serializable class.
+   * Increment only if the <i>serialized</i> form of the
+   * class changes.
+   */
+  private static final long serialVersionUID = 1L;
+
+  /**
+   * An integer that describes the kind of this token.  This numbering
+   * system is determined by JavaCCParser, and a table of these numbers is
+   * stored in the file ...Constants.java.
+   */
+  public int kind;
+
+  /** The line number of the first character of this Token. */
+  public int beginLine;
+  /** The column number of the first character of this Token. */
+  public int beginColumn;
+  /** The line number of the last character of this Token. */
+  public int endLine;
+  /** The column number of the last character of this Token. */
+  public int endColumn;
+
+  /**
+   * The string image of the token.
+   */
+  public String image;
+
+  /**
+   * A reference to the next regular (non-special) token from the input
+   * stream.  If this is the last token from the input stream, or if the
+   * token manager has not read tokens beyond this one, this field is
+   * set to null.  This is true only if this token is also a regular
+   * token.  Otherwise, see below for a description of the contents of
+   * this field.
+   */
+  public Token next;
+
+  /**
+   * This field is used to access special tokens that occur prior to this
+   * token, but after the immediately preceding regular (non-special) token.
+   * If there are no such special tokens, this field is set to null.
+   * When there are more than one such special token, this field refers
+   * to the last of these special tokens, which in turn refers to the next
+   * previous special token through its specialToken field, and so on
+   * until the first special token (whose specialToken field is null).
+   * The next fields of special tokens refer to other special tokens that
+   * immediately follow it (without an intervening regular token).  If there
+   * is no such token, this field is null.
+   */
+  public Token specialToken;
+
+  /**
+   * An optional attribute value of the Token.
+   * Tokens which are not used as syntactic sugar will often contain
+   * meaningful values that will be used later on by the compiler or
+   * interpreter. This attribute value is often different from the image.
+   * Any subclass of Token that actually wants to return a non-null value can
+   * override this method as appropriate.
+   */
+  public Object getValue() {
+    return null;
+  }
+
+  /**
+   * No-argument constructor
+   */
+  public Token() {}
+
+  /**
+   * Constructs a new token for the specified Image.
+   */
+  public Token(int kind)
+  {
+    this(kind, null);
+  }
+
+  /**
+   * Constructs a new token for the specified Image and Kind.
+   */
+  public Token(int kind, String image)
+  {
+    this.kind = kind;
+    this.image = image;
+  }
+
+  /**
+   * Returns the image.
+   */
+  public String toString()
+  {
+    return image;
+  }
+
+  /**
+   * Returns a new Token object, by default. However, if you want, you
+   * can create and return subclass objects based on the value of ofKind.
+   * Simply add the cases to the switch for all those special cases.
+   * For example, if you have a subclass of Token called IDToken that
+   * you want to create if ofKind is ID, simply add something like :
+   *
+   *    case MyParserConstants.ID : return new IDToken(ofKind, image);
+   *
+   * to the following switch statement. Then you can cast matchedToken
+   * variable to the appropriate type and use sit in your lexical actions.
+   */
+  public static Token newToken(int ofKind, String image)
+  {
+    switch(ofKind)
+    {
+      default : return new Token(ofKind, image);
+    }
+  }
+
+  public static Token newToken(int ofKind)
+  {
+    return newToken(ofKind, null);
+  }
+
+}
+/* JavaCC - OriginalChecksum=3ed864437bda9f3d1e5a849df934553d (do not edit this line) */
Index: applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/TokenMgrError.java
===================================================================
--- applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/TokenMgrError.java	(revision 22751)
+++ applications/editors/josm/plugins/OpeningHoursEditor/src/org/openstreetmap/josm/plugins/ohe/parser/TokenMgrError.java	(revision 22751)
@@ -0,0 +1,169 @@
+/* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 5.0 */
+/* JavaCCOptions: */
+package org.openstreetmap.josm.plugins.ohe.parser;
+
+/** Token Manager Error. */
+public class TokenMgrError extends Error {
+
+	/**
+	 * The version identifier for this Serializable class. Increment only if the
+	 * <i>serialized</i> form of the class changes.
+	 */
+	private static final long serialVersionUID = 1L;
+
+	/*
+	 * Ordinals for various reasons why an Error of this type can be thrown.
+	 */
+
+	/**
+	 * Lexical error occurred.
+	 */
+	static final int LEXICAL_ERROR = 0;
+
+	/**
+	 * An attempt was made to create a second instance of a static token
+	 * manager.
+	 */
+	static final int STATIC_LEXER_ERROR = 1;
+
+	/**
+	 * Tried to change to an invalid lexical state.
+	 */
+	static final int INVALID_LEXICAL_STATE = 2;
+
+	/**
+	 * Detected (and bailed out of) an infinite loop in the token manager.
+	 */
+	static final int LOOP_DETECTED = 3;
+
+	/**
+	 * Indicates the reason why the exception is thrown. It will have one of the
+	 * above 4 values.
+	 */
+	int errorCode;
+
+	/**
+	 * Replaces unprintable characters by their escaped (or unicode escaped)
+	 * equivalents in the given string
+	 */
+	protected static final String addEscapes(String str) {
+		StringBuffer retval = new StringBuffer();
+		char ch;
+		for (int i = 0; i < str.length(); i++) {
+			switch (str.charAt(i)) {
+			case 0:
+				continue;
+			case '\b':
+				retval.append("\\b");
+				continue;
+			case '\t':
+				retval.append("\\t");
+				continue;
+			case '\n':
+				retval.append("\\n");
+				continue;
+			case '\f':
+				retval.append("\\f");
+				continue;
+			case '\r':
+				retval.append("\\r");
+				continue;
+			case '\"':
+				retval.append("\\\"");
+				continue;
+			case '\'':
+				retval.append("\\\'");
+				continue;
+			case '\\':
+				retval.append("\\\\");
+				continue;
+			default:
+				if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+					String s = "0000" + Integer.toString(ch, 16);
+					retval.append("\\u"
+							+ s.substring(s.length() - 4, s.length()));
+				} else {
+					retval.append(ch);
+				}
+				continue;
+			}
+		}
+		return retval.toString();
+	}
+
+	/**
+	 * Returns a detailed message for the Error when it is thrown by the token
+	 * manager to indicate a lexical error. Parameters : EOFSeen : indicates if
+	 * EOF caused the lexical error curLexState : lexical state in which this
+	 * error occurred errorLine : line number when the error occurred
+	 * errorColumn : column number when the error occurred errorAfter : prefix
+	 * that was seen before this error occurred curchar : the offending
+	 * character Note: You can customize the lexical error message by modifying
+	 * this method.
+	 */
+	protected static String LexicalError(boolean EOFSeen, int lexState,
+			int errorLine, int errorColumn, String errorAfter, char curChar) {
+		return ("Lexical error at line "
+				+ errorLine
+				+ ", column "
+				+ errorColumn
+				+ ".  Encountered: "
+				+ (EOFSeen ? "<EOF> " : ("\""
+						+ addEscapes(String.valueOf(curChar)) + "\"")
+						+ " (" + (int) curChar + "), ") + "after : \""
+				+ addEscapes(errorAfter) + "\"");
+	}
+
+	/**
+	 * You can also modify the body of this method to customize your error
+	 * messages. For example, cases like LOOP_DETECTED and INVALID_LEXICAL_STATE
+	 * are not of end-users concern, so you can return something like :
+	 *
+	 * "Internal Error : Please file a bug report .... "
+	 *
+	 * from this method for such cases in the release version of your parser.
+	 */
+	@Override
+	public String getMessage() {
+		return super.getMessage();
+	}
+
+	/*
+	 * Constructors of various flavors follow.
+	 */
+
+	/** No arg constructor. */
+	public TokenMgrError() {
+	}
+
+	/** Constructor with message and reason. */
+	public TokenMgrError(String message, int reason) {
+		super(message);
+		errorCode = reason;
+	}
+
+	public boolean EOFSeen;
+	public int lexState;
+	public int errorLine;
+	public int errorColumn;
+	public String errorAfter;
+	public char curChar;
+	public int reason;
+
+	/** Full Constructor. */
+	public TokenMgrError(boolean EOFSeen, int lexState, int errorLine,
+			int errorColumn, String errorAfter, char curChar, int reason) {
+		this.EOFSeen = EOFSeen;
+		this.lexState = lexState;
+		this.errorLine = errorLine;
+		this.errorColumn = errorColumn;
+		this.errorAfter = errorAfter;
+		this.curChar = curChar;
+		this.reason = reason;
+	}
+}
+/*
+ * JavaCC - OriginalChecksum=94ad59ac464bcd0cb1ba18e32211662a (do not edit this
+ * line)
+ */
+
