[ooo-build-commit] ooeclipse: Branch 'master'

Cédric Bosdonnat cbosdo at kemper.freedesktop.org
Fri Jul 31 09:06:42 PDT 2009


 core/source/org/openoffice/ide/eclipse/core/editors/PackagePropertiesEditor.java      |   92 ++
 core/source/org/openoffice/ide/eclipse/core/editors/main/AbstractOverviewSection.java |   55 +
 core/source/org/openoffice/ide/eclipse/core/editors/main/DescriptionSourcePage.java   |   37 +
 core/source/org/openoffice/ide/eclipse/core/editors/main/GeneralSection.java          |   11 
 core/source/org/openoffice/ide/eclipse/core/editors/main/IntegrationSection.java      |   12 
 core/source/org/openoffice/ide/eclipse/core/editors/main/LicenseSection.java          |   13 
 core/source/org/openoffice/ide/eclipse/core/editors/main/LocalizedSection.java        |    5 
 core/source/org/openoffice/ide/eclipse/core/editors/main/MirrorsSection.java          |   16 
 core/source/org/openoffice/ide/eclipse/core/editors/main/PackageOverviewFormPage.java |   53 +
 core/source/org/openoffice/ide/eclipse/core/editors/main/PublisherSection.java        |   10 
 core/source/org/openoffice/ide/eclipse/core/editors/main/ReleaseNotesSection.java     |    9 
 core/source/org/openoffice/ide/eclipse/core/editors/messages.properties               |    2 
 core/source/org/openoffice/ide/eclipse/core/internal/model/UnoFactory.java            |   20 
 core/source/org/openoffice/ide/eclipse/core/model/description/DescriptionHandler.java |   16 
 core/source/org/openoffice/ide/eclipse/core/model/description/DescriptionModel.java   |  360 +++++++++-
 core/source/org/openoffice/ide/eclipse/core/model/description/XMLTokens.java          |   31 
 core/source/org/openoffice/ide/eclipse/core/utils/XMLWriter.java                      |  235 ++++++
 17 files changed, 937 insertions(+), 40 deletions(-)

New commits:
commit b673fb1be43eb3a8daaf5ea33f55be11d432db21
Author: Cédric Bosdonnat <cedricbosdo at openoffice.org>
Date:   Fri Jul 31 18:05:01 2009 +0200

    Description.xml editor: almost working now.
    
    There is still a tiny bug here: when opening the description source
    page, the doSave method isn't called... as if save was disabled at that
    point.
    
    Added a tiny description.xml file creation during the project creation.
    Synchronization between overview page and description source page is now
    ok.

diff --git a/core/source/org/openoffice/ide/eclipse/core/editors/PackagePropertiesEditor.java b/core/source/org/openoffice/ide/eclipse/core/editors/PackagePropertiesEditor.java
index 5aa2650..c0c2deb 100644
--- a/core/source/org/openoffice/ide/eclipse/core/editors/PackagePropertiesEditor.java
+++ b/core/source/org/openoffice/ide/eclipse/core/editors/PackagePropertiesEditor.java
@@ -43,7 +43,11 @@
  ************************************************************************/
 package org.openoffice.ide.eclipse.core.editors;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.StringReader;
 
 import javax.xml.parsers.SAXParser;
 import javax.xml.parsers.SAXParserFactory;
@@ -61,12 +65,14 @@ import org.eclipse.ui.forms.editor.FormEditor;
 import org.eclipse.ui.part.FileEditorInput;
 import org.eclipse.ui.texteditor.IElementStateListener;
 import org.openoffice.ide.eclipse.core.PluginLogger;
+import org.openoffice.ide.eclipse.core.editors.main.DescriptionSourcePage;
 import org.openoffice.ide.eclipse.core.editors.main.PackageContentsFormPage;
 import org.openoffice.ide.eclipse.core.editors.main.PackageOverviewFormPage;
 import org.openoffice.ide.eclipse.core.model.IPackageChangeListener;
 import org.openoffice.ide.eclipse.core.model.PackagePropertiesModel;
 import org.openoffice.ide.eclipse.core.model.description.DescriptionHandler;
 import org.openoffice.ide.eclipse.core.model.description.DescriptionModel;
+import org.xml.sax.InputSource;
 
 /**
  * The project package editor.
@@ -87,6 +93,7 @@ public class PackagePropertiesEditor extends FormEditor {
     private DescriptionModel mDescriptionModel;
     private PackagePropertiesModel mModel;
     private boolean mIgnoreSourceChanges = false;
+    private IFile mDescrFile;
     
     /**
      * Default constructor.
@@ -142,8 +149,10 @@ public class PackagePropertiesEditor extends FormEditor {
             addPage(mSourcePage);
             
             // Add the description.xml source page
-            mDescriptionPage = new SourcePage( this, "description", "description.xml"); //$NON-NLS-1$ //$NON-NLS-2$
+            mDescriptionPage = new DescriptionSourcePage( this, 
+                    "description", "description.xml"); //$NON-NLS-1$ //$NON-NLS-2$
             mDescriptionPage.init( getEditorSite(), mDescrEditorInput );
+            mDescriptionPage.getDocumentProvider();
             addPage( mDescriptionPage );
         } catch (PartInitException e) {
             // log ?
@@ -163,15 +172,15 @@ public class PackagePropertiesEditor extends FormEditor {
             IProject prj = fileInput.getFile().getProject();
             String projectName = prj.getName();
             
-            IFile descrFile = prj.getFile( "description.xml" ); //$NON-NLS-1$
-            mDescrEditorInput = new FileEditorInput( descrFile );
+            mDescrFile = prj.getFile( "description.xml" ); //$NON-NLS-1$
+            mDescrEditorInput = new FileEditorInput( mDescrFile );
             
             IFile propsFile = prj.getFile( "package.properties" ); //$NON-NLS-1$
             mPropsEditorInput = new FileEditorInput( propsFile );
             
             setPartName(projectName);
             
-            // Load the description
+            // Load the description.xml file
             try {
                 SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
                 // Enables the namespaces mapping
@@ -179,14 +188,15 @@ public class PackagePropertiesEditor extends FormEditor {
                 parser.getXMLReader().setFeature( 
                         "http://xml.org/sax/features/namespace-prefixes", true ); //$NON-NLS-1$
                 DescriptionHandler handler = new DescriptionHandler( getDescriptionModel() );
-                File file = new File( descrFile.getLocationURI().getPath() );
+                File file = new File( mDescrFile.getLocationURI().getPath() );
                 parser.parse(file, handler);
                 
             } catch ( Exception e ) {
-                PluginLogger.error( Messages.getString("PackagePropertiesEditor.DescriptionParseError"), e ); //$NON-NLS-1$
+                PluginLogger.error( 
+                        Messages.getString("PackagePropertiesEditor.DescriptionParseError"), //$NON-NLS-1$ 
+                        e );
             }
             
-            
             // Create the package properties
             mModel = new PackagePropertiesModel(fileInput.getFile());
             mModel.addChangeListener(new IPackageChangeListener() {
@@ -209,7 +219,7 @@ public class PackagePropertiesEditor extends FormEditor {
      */
     @Override
     public boolean isDirty() {
-        return mModel.isDirty();
+        return mModel.isDirty() || mOverviewPage.isDirty() || mDescriptionPage.isDirty();
     }
     
     /**
@@ -223,6 +233,12 @@ public class PackagePropertiesEditor extends FormEditor {
         } catch (Exception e) {
             // Log ?
         }
+        
+        // Reload the model if the we haven't moved from the overview page
+        if ( getActivePageInstance() == mOverviewPage ) {  
+            writeDescrToSource();
+            mDescriptionPage.doSave( pMonitor );
+        }
     }
 
     /**
@@ -259,6 +275,66 @@ public class PackagePropertiesEditor extends FormEditor {
     }
     
     /**
+     * Write the description model to the description source page.
+     */
+    public void writeDescrToSource( ) {
+        if ( mDescriptionPage.getDocumentProvider() instanceof TextFileDocumentProvider ) {
+            TextFileDocumentProvider docProvider  = (TextFileDocumentProvider)mDescriptionPage.getDocumentProvider();
+            IDocument doc = docProvider.getDocument( mDescriptionPage.getEditorInput() );
+            if ( doc != null ) {
+                mIgnoreSourceChanges = true;
+                
+                // Write the description.xml to a buffer stream
+                ByteArrayOutputStream out = null;
+                try {
+                    out = new ByteArrayOutputStream( );
+                    mDescriptionModel.serialize( out );
+                    doc.set( out.toString() );
+                } finally {
+                    try { out.close(); } catch (IOException e) { }
+                }
+                
+                mIgnoreSourceChanges = false;
+            }
+        }
+    }
+    
+    /**
+     * Re-load the model from the XML code shown in the description source page.
+     */
+    public void loadDescFromSource( ) {
+        if ( mDescriptionPage.getDocumentProvider() instanceof TextFileDocumentProvider ) {
+            TextFileDocumentProvider docProvider  = (TextFileDocumentProvider)mDescriptionPage.getDocumentProvider();
+            IDocument doc = docProvider.getDocument( mDescriptionPage.getEditorInput() );
+            if ( doc != null ) {
+                
+                StringReader reader = null;
+                try {
+                    SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
+                    // Enables the namespaces mapping
+                    parser.getXMLReader().setFeature( "http://xml.org/sax/features/namespaces" , true ); //$NON-NLS-1$
+                    parser.getXMLReader().setFeature( 
+                            "http://xml.org/sax/features/namespace-prefixes", true ); //$NON-NLS-1$
+                    DescriptionHandler handler = new DescriptionHandler( getDescriptionModel() );
+                    
+                    reader = new StringReader( doc.get( ) );
+                    InputSource is = new InputSource( reader );
+                    parser.parse( is, handler);
+
+                } catch ( Exception e ) {
+                    PluginLogger.error( 
+                            Messages.getString("PackagePropertiesEditor.DescriptionParseError"), //$NON-NLS-1$ 
+                            e );
+                } finally {
+                    reader.close();
+                }
+                
+                mOverviewPage.refresh( );
+            }
+        }
+    }
+    
+    /**
      * Write the properties model to the source editor page.
      */
     public void writeToSource() {
diff --git a/core/source/org/openoffice/ide/eclipse/core/editors/main/AbstractOverviewSection.java b/core/source/org/openoffice/ide/eclipse/core/editors/main/AbstractOverviewSection.java
new file mode 100644
index 0000000..153f745
--- /dev/null
+++ b/core/source/org/openoffice/ide/eclipse/core/editors/main/AbstractOverviewSection.java
@@ -0,0 +1,55 @@
+package org.openoffice.ide.eclipse.core.editors.main;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.SectionPart;
+import org.eclipse.ui.forms.editor.FormPage;
+
+/**
+ * Abstract section class providing mechanisms to suspend the dirty state change
+ * notifications.
+ * 
+ * @author Cédric Bosdonnat
+ *
+ */
+public abstract class AbstractOverviewSection extends SectionPart {
+
+    private boolean mNotifyChanges;
+    
+    /**
+     * The SectionPart constructor.
+     * 
+     * @param pParent the parent composite
+     * @param pPage the form page to use
+     * @param pStyle the SectionPart style
+     */
+    public AbstractOverviewSection(Composite pParent, FormPage pPage,
+            int pStyle) {
+        super(pParent, pPage.getManagedForm().getToolkit(), pStyle);
+        initialize( pPage.getManagedForm() );
+        getManagedForm().addPart( this );
+    }
+    
+    /**
+     * Inhibate the {@link #markDirty()} function.
+     * 
+     * @param pNotify whether to notify or not.
+     */
+    public void setNotifyChanges( boolean pNotify ) {
+        mNotifyChanges = pNotify;
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void markDirty() {
+        if ( mNotifyChanges ) {
+            super.markDirty();
+        }
+    }
+
+    /**
+     * Load the non-localized data from the model into the fields.
+     */
+    public abstract void loadData();
+}
diff --git a/core/source/org/openoffice/ide/eclipse/core/editors/main/DescriptionSourcePage.java b/core/source/org/openoffice/ide/eclipse/core/editors/main/DescriptionSourcePage.java
new file mode 100644
index 0000000..ca0af5f
--- /dev/null
+++ b/core/source/org/openoffice/ide/eclipse/core/editors/main/DescriptionSourcePage.java
@@ -0,0 +1,37 @@
+package org.openoffice.ide.eclipse.core.editors.main;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.openoffice.ide.eclipse.core.editors.PackagePropertiesEditor;
+import org.openoffice.ide.eclipse.core.editors.SourcePage;
+
+/**
+ * The source page for the description.xml file.
+ * 
+ * @author Cédric Bosdonnat
+ *
+ */
+public class DescriptionSourcePage extends SourcePage {
+    
+    /**
+     * Description source editor page constructor.
+     * 
+     * @param pFormEditor the editor hosting the page.
+     * @param pId the page identifier
+     * @param pTitle the page title
+     */
+    public DescriptionSourcePage(FormEditor pFormEditor, String pId, String pTitle) {
+        super( pFormEditor, pId, pTitle );
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean canLeaveThePage() {
+        PackagePropertiesEditor editor = (PackagePropertiesEditor)getEditor();
+        editor.loadDescFromSource();
+        
+        return super.canLeaveThePage();
+    }
+}
diff --git a/core/source/org/openoffice/ide/eclipse/core/editors/main/GeneralSection.java b/core/source/org/openoffice/ide/eclipse/core/editors/main/GeneralSection.java
index 22064ca..75b9271 100644
--- a/core/source/org/openoffice/ide/eclipse/core/editors/main/GeneralSection.java
+++ b/core/source/org/openoffice/ide/eclipse/core/editors/main/GeneralSection.java
@@ -57,6 +57,7 @@ public class GeneralSection extends LocalizedSection {
     private Text mNameTxt;
     private Text mIdTxt;
     private Text mVersionTxt;
+
     
     /**
      * @param pParent the parent composite where to add the section
@@ -65,10 +66,11 @@ public class GeneralSection extends LocalizedSection {
     public GeneralSection( Composite pParent, PackageOverviewFormPage pPage ) {
         super( pParent, pPage, Section.TITLE_BAR );
         
+        
         getSection().setText( Messages.getString("GeneralSection.Title") ); //$NON-NLS-1$
         
         mModel = pPage.getModel();
-        loadValues( );
+        loadData( );
         
         if ( mModel.mDisplayNames == null ) {
             mModel.mDisplayNames = new HashMap<Locale, String>( );
@@ -78,7 +80,7 @@ public class GeneralSection extends LocalizedSection {
     /**
      * Loads the values from the model into the controls.
      */
-    public void loadValues( ) {
+    public void loadData( ) {
         mIdTxt.setText( mModel.mId );
         mVersionTxt.setText( mModel.mVersion );
     }
@@ -106,6 +108,7 @@ public class GeneralSection extends LocalizedSection {
         mNameTxt.addModifyListener( new ModifyListener () {
             public void modifyText(ModifyEvent pE) {
                 mModel.mDisplayNames.put( mCurrentLocale, mNameTxt.getText() );
+                markDirty();
             }
         });
         
@@ -116,6 +119,7 @@ public class GeneralSection extends LocalizedSection {
         mIdTxt.addModifyListener( new ModifyListener () {
             public void modifyText(ModifyEvent pE) {
                 mModel.mId = mIdTxt.getText();
+                markDirty();
             }
         });
         
@@ -126,10 +130,11 @@ public class GeneralSection extends LocalizedSection {
         mVersionTxt.addModifyListener( new ModifyListener () {
             public void modifyText(ModifyEvent pE) {
                 mModel.mVersion = mVersionTxt.getText();
+                markDirty();
             }
         });
     }
-
+    
     /**
      * {@inheritDoc}
      */
diff --git a/core/source/org/openoffice/ide/eclipse/core/editors/main/IntegrationSection.java b/core/source/org/openoffice/ide/eclipse/core/editors/main/IntegrationSection.java
index a8507af..53a299a 100644
--- a/core/source/org/openoffice/ide/eclipse/core/editors/main/IntegrationSection.java
+++ b/core/source/org/openoffice/ide/eclipse/core/editors/main/IntegrationSection.java
@@ -54,7 +54,6 @@ import org.eclipse.swt.widgets.Label;
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.swt.widgets.Table;
 import org.eclipse.swt.widgets.Text;
-import org.eclipse.ui.forms.SectionPart;
 import org.eclipse.ui.forms.widgets.FormToolkit;
 import org.eclipse.ui.forms.widgets.Section;
 import org.openoffice.ide.eclipse.core.editors.Messages;
@@ -66,7 +65,7 @@ import org.openoffice.ide.eclipse.core.model.description.DescriptionModel;
  * @author Cédric Bosdonnat
  *
  */
-public class IntegrationSection extends SectionPart {
+public class IntegrationSection extends AbstractOverviewSection {
 
     static final String SEPARATOR = ","; //$NON-NLS-1$
     static final String[] PLATFORMS = {
@@ -106,19 +105,19 @@ public class IntegrationSection extends SectionPart {
      * @param pPage the parent page
      */
     public IntegrationSection( Composite pParent, PackageOverviewFormPage pPage ) {
-        super( pParent, pPage.getManagedForm().getToolkit(), Section.TITLE_BAR );
+        super( pParent, pPage, Section.TITLE_BAR );
         mPage = pPage;
         
         createContent( );
         
         mModel = pPage.getModel();
-        loadValues( );
+        loadData( );
     }
 
     /**
      * Loads the values from the model into the controls.
      */
-    public void loadValues( ) {
+    public void loadData( ) {
         mMinOOoTxt.setText( mModel.mMinOOo  );
         mMaxOOoTxt.setText( mModel.mMaxOOo );
         mPlatformTxt.setText( mModel.mPlatforms );
@@ -153,6 +152,7 @@ public class IntegrationSection extends SectionPart {
         mMinOOoTxt.addModifyListener( new ModifyListener () {
             public void modifyText(ModifyEvent pE) {
                 mModel.mMinOOo = mMinOOoTxt.getText();
+                markDirty();
             }
         });
         
@@ -165,6 +165,7 @@ public class IntegrationSection extends SectionPart {
         mMaxOOoTxt.addModifyListener( new ModifyListener () {
             public void modifyText(ModifyEvent pE) {
                 mModel.mMaxOOo = mMaxOOoTxt.getText();
+                markDirty();
             }
         });
         
@@ -175,6 +176,7 @@ public class IntegrationSection extends SectionPart {
         mPlatformTxt.addModifyListener( new ModifyListener () {
             public void modifyText(ModifyEvent pE) {
                 mModel.mPlatforms = mPlatformTxt.getText();
+                markDirty();
             }
         });
         
diff --git a/core/source/org/openoffice/ide/eclipse/core/editors/main/LicenseSection.java b/core/source/org/openoffice/ide/eclipse/core/editors/main/LicenseSection.java
index 8f3233a..a853f80 100644
--- a/core/source/org/openoffice/ide/eclipse/core/editors/main/LicenseSection.java
+++ b/core/source/org/openoffice/ide/eclipse/core/editors/main/LicenseSection.java
@@ -36,6 +36,8 @@ import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IResource;
 import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
 import org.eclipse.swt.events.SelectionAdapter;
 import org.eclipse.swt.events.SelectionEvent;
 import org.eclipse.swt.layout.GridData;
@@ -87,7 +89,7 @@ public class LicenseSection extends LocalizedSection {
     /**
      * Load the data from the model into the non-localized controls.
      */
-    private void loadData() {
+    public void loadData() {
         mSuppressUpdateBtn.setSelection( mModel.mSuppressOnUpdate );
         mUserAcceptBtn.setSelection( mModel.mAcceptByUser );
     }
@@ -125,6 +127,7 @@ public class LicenseSection extends LocalizedSection {
         mUserAcceptBtn.addSelectionListener( new SelectionAdapter( ) {
             public void widgetSelected(SelectionEvent pE) {
                 mModel.mAcceptByUser = mUserAcceptBtn.getSelection();
+                markDirty();
             } 
         });
         
@@ -138,6 +141,7 @@ public class LicenseSection extends LocalizedSection {
         mSuppressUpdateBtn.addSelectionListener( new SelectionAdapter( ) {
             public void widgetSelected(SelectionEvent pE) {
                 mModel.mSuppressOnUpdate = mSuppressUpdateBtn.getSelection();
+                markDirty();
             } 
         });
     }
@@ -156,6 +160,12 @@ public class LicenseSection extends LocalizedSection {
         
         mFileTxt = pToolkit.createText( pParent, new String( ) );
         mFileTxt.setLayoutData( new GridData( GridData.FILL_HORIZONTAL ) );
+        mFileTxt.addModifyListener( new ModifyListener( ) {
+            public void modifyText(ModifyEvent pE) {
+                mModel.mLicenses.put( mCurrentLocale, mFileTxt.getText() );
+                markDirty();
+            }            
+        });
         
         mFileBrowseBtn = pToolkit.createButton( pParent, "...", SWT.PUSH ); //$NON-NLS-1$
         mFileBrowseBtn.setLayoutData( new GridData( GridData.HORIZONTAL_ALIGN_END ) );
@@ -165,7 +175,6 @@ public class LicenseSection extends LocalizedSection {
                 // Open the folder selection dialog
                 ProjectSelectionDialog dlg = new ProjectSelectionDialog( mProject, 
                         Messages.getString("LicenseSection.FileChooserTooltip") ); //$NON-NLS-1$
-                dlg.setShowOnlyFolders( true );
                 
                 if ( dlg.open() == ProjectSelectionDialog.OK ) {
                     IResource res = dlg.getSelected();
diff --git a/core/source/org/openoffice/ide/eclipse/core/editors/main/LocalizedSection.java b/core/source/org/openoffice/ide/eclipse/core/editors/main/LocalizedSection.java
index 9609e57..33fab30 100644
--- a/core/source/org/openoffice/ide/eclipse/core/editors/main/LocalizedSection.java
+++ b/core/source/org/openoffice/ide/eclipse/core/editors/main/LocalizedSection.java
@@ -35,7 +35,6 @@ import java.util.Locale;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.widgets.Composite;
-import org.eclipse.ui.forms.SectionPart;
 import org.eclipse.ui.forms.editor.FormPage;
 import org.eclipse.ui.forms.widgets.FormToolkit;
 import org.eclipse.ui.forms.widgets.Section;
@@ -44,7 +43,7 @@ import org.eclipse.ui.forms.widgets.Section;
  * @author Cédric Bosdonnat
  *
  */
-public abstract class LocalizedSection extends SectionPart implements ILocaleListener {
+public abstract class LocalizedSection extends AbstractOverviewSection implements ILocaleListener {
     
     protected Locale mCurrentLocale;
     
@@ -56,7 +55,7 @@ public abstract class LocalizedSection extends SectionPart implements ILocaleLis
      * @param pStyle a bit-or of the styles defined in Section class
      */
     public LocalizedSection ( Composite pParent, FormPage pPage, int pStyle ) {
-        super( pParent, pPage.getManagedForm().getToolkit(), pStyle );
+        super( pParent, pPage, pStyle );
         
         mPage = pPage;
         
diff --git a/core/source/org/openoffice/ide/eclipse/core/editors/main/MirrorsSection.java b/core/source/org/openoffice/ide/eclipse/core/editors/main/MirrorsSection.java
index d97bb63..38cb80a 100644
--- a/core/source/org/openoffice/ide/eclipse/core/editors/main/MirrorsSection.java
+++ b/core/source/org/openoffice/ide/eclipse/core/editors/main/MirrorsSection.java
@@ -57,7 +57,6 @@ import org.eclipse.swt.widgets.Table;
 import org.eclipse.swt.widgets.TableColumn;
 import org.eclipse.swt.widgets.TableItem;
 import org.eclipse.swt.widgets.Text;
-import org.eclipse.ui.forms.SectionPart;
 import org.eclipse.ui.forms.widgets.FormToolkit;
 import org.eclipse.ui.forms.widgets.Section;
 import org.openoffice.ide.eclipse.core.OOEclipsePlugin;
@@ -71,7 +70,7 @@ import org.openoffice.ide.eclipse.core.model.description.DescriptionModel;
  * @author Cédric Bosdonnat
  *
  */
-public class MirrorsSection extends SectionPart {
+public class MirrorsSection extends AbstractOverviewSection {
 
     private static final int COLUMN_WIDTH = 200;
 
@@ -89,7 +88,7 @@ public class MirrorsSection extends SectionPart {
      * @param pPage the parent page
      */
     public MirrorsSection( Composite pParent, PackageOverviewFormPage pPage ) {
-        super( pParent, pPage.getManagedForm().getToolkit(), Section.TITLE_BAR );
+        super( pParent, pPage, Section.TITLE_BAR );
         mPage = pPage;
         
         createContent( );
@@ -98,6 +97,14 @@ public class MirrorsSection extends SectionPart {
     }
     
     /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void loadData() {
+        mTable.setInput( mModel.mUpdateInfos );
+    }
+    
+    /**
      * Creates the sections controls.
      */
     private void createContent() {
@@ -146,6 +153,7 @@ public class MirrorsSection extends SectionPart {
                 mModel.mUpdateInfos.add( text );
                 mTable.add( text );
                 mUrlTxt.setText( new String( ) );
+                markDirty();
             }
         } );
         
@@ -189,6 +197,7 @@ public class MirrorsSection extends SectionPart {
                 Object selected = sel.getFirstElement();
                 mTable.remove( selected );
                 mModel.mUpdateInfos.remove( selected );
+                markDirty();
             } 
         });
         
@@ -259,6 +268,7 @@ public class MirrorsSection extends SectionPart {
                 mModel.mUpdateInfos.set( pos, pValue.toString() );
                 mTable.replace( pValue, pos );
                 mTable.refresh( o );
+                markDirty();
             }
         }
     }
diff --git a/core/source/org/openoffice/ide/eclipse/core/editors/main/PackageOverviewFormPage.java b/core/source/org/openoffice/ide/eclipse/core/editors/main/PackageOverviewFormPage.java
index f79a16d..bef5957 100644
--- a/core/source/org/openoffice/ide/eclipse/core/editors/main/PackageOverviewFormPage.java
+++ b/core/source/org/openoffice/ide/eclipse/core/editors/main/PackageOverviewFormPage.java
@@ -43,6 +43,8 @@
  ************************************************************************/
 package org.openoffice.ide.eclipse.core.editors.main;
 
+import java.util.ArrayList;
+
 import org.eclipse.core.resources.IProject;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.layout.GridData;
@@ -57,6 +59,7 @@ import org.eclipse.ui.forms.editor.FormPage;
 import org.eclipse.ui.forms.widgets.FormToolkit;
 import org.eclipse.ui.forms.widgets.ScrolledForm;
 import org.openoffice.ide.eclipse.core.editors.Messages;
+import org.openoffice.ide.eclipse.core.editors.PackagePropertiesEditor;
 import org.openoffice.ide.eclipse.core.model.description.DescriptionModel;
 
 /**
@@ -71,6 +74,8 @@ public class PackageOverviewFormPage extends FormPage {
     private LocaleSelector mLocaleSel;
     private DescriptionModel mModel;
     
+    private ArrayList<AbstractOverviewSection> mSections;
+    
     /**
      * Constructor.
      * 
@@ -79,6 +84,7 @@ public class PackageOverviewFormPage extends FormPage {
      */
     public PackageOverviewFormPage(FormEditor pEditor, String pId ) {
         super(pEditor, pId, Messages.getString("PackageOverviewFormPage.Title")); //$NON-NLS-1$
+        mSections = new ArrayList<AbstractOverviewSection>( );
     }
 
     /**
@@ -129,6 +135,11 @@ public class PackageOverviewFormPage extends FormPage {
         createMainPage( toolkit, body );
         
         mLocaleSel.loadLocales( mModel.getAllLocales() );
+        
+        // Enable the dirty notifications in all sections 
+        for ( AbstractOverviewSection section : mSections ) {
+            section.setNotifyChanges( true );
+        }
     }
 
     /**
@@ -163,22 +174,60 @@ public class PackageOverviewFormPage extends FormPage {
          */
         GeneralSection generalSection = new GeneralSection( leftColumn, this );
         mLocaleSel.addListener( generalSection );
+        mSections.add( generalSection );
         
-        new IntegrationSection( leftColumn, this );
+        IntegrationSection integrationSection = new IntegrationSection( leftColumn, this );
+        mSections.add( integrationSection );
         
         PublisherSection publisherSection = new PublisherSection( leftColumn, this );
         mLocaleSel.addListener( publisherSection );
+        mSections.add( publisherSection );
         
         ReleaseNotesSection releaseNotesSection = new ReleaseNotesSection( leftColumn, this );
         mLocaleSel.addListener( releaseNotesSection );
+        mSections.add( releaseNotesSection );
         
-        new MirrorsSection( rightColumn, this );
+        MirrorsSection mirrorSection = new MirrorsSection( rightColumn, this );
+        mSections.add( mirrorSection );
         
         IFileEditorInput input = (IFileEditorInput)getEditorInput();
         IProject project = input.getFile().getProject();
         LicenseSection licenseSection = new LicenseSection( rightColumn, this, project );
+        mSections.add( licenseSection );
         mLocaleSel.addListener( licenseSection );
         
+        // Suspend the first dirty notifications in all sections 
+        for ( AbstractOverviewSection section : mSections ) {
+            section.setNotifyChanges( false );
+        }
         return body;
     }
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean canLeaveThePage() {
+        PackagePropertiesEditor editor = (PackagePropertiesEditor)getEditor();
+        editor.writeDescrToSource();
+        
+        return super.canLeaveThePage();
+    }
+    
+    /**
+     * Mark all the sections as saved.
+     */
+    public void setSaved( ) {
+        for (AbstractOverviewSection section : mSections) {
+            section.refresh( );
+        }
+    }
+
+    public void refresh() {
+        for ( AbstractOverviewSection section : mSections ) {
+            section.loadData( );
+        }
+        
+        mLocaleSel.loadLocales( mModel.getAllLocales() );
+    }
 }
diff --git a/core/source/org/openoffice/ide/eclipse/core/editors/main/PublisherSection.java b/core/source/org/openoffice/ide/eclipse/core/editors/main/PublisherSection.java
index 0e6bb53..59136c3 100644
--- a/core/source/org/openoffice/ide/eclipse/core/editors/main/PublisherSection.java
+++ b/core/source/org/openoffice/ide/eclipse/core/editors/main/PublisherSection.java
@@ -70,6 +70,14 @@ public class PublisherSection extends LocalizedSection {
         
         mModel = pPage.getModel();
     }
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void loadData() {
+        // Nothing to do: everything is localized
+    }
 
     /**
      * {@inheritDoc}
@@ -95,6 +103,7 @@ public class PublisherSection extends LocalizedSection {
             public void modifyText(ModifyEvent pE) {
                 PublisherInfos infos = mModel.mPublisherInfos.get( mCurrentLocale );
                 infos.mName = mNameTxt.getText();
+                markDirty();
             }
         });
         
@@ -107,6 +116,7 @@ public class PublisherSection extends LocalizedSection {
             public void modifyText(ModifyEvent pE) {
                 PublisherInfos infos = mModel.mPublisherInfos.get( mCurrentLocale );
                 infos.mUrl = mUrlTxt.getText();
+                markDirty();
             }
         });
     }
diff --git a/core/source/org/openoffice/ide/eclipse/core/editors/main/ReleaseNotesSection.java b/core/source/org/openoffice/ide/eclipse/core/editors/main/ReleaseNotesSection.java
index de83b06..9591c11 100644
--- a/core/source/org/openoffice/ide/eclipse/core/editors/main/ReleaseNotesSection.java
+++ b/core/source/org/openoffice/ide/eclipse/core/editors/main/ReleaseNotesSection.java
@@ -74,6 +74,14 @@ public class ReleaseNotesSection extends LocalizedSection implements
      * {@inheritDoc}
      */
     @Override
+    public void loadData() {
+        // Nothing to do: everything is localized
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     protected void createControls(FormToolkit pToolkit, Composite pParent) {
         pParent.setLayout( new GridLayout( 2, false ) );
         
@@ -92,6 +100,7 @@ public class ReleaseNotesSection extends LocalizedSection implements
         mUrlTxt.addModifyListener( new ModifyListener () {
             public void modifyText(ModifyEvent pE) {
                 mModel.mReleaseNotes.put( mCurrentLocale, mUrlTxt.getText() );
+                markDirty();
             }
         });
     }
diff --git a/core/source/org/openoffice/ide/eclipse/core/editors/messages.properties b/core/source/org/openoffice/ide/eclipse/core/editors/messages.properties
index 796902a..88ecb06 100644
--- a/core/source/org/openoffice/ide/eclipse/core/editors/messages.properties
+++ b/core/source/org/openoffice/ide/eclipse/core/editors/messages.properties
@@ -36,7 +36,7 @@ MirrorsSection.Add=Add
 MirrorsSection.Description=List the URLs of the update sites to use for this extension. The first one is the primary mirror.
 MirrorsSection.MirrorTextTitle=Specify a new mirror URL
 MirrorsSection.Remove=Remove
-PublisherSection.1=Define the localized informartions on the extension publisher.
+PublisherSection.Description=Define the localized informartions on the extension publisher.
 PublisherSection.Name=Name
 PublisherSection.Title=Provider informations
 PublisherSection.Url=Url
diff --git a/core/source/org/openoffice/ide/eclipse/core/internal/model/UnoFactory.java b/core/source/org/openoffice/ide/eclipse/core/internal/model/UnoFactory.java
index 0106d44..b7a8b47 100644
--- a/core/source/org/openoffice/ide/eclipse/core/internal/model/UnoFactory.java
+++ b/core/source/org/openoffice/ide/eclipse/core/internal/model/UnoFactory.java
@@ -43,10 +43,13 @@
  ************************************************************************/
 package org.openoffice.ide.eclipse.core.internal.model;
 
+import java.io.File;
+import java.io.FileOutputStream;
 import java.io.InputStream;
 import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Locale;
 
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.runtime.IPath;
@@ -66,6 +69,7 @@ import org.openoffice.ide.eclipse.core.model.IUnoFactoryConstants;
 import org.openoffice.ide.eclipse.core.model.IUnoidlProject;
 import org.openoffice.ide.eclipse.core.model.ProjectsManager;
 import org.openoffice.ide.eclipse.core.model.UnoFactoryData;
+import org.openoffice.ide.eclipse.core.model.description.DescriptionModel;
 import org.openoffice.ide.eclipse.core.model.language.ILanguage;
 import org.openoffice.ide.eclipse.core.model.language.IProjectHandler;
 
@@ -94,6 +98,22 @@ public final class UnoFactory {
         
         // Creates an empty package.properties file
         prj.getFile("package.properties").getLocation().toFile().createNewFile(); //$NON-NLS-1$
+        
+        // Creates an empty description.xml file
+        File file = prj.getFile("description.xml").getLocation().toFile(); //$NON-NLS-1$
+        DescriptionModel descrModel = new DescriptionModel( );
+        descrModel.mDisplayNames.put( Locale.ENGLISH, prj.getName() );
+        descrModel.mId = prj.getCompanyPrefix().toLowerCase() + "." + 
+            prj.getName().replaceAll( "[^a-zA-Z0-9]", new String( ) ).toLowerCase();
+        FileOutputStream out = null;
+        try {
+            out = new FileOutputStream( file );
+            descrModel.serialize( out );
+        } catch ( Exception e ) {
+            // TODO Log ?
+        } finally {
+            try { out.close(); } catch ( Exception e ) {}
+        }
             
         UnoidlProjectHelper.refreshProject(prj, null);
         UnoidlProjectHelper.forceBuild(prj, pMonitor);
diff --git a/core/source/org/openoffice/ide/eclipse/core/model/description/DescriptionHandler.java b/core/source/org/openoffice/ide/eclipse/core/model/description/DescriptionHandler.java
index cca35b1..07fe328 100644
--- a/core/source/org/openoffice/ide/eclipse/core/model/description/DescriptionHandler.java
+++ b/core/source/org/openoffice/ide/eclipse/core/model/description/DescriptionHandler.java
@@ -189,9 +189,9 @@ public class DescriptionHandler extends DefaultHandler {
                 Attributes pAttributes) throws SAXException {
             super.startElement(pUri, pLocalName, pName, pAttributes);
             
-            if ( XMLTokens.ATTR_OOO_MIN.equals( pName ) ) {
+            if ( XMLTokens.ELEMENT_OOO_MIN.equals( pName ) ) {
                 mModel.mMinOOo = pAttributes.getValue( XMLTokens.ATTR_VALUE );
-            } else if ( XMLTokens.ATTR_OOO_MAX.equals( pName ) ) {
+            } else if ( XMLTokens.ELEMENT_OOO_MAX.equals( pName ) ) {
                 mModel.mMaxOOo = pAttributes.getValue( XMLTokens.ATTR_VALUE );
             }
         }
@@ -217,7 +217,7 @@ public class DescriptionHandler extends DefaultHandler {
                 if ( mModel.mUpdateInfos == null ) {
                     mModel.mUpdateInfos = new ArrayList<String>( );
                 }
-                String value = pAttributes.getValue( XMLTokens.XLINK_URL, XMLTokens.ATTR_HREF );
+                String value = pAttributes.getValue( XMLTokens.URI_XLINK, XMLTokens.ATTR_HREF );
                 mModel.mUpdateInfos.add( value );
             }
         }
@@ -257,7 +257,7 @@ public class DescriptionHandler extends DefaultHandler {
                 mModel.mSuppressOnUpdate = Boolean.parseBoolean( value );
                 
             } else if ( XMLTokens.ELEMENT_LICENSE_TEXT.equals( pName ) ) {
-                String ref = pAttributes.getValue( XMLTokens.XLINK_URL, XMLTokens.ATTR_HREF );
+                String ref = pAttributes.getValue( XMLTokens.URI_XLINK, XMLTokens.ATTR_HREF );
                 String lang = pAttributes.getValue( XMLTokens.ATTR_LANG );
                 
                 Locale locale = parseLocale( lang );
@@ -292,7 +292,7 @@ public class DescriptionHandler extends DefaultHandler {
                 if ( mModel.mPublisherInfos == null ) {
                     mModel.mPublisherInfos = new HashMap<Locale, PublisherInfos>( );
                 }
-                mRef = pAttributes.getValue( XMLTokens.XLINK_URL, XMLTokens.ATTR_HREF );
+                mRef = pAttributes.getValue( XMLTokens.URI_XLINK, XMLTokens.ATTR_HREF );
                 mLocale = parseLocale( pAttributes.getValue( XMLTokens.ATTR_LANG ) );
             }
         }
@@ -347,7 +347,7 @@ public class DescriptionHandler extends DefaultHandler {
                 if ( mModel.mReleaseNotes == null ) {
                     mModel.mReleaseNotes = new HashMap<Locale, String>( );
                 }
-                String value = pAttributes.getValue( XMLTokens.XLINK_URL, XMLTokens.ATTR_HREF );
+                String value = pAttributes.getValue( XMLTokens.URI_XLINK, XMLTokens.ATTR_HREF );
                 Locale locale = parseLocale( pAttributes.getValue( XMLTokens.ATTR_LANG ) );
                 if ( locale != null ) {
                     mModel.mReleaseNotes.put( locale, value );
@@ -425,7 +425,7 @@ public class DescriptionHandler extends DefaultHandler {
                 Attributes pAttributes) throws SAXException {
             super.startElement(pUri, pLocalName, pName, pAttributes);
             
-            String ref = pAttributes.getValue( XMLTokens.XLINK_URL, XMLTokens.ATTR_HREF );
+            String ref = pAttributes.getValue( XMLTokens.URI_XLINK, XMLTokens.ATTR_HREF );
             if ( XMLTokens.ELEMENT_DEFAULT.equals( pName ) ) {
                 mModel.mDefaultIcon = ref;
                 
@@ -455,7 +455,7 @@ public class DescriptionHandler extends DefaultHandler {
                 if ( mModel.mDescriptions == null ) {
                     mModel.mDescriptions = new HashMap<Locale, String>( );
                 }
-                String value = pAttributes.getValue( XMLTokens.XLINK_URL, XMLTokens.ATTR_HREF );
+                String value = pAttributes.getValue( XMLTokens.URI_XLINK, XMLTokens.ATTR_HREF );
                 Locale locale = parseLocale( pAttributes.getValue( XMLTokens.ATTR_LANG ) );
                 if ( locale != null ) {
                     mModel.mDescriptions.put( locale, value );
diff --git a/core/source/org/openoffice/ide/eclipse/core/model/description/DescriptionModel.java b/core/source/org/openoffice/ide/eclipse/core/model/description/DescriptionModel.java
index bb3aaf2..7f60a52 100644
--- a/core/source/org/openoffice/ide/eclipse/core/model/description/DescriptionModel.java
+++ b/core/source/org/openoffice/ide/eclipse/core/model/description/DescriptionModel.java
@@ -30,11 +30,16 @@
  ************************************************************************/
 package org.openoffice.ide.eclipse.core.model.description;
 
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Locale;
 import java.util.Set;
+import java.util.Map.Entry;
+
+import org.openoffice.ide.eclipse.core.utils.XMLWriter;
 
 /**
  * Class representing the description.xml file.
@@ -81,7 +86,7 @@ public class DescriptionModel {
     /**
      * @return all the locales defined in the different parts of the model.
      */
-    public ArrayList<Locale> getAllLocales( ) {
+    public ArrayList<Locale> getAllLocales( ) {        
         ArrayList<Locale> locales = new ArrayList<Locale>( );
         
         appendNew( locales, mDisplayNames.keySet() );
@@ -92,6 +97,359 @@ public class DescriptionModel {
         
         return locales;
     }
+    
+    /**
+     * Serializes the data in XML to an output stream.
+     * 
+     * @param pOut the output stream where to write the data
+     */
+    public void serialize( OutputStream pOut ) {
+        XMLWriter writer = null;
+        try {
+            writer = new XMLWriter( pOut );
+            
+            HashMap<String, String> mapping = new HashMap<String, String>( );
+            mapping.put( XMLTokens.ATTR_XMLNS, XMLTokens.URI_DESCRIPTION );
+            mapping.put( XMLTokens.createQName( XMLTokens.ATTR_XMLNS, XMLTokens.PREFIX_DESCRIPTION ), 
+                    XMLTokens.URI_DESCRIPTION );
+            mapping.put( XMLTokens.createQName( XMLTokens.ATTR_XMLNS, XMLTokens.PREFIX_XLINK), 
+                    XMLTokens.URI_XLINK );
+            writer.startTag( XMLTokens.ELEMENT_DESCRIPTION, mapping );
+            
+            // Write the version element
+            printValueElement( writer, XMLTokens.ELEMENT_VERSION, mVersion );
+            printValueElement( writer, XMLTokens.ELEMENT_IDENTIFIER, mId );
+            printValueElement( writer, XMLTokens.ELEMENT_PLATFORM, mPlatforms );
+            
+            writeDependencies( writer );
+            writeUpdateInfos( writer );
+            writeLicenses( writer );
+            writePublisherInfos( writer );
+            writeReleaseNotes( writer );
+            writeDisplayNames( writer );
+            writeIcons( writer );
+            writeDescriptions( writer );
+            
+            writer.endTag( XMLTokens.ELEMENT_DESCRIPTION );
+            
+        } catch (UnsupportedEncodingException e) {
+            // Should never happen
+        } finally {
+            writer.close();
+        }
+    }
+
+    /**
+     * Write the dependencies element and its children.
+     * 
+     * @param pWriter the XML writer
+     */
+    private void writeDependencies(XMLWriter pWriter) {
+        
+        boolean hasMin = !mMinOOo.trim().isEmpty();
+        boolean hasMax = !mMaxOOo.trim().isEmpty();
+        if ( hasMin || hasMax ) {
+            pWriter.startTag( XMLTokens.ELEMENT_DEPENDENCIES, null );
+
+            if ( hasMin ) {
+                HashMap<String, String> attrs = new HashMap<String, String>( );
+                attrs.put( XMLTokens.ATTR_VALUE, mMinOOo.trim() );
+                attrs.put( XMLTokens.createQName(XMLTokens.PREFIX_DESCRIPTION, XMLTokens.ELEMENT_NAME ),
+                        "OpenOffice.org " + mMinOOo.trim() ); //$NON-NLS-1$
+                pWriter.printSingleTag( XMLTokens.ELEMENT_OOO_MIN, attrs );
+            }
+
+            if ( hasMax ) {
+                HashMap<String, String> attrs = new HashMap<String, String>( );
+                attrs.put( XMLTokens.ATTR_VALUE, mMaxOOo.trim() );
+                attrs.put( XMLTokens.createQName(XMLTokens.PREFIX_DESCRIPTION, XMLTokens.ELEMENT_NAME ),
+                        "OpenOffice.org " + mMaxOOo.trim( ) ); //$NON-NLS-1$
+                pWriter.printSingleTag( XMLTokens.ELEMENT_OOO_MAX, attrs );
+            }
+            
+            pWriter.endTag( XMLTokens.ELEMENT_DEPENDENCIES );
+        }
+    }
+    
+    /**
+     * Write the update-information element and its children.
+     * 
+     * @param pWriter the XML writer
+     */
+    private void writeUpdateInfos(XMLWriter pWriter) {
+        if ( mUpdateInfos.size() > 0 ) {
+            pWriter.startTag( XMLTokens.ELEMENT_UPDATE_INFORMATION, null );
+            
+            HashMap<String, String> attrs = new HashMap<String, String>( );
+            for (String mirror : mUpdateInfos) {
+                attrs.clear();
+                attrs.put( XMLTokens.createQName( XMLTokens.PREFIX_XLINK, XMLTokens.ATTR_HREF ),
+                        mirror.trim( ) );
+                pWriter.printSingleTag( XMLTokens.ELEMENT_SRC, attrs );
+            }
+            
+            pWriter.endTag( XMLTokens.ELEMENT_UPDATE_INFORMATION );
+        }
+    }
+    
+    /**
+     * Write the registration element and its children.
+     * 
+     * @param pWriter the XML writer
+     */
+    private void writeLicenses(XMLWriter pWriter) {
+        
+        // Check the presence of a license
+        boolean hasLicenses = false;
+        Iterator<String> i = mLicenses.values().iterator();
+        while ( !hasLicenses && i.hasNext( ) ) {
+            String value = i.next();
+            hasLicenses |= !value.trim().isEmpty();
+        }
+        
+        //Write the block
+        if ( hasLicenses ) {
+            pWriter.startTag( XMLTokens.ELEMENT_REGISTRATION, null );
+            
+            HashMap<String, String> attrs = new HashMap<String, String>( );
+            String acceptLevel = "admin"; //$NON-NLS-1$
+            if ( mAcceptByUser ) {
+                acceptLevel = "user"; //$NON-NLS-1$
+            }
+            attrs.put( XMLTokens.ATTR_ACCEPT_BY, acceptLevel );
+            if ( mSuppressOnUpdate ) {
+                attrs.put( XMLTokens.ATTR_SUPPRESS_ON_UPDATE, Boolean.toString( mSuppressOnUpdate ) );
+            }
+            pWriter.startTag( XMLTokens.ELEMENT_SIMPLE_LICENSE, attrs );
+            
+            Iterator<Entry<Locale, String>> iter = mLicenses.entrySet().iterator();
+            while ( iter.hasNext() ) {
+                Entry<Locale, String> entry = iter.next();
+                String locale = writeLocale( entry.getKey() );
+                attrs.clear();
+                attrs.put( XMLTokens.createQName( XMLTokens.PREFIX_XLINK, XMLTokens.ATTR_HREF ),
+                        entry.getValue().trim( ) );
+                attrs.put( XMLTokens.ATTR_LANG, locale );
+                pWriter.printSingleTag( XMLTokens.ELEMENT_LICENSE_TEXT, attrs );
+            }
+            
+            pWriter.endTag( XMLTokens.ELEMENT_SIMPLE_LICENSE );
+            pWriter.endTag( XMLTokens.ELEMENT_REGISTRATION );
+        }
+    }
+    
+    /**
+     * Write the publisher element and its children.
+     * 
+     * @param pWriter the XML writer
+     */
+    private void writePublisherInfos(XMLWriter pWriter) {
+        boolean hasInfos = false;
+        // Check the presence of an information
+        Iterator<PublisherInfos> i = mPublisherInfos.values().iterator();
+        while ( !hasInfos && i.hasNext() ) {
+            PublisherInfos info = i.next();
+            boolean hasName = !info.mName.trim().isEmpty();
+            boolean hasUrl = !info.mUrl.trim().isEmpty();
+            
+            hasInfos |= hasName && hasUrl;
+        }
+        
+        // Write the infos
+        if ( hasInfos ) {
+            pWriter.startTag( XMLTokens.ELEMENT_PUBLISHER, null );
+            
+            HashMap<String, String> attrs = new HashMap<String, String>( );
+            Iterator<Entry<Locale, PublisherInfos>> iter = mPublisherInfos.entrySet().iterator();
+            while ( iter.hasNext() ) {
+                Entry<Locale, PublisherInfos> entry = iter.next();
+                String locale = writeLocale( entry.getKey() );
+                attrs.clear();
+                
+                PublisherInfos info = entry.getValue();
+                boolean hasName = !info.mName.trim().isEmpty();
+                boolean hasUrl = !info.mUrl.trim().isEmpty();
+                
+                attrs.put( XMLTokens.createQName( XMLTokens.PREFIX_XLINK, XMLTokens.ATTR_HREF), 
+                        info.mUrl.trim() );
+                attrs.put( XMLTokens.ATTR_LANG, locale );
+                
+                if ( hasName && hasUrl ) {
+                    pWriter.startTag( XMLTokens.ELEMENT_NAME, attrs, false );
+                    pWriter.print( XMLWriter.getEscaped( info.mName.trim( ) ) );
+                    pWriter.endTag( XMLTokens.ELEMENT_NAME, false );
+                }
+            }
+            
+            pWriter.endTag( XMLTokens.ELEMENT_PUBLISHER );
+        }
+    }
+    
+    /**
+     * Write the release-notes element and its children.
+     * 
+     * @param pWriter the XML writer
+     */
+    private void writeReleaseNotes(XMLWriter pWriter) {
+        
+        // Check the presence of a release note
+        boolean hasReleaseNote = false;
+        Iterator<String> i = mReleaseNotes.values().iterator();
+        while ( !hasReleaseNote && i.hasNext( ) ) {
+            String value = i.next();
+            hasReleaseNote |= !value.trim().isEmpty();
+        }
+        
+        //Write the block
+        if ( hasReleaseNote ) {
+            pWriter.startTag( XMLTokens.ELEMENT_RELEASE_NOTES, null );
+            
+            HashMap<String, String> attrs = new HashMap<String, String>( );
+            Iterator<Entry<Locale, String>> iter = mReleaseNotes.entrySet().iterator();
+            while ( iter.hasNext() ) {
+                Entry<Locale, String> entry = iter.next();
+                String locale = writeLocale( entry.getKey() );
+                attrs.clear();
+                attrs.put( XMLTokens.createQName( XMLTokens.PREFIX_XLINK, XMLTokens.ATTR_HREF ),
+                        entry.getValue().trim( ) );
+                attrs.put( XMLTokens.ATTR_LANG, locale );
+                pWriter.printSingleTag( XMLTokens.ELEMENT_SRC, attrs );
+            }
+            
+            pWriter.endTag( XMLTokens.ELEMENT_RELEASE_NOTES );
+        }
+    }
+
+    /**
+     * Write the display-name element and its children.
+     * 
+     * @param pWriter the XML writer
+     */
+    private void writeDisplayNames(XMLWriter pWriter) {
+        
+        // Check the presence of a release note
+        boolean hasReleaseNote = false;
+        Iterator<String> i = mDisplayNames.values().iterator();
+        while ( !hasReleaseNote && i.hasNext( ) ) {
+            String value = i.next();
+            hasReleaseNote |= !value.trim().isEmpty();
+        }
+        
+        //Write the block
+        if ( hasReleaseNote ) {
+            pWriter.startTag( XMLTokens.ELEMENT_DISPLAY_NAME, null );
+            
+            HashMap<String, String> attrs = new HashMap<String, String>( );
+            Iterator<Entry<Locale, String>> iter = mDisplayNames.entrySet().iterator();
+            while ( iter.hasNext() ) {
+                Entry<Locale, String> entry = iter.next();
+                String locale = writeLocale( entry.getKey() );
+                attrs.clear();
+                attrs.put( XMLTokens.ATTR_LANG, locale );
+                pWriter.startTag( XMLTokens.ELEMENT_NAME, attrs, false );
+                pWriter.print( XMLWriter.getEscaped( entry.getValue().trim() ) );
+                pWriter.endTag( XMLTokens.ELEMENT_NAME, false );
+            }
+            
+            pWriter.endTag( XMLTokens.ELEMENT_DISPLAY_NAME );
+        }
+    }
+    
+    /**
+     * Write the icon element and its children.
+     * 
+     * @param pWriter the XML writer
+     */
+    private void writeIcons(XMLWriter pWriter) {
+        boolean hasDefault = !mDefaultIcon.trim().isEmpty();
+        boolean hasHC = !mHCIcon.trim().isEmpty();
+        
+        if ( hasDefault || hasHC ) {
+            pWriter.startTag( XMLTokens.ELEMENT_ICON, null );
+            
+            HashMap<String, String> attrs = new HashMap<String, String>( );
+            if ( hasDefault ) {
+                attrs.put( XMLTokens.createQName( XMLTokens.PREFIX_XLINK, XMLTokens.ATTR_HREF), 
+                        mDefaultIcon.trim() );
+                pWriter.printSingleTag( XMLTokens.ELEMENT_DEFAULT, attrs);
+            }
+            
+            if ( hasHC ) {
+                attrs.clear();
+                attrs.put( XMLTokens.createQName( XMLTokens.PREFIX_XLINK, XMLTokens.ATTR_HREF), 
+                        mHCIcon.trim() );
+                pWriter.printSingleTag( XMLTokens.ELEMENT_HIGH_CONTRAST, attrs);
+            }
+            
+            pWriter.endTag( XMLTokens.ELEMENT_ICON );
+        }
+    }
+    
+    /**
+     * Write the extension-descriptions element and its children.
+     * 
+     * @param pWriter the XML writer
+     */
+    private void writeDescriptions(XMLWriter pWriter) {
+        
+        // Check the presence of a description
+        boolean hasDescription = false;
+        Iterator<String> i = mDescriptions.values().iterator();
+        while ( !hasDescription && i.hasNext( ) ) {
+            String value = i.next();
+            hasDescription |= !value.trim().isEmpty();
+        }
+        
+        //Write the block
+        if ( hasDescription ) {
+            pWriter.startTag( XMLTokens.ELEMENT_EXTENSION_DESCRIPTION, null );
+            
+            HashMap<String, String> attrs = new HashMap<String, String>( );
+            Iterator<Entry<Locale, String>> iter = mDescriptions.entrySet().iterator();
+            while ( iter.hasNext() ) {
+                Entry<Locale, String> entry = iter.next();
+                String locale = writeLocale( entry.getKey() );
+                attrs.clear();
+                attrs.put( XMLTokens.createQName( XMLTokens.PREFIX_XLINK, XMLTokens.ATTR_HREF ),
+                        entry.getValue().trim( ) );
+                attrs.put( XMLTokens.ATTR_LANG, locale );
+                pWriter.printSingleTag( XMLTokens.ELEMENT_SRC, attrs );
+            }
+            
+            pWriter.endTag( XMLTokens.ELEMENT_EXTENSION_DESCRIPTION );
+        }
+    }
+    
+    /**
+     * Outputs the locale in a form ready to output to description.xml file.
+     * 
+     * @param pLocale the locale to write.
+     * 
+     * @return the string form of the locale
+     */
+    private String writeLocale( Locale pLocale ) {
+        char sep = '-'; //$NON-NLS-1$
+        String result = new String( );
+        
+        result = pLocale.toString().replace( '_', sep ); //$NON-NLS-1$
+        
+        return result;
+    }
+    
+    /**
+     * Writes an XML element of the following form:
+     * &lt;pElementName value="pValue"/&gt;.
+     * 
+     * @param pWriter the XML writer
+     * @param pElementName the element name
+     * @param pValue the element value
+     */
+    private void printValueElement(XMLWriter pWriter, String pElementName,
+            String pValue) {
+        HashMap<String, String> pAttributes = new HashMap<String, String>( );
+        pAttributes.put( XMLTokens.ATTR_VALUE , pValue );
+        pWriter.printSingleTag( pElementName, pAttributes );
+    }
 
     /**
      * Merges the pNewLocales into the pLocales, but avoids duplicates elements.
diff --git a/core/source/org/openoffice/ide/eclipse/core/model/description/XMLTokens.java b/core/source/org/openoffice/ide/eclipse/core/model/description/XMLTokens.java
index 336adc4..ce7cf63 100644
--- a/core/source/org/openoffice/ide/eclipse/core/model/description/XMLTokens.java
+++ b/core/source/org/openoffice/ide/eclipse/core/model/description/XMLTokens.java
@@ -37,7 +37,13 @@ package org.openoffice.ide.eclipse.core.model.description;
  *
  */
 public class XMLTokens {
+    
+    public static final String ATTR_XMLNS = "xmlns"; //$NON-NLS-1$
 
+    public static final String URI_DESCRIPTION = "http://openoffice.org/extensions/description/2006"; //$NON-NLS-1$
+    public static final String PREFIX_DESCRIPTION = "d"; //$NON-NLS-1$
+    
+    public static final String ELEMENT_DESCRIPTION = "description"; //$NON-NLS-1$
     public static final String ELEMENT_VERSION = "version"; //$NON-NLS-1$
     public static final String ELEMENT_IDENTIFIER = "identifier"; //$NON-NLS-1$
     public static final String ELEMENT_PLATFORM = "platform"; //$NON-NLS-1$
@@ -50,7 +56,8 @@ public class XMLTokens {
     public static final String ELEMENT_ICON = "icon"; //$NON-NLS-1$
     public static final String ELEMENT_EXTENSION_DESCRIPTION = "extension-description"; //$NON-NLS-1$
     
-    
+    public static final String ELEMENT_OOO_MIN = "OpenOffice.org-minimal-version"; //$NON-NLS-1$
+    public static final String ELEMENT_OOO_MAX = "OpenOffice.org-maximal-version"; //$NON-NLS-1$
     public static final String ELEMENT_NAME = "name"; //$NON-NLS-1$
     public static final String ELEMENT_SRC = "src"; //$NON-NLS-1$
     public static final String ELEMENT_SIMPLE_LICENSE = "simple-license"; //$NON-NLS-1$
@@ -60,14 +67,30 @@ public class XMLTokens {
 
     public static final String ATTR_LANG = "lang"; //$NON-NLS-1$
     public static final String ATTR_VALUE = "value"; //$NON-NLS-1$
-    public static final String ATTR_OOO_MIN = "OpenOffice.org-minimal-version"; //$NON-NLS-1$
-    public static final String ATTR_OOO_MAX = "OpenOffice.org-maximal-version"; //$NON-NLS-1$
     public static final String ATTR_ACCEPT_BY = "accept-by"; //$NON-NLS-1$
     public static final String ATTR_SUPPRESS_ON_UPDATE = "suppress-on-update"; //$NON-NLS-1$
 
     public static final String VALUE_USER = "user"; //$NON-NLS-1$
     public static final String VALUE_ADMIN = "admin"; //$NON-NLS-1$
     
-    public static final String XLINK_URL = "http://www.w3.org/1999/xlink"; //$NON-NLS-1$
+    public static final String URI_XLINK = "http://www.w3.org/1999/xlink"; //$NON-NLS-1$
+    public static final String PREFIX_XLINK = "xlink"; //$NON-NLS-1$
     public static final String ATTR_HREF = "href"; //$NON-NLS-1$
+    
+    /**
+     * Returns the XML qname corresponding to the given prefix and local name.
+     *  
+     * @param pPrefix the prefix (can be <code>null</code>)
+     * @param pLocalName the element local name
+     * 
+     * @return the qname
+     */
+    public static String createQName( String pPrefix, String pLocalName ) {
+        String qname = pLocalName;
+        if ( pPrefix != null ) {
+            qname = pPrefix + ":" + qname; //$NON-NLS-1$
+        }
+        return qname;
+    }
+    
 }
diff --git a/core/source/org/openoffice/ide/eclipse/core/utils/XMLWriter.java b/core/source/org/openoffice/ide/eclipse/core/utils/XMLWriter.java
new file mode 100644
index 0000000..37a8865
--- /dev/null
+++ b/core/source/org/openoffice/ide/eclipse/core/utils/XMLWriter.java
@@ -0,0 +1,235 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Cédric Bosdonnat - cleaned up the code
+ *******************************************************************************/
+package org.openoffice.ide.eclipse.core.utils;
+
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * A simple XML writer.
+ */
+public class XMLWriter extends PrintWriter {
+
+    /* constants */
+    protected static final String XML_VERSION = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; //$NON-NLS-1$
+    private static final int BUFFER_FREE_SPACE = 10;
+    
+    protected int mTab;
+    
+    /**
+     * Creates a new writer using the given output stream to write the data.
+     * 
+     * @param pOutput where to write the XML
+     * @throws UnsupportedEncodingException if the UTF8 charset isn't supported (would be strange)
+     */
+    public XMLWriter(OutputStream pOutput) throws UnsupportedEncodingException {
+        super(new OutputStreamWriter(pOutput, "UTF8")); //$NON-NLS-1$
+        mTab = 0;
+        println(XML_VERSION);
+    }
+
+    /**
+     * Write the end of an XML tag.
+     * 
+     * @param pName the name of the tag
+     */
+    public void endTag(String pName) {
+        mTab--;
+        printTag('/' + pName, null);
+    }
+    
+    /**
+     * Write the end of an XML tag.
+     * 
+     * @param pName the name of the tag
+     * @param pIndentation whether to print the indentation or not
+     */
+    public void endTag(String pName, boolean pIndentation ) {
+        mTab--;
+        printTag('/' + pName, null, pIndentation, true, false );
+    }
+
+    /**
+     * Write a simple XML tag, on the form &lt;name&gt;value&lt;/name&gt;.
+     * 
+     * @param pName the name of the tag
+     * @param pValue the value
+     */
+    public void printSimpleTag(String pName, Object pValue) {
+        if (pValue != null) {
+            printTag(pName, null, true, false, false);
+            print(getEscaped(String.valueOf(pValue)));
+            printTag('/' + pName, null, false, true, false);
+        }
+    }
+
+    /**
+     * Write the tab characters at the beginning of the line.
+     */
+    public void printTabulation() {
+        for (int i = 0; i < mTab; i++) {
+            super.print('\t');
+        }
+    }
+
+    /**
+     * Print an XML Tag in the form &lt;name .../&gt;.
+     * 
+     * @param pName the tag name
+     * @param pParameters the tag attributes
+     * 
+     * @see #startTag(String, HashMap)
+     * @see #startTag(String, HashMap, boolean)
+     */
+    public void printSingleTag(String pName, HashMap<String, ? extends Object> pParameters) {
+        printTag(pName, pParameters, true, true, true);
+    }
+    
+    /**
+     * Print an XML Tag.
+     * 
+     * @param pName the tag name
+     * @param pParameters the tag attributes
+     * 
+     * @see #startTag(String, HashMap)
+     * @see #startTag(String, HashMap, boolean)
+     */
+    public void printTag(String pName, HashMap<String, ? extends Object> pParameters) {
+        printTag(pName, pParameters, true, true, false);
+    }
+
+    /**
+     * Print an XML tag.
+     * 
+     * @param pName the tag name
+     * @param pParameters the tag attributes
+     * @param pShouldTab whether to add a tab or not before the tag
+     * @param pNewLine whether to add a new line or not after the tag
+     * @param pSingleTag writes a tag in the form &lt;name /&gt;
+     * 
+     * @see #startTag(String, HashMap)
+     * @see #startTag(String, HashMap, boolean)
+     */
+    public void printTag(String pName, HashMap<String, ? extends Object> pParameters, 
+            boolean pShouldTab, boolean pNewLine, boolean pSingleTag ) {
+        StringBuffer sb = new StringBuffer();
+        sb.append("<"); //$NON-NLS-1$
+        sb.append(pName);
+        if (pParameters != null) {
+            for (Iterator<String> it = pParameters.keySet().iterator(); it.hasNext();) {
+                sb.append(" "); //$NON-NLS-1$
+                String key = it.next();
+                sb.append(key);
+                sb.append("=\""); //$NON-NLS-1$
+                sb.append(getEscaped(String.valueOf(pParameters.get(key))));
+                sb.append("\""); //$NON-NLS-1$
+            }
+        }
+        if ( pSingleTag ) {
+            sb.append( "/" ); //$NON-NLS-1$
+        }
+        sb.append(">"); //$NON-NLS-1$
+        if (pShouldTab) {
+            printTabulation();
+        }
+        if (pNewLine) {
+            println(sb.toString());
+        } else {
+            print(sb.toString());
+        }
+    }
+
+    /**
+     * Write the start of an XML element.
+     * 
+     * @param pName the name of the element
+     * @param pParameters the attributes of the element
+     */
+    public void startTag(String pName, HashMap<String, ? extends Object> pParameters) {
+        startTag(pName, pParameters, true);
+    }
+
+    /**
+     * Write the start of an XML element.
+     * 
+     * @param pName the name of the element
+     * @param pParameters the attributes of the element
+     * @param pNewLine whether to add a line after the tag or not.
+     */
+    public void startTag(String pName, HashMap<String, ? extends Object> pParameters, boolean pNewLine) {
+        printTag(pName, pParameters, true, pNewLine, false);
+        mTab++;
+    }
+
+    /**
+     * Safely add a character to the buffer, replaces it by the corresponding XML entity
+     * if needed.
+     * 
+     * @param pBuffer where to write the character
+     * @param pC the character to add
+     */
+    private static void appendEscapedChar(StringBuffer pBuffer, char pC) {
+        String replacement = getReplacement(pC);
+        if (replacement != null) {
+            pBuffer.append('&');
+            pBuffer.append(replacement);
+            pBuffer.append(';');
+        } else {
+            pBuffer.append(pC);
+        }
+    }
+
+    /**
+     * Replace the XML problematic characters by their entities in the string.
+     *   
+     * @param pS the string to escape
+     * 
+     * @return the same string with the XML entities instead.
+     */
+    public static String getEscaped(String pS) {
+        StringBuffer result = new StringBuffer(pS.length() + BUFFER_FREE_SPACE);
+        for (int i = 0; i < pS.length(); ++i) {
+            appendEscapedChar(result, pS.charAt(i));
+        }
+        return result.toString();
+    }
+
+    /**
+     * Get the XML entity name for a character, or <code>null</code> if there is no 
+     * replacement for this character.
+     * 
+     * @param pC the character for which to get an XML entity
+     * @return the XML entity name
+     */
+    private static String getReplacement(char pC) {
+        // Encode special XML characters into the equivalent character references.
+        // These five are defined by default for all XML documents.
+        String result = null;
+        switch (pC) {
+            case '<' :
+                result = "lt"; //$NON-NLS-1$
+            case '>' :
+                result = "gt"; //$NON-NLS-1$
+            case '"' :
+                result = "quot"; //$NON-NLS-1$
+            case '\'' :
+                result = "apos"; //$NON-NLS-1$
+            case '&' :
+                result = "amp"; //$NON-NLS-1$
+        }
+        return result;
+    }
+}


More information about the ooo-build-commit mailing list