001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.io.session; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.CardLayout; 007import java.awt.Font; 008import java.awt.GridBagLayout; 009import java.awt.Insets; 010import java.awt.event.ActionEvent; 011import java.awt.event.ActionListener; 012import java.awt.event.ItemEvent; 013import java.awt.event.ItemListener; 014import java.io.File; 015import java.io.IOException; 016import java.io.OutputStream; 017import java.io.OutputStreamWriter; 018import java.io.PrintWriter; 019import java.io.Writer; 020import java.net.MalformedURLException; 021import java.net.URI; 022import java.net.URL; 023import java.nio.charset.StandardCharsets; 024import java.util.Collection; 025import java.util.Collections; 026 027import javax.swing.AbstractAction; 028import javax.swing.ButtonGroup; 029import javax.swing.JButton; 030import javax.swing.JCheckBox; 031import javax.swing.JLabel; 032import javax.swing.JPanel; 033import javax.swing.JRadioButton; 034import javax.swing.SwingConstants; 035 036import org.openstreetmap.josm.actions.SaveAction; 037import org.openstreetmap.josm.gui.layer.Layer; 038import org.openstreetmap.josm.gui.layer.OsmDataLayer; 039import org.openstreetmap.josm.gui.util.GuiHelper; 040import org.openstreetmap.josm.gui.widgets.JosmTextField; 041import org.openstreetmap.josm.io.OsmWriter; 042import org.openstreetmap.josm.io.OsmWriterFactory; 043import org.openstreetmap.josm.io.session.SessionWriter.ExportSupport; 044import org.openstreetmap.josm.tools.GBC; 045import org.openstreetmap.josm.tools.ImageProvider; 046import org.w3c.dom.Element; 047 048public class OsmDataSessionExporter implements SessionLayerExporter { 049 050 private OsmDataLayer layer; 051 private JRadioButton link, include; 052 private JCheckBox export; 053 054 public OsmDataSessionExporter(OsmDataLayer layer) { 055 this.layer = layer; 056 } 057 058 @Override 059 public Collection<Layer> getDependencies() { 060 return Collections.emptySet(); 061 } 062 063 private class LayerSaveAction extends AbstractAction { 064 public LayerSaveAction() { 065 putValue(SMALL_ICON, new ImageProvider("save").setWidth(16).get()); 066 putValue(SHORT_DESCRIPTION, layer.requiresSaveToFile() ? 067 tr("Layer contains unsaved data - save to file.") : 068 tr("Layer does not contain unsaved data.")); 069 updateEnabledState(); 070 } 071 072 @Override 073 public void actionPerformed(ActionEvent e) { 074 SaveAction.getInstance().doSave(layer); 075 updateEnabledState(); 076 } 077 078 public final void updateEnabledState() { 079 setEnabled(layer.requiresSaveToFile()); 080 } 081 } 082 083 @Override 084 public JPanel getExportPanel() { 085 final JPanel p = new JPanel(new GridBagLayout()); 086 JPanel topRow = new JPanel(new GridBagLayout()); 087 export = new JCheckBox(); 088 export.setSelected(true); 089 final JLabel lbl = new JLabel(layer.getName(), layer.getIcon(), SwingConstants.LEFT); 090 lbl.setToolTipText(layer.getToolTipText()); 091 092 JLabel lblData = new JLabel(tr("Data:")); 093 /* I18n: Refer to a OSM data file in session file */ link = new JRadioButton(tr("local file")); 094 link.putClientProperty("actionname", "link"); 095 link.setToolTipText(tr("Link to a OSM data file on your local disk.")); 096 /* I18n: Include OSM data in session file */ include = new JRadioButton(tr("include")); 097 include.setToolTipText(tr("Include OSM data in the .joz session file.")); 098 include.putClientProperty("actionname", "include"); 099 ButtonGroup group = new ButtonGroup(); 100 group.add(link); 101 group.add(include); 102 103 JPanel cardLink = new JPanel(new GridBagLayout()); 104 final File file = layer.getAssociatedFile(); 105 final LayerSaveAction saveAction = new LayerSaveAction(); 106 final JButton save = new JButton(saveAction); 107 if (file != null) { 108 JosmTextField tf = new JosmTextField(); 109 tf.setText(file.getPath()); 110 tf.setEditable(false); 111 cardLink.add(tf, GBC.std()); 112 save.setMargin(new Insets(0,0,0,0)); 113 cardLink.add(save, GBC.eol().insets(2,0,0,0)); 114 } else { 115 cardLink.add(new JLabel(tr("No file association")), GBC.eol()); 116 } 117 118 JPanel cardInclude = new JPanel(new GridBagLayout()); 119 JLabel lblIncl = new JLabel(tr("OSM data will be included in the session file.")); 120 lblIncl.setFont(lblIncl.getFont().deriveFont(Font.PLAIN)); 121 cardInclude.add(lblIncl, GBC.eol().fill(GBC.HORIZONTAL)); 122 123 final CardLayout cl = new CardLayout(); 124 final JPanel cards = new JPanel(cl); 125 cards.add(cardLink, "link"); 126 cards.add(cardInclude, "include"); 127 128 if (file != null) { 129 link.setSelected(true); 130 } else { 131 link.setEnabled(false); 132 link.setToolTipText(tr("No file association")); 133 include.setSelected(true); 134 cl.show(cards, "include"); 135 } 136 137 link.addActionListener(new ActionListener() { 138 @Override 139 public void actionPerformed(ActionEvent e) { 140 cl.show(cards, "link"); 141 } 142 }); 143 include.addActionListener(new ActionListener() { 144 @Override 145 public void actionPerformed(ActionEvent e) { 146 cl.show(cards, "include"); 147 } 148 }); 149 150 topRow.add(export, GBC.std()); 151 topRow.add(lbl, GBC.std()); 152 topRow.add(GBC.glue(1,0), GBC.std().fill(GBC.HORIZONTAL)); 153 p.add(topRow, GBC.eol().fill(GBC.HORIZONTAL)); 154 p.add(lblData, GBC.std().insets(10,0,0,0)); 155 p.add(link, GBC.std()); 156 p.add(include, GBC.eol()); 157 p.add(cards, GBC.eol().insets(15,0,3,3)); 158 159 export.addItemListener(new ItemListener() { 160 @Override 161 public void itemStateChanged(ItemEvent e) { 162 if (e.getStateChange() == ItemEvent.DESELECTED) { 163 GuiHelper.setEnabledRec(p, false); 164 export.setEnabled(true); 165 } else { 166 GuiHelper.setEnabledRec(p, true); 167 save.setEnabled(saveAction.isEnabled()); 168 link.setEnabled(file != null); 169 } 170 } 171 }); 172 return p; 173 } 174 175 @Override 176 public boolean shallExport() { 177 return export.isSelected(); 178 } 179 180 @Override 181 public boolean requiresZip() { 182 return include.isSelected(); 183 } 184 185 @Override 186 public Element export(ExportSupport support) throws IOException { 187 Element layerEl = support.createElement("layer"); 188 layerEl.setAttribute("type", "osm-data"); 189 layerEl.setAttribute("version", "0.1"); 190 191 Element file = support.createElement("file"); 192 layerEl.appendChild(file); 193 194 if (requiresZip()) { 195 String zipPath = "layers/" + String.format("%02d", support.getLayerIndex()) + "/data.osm"; 196 file.appendChild(support.createTextNode(zipPath)); 197 addDataFile(support.getOutputStreamZip(zipPath)); 198 } else { 199 URI uri = layer.getAssociatedFile().toURI(); 200 URL url = null; 201 try { 202 url = uri.toURL(); 203 } catch (MalformedURLException e) { 204 throw new IOException(e); 205 } 206 file.appendChild(support.createTextNode(url.toString())); 207 } 208 return layerEl; 209 } 210 211 protected void addDataFile(OutputStream out) { 212 Writer writer = new OutputStreamWriter(out, StandardCharsets.UTF_8); 213 OsmWriter w = OsmWriterFactory.createOsmWriter(new PrintWriter(writer), false, layer.data.getVersion()); 214 layer.data.getReadLock().lock(); 215 try { 216 w.writeLayer(layer); 217 w.flush(); 218 } finally { 219 layer.data.getReadLock().unlock(); 220 } 221 } 222}