]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.export.ui/src/org/simantics/export/ui/OptionsPage.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.export.ui / src / org / simantics / export / ui / OptionsPage.java
diff --git a/bundles/org.simantics.export.ui/src/org/simantics/export/ui/OptionsPage.java b/bundles/org.simantics.export.ui/src/org/simantics/export/ui/OptionsPage.java
new file mode 100644 (file)
index 0000000..b796f1b
--- /dev/null
@@ -0,0 +1,798 @@
+package org.simantics.export.ui;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Set;\r
+import java.util.TreeMap;\r
+import java.util.TreeSet;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import org.eclipse.jface.wizard.WizardPage;\r
+import org.eclipse.swt.SWT;\r
+import org.eclipse.swt.custom.ScrolledComposite;\r
+import org.eclipse.swt.layout.GridData;\r
+import org.eclipse.swt.layout.GridLayout;\r
+import org.eclipse.swt.widgets.Combo;\r
+import org.eclipse.swt.widgets.Composite;\r
+import org.eclipse.swt.widgets.Control;\r
+import org.eclipse.swt.widgets.Event;\r
+import org.eclipse.swt.widgets.Listener;\r
+import org.osgi.service.prefs.BackingStoreException;\r
+import org.osgi.service.prefs.Preferences;\r
+import org.simantics.databoard.Accessors;\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.databoard.Datatypes;\r
+import org.simantics.databoard.accessor.RecordAccessor;\r
+import org.simantics.databoard.accessor.error.AccessorConstructionException;\r
+import org.simantics.databoard.accessor.error.AccessorException;\r
+import org.simantics.databoard.accessor.reference.ChildReference;\r
+import org.simantics.databoard.accessor.reference.LabelReference;\r
+import org.simantics.databoard.binding.RecordBinding;\r
+import org.simantics.databoard.binding.error.BindingException;\r
+import org.simantics.databoard.binding.error.DatatypeConstructionException;\r
+import org.simantics.databoard.binding.error.RuntimeBindingException;\r
+import org.simantics.databoard.binding.mutable.Variant;\r
+import org.simantics.databoard.forms.DataboardForm;\r
+import org.simantics.databoard.forms.DataboardForm.Problem;\r
+import org.simantics.databoard.type.RecordType;\r
+import org.simantics.databoard.type.UnionType;\r
+import org.simantics.export.core.ExportContext;\r
+import org.simantics.export.core.error.ExportException;\r
+import org.simantics.export.core.intf.ContentType;\r
+import org.simantics.export.core.intf.Exporter;\r
+import org.simantics.export.core.intf.Format;\r
+import org.simantics.export.core.intf.Publisher;\r
+import org.simantics.export.core.manager.Content;\r
+import org.simantics.export.core.manager.ExportAction;\r
+import org.simantics.export.core.manager.ExportManager;\r
+import org.simantics.export.core.manager.ExportPlan;\r
+import org.simantics.export.core.manager.ExportWizardResult;\r
+import org.simantics.export.core.util.ExporterUtils;\r
+import org.simantics.utils.datastructures.Arrays;\r
+import org.simantics.utils.datastructures.MapList;\r
+import org.simantics.utils.datastructures.ToStringComparator;\r
+import org.simantics.utils.datastructures.collections.CollectionUtils;\r
+import org.simantics.utils.ui.ExceptionUtils;\r
+import org.simantics.utils.ui.dialogs.ShowMessage;\r
+\r
+/**\r
+ * Dynamic Options page. Exporter, Importer and Format contributes options to this page. \r
+ *  \r
+ * @author toni.kalajainen@semantum.fi\r
+ */\r
+public class OptionsPage extends WizardPage {\r
+\r
+       public static String S_OUTPUT_OPTIONS = "Output Options";\r
+       public static LabelReference P_OUTPUT_OPTIONS = new LabelReference( S_OUTPUT_OPTIONS );\r
+       \r
+       /** A reference to combo box selection */\r
+       public static String S_PUBLISH = "Publish to";\r
+       public static ChildReference P_PUBLISH = ChildReference.compile(P_OUTPUT_OPTIONS, new LabelReference(S_PUBLISH));\r
+       \r
+       ExportContext ctx; \r
+       \r
+       ScrolledComposite scroll;\r
+       Composite composite;\r
+       DataboardForm form;\r
+\r
+       List<Content> selection;\r
+       int selectionHash;\r
+       String selectedPublisherId;\r
+       \r
+       public OptionsPage(ExportContext ctx) throws ExportException {\r
+               super("Options page", "Select export options", null);\r
+               \r
+               this.ctx = ctx;\r
+       }\r
+\r
+       Listener modificationListener = new Listener() {                \r
+               public void handleEvent(Event event) {\r
+                       if ( updatingForm ) return;\r
+                       validate();\r
+               }\r
+       };\r
+       \r
+       boolean updatingForm;\r
+       Listener outputSettingsModifiedListener = new Listener() {              \r
+               public void handleEvent(Event event) {\r
+                       updatingForm = true;\r
+                       try {\r
+                               Preferences contentScopePrefs = ctx.store.node( "Selection-"+selectionHash );\r
+                               Preferences workspaceScopePrefs = ctx.store;\r
+                               \r
+                               Composite outputOptions = (Composite) form.getControl(composite, P_OUTPUT_OPTIONS);\r
+                               Combo publishTo = (Combo) form.getControl(composite, P_PUBLISH);                        \r
+                               String newPublisherLabel = publishTo.getText();\r
+                               Publisher newPublisher = ctx.eep.getPublisherByLabel(newPublisherLabel);\r
+                               String newPublisherId = newPublisher==null?null:newPublisher.id();\r
+                               //if ( newPublisherId.equals(selectedPublisherId) ) return;\r
+                               \r
+                               // Save Preferences\r
+                               RecordBinding binding = ctx.databoard.getMutableBinding( form.type() );\r
+                               Object obj = binding.createDefault();\r
+                               form.readFields(composite, binding, obj);\r
+                               Variant options = new Variant(binding, obj);\r
+                               \r
+                               Publisher oldPublisher = ctx.eep.getPublisher( selectedPublisherId );\r
+                               if ( oldPublisher!=null ) {\r
+                                       ChildReference oldOptionsRef = new LabelReference( oldPublisher.label() );\r
+                                       try {\r
+                                               Variant locationOptions = options.getComponent( oldOptionsRef );\r
+                                               oldPublisher.publisherClass().savePref(locationOptions, contentScopePrefs, workspaceScopePrefs);\r
+                                       } catch ( AccessorConstructionException ee ) {}\r
+                               }\r
+\r
+                               List<Content> manifest = getManifestFor(selection, options);\r
+                               cleanPublisherFromGroup(selectedPublisherId);\r
+                               addPublisherToGroup(newPublisherId, manifest);\r
+                               \r
+                               outputOptions.pack(true);\r
+                               outputOptions.layout(true);\r
+                               composite.pack(true);\r
+                               composite.layout(true);\r
+                               \r
+                               RecordType dummy = new RecordType();\r
+                               RecordType newPublisherOptions = newPublisher.publisherClass().locationOptions(ctx, manifest);\r
+                               dummy.addComponent( newPublisherLabel, newPublisherOptions );\r
+                               binding = ctx.databoard.getMutableBinding( dummy );\r
+                               obj = binding.createDefault();\r
+                               options = new Variant(binding, obj);\r
+                               ChildReference locationOptionsRef = new LabelReference( newPublisherLabel );\r
+                               Variant locationOptions = options.getComponent( locationOptionsRef );\r
+                               newPublisher.publisherClass().fillDefaultPrefs(ctx, selection, options, locationOptions);\r
+                               newPublisher.publisherClass().loadPref(locationOptions, contentScopePrefs, workspaceScopePrefs);        \r
+                               //loadPublisherPref(newPublisherId, options, contentScopePrefs, workspaceScopePrefs);\r
+                               form.writeFields(outputOptions, (RecordBinding) options.getBinding(), options.getValue());\r
+                               \r
+                               selectedPublisherId = newPublisherId;\r
+\r
+                               updatingForm = false;\r
+                               validate();\r
+                       } catch ( BindingException e ) {\r
+                               setErrorMessage( e.getClass().getName()+": "+ e.getMessage() );\r
+                   ExceptionUtils.logError(e);                 \r
+                       } catch (AccessorConstructionException e) {\r
+                               setErrorMessage( e.getClass().getName()+": "+ e.getMessage() );\r
+                   ExceptionUtils.logError(e);                 \r
+                       } catch (AccessorException e) {\r
+                               setErrorMessage( e.getClass().getName()+": "+ e.getMessage() );\r
+                   ExceptionUtils.logError(e);                 \r
+                       } catch (ExportException e) {\r
+                               setErrorMessage( e.getClass().getName()+": "+ e.getMessage() );\r
+                   ExceptionUtils.logError(e);                 \r
+                       } finally {\r
+                               updatingForm = false;\r
+                       }\r
+               }\r
+       };\r
+       \r
+       @Override\r
+       public void createControl(Composite parent) {\r
+               \r
+           scroll = new ScrolledComposite(parent, SWT.V_SCROLL);\r
+           composite = new Composite(scroll, 0);\r
+               composite.setLayout( new GridLayout(3, false) );                        \r
+               composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1));                   \r
+        scroll.setContent(composite);\r
+        scroll.setExpandHorizontal(true);\r
+        scroll.setExpandVertical(false);\r
+        scroll.getVerticalBar().setIncrement(scroll.getVerticalBar().getIncrement()*8);\r
+\r
+           form = new DataboardForm();\r
+           form.setFirstColumnWidth(200);\r
+\r
+               composite.pack();\r
+               setControl(scroll);\r
+        setPageComplete(true);\r
+        \r
+       }\r
+       \r
+       public void update(List<Content> contents) \r
+       {\r
+               try {\r
+                       // 0. Initialization\r
+                       Set<String> contentUris = new TreeSet<String>();\r
+                       MapList<String, Content> uriToContentMap = new MapList<String, Content>();\r
+                       MapList<ContentType, String> contentByContentType = new MapList<ContentType, String>();\r
+                       MapList<Format, String> contentByFormat = new MapList<Format, String>();\r
+                       List<Format> formats = new ArrayList<Format>();\r
+                       Map<String, Format> contentFormatMap = new TreeMap<String, Format>( String.CASE_INSENSITIVE_ORDER );\r
+                       for (Content content : contents) {\r
+                               contentUris.add(content.url);\r
+                               ContentType ct = ctx.eep.getContentType( content.contentTypeId );\r
+                               contentByContentType.add(ct, content.url);\r
+                               Format format = ctx.eep.getFormat( content.formatId );\r
+                               contentByFormat.add(format, content.url);\r
+                               uriToContentMap.add(content.url, content);\r
+                       }\r
+                       formats.addAll( contentByFormat.getKeys() );\r
+                       \r
+                       Collections.sort(formats, new ToStringComparator());\r
+                                               \r
+                       for (Content content : contents) {\r
+                               String id = content.formatId;\r
+                               Format format = contentFormatMap.get( id );\r
+                               if ( format != null ) continue;\r
+                               format = ctx.eep.getFormat(id);\r
+                               contentFormatMap.put(id, format);\r
+                       }\r
+\r
+                       MapList<Exporter, String> exporterContentMap = new MapList<Exporter, String>();\r
+                       TreeMap<String, Exporter> orderedExporters = new TreeMap<String, Exporter>( String.CASE_INSENSITIVE_ORDER );\r
+                       for (Content content : contents) {\r
+                               for (Exporter exporter : ctx.eep.getExporters(content.formatId, content.contentTypeId) ) {\r
+                                       exporterContentMap.add(exporter, content.url);\r
+                                       orderedExporters.put(exporter.formatId()+exporter.contentTypeId()+exporter.exportAction().getClass(), exporter);\r
+                               }\r
+                       }\r
+                       \r
+                       // 1. Save selections from previous form\r
+                       savePrefs();\r
+                       \r
+                       // 2. Clear previous form\r
+                       form.clear(composite);\r
+\r
+                       // 3. Create options record\r
+                       RecordType optionsRecord = new RecordType();\r
+\r
+                       // Add Output options box\r
+                   RecordType outputOptions = new RecordType();\r
+                       \r
+                       for ( Format format : formats ) {\r
+                               if ( format.isGroupFormat() && contentByFormat.getValues(format).size()>1 ) {\r
+                                       outputOptions.addComponent("Merge "+format.fileext()+" content into one file", Datatypes.BOOLEAN);\r
+                               }\r
+                               \r
+                               if ( format.isContainerFormat() ) {\r
+                                       List<String> formatsContentUris = contentByFormat.getValues(format);\r
+                                       int attachmentCount = 0;\r
+                                       for ( String contentUri : formatsContentUris ) {\r
+                                               for ( Content content : uriToContentMap.getValues(contentUri) ) {\r
+                                                       if ( !content.formatId.equals(format.id()) ) attachmentCount++;\r
+                                               }\r
+                                       }\r
+                                       // Add as possible attachment, all contents that don't have this format\r
+                                       // their their content type.\r
+                                       for ( Content content : contents ) {\r
+                                               if ( ctx.eep.getExporters(format.id(), content.contentTypeId).length == 0) attachmentCount++;\r
+                                       }\r
+                                       \r
+                                       if ( attachmentCount > 0 ) { \r
+                                               outputOptions.addComponent("Include attachments to "+format.fileext(), Datatypes.BOOLEAN);\r
+                                               outputOptions.addComponent("Export attachments of "+format.fileext()+" to separate files", Datatypes.BOOLEAN);\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+                       UnionType publisherType = new UnionType();\r
+                       for (Publisher publisher : ctx.eep.publishers()) publisherType.addComponent(publisher.label(), Datatypes.VOID);\r
+                       outputOptions.addComponent(S_PUBLISH, publisherType);\r
+                       \r
+                   optionsRecord.addComponent(S_OUTPUT_OPTIONS, outputOptions);                    \r
+                   \r
+                       // Add Format specific boxes\r
+                       for (Format format : contentFormatMap.values()) {\r
+                               RecordType formatOptions = format.formatActions().options(ctx);\r
+                               if ( formatOptions==null ) continue;\r
+                               optionsRecord.mergeRecord( formatOptions );\r
+                       }\r
+       \r
+                       // Add Exporter specific boxes\r
+                       for (Exporter exporter : orderedExporters.values()) {                           \r
+                               List<String> exportSpecificContents = exporterContentMap.getValues(exporter);\r
+                               if ( exportSpecificContents==null || exportSpecificContents.isEmpty() ) continue;\r
+                               RecordType exporterOptions = exporter.exportAction().options(ctx, exportSpecificContents);\r
+                               if ( exporterOptions == null ) continue;\r
+                               optionsRecord.mergeRecord( exporterOptions );\r
+                       }\r
+\r
+                       // 4. Load default and previous selections of the options ( All but publisher )\r
+                       ctx.databoard.clear();\r
+                       RecordBinding optionsBinding = ctx.databoard.getMutableBinding( optionsRecord );\r
+                       Object optionsObj = optionsBinding.createDefaultUnchecked();\r
+                       Variant options = new Variant(optionsBinding, optionsObj);\r
+                       {\r
+                               Preferences workspaceScopePrefs = ctx.store;\r
+                               Preferences contentScopePrefs = ctx.store( contents );\r
+                               \r
+                               for (Exporter exporter : orderedExporters.values()) {\r
+                                       exporter.exportAction().fillDefaultPrefs(ctx, options);\r
+                                       exporter.exportAction().loadPref(options, contentScopePrefs, workspaceScopePrefs);\r
+                               }\r
+                               \r
+                               for (Format format : contentByFormat.getKeys()) {\r
+                                       format.formatActions().fillDefaultPrefs( ctx, options );\r
+                                       format.formatActions().loadPref(options, contentScopePrefs, workspaceScopePrefs);\r
+                               }\r
+                               \r
+                               fillDefaultPrefs(options);\r
+                               loadPref(options, contentScopePrefs, workspaceScopePrefs);\r
+                       }\r
+                       \r
+                       // 5. Create form\r
+                       form.addFields(composite, optionsRecord);\r
+                       form.addListener(composite, form.type(), modificationListener);\r
+                       Composite outputOptionsGroup = (Composite) form.getControl(composite, P_OUTPUT_OPTIONS);\r
+                       RecordType dummy = new RecordType();\r
+                       dummy.addComponent(S_OUTPUT_OPTIONS, outputOptions);\r
+                       form.addListener(outputOptionsGroup, dummy, outputSettingsModifiedListener);\r
+                       form.writeFields(composite, optionsBinding, optionsObj);\r
+                       \r
+                       // 6. Add publisher\r
+                       {\r
+                               selection = contents;\r
+                               Preferences workspaceScopePrefs = ctx.store;\r
+                               Preferences contentScopePrefs = ctx.store.node( "Selection-"+selectionHash );\r
+                               selectedPublisherId = ctx.store.get("publisherId", "");         \r
+                               Publisher publisher = ctx.eep.getPublisher(selectedPublisherId);\r
+                               if ( publisher != null ) {\r
+                                       \r
+                                       // 6A. Manifest\r
+                                       List<Content> manifest = getManifestFor(contents, options);\r
+                                       \r
+                                       // 6B. Default and previous settings\r
+                                       String label = publisher.label();\r
+                                       RecordType publisherOptionsType = publisher.publisherClass().locationOptions(ctx, manifest);\r
+                                       RecordType publisherOptionsRootType = new RecordType();\r
+                                       publisherOptionsRootType.addComponent(label, publisherOptionsType);\r
+                                       RecordBinding publisherOptionsRootBinding = ctx.databoard.getMutableBinding( publisherOptionsRootType );\r
+                                       Object publisherOptionsRootObj = publisherOptionsRootBinding.createDefaultUnchecked();\r
+                                       Variant publisherOptionsRoot = new Variant(publisherOptionsRootBinding, publisherOptionsRootObj);\r
+                                       try {\r
+                                               ChildReference locationOptionsRef = new LabelReference( label );\r
+                                               Variant locationOptions = publisherOptionsRoot.getComponent( locationOptionsRef );\r
+                                               publisher.publisherClass().fillDefaultPrefs(ctx, selection, options, locationOptions);\r
+                                               publisher.publisherClass().loadPref(locationOptions, contentScopePrefs, workspaceScopePrefs);   \r
+                                       } catch ( AccessorConstructionException ee ) {\r
+                                       }\r
+                                       \r
+                                       // 6C. Add Publisher form\r
+                                       addPublisherToGroup(selectedPublisherId, manifest);\r
+                                       form.writeFields(composite, publisherOptionsRootBinding, publisherOptionsRootObj);\r
+                               }\r
+                       }\r
+\r
+                       // 8. Validate page\r
+                       composite.pack();\r
+                       validate();\r
+                       \r
+               } catch (BindingException e) {\r
+            ExceptionUtils.logError(e);                        \r
+                       ShowMessage.showError("Unexpected error", e.getClass().getName()+" "+e.getMessage());\r
+                       throw new RuntimeBindingException(e);\r
+               } catch (ExportException e) {\r
+            ExceptionUtils.logError(e);                        \r
+                       ShowMessage.showError("Unexpected error", e.getClass().getName()+" "+e.getMessage());\r
+               } catch (DatatypeConstructionException e) {\r
+            ExceptionUtils.logError(e);                        \r
+                       ShowMessage.showError("Unexpected error", e.getClass().getName()+" "+e.getMessage());\r
+               } catch (AccessorConstructionException e) {\r
+            ExceptionUtils.logError(e);                        \r
+                       ShowMessage.showError("Unexpected error", e.getClass().getName()+" "+e.getMessage());\r
+               } catch (AccessorException e) {\r
+            ExceptionUtils.logError(e);                        \r
+                       // TODO Auto-generated catch block\r
+                       e.printStackTrace();\r
+               }\r
+               \r
+       }\r
+\r
+       /**\r
+        * Saves publisher prefs from the UI to preference nodes.\r
+        * \r
+        * @throws ExportException\r
+        */\r
+       void savePublisherPref(String publisherId, Variant options, Preferences contentScopePrefs, Preferences workspaceScopePrefs) throws ExportException {\r
+               try {\r
+//                     Preferences contentScopePrefs = ctx.store.node( "Selection-"+selectionHash );\r
+//                     Preferences workspaceScopePrefs = ctx.store;\r
+\r
+//                     RecordBinding binding = ctx.databoard.getMutableBinding( form.type() );\r
+//                     Object obj = binding.createDefault();\r
+//                     form.readFields(composite, binding, obj);\r
+//                     Variant options = new Variant(binding, obj);\r
+                       \r
+                       Publisher publisher = ctx.eep.getPublisher( publisherId );\r
+                       if ( publisher==null ) return;\r
+                       Variant locationOptions = options.getComponent( new LabelReference(publisher.label()) );\r
+                       publisher.publisherClass().savePref(locationOptions, contentScopePrefs, workspaceScopePrefs);\r
+//             } catch (BindingException e) {\r
+//                     throw new ExportException( e );\r
+               } catch (AccessorConstructionException e) {\r
+            ExceptionUtils.logError(e);                        \r
+                       //throw new ExportException( e );\r
+//             } catch (AccessorException e) {\r
+//                     throw new ExportException( e );\r
+               }\r
+       }\r
+       \r
+       void loadPublisherPref(String publisherId, Variant options, Preferences contentScopePrefs, Preferences workspaceScopePrefs) throws ExportException {\r
+               try {\r
+//                     Preferences contentScopePrefs = ctx.store.node( "Selection-"+selectionHash );\r
+//                     Preferences workspaceScopePrefs = ctx.store;\r
+                       \r
+                       Publisher publisher = ctx.eep.getPublisher( publisherId );\r
+                       if ( publisher == null ) return;\r
+                       \r
+                       // There is a problem here if selection was built with manifest\r
+//                     RecordType optionsType = publisher.publiserClass().locationOptions(ctx, selection);                     \r
+//                     RecordBinding binding = ctx.databoard.getMutableBinding( optionsType );\r
+//                     Object obj = binding.createDefault();\r
+//                     Variant locationOptions = new Variant(binding, obj);\r
+                       Variant locationOptions = options.getComponent( new LabelReference( publisher.label() ) );\r
+                       publisher.publisherClass().fillDefaultPrefs(ctx, selection, options, locationOptions);\r
+                       publisher.publisherClass().loadPref(locationOptions, contentScopePrefs, workspaceScopePrefs);   \r
+//             } catch (BindingException e) {\r
+//                     throw new ExportException( e );\r
+               } catch (AccessorConstructionException e) {\r
+            ExceptionUtils.logError(e);                        \r
+                       //throw new ExportException( e );\r
+//             } catch (AccessorException e) {\r
+//                     throw new ExportException( e );\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Remove any publisher specific controls from output options.\r
+        */\r
+       void cleanPublisherFromGroup(String oldPublisherId) {\r
+               Publisher publisher = ctx.eep.getPublisher(oldPublisherId);\r
+               if ( publisher != null ) {\r
+                       String fieldName = publisher.label();\r
+                       if ( form.type().hasComponent(fieldName)) {\r
+                               form.type().removeComponent(fieldName);\r
+                               ctx.databoard.clear();\r
+                       }\r
+               }\r
+               Combo publishTo = (Combo) form.getControl(composite, P_PUBLISH);\r
+               Composite group = (Composite) publishTo.getParent();\r
+               Control children[] = group.getChildren();\r
+               int index = Arrays.indexOf( children, publishTo );\r
+               for (int i=children.length-1; i>index; i--) {\r
+                       children[i].dispose();\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * Add controls of a publisher id \r
+        * \r
+        * @param publisherId\r
+        * @throws ExportException \r
+        */\r
+       void addPublisherToGroup( String publisherId, List<Content> contents ) throws ExportException {\r
+               try {\r
+                       Composite group = (Composite) form.getControl(composite, P_OUTPUT_OPTIONS);\r
+                       if ( group == null ) return;\r
+                       Publisher publisher = ctx.eep.getPublisher( publisherId );\r
+                       if ( publisher == null ) return;\r
+                       RecordType publisherOptions = publisher.publisherClass().locationOptions(ctx, contents);                \r
+                       //form.type().addComponent(publisher.label(), publisherOptions);\r
+                       RecordType options = new RecordType();\r
+                       options.addComponent(publisher.label(), publisherOptions);\r
+                       form.addFields(group, publisherOptions, publisher.label());\r
+                       form.addListener(group, options, modificationListener);\r
+                       ctx.databoard.clear();\r
+               } catch (BindingException e) {\r
+                       throw new ExportException(e);\r
+               } catch (AccessorConstructionException e) {\r
+                       throw new ExportException(e);\r
+               } catch (AccessorException e) {\r
+                       throw new ExportException(e);\r
+               }\r
+       }\r
+       \r
+       List<Content> getManifestFor(List<Content> selection, Variant options) {                \r
+               try {\r
+                       ExportWizardResult result = new ExportWizardResult();\r
+                       result.options = options;\r
+                       result.accessor = Accessors.getAccessor(result.options);\r
+                       result.contents = selection;\r
+                       result.type = (RecordType) options.type();\r
+                       result.publisherId = "file";\r
+                       \r
+                       List<ExportAction> actions = new ArrayList<ExportAction>();\r
+                       List<Content> manifest = new ArrayList<Content>(); \r
+                       result.createExportActions(ctx, actions, manifest);\r
+                       return manifest;\r
+               } catch (AccessorConstructionException e) {\r
+                       return selection;\r
+               } catch (ExportException e) {\r
+                       e.printStackTrace();\r
+                       return selection;\r
+               }\r
+       }       \r
+       \r
+       public ExportWizardResult getOutput()\r
+       throws ExportException\r
+       {\r
+               ExportWizardResult result = new ExportWizardResult();\r
+               try {\r
+                       ctx.databoard.clear();\r
+                       result.type = form.type();\r
+                   RecordBinding binding = (RecordBinding) ctx.databoard.getMutableBinding( result.type );\r
+                   Object optionsObj = binding.createDefault();\r
+                   result.options = new Variant(binding, optionsObj);\r
+                       form.readFields(composite, binding, optionsObj);\r
+                       result.accessor = Accessors.getAccessor(result.options);\r
+                       result.contents = selection;\r
+                       \r
+                       \r
+                       String publisherLabel = ExporterUtils.getUnionValue(result.accessor, P_PUBLISH);\r
+                       Publisher publisher = ctx.eep.getPublisherByLabel(publisherLabel);\r
+                       result.publisherId = publisher==null?"":publisher.id();                 \r
+                       \r
+               } catch (BindingException e) {\r
+                       throw new ExportException(e);                   \r
+               } catch (AccessorException e) {\r
+                       throw new ExportException(e);\r
+               } catch (AccessorConstructionException e) {\r
+                       throw new ExportException(e);\r
+               }\r
+               \r
+               return result;\r
+       }\r
+               \r
+       void validate() {               \r
+               List<Problem> problems = form.validate(composite);\r
+               List<String> errs = DataboardForm.toStrings(problems);\r
+               \r
+               if ( errs.isEmpty() ) {\r
+                       try {\r
+                               ExportWizardResult result = getOutput();\r
+                               ExportPlan plan = new ExportPlan();\r
+                               result.createPlan(ctx, plan);\r
+                               ExportManager mgr = new ExportManager(result.options, ctx);\r
+                               errs.addAll( mgr.validate(ctx, plan) ) ;\r
+                       } catch (ExportException e) {\r
+                               errs.add(e.getMessage());\r
+                       }\r
+               } else {\r
+                       CollectionUtils.unique( errs );                 \r
+               }\r
+               \r
+\r
+               setErrorMessage( errs.isEmpty() ? null : CollectionUtils.toString(errs, ", ") );\r
+               setPageComplete( errs.isEmpty() );\r
+       }\r
+               \r
+       /**\r
+        * Save wizard preferences\r
+        * \r
+        * @param options\r
+        * @param contentScopePrefs\r
+        * @param workspaceScopePrefs\r
+        * @throws ExportException\r
+        */\r
+       void savePref(Variant options, Preferences contentScopePrefs, Preferences workspaceScopePrefs) throws ExportException\r
+       {\r
+        try {\r
+                       RecordAccessor ra = Accessors.getAccessor(options);\r
+                       String publisherId = ExporterUtils.getUnionValue(ra, P_PUBLISH); \r
+                       Publisher publisher = ctx.eep.getPublisherByLabel(publisherId);\r
+                       if ( publisher!=null ) workspaceScopePrefs.put("publisherId", publisher.id());\r
+                       \r
+                       if ( ra.type().hasComponent(P_OUTPUT_OPTIONS.label) ) {\r
+                               RecordAccessor rao = ra.getComponent( P_OUTPUT_OPTIONS );\r
+                                                               \r
+                               Pattern merge_pattern = Pattern.compile("Merge ([^\\s]*) content into one file");\r
+                               Pattern include_pattern = Pattern.compile("Include attachments to ([^\\s]*)");\r
+                               Pattern export_pattern = Pattern.compile("Export attachments of ([^\\s]*) to separate files");\r
+                               \r
+                               for (int i=0; i<rao.count(); i++) {\r
+                                       String name = rao.type().getComponent(i).name;\r
+                                       Matcher m;\r
+                                       \r
+                                       m = merge_pattern.matcher(name);\r
+                                       if ( m.matches() ) {\r
+                                               String fileExt = m.group(1);\r
+                                               Format format = ctx.eep.getFormatByExt(fileExt);\r
+                                               Boolean value = (Boolean) rao.getFieldValue(i, Bindings.BOOLEAN);\r
+                                               String key = format.id()+"_merge";\r
+                                               contentScopePrefs.putBoolean(key, value);\r
+                                               workspaceScopePrefs.putBoolean(key, value);\r
+                                       }\r
+                                       \r
+                                       m = include_pattern.matcher(name);\r
+                                       if ( m.matches() ) {\r
+                                               String fileExt = m.group(1);\r
+                                               Format format = ctx.eep.getFormatByExt(fileExt);\r
+                                               Boolean value = (Boolean) rao.getFieldValue(i, Bindings.BOOLEAN);\r
+                                               String key = format.id()+"_include_attachments";\r
+                                               contentScopePrefs.putBoolean(key, value);\r
+                                               workspaceScopePrefs.putBoolean(key, value);\r
+                                       }\r
+                                       \r
+                                       m = export_pattern.matcher(name);\r
+                                       if ( m.matches() ) {\r
+                                               String fileExt = m.group(1);\r
+                                               Format format = ctx.eep.getFormatByExt(fileExt);\r
+                                               Boolean value = (Boolean) rao.getFieldValue(i, Bindings.BOOLEAN);\r
+                                               String key = format.id()+"_export_attachments";\r
+                                               contentScopePrefs.putBoolean(key, value);\r
+                                               workspaceScopePrefs.putBoolean(key, value);\r
+                                       }\r
+                               }\r
+                       }\r
+                       \r
+               } catch (AccessorConstructionException e) {\r
+                       throw new ExportException(e);\r
+               } catch (AccessorException e) {\r
+                       throw new ExportException(e);\r
+               }\r
+               \r
+       }\r
+\r
+       /**\r
+        * Load wizard preferences\r
+        * \r
+        * @param binding\r
+        * @param options\r
+        * @param contentScopePrefs\r
+        * @param workspaceScopePrefs\r
+        * @throws ExportException\r
+        */\r
+       void loadPref(Variant options, Preferences contentScopePrefs, Preferences workspaceScopePrefs) throws ExportException\r
+       {\r
+               //Preferences workspaceScopePrefs = ctx.store;\r
+        try {\r
+                       RecordAccessor ra = Accessors.getAccessor(options);             \r
+                       String publisherId = workspaceScopePrefs.get("publisherId", "");\r
+                       Publisher publisher = ctx.eep.getPublisher(publisherId);\r
+                       if ( publisher != null ) ExporterUtils.setUnionValue(ra, P_PUBLISH, publisher.label());\r
+                       \r
+                       if ( ra.type().hasComponent(P_OUTPUT_OPTIONS.label) ) {\r
+                               RecordAccessor rao = ra.getComponent( P_OUTPUT_OPTIONS );\r
+                                                               \r
+                               for (Format format : ctx.eep.formats()) {\r
+                                       if ( format.isGroupFormat() ) {\r
+                                               String key = format.id()+"_merge";\r
+                                               Boolean value = null;\r
+                                               if ( containsKey(contentScopePrefs, key) ) {\r
+                                                       value = contentScopePrefs.getBoolean(key, false); \r
+                                               } else if ( containsKey(workspaceScopePrefs, key) ) {\r
+                                                       value = workspaceScopePrefs.getBoolean(key, false);\r
+                                               }\r
+                                                                                       \r
+                                               if ( value != null ) {\r
+                                                       String key2 = "Merge "+format.fileext()+" content into one file";\r
+                                                       int index = rao.type().getComponentIndex2(key2);\r
+                                                       if ( index>=0 ) {\r
+                                                               rao.setFieldValue(index, Bindings.BOOLEAN, value);\r
+                                                       }\r
+                                               }                                       \r
+                                       }\r
+                                       \r
+                                       if ( format.isContainerFormat() ) {\r
+                                               String key = format.id()+"_include_attachments";\r
+                                               Boolean value = null;\r
+                                               if ( containsKey(contentScopePrefs, key) ) {\r
+                                                       value = contentScopePrefs.getBoolean(key, false); \r
+                                               } else if ( containsKey(workspaceScopePrefs, key) ) {\r
+                                                       value = workspaceScopePrefs.getBoolean(key, false);\r
+                                               }\r
+                                                                                       \r
+                                               if ( value != null ) {\r
+                                                       String key2 = "Include attachments to "+format.fileext();\r
+                                                       int index = rao.type().getComponentIndex2(key2);\r
+                                                       if ( index>=0 ) {\r
+                                                               rao.setFieldValue(index, Bindings.BOOLEAN, value);\r
+                                                       }\r
+                                               }                                       \r
+                                       }\r
+                                       \r
+                                       if ( format.isContainerFormat() ) {\r
+                                               String key = format.id()+"_export_attachments";\r
+                                               Boolean value = null;\r
+                                               if ( containsKey(contentScopePrefs, key) ) {\r
+                                                       value = contentScopePrefs.getBoolean(key, false); \r
+                                               } else if ( containsKey(workspaceScopePrefs, key) ) {\r
+                                                       value = workspaceScopePrefs.getBoolean(key, false);\r
+                                               }\r
+                                                                                       \r
+                                               if ( value != null ) {\r
+                                                       String key2 = "Export attachments of "+format.fileext()+" to separate files";\r
+                                                       int index = rao.type().getComponentIndex2(key2);\r
+                                                       if ( index>=0 ) {\r
+                                                               rao.setFieldValue(index, Bindings.BOOLEAN, value);\r
+                                                       }\r
+                                               }                                       \r
+                                       }\r
+                                       \r
+                               }\r
+                       }\r
+                                       \r
+               } catch (AccessorConstructionException e) {\r
+                       throw new ExportException(e);\r
+               } catch (AccessorException e) {\r
+                       throw new ExportException(e);\r
+               }               \r
+       }\r
+       \r
+       /**\r
+        * Save selections from previous form\r
+        */\r
+       public void savePrefs() throws ExportException {\r
+               try {\r
+                       int oldSelectionHash = selectionHash;                   \r
+                       Preferences contentScopePrefs = ctx.store.node( "Selection-"+oldSelectionHash );\r
+                       Preferences workspaceScopePrefs = ctx.store;\r
+                                       \r
+                       RecordBinding binding = ctx.databoard.getMutableBinding( form.type() );\r
+                       Object obj = binding.createDefault();\r
+                       Variant options = new Variant(binding, obj);\r
+                       form.readFields(composite, binding, obj);\r
+                                               \r
+                       Combo publishTo = (Combo) form.getControl(composite, P_PUBLISH);                \r
+                       String publisherLabel = publishTo==null?null:publishTo.getText();\r
+                       if ( publisherLabel != null ) {\r
+                               Publisher publisher = ctx.eep.getPublisherByLabel( publisherLabel );\r
+                               if ( publisher!=null ) {\r
+                                       try {\r
+                                               ChildReference locationOptionsRef = new LabelReference(publisher.label());\r
+                                               Variant locationOptions = options.getComponent( locationOptionsRef );\r
+                                               publisher.publisherClass().savePref(locationOptions, contentScopePrefs, workspaceScopePrefs);\r
+                                       } catch ( AccessorConstructionException e ) {} \r
+                               }\r
+                       }\r
+                       \r
+                       for (Exporter exporter : ctx.eep.exporters()) {\r
+                               exporter.exportAction().savePref(options, contentScopePrefs, workspaceScopePrefs);\r
+                       }\r
+                                       \r
+                       for (Format format : ctx.eep.formats()) {\r
+                               format.formatActions().savePref(options, contentScopePrefs, workspaceScopePrefs);\r
+                       }                               \r
+                                       \r
+                       savePref(options, contentScopePrefs, workspaceScopePrefs);\r
+               } catch (BindingException e) {\r
+                       throw new ExportException( e ); \r
+               } catch (AccessorConstructionException e) {\r
+                       throw new ExportException( e ); \r
+               } catch (AccessorException e) {\r
+                       throw new ExportException( e ); \r
+               } catch (ExportException e) {\r
+                       throw new ExportException( e ); \r
+               }\r
+       }\r
+\r
+       public void fillDefaultPrefs(Variant options) throws ExportException {\r
+        try {\r
+                       RecordAccessor ra = Accessors.getAccessor(options);\r
+\r
+                       if ( ra.type().hasComponent(P_OUTPUT_OPTIONS.label) ) {\r
+                               RecordAccessor rao = ra.getComponent( P_OUTPUT_OPTIONS );\r
+                               \r
+                               for (Format format : ctx.eep.formats()) {\r
+                                       if ( format.isContainerFormat() ) {\r
+                                               String key = "Include attachments to "+format.fileext();\r
+                                               int index = rao.type().getComponentIndex2(key);\r
+                                               if ( index>=0 ) rao.setFieldValue(index, Bindings.BOOLEAN, true);\r
+                                               \r
+                                               key = "Export attachments of "+format.fileext()+" to separate files";;\r
+                                               index = rao.type().getComponentIndex2(key);\r
+                                               if ( index>=0 ) rao.setFieldValue(index, Bindings.BOOLEAN, true);\r
+                                       }                       \r
+                               }\r
+                               \r
+                       }\r
+               \r
+               } catch (AccessorConstructionException e) {\r
+                       throw new ExportException(e);\r
+               } catch (AccessorException e) {\r
+                       throw new ExportException(e);\r
+               }\r
+       }       \r
+       \r
+       static boolean containsKey(Preferences pref, String key) {\r
+               try {\r
+                       for (String x : pref.keys()) if ( x.equals(key) ) return true;\r
+               } catch (BackingStoreException e) {\r
+                       e.printStackTrace();\r
+               } \r
+               return false;           \r
+       }\r
+               \r
+}\r