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