Index: applications/editors/josm/plugins/reverter/.classpath
===================================================================
--- applications/editors/josm/plugins/reverter/.classpath	(revision 21201)
+++ applications/editors/josm/plugins/reverter/.classpath	(revision 21201)
@@ -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"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/JOSM"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
Index: applications/editors/josm/plugins/reverter/.project
===================================================================
--- applications/editors/josm/plugins/reverter/.project	(revision 21201)
+++ applications/editors/josm/plugins/reverter/.project	(revision 21201)
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>reverter</name>
+	<comment></comment>
+	<projects>
+		<project>josm</project>
+	</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/reverter/LICENSE
===================================================================
--- applications/editors/josm/plugins/reverter/LICENSE	(revision 21201)
+++ applications/editors/josm/plugins/reverter/LICENSE	(revision 21201)
@@ -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/reverter/README
===================================================================
--- applications/editors/josm/plugins/reverter/README	(revision 21201)
+++ applications/editors/josm/plugins/reverter/README	(revision 21201)
@@ -0,0 +1,4 @@
+README 
+======
+
+Readme for your plugin 
Index: applications/editors/josm/plugins/reverter/build.xml
===================================================================
--- applications/editors/josm/plugins/reverter/build.xml	(revision 21201)
+++ applications/editors/josm/plugins/reverter/build.xml	(revision 21201)
@@ -0,0 +1,256 @@
+<?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="reverter" default="dist" basedir=".">
+
+	<!-- enter the SVN commit message -->
+	<property name="commit.message" value="Add reverter plugin to SVN" />
+	<!-- enter the *lowest* JOSM version this plugin is currently compatible with -->
+	<property name="plugin.main.version" value="3000" />
+
+
+	<!--
+      ************************************************
+      ** should not be necessary to change the following properties
+     -->
+	<property name="josm"                   location="../../core/dist/josm-custom.jar"/>
+	<property name="plugin.build.dir"       value="build"/>
+	<property name="plugin.src.dir"         value="src"/>
+	<!-- this is the directory where the plugin jar is copied to -->
+	<property name="plugin.dist.dir"        value="../../dist"/>
+	<property name="ant.build.javac.target" value="1.5"/>
+	<property name="plugin.dist.dir"        value="../../dist"/>
+	<property name="plugin.jar"             value="${plugin.dist.dir}/${ant.project.name}.jar"/>
+
+	<!--
+    **********************************************************
+    ** 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}/resources">
+			<fileset dir="resources"/>
+		</copy>
+		<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="Upliner"/>
+				<attribute name="Plugin-Class" value="reverter.ReverterPlugin"/>
+				<attribute name="Plugin-Date" value="${version.entry.commit.date}"/>
+				<attribute name="Plugin-Description" value="Plugin for reverting changesets"/>
+				<attribute name="Plugin-Link" value="http://wiki.openstreetmap.org/wiki/JOSM/Plugins/Reverter"/>
+				<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="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/reverter/src/reverter/ChangesetIdQuery.java
===================================================================
--- applications/editors/josm/plugins/reverter/src/reverter/ChangesetIdQuery.java	(revision 21201)
+++ applications/editors/josm/plugins/reverter/src/reverter/ChangesetIdQuery.java	(revision 21201)
@@ -0,0 +1,39 @@
+package reverter;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.GridBagLayout;
+import java.text.NumberFormat;
+import java.text.ParseException;
+
+import javax.swing.JFormattedTextField;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.tools.GBC;
+
+@SuppressWarnings("serial")
+public class ChangesetIdQuery extends ExtendedDialog {
+    private JFormattedTextField tcid = new JFormattedTextField(NumberFormat.getInstance());
+
+    public int ChangesetId()
+    {
+        try {
+          return NumberFormat.getInstance().parse(tcid.getText()).intValue();
+        } catch (ParseException e) {            
+          return 0;
+        }
+    }
+    public ChangesetIdQuery() {
+        super(Main.parent, tr("Objects history"), new String[] {"Revert","Cancel"}, true);
+        contentConstraints = GBC.eol().fill().insets(10,10,10,5);
+        setButtonIcons(new String[] {"ok.png", "cancel.png" });
+        JPanel panel = new JPanel(new GridBagLayout());
+        panel.add(new JLabel(tr("Changeset id:")));
+        panel.add(tcid, GBC.eol().fill(GBC.HORIZONTAL));
+        setContent(panel);
+        setupDialog();        
+    }
+}
Index: applications/editors/josm/plugins/reverter/src/reverter/ChangesetReverter.java
===================================================================
--- applications/editors/josm/plugins/reverter/src/reverter/ChangesetReverter.java	(revision 21201)
+++ applications/editors/josm/plugins/reverter/src/reverter/ChangesetReverter.java	(revision 21201)
@@ -0,0 +1,131 @@
+package reverter;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.command.DeleteCommand;
+import org.openstreetmap.josm.data.osm.Changeset;
+import org.openstreetmap.josm.data.osm.ChangesetDataSet;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.PrimitiveId;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.ChangesetDataSet.ChangesetDataSetEntry;
+import org.openstreetmap.josm.data.osm.ChangesetDataSet.ChangesetModificationType;
+import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.io.OsmServerChangesetReader;
+import org.openstreetmap.josm.io.OsmTransferException;
+
+public class ChangesetReverter {
+    private int changesetId;
+    private Changeset changeset;
+    private ChangesetDataSet osmchange;
+    static class MyCsDataset {
+        public HashMap<PrimitiveId,Integer> created = new HashMap<PrimitiveId,Integer>();
+        public HashMap<PrimitiveId,Integer> updated = new HashMap<PrimitiveId,Integer>();
+        public HashMap<PrimitiveId,Integer> deleted = new HashMap<PrimitiveId,Integer>();
+
+        private static void put(HashMap<PrimitiveId,Integer> map,PrimitiveId id,int version) {
+            if (map.containsKey(id)) {
+                if (version < map.get(id))
+                    map.put(id, version);
+            } else {
+                map.put(id, version);
+            }
+        }
+        
+        private void addEntry(ChangesetDataSetEntry entry) {
+            HistoryOsmPrimitive t = entry.getPrimitive();
+            if (entry.getModificationType() == ChangesetModificationType.CREATED) {
+                put(created, new SimplePrimitiveId(t.getId(),t.getType()), (int)t.getVersion());
+            } else if (entry.getModificationType() == ChangesetModificationType.UPDATED) {
+                put(updated, new SimplePrimitiveId(t.getId(),t.getType()), (int)t.getVersion());
+            } else if (entry.getModificationType() == ChangesetModificationType.DELETED) {
+                put(deleted, new SimplePrimitiveId(t.getId(),t.getType()), (int)t.getVersion());
+            } else throw new AssertionError();
+        }
+        
+        public MyCsDataset(ChangesetDataSet ds) {
+            Iterator<ChangesetDataSetEntry> iterator = ds.iterator();
+            while (iterator.hasNext()) {
+                addEntry(iterator.next());
+            }
+        }
+    }
+    
+    public ChangesetReverter(int changesetId) {
+        this.changesetId = changesetId;
+        try {
+            OsmServerChangesetReader csr = new OsmServerChangesetReader();
+            changeset = csr.readChangeset(changesetId, NullProgressMonitor.INSTANCE);
+            osmchange = csr.downloadChangeset(changesetId, NullProgressMonitor.INSTANCE);
+        } catch (OsmTransferException e) {
+            e.printStackTrace();
+        }
+    }
+    LinkedList<Command> cmds;
+
+    public void RevertChangeset(ProgressMonitor progressMonitor) throws OsmTransferException {
+        final OsmDataLayer layer = Main.main.getEditLayer();
+        final DataSet ds = layer.data;
+        final MyCsDataset cds = new MyCsDataset(osmchange);
+        final OsmServerMultiObjectReader rdr = new OsmServerMultiObjectReader();
+        
+        progressMonitor.beginTask("",cds.updated.size()+cds.deleted.size()+2);
+        progressMonitor.worked(1);
+        try {
+            // Fetch objects that was updated or deleted by changeset
+            for (Map.Entry<PrimitiveId,Integer> entry : cds.updated.entrySet()) {
+                rdr.ReadObject(entry.getKey().getUniqueId(), entry.getValue()-1, entry.getKey().getType(),
+                        progressMonitor.createSubTaskMonitor(1, true));
+            }
+            for (Map.Entry<PrimitiveId,Integer> entry : cds.deleted.entrySet()) {
+                rdr.ReadObject(entry.getKey().getUniqueId(), entry.getValue()-1, entry.getKey().getType(),
+                        progressMonitor.createSubTaskMonitor(1, true));
+            }
+            final DataSet nds = rdr.parseOsm(progressMonitor.createSubTaskMonitor(1, true));
+            
+            // Mark objects that have visible=false to be deleted
+            LinkedList<OsmPrimitive> toDelete = new LinkedList<OsmPrimitive>();
+            for (OsmPrimitive p : nds.allPrimitives()) {
+                if (!p.isVisible()) {
+                    OsmPrimitive dp = ds.getPrimitiveById(p);
+                    if (dp != null) toDelete.add(dp);
+                }
+            }
+            
+            // Create commands to restore/update all affected objects 
+            this.cmds = new DataSetToCmd(nds,ds).getCommandList();
+            
+            // Mark all created objects to be deleted
+            for (PrimitiveId id : cds.created.keySet()) {
+                OsmPrimitive p = ds.getPrimitiveById(id);
+                if (p != null) toDelete.add(p);
+            }
+            // Create a Command to delete all marked objects
+            List<? extends OsmPrimitive> list;
+            list = OsmPrimitive.getFilteredList(toDelete, Relation.class);
+            if (!list.isEmpty()) cmds.add(new DeleteCommand(list));
+            list = OsmPrimitive.getFilteredList(toDelete, Way.class);
+            if (!list.isEmpty()) cmds.add(new DeleteCommand(list));
+            list = OsmPrimitive.getFilteredList(toDelete, Node.class);
+            if (!list.isEmpty()) cmds.add(new DeleteCommand(list));
+        } finally {
+            progressMonitor.finishTask();
+        }
+    }
+    public List<Command> getCommands() {
+        return cmds;
+    }
+}
Index: applications/editors/josm/plugins/reverter/src/reverter/DataSetToCmd.java
===================================================================
--- applications/editors/josm/plugins/reverter/src/reverter/DataSetToCmd.java	(revision 21201)
+++ applications/editors/josm/plugins/reverter/src/reverter/DataSetToCmd.java	(revision 21201)
@@ -0,0 +1,162 @@
+package reverter;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.openstreetmap.josm.command.AddCommand;
+import org.openstreetmap.josm.command.ChangeCommand;
+import org.openstreetmap.josm.command.ChangeNodesCommand;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.data.osm.*;
+
+final class DataSetToCmd {
+
+    /** the source dataset where primitives are merged from */
+    private final DataSet sourceDataSet;
+    private final DataSet targetDataSet;
+
+    /**
+     * A map of all primitives that got replaced with other primitives.
+     * Key is the PrimitiveId in their dataset, the value is the PrimitiveId in my dataset
+     */
+    private final Map<OsmPrimitive, OsmPrimitive> mergedMap = new HashMap<OsmPrimitive, OsmPrimitive>();
+    
+    private final LinkedList<Command> cmds = new LinkedList<Command>();
+
+    /**
+     * constructor
+     */
+    public DataSetToCmd(DataSet sourceDataSet, DataSet targetDataSet) {
+        this.sourceDataSet = sourceDataSet;
+        this.targetDataSet = targetDataSet;
+        merge();
+    }
+
+    /**
+     * Merges a primitive <code>other</code> of type <P> onto my primitives.
+     *
+     * @param <P>  the type of the other primitive
+     * @param source  the other primitive
+     */
+    private void mergePrimitive(OsmPrimitive source) {
+        if (source.isIncomplete()) return;
+        if (!source.isVisible()) return;
+        OsmPrimitive target = targetDataSet.getPrimitiveById(source.getId(), source.getType());
+        if (target == null) {
+            switch(source.getType()) {
+            case NODE: target = new Node(source.getId()); break;
+            case WAY: target = new Way(source.getId()); break;
+            case RELATION: target = new Relation(source.getId()); break;
+            default: throw new AssertionError();
+            }
+            target.mergeFrom(source);
+            target.setOsmId(target.getId(), (int)source.getVersion()+1);
+            target.setModified(true);
+            cmds.add(new AddCommand(target));
+        } else {
+            OsmPrimitive newTarget = null;
+            switch(target.getType()) {
+            case NODE: newTarget = new Node((Node)target); break;
+            case WAY: newTarget = new Way((Way)target); break;
+            case RELATION: newTarget = new Relation((Relation)target); break;
+            default: throw new AssertionError();
+            }
+            newTarget.mergeFrom(source);
+            newTarget.setDeleted(false);
+            cmds.add(new ChangeCommand(target,newTarget));
+        }
+        mergedMap.put(source, target);
+    }
+
+    private OsmPrimitive getMergeTarget(OsmPrimitive mergeSource) {
+        OsmPrimitive p = mergedMap.get(mergeSource);
+        if (p == null) p = targetDataSet.getPrimitiveById(mergeSource.getId(), mergeSource.getType());
+        return p;
+    }
+    
+    /**
+     * Postprocess the dataset and fix all merged references to point to the actual
+     * data.
+     */
+    public void fixReferences() {
+        for (Way w : sourceDataSet.getWays()) {
+                mergeNodeList(w);
+        }
+        for (Relation r : sourceDataSet.getRelations()) {
+                mergeRelationMembers(r);
+        }
+    }
+
+    /**
+     * Merges the node list of a source way onto its target way.
+     *
+     * @param source the source way
+     * @throws IllegalStateException thrown if no target way can be found for the source way
+     * @throws IllegalStateException thrown if there isn't a target node for one of the nodes in the source way
+     *
+     */
+    private void mergeNodeList(Way source) throws IllegalStateException {
+        if (source.isIncomplete()) return;
+        if (!source.isVisible()) return;
+        Way target = (Way)getMergeTarget(source);
+        if (target == null)
+            throw new IllegalStateException(tr("Missing merge target for way with id {0}", source.getUniqueId()));
+
+        List<Node> newNodes = new ArrayList<Node>(source.getNodesCount());
+        for (Node sourceNode : source.getNodes()) {
+            Node targetNode = (Node)getMergeTarget(sourceNode);
+            if (targetNode == null)
+                throw new IllegalStateException(tr("Missing merge target for node with id {0}", sourceNode.getUniqueId()));
+
+            newNodes.add(targetNode);
+        }
+        cmds.add(new ChangeNodesCommand(target,newNodes));
+    }
+
+    /**
+     * Merges the relation members of a source relation onto the corresponding target relation.
+     * @param source the source relation
+     * @throws IllegalStateException thrown if there is no corresponding target relation
+     * @throws IllegalStateException thrown if there isn't a corresponding target object for one of the relation
+     * members in source
+     */
+    private void mergeRelationMembers(Relation source) throws IllegalStateException {
+        if (source.isIncomplete()) return;
+        if (!source.isVisible()) return;
+        Relation target = (Relation) getMergeTarget(source);
+        if (target == null)
+            throw new IllegalStateException(tr("Missing merge target for relation with id {0}", source.getUniqueId()));
+        LinkedList<RelationMember> newMembers = new LinkedList<RelationMember>();
+        for (RelationMember sourceMember : source.getMembers()) {
+            OsmPrimitive targetMember = getMergeTarget(sourceMember.getMember());
+            if (targetMember == null)
+                throw new IllegalStateException(tr("Missing merge target of type {0} with id {1}", sourceMember.getType(), sourceMember.getUniqueId()));
+            newMembers.add(new RelationMember(sourceMember.getRole(), targetMember));
+        }
+        Relation newRelation = new Relation(target); 
+        newRelation.setMembers(newMembers);
+        cmds.add(new ChangeCommand(target,newRelation));
+    }
+    private void merge()
+    {
+        for (Node node: sourceDataSet.getNodes()) {
+            mergePrimitive(node);
+        }
+        for (Way way: sourceDataSet.getWays()) {
+            mergePrimitive(way);
+        }
+        for (Relation relation: sourceDataSet.getRelations()) {
+            mergePrimitive(relation);
+        }
+        fixReferences();
+    }
+
+    public LinkedList<Command> getCommandList() {
+        return cmds;
+    }
+}
Index: applications/editors/josm/plugins/reverter/src/reverter/ModifiyUploadOrderHook.java
===================================================================
--- applications/editors/josm/plugins/reverter/src/reverter/ModifiyUploadOrderHook.java	(revision 21201)
+++ applications/editors/josm/plugins/reverter/src/reverter/ModifiyUploadOrderHook.java	(revision 21201)
@@ -0,0 +1,49 @@
+// License: GPL. For details, see LICENSE file.
+package reverter;
+
+import java.util.Collections;
+import java.util.Comparator;
+
+import org.openstreetmap.josm.data.APIDataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.Way;
+
+import org.openstreetmap.josm.actions.upload.UploadHook;
+
+/**
+ * Sort modified objects before uploading in order: nodes, ways, relations
+ * It is needed because objects undeleted by reverter is marked as "modified",
+ * but in fact they're re-added to JOSM. Without this the "precondition failed"
+ * error appears when trying to upload objects undeleted by reverter.
+ * 
+ */
+public class ModifiyUploadOrderHook implements UploadHook {
+
+    public boolean checkUpload(APIDataSet apiDataSet) {
+        Collections.sort(
+                apiDataSet.getPrimitivesToUpdate(),
+                new Comparator<OsmPrimitive>() {
+                    public int compare(OsmPrimitive o1, OsmPrimitive o2) {
+                        if (o1 instanceof Node && o2 instanceof Node)
+                            return 0;
+                        else if (o1 instanceof Node)
+                            return -1;
+                        else if (o2 instanceof Node)
+                            return 1;
+
+                        if (o1 instanceof Way && o2 instanceof Way)
+                            return 0;
+                        else if (o1 instanceof Way && o2 instanceof Relation)
+                            return -1;
+                        else if (o2 instanceof Way && o1 instanceof Relation)
+                            return 1;
+
+                        return 0;
+                    }
+                }
+                );
+        return true;
+    }
+}
Index: applications/editors/josm/plugins/reverter/src/reverter/MultiOsmReader.java
===================================================================
--- applications/editors/josm/plugins/reverter/src/reverter/MultiOsmReader.java	(revision 21201)
+++ applications/editors/josm/plugins/reverter/src/reverter/MultiOsmReader.java	(revision 21201)
@@ -0,0 +1,566 @@
+package reverter;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.DataSource;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.NodeData;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.data.osm.PrimitiveData;
+import org.openstreetmap.josm.data.osm.PrimitiveId;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.RelationData;
+import org.openstreetmap.josm.data.osm.RelationMember;
+import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
+import org.openstreetmap.josm.data.osm.Storage;
+import org.openstreetmap.josm.data.osm.User;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.WayData;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.io.IllegalDataException;
+import org.openstreetmap.josm.io.OsmDataParsingException;
+import org.openstreetmap.josm.io.OsmReader;
+import org.openstreetmap.josm.tools.DateUtils;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * Parser for the Osm Api. Read from an input stream and construct a dataset out of it.
+ *
+ */
+public class MultiOsmReader {
+    static private final Logger logger = Logger.getLogger(OsmReader.class.getName());
+
+    /**
+     * The dataset to add parsed objects to.
+     */
+    private DataSet ds = new DataSet();
+
+    /**
+     * Replies the parsed data set
+     *
+     * @return the parsed data set
+     */
+    public DataSet getDataSet() {
+        return ds;
+    }
+
+    /** the map from external ids to read OsmPrimitives. External ids are
+     * longs too, but in contrast to internal ids negative values are used
+     * to identify primitives unknown to the OSM server
+     */
+    private Map<PrimitiveId, OsmPrimitive> externalIdMap = new HashMap<PrimitiveId, OsmPrimitive>();
+
+    /**
+     * constructor (for private use only)
+     *
+     * @see #parseDataSet(InputStream, DataSet, ProgressMonitor)
+     * @see #parseDataSetOsm(InputStream, DataSet, ProgressMonitor)
+     */
+    public MultiOsmReader() {
+        externalIdMap = new HashMap<PrimitiveId, OsmPrimitive>();
+    }
+
+    /**
+     * Used as a temporary storage for relation members, before they
+     * are resolved into pointers to real objects.
+     */
+    private static class RelationMemberData {
+        public OsmPrimitiveType type;
+        public long id;
+        public String role;
+    }
+
+    /**
+     * Data structure for the remaining way objects
+     */
+    private Map<Long, Collection<Long>> ways = new HashMap<Long, Collection<Long>>();
+
+    /**
+     * Data structure for relation objects
+     */
+    private Map<Long, Collection<RelationMemberData>> relations = new HashMap<Long, Collection<RelationMemberData>>();
+
+    private class Parser extends DefaultHandler {
+        private Locator locator;
+
+        @Override
+        public void setDocumentLocator(Locator locator) {
+            this.locator = locator;
+        }
+
+        protected void throwException(String msg) throws OsmDataParsingException{
+            throw new OsmDataParsingException(msg).rememberLocation(locator);
+        }
+        /**
+         * The current osm primitive to be read.
+         */
+        private OsmPrimitive currentPrimitive;
+        private long currentExternalId;
+        private String generator;
+        private Storage<String> internedStrings = new Storage<String>();
+
+        // Memory optimization - see #2312
+        private String intern(String s) {
+            String result = internedStrings.get(s);
+            if (result == null) {
+                internedStrings.put(s);
+                return s;
+            } else
+                return result;
+        }
+
+        @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
+
+            if (qName.equals("osm")) {
+                if (atts == null) {
+                    throwException(tr("Missing mandatory attribute ''{0}'' of XML element {1}.", "version", "osm"));
+                }
+                String v = atts.getValue("version");
+                if (v == null) {
+                    throwException(tr("Missing mandatory attribute ''{0}''.", "version"));
+                }
+                if (!(v.equals("0.5") || v.equals("0.6"))) {
+                    throwException(tr("Unsupported version: {0}", v));
+                }
+                // save generator attribute for later use when creating DataSource objects
+                generator = atts.getValue("generator");
+                ds.setVersion(v);
+
+            } else if (qName.equals("bounds")) {
+                // new style bounds.
+                String minlon = atts.getValue("minlon");
+                String minlat = atts.getValue("minlat");
+                String maxlon = atts.getValue("maxlon");
+                String maxlat = atts.getValue("maxlat");
+                String origin = atts.getValue("origin");
+                if (minlon != null && maxlon != null && minlat != null && maxlat != null) {
+                    if (origin == null) {
+                        origin = generator;
+                    }
+                    Bounds bounds = new Bounds(
+                            new LatLon(Double.parseDouble(minlat), Double.parseDouble(minlon)),
+                            new LatLon(Double.parseDouble(maxlat), Double.parseDouble(maxlon)));
+                    DataSource src = new DataSource(bounds, origin);
+                    ds.dataSources.add(src);
+                } else {
+                    throwException(tr(
+                            "Missing manadatory attributes on element ''bounds''. Got minlon=''{0}'',minlat=''{1}'',maxlon=''{3}'',maxlat=''{4}'', origin=''{5}''.",
+                            minlon, minlat, maxlon, maxlat, origin
+                    ));
+                }
+
+                // ---- PARSING NODES AND WAYS ----
+
+            } else if (qName.equals("node")) {
+                NodeData nd = new NodeData();
+                nd.setCoor(new LatLon(getDouble(atts, "lat"), getDouble(atts, "lon")));
+                readCommon(atts, nd);
+                Node n = new Node(nd.getId(), nd.getVersion());
+                n.load(nd);
+                externalIdMap.put(nd.getPrimitiveId(), n);
+                currentPrimitive = n;
+                currentExternalId = nd.getUniqueId();
+            } else if (qName.equals("way")) {
+                WayData wd = new WayData();
+                readCommon(atts, wd);
+                Way w = new Way(wd.getId(), wd.getVersion());
+                w.load(wd);
+                externalIdMap.put(wd.getPrimitiveId(), w);
+                ways.put(wd.getUniqueId(), new ArrayList<Long>());
+                currentPrimitive = w;
+                currentExternalId = wd.getUniqueId();
+            } else if (qName.equals("nd")) {
+                Collection<Long> list = ways.get(currentExternalId);
+                if (list == null) {
+                    throwException(
+                            tr("Found XML element <nd> not as direct child of element <way>.")
+                    );
+                }
+                if (atts.getValue("ref") == null) {
+                    throwException(
+                            tr("Missing mandatory attribute ''{0}'' on <nd> of way {1}.", "ref", currentPrimitive.getUniqueId())
+                    );
+                }
+                long id = getLong(atts, "ref");
+                if (id == 0) {
+                    throwException(
+                            tr("Illegal value of attribute ''ref'' of element <nd>. Got {0}.", id)
+                    );
+                }
+                if (currentPrimitive.isDeleted()) {
+                    logger.info(tr("Deleted way {0} contains nodes", currentPrimitive.getUniqueId()));
+                } else {
+                    list.add(id);
+                }
+
+                // ---- PARSING RELATIONS ----
+
+            } else if (qName.equals("relation")) {
+                RelationData rd = new RelationData();
+                readCommon(atts, rd);
+                Relation r = new Relation(rd.getId(), rd.getVersion());
+                r.load(rd);
+                externalIdMap.put(rd.getPrimitiveId(), r);
+                relations.put(rd.getUniqueId(), new LinkedList<RelationMemberData>());
+                currentPrimitive = r;
+                currentExternalId = rd.getUniqueId();
+            } else if (qName.equals("member")) {
+                Collection<RelationMemberData> list = relations.get(currentExternalId);
+                if (list == null) {
+                    throwException(
+                            tr("Found XML element <member> not as direct child of element <relation>.")
+                    );
+                }
+                RelationMemberData emd = new RelationMemberData();
+                String value = atts.getValue("ref");
+                if (value == null) {
+                    throwException(tr("Missing attribute ''ref'' on member in relation {0}.",currentPrimitive.getUniqueId()));
+                }
+                try {
+                    emd.id = Long.parseLong(value);
+                } catch(NumberFormatException e) {
+                    throwException(tr("Illegal value for attribute ''ref'' on member in relation {0}. Got {1}", Long.toString(currentPrimitive.getUniqueId()),value));
+                }
+                value = atts.getValue("type");
+                if (value == null) {
+                    throwException(tr("Missing attribute ''type'' on member {0} in relation {1}.", Long.toString(emd.id), Long.toString(currentPrimitive.getUniqueId())));
+                }
+                try {
+                    emd.type = OsmPrimitiveType.fromApiTypeName(value);
+                } catch(IllegalArgumentException e) {
+                    throwException(tr("Illegal value for attribute ''type'' on member {0} in relation {1}. Got {2}.", Long.toString(emd.id), Long.toString(currentPrimitive.getUniqueId()), value));
+                }
+                value = atts.getValue("role");
+                emd.role = value;
+
+                if (emd.id == 0) {
+                    throwException(tr("Incomplete <member> specification with ref=0"));
+                }
+
+                if (currentPrimitive.isDeleted()) {
+                    logger.info(tr("Deleted relation {0} contains members", currentPrimitive.getUniqueId()));
+                } else {
+                    list.add(emd);
+                }
+
+                // ---- PARSING TAGS (applicable to all objects) ----
+
+            } else if (qName.equals("tag")) {
+                String key = atts.getValue("k");
+                String value = atts.getValue("v");
+                currentPrimitive.put(intern(key), intern(value));
+
+            } else {
+                System.out.println(tr("Undefined element ''{0}'' found in input stream. Skipping.", qName));
+            }
+        }
+
+        private double getDouble(Attributes atts, String value) {
+            return Double.parseDouble(atts.getValue(value));
+        }
+
+        private User createUser(String uid, String name) throws SAXException {
+            if (uid == null) {
+                if (name == null)
+                    return null;
+                return User.createLocalUser(name);
+            }
+            try {
+                long id = Long.parseLong(uid);
+                return User.createOsmUser(id, name);
+            } catch(NumberFormatException e) {
+                throwException(MessageFormat.format("Illegal value for attribute ''uid''. Got ''{0}''.", uid));
+            }
+            return null;
+        }
+        /**
+         * Read out the common attributes from atts and put them into this.current.
+         */
+        void readCommon(Attributes atts, PrimitiveData current) throws SAXException {
+            current.setId(getLong(atts, "id"));
+            if (current.getUniqueId() == 0) {
+                throwException(tr("Illegal object with ID=0."));
+            }
+
+            String time = atts.getValue("timestamp");
+            if (time != null && time.length() != 0) {
+                current.setTimestamp(DateUtils.fromString(time));
+            }
+
+            // user attribute added in 0.4 API
+            String user = atts.getValue("user");
+            // uid attribute added in 0.6 API
+            String uid = atts.getValue("uid");
+            current.setUser(createUser(uid, user));
+
+            // visible attribute added in 0.4 API
+            String visible = atts.getValue("visible");
+            if (visible != null) {
+                current.setVisible(Boolean.parseBoolean(visible));
+            }
+
+            String versionString = atts.getValue("version");
+            int version = 0;
+            if (versionString != null) {
+                try {
+                    version = Integer.parseInt(versionString);
+                } catch(NumberFormatException e) {
+                    throwException(tr("Illegal value for attribute ''version'' on OSM primitive with ID {0}. Got {1}.", Long.toString(current.getUniqueId()), versionString));
+                }
+                if (ds.getVersion().equals("0.6")){
+                    if (version <= 0 && current.getUniqueId() > 0) {
+                        throwException(tr("Illegal value for attribute ''version'' on OSM primitive with ID {0}. Got {1}.", Long.toString(current.getUniqueId()), versionString));
+                    } else if (version < 0 && current.getUniqueId() <= 0) {
+                        System.out.println(tr("WARNING: Normalizing value of attribute ''version'' of element {0} to {2}, API version is ''{3}''. Got {1}.", current.getUniqueId(), version, 0, "0.6"));
+                        version = 0;
+                    }
+                } else if (ds.getVersion().equals("0.5")) {
+                    if (version <= 0 && current.getUniqueId() > 0) {
+                        System.out.println(tr("WARNING: Normalizing value of attribute ''version'' of element {0} to {2}, API version is ''{3}''. Got {1}.", current.getUniqueId(), version, 1, "0.5"));
+                        version = 1;
+                    } else if (version < 0 && current.getUniqueId() <= 0) {
+                        System.out.println(tr("WARNING: Normalizing value of attribute ''version'' of element {0} to {2}, API version is ''{3}''. Got {1}.", current.getUniqueId(), version, 0, "0.5"));
+                        version = 0;
+                    }
+                } else {
+                    // should not happen. API version has been checked before
+                    throwException(tr("Unknown or unsupported API version. Got {0}.", ds.getVersion()));
+                }
+            } else {
+                // version expected for OSM primitives with an id assigned by the server (id > 0), since API 0.6
+                //
+                if (current.getUniqueId() > 0 && ds.getVersion() != null && ds.getVersion().equals("0.6")) {
+                    throwException(tr("Missing attribute ''version'' on OSM primitive with ID {0}.", Long.toString(current.getUniqueId())));
+                } else if (current.getUniqueId() > 0 && ds.getVersion() != null && ds.getVersion().equals("0.5")) {
+                    // default version in 0.5 files for existing primitives
+                    System.out.println(tr("WARNING: Normalizing value of attribute ''version'' of element {0} to {2}, API version is ''{3}''. Got {1}.", current.getUniqueId(), version, 1, "0.5"));
+                    version= 1;
+                } else if (current.getUniqueId() <= 0 && ds.getVersion() != null && ds.getVersion().equals("0.5")) {
+                    // default version in 0.5 files for new primitives, no warning necessary. This is
+                    // (was) legal in API 0.5
+                    version= 0;
+                }
+            }
+            current.setVersion(version);
+
+            String action = atts.getValue("action");
+            if (action == null) {
+                // do nothing
+            } else if (action.equals("delete")) {
+                current.setDeleted(true);
+                current.setModified(true);
+            } else if (action.equals("modify")) {
+                current.setModified(true);
+            }
+
+            String v = atts.getValue("changeset");
+            if (v == null) {
+                current.setChangesetId(0);
+            } else {
+                try {
+                    current.setChangesetId(Integer.parseInt(v));
+                } catch(NumberFormatException e) {
+                    if (current.getUniqueId() <= 0) {
+                        // for a new primitive we just log a warning
+                        System.out.println(tr("Illegal value for attribute ''changeset'' on new object {1}. Got {0}. Resetting to 0.", v, current.getUniqueId()));
+                        current.setChangesetId(0);
+                    } else {
+                        // for an existing primitive this is a problem
+                        throwException(tr("Illegal value for attribute ''changeset''. Got {0}.", v));
+                    }
+                }
+                if (current.getChangesetId() <=0) {
+                    if (current.getUniqueId() <= 0) {
+                        // for a new primitive we just log a warning
+                        System.out.println(tr("Illegal value for attribute ''changeset'' on new object {1}. Got {0}. Resetting to 0.", v, current.getUniqueId()));
+                        current.setChangesetId(0);
+                    } else {
+                        // for an existing primitive this is a problem
+                        throwException(tr("Illegal value for attribute ''changeset''. Got {0}.", v));
+                    }
+                }
+            }
+        }
+
+        private long getLong(Attributes atts, String name) throws SAXException {
+            String value = atts.getValue(name);
+            if (value == null) {
+                throwException(tr("Missing required attribute ''{0}''.",name));
+            }
+            try {
+                return Long.parseLong(value);
+            } catch(NumberFormatException e) {
+                throwException(tr("Illegal long value for attribute ''{0}''. Got ''{1}''.",name, value));
+            }
+            return 0; // should not happen
+        }
+    }
+
+    /**
+     * Processes the ways after parsing. Rebuilds the list of nodes of each way and
+     * adds the way to the dataset
+     *
+     * @throws IllegalDataException thrown if a data integrity problem is detected
+     */
+    protected void processWaysAfterParsing() throws IllegalDataException{
+        for (Long externalWayId: ways.keySet()) {
+            Way w = (Way)externalIdMap.get(new SimplePrimitiveId(externalWayId, OsmPrimitiveType.WAY));
+            List<Node> wayNodes = new ArrayList<Node>();
+            for (long id : ways.get(externalWayId)) {
+                Node n = (Node)externalIdMap.get(new SimplePrimitiveId(id, OsmPrimitiveType.NODE));
+                if (n == null) {
+                    if (id <= 0)
+                        throw new IllegalDataException (
+                                tr("Way with external ID ''{0}'' includes missing node with external ID ''{1}''.",
+                                        externalWayId,
+                                        id));
+                    // create an incomplete node if necessary
+                    //
+                    n = (Node)ds.getPrimitiveById(id,OsmPrimitiveType.NODE);
+                    if (n == null) {
+                        n = new Node(id);
+                        ds.addPrimitive(n);
+                    }
+                }
+                if (n.isDeleted()) {
+                    logger.warning(tr("Deleted node {0} is part of way {1}", id, w.getId()));
+                } else {
+                    wayNodes.add(n);
+                }
+            }
+            w.setNodes(wayNodes);
+            if (w.hasIncompleteNodes()) {
+                if (logger.isLoggable(Level.FINE)) {
+                    logger.fine(tr("Way {0} with {1} nodes has incomplete nodes because at least one node was missing in the loaded data.",
+                            externalWayId, w.getNodesCount()));
+                }
+            }
+            ds.addPrimitive(w);
+        }
+    }
+
+    /**
+     * Processes the parsed nodes after parsing. Just adds them to
+     * the dataset
+     *
+     */
+    protected void processNodesAfterParsing() {
+        for (OsmPrimitive primitive: externalIdMap.values()) {
+            if (primitive instanceof Node) {
+                this.ds.addPrimitive(primitive);
+            }
+        }
+    }
+
+    /**
+     * Completes the parsed relations with its members.
+     *
+     * @throws IllegalDataException thrown if a data integrity problem is detected, i.e. if a
+     * relation member refers to a local primitive which wasn't available in the data
+     *
+     */
+    private void processRelationsAfterParsing() throws IllegalDataException {
+
+        // First add all relations to make sure that when relation reference other relation, the referenced will be already in dataset
+        for (Long externalRelationId : relations.keySet()) {
+            Relation relation = (Relation) externalIdMap.get(
+                    new SimplePrimitiveId(externalRelationId, OsmPrimitiveType.RELATION)
+            );
+            ds.addPrimitive(relation);
+        }
+
+        for (Long externalRelationId : relations.keySet()) {
+            Relation relation = (Relation) externalIdMap.get(
+                    new SimplePrimitiveId(externalRelationId, OsmPrimitiveType.RELATION)
+            );
+            List<RelationMember> relationMembers = new ArrayList<RelationMember>();
+            for (RelationMemberData rm : relations.get(externalRelationId)) {
+                OsmPrimitive primitive = null;
+
+                // lookup the member from the map of already created primitives
+                primitive = externalIdMap.get(new SimplePrimitiveId(rm.id, rm.type));
+
+                if (primitive == null) {
+                    if (rm.id <= 0)
+                        // relation member refers to a primitive with a negative id which was not
+                        // found in the data. This is always a data integrity problem and we abort
+                        // with an exception
+                        //
+                        throw new IllegalDataException(
+                                tr("Relation with external id ''{0}'' refers to a missing primitive with external id ''{1}''.",
+                                        externalRelationId,
+                                        rm.id));
+
+                    // member refers to OSM primitive which was not present in the parsed data
+                    // -> create a new incomplete primitive and add it to the dataset
+                    //
+                    primitive = ds.getPrimitiveById(rm.id, rm.type);
+                    if (primitive == null) {
+                        switch (rm.type) {
+                        case NODE:
+                            primitive = new Node(rm.id); break;
+                        case WAY:
+                            primitive = new Way(rm.id); break;
+                        case RELATION:
+                            primitive = new Relation(rm.id); break;
+                        default: throw new AssertionError(); // can't happen
+                        }
+
+                        ds.addPrimitive(primitive);
+                        externalIdMap.put(new SimplePrimitiveId(rm.id, rm.type), primitive);
+                    }
+                }
+                if (primitive.isDeleted()) {
+                    logger.warning(tr("Deleted member {0} is used by relation {1}", primitive.getId(), relation.getId()));
+                } else {
+                    relationMembers.add(new RelationMember(rm.role, primitive));
+                }
+            }
+            relation.setMembers(relationMembers);
+        }
+    }
+
+    public void AddData(InputStream source) throws IllegalDataException {
+        try {
+            InputSource inputSource = new InputSource(new InputStreamReader(source, "UTF-8"));
+            SAXParserFactory.newInstance().newSAXParser().parse(inputSource, new Parser());
+        } catch(ParserConfigurationException e) {
+            throw new IllegalDataException(e.getMessage(), e);
+        } catch(SAXException e) {
+            throw new IllegalDataException(e.getMessage(), e);
+        } catch(Exception e) {
+            throw new IllegalDataException(e);
+        }
+    }
+    public void ProcessData() throws IllegalDataException
+    {
+        processNodesAfterParsing();
+        processWaysAfterParsing();
+        processRelationsAfterParsing();
+    }
+    
+    
+}
Index: applications/editors/josm/plugins/reverter/src/reverter/ObjectsHistoryAction.java
===================================================================
--- applications/editors/josm/plugins/reverter/src/reverter/ObjectsHistoryAction.java	(revision 21201)
+++ applications/editors/josm/plugins/reverter/src/reverter/ObjectsHistoryAction.java	(revision 21201)
@@ -0,0 +1,29 @@
+package reverter;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.tools.Shortcut;
+
+@SuppressWarnings("serial")
+public class ObjectsHistoryAction extends JosmAction {
+
+    public ObjectsHistoryAction()
+    {
+        super(tr("Objects history"),null,tr("History reverter"),
+                Shortcut.registerShortcut("tool:history",
+                        "Tool: Display objects history dialog",
+                        KeyEvent.VK_H, Shortcut.GROUP_EDIT, 
+                        Shortcut.SHIFT_DEFAULT),
+                true);
+        setEnabled(false);
+    }
+//    private ObjectsHistoryDialog dlg = null;
+    public void actionPerformed(ActionEvent arg0) {
+//        if (dlg.get)
+        new ObjectsHistoryDialog().setVisible(true);
+    }
+}
Index: applications/editors/josm/plugins/reverter/src/reverter/ObjectsHistoryDialog.java
===================================================================
--- applications/editors/josm/plugins/reverter/src/reverter/ObjectsHistoryDialog.java	(revision 21201)
+++ applications/editors/josm/plugins/reverter/src/reverter/ObjectsHistoryDialog.java	(revision 21201)
@@ -0,0 +1,22 @@
+package reverter;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.GridBagLayout;
+
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.tools.GBC;
+
+@SuppressWarnings("serial")
+public class ObjectsHistoryDialog extends ExtendedDialog {
+    public ObjectsHistoryDialog() {
+        super(Main.parent, tr("Objects history"), new String[] {"Revert","Cancel"}, false);
+        contentConstraints = GBC.eol().fill().insets(10,10,10,5);
+        setButtonIcons(new String[] {"ok.png", "cancel.png" });
+        setContent(new JPanel(new GridBagLayout()));
+        setupDialog();        
+    }
+}
Index: applications/editors/josm/plugins/reverter/src/reverter/OsmServerMultiObjectReader.java
===================================================================
--- applications/editors/josm/plugins/reverter/src/reverter/OsmServerMultiObjectReader.java	(revision 21201)
+++ applications/editors/josm/plugins/reverter/src/reverter/OsmServerMultiObjectReader.java	(revision 21201)
@@ -0,0 +1,62 @@
+package reverter;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.io.OsmServerReader;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.xml.sax.SAXException;
+
+public class OsmServerMultiObjectReader extends OsmServerReader {
+    private MultiOsmReader rdr = new MultiOsmReader();
+    public void ReadObject(long id,int version,OsmPrimitiveType type,ProgressMonitor progressMonitor) throws OsmTransferException
+    {
+        StringBuffer sb = new StringBuffer();
+        sb.append(type.getAPIName());
+        sb.append("/");
+        sb.append(id);
+        sb.append("/");
+        sb.append(version);
+        progressMonitor.beginTask("", 1);
+        InputStream in = getInputStream(sb.toString(), progressMonitor.createSubTaskMonitor(1, true));
+        try {
+            rdr.AddData(in);
+        } catch (Exception e) {
+            throw new OsmTransferException(e);
+        } finally {
+            progressMonitor.finishTask();
+            if (in!=null) {
+                try {
+                    in.close();
+                } catch(Exception e) {}
+            }
+        }
+    }
+    /**
+     * Method to parse downloaded objects
+     * @return the data requested
+     * @throws SAXException
+     * @throws IOException
+     */
+    @Override
+    public DataSet parseOsm(ProgressMonitor progressMonitor) throws OsmTransferException {
+        progressMonitor.beginTask("", 1);
+        progressMonitor.subTask(tr("Preparing history data..."));
+        try {
+            rdr.ProcessData();
+            return rdr.getDataSet();
+        } catch (Exception e) {
+            throw new OsmTransferException(e);
+        } finally
+        {
+            progressMonitor.finishTask();
+            activeConnection = null;
+        }
+    }
+
+}
Index: applications/editors/josm/plugins/reverter/src/reverter/RevertChangesetAction.java
===================================================================
--- applications/editors/josm/plugins/reverter/src/reverter/RevertChangesetAction.java	(revision 21201)
+++ applications/editors/josm/plugins/reverter/src/reverter/RevertChangesetAction.java	(revision 21201)
@@ -0,0 +1,64 @@
+package reverter;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.util.List;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.command.SequenceCommand;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.openstreetmap.josm.tools.Shortcut;
+
+@SuppressWarnings("serial")
+public class RevertChangesetAction extends JosmAction {
+
+    public RevertChangesetAction()
+    {
+        super(tr("Revert changeset"),null,tr("Revert changeset"),
+                Shortcut.registerShortcut("tool:revert",
+                        "Tool: Revert changeset",
+                        KeyEvent.VK_T, Shortcut.GROUP_EDIT, 
+                        Shortcut.SHIFT_DEFAULT),  
+                true);
+    }
+//    private ObjectsHistoryDialog dlg = null;
+    public void actionPerformed(ActionEvent arg0) {
+        
+        ChangesetIdQuery dlg = new ChangesetIdQuery();
+        dlg.setVisible(true);
+        System.out.println(tr("reverter: {0}",dlg.getValue()));
+        if (dlg.getValue() != 1) return;
+        final int changesetId = dlg.ChangesetId();
+        if (changesetId == 0) return;
+        Main.worker.submit(new PleaseWaitRunnable("Reverting...") {
+            @Override
+            protected void realRun() {
+                try {
+                    ChangesetReverter rev = new ChangesetReverter(changesetId);
+                    rev.RevertChangeset(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, true));
+                    List<Command> cmds = rev.getCommands();
+                    Command cmd = new SequenceCommand(tr("Revert changeset #{0}",changesetId),cmds);
+                    Main.main.undoRedo.add(cmd);
+                } catch (OsmTransferException e) {
+                    e.printStackTrace();
+                } finally {
+                }
+            }
+
+            @Override
+            protected void cancel() {
+            }
+
+            @Override
+            protected void finish() {
+            }
+        });
+    }
+
+}
Index: applications/editors/josm/plugins/reverter/src/reverter/ReverterPlugin.java
===================================================================
--- applications/editors/josm/plugins/reverter/src/reverter/ReverterPlugin.java	(revision 21201)
+++ applications/editors/josm/plugins/reverter/src/reverter/ReverterPlugin.java	(revision 21201)
@@ -0,0 +1,27 @@
+package reverter;
+
+import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
+import static org.openstreetmap.josm.tools.I18n.marktr;
+
+import java.awt.event.KeyEvent;
+
+import javax.swing.JMenu;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.UploadAction;
+import org.openstreetmap.josm.gui.MainMenu;
+import org.openstreetmap.josm.plugins.Plugin;
+import org.openstreetmap.josm.plugins.PluginInformation;
+
+public class ReverterPlugin extends Plugin {
+    public ReverterPlugin(PluginInformation info)
+    {
+        super(info);
+        JMenu historyMenu = Main.main.menu.addMenu(marktr("History"), KeyEvent.VK_R, Main.main.menu.defaultMenuPos,ht("/Plugin/Reverter"));
+        MainMenu.add(historyMenu, new ObjectsHistoryAction());       
+        MainMenu.add(historyMenu, new RevertChangesetAction());
+        UploadAction.registerUploadHook(new ModifiyUploadOrderHook());
+
+        //TODO: Download deleted objects
+    }
+}
