Commits

Anonymous committed b359238

#i10000# cws odfmetadata3 corrected

  • Participants
  • Parent commits 7396b04

Comments (0)

Files changed (9)

offapi/com/sun/star/text/InContentMetadata.idl

+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2008 by Sun Microsystems, Inc.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * $RCSfile: TextField.idl,v $
+ * $Revision: 1.11 $
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org.  If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef __com_sun_star_text_InContentMetadata_idl__
+#define __com_sun_star_text_InContentMetadata_idl__
+
+#ifndef __com_sun_star_container_XEnumerationAccess_idl__
+#include <com/sun/star/container/XEnumerationAccess.idl>
+#endif
+
+#ifndef __com_sun_star_text_TextContent_idl__
+#include <com/sun/star/text/TextContent.idl>
+#endif
+
+#ifndef __com_sun_star_rdf_XMetadatable_idl__
+#include <com/sun/star/rdf/XMetadatable.idl>
+#endif
+
+
+//=============================================================================
+
+module com {  module sun {  module star {  module text {
+
+//=============================================================================
+
+/** is a <type>TextContent</type> that can be used to attach RDF metadata
+    to a range of text.
+
+    @since OOo 3.2
+ */
+service InContentMetadata
+{
+    //-------------------------------------------------------------------------
+    /** None of the properties of <type>TextContent</type> are
+        supported. */
+    service com::sun::star::text::TextContent;
+
+    //-------------------------------------------------------------------------
+    /** The <type>InContentMetadata</type> can have RDF metadata attached. */
+    interface com::sun::star::rdf::XMetadatable;
+
+    //-------------------------------------------------------------------------
+    /** The <type>TextContent</type>s that are contained in the
+        annotated range of text can be enumerated. */
+    interface com::sun::star::container::XEnumerationAccess;
+
+};
+
+
+//=============================================================================
+
+}; }; }; };
+
+#endif

offapi/com/sun/star/text/textfield/MetadataField.idl

+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2008 by Sun Microsystems, Inc.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * $RCSfile: MetadataField.idl,v $
+ * $Revision: 1.12 $
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org.  If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef __com_sun_star_text_textfield_MetadataField_idl__
+#define __com_sun_star_text_textfield_MetadataField_idl__
+
+#ifndef __com_sun_star_container_XEnumerationAccess_idl__
+#include <com/sun/star/container/XEnumerationAccess.idl>
+#endif
+
+#ifndef __com_sun_star_text_TextField_idl__
+#include <com/sun/star/text/TextField.idl>
+#endif
+
+#ifndef __com_sun_star_text_XText_idl__
+#include <com/sun/star/text/XText.idl>
+#endif
+
+#ifndef __com_sun_star_rdf_XMetadatable_idl__
+#include <com/sun/star/rdf/XMetadatable.idl>
+#endif
+
+
+//=============================================================================
+
+module com { module sun { module star { module text { module textfield {
+
+//=============================================================================
+/** is a <type>TextField</type> whose content is specified by RDF metadata.
+
+    @since OOo 3.2
+
+    @see com::sun::star::rdf
+*/
+service MetadataField
+{
+    //-------------------------------------------------------------------------
+    /** None of the properties of <type>TextContent</type> are
+        supported. */
+    service  com::sun::star::text::TextField;
+
+    //-------------------------------------------------------------------------
+    /** The <type>MetadataField</type> can have RDF metadata attached. */
+    interface com::sun::star::rdf::XMetadatable;
+
+    //-------------------------------------------------------------------------
+    /** Allows for insertion of text content into, and creating cursors
+        that are bound within, the <type>MetadataField</type>. */
+    interface com::sun::star::text::XText;
+
+    //-------------------------------------------------------------------------
+    /** The <type>TextContent</type>s that are contained in the
+        <type>MetadataField</type> can be enumerated. */
+    interface com::sun::star::container::XEnumerationAccess;
+
+    //-------------------------------------------------------------------------
+    /** this is the number format for this field.
+        @see com::sun::star::util::NumberFormatter
+    */
+    [optional, property] long NumberFormat;
+
+    //-------------------------------------------------------------------------
+    /** determines whether changes in language attributes at the
+        position of the text field also change the number format
+        as appropriate for this language.
+     */
+    [optional, property] boolean IsFixedLanguage;
+};
+
+//=============================================================================
+
+}; }; }; }; };
+
+#endif
+

sw/inc/bookmrk.hxx

-/*************************************************************************
- *
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- * 
- * Copyright 2008 by Sun Microsystems, Inc.
- *
- * OpenOffice.org - a multi-platform office productivity suite
- *
- * $RCSfile: bookmrk.hxx,v $
- * $Revision: 1.11 $
- *
- * This file is part of OpenOffice.org.
- *
- * OpenOffice.org is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License version 3
- * only, as published by the Free Software Foundation.
- *
- * OpenOffice.org is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Lesser General Public License version 3 for more details
- * (a copy is included in the LICENSE file that accompanied this code).
- *
- * You should have received a copy of the GNU Lesser General Public License
- * version 3 along with OpenOffice.org.  If not, see
- * <http://www.openoffice.org/license.html>
- * for a copy of the LGPLv3 License.
- *
- ************************************************************************/
-#ifndef _BOOKMRK_HXX
-#define _BOOKMRK_HXX
-
-#include "hintids.hxx"		//die Ids der Attribute, vor macitem damit die
-							//die Attribut richtig angezogen werden.
-#include <svtools/macitem.hxx>
-
-#ifndef _KEYCOD_HXX //autogen
-#include <vcl/keycod.hxx>
-#endif
-#ifndef _TOOLS_REF_HXX
-#include <tools/ref.hxx>
-#endif
-#include <IDocumentBookmarkAccess.hxx>
-#include <calbck.hxx>
-#include <pam.hxx>
-
-#ifndef SW_DECL_SWSERVEROBJECT_DEFINED
-#define SW_DECL_SWSERVEROBJECT_DEFINED
-SV_DECL_REF( SwServerObject )
-#endif
-
-
-struct SwPosition;	// fwd Decl. wg. UI
-
-class SwBookmark : public SwModify
-{
-	SwPosition *pPos1, *pPos2;	// wird im CTOR gesetzt, im DTOR geloescht
-                                // pPos1 is always != 0, pPos2 may be 0
-	SwServerObjectRef refObj;	// falls DataServer -> Pointer gesetzt
-
-protected:
-	String		aName;
-	String		aShortName;
-	KeyCode		aCode;
-	IDocumentBookmarkAccess::BookmarkType eMarkType;
-
-    SwBookmark( const SwPosition& aPos,
-                const KeyCode& rCode,
-                const String& rName, const String& rShortName);
-
-public:
-	TYPEINFO();
-
-    SwBookmark( const SwPosition& aPos );
-    // --> OD 2007-09-26 #i81002#
-    SwBookmark( const SwPaM& aPaM,
-                const KeyCode& rCode,
-                const String& rName, const String& rShortName);
-    // <--
-
-	// Beim Loeschen von Text werden Bookmarks mitgeloescht!
-	virtual ~SwBookmark();
-
-    // --> OD 2007-10-10 #i81002#
-    // made virtual and thus no longer inline
-    virtual const SwPosition& GetBookmarkPos() const;
-    virtual const SwPosition* GetOtherBookmarkPos() const;
-    // <--
-
-	// nicht undofaehig
-    const String& GetName() const { return aName; }
-	// nicht undofaehig
-    const String& GetShortName() const { return aShortName; }
-	// nicht undofaehig
-    const KeyCode& GetKeyCode() const { return aCode; }
-
-	// Vergleiche auf Basis der Dokumentposition
-	BOOL operator < (const SwBookmark &) const;
-	BOOL operator ==(const SwBookmark &) const;
-	// falls man wirklich auf gleiche Position abfragen will.
-	BOOL IsEqualPos( const SwBookmark &rBM ) const;
-
-	BOOL IsFormFieldMark() const 	{ return IDocumentBookmarkAccess::FORM_FIELDMARK_TEXT == eMarkType || IDocumentBookmarkAccess::FORM_FIELDMARK_NO_TEXT == eMarkType; }
-    BOOL IsBookMark() const 	{ return IDocumentBookmarkAccess::BOOKMARK == eMarkType || IDocumentBookmarkAccess::FORM_FIELDMARK_TEXT == eMarkType || IDocumentBookmarkAccess::FORM_FIELDMARK_NO_TEXT == eMarkType; }
-//    // --> OD 2007-10-17 #TESTING#
-//    BOOL IsBookMark() const
-//    {
-//        return IDocumentBookmarkAccess::BOOKMARK == eMarkType ||
-//               IsCrossRefMark();
-//    }
-//    // <--
-	BOOL IsMark() const 		{ return IDocumentBookmarkAccess::MARK == eMarkType; }
-    BOOL IsUNOMark() const      { return IDocumentBookmarkAccess::UNO_BOOKMARK == eMarkType; }
-    // --> OD 2007-10-11 #i81002# - bookmark type for cross-references
-    BOOL IsCrossRefMark() const { return IDocumentBookmarkAccess::CROSSREF_BOOKMARK == eMarkType; }
-    // <--
-    void SetType( IDocumentBookmarkAccess::BookmarkType eNewType )  { eMarkType = eNewType; }
-	IDocumentBookmarkAccess::BookmarkType GetType() const 	{ return eMarkType; }
-
-		// Daten Server-Methoden
-	void SetRefObject( SwServerObject* pObj );
-	const SwServerObject* GetObject() const		{  return &refObj; }
-		  SwServerObject* GetObject() 			{  return &refObj; }
-	BOOL IsServer() const 						{  return refObj.Is(); }
-
-    // --> OD 2007-10-10 #i81002#
-    // made virtual and thus no longer inline
-    // to access start and end of a bookmark.
-    // start and end may be the same
-    virtual const SwPosition* BookmarkStart() const;
-    virtual const SwPosition* BookmarkEnd() const;
-    // <--
-
-    // --> OD 2007-09-26 #i81002#
-    virtual void SetBookmarkPos( const SwPosition* pNewPos1 );
-    virtual void SetOtherBookmarkPos( const SwPosition* pNewPos2 );
-    // <--
-
-private:
-	// fuer METWARE:
-	// es wird (vorerst) nicht kopiert und nicht zugewiesen
-	SwBookmark(const SwBookmark &);
-	SwBookmark &operator=(const SwBookmark &);
-};
-
-class SwMark: public SwBookmark
-{
-public:
-    SwMark( const SwPosition& aPos,
-            const KeyCode& rCode,
-            const String& rName, const String& rShortName);
-};
-            
-class SW_DLLPUBLIC SwFieldBookmark : public SwBookmark
-{
-private:
-	int fftype; // Type: 0 = Text, 1 = Check Box, 2 = List
-	int ffres;
-    bool ffprot;
-	bool ffsize; // 0 = Auto, 1=Exact (see ffhps)
-	int fftypetxt; // Type of text field: 0 = Regular text, 1 = Number, 2 = Date, 3 = Current date, 4 = Current time, 5 = Calculation
-	bool ffrecalc; 
-	int ffmaxlen; // Number of characters for text field. Zero means unlimited.
-	int ffhps; // Check box size (half-point sizes).
-
-	String ffname;
-	String ffhelptext;
-
-public:
-    SwFieldBookmark(const SwPosition& aPos,
-	       const KeyCode& rCode,
-	       const String& rName, const String& rShortName,
-		   IDocumentBookmarkAccess::BookmarkType eMark);
-
-	void SetFieldType(int fftype);
-	int GetFieldType();
-
-	void SetChecked(bool checked);
-	bool IsChecked();
-
-	void SetFFName(String aNewName) {
-		this->ffname=aNewName;
-	}
-
-	String GetFFName()
-	{
-		return ffname;
-	}
-
-	int GetFFRes() {
-		return ffres;
-	}
-
-	void SetFFRes(int nNew) {
-		this->ffres=nNew;
-	}
-
-	void SetFFHelpText(String newffhelptext) {
-		this->ffhelptext=newffhelptext;
-	}
-
-	String GetFFHelpText() {
-		return ffhelptext;
-	}
-};
-            
-class SwUNOMark: public SwBookmark
-{
-public:
-    // --> OD 2007-09-26 #i81002#
-    SwUNOMark( const SwPaM& aPaM,
-               const KeyCode& rCode,
-               const String& rName, const String& rShortName);
-    // <--
-};
-
-
-#endif

sw/inc/fmthbsh.hxx

-/*************************************************************************
- *
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- * 
- * Copyright 2008 by Sun Microsystems, Inc.
- *
- * OpenOffice.org - a multi-platform office productivity suite
- *
- * $RCSfile: fmthbsh.hxx,v $
- * $Revision: 1.5 $
- *
- * This file is part of OpenOffice.org.
- *
- * OpenOffice.org is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License version 3
- * only, as published by the Free Software Foundation.
- *
- * OpenOffice.org is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Lesser General Public License version 3 for more details
- * (a copy is included in the LICENSE file that accompanied this code).
- *
- * You should have received a copy of the GNU Lesser General Public License
- * version 3 along with OpenOffice.org.  If not, see
- * <http://www.openoffice.org/license.html>
- * for a copy of the LGPLv3 License.
- *
- ************************************************************************/
-#ifndef _FMTHBSH_HXX
-#define _FMTHBSH_HXX
-
-
-#include <svtools/poolitem.hxx>
-
-// ATT_SOFTHYPH ******************************
-// Attribut fuer benutzerdefinierte Trennstellen.
-
-class SwFmtSoftHyph : public SfxPoolItem
-{
-public:
-	SwFmtSoftHyph();
-
-	// "pure virtual Methoden" vom SfxPoolItem
-	virtual int             operator==( const SfxPoolItem& ) const;
-	virtual SfxPoolItem*	Clone( SfxItemPool* pPool = 0 ) const;
-
-    inline SwFmtSoftHyph& operator=(const SwFmtSoftHyph&) {
-			return *this;
-		}
-};
-
-// ATT_HARDBLANK ******************************
-// Attribut fuer geschuetzte Leerzeichen.
-
-class SW_DLLPUBLIC SwFmtHardBlank : public SfxPoolItem
-{
-	sal_Unicode cChar;
-public:
-	SwFmtHardBlank( sal_Unicode cCh, BOOL bCheck = TRUE );
-
-	// "pure virtual Methoden" vom SfxPoolItem
-	virtual int             operator==( const SfxPoolItem& ) const;
-	virtual SfxPoolItem*	Clone( SfxItemPool* pPool = 0 ) const;
-
-
-	inline sal_Unicode GetChar() const { return cChar; }
-	inline SwFmtHardBlank& operator=(const SwFmtHardBlank& rHB)
-	{ cChar = rHB.GetChar(); return *this; }
-};
-
-
-#endif
-

sw/inc/fmtmeta.hxx

+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2008 by Sun Microsystems, Inc.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * $RCSfile: fmtmeta.hxx,v $
+ * $Revision: 1.1 $
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org.  If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef SW_FMTMETA_HXX
+#define SW_FMTMETA_HXX
+
+#include <svtools/poolitem.hxx>
+#include <sfx2/Metadatable.hxx>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+
+#include <vector>
+
+
+namespace com { namespace sun { namespace star {
+    namespace text {
+        class XTextField;
+    }
+}}}
+
+
+/**
+ * The classes that make up a meta entity are:
+ * <dl>
+ *   <dt>SwTxtMeta</dt><dd>the text hint</dd>
+ *   <dt>SwFmtMeta</dt><dd>the pool item</dd>
+ *   <dt>sw::Meta</dt><dd>the metadatable entity itself</dd>
+ *   <dt>SwXMeta</dt><dd>the UNO wrapper object</dd>
+ * </dl>
+ *
+ * The text hint contains the pool item (as usual) and has a pointer to the
+ * text node at which it is attached.
+ * The pool item has a shared pointer to the metadatable entity, and a reverse
+ * pointer to the text attribute at which it is attached.
+ * The pool item is non-poolable; it may only be attached to one text
+ * attribute.
+ * Of all the pool items that refer to a metadatable entity, only one may be
+ * in the document content at any time. Others may be in the undo array, or in
+ * undo objects.
+ * The metadatable entity has a reverse pointer to the pool item that is
+ * currently in the document. It also registers as a client at the text node
+ * at which it is attached via this pool item and its text attribute.
+ * The UNO wrapper object registers as a client at the metadatable entity.
+ *
+ * Copying the metadatable entity proceeds in the following way:
+ * <ol>
+ *   <li>The pool item is cloned (because it is non-poolable); the clone
+ *       points to the same metadatable entity, but the metadatable entity's
+ *       reverse pointer is unchanged.</li>
+ *   <li>A new text hint is created, taking over the new pool item.
+ *       Unfortunately, this also makes the metadatable entity point at the
+ *       cloned pool item.</li>
+ *   <li>The text hint is inserted into the hints array of some text node.</li>
+ *   <li>The DoCopy() method must be called at the new pool item:
+ *       it will clone the metadatable entity (using RegisterAsCopyOf),
+ *       and fix the reverse pointer of the original to point at the
+ *       original pool item.
+ *       This is necessary, because first, a metadatable entity may
+ *       only be inserted once into a document, and second, the copy may be
+ *       inserted into a different document than the source document!</li>
+ * </ol>
+ */
+
+class SwTxtMeta;
+class SwXMeta;
+class SwXMetaField;
+namespace sw {
+    class Meta;
+}
+
+class SwFmtMeta
+    : public SfxPoolItem
+{
+private:
+    friend class SwTxtMeta; // needs SetTxtAttr
+    friend class ::sw::Meta; // needs m_pTxtAttr
+
+    ::boost::shared_ptr< ::sw::Meta > m_pMeta;
+    SwTxtMeta * m_pTxtAttr;
+
+    SwTxtMeta * GetTxtAttr() { return m_pTxtAttr; }
+    void SetTxtAttr(SwTxtMeta * const i_pTxtAttr);
+
+    explicit SwFmtMeta( const USHORT i_nWhich );
+
+public:
+    // takes ownership
+    explicit SwFmtMeta( ::boost::shared_ptr< ::sw::Meta > const & i_pMeta,
+                        const USHORT i_nWhich );
+    virtual ~SwFmtMeta();
+
+    // SfxPoolItem
+    virtual int              operator==( const SfxPoolItem & ) const;
+    virtual SfxPoolItem *    Clone( SfxItemPool *pPool = 0 ) const;
+//    TYPEINFO();
+
+    // notify clients registered at m_pMeta that this meta is being removed
+    void NotifyRemoval();
+    static SwFmtMeta * CreatePoolDefault( const USHORT i_nWhich );
+    ::sw::Meta * GetMeta() { return m_pMeta.get(); }
+    /// this method <em>must</em> be called when the hint is actually copied
+    void DoCopy( SwFmtMeta & rOriginalMeta );
+};
+
+
+namespace sw {
+
+class MetaFieldManager;
+
+class Meta
+    : public ::sfx2::Metadatable
+    , public SwModify
+{
+protected:
+    friend class ::SwFmtMeta; // SetFmtMeta
+    friend class ::SwXMeta; // GetTxtNode, GetTxtAttr
+
+    SwFmtMeta * m_pFmt;
+
+    SwTxtMeta * GetTxtAttr() const;
+    SwTxtNode * GetTxtNode() const; // returns 0 if not in document (undo)
+
+    SwFmtMeta * GetFmtMeta() const { return m_pFmt; }
+    void SetFmtMeta( SwFmtMeta * const i_pFmt ) { m_pFmt = i_pFmt; };
+
+public:
+    explicit Meta(SwFmtMeta * const i_pFmt = 0);
+    virtual ~Meta();
+
+    // SwClient
+    virtual void Modify( SfxPoolItem *pOld, SfxPoolItem *pNew );
+
+    // sfx2::Metadatable
+    virtual ::sfx2::IXmlIdRegistry& GetRegistry();
+    virtual bool IsInClipboard() const;
+    virtual bool IsInUndo() const;
+    virtual bool IsInContent() const;
+    virtual ::com::sun::star::uno::Reference<
+        ::com::sun::star::rdf::XMetadatable > MakeUnoObject();
+};
+
+class MetaField
+    : public Meta
+{
+private:
+    friend class ::SwFmtMeta;
+    friend class ::SwXMetaField;
+    friend class ::sw::MetaFieldManager;
+
+    sal_uInt32 m_nNumberFormat;
+    bool       m_bIsFixedLanguage;
+
+    sal_uInt32 GetNumberFormat(::rtl::OUString const & rContent) const;
+    void SetNumberFormat(sal_uInt32 nNumberFormat);
+    bool IsFixedLanguage() const    { return m_bIsFixedLanguage; }
+    void SetIsFixedLanguage(bool b) { m_bIsFixedLanguage = b; }
+
+    explicit MetaField(SwFmtMeta * const i_pFmt = 0,
+            const sal_uInt32 nNumberFormat = SAL_MAX_UINT32,
+            const bool bIsFixedLanguage = false );
+
+public:
+    /// get prefix/suffix from the RDF repository. @throws RuntimeException
+    void GetPrefixAndSuffix(
+        ::rtl::OUString *const o_pPrefix, ::rtl::OUString *const o_pSuffix);
+};
+
+/** knows all meta-fields in the document. */
+class MetaFieldManager
+    : private ::boost::noncopyable
+{
+private:
+    typedef ::std::vector< ::boost::weak_ptr<MetaField> > MetaFieldList_t;
+    MetaFieldList_t m_MetaFields;
+
+public:
+    MetaFieldManager();
+    ::boost::shared_ptr<MetaField> makeMetaField(
+                SwFmtMeta * const i_pFmt = 0,
+                const sal_uInt32 nNumberFormat = SAL_MAX_UINT32,
+                const bool bIsFixedLanguage = false );
+    /// get all meta fields
+    ::std::vector< ::com::sun::star::uno::Reference<
+        ::com::sun::star::text::XTextField> > getMetaFields();
+};
+
+} // namespace sw
+
+#endif // SW_FMTMETA_HXX
+

sw/qa/complex/writer/TextPortionEnumerationTest.java

+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2008 by Sun Microsystems, Inc.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * $RCSfile: $
+ * $Revision: $
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org.  If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+package complex.writer;
+
+import complexlib.ComplexTestCase;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.AnyConverter;
+import com.sun.star.uno.XComponentContext;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XComponent;
+import com.sun.star.lang.XServiceInfo;
+import com.sun.star.beans.XPropertySet;
+import com.sun.star.beans.XPropertySetInfo;
+import com.sun.star.beans.PropertyValue;
+import com.sun.star.beans.Pair;
+import com.sun.star.beans.StringPair;
+import com.sun.star.container.XNamed;
+import com.sun.star.container.XContentEnumerationAccess;
+import com.sun.star.container.XEnumerationAccess;
+import com.sun.star.container.XEnumeration;
+import com.sun.star.container.NoSuchElementException;
+import com.sun.star.frame.XStorable;
+import com.sun.star.util.XCloseable;
+import com.sun.star.text.XText;
+import com.sun.star.text.XTextContent;
+import com.sun.star.text.XTextDocument;
+import com.sun.star.text.XTextRange;
+import com.sun.star.text.XTextCursor;
+import com.sun.star.text.XWordCursor;
+import com.sun.star.text.XSentenceCursor;
+import com.sun.star.text.XParagraphCursor;
+import com.sun.star.text.XFootnote;
+import com.sun.star.text.XTextField;
+import com.sun.star.text.TextContentAnchorType;
+import static com.sun.star.text.TextContentAnchorType.*;
+import static com.sun.star.text.ControlCharacter.*;
+import com.sun.star.rdf.XMetadatable;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Stack;
+
+
+class TreeNodeEnum
+{
+    private Object[] m_Elements;
+    private int m_Pos;
+    TreeNodeEnum(Object[] Elements) { m_Elements = Elements; m_Pos = 0; }
+    boolean hasNext() { return m_Pos < m_Elements.length; }
+    TreeNode next() { return (TreeNode) m_Elements[m_Pos++]; }
+}
+
+/** base class for tree nodes. only instance: root of tree. */
+class TreeNode
+{
+    private List<TreeNode> m_Children;
+    String getType() { return "__ROOT__"; }
+    boolean hasContent() { return false; }
+    boolean isNesting() { return false; }
+    TreeNode dup() { throw new RuntimeException("TreeNode.dup"); }
+    TreeNodeEnum createEnumeration() {
+        return new TreeNodeEnum(m_Children.toArray());
+    }
+    TreeNode() { m_Children = new ArrayList<TreeNode>(); }
+    TreeNode appendChild(TreeNode child)
+    { m_Children.add(child); return this; }
+    public String toString() {
+        return "<" + getType() + ">";
+    }
+}
+
+abstract class MarkNode extends TreeNode
+{
+    boolean m_isPoint;
+    boolean m_isStart = false;
+    String m_Name;
+    boolean isPoint() { return m_isPoint; }
+    boolean isStart() { return m_isStart; }
+    String getName() { return m_Name; }
+    MarkNode(String name) { m_Name = name; m_isPoint = true; }
+    boolean equals(MarkNode other) {
+        return (other.m_Name.equals(m_Name)) && (other.m_isPoint == m_isPoint)
+            && (other.m_isStart == m_isStart);
+    }
+    public String toString() {
+        return super.toString() + "\tname: " + m_Name +
+            "\tisPoint: " + m_isPoint + "\tisStart: " + m_isStart;
+    }
+}
+
+class BookmarkNode extends MarkNode
+{
+    private StringPair m_XmlId;
+    StringPair getXmlId() { return m_XmlId; }
+    BookmarkNode dup() { return new BookmarkNode(getName(), getXmlId()); }
+    BookmarkNode(String name) { this(name, new StringPair());  }
+    BookmarkNode(String name, StringPair xmlid) {
+        super(name); m_XmlId = xmlid;
+    }
+    String getType() { return "Bookmark"; }
+    public boolean equals(Object other) {
+        return (other instanceof BookmarkNode)
+            && super.equals((MarkNode) other)
+            && MetaNode.eq(((BookmarkNode) other).m_XmlId, m_XmlId);
+    }
+    public String toString() {
+        return super.toString()
+            + "\txmlid: " + m_XmlId.First + "#" + m_XmlId.Second;
+    }
+}
+
+class BookmarkStartNode extends BookmarkNode
+{
+    BookmarkStartNode dup() { return new BookmarkStartNode(getName()); }
+    BookmarkStartNode(String name) { this(name, new StringPair()); }
+    BookmarkStartNode(String name, StringPair xmlid) {
+        super(name, xmlid); m_isPoint = false; m_isStart = true;
+    }
+}
+
+class BookmarkEndNode extends BookmarkNode
+{
+    BookmarkEndNode dup() { return new BookmarkEndNode(getName()); }
+    BookmarkEndNode(String name) { this(name, new StringPair()); }
+    BookmarkEndNode(String name, StringPair xmlid) {
+        super(name, xmlid); m_isPoint = false; m_isStart = false;
+    }
+}
+
+class ReferenceMarkNode extends MarkNode
+{
+    ReferenceMarkNode dup() { return new ReferenceMarkNode(getName()); }
+    ReferenceMarkNode(String name) { super(name); }
+    String getType() { return "ReferenceMark"; }
+    public boolean equals(Object other) {
+        return (other instanceof ReferenceMarkNode)
+            && super.equals((MarkNode) other);
+    }
+}
+
+class ReferenceMarkStartNode extends ReferenceMarkNode
+{
+    ReferenceMarkStartNode dup()
+    { return new ReferenceMarkStartNode(getName()); }
+    ReferenceMarkStartNode(String name) {
+        super(name); m_isPoint = false; m_isStart = true;
+    }
+}
+
+class ReferenceMarkEndNode extends ReferenceMarkNode
+{
+    ReferenceMarkEndNode dup()
+    { return new ReferenceMarkEndNode(getName()); }
+    ReferenceMarkEndNode(String name) {
+        super(name); m_isPoint = false; m_isStart = false;
+    }
+}
+
+class DocumentIndexMarkNode extends MarkNode
+{
+    DocumentIndexMarkNode dup()
+    { return new DocumentIndexMarkNode(getName()); }
+    DocumentIndexMarkNode(String name) { super(name); }
+    String getType() { return "DocumentIndexMark"; }
+    public boolean equals(Object other) {
+        return (other instanceof DocumentIndexMarkNode)
+            && super.equals((MarkNode) other);
+    }
+}
+
+class DocumentIndexMarkStartNode extends DocumentIndexMarkNode
+{
+    DocumentIndexMarkStartNode dup()
+    { return new DocumentIndexMarkStartNode(getName()); }
+    DocumentIndexMarkStartNode(String name) {
+        super(name); m_isPoint = false; m_isStart = true;
+    }
+}
+
+class DocumentIndexMarkEndNode extends DocumentIndexMarkNode
+{
+    DocumentIndexMarkEndNode dup()
+    { return new DocumentIndexMarkEndNode(getName()); }
+    DocumentIndexMarkEndNode(String name) {
+        super(name); m_isPoint = false; m_isStart = false;
+    }
+}
+
+abstract class ContentNode extends TreeNode
+{
+    private String m_Content;
+    String getContent() { return m_Content; }
+    boolean hasContent() { return true; }
+    ContentNode(String content) {
+        m_Content = content;
+    }
+    TreeNode appendChild(TreeNode t) {
+        throw new RuntimeException("ContentNode.appendChild");
+    }
+    public String toString() {
+        return super.toString() + "\tcontent: " + m_Content;
+    }
+    boolean equals(ContentNode other) {
+        return (other.m_Content.equals(m_Content));
+    }
+}
+
+class TextNode extends ContentNode
+{
+    TextNode dup() { return new TextNode(getContent()); }
+    TextNode(String content) { super(content); }
+    String getType() { return "Text"; }
+    public boolean equals(Object other) {
+        return (other instanceof TextNode) && super.equals((ContentNode) other);
+    }
+}
+
+class TextFieldNode extends ContentNode
+{
+    TextFieldNode dup() { return new TextFieldNode(getContent()); }
+    TextFieldNode(String content) { super(content); }
+    String getType() { return "TextField"; }
+    public boolean equals(Object other) {
+        return (other instanceof TextFieldNode)
+            && super.equals((ContentNode) other);
+    }
+}
+
+class FrameNode extends TreeNode
+{
+    private String m_Name;
+    private TextContentAnchorType m_Anchor;
+    String getName() { return m_Name; }
+    TextContentAnchorType getAnchor() { return m_Anchor; }
+    FrameNode dup() { return new FrameNode(getName(), getAnchor()); }
+    FrameNode(String name, TextContentAnchorType anchor) {
+        m_Name = name; m_Anchor = anchor;
+    }
+    String getType() { return "Frame"; }
+    public boolean equals(Object other) {
+        return (other instanceof FrameNode)
+            && (((FrameNode) other).m_Name.equals(m_Name))
+            && (((FrameNode) other).m_Anchor == m_Anchor);
+    }
+    public String toString() {
+        return super.toString()
+            + "\tname: " + m_Name + "\tanchor: " + toString(m_Anchor);
+    }
+    static String toString(TextContentAnchorType anchor) {
+        switch (anchor.getValue()) {
+            case AS_CHARACTER_value: return "AS_CHARACTER";
+            case AT_CHARACTER_value: return "AT_CHARACTER";
+            case AT_PARAGRAPH_value: return "AT_PARAGRAPH";
+            case AT_PAGE_value:      return "AT_PAGE";
+            case AT_FRAME_value:     return "AT_FRAME";
+            default: throw new RuntimeException("unknown anchor");
+        }
+    }
+}
+
+class FootnoteNode extends TreeNode
+{
+    private String m_Label;
+    String getLabel() { return m_Label; }
+    FootnoteNode dup() { return new FootnoteNode(getLabel()); }
+    FootnoteNode(String label) { m_Label = label; }
+    String getType() { return "Footnote"; }
+    public boolean equals(Object other) {
+        return (other instanceof FootnoteNode)
+            && (((FootnoteNode) other).m_Label.equals(m_Label));
+    }
+    public String toString() {
+        return super.toString() + "\tlabel: " + m_Label;
+    }
+}
+
+class ControlCharacterNode extends TreeNode
+{
+    private short m_Char;
+    short getChar() { return m_Char; }
+    ControlCharacterNode dup() { return new ControlCharacterNode(getChar()); }
+    ControlCharacterNode(short c) { m_Char = c; }
+    String getType() { return "ControlCharacter"; }
+    public boolean equals(Object other) {
+        return (other instanceof ControlCharacterNode)
+            && (((ControlCharacterNode) other).m_Char == m_Char);
+    }
+    public String toString() {
+        return super.toString() + "\tchar: " + m_Char;
+    }
+}
+
+class SoftPageBreakNode extends TreeNode
+{
+    String getType() { return "SoftPageBreak"; }
+    public boolean equals(Object other) {
+        return (other instanceof SoftPageBreakNode);
+    }
+}
+
+class HyperlinkNode extends TreeNode
+{
+    private String m_URL;
+    String getURL() { return m_URL; }
+    HyperlinkNode dup() { return new HyperlinkNode(getURL()); }
+    HyperlinkNode(String url) {
+        if (url.length() == 0) throw new RuntimeException("HyperlinkNode");
+        m_URL = url;
+    }
+    String getType() { return "Hyperlink"; }
+    boolean isNesting() { return true; }
+    public boolean equals(Object other) {
+        return (other instanceof HyperlinkNode)
+            && (((HyperlinkNode) other).m_URL.equals(m_URL));
+    }
+    public String toString() {
+        return super.toString() + "\turl: " + m_URL;
+    }
+}
+
+class RubyNode extends TreeNode
+{
+    private String m_Ruby;
+    String getRubyText() { return m_Ruby; }
+    RubyNode dup() { return new RubyNode(getRubyText()); }
+    RubyNode(String ruby) {
+        if (ruby.length() == 0) throw new RuntimeException("RubyNode");
+        m_Ruby = ruby;
+    }
+    String getType() { return "Ruby"; }
+    boolean isNesting() { return true; }
+    public boolean equals(Object other) {
+        return (other instanceof RubyNode)
+            && (((RubyNode) other).m_Ruby.equals(m_Ruby));
+    }
+    public String toString() {
+        return super.toString() + "\trubytext: " + m_Ruby;
+    }
+}
+
+class MetaNode extends TreeNode
+{
+    private StringPair m_XmlId;
+    StringPair getXmlId() { return m_XmlId; }
+    MetaNode dup() { return new MetaNode(getXmlId()); }
+    MetaNode (StringPair xmlid) { m_XmlId = xmlid; }
+    String getType() { return "InContentMetadata"; }
+    boolean isNesting() { return true; }
+    public boolean equals(Object other) {
+        return (other instanceof MetaNode)
+            && eq(((MetaNode) other).m_XmlId, m_XmlId);
+    }
+    static boolean eq(StringPair left, StringPair right)
+    {
+        return left.First.equals(right.First)
+            && left.Second.equals(right.Second);
+    }
+    public String toString() {
+        return super.toString()
+            + "\txmlid: " + m_XmlId.First + "#" + m_XmlId.Second;
+    }
+}
+
+class MetaFieldNode extends MetaNode
+{
+    MetaFieldNode dup() { return new MetaFieldNode(getXmlId()); }
+    MetaFieldNode (StringPair xmlid) { super(xmlid); }
+    String getType() { return "MetadataField"; }
+}
+
+class Range
+{
+    private short m_Start;
+    private short m_End;
+    private TreeNode m_Node;
+    short getStart()    { return m_Start; }
+    short getEnd()      { return m_End  ; }
+    short getExtent()   { return (short) (m_End - m_Start); }
+    TreeNode getNode()  { return m_Node; }
+    Range(int start, int end, TreeNode node)
+    { m_Start = (short) start; m_End = (short) end; m_Node = node; }
+}
+
+//----------------------------------------------------------------------
+
+/* this is where we nail the pudding to the wall */
+class FuzzyTester
+{
+    private long m_DiffContent = 0;
+    private long m_DiffMissing = 0;
+    private long m_DiffNesting = 0;
+    private long m_DiffSpuriousEmptyText = 0;
+    private long m_DiffSequence = 0; // ignored?
+    private Stack<Pair<TreeNode, TreeNodeEnum>> m_StackExpected;
+    private Stack<Pair<TreeNode, TreeNodeEnum>> m_StackActual;
+    private List<TreeNode> m_BufferExpected;
+    private List<TreeNode> m_BufferActual;
+    private share.LogWriter m_Log;
+
+    FuzzyTester(share.LogWriter log) {
+        m_Log = log;
+        m_BufferExpected = new ArrayList<TreeNode>();
+        m_BufferActual = new ArrayList<TreeNode>();
+        m_StackExpected = new Stack<Pair<TreeNode, TreeNodeEnum>>();
+        m_StackActual = new Stack<Pair<TreeNode, TreeNodeEnum>>();
+    }
+
+    /** idea: traverse both trees, enumerate nodes, stopping at content nodes.
+        then compare buffers. */
+    boolean doTest(TreeNode expected, TreeNode actual)
+    {
+        if (!expected.getType().equals("__ROOT__"))
+            throw new RuntimeException("doTest: expected: root");
+        if (!actual.getType().equals("__ROOT__"))
+            throw new RuntimeException("doTest: actual: root");
+        m_StackExpected.push(new Pair(expected, expected.createEnumeration()));
+        m_StackActual.push(new Pair(actual, actual.createEnumeration()));
+        do {
+            traverse(m_StackExpected, m_BufferExpected);
+            traverse(m_StackActual, m_BufferActual);
+        //??? root?
+            testBuffer();
+        } while (!m_StackExpected.empty() || !m_StackActual.empty());
+        if (m_DiffSequence != 0) {
+            m_Log.println("warning: " +  m_DiffSequence
+                    + " differences in sequence");
+        }
+        if (m_DiffSpuriousEmptyText != 0) {
+            m_Log.println("warning: " +  m_DiffSpuriousEmptyText
+                    + " spurious empty text nodes");
+        }
+        if (m_DiffNesting != 0) {
+            m_Log.println("WARNING: " +  m_DiffNesting
+                    + " differences in nesting");
+        }
+        return (m_DiffContent == 0) && (m_DiffMissing == 0);
+    }
+
+    private void traverse(Stack<Pair<TreeNode, TreeNodeEnum>> stack,
+                  List<TreeNode> buffer)
+    {
+        while (!stack.empty()) {
+            TreeNodeEnum topEnum = stack.peek().Second;
+            if (topEnum.hasNext()) {
+                TreeNode node = topEnum.next();
+                buffer.add(node);
+                TreeNodeEnum nodeEnum = node.createEnumeration();
+                if (nodeEnum.hasNext()) {
+                    stack.push(new Pair(node, nodeEnum));
+                }
+                if (node.hasContent()) {
+                    if (!((node instanceof TextNode) // spurious empty text?
+                        && ((TextNode) node).getContent().length() == 0)) {
+                            return; // break here
+                    }
+                }
+            } else {
+                buffer.add(stack.peek().First);
+                stack.pop();
+            }
+        }
+    }
+
+    private void testTerminatingNode()
+    {
+        int lenExpected = m_BufferExpected.size();
+        int lenActual   = m_BufferActual.size();
+        if (lenExpected == 0 || lenActual == 0)
+            return;
+        TreeNode expected = m_BufferExpected.get(lenExpected - 1);
+        TreeNode actual = m_BufferActual.get(lenActual - 1);
+
+        boolean eRoot = expected.getType().equals("__ROOT__");
+        boolean aRoot = actual.getType().equals("__ROOT__");
+        if (eRoot || aRoot) {
+            if (!(eRoot && aRoot)) {
+                if (aRoot) printMissing(expected);
+                else       printUnexpected(actual);
+                m_DiffMissing++;
+            }
+            return;
+        }
+
+        testContentNode((ContentNode) expected, (ContentNode) actual);
+
+        m_BufferExpected.set(lenExpected - 1, null);
+        m_BufferActual.set(lenActual - 1, null);
+    }
+
+    private void testContentNode(ContentNode expected, ContentNode actual)
+    {
+        String contentExpected = expected.getContent();
+        String contentActual = actual.getContent();
+        if (!expected.equals(actual)) {
+            printDiff("text content differs", contentExpected, contentActual);
+            m_DiffContent++;
+        }
+    }
+
+    private void testBuffer()
+    {
+        int lenExpected = m_BufferExpected.size();
+        int lenActual   = m_BufferActual.size();
+        for (int i = 0; i < lenExpected - 1; i++ )
+        {
+            TreeNode node = m_BufferExpected.get(i);
+            int j = m_BufferActual.indexOf(node);
+            if (j >= 0) {
+                TreeNode other = m_BufferActual.get(j);
+                if (j != i)
+                {
+                    //FIXME how bad is this?
+                    printDiff("position differs",
+                            String.valueOf(i), String.valueOf(j));
+                    // a hacky hack
+                    int min = Math.min(i,j);
+                    int max = Math.max(Math.min(lenActual - 1, i),j);
+                    for (int k = min; k != max; k ++) {
+                        TreeNode tmp = m_BufferActual.get(k);
+                        if (tmp != null && tmp.isNesting()) {
+                            printNesting(node, tmp);
+                            m_DiffNesting++;
+                        }
+                    }
+                    m_DiffSequence++;
+                }
+                m_BufferActual.set(j, null);
+            } else {
+//m_Log.println("testBuffer:");
+                printMissing(node);
+                m_DiffMissing++;
+            }
+        }
+        for (int j = 0; j < lenActual - 1; j++)
+        {
+            TreeNode node = m_BufferActual.get(j);
+            if (node != null)
+            {
+//m_Log.println("testBuffer:");
+                printUnexpected(node);
+                if ((node instanceof TextNode) &&
+                        ((TextNode) node).getContent().length() == 0) {
+                    m_DiffSpuriousEmptyText++;
+                } else {
+                    m_DiffMissing++;
+                }
+            }
+        }
+        testTerminatingNode();
+        m_BufferExpected.clear();
+        m_BufferActual.clear();
+    }
+
+    void printDiff(String prefix, String expected, String actual)
+    {
+        m_Log.println(prefix +
+                ":\texpected: " + expected + "\tactual: " + actual);
+    }
+
+    void printNesting(TreeNode node, TreeNode nesting)
+    {
+        m_Log.println("node: " + node.toString()
+                + " possibly moved across nesting " + nesting.toString());
+    }
+
+    void printMissing(TreeNode node)
+    {
+        m_Log.println("   missing node: " + node.toString());
+
+    }
+
+    void printUnexpected(TreeNode node)
+    {
+        m_Log.println("unexpected node: " + node.toString());
+
+    }
+}
+
+
+//----------------------------------------------------------------------
+
+class EnumConverter
+{
+    private Stack<TreeNode> m_Stack;
+    TextPortionEnumerationTest m_T;
+
+    EnumConverter(TextPortionEnumerationTest err) {
+        m_Stack = new Stack<TreeNode>(); m_T = err;
+    }
+
+    TreeNode convert(XEnumeration xEnum) throws Exception
+    {
+        TreeNode root = new TreeNode();
+        m_Stack.push(root);
+        TreeNode ret = convertChildren(xEnum);
+        m_T.assure("EnumConverter.convert: stack", m_Stack.empty());
+        return ret;
+    }
+
+    TreeNode convertChildren(XEnumeration xEnum) throws Exception
+    {
+        while (xEnum.hasMoreElements()) {
+            TreeNode node;
+            Object xElement = xEnum.nextElement();
+            XTextRange xPortion = (XTextRange)
+                UnoRuntime.queryInterface(XTextRange.class, xElement);
+            XPropertySet xPropSet = (XPropertySet)
+                UnoRuntime.queryInterface(XPropertySet.class, xPortion);
+            String type = (String) xPropSet.getPropertyValue("TextPortionType");
+            if (type.equals("Text")) {
+                String text = xPortion.getString();
+                node = new TextNode(text);
+                String url = (String) xPropSet.getPropertyValue("HyperLinkURL");
+                if (url.length() > 0) {
+                    TreeNode temp = node;
+                    node = new HyperlinkNode(url);
+                    node.appendChild(temp);
+                }
+            } else if (type.equals("TextField")) {
+                Object xField = xPropSet.getPropertyValue("TextField");
+                XServiceInfo xService = (XServiceInfo)
+                    UnoRuntime.queryInterface(XServiceInfo.class, xField);
+                if (xService.supportsService(
+                        "com.sun.star.text.textfield.MetadataField"))
+                {
+                    XMetadatable xMeta = (XMetadatable)
+                        UnoRuntime.queryInterface(XMetadatable.class, xField);
+                    StringPair xmlid = xMeta.getMetadataReference();
+                    node = new MetaFieldNode(xmlid);
+                    m_Stack.push(node);
+                    XEnumerationAccess xEA = (XEnumerationAccess)
+                        UnoRuntime.queryInterface(XEnumerationAccess.class,
+                        xMeta);
+                    XEnumeration xEnumChildren = xEA.createEnumeration();
+                    TreeNode node2 = convertChildren(xEnumChildren);
+                    m_T.assure("stack error: meta-field", node == node2);
+                } else {
+                    XPropertySet xFieldPropSet = (XPropertySet)
+                        UnoRuntime.queryInterface(XPropertySet.class, xField);
+                    String content = (String)
+                        xFieldPropSet.getPropertyValue("Content");
+                    boolean isFixed = (Boolean)
+                        xFieldPropSet.getPropertyValue("IsFixed");
+                    m_T.assure("field not fixed?", isFixed);
+                    node = new TextFieldNode(content);
+                }
+            } else if (type.equals("Footnote")) {
+                Object xNote = xPropSet.getPropertyValue("Footnote");
+                XFootnote xFootnote = (XFootnote)
+                    UnoRuntime.queryInterface(XFootnote.class, xNote);
+                String label = xFootnote.getLabel();
+                node = new FootnoteNode(label);
+            } else if (type.equals("Frame")) {
+                XContentEnumerationAccess xCEA = (XContentEnumerationAccess)
+                    UnoRuntime.queryInterface(XContentEnumerationAccess.class,
+                        xPortion);
+                XEnumeration xContentEnum = xCEA.createContentEnumeration("");
+                while (xContentEnum.hasMoreElements()) {
+                    Object xFrame = xContentEnum.nextElement();
+                    XPropertySet xFramePropSet = (XPropertySet)
+                        UnoRuntime.queryInterface(XPropertySet.class, xFrame);
+                    TextContentAnchorType anchor = (TextContentAnchorType)
+                        xFramePropSet.getPropertyValue("AnchorType");
+                    XNamed xNamed = (XNamed)
+                        UnoRuntime.queryInterface(XNamed.class, xFrame);
+                    String name = xNamed.getName();
+                    node = new FrameNode(name, anchor);
+                    m_Stack.peek().appendChild(node);
+                }
+                continue;
+            } else if (type.equals("ControlCharacter")) {
+                short c = (Short)
+                    xPropSet.getPropertyValue("ControlCharacter");
+                node = new ControlCharacterNode(c);
+            } else if (type.equals("Bookmark")) {
+                Object xMark = xPropSet.getPropertyValue("Bookmark");
+                XNamed xNamed = (XNamed)
+                    UnoRuntime.queryInterface(XNamed.class, xMark);
+                String name = xNamed.getName();
+                XMetadatable xMetadatable = (XMetadatable)
+                    UnoRuntime.queryInterface(XMetadatable.class, xMark);
+                StringPair xmlid = xMetadatable.getMetadataReference();
+                boolean isCollapsed = (Boolean)
+                    xPropSet.getPropertyValue("IsCollapsed");
+                if (isCollapsed) {
+                    node = new BookmarkNode(name, xmlid);
+                } else {
+                    boolean isStart = (Boolean)
+                        xPropSet.getPropertyValue("IsStart");
+                    if (isStart) {
+                        node = new BookmarkStartNode(name, xmlid);
+                    } else {
+                        node = new BookmarkEndNode(name, xmlid);
+                    }
+                }
+            } else if (type.equals("ReferenceMark")) {
+                Object xMark = xPropSet.getPropertyValue("ReferenceMark");
+                XNamed xNamed = (XNamed)
+                    UnoRuntime.queryInterface(XNamed.class, xMark);
+                String name = xNamed.getName();
+                boolean isCollapsed = (Boolean)
+                    xPropSet.getPropertyValue("IsCollapsed");
+                if (isCollapsed) {
+                    node = new ReferenceMarkNode(name);
+                } else {
+                    boolean isStart = (Boolean)
+                        xPropSet.getPropertyValue("IsStart");
+                    if (isStart) {
+                        node = new ReferenceMarkStartNode(name);
+                    } else {
+                        node = new ReferenceMarkEndNode(name);
+                    }
+                }
+            } else if (type.equals("DocumentIndexMark")) {
+                Object xMark = xPropSet.getPropertyValue("DocumentIndexMark");
+                XPropertySet xMarkSet = (XPropertySet)
+                    UnoRuntime.queryInterface(XPropertySet.class, xMark);
+                String name = (String) xMarkSet.getPropertyValue("PrimaryKey");
+                boolean isCollapsed = (Boolean)
+                    xPropSet.getPropertyValue("IsCollapsed");
+                if (isCollapsed) {
+                    node = new DocumentIndexMarkNode(name);
+                } else {
+                    boolean isStart = (Boolean)
+                        xPropSet.getPropertyValue("IsStart");
+                    if (isStart) {
+                        node = new DocumentIndexMarkStartNode(name);
+                    } else {
+                        node = new DocumentIndexMarkEndNode(name);
+                    }
+                }
+            } else if (type.equals("SoftPageBreak")) {
+                node = new SoftPageBreakNode();
+            } else if (type.equals("Ruby")) {
+                boolean isStart = (Boolean)
+                    xPropSet.getPropertyValue("IsStart");
+                if (isStart) {
+                    // ARRGH!!! stupid api...
+                    // the text is ONLY at the start!
+                    String ruby = (String)
+                        xPropSet.getPropertyValue("RubyText");
+                    node = new RubyNode(ruby);
+                    m_Stack.push(node);
+                    continue;
+                } else {
+                    node = m_Stack.pop();
+                    m_T.assure("stack error: Ruby expected; is: " +
+                            node.toString(), node instanceof RubyNode);
+//                    m_T.assure("stack error: ruby",
+//                            ruby.equals(((RubyNode)node).getRubyText()));
+                }
+            } else if (type.equals("InContentMetadata")) {
+                Object xMeta = xPropSet.getPropertyValue("InContentMetadata");
+                XMetadatable xMetadatable = (XMetadatable)
+                    UnoRuntime.queryInterface(XMetadatable.class, xMeta);
+                StringPair xmlid = xMetadatable.getMetadataReference();
+                node = new MetaNode(xmlid);
+                m_Stack.push(node);
+                XEnumerationAccess xEA = (XEnumerationAccess)
+                    UnoRuntime.queryInterface(XEnumerationAccess.class, xMeta);
+                XEnumeration xEnumChildren = xEA.createEnumeration();
+                TreeNode node2 = convertChildren(xEnumChildren);
+                m_T.assure("stack error: meta", node == node2);
+//            } else if (type.equals("MetadataField")) {
+//                    Object xMeta = xPropSet.getPropertyValue("MetadataField");
+            } else {
+                throw new RuntimeException("unexpected type: " + type);
+            }
+            m_Stack.peek().appendChild(node);
+        }
+        TreeNode ret = m_Stack.pop();
+        return ret;
+    }
+}
+
+
+//----------------------------------------------------------------------
+
+abstract class Inserter
+{
+    private XMultiServiceFactory m_xDocFactory;
+    XText m_xText;
+    XParagraphCursor m_xCursor;
+
+    Inserter(XTextDocument xDoc)
+    {
+        m_xDocFactory = (XMultiServiceFactory)
+            UnoRuntime.queryInterface(XMultiServiceFactory.class, xDoc);
+        m_xText = xDoc.getText();
+        XTextCursor xCursor = m_xText.createTextCursor();
+        m_xCursor = (XParagraphCursor)
+            UnoRuntime.queryInterface(XParagraphCursor.class, xCursor);
+    }
+
+    void initParagraph() throws Exception
+    {
+        m_xCursor.gotoStartOfParagraph(false);
+        m_xText.insertControlCharacter(m_xCursor, PARAGRAPH_BREAK, false);
+        // we split the first (empty) paragraph, and then insert into the
+        // second (empty) paragraph; this ensures first is always empty!
+    }
+
+    void insertControlCharacter(XTextCursor xCursor, short cchar)
+        throws Exception
+    {
+        m_xText.insertControlCharacter(xCursor, cchar, false);
+    }
+
+    void insertText(XTextCursor xCursor, String text)
+    {
+        xCursor.setString(text);
+    }
+
+    void insertTextField(XTextCursor xCursor, String content) throws Exception
+    {
+        XTextContent xContent = makeTextField(content);
+        xContent.attach(xCursor);
+    }
+
+    XTextContent makeTextField(String content)
+        throws Exception
+    {
+        Object xField =
+            m_xDocFactory.createInstance("com.sun.star.text.textfield.Author");
+        XTextContent xContent = (XTextContent)
+            UnoRuntime.queryInterface(XTextContent.class, xField);
+        XPropertySet xPropSet = (XPropertySet)
+            UnoRuntime.queryInterface(XPropertySet.class, xField);
+        xPropSet.setPropertyValue("IsFixed", true);
+        xPropSet.setPropertyValue("FullName", false);
+        xPropSet.setPropertyValue("Content", content);
+        return xContent;
+    }
+
+    void insertFrame(XTextRange xCursor, String name,
+            TextContentAnchorType anchor) throws Exception
+    {
+        XTextContent xContent = makeFrame(name, anchor);
+        xContent.attach(xCursor);
+    }
+
+    XTextContent makeFrame(String name, TextContentAnchorType anchor)
+        throws Exception
+    {
+        Object xFrame =
+            m_xDocFactory.createInstance("com.sun.star.text.TextFrame");
+        XTextContent xContent = (XTextContent)
+            UnoRuntime.queryInterface(XTextContent.class, xFrame);
+        XPropertySet xPropSet = (XPropertySet)
+            UnoRuntime.queryInterface(XPropertySet.class, xFrame);
+        xPropSet.setPropertyValue("AnchorType", anchor);
+        XNamed xNamed = (XNamed)
+            UnoRuntime.queryInterface(XNamed.class, xContent);
+        xNamed.setName(name);
+        return xContent;
+    }
+
+    void insertFootnote(XTextCursor xCursor, String label) throws Exception
+    {
+        XTextContent xContent = makeFootnote(label);
+        xContent.attach(xCursor);
+    }
+
+    XTextContent makeFootnote(String label) throws Exception
+    {
+        Object xNote =
+            m_xDocFactory.createInstance("com.sun.star.text.Footnote");
+        XTextContent xContent = (XTextContent)
+            UnoRuntime.queryInterface(XTextContent.class, xNote);
+        XFootnote xFootnote = (XFootnote)
+            UnoRuntime.queryInterface(XFootnote.class, xNote);
+        xFootnote.setLabel(label);
+        return xContent;
+    }
+
+    void insertBookmark(XTextCursor xCursor, String name, StringPair xmlid)
+        throws Exception
+    {
+        XTextContent xContent = makeBookmark(name);
+        xContent.attach(xCursor);
+        if (!xmlid.First.equals(""))
+        {
+            XMetadatable xMetadatable = (XMetadatable)
+                UnoRuntime.queryInterface(XMetadatable.class, xContent);
+            xMetadatable.setMetadataReference(xmlid);
+        }
+    }
+
+    XTextContent makeBookmark(String name) throws Exception
+    {
+        Object xBookmark =
+            m_xDocFactory.createInstance("com.sun.star.text.Bookmark");
+        XTextContent xContent = (XTextContent)
+            UnoRuntime.queryInterface(XTextContent.class, xBookmark);
+        XNamed xNamed = (XNamed)
+            UnoRuntime.queryInterface(XNamed.class, xContent);
+        xNamed.setName(name);
+        return xContent;
+    }
+
+    void insertReferenceMark(XTextCursor xCursor, String name) throws Exception
+    {
+        XTextContent xContent = makeReferenceMark(name);
+        xContent.attach(xCursor);
+    }
+
+    XTextContent makeReferenceMark(String name) throws Exception
+    {
+        Object xMark =
+            m_xDocFactory.createInstance("com.sun.star.text.ReferenceMark");
+        XTextContent xContent = (XTextContent)
+            UnoRuntime.queryInterface(XTextContent.class, xMark);
+        XNamed xNamed = (XNamed)
+            UnoRuntime.queryInterface(XNamed.class, xContent);
+        xNamed.setName(name);
+        return xContent;
+    }
+
+    void insertDocumentIndexMark(XTextCursor xCursor, String key)
+        throws Exception
+    {
+        XTextContent xContent = makeDocumentIndexMark(key);
+        xContent.attach(xCursor);
+    }
+
+    XTextContent makeDocumentIndexMark(String key) throws Exception
+    {
+        Object xMark =
+            m_xDocFactory.createInstance("com.sun.star.text.DocumentIndexMark");
+        XTextContent xContent = (XTextContent)
+            UnoRuntime.queryInterface(XTextContent.class, xMark);
+        XPropertySet xPropSet = (XPropertySet)
+            UnoRuntime.queryInterface(XPropertySet.class, xMark);
+        xPropSet.setPropertyValue("PrimaryKey", key);
+        return xContent;
+    }
+
+    void insertHyperlink(XTextCursor xCursor, String url) throws Exception
+    {
+        XPropertySet xPropSet = (XPropertySet)
+            UnoRuntime.queryInterface(XPropertySet.class, xCursor);
+        xPropSet.setPropertyValue("HyperLinkURL", url);
+    }
+
+    void insertRuby(XTextCursor xCursor, String rubytext) throws Exception
+    {
+        XPropertySet xPropSet = (XPropertySet)
+            UnoRuntime.queryInterface(XPropertySet.class, xCursor);
+        xPropSet.setPropertyValue("RubyText", rubytext);
+    }
+
+    void insertMeta(XTextCursor xCursor, StringPair xmlid)
+        throws Exception
+    {
+        XTextContent xContent = makeMeta();
+        xContent.attach(xCursor);
+        XMetadatable xMetadatable = (XMetadatable)
+            UnoRuntime.queryInterface(XMetadatable.class, xContent);
+        xMetadatable.setMetadataReference(xmlid);
+    }
+
+    XTextContent makeMeta() throws Exception
+    {
+        Object xMeta = m_xDocFactory.createInstance(
+                "com.sun.star.text.InContentMetadata");
+        XTextContent xContent = (XTextContent)
+            UnoRuntime.queryInterface(XTextContent.class, xMeta);
+        return xContent;
+    }
+
+    void insertMetaField(XTextCursor xCursor, StringPair xmlid)
+        throws Exception
+    {
+        XTextContent xContent = makeMetaField();
+        xContent.attach(xCursor);
+        XMetadatable xMetadatable = (XMetadatable)
+            UnoRuntime.queryInterface(XMetadatable.class, xContent);
+        xMetadatable.setMetadataReference(xmlid);
+    }
+
+    XTextField makeMetaField() throws Exception
+    {
+        Object xMeta = m_xDocFactory.createInstance(
+                "com.sun.star.text.textfield.MetadataField");
+        XTextField xContent = (XTextField)
+            UnoRuntime.queryInterface(XTextField.class, xMeta);
+        return xContent;
+    }
+
+}
+
+class TreeInserter extends Inserter
+{
+    private Map<String, XTextRange> m_BookmarkStarts;
+    private Map<String, XTextRange> m_ReferenceMarkStarts;
+    private Map<String, XTextRange> m_DocumentIndexMarkStarts;
+    private List<Pair<XTextRange, FrameNode>> m_FrameHints;
+
+    TreeInserter(XTextDocument xDoc)
+    {
+        super(xDoc);
+        m_BookmarkStarts = new HashMap<String, XTextRange>();
+        m_ReferenceMarkStarts = new HashMap<String, XTextRange>();
+        m_DocumentIndexMarkStarts = new HashMap<String, XTextRange>();
+        m_FrameHints = new ArrayList<Pair<XTextRange, FrameNode>>();
+    }
+
+    void insertTree(TreeNode tree) throws Exception
+    {
+        if (!tree.getType().equals("__ROOT__"))
+            throw new RuntimeException("insertTree: test error: no root");
+        initParagraph();
+        insertChildren(tree.createEnumeration());
+        for (int i = 0; i < m_FrameHints.size(); ++i) {
+            Pair<XTextRange, FrameNode> p = m_FrameHints.get(i);
+            insertFrame(p.First, p.Second.getName(), p.Second.getAnchor());
+        }
+    }
+
+    void insertChildren(TreeNodeEnum children) throws Exception
+    {
+        while (children.hasNext()) {
+            m_xCursor.gotoEndOfParagraph(false);
+            TreeNode node = children.next();
+            String type = node.getType();
+            if (type.equals("Bookmark")) {
+                BookmarkNode bkmk = (BookmarkNode) node;
+                String name = bkmk.getName();
+                StringPair id = bkmk.getXmlId();
+                if (bkmk.isPoint()) {
+                    insertBookmark(m_xCursor, name, id);
+                } else if (bkmk.isStart()) {
+                    m_BookmarkStarts.put(name, m_xCursor.getStart());
+                } else {
+                    XTextRange xRange = m_BookmarkStarts.get(name);
+                    XParagraphCursor xCursor = mkCursor(xRange);
+                    insertBookmark(xCursor, name, id);
+                }
+            } else if (type.equals("ReferenceMark")) {
+                ReferenceMarkNode mark = (ReferenceMarkNode) node;
+                String name = mark.getName();
+                if (mark.isPoint()) {
+                    insertReferenceMark(m_xCursor, name);
+                } else if (mark.isStart()) {
+                    m_ReferenceMarkStarts.put(name, m_xCursor.getStart());
+                } else {
+                    XTextRange xRange = m_ReferenceMarkStarts.get(name);
+                    XParagraphCursor xCursor = mkCursor(xRange);
+                    insertReferenceMark(xCursor, name);
+                }
+            } else if (type.equals("DocumentIndexMark")) {
+                DocumentIndexMarkNode mark = (DocumentIndexMarkNode) node;
+                String name = mark.getName();
+                if (mark.isPoint()) {
+                    insertDocumentIndexMark(m_xCursor, name);
+                } else if (mark.isStart()) {
+                    m_DocumentIndexMarkStarts.put(name, m_xCursor.getStart());
+                } else {
+                    XTextRange xRange = m_DocumentIndexMarkStarts.get(name);
+                    XParagraphCursor xCursor = mkCursor(xRange);
+                    insertDocumentIndexMark(xCursor, name);
+                }
+            } else if (type.equals("Hyperlink")) {
+                HyperlinkNode href = (HyperlinkNode) node;
+                XTextRange xRange = m_xCursor.getStart();
+                insertChildren(href.createEnumeration());
+                XParagraphCursor xCursor = mkCursor(xRange);
+                insertHyperlink(xCursor, href.getURL());
+            } else if (type.equals("Ruby")) {
+                RubyNode ruby = (RubyNode) node;
+                XTextRange xRange = m_xCursor.getStart();
+                insertChildren(ruby.createEnumeration());
+                XParagraphCursor xCursor = mkCursor(xRange);
+                insertRuby(xCursor, ruby.getRubyText());
+            } else if (type.equals("InContentMetadata")) {
+                MetaNode meta = (MetaNode) node;
+                XTextRange xRange = m_xCursor.getStart();
+                insertChildren(meta.createEnumeration());
+                XParagraphCursor xCursor = mkCursor(xRange);
+                insertMeta(xCursor, meta.getXmlId());
+            } else if (type.equals("MetadataField")) {
+                MetaFieldNode meta = (MetaFieldNode) node;
+                XTextRange xRange = m_xCursor.getStart();
+                insertChildren(meta.createEnumeration());
+                XParagraphCursor xCursor = mkCursor(xRange);
+                insertMetaField(xCursor, meta.getXmlId());
+            } else if (type.equals("Text")) {
+                TextNode text = (TextNode) node;
+                insertText(m_xCursor, text.getContent());
+            } else if (type.equals("TextField")) {
+                TextFieldNode field = (TextFieldNode) node;
+                insertTextField(m_xCursor, field.getContent());
+            } else if (type.equals("Footnote")) {
+                FootnoteNode note = (FootnoteNode) node;
+                insertFootnote(m_xCursor, note.getLabel());
+            } else if (type.equals("Frame")) {
+                FrameNode frame = (FrameNode) node;
+                if (frame.getAnchor() == AT_CHARACTER) {
+                    m_FrameHints.add( new Pair(m_xCursor.getStart(), frame) );
+                } else {
+                    insertFrame(m_xCursor, frame.getName(), frame.getAnchor());
+                }
+            } else if (type.equals("ControlCharacter")) {
+                ControlCharacterNode cchar = (ControlCharacterNode) node;
+                insertControlCharacter(m_xCursor, cchar.getChar());
+            } else if (type.equals("SoftPageBreak")) {
+                SoftPageBreakNode spbk = (SoftPageBreakNode) node;
+                throw new RuntimeException("sorry, cannot test SoftPageBreak");
+            } else {
+                throw new RuntimeException("unexpected type: " + type);
+            }
+        }
+    }
+
+    XParagraphCursor mkCursor(XTextRange xRange)
+    {
+        XTextCursor xCursor = m_xText.createTextCursorByRange(xRange);
+        XParagraphCursor xParaCursor = (XParagraphCursor)
+            UnoRuntime.queryInterface(XParagraphCursor.class, xCursor);
+        xParaCursor.gotoEndOfParagraph(true);
+        return xParaCursor;
+    }
+}
+
+
+// FIXME: this does not account for inserted dummy characters!
+class RangeInserter extends Inserter
+{
+    RangeInserter(XTextDocument xDoc) throws Exception
+    { super(xDoc); initParagraph(); }
+
+    /*
+    void insertText(int pos, String text)
+    {
+        m_xCursor.gotoStartOfParagraph(false);
+        m_xCursor.goRight((short) pos, false);
+        insertText(m_xCursor, text);
+    }
+    */
+
+    void insertRange(Range range) throws Exception
+    {
+        m_xCursor.gotoStartOfParagraph(false);
+        m_xCursor.goRight(range.getStart(), false);
+        m_xCursor.goRight(range.getExtent(), true);
+        insertNode(m_xCursor, range.getNode());
+    }
+
+    void insertNode(XParagraphCursor xCursor, TreeNode node) throws Exception
+    {
+        String type = node.getType();
+        if (type.equals("Bookmark")) {
+            BookmarkNode bkmk = (BookmarkNode) node;
+            if (bkmk.isPoint()) throw new RuntimeException("range only");
+            insertBookmark(xCursor, bkmk.getName(), bkmk.getXmlId());
+        } else if (type.equals("ReferenceMark")) {
+            ReferenceMarkNode mark = (ReferenceMarkNode) node;
+            if (mark.isPoint()) throw new RuntimeException("range only");
+            insertReferenceMark(xCursor, mark.getName());
+        } else if (type.equals("DocumentIndexMark")) {
+            DocumentIndexMarkNode mark = (DocumentIndexMarkNode) node;
+            if (mark.isPoint()) throw new RuntimeException("range only");
+            insertDocumentIndexMark(xCursor, mark.getName());
+        } else if (type.equals("Hyperlink")) {
+            HyperlinkNode href = (HyperlinkNode) node;
+            insertHyperlink(xCursor, href.getURL());
+        } else if (type.equals("Ruby")) {
+            RubyNode ruby = (RubyNode) node;
+            insertRuby(xCursor, ruby.getRubyText());
+        } else if (type.equals("InContentMetadata")) {
+            MetaNode meta = (MetaNode) node;
+            insertMeta(xCursor, meta.getXmlId());
+        } else if (type.equals("MetadataField")) {
+            MetaFieldNode meta = (MetaFieldNode) node;
+            insertMetaField(xCursor, meta.getXmlId());
+        } else if (type.equals("Text")) {
+            TextNode text = (TextNode) node;
+            insertText(xCursor, text.getContent());
+        } else if (type.equals("TextField")) {
+            TextFieldNode field = (TextFieldNode) node;
+            insertTextField(m_xCursor, field.getContent());
+        } else if (type.equals("Footnote")) {
+            FootnoteNode note = (FootnoteNode) node;
+            insertFootnote(m_xCursor, note.getLabel());
+        } else if (type.equals("Frame")) {
+            FrameNode frame = (FrameNode) node;
+            insertFrame(xCursor, frame.getName(), frame.getAnchor());
+        } else if (type.equals("ControlCharacter")) {
+            ControlCharacterNode cchar = (ControlCharacterNode) node;
+            insertControlCharacter(m_xCursor, cchar.getChar());
+        } else if (type.equals("SoftPageBreak")) {
+            throw new RuntimeException("sorry, cannot test SoftPageBreak");
+        } else {
+            throw new RuntimeException("unexpected type: " + type);
+        }
+    }
+}
+
+
+//----------------------------------------------------------------------
+
+public class TextPortionEnumerationTest extends ComplexTestCase
+{
+    private XMultiServiceFactory m_xMSF = null;
+    private XComponentContext m_xContext = null;
+    private XTextDocument m_xDoc = null;
+    private String m_TmpDir = null;
+
+    private int m_Count = 1;
+
+//    public String[] getTestMethodNames() { return new String[] { "testLoadStore" }; }
+    public String[] getTestMethodNames() {
+        return new String[] {
+            "testText",
+            "testTextField",
+//            "testControlChar",
+//            "testSoftPageBreak",