1 | package org.greenstone.gatherer.gui;
|
---|
2 |
|
---|
3 | import org.greenstone.gatherer.Configuration;
|
---|
4 | import org.greenstone.gatherer.Dictionary;
|
---|
5 | import org.greenstone.gatherer.Gatherer;
|
---|
6 | //import static org.greenstone.gatherer.cdm.Format4gs3Manager.NumberedJTextArea;
|
---|
7 | import org.greenstone.gatherer.cdm.CollectionConfigXMLReadWrite;
|
---|
8 | import org.greenstone.gatherer.util.Codec;
|
---|
9 | import org.greenstone.gatherer.util.StaticStrings;
|
---|
10 | import org.greenstone.gatherer.util.Utility;
|
---|
11 | import org.greenstone.gatherer.util.XMLTools;
|
---|
12 |
|
---|
13 | import org.fife.ui.rsyntaxtextarea.*;
|
---|
14 | import org.w3c.dom.*;
|
---|
15 |
|
---|
16 | import java.io.BufferedReader;
|
---|
17 | import java.io.BufferedWriter;
|
---|
18 | import java.io.Closeable;
|
---|
19 | import java.io.File;
|
---|
20 | import java.io.InputStream;
|
---|
21 | import java.io.InputStreamReader;
|
---|
22 | import java.io.IOException;
|
---|
23 | import java.io.OutputStream;
|
---|
24 | import java.io.OutputStreamWriter;
|
---|
25 |
|
---|
26 | //import java.awt.Dimension;
|
---|
27 | import java.awt.*;
|
---|
28 | import java.awt.event.*;
|
---|
29 | import java.util.*;
|
---|
30 | import javax.swing.*;
|
---|
31 | import javax.swing.border.*;
|
---|
32 | import javax.swing.event.*;
|
---|
33 | import javax.swing.undo.*;
|
---|
34 |
|
---|
35 |
|
---|
36 | // TO DO:
|
---|
37 | // + StreamGobblers: http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html
|
---|
38 | // Need to test on Windows: the Ctrl-D/Ctrl-Z sent to formatconverter.exe. May not need to do anything special for Windows now that this code is using StreamGobblers to process the in/out streams of the process
|
---|
39 | // Need to use dictionary for labels
|
---|
40 | // convertFormat() for remote GS
|
---|
41 | // + HTML tidy (put all "<br/>" back to "<br>" in luce/etc/collectionConfig.xml and run again)
|
---|
42 | // Help tooltips on buttons
|
---|
43 | // + Undo, Redo buttons
|
---|
44 | // X Split class into dialog/widgets and data processing?
|
---|
45 | // http://www.java2s.com/Tutorial/Java/0240__Swing/SettingJOptionPanebuttonlabelstoFrench.htm
|
---|
46 |
|
---|
47 | public class FormatConversionDialog extends ModalDialog
|
---|
48 | {
|
---|
49 | public static final String GSF_FORMAT_GS2_TAG = "gsf:format-gs2";
|
---|
50 | private static final String GSF_GS3_ROOT_TAG = "gsf:gs3-root";
|
---|
51 |
|
---|
52 | // The cmdline programs launched by this dialog
|
---|
53 | private static final int XMLTIDY = 0;
|
---|
54 | private static final int FORMATCONVERTER = 1;
|
---|
55 | // Online HTML tidy for learning usage: http://infohound.net/tidy/
|
---|
56 | private static final String[] xmltidy_cmd_args = {"tidy", "-config", Configuration.getGS3BinPath() + "tidyconfig.cfg", "-utf8", "-wrap", "0", "-raw", "-q"}; // {"tidy", "-xml", "-utf8"};
|
---|
57 | private static final String[] formatconverter_cmd_args = {Configuration.getGS3BinPath() + "formatconverter", "--silent"};
|
---|
58 |
|
---|
59 | private static final Dimension SIZE = new Dimension(640,480);
|
---|
60 |
|
---|
61 | // the important member variables
|
---|
62 | private File collect_cfg_file = null;
|
---|
63 | private Document xml_file_doc = null;
|
---|
64 | private NodeList gsf_format_gs2_list = null;
|
---|
65 |
|
---|
66 | private int current_index = -1;
|
---|
67 | private int dlgResult = OpenCollectionDialog.OK_OPTION;
|
---|
68 | private int process_exitValue = -1;
|
---|
69 |
|
---|
70 | // widgets
|
---|
71 | final String WARNING_TITLE = Dictionary.get("General.Warning")
|
---|
72 | + ": " + Dictionary.get("FormatConversionDialog.Invalid_XML")
|
---|
73 | + " " + Dictionary.get("FormatConversionDialog.Invalid_XML_Warning_Title");
|
---|
74 |
|
---|
75 | private static final Border EMPTYBORDER = new EmptyBorder(5, 5, 5, 5);
|
---|
76 | private JLabel section_label = null;
|
---|
77 | private GLIButton cancel_button = null;
|
---|
78 | private GLIButton next_button = null;
|
---|
79 | private GLIButton accept_all_button = null;
|
---|
80 | private GLIButton undo_button = null;
|
---|
81 | private GLIButton redo_button = null;
|
---|
82 | private GLIButton htmltidy_button = null;
|
---|
83 | private GLIButton xmltidy_button = null;
|
---|
84 | private NumberedJTextArea gs2_textarea = null;
|
---|
85 | private NumberedJTextArea gs3_textarea = null;
|
---|
86 | private JLabel count_label = null;
|
---|
87 | private JLabel statusbar = null;
|
---|
88 | private UndoManager undoManager;
|
---|
89 |
|
---|
90 | public FormatConversionDialog (File collect_cfg_file, Document xml_file_doc, NodeList gsf_format_gs2_list) {
|
---|
91 | super(Gatherer.g_man, "", true);
|
---|
92 |
|
---|
93 | this.collect_cfg_file = collect_cfg_file;
|
---|
94 | this.xml_file_doc = xml_file_doc;
|
---|
95 | this.gsf_format_gs2_list = gsf_format_gs2_list;
|
---|
96 |
|
---|
97 | this.setComponentOrientation(Dictionary.getOrientation());
|
---|
98 | setSize(SIZE);
|
---|
99 | setTitle(Dictionary.get(Dictionary.get("FormatConversionDialog.Title")));
|
---|
100 | this.addWindowListener(new WindowClosingListener()); // the dialog's top right close button should behave as a Cancel press
|
---|
101 | setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
---|
102 |
|
---|
103 |
|
---|
104 | JPanel midbutton_panel = new JPanel(); // FlowLayout by default in a JPanel
|
---|
105 | midbutton_panel.setComponentOrientation(Dictionary.getOrientation());
|
---|
106 | JButton reconvert_button = new GLIButton(Dictionary.get("FormatConversionDialog.Reconvert"), Dictionary.get("FormatConversionDialog.Reconvert_Tooltip"));
|
---|
107 | midbutton_panel.add(reconvert_button);
|
---|
108 | reconvert_button.addActionListener(new ReconvertListener());
|
---|
109 | reconvert_button.setAlignmentY(Component.CENTER_ALIGNMENT);
|
---|
110 |
|
---|
111 | section_label = new JLabel("Section Label Goes Here"); // title will be replaced, don't need to translate
|
---|
112 | section_label.setBorder(EMPTYBORDER);
|
---|
113 | section_label.setComponentOrientation(Dictionary.getOrientation());
|
---|
114 |
|
---|
115 | JPanel button1_panel = new JPanel();
|
---|
116 | button1_panel.setComponentOrientation(Dictionary.getOrientation());
|
---|
117 | undo_button = new GLIButton(Dictionary.get("General.Undo"), Dictionary.get("General.Undo_Tooltip"));
|
---|
118 | undo_button.addActionListener(new UndoListener());
|
---|
119 | undo_button.setEnabled(false);
|
---|
120 |
|
---|
121 | redo_button = new GLIButton(Dictionary.get("General.Redo"), Dictionary.get("General.Redo_Tooltip"));
|
---|
122 | redo_button.addActionListener(new RedoListener());
|
---|
123 | redo_button.setEnabled(false);
|
---|
124 |
|
---|
125 | xmltidy_button = new GLIButton(Dictionary.get("FormatConversionDialog.XHTML_Tidy"), Dictionary.get("FormatConversionDialog.XHTML_Tidy_Tooltip"));
|
---|
126 | xmltidy_button.addActionListener(new XMLTidyButtonListener());
|
---|
127 |
|
---|
128 | button1_panel.add(undo_button);
|
---|
129 | button1_panel.add(redo_button);
|
---|
130 | button1_panel.add(xmltidy_button);
|
---|
131 |
|
---|
132 | JPanel button_panel = new JPanel(new FlowLayout());
|
---|
133 | button_panel.setComponentOrientation(Dictionary.getOrientation());
|
---|
134 | count_label = new JLabel("<count>"); // placeholder text for numbers, no need to translate
|
---|
135 | cancel_button = new GLIButton(Dictionary.get("General.Cancel"), Dictionary.get("FormatConversionDialog.Cancel_Tooltip"));
|
---|
136 | next_button = new GLIButton(Dictionary.get("FormatConversionDialog.Next"), Dictionary.get("FormatConversionDialog.Next_Tooltip"));
|
---|
137 | accept_all_button = new GLIButton(Dictionary.get("FormatConversionDialog.Accept_All"), Dictionary.get("FormatConversionDialog.Accept_All_Tooltip"));
|
---|
138 | cancel_button.addActionListener(new CancelButtonListener());
|
---|
139 | next_button.addActionListener(new NextButtonListener());
|
---|
140 | accept_all_button.addActionListener(new AcceptAllButtonListener());
|
---|
141 | button_panel.add(cancel_button);
|
---|
142 | button_panel.add(next_button);
|
---|
143 | button_panel.add(accept_all_button);
|
---|
144 | button_panel.add(count_label);
|
---|
145 |
|
---|
146 |
|
---|
147 | // The undoable text areas. Adding the UndoableEditListener has to come after instantiation
|
---|
148 | // of the undo and redo buttons, since the listener expects these buttons to already exist
|
---|
149 | undoManager = new UndoManager();
|
---|
150 |
|
---|
151 | gs2_textarea = new NumberedJTextArea();
|
---|
152 | gs3_textarea = new NumberedJTextArea();
|
---|
153 |
|
---|
154 | CustomUndoableEditListener customUndoableEditListener = new CustomUndoableEditListener();
|
---|
155 | gs2_textarea.getDocument().addUndoableEditListener(customUndoableEditListener);
|
---|
156 | gs3_textarea.getDocument().addUndoableEditListener(customUndoableEditListener);
|
---|
157 |
|
---|
158 | initTextArea(gs2_textarea);
|
---|
159 | initTextArea(gs3_textarea);
|
---|
160 | gs2_textarea.setToolTipText(Dictionary.get("FormatConversionDialog.GS2_Text_Tooltip"));
|
---|
161 | gs3_textarea.setToolTipText(Dictionary.get("FormatConversionDialog.GS3_Text_Tooltip"));
|
---|
162 |
|
---|
163 |
|
---|
164 | JPanel centre_panel = new JPanel();
|
---|
165 | centre_panel.setLayout(new BoxLayout(centre_panel, BoxLayout.Y_AXIS));
|
---|
166 | centre_panel.setComponentOrientation(Dictionary.getOrientation());
|
---|
167 | centre_panel.setBorder(EMPTYBORDER);
|
---|
168 | centre_panel.add(new JScrollPane(gs2_textarea));
|
---|
169 | centre_panel.add(midbutton_panel);
|
---|
170 | centre_panel.add(new JScrollPane(gs3_textarea));
|
---|
171 |
|
---|
172 | JPanel bottom_panel = new JPanel();
|
---|
173 | bottom_panel.setLayout(new BoxLayout(bottom_panel, BoxLayout.Y_AXIS));
|
---|
174 | bottom_panel.setComponentOrientation(Dictionary.getOrientation());
|
---|
175 | bottom_panel.setBorder(EMPTYBORDER);
|
---|
176 |
|
---|
177 | bottom_panel.add(button1_panel);
|
---|
178 | bottom_panel.add(button_panel);
|
---|
179 | statusbar = new JLabel("");
|
---|
180 | statusbar.setBorder(EMPTYBORDER);
|
---|
181 | // http://stackoverflow.com/questions/2560784/how-to-center-elements-in-the-boxlayout-using-center-of-the-element
|
---|
182 | statusbar.setAlignmentX(Component.CENTER_ALIGNMENT);
|
---|
183 | bottom_panel.add(statusbar);
|
---|
184 |
|
---|
185 | // add all the widgets to the contentpane
|
---|
186 | JPanel content_pane = (JPanel) getContentPane();
|
---|
187 | content_pane.setComponentOrientation(Dictionary.getOrientation());
|
---|
188 | content_pane.setLayout(new BorderLayout());
|
---|
189 | //content_pane.setLayout(new GridLayout(7,1));
|
---|
190 | //content_pane.setLayout(new BoxLayout(content_pane, BoxLayout.Y_AXIS));
|
---|
191 | //content_pane.setBorder(EMPTYBORDER);
|
---|
192 |
|
---|
193 | content_pane.add(section_label, BorderLayout.NORTH);
|
---|
194 | content_pane.add(centre_panel, BorderLayout.CENTER);
|
---|
195 | content_pane.add(bottom_panel, BorderLayout.SOUTH);
|
---|
196 |
|
---|
197 |
|
---|
198 | // Final dialog setup & positioning.
|
---|
199 | getRootPane().setDefaultButton(next_button);
|
---|
200 | Dimension screen_size = Configuration.screen_size;
|
---|
201 | setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
|
---|
202 | screen_size = null;
|
---|
203 | //setVisible(true);
|
---|
204 | }
|
---|
205 |
|
---|
206 |
|
---|
207 | public static void initTextArea(NumberedJTextArea editor_textarea) {
|
---|
208 | /* Fields specific to RSyntaxQuery inherited class */
|
---|
209 | editor_textarea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_XML);
|
---|
210 | editor_textarea.setBracketMatchingEnabled(true);
|
---|
211 | editor_textarea.setAnimateBracketMatching(true);
|
---|
212 | editor_textarea.setAntiAliasingEnabled(true);
|
---|
213 | editor_textarea.setAutoIndentEnabled(true);
|
---|
214 | editor_textarea.setPaintMarkOccurrencesBorder(false);
|
---|
215 |
|
---|
216 | /* Standard fields to JTextArea */
|
---|
217 | editor_textarea.setOpaque(false);
|
---|
218 | editor_textarea.setBackground(Configuration.getColor("coloring.editable_background", false));
|
---|
219 | editor_textarea.setCaretPosition(0);
|
---|
220 | editor_textarea.setLineWrap(true);
|
---|
221 | editor_textarea.setRows(11);
|
---|
222 | editor_textarea.setWrapStyleWord(false);
|
---|
223 | }
|
---|
224 |
|
---|
225 | public int getDialogResult() {
|
---|
226 | return dlgResult; // OK_OPTION or CANCEL_OPTION
|
---|
227 | }
|
---|
228 |
|
---|
229 | //*************************PROCESSING FUNCTIONS***************************//
|
---|
230 | public static int checkForGS2FormatStatements(File collect_cfg_file) {
|
---|
231 |
|
---|
232 | if(Gatherer.GS3 && collect_cfg_file.getAbsolutePath().endsWith(".xml")) {
|
---|
233 |
|
---|
234 | //System.err.println("*** Opening an xml config file");
|
---|
235 |
|
---|
236 | Document xml_file_doc = XMLTools.parseXMLFile(collect_cfg_file);
|
---|
237 | Element root = xml_file_doc.getDocumentElement();
|
---|
238 |
|
---|
239 | // check if there are any <gsf:format-gs2 /> elements. If there are, then may need to process them
|
---|
240 |
|
---|
241 | //NodeList gsf_format_gs2_list = root.getElementsByTagNameNS("gsf", "format-gs2");
|
---|
242 | NodeList gsf_format_gs2_list = root.getElementsByTagName(FormatConversionDialog.GSF_FORMAT_GS2_TAG);
|
---|
243 | if(gsf_format_gs2_list != null && gsf_format_gs2_list.getLength() > 0) {
|
---|
244 |
|
---|
245 | // Sample the first of the <gsf:gs2-format/> elements to
|
---|
246 | // check we don't have any CDataSections in the <gsf:gs2-format/> elements
|
---|
247 | // If the first <gsf:gs2-format/> has a CDataSection, it means we've already
|
---|
248 | // converted it to GS3 format statements during an earlier GLI session.
|
---|
249 |
|
---|
250 | Node gs2format = gsf_format_gs2_list.item(0);
|
---|
251 | //gs2format.normalize();
|
---|
252 | NodeList children = gs2format.getChildNodes();
|
---|
253 |
|
---|
254 | for(int i = 0; i < children.getLength(); i++) {
|
---|
255 | Node child = children.item(i);
|
---|
256 | if(child.getNodeType() == Node.CDATA_SECTION_NODE) {
|
---|
257 | // there are GS2 statements in col config, but they've already been converted to GS3
|
---|
258 | // can open the collection without going through the FormatConversionDialog
|
---|
259 | return OpenCollectionDialog.OK_OPTION;
|
---|
260 | }
|
---|
261 | }
|
---|
262 | System.err.println("*** Found GS2 format statements in config file to be converted to GS3.");
|
---|
263 |
|
---|
264 |
|
---|
265 | // if remote GS3, do we open the collection with the html-encoded GS2 format statements
|
---|
266 | // or do we not allow the remote user to open such a collection at all?
|
---|
267 | // For now we allow them to open it, but print a warning that conversions are not possible.
|
---|
268 |
|
---|
269 | if(Gatherer.isGsdlRemote) { // remote GS3
|
---|
270 | System.err.println("*** Cannot convert GS2 collections from a remote GS3 server.");
|
---|
271 | return OpenCollectionDialog.OK_OPTION;
|
---|
272 | }
|
---|
273 |
|
---|
274 | // If we get here, it means there were no CDataSections in the first (any) <gsf:gs2-format/>
|
---|
275 | // This means it's the first time the GS2 collection is being opened in GLI
|
---|
276 | // Open the FormatCconversionDialog and convert the gs2 statements to gs3:
|
---|
277 | FormatConversionDialog formatconversionDlg = new FormatConversionDialog(collect_cfg_file, xml_file_doc, gsf_format_gs2_list);
|
---|
278 | formatconversionDlg.convertGS2FormatStatements();
|
---|
279 | return formatconversionDlg.getDialogResult();
|
---|
280 |
|
---|
281 | }
|
---|
282 | }
|
---|
283 | return OpenCollectionDialog.OK_OPTION; // no GS2 statements in col config, and can open the collection
|
---|
284 | }
|
---|
285 |
|
---|
286 |
|
---|
287 | /**
|
---|
288 | * runInteractiveProgram() runs a cmdline program that reads from stdinput
|
---|
289 | * until Ctrl-D is encountered. It outputs the result to stdout.
|
---|
290 | *
|
---|
291 | * The cmdline programs HTML Tidy and FormatConverter both behave the same way:
|
---|
292 | * When the formatconverter binary is run in silent mode, it expects input
|
---|
293 | * followed by a newline and then EOF (or if no newline, then 2 EOFs)
|
---|
294 | * which is Ctrl-D on Linux/Mac and Ctrl-Z on Windows.
|
---|
295 | * Then the cmdline program exits by printing the result of the conversion.
|
---|
296 | *
|
---|
297 | * Explicitly sending EOFs from java is no longer necessary, as the SendStreamGobbler
|
---|
298 | * Thread takes care of closing the process stream.
|
---|
299 | *
|
---|
300 | * HTMLTidy returns 0 if no warnings or errors, 1 if just Warnings, 2 if Errors (failure)
|
---|
301 | * http://sourceforge.net/p/tidy/mailman/tidy-develop/thread/[email protected]/
|
---|
302 | *
|
---|
303 | * This code uses the StreamGobbler classes based on
|
---|
304 | * http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2
|
---|
305 | */
|
---|
306 | public String runInteractiveProgram(int program, String inputstr) {
|
---|
307 | String outputstr = "";
|
---|
308 | String[] command_args;
|
---|
309 | process_exitValue = -1;
|
---|
310 |
|
---|
311 | if(program == XMLTIDY) {
|
---|
312 | command_args = xmltidy_cmd_args;
|
---|
313 | } else if(program == FORMATCONVERTER) {
|
---|
314 | command_args = formatconverter_cmd_args;
|
---|
315 | } else { // unknown command
|
---|
316 | return outputstr;
|
---|
317 | }
|
---|
318 |
|
---|
319 | // Generate the formatconverter command
|
---|
320 | /*if (Gatherer.isGsdlRemote) {
|
---|
321 |
|
---|
322 | }*/
|
---|
323 |
|
---|
324 | try {
|
---|
325 |
|
---|
326 | Runtime rt = Runtime.getRuntime();
|
---|
327 | Process prcs = null;
|
---|
328 |
|
---|
329 | prcs = rt.exec(command_args);
|
---|
330 |
|
---|
331 | // send inputstr to process
|
---|
332 | SendStreamGobbler inputGobbler = new SendStreamGobbler(prcs.getOutputStream(), inputstr);
|
---|
333 |
|
---|
334 | // monitor for any error messages
|
---|
335 | ReadStreamGobbler errorGobbler = new ReadStreamGobbler(prcs.getErrorStream(), true);
|
---|
336 |
|
---|
337 | // monitor for the expected output line(s)
|
---|
338 | ReadStreamGobbler outputGobbler = new ReadStreamGobbler(prcs.getInputStream());
|
---|
339 |
|
---|
340 | // kick them off
|
---|
341 | inputGobbler.start();
|
---|
342 | errorGobbler.start();
|
---|
343 | outputGobbler.start();
|
---|
344 |
|
---|
345 | // any error???
|
---|
346 | process_exitValue = prcs.waitFor();
|
---|
347 | //System.out.println("ExitValue: " + exitVal);
|
---|
348 |
|
---|
349 | // From the comments of
|
---|
350 | // http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2
|
---|
351 | // To avoid running into nondeterministic failures to get the process output
|
---|
352 | // if there's no waiting for the threads, call join() on each Thread (StreamGobbler) object:
|
---|
353 | outputGobbler.join();
|
---|
354 | errorGobbler.join();
|
---|
355 | inputGobbler.join();
|
---|
356 |
|
---|
357 | outputstr = outputGobbler.getOutput();
|
---|
358 | String errmsg = errorGobbler.getOutput();
|
---|
359 | if(!errmsg.equals("")) {
|
---|
360 | System.err.println("*** Process errorstream: \n" + errmsg + "\n****");
|
---|
361 | }
|
---|
362 |
|
---|
363 | } catch(IOException ioe) {
|
---|
364 | System.err.println("IOexception " + ioe.getMessage());
|
---|
365 | //ioe.printStackTrace();
|
---|
366 | } catch(InterruptedException ie) {
|
---|
367 | System.err.println("Process InterruptedException " + ie.getMessage());
|
---|
368 | //ie.printStackTrace();
|
---|
369 | }
|
---|
370 |
|
---|
371 | return outputstr;
|
---|
372 | }
|
---|
373 |
|
---|
374 |
|
---|
375 | // http://docs.oracle.com/javase/tutorial/essential/exceptions/finally.html
|
---|
376 | // http://stackoverflow.com/questions/481446/throws-exception-in-finally-blocks
|
---|
377 | private void closeResource(Closeable resourceHandle) {
|
---|
378 | try {
|
---|
379 | if(resourceHandle != null) {
|
---|
380 | resourceHandle.close();
|
---|
381 | resourceHandle = null;
|
---|
382 | }
|
---|
383 | } catch(Exception e) {
|
---|
384 | System.err.println("Exception closing resource: " + e.getMessage());
|
---|
385 | e.printStackTrace();
|
---|
386 | }
|
---|
387 | }
|
---|
388 |
|
---|
389 | public void convertGS2FormatStatements() {
|
---|
390 |
|
---|
391 | // at this point, we know there are one or more <gsf:format-gs2 /> elements
|
---|
392 | // process each of them as follows: unescape, then call formatconverter, then call html tidy on it
|
---|
393 | //NodeList gsf_format_gs2_list = root.getElementsByTagNameNS("gsf", "format-gs2");
|
---|
394 |
|
---|
395 | int len = gsf_format_gs2_list.getLength();
|
---|
396 |
|
---|
397 | for(int i = 0; i < len; i++) {
|
---|
398 |
|
---|
399 | Element gs2format = (Element)gsf_format_gs2_list.item(i);
|
---|
400 | String gs2formatstr = XMLTools.getElementTextValue(gs2format); // seems to already unescape the html entities
|
---|
401 | //gs2formatstr = Codec.transform(gs2formatstr, Codec.ESCAPEDHTML_TO_UNESCAPED);
|
---|
402 |
|
---|
403 | processFormatStatement(i, gs2formatstr);
|
---|
404 | }
|
---|
405 |
|
---|
406 | increment(); // modifies the textareas to initialise them
|
---|
407 | setVisible(true);
|
---|
408 | }
|
---|
409 |
|
---|
410 |
|
---|
411 | private String processFormatStatement(int i, String gs2formatstr) {
|
---|
412 | String errorMsg = "";
|
---|
413 |
|
---|
414 | boolean startsWithTableCell = (gs2formatstr.toLowerCase().startsWith("<td")) ? true : false;
|
---|
415 |
|
---|
416 | //System.err.println("*** Found: " + gs2formatstr);
|
---|
417 |
|
---|
418 | String gs3formatstr = runInteractiveProgram(FORMATCONVERTER, gs2formatstr);
|
---|
419 | gs3formatstr = gs3formatstr.replace("> <", "><");
|
---|
420 |
|
---|
421 | //System.err.println("*** Format is now: " + gs3formatstr);
|
---|
422 |
|
---|
423 | String gs3formatstr_notags = gs3formatstr;
|
---|
424 | gs3formatstr = addSurroundingTags(gs3formatstr);
|
---|
425 |
|
---|
426 |
|
---|
427 | String validationMsg = XMLTools.parseDOM(gs3formatstr);
|
---|
428 | if(!validationMsg.startsWith(XMLTools.WELLFORMED)) {
|
---|
429 | // Run Html Tidy in XML mode
|
---|
430 | System.err.println("*** Needing to run HTML Tidy on: ");
|
---|
431 | System.err.println(gs3formatstr_notags);
|
---|
432 |
|
---|
433 |
|
---|
434 | // HTMLTidy returns 0 if no warnings or errors, 1 if just Warnings, 2 if Errors (failure)
|
---|
435 | // http://sourceforge.net/p/tidy/mailman/tidy-develop/thread/[email protected]/
|
---|
436 | String htmltidy_string = runInteractiveProgram(XMLTIDY, gs3formatstr_notags);//removeSurroundingTags(gs3formatstr));
|
---|
437 |
|
---|
438 | if(process_exitValue >= 2) {
|
---|
439 | System.err.println("@@@ Process exit value: " + process_exitValue);
|
---|
440 | errorMsg = Dictionary.get("FormatConversionDialog.Tidy_Failed")
|
---|
441 | + " " + Dictionary.get("FormatConversionDialog.XML_Still_Invalid");
|
---|
442 | } else {
|
---|
443 | errorMsg = "";
|
---|
444 |
|
---|
445 | gs3formatstr_notags = htmltidy_string;
|
---|
446 |
|
---|
447 | gs3formatstr_notags = removeHTMLTags(i, gs3formatstr_notags, startsWithTableCell);
|
---|
448 | gs3formatstr = addSurroundingTags(gs3formatstr_notags);
|
---|
449 |
|
---|
450 | // Having applied html-tidy, setGS3Format() will return true if it finally parsed
|
---|
451 | }
|
---|
452 | }
|
---|
453 |
|
---|
454 | // For now, assume HTML Tidy has worked and that the gs3format has now been parsed successfully
|
---|
455 |
|
---|
456 | boolean parsed = setGS3Format(i, gs3formatstr); // will parse the gs3formatstr into a DOM object
|
---|
457 | if(!parsed && errorMsg.equals("")) {
|
---|
458 | errorMsg = Dictionary.get("FormatConversionDialog.Tidy_Done")
|
---|
459 | + " " + Dictionary.get("FormatConversionDialog.XML_Still_Invalid");
|
---|
460 | }
|
---|
461 | return errorMsg;
|
---|
462 | //return gs3formatstr;
|
---|
463 | }
|
---|
464 |
|
---|
465 | // HTML tidy adds entire HTML tags around a single format statement. This method removes it.
|
---|
466 | private String removeHTMLTags(int i, String gs3formatstr_notags, boolean startsWithTD) {
|
---|
467 |
|
---|
468 | // if it's a VList classifier <gsf:template match="documentNode|classifierNode"/>
|
---|
469 | // and the gs2 format statement starts with a <td>,
|
---|
470 | // then remove up to and including the outermost <tr>,
|
---|
471 | // else remove up to and including the <body> tag
|
---|
472 |
|
---|
473 | String removeOuterTag = "body>";
|
---|
474 | Element parent = (Element)getParentNode(i); // gets parent of GS2format: <gsf:template>
|
---|
475 | if(parent.hasAttribute("match") && (!parent.hasAttribute("mode") || !parent.getAttribute("mode").equals("horizontal"))) {
|
---|
476 | if(startsWithTD) {
|
---|
477 | removeOuterTag = "tr>"; // remove the outermost <tr> cell HTML tidy added around the tablecell
|
---|
478 | }
|
---|
479 | }
|
---|
480 |
|
---|
481 | // <unwanted>
|
---|
482 | // <removeThisOuterTag>
|
---|
483 | // lines we want
|
---|
484 | // </removeThisOuterTag>
|
---|
485 | // <unwanted>
|
---|
486 |
|
---|
487 | int end = gs3formatstr_notags.indexOf(removeOuterTag);
|
---|
488 | if(end != -1) {
|
---|
489 | gs3formatstr_notags = gs3formatstr_notags.substring(end+removeOuterTag.length());
|
---|
490 | }
|
---|
491 | int start = gs3formatstr_notags.lastIndexOf("</"+removeOuterTag); //closing tag
|
---|
492 | if(start != -1) {
|
---|
493 | gs3formatstr_notags = gs3formatstr_notags.substring(0, start);
|
---|
494 | }
|
---|
495 |
|
---|
496 | //System.err.println("@@@@ " + startsWithTD + " - TAG: " + removeOuterTag + " - AFTER REMOVING TAGS:\n" + gs3formatstr_notags);
|
---|
497 |
|
---|
498 | return gs3formatstr_notags;
|
---|
499 | }
|
---|
500 |
|
---|
501 |
|
---|
502 | //**************** ACCESS FUNCTIONS ***************//
|
---|
503 |
|
---|
504 | // gs2 format text is the text string that goes into <gsf:format-gs2/>: <gsf:format-gs2>text</gsf:format-gs2>
|
---|
505 | private void setGS2Format(int i, String text) {
|
---|
506 | XMLTools.setElementTextValue(getGS2Format(i), text);
|
---|
507 | }
|
---|
508 |
|
---|
509 | // gs3FormatStr represents DOM, and must be parsed and appended as sibling to the gs2format element
|
---|
510 | // as <gsf:gs3-root/>. If it fails to parse, nest it in a CDATA element of <gsf:gs3-root/>
|
---|
511 | private boolean setGS3Format(int i, String gs3formatstr) {
|
---|
512 |
|
---|
513 | Document ownerDoc = getGS2Format(i).getOwnerDocument();
|
---|
514 | Node gs3format = null;
|
---|
515 | boolean parse_success = false;
|
---|
516 |
|
---|
517 |
|
---|
518 | String validationMsg = XMLTools.parseDOM(gs3formatstr);
|
---|
519 | if(!validationMsg.startsWith(XMLTools.WELLFORMED)) {
|
---|
520 | // silently add the gs3formatstr in a CDATA block into the root
|
---|
521 |
|
---|
522 | gs3formatstr = removeSurroundingTags(gs3formatstr);
|
---|
523 | gs3format = ownerDoc.createElement(GSF_GS3_ROOT_TAG);
|
---|
524 | Node cdataSection = ownerDoc.createCDATASection(gs3formatstr);
|
---|
525 | gs3format.appendChild(cdataSection);
|
---|
526 | parse_success = false;
|
---|
527 | }
|
---|
528 | else {
|
---|
529 |
|
---|
530 | // parse DOM into XML
|
---|
531 | Document doc = XMLTools.getDOM(gs3formatstr);
|
---|
532 | Element root = doc.getDocumentElement();
|
---|
533 | gs3format = ownerDoc.importNode(root, true); // an element
|
---|
534 | parse_success = true;
|
---|
535 |
|
---|
536 | // System.err.println("@@@@ ROOT:\n" + XMLTools.xmlNodeToString(root));
|
---|
537 | }
|
---|
538 |
|
---|
539 | // add gs3format element as sibling to gs2format element
|
---|
540 | Element oldgs3format = getGS3Format(i);
|
---|
541 | if(oldgs3format == null) {
|
---|
542 | getParentNode(i).appendChild(gs3format);
|
---|
543 | // System.err.println("@@@ APPEND");
|
---|
544 | } else {
|
---|
545 | // replace the existing
|
---|
546 | getParentNode(i).replaceChild(gs3format, oldgs3format);
|
---|
547 | // System.err.println("@@@ REPLACE");
|
---|
548 | }
|
---|
549 |
|
---|
550 | //http://stackoverflow.com/questions/12524727/remove-empty-nodes-from-a-xml-recursively
|
---|
551 |
|
---|
552 | return parse_success;
|
---|
553 | }
|
---|
554 |
|
---|
555 | private Node getParentNode(int i) {
|
---|
556 | return gsf_format_gs2_list.item(i).getParentNode();
|
---|
557 | }
|
---|
558 |
|
---|
559 | private Element getGS2Format(int i) {
|
---|
560 | return (Element)gsf_format_gs2_list.item(i);
|
---|
561 | }
|
---|
562 |
|
---|
563 | private String getGS2FormatString(int i) {
|
---|
564 | return XMLTools.getElementTextValue(getGS2Format(i));
|
---|
565 | }
|
---|
566 |
|
---|
567 | private Element getGS3Format(int i) {
|
---|
568 | Element parent = (Element)getParentNode(i);
|
---|
569 | NodeList nl = parent.getElementsByTagName(GSF_GS3_ROOT_TAG);
|
---|
570 | if(nl.getLength() > 0) {
|
---|
571 | return (Element)nl.item(0);
|
---|
572 | }
|
---|
573 | return null;
|
---|
574 | }
|
---|
575 |
|
---|
576 | private String getGS3FormatString(int i) {
|
---|
577 | Element gs3format = getGS3Format(i);
|
---|
578 | if(gs3format == null) {
|
---|
579 | return "";
|
---|
580 | } else {
|
---|
581 |
|
---|
582 | // if any child is a CData section, return its text as-is. There will be no indenting
|
---|
583 | NodeList children = gs3format.getChildNodes();
|
---|
584 | for(int j = 0 ; j < children.getLength(); j++) {
|
---|
585 | if(children.item(j).getNodeType() == Node.CDATA_SECTION_NODE) {
|
---|
586 | return children.item(j).getNodeValue(); // content of CDataSection
|
---|
587 | }
|
---|
588 | }
|
---|
589 |
|
---|
590 | // else we have proper nodes, return indented string
|
---|
591 | StringBuffer sb = new StringBuffer();
|
---|
592 | XMLTools.xmlNodeToString(sb, gs3format, true, " ", 0);
|
---|
593 | return sb.toString();
|
---|
594 | }
|
---|
595 | }
|
---|
596 |
|
---|
597 |
|
---|
598 | private String getLabel(int i) {
|
---|
599 | String label = "";
|
---|
600 |
|
---|
601 | // Given XML of the form:
|
---|
602 | // <browse|search>
|
---|
603 | // <format>
|
---|
604 | // <gsf:template match="documentNode|classifierNode" [mode=horizontal]>
|
---|
605 | // <gsf-format:gs2 />
|
---|
606 | // <gs3format/>
|
---|
607 | // </format>
|
---|
608 | // </browse|search>
|
---|
609 |
|
---|
610 | // Want to return the label: "browse|search > documentNode|classifierNode [> horizontal]"
|
---|
611 |
|
---|
612 | Element parent = (Element)getParentNode(i); // gets parent of GS2format: <gsf:template>
|
---|
613 | label = parent.getAttribute("match"); //e.g. documentNode, classifierNode
|
---|
614 |
|
---|
615 | if(parent.hasAttribute("mode")) { // e.g. classifierNode mode=horizontal
|
---|
616 | label = label + " > " + parent.getAttribute("mode");
|
---|
617 | }
|
---|
618 |
|
---|
619 | Element ancestor = (Element) parent.getParentNode().getParentNode(); // ancestor: <browse>|<search>
|
---|
620 | label = ancestor.getTagName() + " > " + label;
|
---|
621 |
|
---|
622 | return label;
|
---|
623 | }
|
---|
624 |
|
---|
625 | // gs2 format statements have spaces instead of newlines. For display, replace with newlines
|
---|
626 | private String makeLines(String gs2formatstr) {
|
---|
627 | return gs2formatstr.replaceAll(">\\s+<", ">\n<");
|
---|
628 | }
|
---|
629 |
|
---|
630 | private String singleLine(String gs2formatstr) {
|
---|
631 | return gs2formatstr.replace(">\n<", "> <"); // put the spaces back
|
---|
632 | }
|
---|
633 |
|
---|
634 | private String removeSurroundingTags(String xml)
|
---|
635 | {
|
---|
636 | //return xml.replace("<"+GSF_GS3_ROOT_TAG+" xmlns:gsf=\"http://www.greenstone.org/greenstone3/schema/ConfigFormat\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n", "").replace("\n</"+GSF_GS3_ROOT_TAG+">", "");//.trim();
|
---|
637 | return xml.replaceAll("<"+GSF_GS3_ROOT_TAG+" xmlns:gsf=(\"|\')http://www.greenstone.org/greenstone3/schema/ConfigFormat(\"|\') xmlns:xsl=(\"|\')http://www.w3.org/1999/XSL/Transform(\"|\')>\n?", "").replaceAll("\n?</"+GSF_GS3_ROOT_TAG+">", "");//.trim();
|
---|
638 | }
|
---|
639 |
|
---|
640 | private String addSurroundingTags(String gs3formatstr) {
|
---|
641 | return "<"+GSF_GS3_ROOT_TAG+" xmlns:gsf='http://www.greenstone.org/greenstone3/schema/ConfigFormat' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>"+gs3formatstr+"</"+GSF_GS3_ROOT_TAG+">";
|
---|
642 | }
|
---|
643 |
|
---|
644 | //*************************FUNCTIONS THAT INTERACT WITH WIDGETS************************//
|
---|
645 | // increment() loads the next values into the dialog
|
---|
646 | private boolean increment() {
|
---|
647 | current_index++;
|
---|
648 | section_label.setText ( getLabel(current_index) );
|
---|
649 | gs2_textarea.setText( makeLines(getGS2FormatString(current_index)) );
|
---|
650 | gs3_textarea.setText( removeSurroundingTags(getGS3FormatString(current_index)) );
|
---|
651 | statusbar.setText("");
|
---|
652 |
|
---|
653 | // as we're on a new screen of dialog, need to clear all undo/redo history
|
---|
654 | undoManager.discardAllEdits();
|
---|
655 | undo_button.setEnabled(false);
|
---|
656 | redo_button.setEnabled(false);
|
---|
657 |
|
---|
658 | int len = gsf_format_gs2_list.getLength();
|
---|
659 | count_label.setText((current_index+1) + " / " + len);
|
---|
660 | if((current_index+1) == len) {
|
---|
661 | return false;
|
---|
662 | } else {
|
---|
663 | return true;
|
---|
664 | }
|
---|
665 | }
|
---|
666 |
|
---|
667 |
|
---|
668 | private void setStatus(String msg) {
|
---|
669 | statusbar.setText(msg);
|
---|
670 | }
|
---|
671 | private void setErrorStatus(String msg) {
|
---|
672 | statusbar.setBackground(Color.red);
|
---|
673 | statusbar.setText(msg);
|
---|
674 | }
|
---|
675 |
|
---|
676 |
|
---|
677 | public void dispose() {
|
---|
678 | //System.err.println("@@@@ DIALOG CLOSING!");
|
---|
679 | if(dlgResult != OpenCollectionDialog.CANCEL_OPTION) {
|
---|
680 | // Need to remove the <gsf:gs3-root/> siblings of all <gsf:format-gs2/>
|
---|
681 | // Then, get the children of each <gsf:gs3-root/> and add these as siblings of <gsf:format-gs2/>
|
---|
682 |
|
---|
683 |
|
---|
684 | int len = gsf_format_gs2_list.getLength();
|
---|
685 | for(int k=len-1; k >= 0; k--) {
|
---|
686 | Element parent = (Element)getParentNode(k);
|
---|
687 |
|
---|
688 | //parent.normalize();
|
---|
689 |
|
---|
690 | NodeList children = parent.getChildNodes();
|
---|
691 |
|
---|
692 | // now have to loop/remove nodes in reverse order, since the following loop
|
---|
693 | // modifies the very nodelist we're looping over by removing nodes from it
|
---|
694 |
|
---|
695 | int numChildren = children.getLength()-1;
|
---|
696 |
|
---|
697 | for(int i=numChildren; i >= 0; i--) {
|
---|
698 | //for(int i = 0; i < children.getLength(); i++) {
|
---|
699 | Node child = children.item(i);
|
---|
700 |
|
---|
701 | if(child.getNodeName().equals(GSF_FORMAT_GS2_TAG)) {
|
---|
702 | // if we're dealing with gs2-format-stmts, put their textnode contents in CData sections
|
---|
703 | // http://www.w3schools.com/xml/xml_cdata.asp
|
---|
704 | // This avoids having to look at html-encoded gs2-format tags in the Format pane
|
---|
705 |
|
---|
706 | Element gs2format = (Element)child;
|
---|
707 | String gs2formatstr = XMLTools.getElementTextValue(gs2format);
|
---|
708 | gs2formatstr = Codec.transform(gs2formatstr, Codec.ESCAPEDHTML_TO_UNESCAPED);
|
---|
709 |
|
---|
710 | Node textNode = XMLTools.getNodeTextNode(gs2format);
|
---|
711 | Node cdataSection = gs2format.getOwnerDocument().createCDATASection(gs2formatstr);
|
---|
712 | gs2format.replaceChild(cdataSection, textNode);
|
---|
713 | }
|
---|
714 | else if(child.getNodeName().equals(GSF_GS3_ROOT_TAG)) {
|
---|
715 |
|
---|
716 | // remove GS3 node and append its children to the parent in its place
|
---|
717 | // the <gsf:gs3-root /> elements wouldn't be in the xml_file_doc DOM tree
|
---|
718 | // unless they were valid XML, so don't need to check for validity here
|
---|
719 |
|
---|
720 | Node gs3root = child;
|
---|
721 | NodeList gs3_format_lines = gs3root.getChildNodes();
|
---|
722 |
|
---|
723 | for(int j = 0; j < gs3_format_lines.getLength(); j++) {
|
---|
724 | Node duplicate = gs3_format_lines.item(j).cloneNode(true);
|
---|
725 | parent.appendChild(duplicate);
|
---|
726 | }
|
---|
727 | gs3root = parent.removeChild(gs3root);
|
---|
728 | gs3root = null; // finished processing
|
---|
729 |
|
---|
730 | } // else - skip all nodes other than <gsf:format-gs2/> and <gsf:gs3-root/>
|
---|
731 | }
|
---|
732 | }
|
---|
733 |
|
---|
734 | Element root = xml_file_doc.getDocumentElement();
|
---|
735 | //System.err.println("### XML file to write out:\n" + XMLTools.xmlNodeToString(root));
|
---|
736 |
|
---|
737 | // Finally, write out the collection xml file
|
---|
738 | String[] nonEscapingTagNames = { StaticStrings.FORMAT_STR, StaticStrings.DISPLAYITEM_STR };
|
---|
739 | XMLTools.writeXMLFile(collect_cfg_file, xml_file_doc, nonEscapingTagNames);
|
---|
740 |
|
---|
741 | }
|
---|
742 | super.dispose();
|
---|
743 |
|
---|
744 | }
|
---|
745 |
|
---|
746 | //******************INNER CLASSES including LISTENERS and STREAMGOBBLERS****************//
|
---|
747 |
|
---|
748 | /**
|
---|
749 | * A textarea with the line number next to each line of the text
|
---|
750 | */
|
---|
751 | public class NumberedJTextArea extends RSyntaxTextArea /* JTextArea */
|
---|
752 | {
|
---|
753 | public void paintComponent(Graphics g)
|
---|
754 | {
|
---|
755 | Insets insets = getInsets();
|
---|
756 | Rectangle rectangle = g.getClipBounds();
|
---|
757 | g.setColor(Color.white);
|
---|
758 | g.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
|
---|
759 |
|
---|
760 | super.paintComponent(g);
|
---|
761 |
|
---|
762 | if (rectangle.x < insets.left)
|
---|
763 | {
|
---|
764 | FontMetrics font_metrics = g.getFontMetrics();
|
---|
765 | int font_height = font_metrics.getHeight();
|
---|
766 | int y = font_metrics.getAscent() + insets.top;
|
---|
767 | int line_number_start_point = ((rectangle.y + insets.top) / font_height) + 1;
|
---|
768 | if (y < rectangle.y)
|
---|
769 | {
|
---|
770 | y = line_number_start_point * font_height - (font_height - font_metrics.getAscent());
|
---|
771 | }
|
---|
772 | int y_axis_end_point = y + rectangle.height + font_height;
|
---|
773 | int x_axis_start_point = insets.left;
|
---|
774 | x_axis_start_point -= getFontMetrics(getFont()).stringWidth(Math.max(getRows(), getLineCount() + 1) + " ");
|
---|
775 | if (!this.getText().trim().equals(""))
|
---|
776 | {
|
---|
777 | g.setColor(Color.DARK_GRAY);
|
---|
778 | }
|
---|
779 | else
|
---|
780 | {
|
---|
781 | g.setColor(Color.white);
|
---|
782 | }
|
---|
783 | int length = ("" + Math.max(getRows(), getLineCount() + 1)).length();
|
---|
784 | while (y < y_axis_end_point)
|
---|
785 | {
|
---|
786 | g.drawString(line_number_start_point + " ", x_axis_start_point, y);
|
---|
787 | y += font_height;
|
---|
788 | line_number_start_point++;
|
---|
789 | }
|
---|
790 | }
|
---|
791 | }
|
---|
792 |
|
---|
793 |
|
---|
794 | public Insets getInsets()
|
---|
795 | {
|
---|
796 | Insets insets = super.getInsets(new Insets(0, 0, 0, 0));
|
---|
797 | insets.left += getFontMetrics(getFont()).stringWidth(Math.max(getRows(), getLineCount() + 1) + " ");
|
---|
798 | return insets;
|
---|
799 | }
|
---|
800 | }
|
---|
801 |
|
---|
802 | // windowClosing() is called when the user presses the top-right close button the dialog
|
---|
803 | // this means the user wanted to cancel out of the entire Format Conversion Wizard.
|
---|
804 | private class WindowClosingListener extends WindowAdapter {
|
---|
805 | public void windowClosing(WindowEvent e) {
|
---|
806 | dlgResult = OpenCollectionDialog.CANCEL_OPTION;
|
---|
807 | }
|
---|
808 | }
|
---|
809 |
|
---|
810 | private class ReconvertListener implements ActionListener {
|
---|
811 | public void actionPerformed(ActionEvent e) {
|
---|
812 | String gs2formatstr = singleLine(gs2_textarea.getText());
|
---|
813 | String errorMsg = processFormatStatement(current_index, gs2formatstr);
|
---|
814 | gs3_textarea.setText( removeSurroundingTags(getGS3FormatString(current_index)) );
|
---|
815 | if(!errorMsg.equals("")) {
|
---|
816 | setErrorStatus(errorMsg);
|
---|
817 | } else {
|
---|
818 | statusbar.setText("");
|
---|
819 | }
|
---|
820 | }
|
---|
821 | }
|
---|
822 |
|
---|
823 |
|
---|
824 | private class NextButtonListener implements ActionListener {
|
---|
825 | public void actionPerformed(ActionEvent e) {
|
---|
826 | //statusbar.setText("");
|
---|
827 |
|
---|
828 | // check if the GS3 format statement is valid XML before storing. If not, let user to decide
|
---|
829 | // whether they want to re-edit it or if they want to keep it as-is, in which case it needs
|
---|
830 | // to be stored as CDATA, which will make it an inactive format statement.
|
---|
831 | // See http://www.w3schools.com/xml/xml_cdata.asp
|
---|
832 | // setGS3Format() already stores invalidXML as CDATA.
|
---|
833 |
|
---|
834 | // Check if the GS3 format statement is valid XML before storing. If not, let the
|
---|
835 | // user to decide whether they want to re-edit it or store it as-is and continue
|
---|
836 |
|
---|
837 | // user okay-ed the lines currently displayed, store them
|
---|
838 | setGS2Format( current_index, singleLine(gs2_textarea.getText()) );
|
---|
839 | boolean parse_success = setGS3Format( current_index, addSurroundingTags(gs3_textarea.getText()) );
|
---|
840 |
|
---|
841 | if(!parse_success) { // invalid XML, warn the user
|
---|
842 | String message = Dictionary.get("FormatConversionDialog.Invalid_XML") + " " + Dictionary.get("FormatConversionDialog.Cancel_Or_Continue_Next");
|
---|
843 | int user_choice = JOptionPane.showConfirmDialog(FormatConversionDialog.this, message, WARNING_TITLE, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
|
---|
844 |
|
---|
845 | if(user_choice == JOptionPane.CANCEL_OPTION) {
|
---|
846 | return; // do nothing on this NextButton press. Don't increment. Let user re-adjust invalid XML for GS3 statement.
|
---|
847 | }
|
---|
848 | }
|
---|
849 |
|
---|
850 | if(increment()) {
|
---|
851 | repaint();
|
---|
852 | } else {
|
---|
853 | next_button.setEnabled(false);
|
---|
854 | getRootPane().setDefaultButton(accept_all_button);
|
---|
855 |
|
---|
856 | }
|
---|
857 |
|
---|
858 | }
|
---|
859 | }
|
---|
860 |
|
---|
861 | private class CancelButtonListener implements ActionListener {
|
---|
862 | public void actionPerformed(ActionEvent e) {
|
---|
863 | dlgResult = OpenCollectionDialog.CANCEL_OPTION;
|
---|
864 | FormatConversionDialog.this.dispose(); // close dialog
|
---|
865 | }
|
---|
866 | }
|
---|
867 |
|
---|
868 |
|
---|
869 | private class AcceptAllButtonListener implements ActionListener {
|
---|
870 | public void actionPerformed(ActionEvent e) {
|
---|
871 | //statusbar.setText("");
|
---|
872 |
|
---|
873 | // user okay-ed the lines, store them
|
---|
874 | setGS2Format(current_index, gs2_textarea.getText());
|
---|
875 | String gs3formatstr = gs3_textarea.getText();
|
---|
876 | boolean parse_success = setGS3Format(current_index, addSurroundingTags(gs3formatstr));
|
---|
877 | String message = "";
|
---|
878 |
|
---|
879 | if(!parse_success) { // invalid XML for current format statement, warn the user
|
---|
880 | setErrorStatus(Dictionary.get("FormatConversionDialog.Invalid_XML"));
|
---|
881 |
|
---|
882 | message = Dictionary.get("FormatConversionDialog.Invalid_XML") + " " + Dictionary.get("FormatConversionDialog.Cancel_Or_Continue_Next");
|
---|
883 | }
|
---|
884 |
|
---|
885 | // Even if the current GS3 format statement is valid XML, the user could have pressed
|
---|
886 | // Accept All at the very start of the FormatConversion dialog too. Check all the
|
---|
887 | // subsequent format statements, and if any have invalid XML, warn user.
|
---|
888 | for(int i = current_index+1; parse_success && i < gsf_format_gs2_list.getLength(); i++) {
|
---|
889 | gs3formatstr = getGS3FormatString(i);
|
---|
890 | String validationMsg = XMLTools.parseDOM(gs3formatstr);
|
---|
891 | if(!validationMsg.startsWith(XMLTools.WELLFORMED)) {
|
---|
892 | parse_success = false;
|
---|
893 | message = Dictionary.get("FormatConversionDialog.Cancel_Or_Accept_All");
|
---|
894 | }
|
---|
895 | }
|
---|
896 |
|
---|
897 | if(!parse_success) {
|
---|
898 | int user_choice = JOptionPane.showConfirmDialog(FormatConversionDialog.this, message, WARNING_TITLE, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
|
---|
899 | if(user_choice == JOptionPane.CANCEL_OPTION) {
|
---|
900 | return; // Don't close the dialog. Let the user continue looking at this or subsequent GS3 format statements.
|
---|
901 | }
|
---|
902 | }
|
---|
903 |
|
---|
904 | // If we're here, then either the format statements parsed, or the user accepted them anyway
|
---|
905 | FormatConversionDialog.this.dispose(); // close dialog
|
---|
906 | }
|
---|
907 | }
|
---|
908 |
|
---|
909 | private class XMLTidyButtonListener implements ActionListener {
|
---|
910 | // run HTML tidy taking input from stdin
|
---|
911 | // http://www.w3.org/People/Raggett/tidy/
|
---|
912 | public void actionPerformed(ActionEvent e) {
|
---|
913 | String gs3formatstr_notags = gs3_textarea.getText();
|
---|
914 |
|
---|
915 | // Flag to determine which tags that HTML tidy adds need to be removed
|
---|
916 | boolean startsWithTableCell = (gs3formatstr_notags.trim().toLowerCase().startsWith("<td")) ? true : false;
|
---|
917 | // run HTML tidy on the GS3 format statement
|
---|
918 | String htmltidy_string = runInteractiveProgram(XMLTIDY, gs3formatstr_notags);
|
---|
919 |
|
---|
920 | if(process_exitValue >= 2) {
|
---|
921 | System.err.println("@@@ Process exit value: " + process_exitValue);
|
---|
922 | setErrorStatus(Dictionary.get("FormatConversionDialog.Tidy_Failed"));
|
---|
923 | } else {
|
---|
924 | gs3formatstr_notags = htmltidy_string;
|
---|
925 |
|
---|
926 | // HTML tidy adds extra HTML markup around the formatstring, so will need to remove it:
|
---|
927 | gs3formatstr_notags = removeHTMLTags(current_index, gs3formatstr_notags, startsWithTableCell);
|
---|
928 |
|
---|
929 | // put the XML tags around the gs3 format string again so we can convert it to a DOM object
|
---|
930 | String gs3formatstr = addSurroundingTags(gs3formatstr_notags);
|
---|
931 |
|
---|
932 | boolean parse_success = setGS3Format(current_index, gs3formatstr); // converts to DOM object
|
---|
933 |
|
---|
934 | // Get indented GS3 format string from DOM object and display it in the text area
|
---|
935 | gs3_textarea.setText( removeSurroundingTags(getGS3FormatString(current_index)) );
|
---|
936 |
|
---|
937 | if(parse_success) {
|
---|
938 | statusbar.setText(Dictionary.get("FormatConversionDialog.Tidy_Done"));
|
---|
939 | } else {
|
---|
940 | setErrorStatus(Dictionary.get("FormatConversionDialog.Tidy_Done")
|
---|
941 | + " " + Dictionary.get("FormatConversionDialog.Error_GS3_Format")
|
---|
942 | + " " + Dictionary.get("FormatConversionDialog.Invalid_XML"));
|
---|
943 | }
|
---|
944 | }
|
---|
945 | }
|
---|
946 | }
|
---|
947 |
|
---|
948 | // http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2
|
---|
949 | class ReadStreamGobbler extends Thread
|
---|
950 | {
|
---|
951 | InputStream is = null;
|
---|
952 | StringBuffer outputstr = new StringBuffer();
|
---|
953 | boolean split_newlines = false;
|
---|
954 |
|
---|
955 |
|
---|
956 | public ReadStreamGobbler(InputStream is)
|
---|
957 | {
|
---|
958 | this.is = is;
|
---|
959 | split_newlines = false;
|
---|
960 | }
|
---|
961 |
|
---|
962 | public ReadStreamGobbler(InputStream is, boolean split_newlines)
|
---|
963 | {
|
---|
964 | this.is = is;
|
---|
965 | this.split_newlines = split_newlines;
|
---|
966 | }
|
---|
967 |
|
---|
968 | public void run()
|
---|
969 | {
|
---|
970 | BufferedReader br = null;
|
---|
971 | try {
|
---|
972 | br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
|
---|
973 | String line=null;
|
---|
974 | while ( (line = br.readLine()) != null) {
|
---|
975 | //System.out.println("@@@ GOT LINE: " + line);
|
---|
976 | outputstr.append(line);
|
---|
977 | if(split_newlines) {
|
---|
978 | outputstr.append("\n");
|
---|
979 | }
|
---|
980 |
|
---|
981 | }
|
---|
982 | } catch (IOException ioe) {
|
---|
983 | ioe.printStackTrace();
|
---|
984 | } finally {
|
---|
985 | closeResource(br);
|
---|
986 | }
|
---|
987 | }
|
---|
988 |
|
---|
989 | public String getOutput() {
|
---|
990 | return outputstr.toString(); // implicit toString() call anyway. //return outputstr;
|
---|
991 | }
|
---|
992 | }
|
---|
993 |
|
---|
994 | class SendStreamGobbler extends Thread
|
---|
995 | {
|
---|
996 | OutputStream os = null;
|
---|
997 | String inputstr = "";
|
---|
998 |
|
---|
999 | public SendStreamGobbler(OutputStream os, String inputstr)
|
---|
1000 | {
|
---|
1001 | this.os = os;
|
---|
1002 | this.inputstr = inputstr;
|
---|
1003 | }
|
---|
1004 |
|
---|
1005 | public void run()
|
---|
1006 | {
|
---|
1007 | BufferedWriter osw = null;
|
---|
1008 | try {
|
---|
1009 | osw = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
|
---|
1010 | //System.out.println("@@@ SENDING LINE: " + inputstr);
|
---|
1011 | osw.write(inputstr, 0, inputstr.length());
|
---|
1012 | osw.newLine();//osw.write("\n");
|
---|
1013 | osw.flush();
|
---|
1014 |
|
---|
1015 | // Don't explicitly send EOF when using StreamGobblers as below,
|
---|
1016 | // as the EOF char is echoed to output.
|
---|
1017 | // Flushing the write handle and/or closing the resource seems
|
---|
1018 | // to already send EOF silently.
|
---|
1019 |
|
---|
1020 | /*if(Utility.isWindows()) {
|
---|
1021 | osw.write("\032"); // octal for Ctrl-Z, EOF on Windows
|
---|
1022 | } else { // EOF on Linux/Mac is Ctrl-D
|
---|
1023 | osw.write("\004"); // octal for Ctrl-D, see http://www.unix-manuals.com/refs/misc/ascii-table.html
|
---|
1024 | }
|
---|
1025 | osw.flush();
|
---|
1026 | */
|
---|
1027 | } catch (IOException ioe) {
|
---|
1028 | ioe.printStackTrace();
|
---|
1029 | } finally {
|
---|
1030 | closeResource(osw);
|
---|
1031 | }
|
---|
1032 | }
|
---|
1033 | }
|
---|
1034 |
|
---|
1035 | private class UndoListener implements ActionListener
|
---|
1036 | {
|
---|
1037 | public void actionPerformed(ActionEvent event)
|
---|
1038 | {
|
---|
1039 | try {
|
---|
1040 | if (undoManager.canUndo()) {
|
---|
1041 | redo_button.setEnabled(true);
|
---|
1042 | undoManager.undo();
|
---|
1043 | }
|
---|
1044 |
|
---|
1045 | if (!undoManager.canUndo()) {
|
---|
1046 | undo_button.setEnabled(false);
|
---|
1047 | } else {
|
---|
1048 | undo_button.setEnabled(true);
|
---|
1049 | }
|
---|
1050 |
|
---|
1051 | } catch (Exception e) {
|
---|
1052 | System.err.println("Exception trying to undo: " + e.getMessage());
|
---|
1053 | }
|
---|
1054 | }
|
---|
1055 | }
|
---|
1056 |
|
---|
1057 | private class RedoListener implements ActionListener
|
---|
1058 | {
|
---|
1059 | public void actionPerformed(ActionEvent evt)
|
---|
1060 | {
|
---|
1061 | try {
|
---|
1062 | if (undoManager.canRedo()) {
|
---|
1063 | undo_button.setEnabled(true); // the difference with Format4gs3Manager, and no DocumentListener
|
---|
1064 | undoManager.redo();
|
---|
1065 | }
|
---|
1066 |
|
---|
1067 | if (!undoManager.canRedo()) {
|
---|
1068 | redo_button.setEnabled(false);
|
---|
1069 | } else {
|
---|
1070 | redo_button.setEnabled(true);
|
---|
1071 | }
|
---|
1072 |
|
---|
1073 | } catch (Exception e) {
|
---|
1074 | System.err.println("Exception trying to redo: " + e.getMessage());
|
---|
1075 | }
|
---|
1076 | }
|
---|
1077 | }
|
---|
1078 |
|
---|
1079 | private class CustomUndoableEditListener implements UndoableEditListener {
|
---|
1080 | public void undoableEditHappened(UndoableEditEvent evt)
|
---|
1081 | {
|
---|
1082 |
|
---|
1083 | undoManager.addEdit(evt.getEdit());
|
---|
1084 | redo_button.setEnabled(false);
|
---|
1085 | undo_button.setEnabled(true);
|
---|
1086 | }
|
---|
1087 | }
|
---|
1088 |
|
---|
1089 | }
|
---|