--- /dev/null
+package org.simantics.export.core.manager;\r
+\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.Collections;\r
+import java.util.Comparator;\r
+import java.util.List;\r
+\r
+import org.eclipse.core.runtime.IProgressMonitor;\r
+import org.eclipse.core.runtime.OperationCanceledException;\r
+import org.eclipse.core.runtime.SubMonitor;\r
+import org.simantics.Simantics;\r
+import org.simantics.databoard.binding.mutable.Variant;\r
+import org.simantics.databoard.util.URIUtil;\r
+import org.simantics.export.core.ExportContext;\r
+import org.simantics.export.core.error.ExportException;\r
+import org.simantics.export.core.intf.Exporter;\r
+import org.simantics.export.core.intf.Format;\r
+import org.simantics.export.core.util.ExporterUtils;\r
+import org.simantics.utils.datastructures.MapList;\r
+import org.simantics.utils.strings.AlphanumComparator;\r
+\r
+/**\r
+ * This action exports similar content types into one file of group format (e.g. Pdf, CSV).\r
+ * Group format is a file that can carry multiple items of one content type. \r
+ *\r
+ * @author toni.kalajainen@semantum.fi\r
+ */\r
+public class ExportGroupCreateAction extends ExportAction {\r
+\r
+ // Contents and attachments\r
+ public Content dstContent;\r
+ public List<Content> contents = new ArrayList<Content>();\r
+ // All attachments. Note, there are also attachments for the key: null\r
+ public MapList<Content, Content> attachments = new MapList<Content, Content>();\r
+ public String formatId;\r
+ public File outputFile;\r
+\r
+ public ExportGroupCreateAction(Content dstContent, String formatId) \r
+ {\r
+ this.dstContent = dstContent;\r
+ this.formatId = formatId;\r
+ }\r
+\r
+ public void addContent(Content srcContent, List<Content> attachments) {\r
+ if ( srcContent != null ) contents.add(srcContent);\r
+ if (attachments!=null) this.attachments.addAll(srcContent, attachments);\r
+ }\r
+\r
+ public void execute(ExportContext ctx, IProgressMonitor monitor, Variant options) throws ExportException\r
+ {\r
+ Format format = ctx.eep.getFormat( formatId );\r
+\r
+ // Sort content to exporter, and create the exporters\r
+ MapList<List<Exporter>, Content> map = new MapList<List<Exporter>, Content>();\r
+ for ( Content content : contents ) {\r
+ Exporter[] exporters = ctx.eep.getExporters(content.formatId, content.contentTypeId);\r
+ if ( exporters.length == 0 ) {\r
+ throw new ExportException("No suitable exporter found for exporting "+content.contentTypeId+" to a "+format.label());\r
+ }\r
+ map.add(Arrays.asList(exporters), content);\r
+ }\r
+\r
+ try {\r
+ // Prefix must be at least 3 characters in length\r
+ String prefix = "___" + URIUtil.encodeFilename(dstContent.label);\r
+ dstContent.tmpFile = outputFile = File.createTempFile( prefix, URIUtil.encodeFilename(format.fileext()), Simantics.getTemporaryDirectory("export.core") );\r
+ } catch (IOException e) {\r
+ throw new ExportException(e);\r
+ }\r
+\r
+ // Calculate amount of total work from the amount of exporters and contents.\r
+ int totalWork = 0;\r
+ final int singleExportWork = 10000;\r
+ for (List<Exporter> exporters : map.getKeys()) {\r
+ List<Content> contents = map.getValuesUnsafe( exporters );\r
+ totalWork += (contents.size() * exporters.size() + 1) * singleExportWork;\r
+ }\r
+\r
+ SubMonitor mon = SubMonitor.convert(monitor, totalWork);\r
+\r
+ Object writer = format.createFile(ctx, outputFile, options);\r
+ try {\r
+ // Sort exporters based on content type id to give a deterministic order\r
+ // for List<Exporter> -> List<Content> iteration.\r
+ List<List<Exporter>> sortedKeys = new ArrayList<List<Exporter>>( map.getKeys() );\r
+ Collections.sort(sortedKeys, EXPORTER_LIST_COMPARATOR);\r
+ //System.out.println("sorted keys:\n" + EString.implode(sortedKeys));\r
+\r
+ // Write pages, exporter at a time.\r
+ for ( List<Exporter> exporters : sortedKeys ) {\r
+ //System.out.println("exporters:\n" + EString.implode(exporters));\r
+ List<Content> contents = map.getValues( exporters );\r
+ List<Content> sortedContent = ExporterUtils.sortedContent( contents );\r
+ for (Content content : sortedContent) {\r
+ //System.out.println("content: " + content);\r
+ for ( Exporter exporter : exporters ) {\r
+ if (monitor.isCanceled())\r
+ throw new OperationCanceledException();\r
+\r
+ //System.out.println("exporter: " + exporter);\r
+ mon.subTask( exporter.formatId() + ": " + content.label);\r
+ exporter.exportAction().export(\r
+ Collections.singletonList(content),\r
+ writer, \r
+ ctx, \r
+ options,\r
+ mon.newChild(singleExportWork),\r
+ attachments);\r
+ }\r
+ }\r
+ }\r
+\r
+ // Write attachments\r
+ List<Content> remainingAttachments = attachments.getValues(null);\r
+ if ( remainingAttachments!=null && !remainingAttachments.isEmpty() ) {\r
+ format.addAttachment(ctx, writer, remainingAttachments);\r
+ }\r
+\r
+ // Remember the output file (This is for the publisher)\r
+ dstContent.tmpFile = outputFile;\r
+\r
+ } catch (ExportException ee) {\r
+ if ( outputFile.exists() ) outputFile.delete();\r
+ throw ee;\r
+ } finally {\r
+ if (writer!=null) {\r
+ // TODO: pass progress monitor to closeFile since closeFile may do a lot of work in some cases\r
+ format.closeFile( ctx, writer );\r
+ mon.worked(singleExportWork);\r
+ writer = null;\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Required for sorting the PDF output into a deterministic order.\r
+ */\r
+ Comparator<List<Exporter>> EXPORTER_LIST_COMPARATOR = new Comparator<List<Exporter>>() {\r
+ String repr(List<Exporter> l) {\r
+ for (Exporter e : l)\r
+ return e.contentTypeId();\r
+ throw new IllegalArgumentException("empty exporter list");\r
+ }\r
+ @Override\r
+ public int compare(List<Exporter> o1, List<Exporter> o2) {\r
+ String k1 = repr(o1);\r
+ String k2 = repr(o2);\r
+ return AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(k1, k2);\r
+ }\r
+ };\r
+\r
+ @Override\r
+ public String label(ExportContext ctx) {\r
+ String label;\r
+ Format format = ctx.eep.getFormat( formatId );\r
+ if ( contents.size()==1 ) {\r
+ label = contents.iterator().next().label;\r
+ } else {\r
+ label = "Writing "+contents.size()+" items to a "+format.label()+" file";\r
+ }\r
+ if ( outputFile == null ) return label;\r
+ return label + " export to "+outputFile.getName();\r
+ }\r
+\r
+ @Override\r
+ public int work(ExportContext ctx) {\r
+ return contents.size() + 1;\r
+ }\r
+\r
+ @Override\r
+ public List<String> validate(ExportContext ctx, Variant options) {\r
+ List<String> result = new ArrayList<String>(0);\r
+\r
+ for (Content content : contents) {\r
+\r
+ Exporter[] exporters = ctx.eep.getExporters(formatId, content.contentTypeId);\r
+ if ( exporters.length == 0 ) {\r
+ result.add("Could not find exporter for "+content.filename);\r
+ continue;\r
+ }\r
+\r
+ for ( Exporter exporter : exporters) {\r
+ try {\r
+ result.addAll( exporter.exportAction().validate(content.url, ctx, options) );\r
+ } catch (ExportException e) {\r
+ result.add( e.getClass().getName()+": "+e.getMessage() );\r
+ }\r
+ }\r
+ }\r
+\r
+ return result;\r
+ }\r
+\r
+ @Override\r
+ public void cleanup(ExportContext ctx, IProgressMonitor progress, Variant options) throws ExportException {\r
+ if ( this.outputFile != null ) { \r
+ this.outputFile.delete();\r
+ dstContent.tmpFile = null;\r
+ }\r
+ }\r
+\r
+ public List<Content> getAttachments() {\r
+ List<Content> result = new ArrayList<Content>();\r
+ for ( Content key : attachments.getKeys() ) {\r
+ for ( Content value : attachments.getValuesUnsafe(key) ) {\r
+ if ( !result.contains(value) ) result.add(value);\r
+ }\r
+ } \r
+ return result;\r
+ }\r
+\r
+}\r