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

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

Previous commit also puts the caret/cursor at the start when using 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(toolbars_panel, BorderLayout.SOUTH);
150
151 // NOW WE CAN PREPARE THE ACTUAL CONTENT TO GO INTO THE XML EDITOR TEXTAREA
152 Document xmlDoc = null;
153 xmlDoc = XMLTools.parseXMLFile(config_file);
154
155 if(xmlDoc != null) {
156
157 // Save the collectionConfig.xml before loading it.
158 String[] nonEscapingTagNames = { StaticStrings.FORMAT_STR, StaticStrings.DISPLAYITEM_STR };
159 XMLTools.writeXMLFile(this.config_file, xmlDoc, nonEscapingTagNames); //prepends proc instruction
160
161 // now load the contents
162 editor_msgarea.setBackground(Color.white);
163 StringBuffer sb = new StringBuffer();
164 XMLTools.xmlNodeToString(sb, xmlDoc.getDocumentElement(), true, " ", 0);
165 editor.setText(sb.toString());
166 //editor.setText(elementToString(xmlDoc.getDocumentElement(), true)); // reading in file this way adds processing instruction, which the parser doesn't like.
167
168 } else { // parsing failed
169 // so read in file as text and display as text and show the validation box in red
170
171 // Won't re-save file that is already invalid before it's even been edited through this Editor
172 System.err.println("*** Warning: Parsing error in file " + config_file + ".");
173 System.err.println("*** Not saving ahead of editing.");
174
175 String xml_str = Utility.readFile(config_file);
176
177 // re-parse to get error msg to display in validation field
178
179 // but first get rid of the preprocessing instruction, as this will fail parsing
180 String processingInstruction = getProcessingInstruction(xml_str);
181 xml_str = xml_str.replace(processingInstruction, "").trim(); // also trim newline at start
182
183 if(!xml_str.equals("")) {
184
185 String msg = XMLTools.parse(xml_str); // re-parse
186 editor_msgarea.setBackground(Color.red);
187 editor_msgarea.setText(msg); // display the parsing error
188 save_button.setEnabled(false);
189
190 editor.setText(xml_str); // display the xml contents with error and all
191
192 } else {
193 editor.setText("");
194 editor_msgarea.setText("Empty collection config file");
195 }
196 }
197
198 // Final dialog setup & positioning.
199 setDefaultCloseOperation(DISPOSE_ON_CLOSE); // get rid of this dialog when it's closed (on dispose())
200 getRootPane().setDefaultButton(save_button);
201 Dimension screen_size = Configuration.screen_size;
202 setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
203 editor.setCaretPosition(0); // start at top of config XML
204 }
205
206 public void actionPerformed(ActionEvent e) {
207
208 if(e.getSource() == cancel_button) {
209 this.dispose(); // get rid of this dialog, we're done
210 }
211 else if(e.getSource() == save_button) {
212 // write out the XML to the collectionConfig.xml file and we're done.
213
214 // already know the xml in the textarea is valid, else the save_button would have been inactive
215 Document xml_file_doc = XMLTools.getDOM(editor.getText());
216 // This code from FormatConversionDialog.dispose()
217 // saves the XML, prepending the processing instruction (<?xml ... ?>)
218 String[] nonEscapingTagNames = { StaticStrings.FORMAT_STR, StaticStrings.DISPLAYITEM_STR };
219 XMLTools.writeXMLFile(this.config_file, xml_file_doc, nonEscapingTagNames);
220
221 this.dispose(); // get rid of this dialog, we're done
222
223 // close and reopen the collection in GLI
224 String current_collection_filepath = Gatherer.c_man.getCollection().getCollectionPath();
225 Gatherer.c_man.closeCollection();
226 Gatherer.c_man.loadCollection(current_collection_filepath);
227 }
228
229 // Find functionality, can extend for Find and Replace
230 // taken from https://github.com/bobbylight/RSyntaxTextArea/wiki/Example:-Using-Find-and-Replace
231 else if(e.getSource() == searchField) {
232 nextButton.doClick(0);
233 }
234 else if(e.getSource() == nextButton || e.getSource() == prevButton) {
235 // "FindNext" => search forward, "FindPrev" => search backward
236 String command = e.getActionCommand();
237 boolean forward = "FindNext".equals(command);
238
239 // Create an object defining our search parameters.
240 SearchContext context = new SearchContext();
241 String text = searchField.getText();
242 if (text.length() == 0) {
243 return;
244 }
245 context.setSearchFor(text);
246 context.setMatchCase(matchCaseCB.isSelected());
247 context.setRegularExpression(regexCB.isSelected());
248 context.setSearchForward(forward);
249 context.setWholeWord(false);
250
251 //boolean found = SearchEngine.find(this.editor, context).wasFound();
252 boolean found = false;
253 try {
254 found = SearchEngine.find(this.editor, context);
255 // if the current search string is a correction to a previous invalid regex,
256 // then return background colour back to normal
257 searchField.setBackground(Color.white);
258 if (!found) {
259 JOptionPane.showMessageDialog(this, "Text not found");
260 }
261 } catch(java.util.regex.PatternSyntaxException regex_exception) {
262 searchField.setBackground(Color.red);
263 JOptionPane.showMessageDialog(this, "Invalid regex");
264 }
265
266 }
267 }
268
269
270 // This method returns a proper processing instruction (starts with <?xml and ends with ?>)
271 // else the empty string is returned
272 public String getProcessingInstruction(String xmlStr) {
273 String processingInstruction = "";
274
275 xmlStr = xmlStr.trim();
276 if(xmlStr.startsWith("<?xml")) {
277 int endIndex = xmlStr.indexOf("?>"); // end of processing instruction
278
279 if(endIndex != -1) {
280 endIndex += 2;
281 processingInstruction = xmlStr.substring(0, endIndex);
282 }
283 }
284 return processingInstruction;
285 }
286
287
288 // THE FOLLOWING FUNCTIONS ARE LARGELY FROM Format4gs3Manager.java.EditorListener
289 public void changedUpdate(DocumentEvent e)
290 {
291 update();
292 }
293
294 public void insertUpdate(DocumentEvent e)
295 {
296 update();
297 updateUndo("insert");
298
299 }
300
301 public void removeUpdate(DocumentEvent e)
302 {
303 update();
304 updateUndo("remove");
305
306 }
307
308 private void updateUndo(String from)
309 {
310
311 editor.undoButton.setEnabled(true);
312 }
313
314 public void update()
315 {
316
317 String xml_str = editor.getText();
318
319 // Processing instructions "<?xml...?>" are no longer displayed in the editor (but
320 // they are written out to the file on save) so we don't need to deal with them here.
321
322 // can't parse with processing instruction <?xml...?>, so remove that
323 ///String processingInstruction = getProcessingInstruction(xml_str);
324 ///xml_str = xml_str.replace(processingInstruction, "");
325 // now can parse the XML portion without the processing instruction,
326 // while the original XML remains unchanged in the editor area
327 // If the processing instruction was incomplete, then xml_str would still
328 // include the incomplete processing instruction, and parsing below will catch the error.
329
330 String msg = XMLTools.parse(xml_str);
331 editor_msgarea.setText(msg);
332
333 if (msg.startsWith(XMLTools.WELLFORMED))
334 {
335 editor_msgarea.setBackground(Color.white);
336 save_button.setEnabled(true);
337 }
338 else
339 {
340 editor_msgarea.setBackground(Color.red);
341 save_button.setEnabled(false);
342 }
343 }
344
345}
Note: See TracBrowser for help on using the repository browser.