source: main/trunk/gli/src/org/greenstone/gatherer/gui/ConfigFileEditor.java@ 32091

Last change on this file since 32091 was 32091, checked in by ak19, 6 years ago

Find functionality for GS3 GLI's Config file editor.

File size: 12.7 KB
Line 
1/**
2 *#########################################################################
3 *
4 * A component of the Gatherer application, part of the Greenstone digital
5 * library suite from the New Zealand Digital Library Project at the
6 * University of Waikato, New Zealand.
7 *
8 * Copyright (C) 1999 New Zealand Digital Library Project
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *########################################################################
24 */
25
26package org.greenstone.gatherer.gui;
27
28// for RSyntaxTextArea editor's search functionality:
29import org.fife.ui.rtextarea.SearchEngine;
30import org.fife.ui.rtextarea.SearchContext;
31
32import org.greenstone.gatherer.Configuration;
33import org.greenstone.gatherer.Dictionary;
34import org.greenstone.gatherer.Gatherer;
35import org.greenstone.gatherer.util.StaticStrings;
36import org.greenstone.gatherer.util.Utility;
37import org.greenstone.gatherer.util.XMLTools;
38
39import org.w3c.dom.*;
40
41import java.io.File;
42import java.io.IOException;
43
44import java.awt.*;
45import java.awt.event.*;
46import java.util.*;
47import javax.swing.*;
48import javax.swing.border.*;
49import javax.swing.event.*;
50
51
52public class ConfigFileEditor extends ModalDialog
53 implements ActionListener, DocumentListener
54{
55 public final File config_file;
56
57 private GLIButton cancel_button = null;
58 private GLIButton save_button = null;
59 private NumberedJTextArea editor = null;
60 private JTextArea editor_msgarea;
61
62
63 // https://github.com/bobbylight/RSyntaxTextArea/wiki/Example:-Using-Find-and-Replace
64 private JTextField searchField;
65 private JCheckBox regexCB;
66 private JCheckBox matchCaseCB;
67 private JButton nextButton;
68 private JButton prevButton;
69
70 //private final String DEFAULT_PROCESSING_INSTRUCTION = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
71
72 private static final Dimension SIZE = new Dimension(850,550);
73
74 public ConfigFileEditor() {
75
76 super(Gatherer.g_man, true);
77 setModal(true);
78 setSize(SIZE);
79
80 String collection_folder_path = Gatherer.getCollectDirectoryPath();
81 String collectionName = Gatherer.c_man.getCollection().getName(); // we won't be in this constructor if collection is null.
82 this.config_file = new File(collection_folder_path + File.separator + collectionName, Utility.CONFIG_GS3_FILE); // col config editing is a GS3 feature
83
84
85 // Most of the validation_msg_panel code is from Format4gs3Manager.java
86 JPanel validation_msg_panel = new JPanel();
87 JLabel validation_label = new JLabel(Dictionary.get("CDM.FormatManager.MessageBox"));
88 validation_label.setBorder(new EmptyBorder(10,10,10,10)); // add some margin
89 editor_msgarea = new JTextArea();
90
91 editor_msgarea.setCaretPosition(0);
92 editor_msgarea.setLineWrap(true);
93 editor_msgarea.setRows(3);
94 editor_msgarea.setWrapStyleWord(false);
95 editor_msgarea.setEditable(false);
96 editor_msgarea.setToolTipText(Dictionary.get("CDM.FormatManager.MessageBox_Tooltip"));
97 validation_msg_panel.setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0));
98 validation_msg_panel.setLayout(new BorderLayout(5, 0));
99 validation_msg_panel.add(validation_label, BorderLayout.WEST);
100 validation_msg_panel.add(new JScrollPane(editor_msgarea), BorderLayout.CENTER);
101
102 // Find functionality - can extend for Find and Replace
103 // taken from https://github.com/bobbylight/RSyntaxTextArea/wiki/Example:-Using-Find-and-Replace
104 // Create a toolbar with searching options.
105 JToolBar toolBar = new JToolBar();
106 searchField = new JTextField(30);
107 toolBar.add(searchField);
108 nextButton = new JButton("Find Next");
109 nextButton.setActionCommand("FindNext");
110 nextButton.addActionListener(this);
111 toolBar.add(nextButton);
112 searchField.addActionListener(this);
113 prevButton = new JButton("Find Previous");
114 prevButton.setActionCommand("FindPrev");
115 prevButton.addActionListener(this);
116 toolBar.add(prevButton);
117 regexCB = new JCheckBox("Regex");
118 toolBar.add(regexCB);
119 matchCaseCB = new JCheckBox("Match Case");
120 toolBar.add(matchCaseCB);
121
122 // the all important XML editor
123 editor = new NumberedJTextArea(Dictionary.get("ConfigFileEditor.Tooltip"));
124 editor.getDocument().addDocumentListener(this);
125
126 save_button = new GLIButton(Dictionary.get("General.Save"), Dictionary.get("ConfigFileEditor.Save_Tooltip"));
127 cancel_button = new GLIButton(Dictionary.get("General.Cancel"), Dictionary.get("ConfigFileEditor.Cancel_Tooltip")); // tooltip is the same
128 cancel_button.addActionListener(this);
129 save_button.addActionListener(this);
130
131 // LAYOUT
132 JPanel button_panel = new JPanel(new FlowLayout());
133 button_panel.setComponentOrientation(Dictionary.getOrientation());
134 button_panel.add(editor.undoButton);
135 button_panel.add(editor.redoButton);
136 button_panel.add(cancel_button);
137 button_panel.add(save_button);
138
139 // toolbars_panel to contain find-and-replace toolbar and undo/redo/cancel/save button panel
140 JPanel toolbars_panel = new JPanel(new BorderLayout());
141 toolbars_panel.add(toolBar, BorderLayout.NORTH);
142 toolbars_panel.add(button_panel, BorderLayout.CENTER);
143
144 JPanel content_pane = (JPanel) getContentPane();
145 content_pane.setComponentOrientation(Dictionary.getOrientation());
146 content_pane.setLayout(new BorderLayout());
147 content_pane.add(validation_msg_panel, BorderLayout.NORTH);
148 content_pane.add(new JScrollPane(editor), BorderLayout.CENTER); // make editor scrollable
149 //content_pane.add(button_panel, BorderLayout.SOUTH);
150 content_pane.add(toolbars_panel, BorderLayout.SOUTH);
151
152 // NOW WE CAN PREPARE THE ACTUAL CONTENT TO GO INTO THE XML EDITOR TEXTAREA
153 Document xmlDoc = null;
154 xmlDoc = XMLTools.parseXMLFile(config_file);
155
156 if(xmlDoc != null) {
157
158 // Save the collectionConfig.xml before loading it.
159 String[] nonEscapingTagNames = { StaticStrings.FORMAT_STR, StaticStrings.DISPLAYITEM_STR };
160 XMLTools.writeXMLFile(this.config_file, xmlDoc, nonEscapingTagNames); //prepends proc instruction
161
162 // now load the contents
163 editor_msgarea.setBackground(Color.white);
164 StringBuffer sb = new StringBuffer();
165 XMLTools.xmlNodeToString(sb, xmlDoc.getDocumentElement(), true, " ", 0);
166 editor.setText(sb.toString());
167 //editor.setText(elementToString(xmlDoc.getDocumentElement(), true)); // reading in file this way adds processing instruction, which the parser doesn't like.
168
169 } else { // parsing failed
170 // so read in file as text and display as text and show the validation box in red
171
172 // Won't re-save file that is already invalid before it's even been edited through this Editor
173 System.err.println("*** Warning: Parsing error in file " + config_file + ".");
174 System.err.println("*** Not saving ahead of editing.");
175
176 String xml_str = Utility.readFile(config_file);
177
178 // re-parse to get error msg to display in validation field
179
180 // but first get rid of the preprocessing instruction, as this will fail parsing
181 String processingInstruction = getProcessingInstruction(xml_str);
182 xml_str = xml_str.replace(processingInstruction, "").trim(); // also trim newline at start
183
184 if(!xml_str.equals("")) {
185
186 String msg = XMLTools.parse(xml_str); // re-parse
187 editor_msgarea.setBackground(Color.red);
188 editor_msgarea.setText(msg); // display the parsing error
189 save_button.setEnabled(false);
190
191 editor.setText(xml_str); // display the xml contents with error and all
192
193 } else {
194 editor.setText("");
195 editor_msgarea.setText("Empty collection config file");
196 }
197 }
198
199 // Final dialog setup & positioning.
200 setDefaultCloseOperation(DISPOSE_ON_CLOSE); // get rid of this dialog when it's closed (on dispose())
201 getRootPane().setDefaultButton(save_button);
202 Dimension screen_size = Configuration.screen_size;
203 setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
204 editor.setCaretPosition(0); // start at top of config XML
205 }
206
207 public void actionPerformed(ActionEvent e) {
208
209 if(e.getSource() == cancel_button) {
210 this.dispose(); // get rid of this dialog, we're done
211 }
212 else if(e.getSource() == save_button) {
213 // write out the XML to the collectionConfig.xml file and we're done.
214
215 // already know the xml in the textarea is valid, else the save_button would have been inactive
216 Document xml_file_doc = XMLTools.getDOM(editor.getText());
217 // This code from FormatConversionDialog.dispose()
218 // saves the XML, prepending the processing instruction (<?xml ... ?>)
219 String[] nonEscapingTagNames = { StaticStrings.FORMAT_STR, StaticStrings.DISPLAYITEM_STR };
220 XMLTools.writeXMLFile(this.config_file, xml_file_doc, nonEscapingTagNames);
221
222 this.dispose(); // get rid of this dialog, we're done
223
224 // close and reopen the collection in GLI
225 String current_collection_filepath = Gatherer.c_man.getCollection().getCollectionPath();
226 Gatherer.c_man.closeCollection();
227 Gatherer.c_man.loadCollection(current_collection_filepath);
228 }
229
230 // Find functionality, can extend for Find and Replace
231 // taken from https://github.com/bobbylight/RSyntaxTextArea/wiki/Example:-Using-Find-and-Replace
232 else if(e.getSource() == searchField) {
233 nextButton.doClick(0);
234 }
235 else if(e.getSource() == nextButton || e.getSource() == prevButton) {
236 // "FindNext" => search forward, "FindPrev" => search backward
237 String command = e.getActionCommand();
238 boolean forward = "FindNext".equals(command);
239
240 // Create an object defining our search parameters.
241 SearchContext context = new SearchContext();
242 String text = searchField.getText();
243 if (text.length() == 0) {
244 return;
245 }
246 context.setSearchFor(text);
247 context.setMatchCase(matchCaseCB.isSelected());
248 context.setRegularExpression(regexCB.isSelected());
249 context.setSearchForward(forward);
250 context.setWholeWord(false);
251
252 //boolean found = SearchEngine.find(this.editor, context).wasFound();
253 boolean found = false;
254 try {
255 found = SearchEngine.find(this.editor, context);
256 // if the current search string is a correction to a previous invalid regex,
257 // then return background colour back to normal
258 searchField.setBackground(Color.white);
259 if (!found) {
260 JOptionPane.showMessageDialog(this, "Text not found");
261 }
262 } catch(java.util.regex.PatternSyntaxException regex_exception) {
263 searchField.setBackground(Color.red);
264 JOptionPane.showMessageDialog(this, "Invalid regex");
265 }
266
267 }
268 }
269
270
271 // This method returns a proper processing instruction (starts with <?xml and ends with ?>)
272 // else the empty string is returned
273 public String getProcessingInstruction(String xmlStr) {
274 String processingInstruction = "";
275
276 xmlStr = xmlStr.trim();
277 if(xmlStr.startsWith("<?xml")) {
278 int endIndex = xmlStr.indexOf("?>"); // end of processing instruction
279
280 if(endIndex != -1) {
281 endIndex += 2;
282 processingInstruction = xmlStr.substring(0, endIndex);
283 }
284 }
285 return processingInstruction;
286 }
287
288
289 // THE FOLLOWING FUNCTIONS ARE LARGELY FROM Format4gs3Manager.java.EditorListener
290 public void changedUpdate(DocumentEvent e)
291 {
292 update();
293 }
294
295 public void insertUpdate(DocumentEvent e)
296 {
297 update();
298 updateUndo("insert");
299
300 }
301
302 public void removeUpdate(DocumentEvent e)
303 {
304 update();
305 updateUndo("remove");
306
307 }
308
309 private void updateUndo(String from)
310 {
311
312 editor.undoButton.setEnabled(true);
313 }
314
315 public void update()
316 {
317
318 String xml_str = editor.getText();
319
320 // Processing instructions "<?xml...?>" are no longer displayed in the editor (but
321 // they are written out to the file on save) so we don't need to deal with them here.
322
323 // can't parse with processing instruction <?xml...?>, so remove that
324 ///String processingInstruction = getProcessingInstruction(xml_str);
325 ///xml_str = xml_str.replace(processingInstruction, "");
326 // now can parse the XML portion without the processing instruction,
327 // while the original XML remains unchanged in the editor area
328 // If the processing instruction was incomplete, then xml_str would still
329 // include the incomplete processing instruction, and parsing below will catch the error.
330
331 String msg = XMLTools.parse(xml_str);
332 editor_msgarea.setText(msg);
333
334 if (msg.startsWith(XMLTools.WELLFORMED))
335 {
336 editor_msgarea.setBackground(Color.white);
337 save_button.setEnabled(true);
338 }
339 else
340 {
341 editor_msgarea.setBackground(Color.red);
342 save_button.setEnabled(false);
343 }
344 }
345
346}
Note: See TracBrowser for help on using the repository browser.