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