[svn-upgrade] Integrating new upstream version, jabref-plugin-oo (0.7.4+ds) upstream/0.7.4+ds
authorgregor herrmann <gregoa@debian.org>
Sun, 16 May 2010 15:39:36 +0000 (15:39 -0000)
committergregor herrmann <gregoa@debian.org>
Sun, 16 May 2010 15:39:36 +0000 (15:39 -0000)
CHANGELOG [new file with mode: 0755]
OOPlugin.html [new file with mode: 0755]
build.xml [new file with mode: 0755]
example_style_file.jstyle [new file with mode: 0755]
net/sf/jabref/oo/OOBibBase.java [new file with mode: 0755]
net/sf/jabref/oo/OOBibStyle.java [new file with mode: 0755]
net/sf/jabref/oo/OOUtil.java [new file with mode: 0755]
plugin.xml [new file with mode: 0755]

diff --git a/CHANGELOG b/CHANGELOG
new file mode 100755 (executable)
index 0000000..aac2787
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,131 @@
+
+0.7.4:
+ - Added optional citation property "AuthorLastSeparatorInText" which, if present,
+   overrides "AuthorLastSeparator" for in-text author-year citations.
+ - Added citation property "MultiCiteChronological". For author-year citations, this
+   property can be set to false instead of true to get alphabetical sorting of entries
+   within the same citation.
+ - Fixed bug: for normal characters, CharEscapementHeight was set to 0 instead of 100.
+0.7.3:
+ - Changed order of formatting operations in order to fix the problem that formatting
+   of the first part of a reference list entry gets lost.
+ - Added <smallcaps> tag to indicate small caps in reference list.
+ - Modified parsing of style file so boolean properties can be parsed even if extra
+   white space is included, e.g. "true ".
+ - Fixed bug in "MaxAuthorsFirst" handling: if two citations can be grouped, but one of
+   them has appeared earlier, this should prevent grouping if the difference between
+   "MaxAuthorsFirst" and "MaxAuthors" has bearing on the citations in question.
+0.7.2:
+ - Added option "MaxAuthorsFirst" to control how many authors can be shown the first
+   time a citation appears before "et al" is used. "MaxAuthors" controls the following
+   appearances.
+0.7.1:
+ - The preformatter is now preferentially run after instead of before formatters called
+   from the layout specification. This solves the problems of e.g. organization author
+   names wrapped in braces (e.g. {World Bank}), where the braces have no effect as long
+   as the preprocessor is run first. This feature depends on a change introduced in
+   JabRef 2.6, and if older versions are used, the preprocessor will be run first as before.
+ - Fixed problem with repeated citations in numbered citations.
+ - Fixed problem with paragraph format not affecting the entire bibliography entries.
+0.7:
+ - All BibTeX fields are now run through a formatter that translates LaTeX character
+   sequences and \textit{} and \textbf{} commands before insertion into OO. 
+0.6:
+ - Improved behaviour when the document references BibTeX keys not found in the
+   BibTeX database. Citation is now marked as undefined, and the remaining
+   bibliography gets created as normal.
+ - Added settings popup menu, and option for whether to automatically sync
+   bibliography when inserting new citations.
+0.5:
+ - Fixed bug in Merge citations feature - citations would be lost when combined
+   citations have same year.
+ - Fixed problem with connection on Mac.
+0.4:
+ - Added optional citation properties BracketBeforeInList and BracketAfterInList that override
+   BracketBefore and BracketAfter for the numbering of the reference list.
+ - Added support for <sup> and <sub> tags in reference layouts, for superscript and
+   subscript, respectively.
+ - Added "SubscriptCitations" property that gives subscripted citation markers.
+0.3:
+ - Some changes to the interface.
+ - Added property MinimumGroupingCount the determines how many consecutive numbers are
+   required for grouping in a citation marker (e.g. [1-3] or[2;5-8]).
+ - Fixed bug in properties parsing - empty strings are no longer ignored and replaced by
+   default values.
+0.2.1:
+ - Fixed bug in sorting of reference list.
+0.2:
+ - Citation markers are now set to language [None] to prevent spell checker from underlining
+   names.
+ - Fixed sorting of citations in table cells and footnotes.
+ - Added option for superscripted citation markers.
+0.1.9:
+ - Added support for citations in table cells and footnotes. Currently sorting by position
+   fails if there are such citations.
+ - Added properties for setting bold/italic formatting on citation markers.
+0.1.2:
+ - Added button for inserting invisible citations (without text in the citation marker),
+   which can be used to insert an entry into the reference list without citing it.
+ - When citations are merged, they are now sorted chronologically (oldest first).
+0.1.1:
+ - It is now possible to connect again after OpenOffice has been shut down and started
+   again.
+ - Added property ReferenceHeaderParagraphFormat in style file to control the paragraph
+   format of the bibliography header.
+ - Added error message for the case that OpenOffice has been shut down after connection.
+ - Fixed bug in handling of von particles in citation markers.
+0.1:
+ - Added functionality to choose which Writer document to connect to - selector is shown 
+   both when connecting, and when using the new "Select Writer document" button.
+ - Improved appearance of the OpenOffice panel.
+ - A space is now inserted after newly inserted citations, in order to alleviate problem
+   with new text becoming part of the citation marker.
+ - Some improvements to cursor placement after inserting citation.
+
+0.0.9 (2008-10-19):
+ - Streamlined connection process by adding button that attempts to find the necessary
+   paths automatically.
+ - Made adaptations to connect to OpenOffice.org 3.x, due to changed library locations.
+ - Fixed bug: plugin tries to connect even if user cancels the connect dialog.
+
+0.0.8 (2008-09-03):
+ - No changes, but packaged with JabRef 2.4.
+
+0.0.7 (2008-02-20):
+ - Added interface for selecting style. You can set up a list of single files and directories
+   that will be scanned to build a list of styles (directories can be scanned recursively).
+ - Fixed problem with layouts starting with a tag such as "<b>" would disable bold/italics
+   formatting for the entire entry.
+ - Added property "ReferenceParagraphFormat" that determines what paragraph format is used
+   for the reference list (default value is "Default").
+
+0.0.6 (2008-02-12):
+ - "Test" button in this version autocombines citations that are separated by spaces only.
+ - Number citation markers now give citations in ascending order (instead of chronologically,
+   as with author-year citations), and group consecutive entries (e.g. [1-3; 6]). 
+
+0.0.5 (2008-02-06):
+ - Fixed bug in author-year citations with names like "Van der Waal". Von-particles are
+   now included in the citations.
+
+0.0.4 (2008-01-09):
+ - When synchronizing bibliography, the style file is now automatically reloaded if it has
+   been modified since the last read.
+ - Cleaned out some obsolete code.
+
+0.0.3 (2007-12-22):
+ - Reworked the entire plugin to bypass OpenOffice.org's built-in bibliography functions.
+   This makes it possible to solve the open issues in previous versions, and add support
+   for multiple citations, and uniquefier letters and formatting within fields in the
+   bibliography.
+ - The bibliography is now formatted using standard JabRef layout, including formatters.
+ - A new formatter FormatChars is added, which translates LaTeX commands for bold and italic
+   into the <b> and <i> markup used by this plugin. This lets the formatting carry into
+   the bibliography in OO where appropriate.
+
+0.0.2 (2007-12-04):
+ - Fixed bug that prevented the "editor" field from being exported.
+ - Missing fields are now populated from crossreferenced entry where available.
+
+0.0.1 (2007-12-01):
+ - First version.
diff --git a/OOPlugin.html b/OOPlugin.html
new file mode 100755 (executable)
index 0000000..dabb3ea
--- /dev/null
@@ -0,0 +1,269 @@
+<h1>OpenOffice plugin for JabRef</h1>
+
+<h2>Introduction</h2>
+
+       <p><a href="http://jabref.sf.net">JabRef</a> is an open source BibTeX bibliography
+         manager.
+        <p>This plugin offers an interface for inserting citations and formatting a Bibliography in an
+        OpenOffice writer document from JabRef.</p>
+
+
+        <h2>How to use the plugin</h2>
+
+        <p>The plugin can be used with JabRef 2.4 or newer. If your JabRef version doesn't
+        have a plugin manager (versions 2.4.x), you need to put the plugin jar file
+        in a directory named <code>plugins</code> below the directory where the JabRef
+        jar file is installed (typically under <code>C:\Program files\Jabref 2.x</code>
+        if you are running under Windows). The plugin should be loaded next time you
+        start JabRef, and an item named <b>OpenOffice.org panel</b> should appear in
+        JabRef's <b>Plugins</b> menu.
+        <p>The plugin should work with OpenOffice versions 2.4.x and 3.x, provided it
+        is installed with Java support (this is usually the case on Windows, while in
+        some Linux distributions you need to install a separate package named
+        <code>openoffice.org-java-common</code> or something similar).</p>
+
+        <p><b>Updates:</b></p>
+        2010-05-11: Version 0.7.4: Added option to sort alphabetically for citations with multiple entries.<br>
+        2010-03-04: Version 0.7.3: Added support for &lt;smallcaps&gt; tag to indicate small caps in reference list. Several bug fixes.<br>
+        2010-02-02: Version 0.7.2: Added MaxAuthorsFirst property to override MaxAuthors the first time each citation appears.<br>
+        2009-10-07: Version 0.7.1: Several important bug fixes.<br>
+           2009-08-26: Version 0.7: BibTeX fields are now preprocessed to handle LaTeX \textit and \textbf commands and
+          character sequences. <b>NOTE: it is no longer necessary to run FormatChars on fields.</b><br>
+        2009-08-23: Version 0.6: Improved handling of undefined BibTeX keys. Added option to not sync automatically when adding citation.<br>
+        2009-08-05: Version 0.5: Fixed connection problem on Mac. Fixed bug in merge function.<br>
+        2009-06-03: Version 0.4: Added support for superscript/subscript tags in reference list, subscripted citations
+           and different brackets for numbering in the reference list.<br>
+           2009-05-17: Version 0.3: Added MinimumGroupingCount property. Some GUI changes.<br>
+        2009-04-02: Version 0.2.1: Fixed bug in sorting of reference list.<br>
+        2009-03-01: Version 0.2: Better sorting of citations in captions, tables and footnotes.<br> 
+        2009-02-25: Version 0.1.9: Added support for bold/italic citation markers, and for citations in table cells and footnotes.<br>
+        2008-12-21: Version 0.1.2: Added invisible citations. Merged citations are now sorted.<br>
+           2008-11-19: Version 0.1.1: Improved handling of OpenOffice shutdown and reconnect.<br>
+           2008-10-25: Version 0.1: User interface improvements. Can now select which Writer document to work with.<br>
+        2008-10-19: Version 0.0.9: Enabled connection to OpenOffice.org 3. Streamlined connection process.<br>
+        2008-09-03: Version 0.0.8: No major changes, but packaged with JabRef 2.4.<br>
+        2008-02-20: Version 0.0.7: New interface for style selection. Styles can now specify paragraph format.<br>
+        2008-02-13: Version 0.0.6: Sorting and grouping of number citation markers.<br>
+        2008-02-06: Version 0.0.5: Modified style file format. Fixed bug in handling names with elements like "van der".<br>
+        2008-01-09: Version 0.0.4: Style file is now automatically reloaded if modified.<br>
+        2007-12-17: Version 0.0.3: From this version, we bypass OO's built-in bibliographic system.<br>
+        2007-12-04: Version 0.0.2<br>
+        2007-12-01: Version 0.0.1<br>
+
+        <p><b>Downloads:</b></p>
+        <p><a href="net.sf.jabref.oo.ooplugin-0..jar">The plugin.</a></p>
+        <p><a href="JabRef-oo-0.6-src.zip">Plugin source code.</a> The source code tree includes four
+        OpenOffice.org jars and JabRef 2.5. The plugin is built using
+        an included Ant build file.</p>
+        <p><a href="example_style_file.jstyle">Example style file</a></p>
+
+        <p>The plugin is distributed under the terms of the GNU
+        <a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html">General Public License</a>,
+        version 2 or later.</p>
+
+        <h2>Using the OpenOffice interface</h2>
+
+        <p>To communicate with OpenOffice, the OO plugin must first connect to a running OpenOffice
+        instance. You need to start OpenOffice and enter your document before connecting from
+        JabRef. The plugin needs to know the location of your OpenOffice executable (<b>soffice.exe</b> on
+        Windows, and <b>soffice</b> on other platforms), and the directory where several OpenOffice
+        jar files reside. If you connect by clicking the <b>Connect</b> button, the plugin will try to
+        automatically determine these locations. If this does not work, you need to connect using the
+        <b>Manual connect</b> button, which will open a window asking you for the needed locations.</p>
+
+        <p>After the connection has been established, you can insert citations by selecting one or more
+        entries in JabRef and using the <b>Push to OpenOffice</b> button in the dropdown menu of JabRef's
+        toolbar, or by using the appropriate button in the OpenOffice plugin panel in the side pane. This
+        will insert citations for the selected entries at the current cursor position in the OpenOffice
+        document, and update the bibliography to contain the full reference.</p>
+
+        <p><b>Note:</b> JabRef does not use OpenOffice's built-in bibliography system, because of the
+        limitations of that system. A document containing citations inserted from JabRef will
+        not generally be compatible with other reference managers such as Bibus and Zotero.</p>
+
+        <p>Two different types of citations
+        can be inserted - either a citation in parenthesis, "(Author 2007)", or an in-text citation,
+        "Author (2007)". This distinction is only meaningful if author-year citations are used instead of
+        numbered citations, but the distinction will be preserved if you switch between the two styles.</p>
+
+        <p>If you modify entries in JabRef after inserting their citations into OpenOffice, you will
+        need to synchronize the bibliography. The <b>Sync OO bibliography</b> button will update all
+        entries of the bibliography, provided their BibTeX keys have not been altered (JabRef encodes the
+        BibTeX key into the reference name for each citation to keep track of which BibTeX key
+        the original JabRef entry has).</p>
+
+        <h3>The style file</h3>
+
+        You need to select a style file before connecting to OpenOffice - an external file which is selected
+        using a standard file dialog. The style file defines the
+        format of citations and the format of the bibliography. You can use standard JabRef export
+        formatters to process entry fields before they are sent to OpenOffice.
+        Through the style file, the intention is to give as much flexibility in citation styles as possible.
+        
+        <p>Here is an example style file:</p>
+        <code><pre>
+NAME
+Example style file for JabRef-oo plugin.
+
+JOURNALS
+Journal name 1
+Journal name 2
+
+PROPERTIES
+Title=References
+IsSortByPosition=false
+IsNumberEntries=false
+ReferenceParagraphFormat=Default
+ReferenceHeaderParagraphFormat=Heading 1
+
+CITATION
+AuthorField=author/editor
+YearField=year
+MaxAuthors=3
+MaxAuthorsFirst=6
+AuthorSeparator=,
+AuthorLastSeparator= &
+EtAlString= et al.
+YearSeparator=
+InTextYearSeparator=
+BracketBefore=[
+BracketAfter=]
+BracketBeforeInList=[
+BracketAfterInList=]
+CitationSeparator=;
+UniquefierSeparator=,
+GroupedNumbersSeparator=-
+MinimumGroupingCount=3
+FormatCitations=false
+ItalicCitations=false
+BoldCitations=false
+SuperscriptCitations=false
+SubscriptCitations=false
+
+
+LAYOUT
+article=\format[AuthorLastFirst,AuthorAbbreviator,AuthorAndsReplacer]{\author} (&lt;b&gt;\year\uniq</b>). &lt;i&gt;\title</i>, \journal \volume\begin{pages} : \format[FormatPagesForHTML]{\pages}\end{pages}.
+
+book=\format[AuthorLastFirst,AuthorAbbreviator,AuthorAndsReplacer]{\author}\begin{editor}\format[AuthorLastFirst,AuthorAbbreviator,AuthorAndsReplacer]{\editor} (Ed.)\end{editor}, &lt;b&gt;\year\uniq</b>. &lt;i&gt;\title</i>. \publisher, \address.
+
+incollection=\format[AuthorLastFirst,AuthorAbbreviator,AuthorAndsReplacer]{\author} (&lt;b&gt;\year\uniq</b>). &lt;i&gt;\title</i>. In: \format[AuthorLastFirst,AuthorAbbreviator,AuthorAndsReplacer]{\editor} (Ed.), &lt;i&gt;\booktitle</i>, \publisher.
+
+inbook=\format[AuthorLastFirst,AuthorAbbreviator,AuthorAndsReplacer]{\author} (&lt;b&gt;\year\uniq</b>). &lt;i&gt;\chapter</i>. In: \format[AuthorLastFirst,AuthorAbbreviator,AuthorAndsReplacer]{\editor} (Ed.), &lt;i&gt;\title</i>, \publisher.
+
+phdthesis=\format[AuthorLastFirst,AuthorAbbreviator,AuthorAndsReplacer]{\author} (&lt;b&gt;\year\uniq</b>). &lt;i&gt;\title</i>, \school.
+
+default=\format[AuthorLastFirst,AuthorAbbreviator,AuthorAndsReplacer]{\author} (&lt;b&gt;\year\uniq</b>). &lt;i&gt;\title</i>, \journal \volume\begin{pages} : \format[FormatPagesForHTML]{\pages}\end{pages}.
+
+</pre>
+
+<p>(Note that the layout for each entry type must be constrained to a single line in the style file - above, the lines are broken up to improve readability.)</p>
+        </code>
+
+        <p>The <b>PROPERTIES</b> section describes global properties for the bibliography:
+            <ul><li><code>Title</code>: determines the header text for the bibliography.</li></ul>
+            <ul><li><code>IsSortByPosition</code>: determines how the bibliography is sorted. If true, the entries
+                will be sorted according to the order in which they are cited. If false, the entries will be
+                sorted alphabetically by authors.</li></ul>
+            <ul><li><code>IsNumberEntries</code>: determines the type of citations to use. If true, number
+                citations will be used. The citation numbers will be included in the bibliography as well.
+                If false, author-year citations will be used.</li></ul>
+        </p>
+
+        <p>The <b>CITATION</b> section describes the format of the citation markers inserted into the text.
+        If numbered entries are used, only the <code>BracketBefore</code> and <code>BracketAfter</code> properties
+        are relevant - they define which characters the citation number is wrapped in. The citation is composed
+        as follows:<br>
+            [BracketBefore][Number][BracketAfter]<br>
+        where [Number] is the number of the citation, determined according to the ordering of the bibliography and/or
+        the position of the citation in the text. If a citation refers to several entries, these will be separated
+        by the string given in the property <code>CitationSeparator</code> (for instance, if <code>CitationSeparator</code>=;,
+        the citation could look like <code>[2;4;6]</code>). If two or more of the entries have a series of consecutive
+        numbers, the numbers can be grouped (for instance <code>[2-4]</code> for 2, 3 and 4 or <code>[2;5-7]</code> for
+        2, 5, 6 and 7). The property <code>GroupedNumbersSeparator</code> (default <code>-</code>) determines which string separates the first and last
+        of the grouped numbers. The integer property <code>MinimumGroupingCount</code> (default 3) determines what number of
+        consecutive numbers are required before entries are grouped. If <code>MinimumGroupingCount</code>=3, the numbers
+        2 and 3 will not be grouped, while 2, 3, 4 will be. If <code>MinimumGroupingCount</code>=0, no grouping will be
+        done regardless of the number of consecutive numbers.
+        </p>
+        <p>If numbered entries are not used, author-year citations will be created based on the citation properties.
+        A parenthesis citation is composed as follows:<br>
+            [BracketBefore][Author][YearSeparator][Year][BracketAfter]<br>
+        where [Author] is the result of looking up the field or fields given in the <code>AuthorField</code> property,
+        and formatting a list of authors. The list can contain up to <code>MaxAuthors</code> names - if more are present,
+        the list will be composed as the first author plus the text specified in the property <code>EtAlString</code>.
+        If several, slash-separated, fields are given in the <code>AuthorField</code> property, they will be looked up
+        successively if the first field is empty for the given BibTeX entry. In the example above, the "author" field will
+        be used, but if empty, the "editor" field will be used as a backup.
+        <p>The names in the author list will be separated by the text given by the <code>AuthorSeparator</code>
+        property, except for the last two names, which will be separated by the text given by <code>AuthorLastSeparator</code>.
+        If the property <code>AuthorLastSeparatorInText</code> is given, it overrides the former for citations of the in-text
+        type. This makes it possible to get citations like <code>(Olsen & Jensen, 2008)</code> and <code>Olsen and Jensen (2008)</code>
+        for the same style.
+        </p>
+        <p>[Year] is the result of looking up the field or fields given in the [YearField] property.</p>
+        An in-text citation is composed as follows:<br>
+            [Author][InTextYearSeparator][BracketBefore][Year][BracketAfter]<br>
+        where [Author] and [Year] are resolved in exactly the same way as for the parenthesis citations.
+        </p>
+        <p>If two different cited sources have the same authors and publication year, and author-year citations are used,
+        their markers will need modification in order to be distinguishable. This is done automatically by appending a
+        letter after the year for
+        each of the publications; 'a' for the first cited reference, 'b' for the next, and so on.
+        For instance, if the author "Olsen" has two cited papers from 2005, the citation markers will be modified to
+        <code>(Olsen, 2005a)</code> and <code>(Olsen, 2005b)</code>. In the bibliography
+        layout, the placement of the "uniquefier" letter is indicated explicitly by inserting the virtual field
+        <code>uniq</code>.</p>
+        <p>If several entries that have been "uniquefied" are cited together, they will be grouped in the citation
+        marker. For instance, of the two entries in the example above are cited together, the citation marker will
+        be <code>(Olsen, 2005a, b)</code> rather than <code>Olsen, 2005a; Olsen, 2005b)</code>. The grouped uniquefier
+        letters (a and b in our example) will be separated by the string specified by the <code>UniquefierSeparator</code>
+        property.
+        </p>
+        <p>The property <code>FormatCitations</code> determines whether the citation markers should be formatted with
+        regards to italics, boldness, superscript and subscript. If <code>FormatCitations</code> is false, no such formatting
+        will be done. If true, the citations will be italicized or not depending on the <code>ItalicCitations</code> property, set to bold
+        or not depending on the <code>BoldCitations</code> property, and similar for the <code>SuperscriptCitations</code> and
+        <code>SubscriptCitations</code> properties.</p>
+        <p>The <b>LAYOUT</b> section describes how the bibliography entry for each entry type in JabRef
+        should appear. Each line should start with either the name of a BibTeX entry type, or the word
+        <code>default</code>, followed by a '='. The <code>default</code> layout will be used for all
+        entry types for which an explicit layout hasn't been given.</p>
+        <p>The remainder of each line defines the layout, with normal text and spaces appearing literally
+        in the bibliography entry. Information from the BibTeX entry is inserted by adding <code>\field</code> markers
+        with the appropriate field name (e.g. <code>\author</code> for inserting the author names). Formatting
+        information for the field can be included here, following JabRef's standard export layout syntax.
+        Refer to <a href="http://jabref.sourceforge.net/help/CustomExports.php">JabRef's documentation on custom export filters</a>
+        for more information about which formatters are available.</p>
+        <p>If author-year citations are used, you have to explicitly specify the position of the "uniquefier" letter
+        that is added to distinguish similar-looking citations. This is done by including a marker for the virtual field
+        <code>uniq</code>, typically right after the year (as shown in the example style file). The <code>uniq</code>
+        field is automatically set correctly for each entry before its reference text is laid out.
+        </p>
+        <p>To indicate formatting in the bibliography, you can use the HTML-like tag pairs &lt;b&gt; &lt;/b&gt;,
+         &lt;i&gt; &lt;/i&gt;,  &lt;sup&gt; &lt;/sup&gt; and  &lt;sub&gt; &lt;/sub&gt; to specify bold text,
+         italic text, superscript and subscript, respectively.</p>
+        <p>If you are using numbered citations, the number for each entry will be automatically inserted at the start
+        of each entry in the reference list. By default, the numbers will be enclosed in the same brackets defined for
+        citations. The optional citation properties <code>BracketBeforeInList</code> and
+        <code>BracketAfterInList</code> override <code>BracketBefore</code> and <code>BracketAfter</code> if set. These
+        can be used if you want different types of brackets (or no brackets) in the reference list. Note that these need
+        not be brackets as such - they can be any combination of characters.</p>
+
+<h2>Known issues</h2>
+
+<ul>
+  <li>Make sure to save your Writer document in OpenDocument format
+  (odt). Saving to Word format will lose your reference marks.</li>
+  <li>There is currently no support for footnote based citations.</li>
+  <li>The cursor may be poorly positioned after inserting a citation.</li>
+  <li>Copy-pasting the example style file directly from this page can give an unparseable
+    file. To avoid this, instead download the example file from the link in the download section.</li>
+</ul>
+
+<h2>Contact</h2>
+
+If you have tested this plugin, and want to give your feedback, or if you
+want to contribute to the development of the plugin, please contact me at the
+e-mail address mortenalver at users.sourceforge.net.
+
+        
diff --git a/build.xml b/build.xml
new file mode 100755 (executable)
index 0000000..712e5cb
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0"?>
+<!--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+   Example Ant build file how to create a jar for distribution 
+   for an export plugin.
+
+   Version:  $Revision: 2186 $
+             $Date: 2007-07-19 03:35:35 +0200 (Thu, 19 Jul 2007) $
+             $Author: coezbek $
+
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-->
+<project name="net.sf.jabref.oo.ooplugin" default="jar" basedir=".">
+
+       <!-- Properties -->
+       <property name="version" value="0.7.4" />
+       <property name="build.classes" value="classes" />
+       <property name="jabref.build.dir" value="lib" />
+    <property name="jar.dir" value="dist" />
+    <property name="jar" value="${jar.dir}/${ant.project.name}-${version}.jar" />
+       
+       <path id="classpath">
+               <fileset dir="${jabref.build.dir}">
+            <include name="jurt.jar" />
+            <include name="juh.jar" />
+            <include name="unoil.jar" />
+            <include name="ridl.jar" />
+            <include name="JabRef-2.6.jar" />
+               </fileset>
+       </path>
+
+       <!-- Compiles project excluding tests -->
+       <target name="compile">
+               <mkdir dir="${build.classes}" />
+               <javac debug="on" deprecation="on" destdir="${build.classes}" source="1.5" target="1.5">
+                       <src path="." />
+                       <classpath refid="classpath" />
+               </javac>
+       </target>
+
+       <!-- Jars up project -->
+       <target name="jar" depends="compile" >
+               <jar destfile="${jar}">
+                       <fileset dir=".">
+                <exclude name="UNO*"/>
+                <exclude name="dist"/>
+                <exclude name="lib"/>
+                <include name="plugin.xml"/>
+                <include name="CHANGELOG"/>
+                <include name="LICENSE"/>
+                <include name="gpl-2.0.txt"/>
+                <include name="OOPlugin.html"/>
+                <include name="images/**"/>
+            </fileset>
+            <fileset dir="classes">
+                <include name="**"/>
+            </fileset>
+        </jar>
+       </target>
+
+       <target name="clean" description="Clean project">
+               <delete dir="${build.classes}" />
+               <delete file="${jar}" />
+
+       </target>
+
+</project>
diff --git a/example_style_file.jstyle b/example_style_file.jstyle
new file mode 100755 (executable)
index 0000000..7208ba8
--- /dev/null
@@ -0,0 +1,53 @@
+NAME
+Example style file for JabRef-oo plugin.
+
+JOURNALS
+Journal name 1
+Journal name 2
+
+PROPERTIES
+Title=References
+IsSortByPosition=false
+IsNumberEntries=false
+ReferenceParagraphFormat=Text body
+ReferenceHeaderParagraphFormat=Heading 2
+
+CITATION
+AuthorField=author/editor
+YearField=year
+MaxAuthors=2
+MaxAuthorsFirst=6
+AuthorSeparator=,
+AuthorLastSeparator= & 
+AuthorLastSeparatorInText= & 
+EtAlString= et al.
+YearSeparator= 
+InTextYearSeparator= 
+BracketBefore=[
+BracketAfter=]
+BracketBeforeInList=[
+BracketAfterInList=]
+CitationSeparator=; 
+UniquefierSeparator=,
+GroupedNumbersSeparator=-
+MinimumGroupingCount=3
+FormatCitations=false
+ItalicCitations=false
+BoldCitations=false
+SuperscriptCitations=false
+SubscriptCitations=false
+MultiCiteChronological=true
+
+LAYOUT
+article=<b>\format[AuthorLastFirst,AuthorAbbreviator,AuthorAndsReplacer]{\author}</b> (<b>\year\uniq</b>). <i>\title</i>, \journal \volume\begin{pages} : \format[FormatPagesForHTML]{\pages}\end{pages}.
+
+book=\format[AuthorLastFirst,AuthorAbbreviator,AuthorAndsReplacer]{\author}\begin{editor}\format[AuthorLastFirst,AuthorAbbreviator,AuthorAndsReplacer]{\editor} (Ed.)\end{editor}, <b>\year\uniq</b>. <i>\title</i>. \publisher, \address.
+
+incollection=\format[AuthorLastFirst,AuthorAbbreviator,AuthorAndsReplacer]{\author} (<b>\year\uniq</b>). <i>\title</i>. In: \format[AuthorLastFirst,AuthorAbbreviator,AuthorAndsReplacer]{\editor} (Ed.), <i>\booktitle</i>, \publisher.
+
+inbook=\format[AuthorLastFirst,AuthorAbbreviator,AuthorAndsReplacer]{\author} (<b>\year\uniq</b>). <i>\chapter</i>. In: \format[AuthorLastFirst,AuthorAbbreviator,AuthorAndsReplacer]{\editor} (Ed.), <i>\title</i>, \publisher.
+
+phdthesis=\format[AuthorLastFirst,AuthorAbbreviator,AuthorAndsReplacer]{\author} (<b>\year\uniq</b>). <i>\title</i>, \school.
+
+default=\format[AuthorLastFirst,AuthorAbbreviator,AuthorAndsReplacer]{\author} (<b>\year\uniq</b>). <i>\title</i>, \journal \volume\begin{pages} : \format[FormatPagesForHTML]{\pages}\end{pages}.
+
diff --git a/net/sf/jabref/oo/OOBibBase.java b/net/sf/jabref/oo/OOBibBase.java
new file mode 100755 (executable)
index 0000000..6fbc11d
--- /dev/null
@@ -0,0 +1,1181 @@
+package net.sf.jabref.oo;
+
+import com.sun.star.awt.Point;
+import com.sun.star.awt.XWindow;
+import com.sun.star.beans.XPropertySet;
+import com.sun.star.beans.PropertyValue;
+import com.sun.star.comp.helper.Bootstrap;
+import com.sun.star.container.*;
+import com.sun.star.frame.*;
+import com.sun.star.lang.*;
+import com.sun.star.lang.Locale;
+import com.sun.star.text.*;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XComponentContext;
+import net.sf.jabref.*;
+import net.sf.jabref.export.layout.Layout;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import classes.net.sf.jabref.oo.ComparableMark;
+
+/**
+ * Class for manipulating the Bibliography of the currently start document in OpenOffice.
+ */
+public class OOBibBase {
+
+    final static String BIB_SECTION_NAME = "JR_bib";
+    final static String BIB_SECTION_END_NAME = "JR_bib_end";
+    final static String BIB_CITATION = "JR_cite";
+    final Pattern citePattern = Pattern.compile(BIB_CITATION+"\\d*_(\\d*)_(.*)");
+
+    final static int
+        AUTHORYEAR_PAR = 1,
+        AUTHORYEAR_INTEXT = 2,
+        INVISIBLE_CIT = 3;
+
+    final static String DEFAULT_CONNECTION_STRING = "uno:socket,host=localhost,port=2002;urp;StarOffice.ServiceManager";
+    final String[] BIB_TYPES = new String[] { "ARTICLE", "BOOK", "BOOKLET", "CONFERENCE",
+            "INBOOK", "INCOLLECTION", "INPROCEEDINGS", "JOURNAL", "MANUAL", "MASTERTHESIS",
+            "MISC", "PHDTHESIS", "PROCEEDINGS", "TECHREPORT", "UNPUBLISHED", "EMAIL", "WWW",
+            "CUSTOM1", "CUSTOM2", "CUSTOM3", "CUSTOM4", "CUSTOM5" };
+
+    
+    private XMultiServiceFactory mxDocFactory = null;
+    private XTextDocument mxDoc = null;
+    private XText text = null;
+    private XDesktop xDesktop = null;
+    XTextViewCursorSupplier xViewCursorSupplier = null;
+    XComponent xCurrentComponent = null;
+    XComponentLoader xComponentLoader = null;
+    private boolean atEnd;
+    private AlphanumericComparator entryComparator = new AlphanumericComparator();
+    private YearComparator yearComparator = new YearComparator();
+
+    private HashMap<String,String> uniquefiers = new HashMap<String, String>();
+
+    private String[] sortedReferenceMarks = null;
+
+    public OOBibBase(String pathToOO, boolean atEnd) throws Exception {
+        this.atEnd = atEnd;
+        xDesktop = simpleBootstrap(pathToOO);//getDesktop();
+        try {
+            selectDocument();
+        } catch (Exception ex) {
+            // Could not find a writer document?
+            return;
+        }
+        
+    }
+
+    public boolean isConnectedToDocument() {
+        return xCurrentComponent != null;
+    }
+
+    public String getCurrentDocumentTitle() {
+        if (mxDoc != null) {
+            try {
+                return String.valueOf(OOUtil.getProperty
+                        (mxDoc.getCurrentController().getFrame(), "Title"));
+            } catch (Exception e) {
+                e.printStackTrace();
+                return null;
+            }
+        }
+        else
+            return null;
+    }
+
+    public void selectDocument() throws Exception {
+        List<XTextDocument> ls = getTextDocuments();
+        XTextDocument selected = null;
+        if (ls.size() == 0) {
+            // No text documents found.
+            throw new Exception("No Writer documents found");
+        }
+        else if (ls.size() > 1) {
+            selected = OOUtil.selectComponent(null, xDesktop, ls);
+        }
+        else
+            selected = ls.get(0);
+
+        if (selected == null) {
+            return;
+        }
+        xCurrentComponent = (XComponent) UnoRuntime.queryInterface(
+                XComponent.class, selected);
+        mxDoc = selected;
+
+        com.sun.star.text.XDocumentIndexesSupplier indexesSupp = (com.sun.star.text.XDocumentIndexesSupplier) UnoRuntime.queryInterface(
+                 com.sun.star.text.XDocumentIndexesSupplier.class, xCurrentComponent);
+
+        XModel xModel = (XModel) UnoRuntime.queryInterface(XModel.class, xCurrentComponent);
+        XController xController = xModel.getCurrentController();
+        xViewCursorSupplier =
+            (com.sun.star.text.XTextViewCursorSupplier) UnoRuntime.queryInterface(
+                    com.sun.star.text.XTextViewCursorSupplier.class, xController);
+
+        // get a reference to the body text of the document
+        text = mxDoc.getText();
+
+        // Access the text document's multi service factory:
+        mxDocFactory = (XMultiServiceFactory) UnoRuntime.queryInterface(XMultiServiceFactory.class, mxDoc);
+
+    }
+
+    public XDesktop simpleBootstrap(String pathToExecutable) throws Exception {
+
+
+        ClassLoader loader = ClassLoader.getSystemClassLoader();
+        if (loader instanceof URLClassLoader) {
+            URLClassLoader cl = (URLClassLoader) loader;
+            Class sysclass = URLClassLoader.class;
+            try {
+
+                 Method method = sysclass.getDeclaredMethod("addURL", new Class[]{URL.class});
+                 method.setAccessible(true);
+                
+                 method.invoke(cl, new Object[]{new File(pathToExecutable).toURI().toURL()});
+             } catch (Throwable t) {
+                 t.printStackTrace();
+                 throw new IOException("Error, could not add URL to system classloader");
+             }
+         } else {
+             System.out.println("Error occured, URLClassLoader expected but " +
+                     loader.getClass() + " received. Could not continue.");
+         }
+
+         //Get the office component context:
+         XComponentContext xContext = Bootstrap.bootstrap();
+
+         //Get the office service manager:
+         XMultiComponentFactory xServiceManager = xContext.getServiceManager();
+
+         //Create the desktop, which is the root frame of the
+         //hierarchy of frames that contain viewable components:
+         Object desktop = xServiceManager.createInstanceWithContext("com.sun.star.frame.Desktop", xContext);
+
+        XDesktop xD = (XDesktop) UnoRuntime.queryInterface(XDesktop.class, desktop);
+
+        xComponentLoader = (XComponentLoader)UnoRuntime.queryInterface(
+             XComponentLoader.class, desktop);
+
+        return xD;
+
+     }
+
+     public List<XTextDocument> getTextDocuments() throws Exception {
+         List<XTextDocument> res = new ArrayList<XTextDocument>();
+         XEnumerationAccess enumA = xDesktop.getComponents();
+         XEnumeration e = enumA.createEnumeration();
+
+         // TODO: http://api.openoffice.org/docs/DevelopersGuide/OfficeDev/OfficeDev.xhtml#1_1_3_2_1_2_Frame_Hierarchies
+
+         while (e.hasMoreElements()) {
+             Object o = e.nextElement();
+             XComponent comp = (XComponent) UnoRuntime.queryInterface(XComponent.class, o);
+             XTextDocument doc = (XTextDocument) UnoRuntime.queryInterface(
+                    XTextDocument.class, comp);
+             if (doc != null) {
+                res.add(doc);
+             }
+         }
+         return res;
+     }
+
+
+     public void updateSortedReferenceMarks() throws Exception {
+         XReferenceMarksSupplier supplier = (XReferenceMarksSupplier) UnoRuntime.queryInterface(
+                XReferenceMarksSupplier.class, xCurrentComponent);
+        XNameAccess nameAccess = supplier.getReferenceMarks();
+        String[] names;
+        sortedReferenceMarks = getSortedReferenceMarks(nameAccess);
+     }
+     
+    /**
+     * This method inserts a cite marker in the text for the given BibtexEntry,
+     * and may refresh the bibliography.
+     * @param entries The entries to cite.
+     * @param database The database the entry belongs to.
+     * @param style The bibliography style we are using.
+     * @param inParenthesis Indicates whether it is an in-text citation or a citation in parenthesis.
+     *   This is not relevant if numbered citations are used.
+     * @param withText Indicates whether this should be a normal citation (true) or an empty
+     *   (invisible) citation (false).
+     * @param sync Indicates whether the reference list should be refreshed.
+     * @throws Exception
+     */
+    public void insertEntry(BibtexEntry[] entries, BibtexDatabase database, OOBibStyle style,
+                            boolean inParenthesis, boolean withText, boolean sync) throws Exception {
+
+        try {
+            XTextViewCursor xViewCursor = xViewCursorSupplier.getViewCursor();
+
+            if (entries.length > 1) {
+                if (style.getBooleanCitProperty("MultiCiteChronological"))
+                    Arrays.sort(entries, yearComparator);
+                else
+                    Arrays.sort(entries, entryComparator);
+            }
+
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < entries.length; i++) {
+                BibtexEntry entry = entries[i];
+                if (i > 0)
+                    sb.append(",");
+                sb.append(entry.getCiteKey());
+            }
+            String keyString = sb.toString();
+            // Insert bookmark:
+            String bName = getUniqueReferenceMarkName(keyString,
+                    withText ? (inParenthesis ? AUTHORYEAR_PAR : AUTHORYEAR_INTEXT) : INVISIBLE_CIT);
+            //XTextContent content = insertBookMark(bName, xViewCursor);
+
+
+            String citeText = style.isNumberEntries() ? "-" : style.getCitationMarker(entries, database, inParenthesis, null, null);
+
+            //System.out.println(text+" / "+xViewCursor.getText());
+            xViewCursor.getText().insertString(xViewCursor, " ", false);
+            xViewCursor.goLeft((short)1,false);
+            insertReferenceMark(bName, citeText, xViewCursor, withText, style);
+            //xViewCursor.collapseToEnd();
+
+            xViewCursor.collapseToEnd();
+            xViewCursor.goRight((short)1,false);
+
+            XTextRange position = xViewCursor.getEnd();
+
+            if (sync) {
+                // To account for numbering and for uniqiefiers, we must refresh the cite markers:
+                updateSortedReferenceMarks();
+                refreshCiteMarkers(database, style);
+
+                // Insert it at the current position:
+                rebuildBibTextSection(database, style);
+            }
+
+            // Go back to the relevant position:
+            try {
+                xViewCursor.gotoRange(position, false);
+            } catch (Exception ex) {
+                System.out.println("Catch");
+                ex.printStackTrace();
+            }
+        } catch (DisposedException ex) {
+            // We need to catch this one here because the OOTestPanel class is
+            // loaded before connection, and therefore cannot directly reference
+            // or catch a DisposedException (which is in a OO jar file).
+            throw new ConnectionLostException(ex.getMessage());
+        }
+    }
+
+    /**
+     * Refresh all cite markers in the document.
+     * @param database The database to get entries from.
+     * @param style The bibliography style to use.
+     * @return A list of those referenced BibTeX keys that could not be resolved.
+     * @throws UndefinedParagraphFormatException
+     * @throws Exception
+     */
+    public List<String> refreshCiteMarkers(BibtexDatabase database, OOBibStyle style) throws
+            UndefinedParagraphFormatException, Exception {
+        try {
+            return refreshCiteMarkersInternal(database, style);
+        } catch (DisposedException ex) {
+            // We need to catch this one here because the OOTestPanel class is
+            // loaded before connection, and therefore cannot directly reference
+            // or catch a DisposedException (which is in a OO jar file).
+            throw new ConnectionLostException(ex.getMessage());
+        }
+    }
+
+    private List<String> refreshCiteMarkersInternal(BibtexDatabase database, OOBibStyle style) throws
+            UndefinedParagraphFormatException, Exception {
+
+        List<String> cited = findCitedKeys();
+        List<BibtexEntry> entries = findCitedEntries(database, cited);
+
+        XReferenceMarksSupplier supplier = (XReferenceMarksSupplier) UnoRuntime.queryInterface(
+                XReferenceMarksSupplier.class, xCurrentComponent);
+        XNameAccess nameAccess = supplier.getReferenceMarks();
+        
+        String[] names;
+        if (style.isSortByPosition()) {
+            // We need to sort the reference marks according to their order of appearance:
+            /*if (sortedReferenceMarks == null)
+                updateSortedReferenceMarks();*/
+            names = sortedReferenceMarks;
+        }
+        else if (style.isNumberEntries()) {
+            // We need to sort the reference marks according to the sorting of the bibliographic
+            // entries:
+            Collections.sort(entries, entryComparator);
+            // Rebuild the list of cited keys according to the sort order:
+            cited.clear();
+            for (Iterator<BibtexEntry> iterator = entries.iterator(); iterator.hasNext();) {
+                BibtexEntry entry = iterator.next();
+                cited.add(entry.getCiteKey());
+            }
+            names = nameAccess.getElementNames();
+        }
+        else {
+            /*if (sortedReferenceMarks == null)
+                updateSortedReferenceMarks();*/
+            names = sortedReferenceMarks;            
+        }
+
+        HashMap<String,Integer> numbers = new HashMap<String, Integer>();
+        //HashMap<S
+        int lastNum = 0;
+        // First compute citation markers for all citations:
+        String[] citMarkers = new String[names.length];
+        String[][] normCitMarkers = new String[names.length][];
+        String[][] bibtexKeys = new String[names.length][];
+
+        int minGroupingCount = style.getIntCitProperty("MinimumGroupingCount");
+
+        int[] types = new int[names.length];
+        for (int i = 0; i < names.length; i++) {
+            Matcher m = citePattern.matcher(names[i]);
+            if (m.find()) {
+                String typeStr = m.group(1);
+                int type = Integer.parseInt(typeStr);
+                types[i] = type; // Remember the type in case we need to uniqiefy.
+                String[] keys = m.group(2).split(",");
+                bibtexKeys[i] = keys;
+                BibtexEntry[] cEntries = new BibtexEntry[keys.length];
+                for (int j = 0; j < cEntries.length; j++) {
+                    cEntries[j] = OOUtil.createAdaptedEntry(database.getEntryByKey(keys[j]));
+                     if (cEntries[j] == null) {
+                         System.out.println("Bibtex key not found : '"+keys[j]+"'");
+                         System.out.println("Problem with reference mark: '"+names[i]+"'");
+                         cEntries[j] = new UndefinedBibtexEntry(keys[j]);
+                        //throw new BibtexEntryNotFoundException(keys[j], "");
+                     }
+                }
+
+                String[] normCitMarker = new String[keys.length];
+                String citationMarker;
+                if (style.isNumberEntries()) {
+                    if (style.isSortByPosition()) {
+                        // We have sorted the citation markers according to their order of appearance,
+                        // so we simply count up for each marker referring to a new entry:
+                        int[] num = new int[keys.length];
+                        for (int j=0; j<keys.length; j++) {
+                            if (cEntries[j] instanceof UndefinedBibtexEntry) {
+                                num[j] = -1;
+                            } else {
+                                num[j] = lastNum + 1;
+                                if (numbers.containsKey(keys[j]))
+                                    num[j] = numbers.get(keys[j]);
+                                else {
+                                    numbers.put(keys[j], num[j]);
+                                    lastNum = num[j];
+                                }
+                            }
+                        }
+                        citationMarker = style.getNumCitationMarker(num, minGroupingCount, false);
+                        for (int j=0; j<keys.length; j++)
+                            normCitMarker[j] = style.getNumCitationMarker(new int[] {num[j]},
+                                    minGroupingCount, false);
+                    }
+                    else {
+                        // We need to find the number of the cited entry in the bibliography,
+                        // and use that number for the cite marker:
+                        int[] num = findCitedEntryIndex(names[i], cited);
+
+                        if (num != null)
+                            citationMarker = style.getNumCitationMarker(num, minGroupingCount, false);
+                        else
+                            throw new BibtexEntryNotFoundException(names[i], Globals.lang("Could not resolve BibTeX entry for citation marker '%0'.", names[i]));
+
+                        for (int j=0; j<keys.length; j++)
+                            normCitMarker[j] = style.getNumCitationMarker(new int[] {num[j]},
+                                    minGroupingCount, false);
+                    }
+                }
+                else {
+
+                    if (cEntries.length > 1) {
+                        if (style.getBooleanCitProperty("MultiCiteChronological"))
+                            Arrays.sort(cEntries, yearComparator);
+                        else
+                            Arrays.sort(cEntries, entryComparator);
+                        // Update key list to match the new sorting:
+                        for (int j = 0; j < cEntries.length; j++) {
+                            bibtexKeys[i][j] = cEntries[j].getCiteKey();
+                        }
+                    }
+                    /*System.out.println(style.getBooleanCitProperty("MultiCiteChronological"));
+                    for (int j = 0; j < cEntries.length; j++) {
+                        BibtexEntry cEntry = cEntries[j];
+                        System.out.println(cEntry.getCiteKey());
+                    } */
+
+                    citationMarker = style.getCitationMarker(cEntries, database, type == AUTHORYEAR_PAR, null, null);
+                    // We need "normalized" (in parenthesis) markers for uniqueness checking purposes:
+                    for (int j=0; j<cEntries.length; j++)
+                        normCitMarker[j] = style.getCitationMarker(cEntries[j], database, true, null, -1);
+                }
+                citMarkers[i] = citationMarker;
+                normCitMarkers[i] = normCitMarker;
+            }
+
+        }
+
+        uniquefiers.clear();
+        if (!style.isNumberEntries()) {
+            // See if there are duplicate citations marks referring to different entries. If so, we need to
+            // use uniquefiers:
+            HashMap<String,List<String>> refKeys = new HashMap<String, List<String>>();
+            HashMap<String,List<Integer>> refNums = new HashMap<String, List<Integer>>();
+            for (int i = 0; i < citMarkers.length; i++) {
+                String[] markers = normCitMarkers[i]; // compare normalized markers, since the actual markers can be different
+                for (int j=0; j<markers.length; j++) {
+                    String marker = markers[j];
+                    if (!refKeys.containsKey(marker)) {
+                        List<String> l = new ArrayList<String>(1);
+                        l.add(bibtexKeys[i][j]);
+                        refKeys.put(marker, l);
+                        List<Integer> l2 = new ArrayList<Integer>(1);
+                        l2.add(i);
+                        refNums.put(marker, l2);
+                    }
+                    else {
+                        // Ok, we have seen this exact marker before.
+                        if (!refKeys.get(marker).contains(bibtexKeys[i][j])) {
+                            // ... but not for this entry.
+                            refKeys.get(marker).add(bibtexKeys[i][j]);
+                            refNums.get(marker).add(i);
+                        }
+                    }
+                }
+            }
+            // Go through the collected lists and see where we need to uniquefy:
+            for (String marker : refKeys.keySet()) {
+                List<String> keys = refKeys.get(marker);
+                if (keys.size() > 1) {
+                    // This marker appears for more than one unique entry:
+                    int uniq = 'a';
+                    for (String key : keys) {
+                        // Update the map of uniquefiers for the benefit of both the following generation of new
+                        // citation markers, and for the method that builds the bibliography:
+                        uniquefiers.put(key, String.valueOf((char)uniq));
+                        uniq++;
+                    }
+                }
+            }
+
+            // Finally, go through all citation markers, and update those referring to entries in our current list:
+            int maxAuthorsFirst = style.getIntCitProperty("MaxAuthorsFirst");
+            HashSet<String> seenBefore = new HashSet<String>();
+            for (int j = 0; j < bibtexKeys.length; j++) {
+                boolean needsChange = false;
+                int[] firstLimAuthors = new int[bibtexKeys[j].length];
+                String[] uniquif = new String[bibtexKeys[j].length];
+                BibtexEntry[] cEntries = new BibtexEntry[bibtexKeys[j].length];
+                for (int k=0; k<bibtexKeys[j].length; k++) {
+                    firstLimAuthors[k] = -1;
+                    if (maxAuthorsFirst > 0) {
+                        if (!seenBefore.contains(bibtexKeys[j][k])) {
+                            firstLimAuthors[k] = maxAuthorsFirst;
+                        }
+                        seenBefore.add(bibtexKeys[j][k]);
+                    }
+                    String uniq = uniquefiers.get(bibtexKeys[j][k]);
+                    if ((uniq != null) && (uniq.length() >= 0)) {
+                        needsChange = true;
+                        cEntries[k] = OOUtil.createAdaptedEntry(database.getEntryByKey(bibtexKeys[j][k]));
+                        uniquif[k] = uniq;
+                    }
+                    else if (firstLimAuthors[k] > 0) {
+                        needsChange = true;
+                        cEntries[k] = OOUtil.createAdaptedEntry(database.getEntryByKey(bibtexKeys[j][k]));
+                        uniquif[k] = "";
+                    }
+                    else {
+                        cEntries[k] = OOUtil.createAdaptedEntry(database.getEntryByKey(bibtexKeys[j][k]));
+                        uniquif[k] = "";
+                    }
+                }
+                if (needsChange)
+                    citMarkers[j] = style.getCitationMarker(cEntries, database, types[j] == AUTHORYEAR_PAR,
+                        uniquif, firstLimAuthors);
+            }
+        }
+
+        // Refresh all reference marks with the citation markers we computed:
+        boolean hadBibSection = getBookmarkRange(BIB_SECTION_NAME) != null;
+        for (int i = 0; i < names.length; i++) {
+            Object o = nameAccess.getByName(names[i]);
+            XTextContent bm = (XTextContent) UnoRuntime.queryInterface
+                    (XTextContent.class, o);
+
+            XTextCursor cursor = bm.getAnchor().getText().createTextCursorByRange(bm.getAnchor());
+            text.removeTextContent(bm);
+            insertReferenceMark(names[i], citMarkers[i], cursor, types[i] != INVISIBLE_CIT, style);
+            if (hadBibSection && (getBookmarkRange(BIB_SECTION_NAME) == null)) {
+                // We have overwritten the marker for the start of the reference list.
+                // We need to add it again.
+                cursor.collapseToEnd();
+                OOUtil.insertParagraphBreak(text, cursor);
+                insertBookMark(BIB_SECTION_NAME, cursor);
+                /* The following is for resetting the paragraph format, but should probably
+                   not be done.
+                   
+                XParagraphCursor parCursor =
+                    (XParagraphCursor)UnoRuntime.queryInterface(
+                    java.lang.Class.forName("com.sun.star.text.XParagraphCursor"), cursor);
+                parCursor.gotoPreviousParagraph(false);
+                parCursor.gotoStartOfParagraph(false);
+                parCursor.gotoEndOfParagraph(true);
+                XPropertySet props = (XPropertySet) UnoRuntime.queryInterface(
+                    XPropertySet.class, parCursor);
+
+                try {
+                    props.setPropertyValue("ParaStyleName", "Default");
+                } catch (com.sun.star.lang.IllegalArgumentException ex) {
+                    throw new UndefinedParagraphFormatException("Default");
+                }
+                */
+
+            }
+        }
+
+        ArrayList<String> unresolvedKeys = new ArrayList<String>();
+        for (BibtexEntry entry : entries) {
+            if (entry instanceof UndefinedBibtexEntry) {
+                String key = ((UndefinedBibtexEntry)entry).getKey();
+                if (!unresolvedKeys.contains(key))
+                    unresolvedKeys.add(key);
+            }
+        }
+        return unresolvedKeys;
+    }
+
+    public String[] getSortedReferenceMarks(final XNameAccess nameAccess) throws Exception {
+        /*
+        PropertyValue[] props = new PropertyValue[2];
+
+        props[0] = new PropertyValue();
+        props[0].Name = "Model";
+
+        props[0].Value = mxDoc.getCurrentController().getModel();
+        props[1] = new PropertyValue();
+        props[1].Name = "Hidden";
+        props[1].Value = true;
+
+        // argument xModel wins over URL.
+        System.out.println("her");
+        XComponent comp = xComponentLoader.loadComponentFromURL("private:factory/swriter",
+                           "_blank", 0, props);
+        System.out.println("her2");
+
+        XTextDocument newDoc = (XTextDocument)UnoRuntime.queryInterface(
+                XTextDocument.class, comp);
+        System.out.println("newDoc = "+newDoc);
+
+        // Controller of the hidden frame
+        XController xController = newDoc.getCurrentController();
+
+        XFrame xFrame = xController.getFrame();
+        XWindow xContainerWindow = xFrame.getContainerWindow();
+        XWindow xComponentWindow = xFrame.getComponentWindow();
+
+        xContainerWindow.setVisible(true);
+        xComponentWindow.setFocus();
+        xContainerWindow.setVisible(false);
+        */
+        XTextViewCursorSupplier css = (XTextViewCursorSupplier)UnoRuntime.queryInterface(
+                XTextViewCursorSupplier.class, mxDoc.getCurrentController());
+
+        XTextViewCursor tvc = css.getViewCursor();
+        XTextRange initialPos = tvc.getStart();
+        String[] names = nameAccess.getElementNames();
+        Point[] positions = new Point[names.length];
+        for (int i = 0; i < names.length; i++) {
+            String name = names[i];
+            XTextContent tc = (XTextContent) UnoRuntime.queryInterface
+                    (XTextContent.class, nameAccess.getByName(name));
+            XTextRange r = tc.getAnchor();
+            // Check if we are inside a footnote:
+            if (UnoRuntime.queryInterface(XFootnote.class, r.getText()) != null) {
+                // Find the linking footnote marker:
+                XFootnote footer = (XFootnote)UnoRuntime.queryInterface(XFootnote.class, r.getText());
+                // The footnote's anchor gives the correct position in the text:
+                r = footer.getAnchor();
+            }
+
+            positions[i] = findPosition(tvc, r);
+        }
+        TreeSet<ComparableMark> set = new TreeSet<ComparableMark>();
+        for (int i = 0; i < positions.length; i++) {
+            set.add(new ComparableMark(names[i], positions[i]));
+        }
+        int i=0;
+        for (Iterator<ComparableMark> iterator = set.iterator(); iterator.hasNext();) {
+            ComparableMark mark = iterator.next();
+            //System.out.println(mark.getPosition().X+" -- "+mark.getPosition().Y+" : "+mark.getName());
+            names[i++] = mark.getName();
+        }
+        tvc.gotoRange(initialPos, false);
+        //xFrame.dispose();
+
+        return names;
+        
+        /*final XTextRangeCompare compare = (XTextRangeCompare) UnoRuntime.queryInterface
+                (XTextRangeCompare.class, text);
+        Arrays.sort(names, new Comparator<String>() {
+            public int compare(String o1, String o2) {
+                try {
+                    XTextRange r1 = ((XTextContent) UnoRuntime.queryInterface
+                            (XTextContent.class, nameAccess.getByName(o1))).getAnchor();
+                    XTextRange r2 = ((XTextContent) UnoRuntime.queryInterface
+                            (XTextContent.class, nameAccess.getByName(o2))).getAnchor();
+
+                    try {
+                        return compare.compareRegionStarts(r2, r1);
+                    } catch (com.sun.star.lang.IllegalArgumentException ex) {
+                        // problem comparing reference marks in different areas (text, table, etc.)
+                        return 0;
+                    }
+                } catch (Exception ex) {
+                    ex.printStackTrace();
+                    return 0;
+                }
+            }
+        });
+        return names;*/
+    }
+
+    public void rebuildBibTextSection(BibtexDatabase database, OOBibStyle style)
+            throws Exception {
+        List<String> cited = findCitedKeys();
+        List<BibtexEntry> entries = findCitedEntries(database, cited);
+        
+        String[] names = sortedReferenceMarks;
+        if (style.isSortByPosition()) {
+            // We need to sort the entries according to their order of appearance:
+           entries = getSortedEntriesFromSortedRefMarks(names, database, entries);
+        }
+        else {
+            Collections.sort(entries, entryComparator);
+        }
+        
+        clearBibTextSectionContent();
+        populateBibTextSection(database, entries, style);
+    }
+
+
+
+    public String getUniqueReferenceMarkName(String bibtexKey, int type) {
+        XReferenceMarksSupplier supplier = (XReferenceMarksSupplier) UnoRuntime.queryInterface(
+                XReferenceMarksSupplier.class, xCurrentComponent);
+        XNameAccess xNamedRefMarks = supplier.getReferenceMarks();
+        int i=0;
+        String name = BIB_CITATION+"_"+type+"_"+bibtexKey;
+        while (xNamedRefMarks.hasByName(name)) {
+            name = BIB_CITATION+i+"_"+type+"_"+bibtexKey;
+            i++;
+        }
+        return name;
+    }
+
+    public List<BibtexEntry> findCitedEntries(BibtexDatabase database, List<String> keys) {
+        List<BibtexEntry> entries = new ArrayList<BibtexEntry>();
+        for (String key : keys) {
+            BibtexEntry entry = database.getEntryByKey(key);
+
+            if (entry != null)
+                entries.add(OOUtil.createAdaptedEntry(entry));
+            else {
+                entries.add(new UndefinedBibtexEntry(key));
+            }
+        }
+        return entries;
+    }
+
+    public List<String> findCitedKeys() throws com.sun.star.container.NoSuchElementException, WrappedTargetException {
+
+        XReferenceMarksSupplier supplier = (XReferenceMarksSupplier) UnoRuntime.queryInterface(
+                XReferenceMarksSupplier.class, xCurrentComponent);
+        XNameAccess xNamedMarks = supplier.getReferenceMarks();
+        String[] names = xNamedMarks.getElementNames();
+        ArrayList<String> keys = new ArrayList<String>();
+        for (int i = 0; i < names.length; i++) {
+            Object bookmark = xNamedMarks.getByName(names[i]);
+            XTextContent xTextContent = (XTextContent) UnoRuntime.queryInterface(
+                    XTextContent.class, bookmark);
+
+            String name = names[i];
+            List<String> newKeys = parseRefMarkName(name);
+            for (String key : newKeys)
+                if (!keys.contains(key))
+                    keys.add(key);
+        }
+
+        return keys;
+    }
+
+    public List<BibtexEntry> getSortedEntriesFromSortedRefMarks(String[] names, 
+            BibtexDatabase database, List<BibtexEntry> entries) 
+            throws BibtexEntryNotFoundException {
+        
+        List<BibtexEntry> newList = new ArrayList<BibtexEntry>();
+        HashMap<BibtexEntry,BibtexEntry> adaptedEntries = new HashMap<BibtexEntry,BibtexEntry>();
+        for (int i = 0; i < names.length; i++) {
+            Matcher m = citePattern.matcher(names[i]);
+            if (m.find()) {
+                String[] keys = m.group(2).split(",");
+                for (int j = 0; j < keys.length; j++) {
+                    BibtexEntry origEntry = database.getEntryByKey(keys[j]);
+                    if (origEntry == null) {
+                        System.out.println("Bibtex key not found : '"+keys[j]+"'");
+                        System.out.println("Problem with reference mark: '"+names[i]+"'");
+                        newList.add(new UndefinedBibtexEntry(keys[j]));
+                        //throw new BibtexEntryNotFoundException(keys[j], "");
+                    } else {
+                        BibtexEntry entry = adaptedEntries.get(origEntry);
+                        if (entry == null) {
+                            entry = OOUtil.createAdaptedEntry(origEntry);
+                            adaptedEntries.put(origEntry, entry);
+                        }
+                        if (!newList.contains(entry)) {
+                            newList.add(entry);
+                        }
+                    }
+                }
+            }
+        }
+
+        return newList;
+    }
+    
+    public Point findPosition(XTextViewCursor cursor, XTextRange range) {
+        cursor.gotoRange(range, false);
+        return cursor.getPosition();
+    }
+
+    /**
+     * Extract the list of bibtex keys from a reference mark name.
+     * @param name The reference mark name.
+     * @return The list of bibtex keys encoded in the name.
+     */
+    public List<String> parseRefMarkName(String name) {
+        ArrayList<String> keys = new ArrayList<String>();
+        Matcher m = citePattern.matcher(name);
+        if (m.find()) {
+            String[] keystring = m.group(2).split(",");
+            for (int j = 0; j < keystring.length; j++) {
+                if (!keys.contains(keystring[j]))
+                    keys.add(keystring[j]);
+            }
+        }
+        return keys;
+    }
+
+    /**
+     * Resolve the bibtex key from a citation reference marker name, and look up
+     * the index of the key in a list of keys.
+     * @param citRefName The name of the ReferenceMark representing the citation.
+     * @param keys A List of bibtex keys representing the entries in the bibliography.
+     * @return the indices of the cited keys, -1 if a key is not found. Returns null if the ref name
+     *   could not be resolved as a citation.
+     */
+    public int[] findCitedEntryIndex(String citRefName, List<String> keys) {
+        Matcher m = citePattern.matcher(citRefName);
+        if (m.find()) {
+            String[] keyStrings = m.group(2).split(",");
+            int[] res = new int[keyStrings.length];
+            for (int i=0; i<keyStrings.length; i++) {
+                int ind = keys.indexOf(keyStrings[i]);
+                res[i] = ind != -1 ? 1+ind : -1;
+            }
+            return res;
+        }
+        else
+            return null;
+    }
+
+
+    public void insertFullReferenceAtCursor(XTextCursor cursor, BibtexDatabase database,
+                                            List<BibtexEntry> entries,
+                                            OOBibStyle style, String parFormat)
+            throws UndefinedParagraphFormatException, Exception {
+        // If we don't have numbered entries, we need to sort the entries before adding them:
+        if (!style.isSortByPosition())
+            Collections.sort(entries, entryComparator);
+        int number = 1;
+        for (BibtexEntry entry : entries) {
+            if (entry instanceof UndefinedBibtexEntry)
+                continue;
+            OOUtil.insertParagraphBreak(text, cursor);
+            if (style.isNumberEntries()) {
+                int minGroupingCount = style.getIntCitProperty("MinimumGroupingCount");
+                OOUtil.insertTextAtCurrentLocation(text, cursor,
+                        style.getNumCitationMarker(new int[] {number++}, minGroupingCount, true)+" ",
+                        false, false, false, false, false, false);
+            }
+            Layout layout = style.getReferenceFormat(entry.getType().getName());
+            try {
+                layout.setPostFormatter(OOUtil.postformatter);
+            } catch (NoSuchMethodError ex) {
+                
+            }
+            OOUtil.insertFullReferenceAtCurrentLocation(text, cursor, layout, parFormat, entry, database,
+                    uniquefiers.get(entry.getCiteKey()));
+        }
+        
+    }
+
+    public void insertFullReferenceAtViewCursor(BibtexDatabase database, List<BibtexEntry> entries,
+                                                OOBibStyle style, String parFormat) throws Exception {
+        XTextViewCursor xViewCursor = xViewCursorSupplier.getViewCursor();
+        insertFullReferenceAtCursor(xViewCursor, database, entries, style, parFormat);
+    }
+
+
+    public void insertMarkedUpTextAtViewCursor(String lText, String parFormat) throws Exception {
+        XTextViewCursor xViewCursor = xViewCursorSupplier.getViewCursor();
+        XTextCursor cursor = text.createTextCursorByRange(xViewCursor.getEnd());
+        OOUtil.insertOOFormattedTextAtCurrentLocation(text,cursor, lText, parFormat);
+        
+    }
+
+    /**
+     * This method creates and inserts an XTextSection named as determined by the
+     * string BIB_SECTION_NAME.
+     * @param end true to indicate that the section should be put at the end of the document,
+     *  false to indicate that it should be put at the cursor position.
+     * @return true if the bibliography already existed, false otherwise..
+     * @throws Exception
+     */
+    public boolean createBibTextSection(boolean end) throws Exception {
+        // Check if there already is a bookmarked section:
+        XBookmarksSupplier bSupp = (XBookmarksSupplier) UnoRuntime.queryInterface(
+                 XBookmarksSupplier.class, mxDoc);
+        if (bSupp.getBookmarks().hasByName(BIB_SECTION_NAME)) {
+            System.out.println("Found existing JabRef bookmark");
+            return true;
+        }
+        XTextCursor mxDocCursor = text.createTextCursor();
+        if (end)
+            mxDocCursor.gotoEnd(false);
+        OOUtil.insertParagraphBreak(text, mxDocCursor);
+        insertBookMark(BIB_SECTION_NAME, mxDocCursor);
+        return false;
+    }
+
+    public void clearBibTextSectionContent() throws Exception {
+        // Get a range comparator:
+        XTextRangeCompare compare = (XTextRangeCompare) UnoRuntime.queryInterface
+                (XTextRangeCompare.class, text);
+        // Find the bookmarks for the bibliography:
+        XTextRange range = getBookmarkRange(BIB_SECTION_NAME);
+        if (range == null)
+            createBibTextSection(atEnd);
+        XTextRange rangeEnd = getBookmarkRange(BIB_SECTION_END_NAME);
+        if (rangeEnd == null) {
+            // No end bookmark. This means that there is no bibliography.
+            return;
+        }
+        // Get a paragraph cursor at the start of the bibliography:
+        //System.out.println("text="+text+" range="+range);
+        XTextCursor mxDocCursor = text.createTextCursorByRange(range.getEnd());
+        mxDocCursor.goRight((short)1, true);
+        boolean couldExpand = true;
+        while (couldExpand && (compare.compareRegionEnds(mxDocCursor, rangeEnd) > 0)) {
+            couldExpand = mxDocCursor.goRight((short)1, true);
+        }
+        // Finally, clear the bibliography:
+        mxDocCursor.setString("");
+        mxDocCursor.collapseToStart();
+        removeBookMark(BIB_SECTION_END_NAME);
+        // If we lost the start bookmark, recreate it:
+        if (getBookmarkRange(BIB_SECTION_NAME) == null)
+            insertBookMark(BIB_SECTION_NAME, mxDocCursor);
+    }
+
+    public void populateBibTextSection(BibtexDatabase database, List<BibtexEntry> entries,
+                                       OOBibStyle style)
+            throws UndefinedParagraphFormatException, Exception {
+        XTextRange range = getBookmarkRange(BIB_SECTION_NAME);
+        XTextCursor cursor = text.createTextCursorByRange(range.getEnd());
+        OOUtil.insertTextAtCurrentLocation(text, cursor, (String)style.getProperty("Title"),
+                (String)style.getProperty("ReferenceHeaderParagraphFormat"));
+        insertFullReferenceAtCursor(cursor, database, entries, style,
+                (String)style.getProperty("ReferenceParagraphFormat"));
+        insertBookMark(BIB_SECTION_END_NAME, cursor);
+    }
+
+    public XTextContent insertBookMark(String name, XTextCursor position) throws Exception {
+        Object bookmark = mxDocFactory.createInstance("com.sun.star.text.Bookmark");
+        // name the bookmark
+        XNamed xNamed = (XNamed) UnoRuntime.queryInterface(
+                XNamed.class, bookmark);
+        xNamed.setName(name);
+        // get XTextContent interface
+        XTextContent xTextContent = (XTextContent) UnoRuntime.queryInterface(
+                XTextContent.class, bookmark);
+        // insert bookmark at the end of the document
+        // instead of mxDocText.getEnd you could use a text cursor's XTextRange interface or any XTextRange
+        text.insertTextContent(position, xTextContent, true);
+        position.collapseToEnd();
+        return xTextContent;
+    }
+
+    public void insertReferenceMark(String name, String citText, XTextCursor position, boolean withText,
+                               OOBibStyle style)
+            throws Exception {
+        Object bookmark = mxDocFactory.createInstance("com.sun.star.text.ReferenceMark");
+        // Name the reference
+        XNamed xNamed = (XNamed) UnoRuntime.queryInterface(
+                XNamed.class, bookmark);
+        xNamed.setName(name);
+        // get XTextContent interface
+        if (true) {
+
+            XTextContent xTextContent = (XTextContent) UnoRuntime.queryInterface(
+                    XTextContent.class, bookmark);
+            if (withText) {
+                position.setString(citText);
+                XPropertySet xCursorProps = (XPropertySet) UnoRuntime.queryInterface(
+                    XPropertySet.class, position);
+                // Set language to [None]:
+                xCursorProps.setPropertyValue("CharLocale", new Locale("zxx", "", ""));
+
+                // See if we should format the citation marker or not:
+                if (style.isFormatCitations()) {
+
+                    if (style.getBooleanCitProperty("SuperscriptCitations")) {
+                        xCursorProps.setPropertyValue("CharEscapement",
+                                (byte)101);
+                        xCursorProps.setPropertyValue("CharEscapementHeight",
+                                (byte)58);
+                    }
+                    else if (style.getBooleanCitProperty("SubscriptCitations")) {
+                        xCursorProps.setPropertyValue("CharEscapement",
+                                (byte)-101);
+                        xCursorProps.setPropertyValue("CharEscapementHeight",
+                                (byte)58);
+                    }
+                    else {
+                        xCursorProps.setPropertyValue("CharEscapement",
+                                (byte)0);
+                        xCursorProps.setPropertyValue("CharEscapementHeight",
+                                (byte)0);
+                    }
+
+                    xCursorProps.setPropertyValue("CharPosture",
+                            style.isItalicCitations() ? com.sun.star.awt.FontSlant.ITALIC :
+                                com.sun.star.awt.FontSlant.NONE);
+                    xCursorProps.setPropertyValue("CharWeight",
+                            style.isBoldCitations() ? com.sun.star.awt.FontWeight.BOLD :
+                                com.sun.star.awt.FontWeight.NORMAL);
+                    
+                }
+            }
+            else
+                position.setString("");
+
+
+            position.getText().insertTextContent(position, xTextContent, true);
+        }
+        position.collapseToEnd();
+
+    }
+
+    public void testFootnote() throws Exception {
+        XTextViewCursor xViewCursor = xViewCursorSupplier.getViewCursor();
+        insertFootnote("jabbes", "Cite text", xViewCursor);
+    }
+
+    public void insertFootnote(String name, String citText, XTextCursor position) throws Exception {
+        XFootnote xFootnote = (XFootnote) UnoRuntime.queryInterface( XFootnote.class,
+            mxDocFactory.createInstance("com.sun.star.text.Footnote"));
+        xFootnote.setLabel("");
+        XPropertySet props = (XPropertySet)UnoRuntime.queryInterface(XPropertySet.class, xFootnote);
+        props.setPropertyValue("ReferenceId", name); // doesn't work: short data type
+        System.out.println(props.getPropertyValue("ReferenceId"));
+        XTextContent xContent = (XTextContent)UnoRuntime.queryInterface(
+            XTextContent.class, xFootnote);
+        text.insertTextContent (position, xContent, false);
+        XSimpleText xSimple = (XSimpleText)UnoRuntime.queryInterface(XSimpleText.class, xFootnote);
+        XTextRange xRange = (XTextRange)UnoRuntime.queryInterface(XTextRange.class, xSimple.createTextCursor());
+        xSimple.insertString (xRange, citText, false);
+    }
+
+    public void removeBookMark(String name) throws Exception {
+        XBookmarksSupplier xBookmarksSupplier = (XBookmarksSupplier) UnoRuntime.queryInterface(
+                XBookmarksSupplier.class, xCurrentComponent);
+        if (xBookmarksSupplier.getBookmarks().hasByName(name)) {
+            Object o = xBookmarksSupplier.getBookmarks().getByName(name);
+            XTextContent bm = (XTextContent) UnoRuntime.queryInterface(
+                XTextContent.class, o);
+            text.removeTextContent(bm);
+        }
+    }
+
+    public void removeReferenceMark(String name) throws Exception {
+        XReferenceMarksSupplier xSupplier = (XReferenceMarksSupplier) UnoRuntime.queryInterface(
+                XReferenceMarksSupplier.class, xCurrentComponent);
+        if (xSupplier.getReferenceMarks().hasByName(name)) {
+            Object o = xSupplier.getReferenceMarks().getByName(name);
+            XTextContent bm = (XTextContent) UnoRuntime.queryInterface(
+                XTextContent.class, o);
+            text.removeTextContent(bm);
+        }
+    }
+
+    /**
+     * Get the XTextRange corresponding to the named bookmark.
+     * @param name The name of the bookmark to find.
+     * @return The XTextRange for the bookmark.
+     * @throws Exception
+     */
+    public XTextRange getBookmarkRange(String name) throws Exception {
+        // query XBookmarksSupplier from document model and get bookmarks collection
+        XBookmarksSupplier xBookmarksSupplier = (XBookmarksSupplier) UnoRuntime.queryInterface(
+                XBookmarksSupplier.class, xCurrentComponent);
+        XNameAccess xNamedBookmarks = xBookmarksSupplier.getBookmarks();
+
+        // retrieve bookmark by name
+        //System.out.println("Name="+name+" : "+xNamedBookmarks.hasByName(name));
+        if (!xNamedBookmarks.hasByName(name))
+            return null;
+        Object foundBookmark = xNamedBookmarks.getByName(name);
+        XTextContent xFoundBookmark = (XTextContent) UnoRuntime.queryInterface(
+                XTextContent.class, foundBookmark);
+        return xFoundBookmark.getAnchor();
+    }
+
+    public void printBookmarkNames() throws Exception {
+        XBookmarksSupplier xBookmarksSupplier = (XBookmarksSupplier) UnoRuntime.queryInterface(
+                XBookmarksSupplier.class, xCurrentComponent);
+        XNameAccess xNamedBookmarks = xBookmarksSupplier.getBookmarks();
+        String[] names = xNamedBookmarks.getElementNames();
+        for (int i = 0; i < names.length; i++) {
+            System.out.println(i+". "+names[i]);
+        }
+    }
+
+
+    /**
+     * Focus the active OO document.
+     */
+    public void setFocus() {
+        xDesktop.getCurrentFrame().getContainerWindow().setFocus();
+    }
+
+
+    public void combineCiteMarkers(BibtexDatabase database, OOBibStyle style) throws Exception {
+        XReferenceMarksSupplier supplier = (XReferenceMarksSupplier) UnoRuntime.queryInterface(
+                XReferenceMarksSupplier.class, xCurrentComponent);
+        XNameAccess nameAccess = supplier.getReferenceMarks();
+        // TODO: doesn't work for citations in footnotes/tables
+        String[] names = getSortedReferenceMarks(nameAccess);
+
+
+        final XTextRangeCompare compare = (XTextRangeCompare) UnoRuntime.queryInterface
+                (XTextRangeCompare.class, text);
+
+        int piv = 0;
+        boolean madeModifications = false;
+        while (piv < names.length-1) {
+            XTextRange r1 = ((XTextContent) UnoRuntime.queryInterface
+                            (XTextContent.class, nameAccess.getByName(names[piv]))).getAnchor().getEnd();
+            XTextRange r2 = ((XTextContent) UnoRuntime.queryInterface
+                            (XTextContent.class,
+                                    nameAccess.getByName(names[piv+1]))).getAnchor().getStart();
+            if (r1.getText() != r2.getText()) {
+                piv++;
+                continue;
+            }
+            XTextCursor mxDocCursor = r1.getText().createTextCursorByRange(r1);
+            mxDocCursor.goRight((short)1, true);
+            boolean couldExpand = true;
+            while (couldExpand && (compare.compareRegionEnds(mxDocCursor, r2) > 0)) {
+                couldExpand = mxDocCursor.goRight((short)1, true);
+            }
+            String text = mxDocCursor.getString();
+            // Check if the string contains no line breaks and only whitespace:
+            if ((text.indexOf('\n') == -1) && (text.trim().length() == 0)) {
+                List<String> keys = parseRefMarkName(names[piv]);
+                keys.addAll(parseRefMarkName(names[piv+1]));
+                removeReferenceMark(names[piv]);
+                removeReferenceMark(names[piv+1]);
+                ArrayList<BibtexEntry> entries = new ArrayList<BibtexEntry>();
+                for (String key : keys) {
+                    entries.add(OOUtil.createAdaptedEntry(database.getEntryByKey(key)));
+                }
+                Collections.sort(entries, new FieldComparator("year"));
+                StringBuilder sb = new StringBuilder();
+                int i=0;
+                for (BibtexEntry entry : entries) {
+                    if (i > 0)
+                        sb.append(",");
+                    sb.append(entry.getCiteKey());
+                    i++;
+                }
+                String keyString = sb.toString();
+                boolean inParenthesis = true;
+                // Insert bookmark:
+                String bName = getUniqueReferenceMarkName(keyString,
+                        inParenthesis ? AUTHORYEAR_PAR : AUTHORYEAR_INTEXT);
+                insertReferenceMark(bName, "tmp", mxDocCursor, true, style);
+                names[piv+1] = bName;
+                madeModifications = true;
+            }
+            piv++;
+        }
+        if (madeModifications) {
+            updateSortedReferenceMarks();
+            refreshCiteMarkers(database, style);
+        }
+
+
+    }
+
+    public void testFrameHandling() throws Exception {
+
+        XController oldController = mxDoc.getCurrentController();
+        PropertyValue[] props = new PropertyValue[2];
+
+        props[0] = new PropertyValue();
+        props[0].Name = "Model";
+
+        props[0].Value = mxDoc.getCurrentController().getModel();
+        props[1] = new PropertyValue();
+        props[1].Name = "Hidden";
+        props[1].Value = true;
+
+        // argument xModel wins over URL.
+        System.out.println("her");
+        XComponent comp = xComponentLoader.loadComponentFromURL("private:factory/swriter",
+                           "_blank", 0, props);
+        System.out.println("her2");
+
+        XTextDocument newDoc = (XTextDocument)UnoRuntime.queryInterface(
+                XTextDocument.class, comp);
+        System.out.println("newDoc = "+newDoc);
+
+        // Controller of the hidden frame
+        XController xController = newDoc.getCurrentController();
+
+        XFrame xFrame = xController.getFrame();
+        XWindow xContainerWindow = xFrame.getContainerWindow();
+        XWindow xComponentWindow = xFrame.getComponentWindow();
+
+        //xContainerWindow.setVisible(true);
+        //xComponentWindow.setFocus();
+        //xContainerWindow.setVisible(false);
+        xFrame.dispose();
+    }
+}
diff --git a/net/sf/jabref/oo/OOBibStyle.java b/net/sf/jabref/oo/OOBibStyle.java
new file mode 100755 (executable)
index 0000000..3cf8b4d
--- /dev/null
@@ -0,0 +1,840 @@
+package net.sf.jabref.oo;
+
+import net.sf.jabref.AuthorList;
+import net.sf.jabref.BibtexDatabase;
+import net.sf.jabref.BibtexEntry;
+import net.sf.jabref.Globals;
+import net.sf.jabref.export.layout.Layout;
+import net.sf.jabref.export.layout.LayoutFormatter;
+import net.sf.jabref.export.layout.LayoutHelper;
+import net.sf.jabref.export.layout.format.RemoveLatexCommands;
+
+import java.io.*;
+import java.util.*;
+import java.util.regex.Pattern;
+
+/**
+ * This class embodies a bibliography formatting for OpenOffice, which is composed
+ * of the following elements:
+ *
+ * 1) Each OO bib entry type must have a formatting. A formatting is an array of elements, each
+ *    of which is either a piece of constant text, an entry field value, or a tab. Each element has
+ *    a character format associated with it.
+ *
+ * 2) Many field values (e.g. author) need to be formatted before input to OpenOffice. The style
+ *    has the responsibility of formatting all field values. Formatting is handled by 0-n
+ *    JabRef LayoutFormatter classes.
+ *
+ * 3) If the entries are not numbered, a citation marker must be produced for each entry. This
+ *    operation is performed for each JabRef BibtexEntry.
+ */
+public class OOBibStyle implements Comparable {
+
+    public static final String UNDEFINED_CITATION_MARKER = "??";
+    String name = null;
+    SortedSet<String> journals = new TreeSet<String>();
+
+    Layout defaultBibLayout;
+    // formatters mapped from field names. Each field can have no mapping, or a set of formatters:
+    HashMap formatters = new HashMap();
+    // 0-n formatters applied to all fields before field-speficic formatters:
+    LayoutFormatter[] allBeforeFormat, allAfterFormat;
+    // reference layout mapped from entry type number:
+    HashMap<String, Layout> bibLayout = new HashMap<String, Layout>();
+
+    HashMap properties = new HashMap();
+    HashMap citProperties = new HashMap();
+
+    Pattern numPattern = Pattern.compile("-?\\d+");
+
+    boolean valid = false;
+
+    List juks = new ArrayList();
+
+    final static int NONE = 0, LAYOUT = 1, PROPERTIES=2, CITATION=3, NAME=4, JOURNALS=5;
+    final static String LAYOUT_MRK = "LAYOUT",
+        PROPERTIES_MARK = "PROPERTIES",
+        CITATION_MARK = "CITATION",
+        NAME_MARK = "NAME",
+        JOURNALS_MARK = "JOURNALS",
+        DEFAULT_MARK = "default";
+    private File styleFile = null;
+    private static long styleFileModificationTime = Long.MIN_VALUE;
+    private String COMBINED_ENTRIES_SEPARATOR = "-";
+
+    public OOBibStyle(File styleFile) throws Exception {
+        this(new FileReader(styleFile));
+        this.styleFile = styleFile;
+        styleFileModificationTime = (styleFile).lastModified();
+    }
+
+    public OOBibStyle(Reader in) throws Exception {
+
+        // Set default property values:
+        properties.put("Title", "Bibliography");
+        properties.put("SortAlgorithm", "alphanumeric");
+        properties.put("IsSortByPosition", Boolean.FALSE);
+        properties.put("IsNumberEntries", Boolean.FALSE);
+        properties.put("BracketBefore", "[");
+        properties.put("BracketAfter", "]");
+        properties.put("ReferenceParagraphFormat", "Default");
+        properties.put("ReferenceHeaderParagraphFormat", "Heading 1");
+
+        // Set default properties for the citation marker:
+        citProperties.put("AuthorField", "author/editor");
+        citProperties.put("YearField", "year");
+        citProperties.put("MaxAuthors", 3);
+        citProperties.put("MaxAuthorsFirst", -1);
+        citProperties.put("AuthorSeparator", ", ");
+        citProperties.put("AuthorLastSeparator", " & ");
+        citProperties.put("AuthorLastSeparatorInText", null);
+        citProperties.put("EtAlString", " et al.");
+        citProperties.put("YearSeparator", ", ");
+        citProperties.put("InTextYearSeparator", " ");
+        citProperties.put("BracketBefore", "(");
+        citProperties.put("BracketAfter", ")");
+        citProperties.put("CitationSeparator", "; ");
+        citProperties.put("GroupedNumbersSeparator", "-");
+        citProperties.put("MinimumGroupingCount", 3);
+        citProperties.put("FormatCitations", Boolean.FALSE);
+        citProperties.put("ItalicCitations", Boolean.FALSE);
+        citProperties.put("BoldCitations", Boolean.FALSE);
+        citProperties.put("SuperscriptCitations", Boolean.FALSE);
+        citProperties.put("SubscriptCitations", Boolean.FALSE);
+        citProperties.put("MultiCiteChronological", Boolean.TRUE);
+
+        initialize(in);
+
+
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public File getFile() {
+        return styleFile;
+    }
+
+    public Set<String> getJournals() {
+        return Collections.unmodifiableSet(journals);
+    }
+
+    private void initialize(Reader in) throws IOException {
+        name = null;
+        readFormatFile(in);
+        allBeforeFormat = new LayoutFormatter[] {new RemoveLatexCommands()};
+    }
+
+    /**
+     * If this style was initialized from a file on disk, reload the style
+     * if the file has been modified since it was read.
+     * @throws Exception
+     */
+    public void ensureUpToDate() throws Exception {
+        if (!isUpToDate())
+            reload();
+    }
+
+    /**
+     * If this style was initialized from a file on disk, reload the style
+     * information.
+     * @throws Exception
+     */
+    public void reload() throws Exception {
+        if (styleFile != null) {
+            styleFileModificationTime = (styleFile).lastModified();
+            initialize(new FileReader(styleFile));
+        }
+    }
+
+    /**
+     * If this style was initialized from a file on disk, check whether the file
+     * is unmodified since initialization.
+     * @return true if the file has not been modified, false otherwise.
+     */
+    public boolean isUpToDate() {
+        if (styleFile != null) {
+            return styleFile.lastModified() == styleFileModificationTime;
+        }
+        else return true;
+    }
+
+    private void readFormatFile(Reader in) throws IOException {
+
+        // First read all the contents of the file:
+        StringBuffer sb = new StringBuffer();
+        int c;
+        while ((c = in.read()) != -1) {
+            sb.append((char)c);
+        }
+        // Break into separate lines:
+        String[] lines = sb.toString().split("\n");
+        int mode = NONE;
+
+        for (int i = 0; i < lines.length; i++) {
+            String line = lines[i];
+            // Check for empty line or comment:
+            if ((line.trim().length() == 0) || (line.charAt(0) == '#'))
+                continue;
+            // Check if we should change mode:
+            if (line.equals(NAME_MARK)) {
+                mode = NAME;
+                continue;
+            }
+            else if (line.equals(LAYOUT_MRK)) {
+                mode = LAYOUT;
+                continue;
+            }
+            else if (line.equals(PROPERTIES_MARK)) {
+                mode = PROPERTIES;
+                continue;
+            }
+            else if (line.equals(CITATION_MARK)) {
+                mode = CITATION;
+                continue;
+            }
+            else if (line.equals(JOURNALS_MARK)) {
+                mode = JOURNALS;
+                continue;
+            }
+
+            switch (mode) {
+                case NAME:
+                    if (line.trim().length() > 0)
+                        name = line.trim();
+                case LAYOUT:
+                    handleStructureLine(line);
+                    break;
+                case PROPERTIES:
+                    handlePropertiesLine(line, properties);
+                    break;
+                case CITATION:
+                    handlePropertiesLine(line, citProperties);
+                    break;
+                case JOURNALS:
+                    handleJournalsLine(line);
+            }
+
+        }
+
+        // Set validity boolean based on whether we found anything interesting
+        // in the file:
+        if (mode != NONE)
+            valid = true;
+
+    }
+
+    /**
+     * After initalizing this style from a file, this method can be used to check
+     * whether the file appeared to be a proper style file.
+     * @return true if the file could be parsed as a style file, false otherwise.
+     */
+    public boolean isValid() {
+        return valid;
+    }
+
+
+    /**
+     * Parse a line providing bibliography structure information for an entry type.
+     * @param line The string containing the structure description.
+     * @throws IOException
+     */
+    private void handleStructureLine(String line) throws IOException {
+        int index = line.indexOf("=");
+        if ((index > 0) && (index < line.length()-1)) {
+            String formatString = line.substring(index+1);
+            //System.out.println("'"+line.substring(0, index)+"' : '"+formatString+"'");
+            boolean setDefault = line.substring(0, index).equals(DEFAULT_MARK);
+            String type = line.substring(0, index);
+            Short typeS;
+            try {
+                /*typeS = new Short(Short.parseShort(type));
+                OOBibFormatParser parser = new OOBibFormatParser(new StringReader(formatString));
+                PropertyValue[][] layout = parser.parse();*/
+                Layout layout = new LayoutHelper(new StringReader(formatString)).
+                        getLayoutFromText(Globals.FORMATTER_PACKAGE);
+                if (setDefault)
+                    defaultBibLayout = layout;
+                else
+                    bibLayout.put(type.toLowerCase(), layout);
+
+            } catch (Exception ex) {
+                ex.printStackTrace();
+
+            }
+        }
+    }
+
+
+    /**
+     * Parse a line providing a property name and value.
+     * @param line The line containing the formatter names.
+     * @throws IOException
+     */
+    private void handlePropertiesLine(String line, HashMap map) throws IOException {
+        int index = line.indexOf("=");
+        if ((index > 0) && (index <= line.length()-1)) {
+            String propertyName = line.substring(0, index).trim();
+            String value = line.substring(index+1);
+            Object toSet = value;
+            if (numPattern.matcher(value).matches()) {
+                toSet = Integer.parseInt(value);
+            }
+            else if (value.toLowerCase().trim().equals("true"))
+                toSet = Boolean.TRUE;
+            else if (value.toLowerCase().trim().equals("false"))
+                toSet = Boolean.FALSE;
+            map.put(propertyName, toSet);
+        }
+    }
+
+    /**
+     * Parse a line providing a journal name for which this style is valid.
+     * @param line
+     * @throws IOException
+     */
+    private void handleJournalsLine(String line) throws IOException {
+        if (line.trim().length() > 0)
+            journals.add(line.trim());
+    }
+
+    public Layout getReferenceFormat(String type) {
+        Layout l = bibLayout.get(type.toLowerCase());
+        if (l != null)
+            return l;
+        else
+            return defaultBibLayout;
+    }
+
+    /**
+     * Get the array of elements composing the reference for a given entry type.
+     * @param bibType The OO type number.
+     * @return The format definition.
+
+    public PropertyValue[][] getReferenceFormat(short bibType) {
+        Object o = bibLayout.get(new Short(bibType));
+        if (o != null)
+            return (PropertyValue[][])o;
+        else
+            return defaultBibLayout;
+    }*/
+
+    /**
+     * Format the given field value based on the rules of this bib style.
+     * @param field The name of the field.
+     * @param content The unformatted field content.
+     * @return The formatted field value.
+     */
+    public String formatField(String field, String content) {
+        if (allBeforeFormat != null)
+            for (int i = 0; i < allBeforeFormat.length; i++) {
+                LayoutFormatter formatter = allBeforeFormat[i];
+                content = formatter.format(content);
+            }
+
+        Object o = formatters.get(field);
+        if (o == null)
+            return content;
+        LayoutFormatter[] form = (LayoutFormatter[])o;
+        for (int i = 0; i < form.length; i++) {
+            LayoutFormatter formatter = form[i];
+            content = formatter.format(content);
+        }
+
+        if (allAfterFormat != null)
+            for (int i = 0; i < allAfterFormat.length; i++) {
+                LayoutFormatter formatter = allAfterFormat[i];
+                content = formatter.format(content);
+            }
+
+        return content;
+    }
+
+    /**
+     * Format a number-based citation marker for the given number.
+     * @param number The citation numbers.
+     * @return The text for the citation.
+     */
+    public String getNumCitationMarker(int[] number, int minGroupingCount, boolean inList) {
+        String bracketBefore = (String)citProperties.get("BracketBefore");
+        if (inList && (citProperties.get("BracketBeforeInList")!=null)) {
+            bracketBefore = (String)citProperties.get("BracketBeforeInList");
+        }
+        String bracketAfter = (String)citProperties.get("BracketAfter");
+        if (inList && (citProperties.get("BracketAfterInList")!=null)) {
+            bracketAfter = (String)citProperties.get("BracketAfterInList");
+        }
+        // Sort the numbers:
+        int[] lNum = new int[number.length];
+        for (int i = 0; i < lNum.length; i++) {
+            lNum[i] = number[i];
+
+        }
+        //Arrays.copyOf(number, number.length);
+        Arrays.sort(lNum);
+        StringBuilder sb = new StringBuilder(bracketBefore);
+        int combineFrom = -1, written = 0;
+        for (int i = 0; i < lNum.length; i++) {
+            int i1 = lNum[i];
+            if (combineFrom < 0) {
+                // Check if next entry is the next in the ref list:
+                if ((i < lNum.length-1) && (lNum[i+1] == i1+1))
+                    combineFrom = i1;
+                else {
+                    // Add single entry:
+                    if (i>0)
+                        sb.append((String)citProperties.get("CitationSeparator"));
+                    sb.append(lNum[i] > 0 ? String.valueOf(lNum[i]) : UNDEFINED_CITATION_MARKER);
+                    written++;
+                }
+            } else {
+                // We are building a list of combined entries.
+                // Check if it ends here:
+                if ((i == lNum.length-1) || (lNum[i+1] != i1+1)) {
+                    if (written>0)
+                        sb.append((String)citProperties.get("CitationSeparator"));
+                    if ((minGroupingCount > 0) && (i1+1-combineFrom >= minGroupingCount)) {
+                        sb.append(combineFrom);
+                        sb.append((String)citProperties.get("GroupedNumbersSeparator"));
+                        sb.append(i1);
+                        written++;
+                    }
+                    else {
+                        // Either we should never group, or there aren't enough
+                        // entries in this case to group. Output all:
+                        for (int jj=combineFrom; jj<=i1; jj++) {
+                            sb.append(jj);
+                            if (jj < i1)
+                                sb.append((String)citProperties.get("CitationSeparator"));
+                            written++;
+                        }
+                    }
+                    combineFrom = -1;
+
+                }
+                // If it doesn't end here, just keep iterating.
+            }
+
+        }
+        sb.append(bracketAfter);
+        return sb.toString();
+    }
+
+    /**
+     * Format the marker for the in-text citation according to this bib style.
+     *
+     * @param entry The JabRef BibtexEntry providing the data.
+     * @param inParenthesis Signals whether a parenthesized citation or an in-text citation is wanted.
+     * @param uniquefier String to add behind the year in case it's needed to separate similar
+     *   entries.
+     * @return The formatted citation.
+     */
+    public String getCitationMarker(BibtexEntry entry, BibtexDatabase database, boolean inParenthesis,
+                                    String uniquefier, int unlimAuthors) {
+        return getCitationMarker(new BibtexEntry[] {entry}, database, inParenthesis, new String[] {uniquefier},
+                new int[] {unlimAuthors});
+    }
+
+    /**
+     * Format the marker for the in-text citation according to this bib style. Uniquefier letters are added as
+     * provided by the uniquefiers argument. If successive entries within the citation are uniquefied from each other,
+     * this method will perform a grouping of these entries.
+     *
+     * @param entries The array of JabRef BibtexEntry providing the data.
+     * @param inParenthesis Signals whether a parenthesized citation or an in-text citation is wanted.
+     * @param uniquefiers Strings to add behind the year for each entry in case it's needed to separate similar
+     *   entries.
+     * @param unlimAuthors Boolean for each entry. If true, we should not use "et al" formatting regardless
+     *   of the number of authors. Can be null to indicate that no entries should have unlimited names.
+     * @return The formatted citation.
+     */
+    public String getCitationMarker(BibtexEntry[] entries, BibtexDatabase database, boolean inParenthesis,
+                                    String[] uniquefiers, int[] unlimAuthors) {
+
+        // Look for groups of uniquefied entries that should be combined in the output.
+        // E.g. (Olsen, 2005a, b) should be output instead of (Olsen, 2005a; Olsen, 2005b).
+        int piv = -1;
+        String tmpMarker = null;
+        if (uniquefiers != null) {
+            for (int i = 0; i < uniquefiers.length; i++) {
+
+                if ((uniquefiers[i] != null) && (uniquefiers[i].length() > 0)) {
+                    String authorField = (String)citProperties.get("AuthorField");
+                    int maxAuthors = (Integer)citProperties.get("MaxAuthors");
+                    if (piv == -1) {
+                        piv = i;
+                        tmpMarker = getAuthorYearParenthesisMarker(new BibtexEntry[] {entries[i]}, database,
+                            authorField,
+                            (String)citProperties.get("YearField"),
+                            maxAuthors,
+                            (String)citProperties.get("AuthorSeparator"),
+                            (String)citProperties.get("AuthorLastSeparator"),
+                            (String)citProperties.get("EtAlString"),
+                            (String)citProperties.get("YearSeparator"),
+                            (String)citProperties.get("BracketBefore"),
+                            (String)citProperties.get("BracketAfter"),
+                            (String)citProperties.get("CitationSeparator"), null, unlimAuthors);
+                        //System.out.println("piv="+piv+" tmpMarker='"+tmpMarker+"'");
+                    }
+                    else {
+                        // See if this entry can go into a group with the previous one:
+                        String thisMarker = getAuthorYearParenthesisMarker(new BibtexEntry[] {entries[i]}, database,
+                            authorField,
+                            (String)citProperties.get("YearField"),
+                            maxAuthors,
+                            (String)citProperties.get("AuthorSeparator"),
+                            (String)citProperties.get("AuthorLastSeparator"),
+                            (String)citProperties.get("EtAlString"),
+                            (String)citProperties.get("YearSeparator"),
+                            (String)citProperties.get("BracketBefore"),
+                            (String)citProperties.get("BracketAfter"),
+                            (String)citProperties.get("CitationSeparator"), null, unlimAuthors);
+
+                        String author = getCitationMarkerField(entries[i], database,
+                                authorField);
+                        AuthorList al = AuthorList.getAuthorList(author);
+                        //System.out.println("i="+i+" thisMarker='"+thisMarker+"'");
+                        int prevALim = i > 0 ? unlimAuthors[i-1] : unlimAuthors[0];
+                        if (!thisMarker.equals(tmpMarker) ||
+                                ((al.size() > maxAuthors) && (unlimAuthors[i] != prevALim))) {
+                            // No match. Update piv to exclude the previous entry. But first check if the
+                            // previous entry was part of a group:
+                            if ((piv > -1) && (i > piv+1)) {
+                                // Do the grouping:
+                                group(entries, uniquefiers, piv, i-1, (String)citProperties.get("UniquefierSeparator"));
+                            }
+                            tmpMarker = thisMarker;
+                            piv = i;
+                        }
+                    }
+                }
+                else {
+                    // This entry has no uniquefier.
+                    // Check if we just passed a group of more than one entry with uniquefier:
+                    if ((piv > -1) && (i > piv+1)) {
+                        // Do the grouping:
+                        group(entries, uniquefiers, piv, i-1, (String)citProperties.get("UniquefierSeparator"));
+                    }
+
+                    piv = -1;
+                }
+
+            }
+            // Finished with the loop. See if the last entries form a group:
+            if (piv >= 0) {
+                // Do the grouping:
+                group(entries, uniquefiers, piv, uniquefiers.length-1, (String)citProperties.get("UniquefierSeparator"));
+            }
+        }
+
+        if (inParenthesis)
+            return getAuthorYearParenthesisMarker(entries, database,
+                    (String)citProperties.get("AuthorField"),
+                    (String)citProperties.get("YearField"),
+                    (Integer)citProperties.get("MaxAuthors"),
+                    (String)citProperties.get("AuthorSeparator"),
+                    (String)citProperties.get("AuthorLastSeparator"),
+                    (String)citProperties.get("EtAlString"),
+                    (String)citProperties.get("YearSeparator"),
+                    (String)citProperties.get("BracketBefore"),
+                    (String)citProperties.get("BracketAfter"),
+                    (String)citProperties.get("CitationSeparator"),
+                    uniquefiers, unlimAuthors);
+        else {
+            String authorLastSeparator = (String)citProperties.get("AuthorLastSeparator");
+            String alsInText = (String)citProperties.get("AuthorLastSeparatorInText");
+            if (alsInText != null)
+                authorLastSeparator = alsInText;
+            return getAuthorYearInTextMarker(entries, database,
+                    (String)citProperties.get("AuthorField"),
+                    (String)citProperties.get("YearField"),
+                    (Integer)citProperties.get("MaxAuthors"),
+                    (String)citProperties.get("AuthorSeparator"),
+                    authorLastSeparator,
+                    (String)citProperties.get("EtAlString"),
+                    (String)citProperties.get("InTextYearSeparator"),
+                    (String)citProperties.get("BracketBefore"),
+                    (String)citProperties.get("BracketAfter"),
+                    (String)citProperties.get("CitationSeparator"),
+                    uniquefiers, unlimAuthors);
+        }
+    }
+
+    /**
+     * Modify entry and uniqiefier arrays to facilitate a grouped presentation of uniqiefied entries.
+     * @param entries The entry array.
+     * @param uniquefiers The uniquefier array.
+     * @param from The first index to group (inclusive)
+     * @param to The last index to group (inclusive)
+     * @param separator The separator for the uniquefier letters.
+     */
+    private void group(BibtexEntry[] entries, String[] uniquefiers, int from, int to, String separator) {
+        StringBuilder sb = new StringBuilder(uniquefiers[from]);
+        for (int i=from+1; i<=to; i++) {
+            sb.append(separator);
+            sb.append(uniquefiers[i]);
+            entries[i] = null;
+        }
+        uniquefiers[from] = sb.toString();
+    }
+
+    /**
+     * This method produces (Author, year) style citation strings in many different forms.
+     *
+     * @param entries The array of BibtexEntry to get fields from.
+     * @param authorField The bibtex field providing author names, e.g. "author" or "editor".
+     * @param yearField The bibtex field providing the year, e.g. "year".
+     * @param maxA The maximum number of authors to write out in full without using etal. Set to
+     *              -1 to always write out all authors.
+     * @param authorSep The String to add between author names except the last two, e.g. ", ".
+     * @param andString The String to add between the two last author names, e.g. " & ".
+     * @param etAlString The String to represent authors that are not mentioned, e.g. " et al."
+     * @param yearSep The String to separate authors from year, e.g. "; ".
+     * @param startBrace The opening parenthesis.
+     * @param endBrace The closing parenthesis.
+     * @param citationSeparator The String to separate citations from each other.
+     * @param uniquifiers Optional parameter to separate similar citations. Elements can be null if not needed.
+     * @return The formatted citation.
+     */
+    public String getAuthorYearParenthesisMarker(BibtexEntry[] entries, BibtexDatabase database,
+                                                 String authorField, String yearField,
+                                                 int maxA, String authorSep,
+                                                 String andString, String etAlString, String yearSep,
+                                                 String startBrace, String endBrace, String citationSeparator,
+                                                 String[] uniquifiers, int[] unlimAuthors) {
+
+
+        StringBuffer sb = new StringBuffer(startBrace);
+        for (int j=0; j<entries.length; j++) {
+
+            int unlimA = (unlimAuthors != null ? unlimAuthors[j] : -1);
+            int maxAuthors = unlimA > 0 ? unlimA : maxA;
+
+            BibtexEntry entry = entries[j];
+            
+            // Check if this entry has been nulled due to grouping with the previous entry(ies):
+            if (entry == null)
+                continue;
+
+            if (j > 0)
+                sb.append(citationSeparator);
+
+            String author = getCitationMarkerField(entry, database, authorField);
+
+            if (author != null) {
+                AuthorList al = AuthorList.getAuthorList(author);
+                sb.append(getAuthorLastName(al, 0));
+
+                if ((al.size() > 1) && ((al.size() <= maxAuthors) || (maxAuthors < 0))) {
+                    int i=1;
+                    while (i < al.size()-1) {
+                        sb.append(authorSep);
+                        sb.append(getAuthorLastName(al, i));
+                        i++;
+                    }
+                    sb.append(andString);
+                    sb.append(getAuthorLastName(al, al.size()-1));
+                } else if (al.size() > maxAuthors) {
+                    sb.append(etAlString);
+                }
+                sb.append(yearSep);
+            }
+            String year = getCitationMarkerField(entry, database, yearField);
+            if (year != null)
+                sb.append(year);
+            if ((uniquifiers != null) && (uniquifiers[j] != null))
+                sb.append(uniquifiers[j]);
+        }
+        sb.append(endBrace);
+        return sb.toString();
+
+    }
+
+    /**
+     * This method produces "Author (year)" style citation strings in many different forms.
+     *
+     * @param entries The array of BibtexEntry to get fields from.
+     * @param authorField The bibtex field providing author names, e.g. "author" or "editor".
+     * @param yearField The bibtex field providing the year, e.g. "year".
+     * @param maxA The maximum number of authors to write out in full without using etal. Set to
+     *              -1 to always write out all authors.
+     * @param authorSep The String to add between author names except the last two, e.g. ", ".
+     * @param andString The String to add between the two last author names, e.g. " & ".
+     * @param etAlString The String to represent authors that are not mentioned, e.g. " et al."
+     * @param yearSep The String to separate authors from year, e.g. "; ".
+     * @param startBrace The opening parenthesis.
+     * @param endBrace The closing parenthesis.
+     * @param uniquefiers Optional parameters to separate similar citations. Can be null if not needed.
+     * @return The formatted citation.
+     */
+    public String getAuthorYearInTextMarker(BibtexEntry[] entries, BibtexDatabase database, String authorField,
+                                            String yearField, int maxA, String authorSep,
+                                            String andString, String etAlString, String yearSep,
+                                            String startBrace, String endBrace, String citationSeparator,
+                                            String[] uniquefiers, int[] unlimAuthors) {
+        StringBuffer sb = new StringBuffer();
+        for (int i=0; i<entries.length; i++) {
+
+            int unlimA = (unlimAuthors != null ? unlimAuthors[i] : -1);
+            int maxAuthors = unlimA > 0 ? unlimA : maxA;
+
+            // Check if this entry has been nulled due to grouping with the previous entry(ies):
+            if (entries[i] == null)
+                continue;
+
+            if (i > 0)
+                sb.append(citationSeparator);
+            String author = getCitationMarkerField(entries[i], database, authorField);
+            if (author != null) {
+                AuthorList al = AuthorList.getAuthorList(author);
+                if (al.size() > 0)
+                    sb.append(getAuthorLastName(al, 0));
+                if ((al.size() > 1) && ((al.size() <= maxAuthors) || (maxAuthors < 0))) {
+                    int j=1;
+                    while (j < al.size()-1) {
+                        sb.append(authorSep);
+                        sb.append(getAuthorLastName(al, j));
+                        j++;
+                    }
+                    sb.append(andString);
+                    sb.append(getAuthorLastName(al, al.size()-1));
+                } else if (al.size() > maxAuthors) {
+                    sb.append(etAlString);
+                }
+                sb.append(yearSep);
+            }
+            sb.append(startBrace);
+            String year = getCitationMarkerField(entries[i], database, yearField);
+            if (year != null)
+                sb.append(year);
+            if ((uniquefiers != null) && (uniquefiers[i] != null))
+                sb.append(uniquefiers[i]);
+            sb.append(endBrace);
+        }
+        return sb.toString();
+
+    }
+
+    /**
+     * This method looks up a field for en entry in a database. Any number of backup fields can be used
+     * if the primary field is empty.
+     * @param entry The entry.
+     * @param database The database the entry belongs to.
+     * @param field The field, or succession of fields, to look up. If backup fields are needed, separate
+     *   field names by /. E.g. to use "author" with "editor" as backup, specify "author/editor".
+     * @return The resolved field content, or an empty string if the field(s) were empty.
+     */
+    public String getCitationMarkerField(BibtexEntry entry, BibtexDatabase database, String field) {
+        String[] fields = field.split("/");
+        for (int i = 0; i < fields.length; i++) {
+            String s = fields[i];
+            String content = BibtexDatabase.getResolvedField(s, entry, database);
+            if ((content != null) && (content.trim().length() > 0))
+                return content;
+        }
+        // No luck? Return an empty string:
+        return "";
+    }
+
+    /**
+     * Look up the nth author and return the proper last name for citation markers.
+     * @param al The author list.
+     * @param number The number of the author to return.
+     * @return The author name, or an empty String if inapplicable.
+     */
+    public String getAuthorLastName(AuthorList al, int number) {
+        StringBuilder sb = new StringBuilder();
+
+        if (al.size() > number) {
+            AuthorList.Author a = al.getAuthor(number);
+            if ((a.getVon() != null) && a.getVon().length() > 0) {
+                String von = a.getVon();
+                sb.append(von);
+                /*sb.append(von.substring(0, 1).toUpperCase());
+                if (von.length() > 1)
+                    sb.append(von.substring(1));*/
+                sb.append(' ');
+            }
+            sb.append(a.getLast());
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Convenience method for checking the property for whether we use number citations or
+     * author-year citations.
+     * @return true if we use numbered citations, false otherwise.
+     */
+    public boolean isNumberEntries() {
+        return (Boolean)getProperty("IsNumberEntries");
+    }
+
+    /**
+     * Convenience method for checking the property for whether we sort the bibliography
+     * according to their order of appearance in the text.
+     * @return true to sort by appearance, false to sort alphabetically.
+     */
+    public boolean isSortByPosition() {
+        return (Boolean)getProperty("IsSortByPosition");
+    }
+
+    /**
+     * Convenience method for checking whether citation markers should be italicised.
+     * Will only be relevant if isFormatCitations() returns true.
+     * @return true to indicate that citations should be in italics.
+     */
+    public boolean isItalicCitations() {
+        return (Boolean)citProperties.get("ItalicCitations");
+    }
+
+    /**
+     * Convenience method for checking whether citation markers should be bold.
+     * Will only be relevant if isFormatCitations() returns true.
+     * @return true to indicate that citations should be in bold.
+     */
+    public boolean isBoldCitations() {
+        return (Boolean)citProperties.get("BoldCitations");
+    }
+
+    /**
+     * Convenience method for checking whether citation markers formatted
+     * according to the results of the isItalicCitations() and
+     * isBoldCitations() methods.
+     * @return true to indicate that citations should be in italics.
+     */
+    public boolean isFormatCitations() {
+        return (Boolean)citProperties.get("FormatCitations");
+    }
+
+    /**
+     * Get boolean property.
+     * @param key The property key
+     * @return the value
+     */
+    public boolean getBooleanCitProperty(String key) {
+        return (Boolean)citProperties.get(key);
+    }
+
+    public int getIntCitProperty(String key) {
+        return (Integer)citProperties.get(key);
+    }
+    /**
+     * Get a style property.
+     * @param name The property name.
+     * @return The property value, or null if it doesn't exist.
+     */
+    public Object getProperty(String name) {
+        return properties.get(name);
+    }
+
+    public int compareTo(Object o) {
+        OOBibStyle other = (OOBibStyle)o;
+        return getName().compareTo(other.getName());
+    }
+
+    public boolean equals(Object o) {
+        return styleFile.equals(((OOBibStyle)o).styleFile);
+    }
+}
diff --git a/net/sf/jabref/oo/OOUtil.java b/net/sf/jabref/oo/OOUtil.java
new file mode 100755 (executable)
index 0000000..c2ca2e5
--- /dev/null
@@ -0,0 +1,285 @@
+package net.sf.jabref.oo;
+
+import com.sun.star.beans.XPropertySet;
+import com.sun.star.beans.Property;
+import com.sun.star.text.*;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.frame.XDesktop;
+import net.sf.jabref.BibtexDatabase;
+import net.sf.jabref.BibtexEntry;
+import net.sf.jabref.Globals;
+import net.sf.jabref.BibtexFields;
+import net.sf.jabref.export.layout.Layout;
+
+import javax.swing.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.List;
+import java.util.Iterator;
+
+/**
+ * Utility methods for processing OO Writer documents.
+ */
+public class OOUtil {
+
+    
+    static Pattern htmlTag = Pattern.compile("</?[a-z]+>");
+
+    static OOPreFormatter postformatter = new OOPreFormatter();
+
+    /**
+     * Insert a reference, formatted using a Layout, at the position of a given cursor.
+     * @param text The text to insert in.
+     * @param cursor The cursor giving the insert location.
+     * @param layout The Layout to format the reference with.
+     * @param parStyle The name of the paragraph style to use.
+     * @param entry The entry to insert.
+     * @param database The database the entry belongs to.
+     * @param uniquefier Uniqiefier letter, if any, to append to the entry's year.
+     * @throws Exception
+     */
+    public static void insertFullReferenceAtCurrentLocation(XText text, XTextCursor cursor,
+            Layout layout, String parStyle, BibtexEntry entry, BibtexDatabase database, String uniquefier)
+            throws UndefinedParagraphFormatException, Exception {
+
+        final String UNIQUEFIER_FIELD = "uniq";
+
+        // Backup the value of the uniq field, just in case the entry already has it:
+        String oldUniqVal = (String)entry.getField(UNIQUEFIER_FIELD);
+
+        // Set the uniq field with the supplied uniquefier:
+        entry.setField(UNIQUEFIER_FIELD, uniquefier);
+
+        // Do the layout for this entry:
+        String lText = layout.doLayout(entry, database);
+
+        // Afterwards, reset the old value:
+        entry.setField(UNIQUEFIER_FIELD, oldUniqVal);
+
+        // Insert the formatted text:
+        insertOOFormattedTextAtCurrentLocation(text, cursor, lText, parStyle);
+    }
+
+    /**
+     * Insert a text with formatting indicated by HTML-like tags, into a text at
+         * the position given by a cursor.
+     * @param text The text to insert in.
+     * @param cursor The cursor giving the insert location.
+     * @param lText The marked-up text to insert.
+     * @param parStyle The name of the paragraph style to use.
+     * @throws Exception
+     */
+    public static void insertOOFormattedTextAtCurrentLocation(XText text, XTextCursor cursor,
+              String lText, String parStyle) throws UndefinedParagraphFormatException, Exception {
+
+        XParagraphCursor parCursor = (XParagraphCursor)UnoRuntime.queryInterface(
+            XParagraphCursor.class, cursor);
+        XPropertySet props = (XPropertySet) UnoRuntime.queryInterface(
+            XPropertySet.class, parCursor);
+
+        try {
+            props.setPropertyValue("ParaStyleName", parStyle);
+        } catch (com.sun.star.lang.IllegalArgumentException ex) {
+            throw new UndefinedParagraphFormatException(parStyle);
+        }
+        
+        // We need to extract formatting. Use a simple regexp search iteration:
+        int piv = 0;
+        int italic = 0, bold = 0, sup = 0, sub = 0, mono = 0, smallCaps = 0;
+        //insertTextAtCurrentLocation(text, cursor, "_",
+        //    false, false, false, false, false, false);
+        //cursor.goLeft((short)1, true);
+        Matcher m = htmlTag.matcher(lText);
+        while (m.find()) {
+            String ss = lText.substring(piv, m.start());
+            if (ss.length() > 0) {
+                insertTextAtCurrentLocation(text, cursor, ss, (bold % 2) > 0, (italic % 2) > 0,
+                        mono > 0, smallCaps > 0, sup > 0, sub > 0);
+            }
+            String tag = m.group();
+            // Handle tags:
+            if (tag.equals("<b>"))
+                bold++;
+            else if (tag.equals("</b>"))
+                bold--;
+            else if (tag.equals("<i>") || tag.equals("<em>"))
+                italic++;
+            else if (tag.equals("</i>") || tag.equals("</em>"))
+                italic--;
+            else if (tag.equals("</monospace>"))
+                mono = 0;
+            else if (tag.equals("<monospace>"))
+                mono = 1;
+            else if (tag.equals("</smallcaps>"))
+                smallCaps = 0;
+            else if (tag.equals("<smallcaps>"))
+                smallCaps = 1;
+            else if (tag.equals("</sup>"))
+                sup = 0;
+            else if (tag.equals("<sup>"))
+                sup = 1;
+            else if (tag.equals("</sub>"))
+                sub = 0;
+            else if (tag.equals("<sub>"))
+                sub = 1;
+
+            piv = m.end();
+            
+        }
+
+        if (piv < lText.length())
+            insertTextAtCurrentLocation(text, cursor,lText.substring(piv),
+                    (bold % 2) > 0, (italic % 2) > 0, mono > 0, smallCaps > 0, sup > 0, sub > 0);
+
+
+
+        cursor.collapseToEnd();
+    }
+
+    public static void insertParagraphBreak(XText text, XTextCursor cursor) throws Exception {
+        text.insertControlCharacter(cursor, ControlCharacter.PARAGRAPH_BREAK, true);
+        cursor.collapseToEnd();
+    }
+
+    public static void insertTextAtCurrentLocation(XText text, XTextCursor cursor, String string,
+                   boolean bold, boolean italic, boolean monospace, boolean smallCaps, boolean superscript,
+                   boolean subscript) throws Exception {
+        text.insertString(cursor, string, true);
+        // Access the property set of the cursor, and set the currently selected text
+        // (which is the string we just inserted) to be bold
+        XPropertySet xCursorProps = (XPropertySet) UnoRuntime.queryInterface(
+            XPropertySet.class, cursor);
+        if (bold)
+            xCursorProps.setPropertyValue("CharWeight",
+                    new Float(com.sun.star.awt.FontWeight.BOLD));
+        else
+            xCursorProps.setPropertyValue("CharWeight",
+                    new Float(com.sun.star.awt.FontWeight.NORMAL));
+
+        if (italic)
+            xCursorProps.setPropertyValue("CharPosture",
+                            com.sun.star.awt.FontSlant.ITALIC);
+        else
+            xCursorProps.setPropertyValue("CharPosture",
+                            com.sun.star.awt.FontSlant.NONE);
+
+        if (smallCaps) {
+            xCursorProps.setPropertyValue("CharCaseMap",
+                            com.sun.star.style.CaseMap.SMALLCAPS);
+        }
+        else {
+            xCursorProps.setPropertyValue("CharCaseMap",
+                            com.sun.star.style.CaseMap.NONE);
+        }
+
+        // TODO: the <monospace> tag doesn't work
+        /*
+        if (monospace) {
+            xCursorProps.setPropertyValue("CharFontPitch",
+                            com.sun.star.awt.FontPitch.FIXED);
+        }
+        else {
+            xCursorProps.setPropertyValue("CharFontPitch",
+                            com.sun.star.awt.FontPitch.VARIABLE);
+        } */
+        if (subscript) {
+            xCursorProps.setPropertyValue("CharEscapement",
+                    (byte)-101);
+            xCursorProps.setPropertyValue("CharEscapementHeight",
+                    (byte)58);
+        }
+        else if (superscript) {
+            xCursorProps.setPropertyValue("CharEscapement",
+                    (byte)101);
+            xCursorProps.setPropertyValue("CharEscapementHeight",
+                    (byte)58);
+        }
+        else {
+            xCursorProps.setPropertyValue("CharEscapement",
+                    (byte)0);
+            xCursorProps.setPropertyValue("CharEscapementHeight",
+                    (byte)100);
+        }
+
+        cursor.collapseToEnd();
+
+    }
+
+    public static void insertTextAtCurrentLocation(XText text, XTextCursor cursor, String string,
+                                                   String parStyle) throws Exception {
+        text.insertString(cursor, string, true);
+        XParagraphCursor parCursor = (XParagraphCursor)UnoRuntime.queryInterface(
+            XParagraphCursor.class, cursor);
+        // Access the property set of the cursor, and set the currently selected text
+        // (which is the string we just inserted) to be bold
+        XPropertySet props = (XPropertySet) UnoRuntime.queryInterface(
+            XPropertySet.class, parCursor);
+        try {
+            props.setPropertyValue("ParaStyleName", parStyle);
+        } catch (com.sun.star.lang.IllegalArgumentException ex) {
+            throw new UndefinedParagraphFormatException(parStyle);
+        }
+        cursor.collapseToEnd();
+
+    }
+
+
+
+    public static Object getProperty(Object o, String property) throws Exception {
+        XPropertySet props = (XPropertySet) UnoRuntime.queryInterface(
+                XPropertySet.class, o);
+        return props.getPropertyValue(property);
+    }
+
+    public static void listProperties(Object o) throws Exception {
+        XPropertySet props = (XPropertySet) UnoRuntime.queryInterface(
+                XPropertySet.class, o);
+        Property[] pr = props.getPropertySetInfo().getProperties();
+        for (int i = 0; i < pr.length; i++) {
+            Property property1 = pr[i];
+            System.out.println(property1.Name+" : "+props.getPropertyValue(property1.Name));
+        }
+    }
+
+    public static XTextDocument selectComponent(JFrame parent, XDesktop xDesktop, List<XTextDocument> list) throws Exception {
+        String[] values = new String[list.size()];
+        int ii=0;
+        for (Iterator<XTextDocument> iterator = list.iterator(); iterator.hasNext();) {
+            XTextDocument doc = iterator.next();
+            values[ii++] = String.valueOf(getProperty(doc.getCurrentController().getFrame(), "Title"));
+        }
+        JList sel = new JList(values);
+        sel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+        sel.setSelectedIndex(0);
+        int ans = JOptionPane.showConfirmDialog(parent, new JScrollPane(sel), Globals.lang("Select document"),
+                JOptionPane.OK_CANCEL_OPTION);
+        if (ans == JOptionPane.OK_OPTION) {
+            return list.get(sel.getSelectedIndex());
+        }
+        else return null;
+    }
+
+    /**
+     * Make a cloned BibtexEntry and do the necessary preprocessing for use by the plugin.
+     * If the running JabRef version doesn't support post-processing in Layout, this
+     * preprocessing includes running the OOPreFormatter formatter for all fields except the
+     * BibTeX key.
+     * @param entry the original entry
+     * @return the cloned and processed entry
+     */
+    public static BibtexEntry createAdaptedEntry(BibtexEntry entry) {
+        if (entry == null)
+            return null;
+        BibtexEntry e = (BibtexEntry)entry.clone();
+        for (String field : e.getAllFields()) {
+            if (field.equals(BibtexFields.KEY_FIELD))
+                continue;
+            String value = e.getField(field);
+            // If the running JabRef version doesn't support post-processing in Layout,
+            // preprocess fields instead:
+            if (!OOTestPanel.postLayoutSupported && (value != null))
+                e.setField(field, postformatter.format(value));
+        }
+        return e;
+    }
+}
diff --git a/plugin.xml b/plugin.xml
new file mode 100755 (executable)
index 0000000..5f967c6
--- /dev/null
@@ -0,0 +1,32 @@
+<?xml version="1.0" ?>
+<!DOCTYPE plugin PUBLIC "-//JPF//Java Plug-in Manifest 1.0" "http://jpf.sourceforge.net/plugin_1_0.dtd">
+<plugin id="net.sf.jabref.oo.ooplugin" version="0.7.4">
+
+       <requires>
+               <import plugin-id="net.sf.jabref.core"/>
+       </requires>
+
+       <runtime>
+               <library type="code" path="./" id="code"/>
+    </runtime>
+
+       <extension id="OpenOfficePlugin" plugin-id="net.sf.jabref.core" point-id="SidePanePlugin">
+        <parameter id="sidePanePlugin" value="net.sf.jabref.oo.OOTestPanel"/>
+        <parameter id="name" value="Interface to OpenOffice Writer"/>
+        <parameter id="description" value=""/>
+    </extension>
+
+    <extension id="PushToOpenOfficePlugin" plugin-id="net.sf.jabref.core" point-id="PushToApplication">
+        <parameter id="pushToApp" value="net.sf.jabref.oo.OOTestPanel"/>
+        <parameter id="name" value="Interface to OpenOffice Writer"/>
+        <parameter id="description" value=""/>
+    </extension>
+
+    <extension id="FormatChars" plugin-id="net.sf.jabref.core" point-id="LayoutFormatter">
+               <parameter id="layoutFormatter"
+                       value="net.sf.jabref.export.layout.format.FormatChars" />
+               <parameter id="description"
+                       value="Conversion of BibTeX codes into unicode values." />
+       </extension>
+
+</plugin>