>")) {
+
+ JLabel a_label = new JLabel(line);
+ a_label.setAlignmentX(Component.LEFT_ALIGNMENT);
+ info_list_pane.add(a_label);
+ }
+
+ } catch (Exception ioe) {
+ ioe.printStackTrace();
+ }
+
+ }
+
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/ActionRecorderDialog.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/ActionRecorderDialog.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/ActionRecorderDialog.java (revision 31635)
@@ -0,0 +1,2135 @@
+package org.greenstone.gatherer.feedback;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import javax.swing.tree.*;
+import javax.swing.event.*;
+import javax.swing.text.*;
+import java.io.*;
+import java.awt.image.*;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.text.MessageFormat;
+import javax.swing.border.*;
+import java.text.*;
+
+import org.greenstone.gatherer.util.JarTools;
+
+/**
+ * This is the special class listener.
+ * This class will record all the action did to any of the
+ * component inside the window that were open by the application.
+ * This listener will start listening since the beginning of the application
+ * until the user quits from the application. It will record :
+ * 1. All the information about the window and its components where the action occured.
+ * 2. All the information about all windows and its components that were open when the action occured.
+ * Note : point 1 and 2 only start when user wants to send feedback.
+ * 3. The action's command the user did.
+ * 4. The date of when the action occured.
+ * The history of the action will be stored in history.log file, which then will be compressed
+ * to a zip file called MyZipFile.zip.
+ * The listener this class implements are only the listener interface that a javax.swing.* component
+ * could have as stated in API.Some of the listener however is not implemented here as it is considered the event
+ * listened by those listener are not essential to be recorded as they not making any major changes
+ * to the application.
+ * This listener will only record maximum last 60 instructions user did to the application.
+ * In this class also we can add some stuff globally through out all the window open by
+ * the application.
+ * This is the class will add a menu called Feedback through out all the window of type JFrame or JDialog
+ * open by the application.This menu will allow user to either sending feedback or viewing the history
+ * of all the action user did so far.
+ * @author Veronica Liesaputra
+ */
+public class ActionRecorderDialog
+ implements ActionListener,TreeModelListener,ItemListener,ListDataListener,ChangeListener,
+ ListSelectionListener,MenuListener,TableColumnModelListener,TreeSelectionListener
+{
+ /**
+ * This hold the title of the window where the action occured.
+ */
+ private String myframe;
+
+ /**
+ * This hold the title of the window where user called send feedback.
+ */
+ private String feedbackframe;
+
+ /**
+ * This hold the instance of ComponentInformation to get all the information inside a window.
+ */
+ private ComponentInformation compInfo;
+
+ /**
+ * This will hold a record of the action that user just did at the moment.
+ */
+ private History log;
+
+ /**
+ * This will reference to the window where the action occured.
+ */
+ private Window point;
+
+ /**
+ * This hold the vector of all the History instances of all the record of action user did
+ * so far.
+ * This vector will hold the history of all the 30 actions user just did.
+ * There are 2 situation for this vector :
+ * 1. Before user choose to send feedback.
+ * Here, If we want to add another record to the vector when the vector size already 30 then,
+ * all the 30 records inside the vector will be saved in the temp_history.log file. The vector will
+ * be cleared and then added with the new record.
+ * If the user quits the application, then whats in the vector will be saved to history.log file.
+ * 2. After user choose to send feedback.
+ * When user choose to send feedback,then what happen is what ever inside the vector at the moment
+ * will be saved to history.log file and then vector will be cleared and it will add the new instruction.
+ * If we want to add another record to the vector when the vector size already 30 then,
+ * all the 30 records inside the vector will be saved in the temp_feedbackhist.log file. The vector will
+ * be cleared and then added with the new record.
+ * If the user finished with the feedback then it will cleared all the vector and load the vector
+ * inside the history.log to it and add the new record to the vector.
+ * If the user quits the application, then whats in the vector will not be saved.
+ */
+ private static Vector vector;
+
+ /**
+ * This is the flag to tell whether or not the image for the window where the action occured is
+ * finished being added properly to the appropriate record.
+ * If savefinish = true then it means that the thread of adding image to the record stop, otherwise
+ * it means its not yet finished so don't stop the thread.
+ */
+ private static boolean savefinish = true;
+
+ /**
+ * This is the thread that contains all the code that the application should be when the user exiting
+ * the application.
+ */
+ private Thread writeThread;
+
+ /**
+ * This is the thread that contains all the code to add the image of the window where the action
+ * occured to the proper record.
+ * This thread will stop if savefinish = true.
+ */
+ private Thread saveThread;
+
+ /**
+ * This is the runtime of this application.
+ */
+ private Runtime run;
+
+ /**
+ * This variable will hold the resource of the words that is stored in feedback.properties file.
+ * The calling using messages.getString(someString) will caused someString to be translated
+ * into some other string that is hold in that file.usually it will caused it to be translated
+ * to the language that the user use or choose to have as stated in Locale.
+ */
+ private static ResourceBundle messages;
+
+ /**
+ * This is an instance of ScreenShot that will allow this application to take screen shot of the whole
+ * screen or just the screen shot of the window where the actions occured.
+ */
+ private ScreenShot sh;
+
+ /**
+ * This HashMap stored the pair of model and its component.
+ * This will allow this listener to know which component the model where the actions occured belongs to.
+ */
+ private HashMap modelHash;
+
+ /**
+ * This is the flag variable to sign that the user start the Reporting Feedback sequence.
+ * If start_rec = false means that the user did not start reporting feedback sequence.
+ * Otherwise, it means the user start reporting feedback sequence, here there will be changes in the
+ * content of the vector used to save all the records.This also will change the appearance of the
+ * window that are currently open by the application. it will make toolbar value = true, rec = true and
+ * stop_rec = false.
+ */
+ private boolean start_rec = false;
+
+ /**
+ * This is the flag variable to sign that the user are now recording the Reporting Feedback sequence.
+ * If rec = true, it means its recording, otherwise its false.It also will decide
+ * the GUI appearance of the window that are currently open by the application.
+ */
+ private boolean rec = false;
+
+ /**
+ * This is the flag variable to sign that user are finished the Reporting Feedback sequence.
+ * If stop_rec = false, then it means user stop the Reporting Feedback sequence.So here, there will
+ * be changes in the vector's content. If rec = true then it will also delete the added toolbar on the currently open
+ * window by the application and its also will delete all the history and file taken when user doing the
+ * Reporting Feedback sequence.
+ */
+ private boolean stop_rec = true;
+
+ /**
+ * This is the thread do task to take history of the actions.
+ */
+ private Thread saveallThread;
+
+ /**
+ * This is the flag to say whether or not the send feedback button been pushed by user.
+ */
+ private boolean start_fd = false;
+
+ /**
+ * This is a blocker to say that user cannot do anything while we taking history.
+ */
+ private MouseListener mouse_blocker_listener = new MouseAdapter() {};
+
+ /**
+ * This is the feedback interface window.
+ */
+ private FeedbackInterface frame;
+
+ /**
+ * This is an instance of CompListener that will add listener to all the components inside it.
+ */
+ private CompListener compList;
+
+ /**
+ * This is the accelerator key (shortcuts key) for taking screenshot.
+ */
+ private KeyStroke shKey;
+
+ /**
+ * This is the accelerator key (shortcuts key) for reporting feedback.
+ */
+ private KeyStroke fdKey;
+
+ /**
+ * This is the accelerator key (shortcuts key) for viewing history.
+ */
+ private KeyStroke histKey;
+
+ /**
+ * This is the accelerator key (shortcuts key) for sending feedback straight away.
+ */
+ private KeyStroke sndfdKey;
+
+ /**
+ * This is the window that is active at the moment.
+ */
+ private Window activeWindow;
+
+ /**
+ * This is the modal window where user wants to take the screeshot or sending feedback.
+ */
+ private Window modalWindow;
+
+ /**
+ * This constructor will seeting up the data member to its appropriate value.
+ * It will get the vector that are stored in the history.log file and also it will
+ * setup the writeThread.
+ * @param currentLocale is the locale user choose all the text to be displayed as.
+ */
+ public ActionRecorderDialog (Locale currentLocale)
+ {
+ messages = ResourceBundle.getBundle("feedback",currentLocale);
+ setup_UIManager();
+
+ fdKey = KeyStroke.getKeyStroke("F2");
+ histKey = KeyStroke.getKeyStroke("F3");
+ shKey = KeyStroke.getKeyStroke("F5");
+ sndfdKey = KeyStroke.getKeyStroke("F12");
+ /*shKey = KeyStroke.getKeyStroke(KeyEvent.VK_SLASH,InputEvent.CTRL_MASK|
+ InputEvent.SHIFT_MASK);*/
+
+ final ZipFile zip;
+ zip = new ZipFile();
+ zip.unZipFile();
+ vector = getPrev_log("history.log");
+ sh = new ScreenShot();
+ modelHash = new HashMap();
+ compInfo = new ComponentInformation(this,currentLocale,modelHash);
+ compList = new CompListener(this,modelHash);
+ EventQueue eq;
+ eq = Toolkit.getDefaultToolkit().getSystemEventQueue();
+ eq.push(new MyEventQueue());
+ run = Runtime.getRuntime();
+ Runnable writeRun;
+ writeRun = new Runnable()
+ {
+ public void run()
+ {
+ try
+ {
+ if (stop_rec == true)
+ {
+ File f;
+ f = new File("history.log");
+ FileOutputStream fos;
+ fos = new FileOutputStream("history.log");
+ ObjectOutputStream oos;
+ oos = new ObjectOutputStream(fos);
+ oos.writeObject(vector);
+ vector.removeAllElements();
+ vector = getPrev_log("temp_history.log");
+ oos.writeObject(vector);
+ oos.close();
+ f = new File("temp_history.log");
+ f.delete();
+ }
+
+ File f2;
+ f2 = new File("feedbackhist.log");
+ f2.delete();
+ f2 = new File("temp_feedbackhist.log");
+ f2.delete();
+
+ zip.sendZipFile();
+ writeThread = null;
+ }
+ catch (IOException exp) {}
+ }
+ };
+
+ writeThread = new Thread(writeRun);
+ run.addShutdownHook(writeThread);
+ }
+
+ /** change the locale for the resource bundle*/
+ public void setLocale(Locale loc) {
+ messages = ResourceBundle.getBundle("feedback",loc);
+ }
+ /**
+ * This method will setup some of the GUI appearance that window should have when the user
+ * choose to start the Reporting Feedback sequence.
+ */
+ public void setup_UIManager ()
+ {
+ // why do you do all this???
+ //UIManager.put("TabbedPane.selected",(new Color(224,240,224)));
+ //UIManager.put("Button.select",(new Color(224,240,224)));
+ //UIManager.put("ScrollBar.background",(new Color(176,208,176)));
+ //UIManager.put("ScrollBar.highlight",(new Color(224,240,224)));
+ //UIManager.put("ScrollBar.thumb",(new Color(224,240,224)));
+ //UIManager.put("ScrollBar.track",(new Color(224,240,224)));
+ //UIManager.put("Tree.textBackground",new Color(224,240,224));
+ //UIManager.put("Label.font",new Font("Dialog",Font.PLAIN,12));
+ //UIManager.put("ComboBox.font",new Font("Dialog",Font.PLAIN,12));
+ //UIManager.put("TextArea.font",new Font("Dialog",Font.PLAIN,12));
+ }
+
+ /**
+ * This method will get the information about all the components inside the window that are
+ * open by the application when the action occured and save it to vector.
+ * @param comp its the component where the user did the action.
+ * @param command its the action's command the user did.
+ */
+ private void saving (final Component comp,final String command)
+ {
+ if ((command.equals(messages.getString("SendFeedback"))) ||
+ ((command.equals("FastSend")) && (stop_rec == false)))
+ {
+ start_fd = true;
+ }
+
+ Runnable saveall;
+ saveall = new Runnable ()
+ {
+ public void run ()
+ {
+ try
+ {
+ Runnable savework;
+ savework = new Runnable ()
+ {
+ public void run ()
+ {
+ Date curr_date;
+ curr_date = new Date();
+ log = new History(curr_date,command);
+
+ if ((start_fd == true)&&(command.equals(messages.getString("WindowOpened"))))
+ {
+ start_rec = true;
+ rec = true;
+ start_fd = false;
+ }
+
+ getFrame(comp,command);
+ saveState(command);
+ saveLog();
+
+ if ((point != null) && (rec == true))
+ {
+ if (command.equals(messages.getString("WindowClosed")))
+ {
+ if (point instanceof JDialog)
+ if (((JDialog)point).isModal() == true)
+ if (point == modalWindow)
+ frame.setComment(false);
+ }
+ }
+ }
+ };
+
+ SwingUtilities.invokeAndWait(savework);
+ }
+ catch (Exception ex) {ex.printStackTrace();}
+ }
+ };
+
+ saveallThread = new Thread(saveall);
+ saveallThread.start();
+ }
+
+ /**
+ * This method will get the screen shot of the window where the actions occured and
+ * add the image also its dimension to the passed record.
+ * It will caused the saveThread to start so it can allows other stuff to be run concurrently
+ * and this thread will be stopped when the screen shot of the window alreadyfinished taken.
+ * @param hst is the record where the image and its dimension should be stored to.
+ */
+ public void saveImage (final History hst)
+ {
+ Runnable saveRun;
+ saveRun = new Runnable()
+ {
+ History hist = hst;
+ public void run()
+ {
+ String img,width,height;
+ Rectangle rect;
+ int w, h,dimwidth,dimheight;
+
+ if (point != null)
+ {
+ dimwidth = sh.getWidth();
+ dimheight = sh.getHeight();
+
+ w = point.getWidth();
+ h = point.getHeight();
+
+ try
+ {
+ rect = new Rectangle(point.getLocationOnScreen(),
+ new Dimension(w,h));
+
+ if (w >= dimwidth)
+ w = dimwidth - 50;
+
+ width = "" + w;
+
+ if (h >= dimheight)
+ h = dimheight - 50;
+
+ height = "" + h;
+ }
+ catch (IllegalComponentStateException exp)
+ {
+ rect = null;
+ width = null;
+ height = null;
+ }
+ }
+ else
+ {
+ rect = null;
+ width = null;
+ height = null;
+ }
+
+ if (rect != null)
+ {
+ img = sh.getImage(rect);
+ if (savefinish == true)
+ {
+ hist.setImage(img,height,width);
+ }
+ }
+ }
+ };
+
+ Thread saveThread;
+ saveThread = new Thread(saveRun);
+ saveThread.start();
+ }
+
+ /**
+ * This method will setup the savefinish value.
+ * (Precondition: (save != null))
+ * @param save the value savefinish should have.
+ */
+ public static void setSavefinish (boolean save)
+ {
+ savefinish = save;
+ }
+
+
+ /**
+ * This method will make the JMenu that is should be added to all the
+ * window opened by the application.
+ * If rec = false then the JMenu will give user option to start the Reporting Feedback sequence
+ * or Sending feedback or to view the history of actions they have done so far.
+ * If rec = true then the JMenu will only give user option to view the history of actions
+ * they have done so far or sending feedback or taking screenshot.
+ * @return JMenu that should be added to all window open.
+ */
+ public JMenu makeMenu ()
+ {
+ JMenu menu;
+ JMenuItem item,item2,item3;
+ ImageIcon icon,icon2,icon3;
+
+ icon3 = JarTools.getImage("fastsend.gif");
+ item3 = new JMenuItem(messages.getString("SendFeedback"),
+ new ImageIcon(icon3.getImage().getScaledInstance
+ (20,20,Image.SCALE_SMOOTH)));
+ item3.setActionCommand("FastSend");
+ item3.addActionListener(this);
+ item3.setMnemonic(KeyEvent.VK_N);
+ item3.setAccelerator(sndfdKey);
+ item3.setToolTipText("Send feedback now");
+
+ item = null;
+
+ if (rec == false)
+ {
+ icon = JarTools.getImage("feedback.gif");
+ item = new JMenuItem("Report Feedback",
+ new ImageIcon(icon.getImage().getScaledInstance
+ (20,20,Image.SCALE_SMOOTH)));
+ item.setActionCommand(messages.getString("SendFeedback"));
+ item.addActionListener(this);
+ item.setMnemonic(KeyEvent.VK_F);
+ item.setAccelerator(fdKey);
+ item.setToolTipText("Report feedback");
+ }
+ else
+ {
+ icon = JarTools.getImage("camera.gif");
+ item = new JMenuItem(messages.getString("ScreenShot"),
+ new ImageIcon(icon.getImage().getScaledInstance
+ (20,20,Image.SCALE_SMOOTH)));
+ item.setActionCommand("ScreenShot");
+ item.setMnemonic(KeyEvent.VK_S);
+ item.addActionListener(this);
+ item.setAccelerator(shKey);
+ item.setToolTipText("Take screenshot of the whole screen.");
+ }
+
+ icon2 = JarTools.getImage("history.gif");
+ item2 = new JMenuItem(messages.getString("LookHistory"),
+ new ImageIcon(icon2.getImage().getScaledInstance
+ (20,20,Image.SCALE_SMOOTH)));
+ item2.setActionCommand(messages.getString("LookHistory"));
+ item2.addActionListener(this);
+ item2.setMnemonic(KeyEvent.VK_H);
+ item2.setAccelerator(histKey);
+ item2.setToolTipText(messages.getString("LookHistoryButton"));
+
+ menu = new JMenu (messages.getString("Feedback"));
+ menu.setMnemonic(KeyEvent.VK_D);
+
+
+ if (item != null)
+ menu.add(item);
+
+ menu.add(item3);
+
+ menu.add(item2);
+
+ if (rec == true)
+ menu.setBackground(new Color(176,209,217));
+ else
+ menu.setBackground(new Color(204,204,204));
+
+ return menu;
+ }
+
+ public void refreshLocationAndSize(Window window) {
+
+ window.setLocation(window.getX(),window.getY());
+
+ Toolkit kit;
+ kit = Toolkit.getDefaultToolkit();
+ Dimension screenSize;
+ screenSize = kit.getScreenSize();
+ int screenHeight;
+ screenHeight = screenSize.height;
+ int screenWidth;
+ screenWidth = screenSize.width;
+
+ if ((window.getSize().width > screenWidth) &&
+ (window.getSize().height > screenHeight)) {
+ window.setSize(window.getPreferredSize());
+ }
+ }
+
+
+ /**
+ * This method will setup the GUI that this window should have at the moment.
+ * If start_rec is true then JMenu don't have the option to start Reporting Feedback sequence in it.
+ * If stop_rec is true and rec is true then JMenu there should be an option to start Reporting Feedback sequence in it.
+ * The JMenu will only be affected window of type JDialog and JFrame only.
+ * (Precondition: (window != null))
+ * @param window its the window that we want to set the GUI
+ */
+ public void setUI (Window window)
+ {
+ if (start_rec == true) {
+ if (window instanceof JFrame) {
+ JFrame a;
+ a = (JFrame) window;
+
+ JMenuBar menuBar;
+ menuBar = a.getJMenuBar();
+
+ if (menuBar != null) {
+
+ JMenu temp_menu;
+ temp_menu = menuBar.getMenu(menuBar.getMenuCount() - 1);
+ temp_menu.setBackground(new Color(176,209,217));
+ temp_menu.remove(0);
+
+ JMenuItem item;
+ ImageIcon icon;
+ icon = JarTools.getImage("camera.gif");
+ item = new JMenuItem(messages.getString("ScreenShot"),
+ new ImageIcon(icon.getImage().getScaledInstance
+ (20,20,Image.SCALE_SMOOTH)));
+ item.setActionCommand("ScreenShot");
+ item.setMnemonic(KeyEvent.VK_S);
+ item.addActionListener(this);
+ item.setAccelerator(shKey);
+ item.setToolTipText("Take screenshot of this window");
+
+ temp_menu.insert(item,0);
+ }
+
+ refreshLocationAndSize(a);
+ a.validate();
+ a.repaint();
+ }
+ else if (window instanceof JDialog) {
+
+ JDialog a;
+ a = (JDialog) window;
+ JMenuBar menuBar;
+ menuBar = a.getJMenuBar();
+
+ if (menuBar != null) {
+
+ JMenu temp_menu;
+ temp_menu = menuBar.getMenu(menuBar.getMenuCount() - 1);
+ temp_menu.setBackground(new Color(176,209,217));
+ temp_menu.remove(0);
+
+ JMenuItem item;
+ ImageIcon icon;
+ icon = JarTools.getImage("camera.gif");
+ item = new JMenuItem(messages.getString("ScreenShot"),
+ new ImageIcon(icon.getImage().getScaledInstance
+ (20,20,Image.SCALE_SMOOTH)));
+ item.setActionCommand("ScreenShot");
+ item.setMnemonic(KeyEvent.VK_S);
+ item.addActionListener(this);
+ item.setAccelerator(shKey);
+ item.setToolTipText("Take screenshot of this window");
+
+ temp_menu.insert(item,0);
+ }
+ refreshLocationAndSize(a);
+ a.validate();
+ a.repaint();
+ }
+ else if (window instanceof JWindow) {
+ JWindow a;
+ a = (JWindow) window;
+
+ ((JComponent) a.getContentPane()).unregisterKeyboardAction(fdKey);
+
+ ((JComponent) a.getContentPane()).registerKeyboardAction(this, "ScreenShot", shKey,JComponent.WHEN_IN_FOCUSED_WINDOW);
+ }
+ else
+ return;
+ }
+ else {
+
+ if ((stop_rec == true) && (rec == true)) {
+
+ if (window instanceof JFrame) {
+ JFrame a;
+ a = (JFrame) window;
+
+ JMenuBar menuBar;
+ menuBar = a.getJMenuBar();
+
+ if (menuBar != null) {
+
+ JMenu temp_menu;
+ temp_menu = menuBar.getMenu(menuBar.getMenuCount() - 1);
+ temp_menu.setBackground(new Color(204,204,204));
+ temp_menu.remove(0);
+
+ ImageIcon icon;
+ JMenuItem item;
+
+ icon = JarTools.getImage("feedback.gif");
+ item = new JMenuItem("Report Feedback", new ImageIcon(icon.getImage().getScaledInstance(20,20,Image.SCALE_SMOOTH)));
+ item.setActionCommand(messages.getString("SendFeedback"));
+ item.addActionListener(this);
+ item.setMnemonic(KeyEvent.VK_F);
+ item.setAccelerator(fdKey);
+ item.setToolTipText(messages.getString("SendFeedbackButton"));
+
+ temp_menu.insert(item,0);
+ }
+ a.setSize(a.getPreferredSize());
+ a.validate();
+ a.repaint();
+ }
+ else if (window instanceof JDialog) {
+ JDialog a;
+ a = (JDialog) window;
+
+ JMenuBar menuBar;
+ menuBar = a.getJMenuBar();
+
+ if (menuBar != null) {
+
+ JMenu temp_menu;
+ temp_menu = menuBar.getMenu(menuBar.getMenuCount() - 1);
+ temp_menu.setBackground(new Color(204,204,204));
+ temp_menu.remove(0);
+
+ ImageIcon icon;
+ JMenuItem item;
+
+ icon = JarTools.getImage("feedback.gif");
+ item = new JMenuItem("Report Feedback", new ImageIcon(icon.getImage().getScaledInstance(20,20,Image.SCALE_SMOOTH)));
+ item.setActionCommand(messages.getString("SendFeedback"));
+ item.addActionListener(this);
+ item.setMnemonic(KeyEvent.VK_F);
+ item.setAccelerator(fdKey);
+ item.setToolTipText(messages.getString("SendFeedbackButton"));
+
+ temp_menu.insert(item,0);
+ }
+ a.setSize(a.getPreferredSize());
+ a.validate();
+ a.repaint();
+ }
+ else if (window instanceof JWindow) {
+ JWindow a;
+ a = (JWindow) window;
+
+ ((JComponent) a.getContentPane()).registerKeyboardAction(this, messages.getString("SendFeedback"), fdKey, JComponent.WHEN_IN_FOCUSED_WINDOW);
+
+ ((JComponent) a.getContentPane()).unregisterKeyboardAction(shKey);
+
+ }
+ else
+ return;
+ }
+ else
+ return;
+ }
+ }
+
+ /**
+ * This will make an Window[] convert to ArrayList that contain list of Window.
+ * (Precondition: (windows != null))
+ * @param windows the array that we want to convert to ArrayList
+ * @return the array list that contains all object in array windows.
+ */
+ public ArrayList addToArrayList (Window[] windows,ArrayList arr)
+ {
+ int i;
+
+ for (i = 0 ; i < windows.length ; i++)
+ {
+ arr.add(windows[i]);
+ }
+
+ return arr;
+ }
+
+ /**
+ * This method will get all the information of the window that are currently open when the action
+ * occured and these windows are not the window where the action occured.
+ * It will add these windows information to the current record and it will also setting up the
+ * GUI for each of these windows.
+ * @param command the action's command user did.
+ */
+ public void saveState(String command)
+ {
+ Window[] frame_windows;
+ ArrayList windows;
+ Frame[] frames;
+ int i,j;
+
+ if (log == null)
+ return;
+
+ windows = new ArrayList();
+
+ frames = Frame.getFrames();
+ for (i = 0 ; i 30)
+ {
+ if (rec == false)
+ {
+ save_tempHistLog("history.log");
+ }
+ else
+ save_tempHistLog("feedbackhist.log");
+ vector.removeAllElements();
+ //vector.removeElementAt(vector.size() - 1);
+ System.gc();
+ }
+
+ if ((rec == true) && (stop_rec == true))
+ {
+ save_HistLog("feedbackhist.log");
+ vector.removeAllElements();
+ System.gc();
+ vector = getPrev_log("history.log");
+ File f2;
+ f2 = new File("feedbackhist.log");
+ f2.delete();
+ f2 = new File("temp_feedbackhist.log");
+ f2.delete();
+ start_rec = false;
+ stop_rec = true;
+ rec = false;
+ }
+
+ if (point != null)
+ {
+ point.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+ point.removeMouseListener(mouse_blocker_listener);
+ }
+ }
+
+ /**
+ * This method will save whatever in the vector at the moment to a file
+ * with the specified filename.
+ * Then it will also save the vector inside the file with the name "temp_"
+ * concatenated with the specified filename into it.
+ * (Precondition : (filename != null))
+ * @param filename its the name of the file where the vector should be saved.
+ */
+ public void save_HistLog(String filename)
+ {
+ try
+ {
+ File f;
+ f = new File(filename);
+ FileOutputStream fos;
+ fos = new FileOutputStream(f);
+ ObjectOutputStream oos;
+ oos = new ObjectOutputStream(fos);
+ oos.writeObject(vector);
+ vector.removeAllElements();
+ System.gc();
+ vector = getPrev_log("temp_"+ filename);
+ oos.writeObject(vector);
+ oos.close();
+ f = new File("temp_" + filename);
+ f.delete();
+ writeThread = null;
+ }
+ catch (IOException exp) {}
+ }
+
+ /**
+ * This method will save whatever in the vector at the moment to a file with a
+ * name "temp_" concatenated with the specified filename into it.
+ * (Precondition : (filename != null))
+ * @param filename its part of the name of the file where the vector should be saved.
+ */
+ public void save_tempHistLog(String filename)
+ {
+ try
+ {
+ File f;
+ f = new File("temp_"+ filename);
+ FileOutputStream fos;
+ fos = new FileOutputStream(f);
+ ObjectOutputStream oos;
+ oos = new ObjectOutputStream(fos);
+ oos.writeObject(vector);
+ oos.close();
+ }
+ catch (IOException exp) {}
+ }
+
+ /**
+ * This method will get vector that is stored in a file with the specified filename and
+ * it will also save the second vector in that file to a file with a name
+ * temp_history.log.
+ * If file with the specified filename does not exist then it will return a new vector.
+ * (Precondition : (filename != null))
+ * @param filename is the name of the file where we want to get the vector.
+ * @return the first vector saved in the file with the specified filename.
+ */
+ public Vector getPrev_log(String filename)
+ {
+ Vector stack;
+ ObjectInputStream ois;
+ stack = null;
+ try
+ {
+ File f;
+ f = new File(filename);
+ if (f.exists() == true)
+ {
+ FileInputStream fis;
+ fis = new FileInputStream(f);
+ ois = new ObjectInputStream(fis);
+ stack = (Vector) ois.readObject();
+
+ Vector stack2;
+ stack2 = null;
+
+ try
+ {
+ stack2 = (Vector) ois.readObject();
+ }
+ catch (EOFException e) {stack2 = null;}
+
+ if (stack2 != null)
+ {
+ File f2;
+ f2 = new File("temp_history.log");
+ FileOutputStream fos;
+ fos = new FileOutputStream(f2);
+ ObjectOutputStream oos;
+ oos = new ObjectOutputStream(fos);
+ oos.writeObject(stack2);
+ oos.close();
+ stack2.removeAllElements();
+ stack2 = null;
+ System.gc();
+ }
+
+ ois.close();
+ return stack;
+ }
+ else
+ stack = new Vector();
+ }
+ catch (IOException exp) {exp.printStackTrace();}
+ catch (ClassNotFoundException exp2) {System.out.println("class exp");}
+
+ return stack;
+ }
+
+ /**
+ * This method will make sure that there is something running at the moment.
+ */
+ private static void ensureEventThread()
+ {
+ if (SwingUtilities.isEventDispatchThread())
+ {
+ return;
+ }
+ throw new RuntimeException("something running");
+ }
+
+ /**
+ * This method will called the Conformation window to show up.
+ * @param err_array this is the array containing all the information taken when user doing the Feedback form.
+ * @param imgFile this is the array conataining the String representation of the images taken when user doing the
+ * Feedback form.
+ */
+ /*public void getVector(String[] err_array,String[] imgFile)
+ {
+ Conformation rd;
+ rd = new Conformation(vector,err_array,imgFile,messages);
+ return;
+ }*/
+
+ /**
+ * This method will get the vector that hold the history.
+ * @return the vector that hold the history stored so far.
+ */
+ public Vector getVector()
+ {
+ return vector;
+ }
+
+ /**
+ * This class will catch the window opened and window closed event.
+ * If an application open or close a window it will be listened by this class so the information
+ * about that window and all the other open window can be took.
+ */
+ class MyEventQueue extends EventQueue
+ {
+ /**
+ * This method will catch the window opened and window closed event.
+ * If an application open or close a window it will be listened by this class so the information
+ * about that window and all the other open window can be took.
+ * @param evt its the event.
+ */
+ protected void dispatchEvent(AWTEvent evt)
+ {
+ super.dispatchEvent(evt);
+ int id;
+ id = evt.getID();
+
+ if ((id == WindowEvent.WINDOW_OPENED)||(id == WindowEvent.WINDOW_CLOSED))
+ {
+ String command;
+ command = " ";
+
+ if (id == WindowEvent.WINDOW_OPENED) command = messages.getString("WindowOpened");
+ else if (id == WindowEvent.WINDOW_CLOSED) command = messages.getString("WindowClosed");
+
+ WindowEvent e;
+ e = (WindowEvent) evt;
+ Window comp;
+ comp = e.getWindow();
+
+ if ((comp instanceof ReportDetails.View)||
+ (comp instanceof Movie))
+ return;
+
+ saving(comp,command);
+ }
+ }
+ }
+
+ /**
+ * This method will take the screenshot of the whole screen.
+ * @param p the window where user select to take screenshot.
+ * @return the screenshot image.
+ */
+ //public byte[] recreateEvent (final Window p)
+ public BufferedImage recreateEvent (final Window p)
+ {
+ setup_UIManager();
+ Rectangle rect;
+ int w, h;
+
+ if (p != null)
+ {
+ w = p.getWidth();
+ h = p.getHeight();
+
+ try
+ {
+ rect = new Rectangle(p.getLocationOnScreen(),
+ new Dimension(w,h));
+ }
+ catch (IllegalComponentStateException exp)
+ {
+ rect = null;
+ }
+ }
+ else
+ {
+ rect = null;
+ }
+
+ if (rect != null)
+ {
+ //byte[] img;
+ BufferedImage img;
+ img = sh.captureScreen();
+
+ return img;
+ }
+ else
+ return null;
+ }
+
+ /**
+ * This method will listen to any action event that occured.
+ * It will allow this special listener class to record all the information inside all the
+ * window open when the action occured.
+ * @param e the ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ String command;
+ command = e.getActionCommand();
+ Component comp;
+ comp = (Component)e.getSource();
+
+ saving(comp,command);
+
+ if (myframe == null)
+ myframe = " ";
+
+ if("FastSend".equals(e.getActionCommand()))
+ {
+ point.addMouseListener(mouse_blocker_listener);
+ point.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+
+ BufferedImage img;
+ boolean modal;
+
+ modal = false;
+
+ img = recreateEvent(point);
+
+ if (stop_rec == true)
+ {
+ feedbackframe = myframe;
+
+ JDialog.setDefaultLookAndFeelDecorated(true);
+ stop_rec = false;
+ frame = null;
+ frame = new FeedbackInterface(sh,feedbackframe,messages,this);
+ if ((img != null) && (savefinish == true))
+ {
+ int x,y,w,h,iw,ih;
+ double wrat,hrat;
+ Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
+
+ iw = (int) (dim.getWidth() - 60);
+ ih = (int) (dim.getHeight() * 0.75) - 50;
+
+ wrat = iw/((double)dim.getWidth());
+ hrat = ih/((double)dim.getHeight());
+
+ w = (int) (point.getWidth() * wrat);
+ h = (int) (point.getHeight() * hrat);
+ x = (int) (point.getLocationOnScreen().x * wrat);
+ y = (int) (point.getLocationOnScreen().y * hrat);
+
+ //frame.addScreenPanel(feedbackframe,sh.getBuffImage(),img);
+ frame.addScreenPanel(feedbackframe,img,x,y,w,h);
+ }
+ frame.setVisible(false);
+ }
+ else
+ {
+ if ((img != null) &&(savefinish == true))
+ {
+ int x,y,w,h,iw,ih;
+ double wrat,hrat;
+ Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
+
+ iw = (int) (dim.getWidth() - 60);
+ ih = (int) (dim.getHeight() * 0.75) - 50;
+
+ wrat = iw/((double)dim.getWidth());
+ hrat = ih/((double)dim.getHeight());
+
+ w = (int) (point.getWidth() * wrat);
+ h = (int) (point.getHeight() * hrat);
+ x = (int) (point.getLocationOnScreen().x * wrat);
+ y = (int) (point.getLocationOnScreen().y * hrat);
+
+ //frame.addScreenPanel(feedbackframe,sh.getBuffImage(),img);
+ if (point instanceof JDialog)
+ modal = ((JDialog) point).isModal();
+
+ feedbackframe = myframe;
+ frame.addScreenPanel(feedbackframe,img,x,y,w,h);
+ }
+ }
+
+ ActionEvent evt;
+ evt = new ActionEvent(frame.send_button,ActionEvent.ACTION_PERFORMED,messages.getString("Send"));
+
+ frame.actionPerformed(evt);
+
+ point.removeMouseListener(mouse_blocker_listener);
+ point.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+ }
+
+ if(messages.getString("SendFeedback").equals(e.getActionCommand()))
+ {
+ setup_UIManager();
+ //byte[] img;
+ BufferedImage img;
+ boolean modal;
+
+ point.addMouseListener(mouse_blocker_listener);
+ point.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+
+ modal = false;
+
+ img = recreateEvent(point);
+ feedbackframe = myframe;
+ if (point instanceof JDialog)
+ modal = ((JDialog)point).isModal();
+
+ JDialog.setDefaultLookAndFeelDecorated(true);
+ stop_rec = false;
+ frame = null;
+ frame = new FeedbackInterface(sh,feedbackframe,messages,this);
+ if ((img != null) && (savefinish == true))
+ {
+ int x,y,w,h,iw,ih;
+ double wrat,hrat;
+ Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
+
+ iw = (int) (dim.getWidth() - 60);
+ ih = (int) (dim.getHeight() * 0.75) - 50;
+
+ wrat = iw/((double)dim.getWidth());
+ hrat = ih/((double)dim.getHeight());
+
+ w = (int) (point.getWidth() * wrat);
+ h = (int) (point.getHeight() * hrat);
+ x = (int) (point.getLocationOnScreen().x * wrat);
+ y = (int) (point.getLocationOnScreen().y * hrat);
+
+ //frame.addScreenPanel(feedbackframe,sh.getBuffImage(),img);
+ frame.addScreenPanel(feedbackframe,img,x,y,w,h);
+ frame.setComment(modal);
+
+ if (modal == true)
+ {
+ modalWindow = point;
+ }
+ }
+
+ point.removeMouseListener(mouse_blocker_listener);
+ point.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+ }
+
+ if ("ScreenShot".equals(e.getActionCommand()))
+ {
+ point.addMouseListener(mouse_blocker_listener);
+ point.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+
+ //byte[] img;
+ BufferedImage img;
+ boolean modal;
+ modal = false;
+ img = recreateEvent(point);
+
+ if ((img != null) &&(savefinish == true))
+ {
+ int x,y,w,h,iw,ih;
+ double wrat,hrat;
+ Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
+
+ iw = (int) (dim.getWidth() - 60);
+ ih = (int) (dim.getHeight() * 0.75) - 50;
+
+ wrat = iw/((double)dim.getWidth());
+ hrat = ih/((double)dim.getHeight());
+
+ w = (int) (point.getWidth() * wrat);
+ h = (int) (point.getHeight() * hrat);
+ x = (int) (point.getLocationOnScreen().x * wrat);
+ y = (int) (point.getLocationOnScreen().y * hrat);
+
+ //frame.addScreenPanel(feedbackframe,sh.getBuffImage(),img);
+ if (point instanceof JDialog)
+ modal = ((JDialog) point).isModal();
+
+ feedbackframe = myframe;
+ frame.addScreenPanel(feedbackframe,img,x,y,w,h);
+ frame.setComment(modal);
+
+ if (modal == true)
+ modalWindow = point;
+ }
+
+ point.removeMouseListener(mouse_blocker_listener);
+ point.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+ }
+
+ if (messages.getString("LookHistory").equals(e.getActionCommand()))
+ {
+ point.addMouseListener(mouse_blocker_listener);
+ point.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+
+ setup_UIManager();
+ ReportDetails rd;
+ rd = new ReportDetails(vector,null,null,messages,rec);
+
+ point.removeMouseListener(mouse_blocker_listener);
+ point.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+ }
+ }
+
+
+ /**
+ * This method will listen to any Change event that occured.
+ * It will allow this special listener class to record all the information inside all the
+ * window open when the action occured.
+ * @param e the ChangeEvent.
+ */
+ public void stateChanged(ChangeEvent e)
+ {
+ String command;
+ command = messages.getString("ChangeState");
+ Object obj;
+ obj = e.getSource();
+
+ Component comp;
+
+ if (obj instanceof Component)
+ {
+ comp = (Component)e.getSource();
+ }
+ else
+ {
+ if (modelHash.containsKey(obj) == true)
+ {
+ comp = (Component) modelHash.get(obj);
+ }
+ else
+ {
+ comp = null;
+ System.out.println("weird" + obj.toString());
+ }
+ }
+
+ saving(comp,command);
+ }
+
+ /**
+ * This method will listen to any tree selection event that occured.
+ * It will allow this special listener class to record all the information inside all the
+ * window open when the action occured.
+ * @param e the TreeSelectionEvent.
+ */
+ public void valueChanged(TreeSelectionEvent e)
+ {
+ MutableTreeNode node;
+ node = (MutableTreeNode)
+ (e.getPath().getLastPathComponent());
+
+ if (node == null) return;
+
+ Object obj;
+ obj = e.getSource();
+
+ Component comp;
+
+ if (obj instanceof Component)
+ {
+ comp = (Component)e.getSource();
+ }
+ else
+ {
+ if (modelHash.containsKey(obj) == true)
+ {
+ comp = (Component) modelHash.get(obj);
+ }
+ else
+ {
+ comp = null;
+ System.out.println("weird" + obj.toString());
+ }
+ }
+
+ String command;
+ command = messages.getString("Node") + " ";
+ if (node instanceof DefaultMutableTreeNode)
+ command = command + ((DefaultMutableTreeNode)node).getUserObject() + " ";
+ command = command + messages.getString("ValueChanged");
+
+ saving(comp,command);
+ }
+
+ /**
+ * This method will listen to any tree nodes change in the tree model event that occured.
+ * It will allow this special listener class to record all the information inside all the
+ * window open when the action occured.
+ * @param e the TreeModelEvent.
+ */
+ public void treeNodesChanged(TreeModelEvent e)
+ {
+ MutableTreeNode node;
+ node = (MutableTreeNode)
+ (e.getTreePath().getLastPathComponent());
+
+ try {
+ int index;
+ index = e.getChildIndices()[0];
+ node = (MutableTreeNode)(node.getChildAt(index));
+ } catch (NullPointerException exc) {}
+
+ Object obj;
+ obj = e.getSource();
+
+ Component comp;
+
+ if (obj instanceof Component)
+ {
+ comp = (Component)e.getSource();
+ }
+ else
+ {
+ if (modelHash.containsKey(obj) == true)
+ {
+ comp = (Component) modelHash.get(obj);
+ }
+ else
+ {
+ comp = null;
+ System.out.println("weird" + obj.toString());
+ }
+ }
+
+ String command;
+ command = messages.getString("Node");
+
+ if (node instanceof DefaultMutableTreeNode)
+ command = command + " " + ((DefaultMutableTreeNode)node).getUserObject();
+
+ command = command + " " + messages.getString("Change");
+
+ saving(comp,command);
+ }
+
+ /**
+ * This method will listen to any tree nodes inserted in the tree model event that occured.
+ * It will allow this special listener class to record all the information inside all the
+ * window open when the action occured.
+ * @param e the TreeModelEvent.
+ */
+ public void treeNodesInserted(TreeModelEvent e)
+ {
+ MutableTreeNode node;
+ node = (MutableTreeNode)
+ (e.getTreePath().getLastPathComponent());
+
+ try {
+ int index;
+ index = e.getChildIndices()[0];
+ node = (MutableTreeNode)(node.getChildAt(index));
+ } catch (NullPointerException exc) {}
+
+ Object obj;
+ obj = e.getSource();
+
+ Component comp;
+
+ if (obj instanceof Component)
+ {
+ comp = (Component)e.getSource();
+ }
+ else
+ {
+ if (modelHash.containsKey(obj) == true)
+ {
+ comp = (Component) modelHash.get(obj);
+ }
+ else
+ {
+ comp = null;
+ System.out.println("weird" + obj.toString());
+ }
+ }
+
+ String command;
+ command = messages.getString("Node");
+ if (node instanceof DefaultMutableTreeNode)
+ command = command + " " + ((DefaultMutableTreeNode)node).getUserObject();
+
+ command = command + " " + messages.getString("Inserted");
+
+ saving(comp,command);
+ }
+
+ /**
+ * This method will listen to any tree nodes removed in the tree model event that occured.
+ * It will allow this special listener class to record all the information inside all the
+ * window open when the action occured.
+ * @param e the TreeModelEvent.
+ */
+ public void treeNodesRemoved(TreeModelEvent e)
+ {
+ MutableTreeNode node;
+ node = (MutableTreeNode)
+ (e.getTreePath().getLastPathComponent());
+
+ try {
+ int index;
+ index = e.getChildIndices()[0];
+ node = (MutableTreeNode)(node.getChildAt(index));
+ } catch (NullPointerException exc) {}
+
+ Object obj;
+ obj = e.getSource();
+
+ Component comp;
+
+ if (obj instanceof Component)
+ {
+ comp = (Component)e.getSource();
+ }
+ else
+ {
+ if (modelHash.containsKey(obj) == true)
+ {
+ comp = (Component) modelHash.get(obj);
+ }
+ else
+ {
+ comp = null;
+ System.out.println("weird" + obj.toString());
+ }
+ }
+
+ String command;
+ command = messages.getString("Node");
+ if (node instanceof DefaultMutableTreeNode)
+ command = command + " " + ((DefaultMutableTreeNode)node).getUserObject();
+ command = command + " " + messages.getString("Removed");
+
+ saving(comp,command);
+ }
+
+ /**
+ * This method will listen to any tree structure change in the tree model event that occured.
+ * It will allow this special listener class to record all the information inside all the
+ * window open when the action occured.
+ * @param e the TreeModelEvent.
+ */
+ public void treeStructureChanged(TreeModelEvent e)
+ {
+ Object obj;
+ obj = e.getSource();
+
+ Component comp;
+
+ if (obj instanceof Component)
+ {
+ comp = (Component)e.getSource();
+ }
+ else
+ {
+ if (modelHash.containsKey(obj) == true)
+ {
+ comp = (Component) modelHash.get(obj);
+ }
+ else
+ {
+ comp = null;
+ System.out.println("weird" + obj.toString());
+ }
+ }
+
+ String command;
+ command = messages.getString("TreeStructuredChanged");
+
+ saving(comp,command);
+ }
+
+ /**
+ * This method will listen to any item state change in the item event that occured.
+ * It will allow this special listener class to record all the information inside all the
+ * window open when the action occured.
+ * @param e the ItemEvent.
+ */
+ public void itemStateChanged(ItemEvent e)
+ {
+ Component comp;
+ comp = (Component) e.getSource();
+ String command;
+ command = ((String) e.getItem()) + " " +
+ messages.getString("ItemStateChanged");
+
+ saving(comp,command);
+ }
+
+ /**
+ * This method will listen to any contents change in the list data event that occured.
+ * It will allow this special listener class to record all the information inside all the
+ * window open when the action occured.
+ * @param e the ListDataEvent.
+ */
+ public void contentsChanged (ListDataEvent e)
+ {
+ Object obj;
+ obj = e.getSource();
+
+ Component comp;
+
+ if (obj instanceof Component)
+ {
+ comp = (Component)e.getSource();
+ }
+ else
+ {
+ if (modelHash.containsKey(obj) == true)
+ {
+ comp = (Component) modelHash.get(obj);
+ }
+ else
+ {
+ comp = null;
+ System.out.println("weird" + obj.toString());
+ }
+ }
+
+ String command;
+ command = messages.getString("ListChanged");
+
+ saving(comp,command);
+ }
+
+ /**
+ * This method will listen to any interval added in the list data event that occured.
+ * It will allow this special listener class to record all the information inside all the
+ * window open when the action occured.
+ * @param e the ListDataEvent.
+ */
+ public void intervalAdded (ListDataEvent e)
+ {
+ Object obj;
+ obj = e.getSource();
+
+ Component comp;
+
+ if (obj instanceof Component)
+ {
+ comp = (Component)e.getSource();
+ }
+ else
+ {
+ if (modelHash.containsKey(obj) == true)
+ {
+ comp = (Component) modelHash.get(obj);
+ }
+ else
+ {
+ comp = null;
+ System.out.println("weird" + obj.toString());
+ }
+ }
+
+ String command;
+ command = messages.getString("ListContentsAdded");
+
+ saving(comp,command);
+ }
+
+ /**
+ * This method will listen to any interval removed in the list data event that occured.
+ * It will allow this special listener class to record all the information inside all the
+ * window open when the action occured.
+ * @param e the ListDataEvent.
+ */
+ public void intervalRemoved (ListDataEvent e)
+ {
+ Object obj;
+ obj = e.getSource();
+
+ Component comp;
+
+ if (obj instanceof Component)
+ {
+ comp = (Component)e.getSource();
+ }
+ else
+ {
+ if (modelHash.containsKey(obj) == true)
+ {
+ comp = (Component) modelHash.get(obj);
+ }
+ else
+ {
+ comp = null;
+ System.out.println("weird" + obj.toString());
+ }
+ }
+
+ String command;
+ command = messages.getString("ListContentRemoved");
+
+ saving(comp,command);
+ }
+
+ /**
+ * This method will listen to any value changed in the list data event that occured.
+ * It will allow this special listener class to record all the information inside all the
+ * window open when the action occured.
+ * @param e the ListDataEvent.
+ */
+ public void valueChanged (ListSelectionEvent e)
+ {
+ Object obj;
+ obj = e.getSource();
+
+ Component comp;
+
+ if (obj instanceof Component)
+ {
+ comp = (Component)e.getSource();
+ }
+ else
+ {
+ if (modelHash.containsKey(obj) == true)
+ {
+ comp = (Component) modelHash.get(obj);
+ }
+ else
+ {
+ comp = null;
+ System.out.println("weird" + obj.toString());
+ }
+ }
+
+ String command;
+ command = messages.getString("ListValueChanged");
+
+ saving(comp,command);
+ }
+
+ /**
+ * This method will listen to any menu canceled in the menu event that occured.
+ * It will allow this special listener class to record all the information inside all the
+ * window open when the action occured.
+ * @param e the MenuEvent.
+ */
+ public void menuCanceled (MenuEvent e)
+ {
+ Component comp;
+ comp = (Component) e.getSource();
+ String command;
+ command = messages.getString("MenuCanceled");
+
+ saving(comp,command);
+ }
+
+ /**
+ * This method will listen to any menu deselected in the menu event that occured.
+ * It will allow this special listener class to record all the information inside all the
+ * window open when the action occured.
+ * @param e the MenuEvent.
+ */
+ public void menuDeselected (MenuEvent e)
+ {
+ Component comp;
+ comp = (Component) e.getSource();
+ String command;
+ command = messages.getString("MenuDeselected");
+
+ saving(comp,command);
+ }
+
+ /**
+ * This method will listen to any menu selected in the menu event that occured.
+ * It will allow this special listener class to record all the information inside all the
+ * window open when the action occured.
+ * @param e the MenuEvent.
+ */
+ public void menuSelected(MenuEvent e)
+ {
+ Component comp;
+ comp = (Component) e.getSource();
+ String command;
+ command = messages.getString("MenuSelected");
+
+ saving(comp,command);
+ }
+
+ /**
+ * This method will listen to any column added in the table column model event that occured.
+ * It will allow this special listener class to record all the information inside all the
+ * window open when the action occured.
+ * @param e the TableColumnModelEvent.
+ */
+ public void columnAdded (TableColumnModelEvent e)
+ {
+ Object obj;
+ obj = e.getSource();
+
+ Component comp;
+
+ if (obj instanceof Component)
+ {
+ comp = (Component)e.getSource();
+ }
+ else
+ {
+ if (modelHash.containsKey(obj) == true)
+ {
+ comp = (Component) modelHash.get(obj);
+ }
+ else
+ {
+ comp = null;
+ System.out.println("weird" + obj.toString());
+ }
+ }
+
+ String command;
+ command = messages.getString("ColumnIndex") + " " + e.getToIndex()
+ + messages.getString("AddedToTable");
+
+ saving(comp,command);
+ }
+
+ public void columnMarginChanged(ChangeEvent e) {}
+
+ /**
+ * This method will listen to any column moved in the table column model event that occured.
+ * It will allow this special listener class to record all the information inside all the
+ * window open when the action occured.
+ * @param e the TableColumnModelEvent.
+ */
+ public void columnMoved(TableColumnModelEvent e)
+ {
+ Object obj;
+ obj = e.getSource();
+
+ Component comp;
+
+ if (obj instanceof Component)
+ {
+ comp = (Component)e.getSource();
+ }
+ else
+ {
+ if (modelHash.containsKey(obj) == true)
+ {
+ comp = (Component) modelHash.get(obj);
+ }
+ else
+ {
+ comp = null;
+ System.out.println("weird" + obj.toString());
+ }
+ }
+
+ String command;
+ command = messages.getString("ColumnIndexFrom") + " " +
+ e.getFromIndex() + " " +
+ messages.getString("MovedToColumnIndex") + " " + e.getToIndex() +
+ " " + messages.getString("InTheTable");
+
+ saving(comp,command);
+ }
+
+ /**
+ * This method will listen to any column removed in the table column model event that occured.
+ * It will allow this special listener class to record all the information inside all the
+ * window open when the action occured.
+ * @param e the TableColumnModelEvent.
+ */
+ public void columnRemoved(TableColumnModelEvent e)
+ {
+ Object obj;
+ obj = e.getSource();
+
+ Component comp;
+
+ if (obj instanceof Component)
+ {
+ comp = (Component)e.getSource();
+ }
+ else
+ {
+ if (modelHash.containsKey(obj) == true)
+ {
+ comp = (Component) modelHash.get(obj);
+ }
+ else
+ {
+ comp = null;
+ System.out.println("weird" + obj.toString());
+ }
+ }
+
+ String command;
+ command = messages.getString("ColumnIndex") + " " + e.getFromIndex() +
+ " " + messages.getString("RemovedFromTheTable");
+
+ saving(comp,command);
+ }
+
+ public void columnSelectionChanged(ListSelectionEvent e) {}
+}
+
+
+
+
+
+
+
+
+
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/Base64.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/Base64.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/Base64.java (revision 31635)
@@ -0,0 +1,1289 @@
+package org.greenstone.gatherer.feedback;
+
+import java.io.*;
+/**
+ * Encodes and decodes to and from Base64 notation.
+ *
+ *
+ * Change Log:
+ *
+ *
+ * v2.0.1 - Fixed an error when decoding a single byte, that is, when the
+ * encoded data was a single byte.
+ * v2.0 - I got rid of methods that used booleans to set options.
+ * Now everything is more consolidated and cleaner. The code now detects
+ * when data that's being decoded is gzip-compressed and will decompress it
+ * automatically. Generally things are cleaner. You'll probably have to
+ * change some method calls that you were making to support the new
+ * options format (int s that you "OR" together).
+ * v1.5.1 - Fixed bug when decompressing and decoding to a
+ * byte[] using decode( String s, boolean gzipCompressed ) .
+ * Added the ability to "suspend" encoding in the Output Stream so
+ * you can turn on and off the encoding if you need to embed base64
+ * data in an otherwise "normal" stream (like an XML file).
+ * v1.5 - Output stream pases on flush() command but doesn't do anything itself.
+ * This helps when using GZIP streams.
+ * Added the ability to GZip-compress objects before encoding them.
+ * v1.4 - Added helper methods to read/write files.
+ * v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.
+ * v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream
+ * where last buffer being read, if not completely full, was not returned.
+ * v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.
+ * v1.3.3 - Fixed I/O streams which were totally messed up.
+ *
+ *
+ *
+ * I am placing this code in the Public Domain. Do with it as you will.
+ * This software comes with no guarantees or warranties but with
+ * plenty of well-wishing instead!
+ * Please visit http://iharder.net/base64
+ * periodically to check for updates or to contribute improvements.
+ *
+ *
+ * @author Robert Harder
+ * @author rob@iharder.net
+ * @version 2.0
+ */
+public class Base64
+{
+
+/* ******** P U B L I C F I E L D S ******** */
+
+
+ /** No options specified. Value is zero. */
+ public final static int NO_OPTIONS = 0;
+
+ /** Specify encoding. */
+ public final static int ENCODE = 1;
+
+
+ /** Specify decoding. */
+ public final static int DECODE = 0;
+
+
+ /** Specify that data should be gzip-compressed. */
+ public final static int GZIP = 2;
+
+
+ /** Don't break lines when encoding (violates strict Base64 specification) */
+ public final static int DONT_BREAK_LINES = 8;
+
+
+/* ******** P R I V A T E F I E L D S ******** */
+
+
+ /** Maximum line length (76) of Base64 output. */
+ private final static int MAX_LINE_LENGTH = 76;
+
+
+ /** The equals sign (=) as a byte. */
+ private final static byte EQUALS_SIGN = (byte)'=';
+
+
+ /** The new line character (\n) as a byte. */
+ private final static byte NEW_LINE = (byte)'\n';
+
+
+ /** The 64 valid Base64 values. */
+ private final static byte[] ALPHABET =
+ {
+ (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
+ (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
+ (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
+ (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
+ (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
+ (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
+ (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
+ (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
+ (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
+ (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
+ };
+
+ /**
+ * Translates a Base64 value to either its 6-bit reconstruction value
+ * or a negative number indicating some other meaning.
+ **/
+ private final static byte[] DECODABET =
+ {
+ -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8
+ -5,-5, // Whitespace: Tab and Linefeed
+ -9,-9, // Decimal 11 - 12
+ -5, // Whitespace: Carriage Return
+ -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26
+ -9,-9,-9,-9,-9, // Decimal 27 - 31
+ -5, // Whitespace: Space
+ -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42
+ 62, // Plus sign at decimal 43
+ -9,-9,-9, // Decimal 44 - 46
+ 63, // Slash at decimal 47
+ 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine
+ -9,-9,-9, // Decimal 58 - 60
+ -1, // Equals sign at decimal 61
+ -9,-9,-9, // Decimal 62 - 64
+ 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N'
+ 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z'
+ -9,-9,-9,-9,-9,-9, // Decimal 91 - 96
+ 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm'
+ 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z'
+ -9,-9,-9,-9 // Decimal 123 - 126
+ /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
+ -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
+ -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
+ -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
+ -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
+ -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
+ -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
+ -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
+ -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
+ -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
+ };
+
+ private final static byte BAD_ENCODING = -9; // Indicates error in encoding
+ private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
+ private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
+
+
+ /** Defeats instantiation. */
+ private Base64(){}
+
+
+
+/* ******** E N C O D I N G M E T H O D S ******** */
+
+
+ /**
+ * Encodes the first three bytes of array threeBytes
+ * and returns a four-byte array in Base64 notation.
+ *
+ * @param threeBytes the array to convert
+ * @return four byte array in Base64 notation.
+ * @since 1.3
+ */
+ private static byte[] encode3to4( byte[] threeBytes )
+ {
+ return encode3to4( threeBytes, 3 );
+ } // end encodeToBytes
+
+
+
+ /**
+ * Encodes up to the first three bytes of array threeBytes
+ * and returns a four-byte array in Base64 notation.
+ * The actual number of significant bytes in your array is
+ * given by numSigBytes .
+ * The array threeBytes needs only be as big as
+ * numSigBytes .
+ *
+ * @param threeBytes the array to convert
+ * @param numSigBytes the number of significant bytes in your array
+ * @return four byte array in Base64 notation.
+ * @since 1.3
+ */
+ private static byte[] encode3to4( byte[] threeBytes, int numSigBytes )
+ {
+ byte[] dest = new byte[4];
+ encode3to4( threeBytes, 0, numSigBytes, dest, 0 );
+ return dest;
+ }
+
+ /**
+ * Encodes up to the first three bytes of array threeBytes
+ * and returns a four-byte array in Base64 notation.
+ * The actual number of significant bytes in your array is
+ * given by numSigBytes .
+ * The array threeBytes needs only be as big as
+ * numSigBytes .
+ * Code can reuse a byte array by passing a four-byte array as b4 .
+ *
+ * @param b4 A reusable byte array to reduce array instantiation
+ * @param threeBytes the array to convert
+ * @param numSigBytes the number of significant bytes in your array
+ * @return four byte array in Base64 notation.
+ * @since 1.5.1
+ */
+ private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes )
+ {
+ encode3to4( threeBytes, 0, numSigBytes, b4, 0 );
+ return b4;
+ } // end encode3to4
+
+
+ /**
+ * Encodes up to three bytes of the array source
+ * and writes the resulting four Base64 bytes to destination .
+ * The source and destination arrays can be manipulated
+ * anywhere along their length by specifying
+ * srcOffset and destOffset .
+ * This method does not check to make sure your arrays
+ * are large enough to accomodate srcOffset + 3 for
+ * the source array or destOffset + 4 for
+ * the destination array.
+ * The actual number of significant bytes in your array is
+ * given by numSigBytes .
+ *
+ * @param source the array to convert
+ * @param srcOffset the index where conversion begins
+ * @param numSigBytes the number of significant bytes in your array
+ * @param destination the array to hold the conversion
+ * @param destOffset the index where output will be put
+ * @return the destination array
+ * @since 1.3
+ */
+ private static byte[] encode3to4(
+ byte[] source, int srcOffset, int numSigBytes,
+ byte[] destination, int destOffset )
+ {
+ // 1 2 3
+ // 01234567890123456789012345678901 Bit position
+ // --------000000001111111122222222 Array position from threeBytes
+ // --------| || || || | Six bit groups to index ALPHABET
+ // >>18 >>12 >> 6 >> 0 Right shift necessary
+ // 0x3f 0x3f 0x3f Additional AND
+
+ // Create buffer with zero-padding if there are only one or two
+ // significant bytes passed in the array.
+ // We have to shift left 24 in order to flush out the 1's that appear
+ // when Java treats a value as negative that is cast from a byte to an int.
+ int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 )
+ | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 )
+ | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 );
+
+ switch( numSigBytes )
+ {
+ case 3:
+ destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
+ destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
+ destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ];
+ destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ];
+ return destination;
+
+ case 2:
+ destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
+ destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
+ destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ];
+ destination[ destOffset + 3 ] = EQUALS_SIGN;
+ return destination;
+
+ case 1:
+ destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
+ destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
+ destination[ destOffset + 2 ] = EQUALS_SIGN;
+ destination[ destOffset + 3 ] = EQUALS_SIGN;
+ return destination;
+
+ default:
+ return destination;
+ } // end switch
+ } // end encode3to4
+
+
+
+ /**
+ * Serializes an object and returns the Base64-encoded
+ * version of that serialized object. If the object
+ * cannot be serialized or there is another error,
+ * the method will return null .
+ * The object is not GZip-compressed before being encoded.
+ *
+ * @param serializableObject The object to encode
+ * @return The Base64-encoded object
+ * @since 1.4
+ */
+ public static String encodeObject( java.io.Serializable serializableObject )
+ {
+ return encodeObject( serializableObject, NO_OPTIONS );
+ } // end encodeObject
+
+
+
+ /**
+ * Serializes an object and returns the Base64-encoded
+ * version of that serialized object. If the object
+ * cannot be serialized or there is another error,
+ * the method will return null .
+ *
+ * Valid options:
+ * GZIP: gzip-compresses object before encoding it.
+ * DONT_BREAK_LINES: don't break lines at 76 characters
+ * Note: Technically, this makes your encoding non-compliant.
+ *
+ *
+ * Example: encodeObject( myObj, Base64.GZIP )
or
+ *
+ * Example: encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )
+ *
+ * @param serializableObject The object to encode
+ * @param options Specified options
+ * @return The Base64-encoded object
+ * @see Base64#GZIP
+ * @see Base64#DONT_BREAK_LINES
+ * @since 2.0
+ */
+ public static String encodeObject( java.io.Serializable serializableObject, int options )
+ {
+ // Streams
+ java.io.ByteArrayOutputStream baos = null;
+ java.io.OutputStream b64os = null;
+ java.io.ObjectOutputStream oos = null;
+ java.util.zip.GZIPOutputStream gzos = null;
+
+ // Isolate options
+ int gzip = (options & GZIP);
+ int dontBreakLines = (options & DONT_BREAK_LINES);
+
+ try
+ {
+ // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
+ baos = new java.io.ByteArrayOutputStream();
+ b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines );
+
+ // GZip?
+ if( gzip == GZIP )
+ {
+ gzos = new java.util.zip.GZIPOutputStream( b64os );
+ oos = new java.io.ObjectOutputStream( gzos );
+ } // end if: gzip
+ else
+ oos = new java.io.ObjectOutputStream( b64os );
+
+ oos.writeObject( serializableObject );
+ } // end try
+ catch( java.io.IOException e )
+ {
+ e.printStackTrace();
+ return null;
+ } // end catch
+ finally
+ {
+ try{ oos.close(); } catch( Exception e ){}
+ try{ gzos.close(); } catch( Exception e ){}
+ try{ b64os.close(); } catch( Exception e ){}
+ try{ baos.close(); } catch( Exception e ){}
+ } // end finally
+
+ return new String( baos.toByteArray() );
+ } // end encode
+
+
+
+ /**
+ * Encodes a byte array into Base64 notation.
+ * Does not GZip-compress data.
+ *
+ * @param source The data to convert
+ * @since 1.4
+ */
+ public static String encodeBytes( byte[] source )
+ {
+ return encodeBytes( source, 0, source.length, NO_OPTIONS );
+ } // end encodeBytes
+
+ /**
+ * Added this variant of the method in so that RemoteGreenstoneServer.java
+ * can process large source and target filenames, without Base64 encoding
+ * them introducing a new line.
+ * @param source The data to convert
+ * @since January
+ */
+ public static String encodeBytesInSingleLine( byte[] source)
+ {
+ return encodeBytes( source, 0, source.length, DONT_BREAK_LINES);
+ } // end encodeBytes
+
+ /**
+ * Encodes a byte array into Base64 notation.
+ *
+ * Valid options:
+ * GZIP: gzip-compresses object before encoding it.
+ * DONT_BREAK_LINES: don't break lines at 76 characters
+ * Note: Technically, this makes your encoding non-compliant.
+ *
+ *
+ * Example: encodeBytes( myData, Base64.GZIP )
or
+ *
+ * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )
+ *
+ *
+ * @param source The data to convert
+ * @param options Specified options
+ * @see Base64#GZIP
+ * @see Base64#DONT_BREAK_LINES
+ * @since 2.0
+ */
+ public static String encodeBytes( byte[] source, int options )
+ {
+ return encodeBytes( source, 0, source.length, options );
+ } // end encodeBytes
+
+
+ /**
+ * Encodes a byte array into Base64 notation.
+ * Does not GZip-compress data.
+ *
+ * @param source The data to convert
+ * @param off Offset in array where conversion should begin
+ * @param len Length of data to convert
+ * @since 1.4
+ */
+ public static String encodeBytes( byte[] source, int off, int len )
+ {
+ return encodeBytes( source, off, len, NO_OPTIONS );
+ } // end encodeBytes
+
+
+
+ /**
+ * Encodes a byte array into Base64 notation.
+ *
+ * Valid options:
+ * GZIP: gzip-compresses object before encoding it.
+ * DONT_BREAK_LINES: don't break lines at 76 characters
+ * Note: Technically, this makes your encoding non-compliant.
+ *
+ *
+ * Example: encodeBytes( myData, Base64.GZIP )
or
+ *
+ * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )
+ *
+ *
+ * @param source The data to convert
+ * @param off Offset in array where conversion should begin
+ * @param len Length of data to convert
+ * @param options Specified options
+ * @see Base64#GZIP
+ * @see Base64#DONT_BREAK_LINES
+ * @since 2.0
+ */
+ public static String encodeBytes( byte[] source, int off, int len, int options )
+ {
+ // Isolate options
+ int dontBreakLines = ( options & DONT_BREAK_LINES );
+ int gzip = ( options & GZIP );
+
+ // Compress?
+ if( gzip == GZIP )
+ {
+ java.io.ByteArrayOutputStream baos = null;
+ java.util.zip.GZIPOutputStream gzos = null;
+ Base64.OutputStream b64os = null;
+
+
+ try
+ {
+ // GZip -> Base64 -> ByteArray
+ baos = new java.io.ByteArrayOutputStream();
+ b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines );
+ gzos = new java.util.zip.GZIPOutputStream( b64os );
+
+ gzos.write( source, off, len );
+ gzos.close();
+ } // end try
+ catch( java.io.IOException e )
+ {
+ e.printStackTrace();
+ return null;
+ } // end catch
+ finally
+ {
+ try{ gzos.close(); } catch( Exception e ){}
+ try{ b64os.close(); } catch( Exception e ){}
+ try{ baos.close(); } catch( Exception e ){}
+ } // end finally
+
+ return new String( baos.toByteArray() );
+ } // end if: compress
+
+ // Else, don't compress. Better not to use streams at all then.
+ else
+ {
+ // Convert option to boolean in way that code likes it.
+ boolean breakLines = dontBreakLines == 0;
+
+ int len43 = len * 4 / 3;
+ byte[] outBuff = new byte[ ( len43 ) // Main 4:3
+ + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding
+ + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines
+ int d = 0;
+ int e = 0;
+ int len2 = len - 2;
+ int lineLength = 0;
+ for( ; d < len2; d+=3, e+=4 )
+ {
+ encode3to4( source, d+off, 3, outBuff, e );
+
+ lineLength += 4;
+ if( breakLines && lineLength == MAX_LINE_LENGTH )
+ {
+ outBuff[e+4] = NEW_LINE;
+ e++;
+ lineLength = 0;
+ } // end if: end of line
+ } // en dfor: each piece of array
+
+ if( d < len )
+ {
+ encode3to4( source, d+off, len - d, outBuff, e );
+ e += 4;
+ } // end if: some padding needed
+
+ return new String( outBuff, 0, e );
+ } // end else: don't compress
+
+ } // end encodeBytes
+
+
+
+
+
+/* ******** D E C O D I N G M E T H O D S ******** */
+
+
+ /**
+ * Decodes the first four bytes of array fourBytes
+ * and returns an array up to three bytes long with the
+ * decoded values.
+ *
+ * @param fourBytes the array with Base64 content
+ * @return array with decoded values
+ * @since 1.3
+ */
+ private static byte[] decode4to3( byte[] fourBytes )
+ {
+ byte[] outBuff1 = new byte[3];
+ int count = decode4to3( fourBytes, 0, outBuff1, 0 );
+ byte[] outBuff2 = new byte[ count ];
+
+ for( int i = 0; i < count; i++ )
+ outBuff2[i] = outBuff1[i];
+
+ return outBuff2;
+ }
+
+
+
+
+ /**
+ * Decodes four bytes from array source
+ * and writes the resulting bytes (up to three of them)
+ * to destination .
+ * The source and destination arrays can be manipulated
+ * anywhere along their length by specifying
+ * srcOffset and destOffset .
+ * This method does not check to make sure your arrays
+ * are large enough to accomodate srcOffset + 4 for
+ * the source array or destOffset + 3 for
+ * the destination array.
+ * This method returns the actual number of bytes that
+ * were converted from the Base64 encoding.
+ *
+ *
+ * @param source the array to convert
+ * @param srcOffset the index where conversion begins
+ * @param destination the array to hold the conversion
+ * @param destOffset the index where output will be put
+ * @return the number of decoded bytes converted
+ * @since 1.3
+ */
+ private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset )
+ {
+ // Example: Dk==
+ if( source[ srcOffset + 2] == EQUALS_SIGN )
+ {
+ // Two ways to do the same thing. Don't know which way I like best.
+ //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
+ // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
+ int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
+ | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 );
+
+ destination[ destOffset ] = (byte)( outBuff >>> 16 );
+ return 1;
+ }
+
+ // Example: DkL=
+ else if( source[ srcOffset + 3 ] == EQUALS_SIGN )
+ {
+ // Two ways to do the same thing. Don't know which way I like best.
+ //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
+ // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
+ // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
+ int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
+ | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
+ | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 );
+
+ destination[ destOffset ] = (byte)( outBuff >>> 16 );
+ destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 );
+ return 2;
+ }
+
+ // Example: DkLE
+ else
+ {
+ try{
+ // Two ways to do the same thing. Don't know which way I like best.
+ //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
+ // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
+ // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
+ // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
+ int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
+ | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
+ | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6)
+ | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) );
+
+
+ destination[ destOffset ] = (byte)( outBuff >> 16 );
+ destination[ destOffset + 1 ] = (byte)( outBuff >> 8 );
+ destination[ destOffset + 2 ] = (byte)( outBuff );
+
+ return 3;
+ }catch( Exception e){
+ System.out.println(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset ] ] ) );
+ System.out.println(""+source[srcOffset+1]+ ": " + ( DECODABET[ source[ srcOffset + 1 ] ] ) );
+ System.out.println(""+source[srcOffset+2]+ ": " + ( DECODABET[ source[ srcOffset + 2 ] ] ) );
+ System.out.println(""+source[srcOffset+3]+ ": " + ( DECODABET[ source[ srcOffset + 3 ] ] ) );
+ return -1;
+ } //e nd catch
+ }
+ } // end decodeToBytes
+
+
+
+
+ /**
+ * Very low-level access to decoding ASCII characters in
+ * the form of a byte array. Does not support automatically
+ * gunzipping or any other "fancy" features.
+ *
+ * @param source The Base64 encoded data
+ * @param off The offset of where to begin decoding
+ * @param len The length of characters to decode
+ * @return decoded data
+ * @since 1.3
+ */
+ public static byte[] decode( byte[] source, int off, int len )
+ {
+ int len34 = len * 3 / 4;
+ byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output
+ int outBuffPosn = 0;
+
+ byte[] b4 = new byte[4];
+ int b4Posn = 0;
+ int i = 0;
+ byte sbiCrop = 0;
+ byte sbiDecode = 0;
+ for( i = off; i < off+len; i++ )
+ {
+ sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
+ sbiDecode = DECODABET[ sbiCrop ];
+
+ if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better
+ {
+ if( sbiDecode >= EQUALS_SIGN_ENC )
+ {
+ b4[ b4Posn++ ] = sbiCrop;
+ if( b4Posn > 3 )
+ {
+ outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn );
+ b4Posn = 0;
+
+ // If that was the equals sign, break out of 'for' loop
+ if( sbiCrop == EQUALS_SIGN )
+ break;
+ } // end if: quartet built
+
+ } // end if: equals sign or better
+
+ } // end if: white space, equals sign or better
+ else
+ {
+ System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" );
+ return null;
+ } // end else:
+ } // each input character
+
+ byte[] out = new byte[ outBuffPosn ];
+ System.arraycopy( outBuff, 0, out, 0, outBuffPosn );
+ return out;
+ } // end decode
+
+
+
+
+ /**
+ * Decodes data from Base64 notation, automatically
+ * detecting gzip-compressed data and decompressing it.
+ *
+ * @param s the string to decode
+ * @return the decoded data
+ * @since 1.4
+ */
+ public static byte[] decode( String s )
+ {
+ byte[] bytes = s.getBytes();
+ bytes = decode( bytes, 0, bytes.length );
+
+ // Check to see if it's gzip-compressed
+ // GZIP Magic Two-Byte Number: 0x8b1f (35615)
+ if( bytes.length >= 2 )
+ {
+
+ int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
+ if(
+ bytes != null && // In case decoding returned null
+ bytes.length >= 4 && // Don't want to get ArrayIndexOutOfBounds exception
+ java.util.zip.GZIPInputStream.GZIP_MAGIC == head )
+ {
+ java.io.ByteArrayInputStream bais = null;
+ java.util.zip.GZIPInputStream gzis = null;
+ java.io.ByteArrayOutputStream baos = null;
+ byte[] buffer = new byte[2048];
+ int length = 0;
+
+ try
+ {
+ baos = new java.io.ByteArrayOutputStream();
+ bais = new java.io.ByteArrayInputStream( bytes );
+ gzis = new java.util.zip.GZIPInputStream( bais );
+
+ while( ( length = gzis.read( buffer ) ) >= 0 )
+ {
+ baos.write(buffer,0,length);
+ } // end while: reading input
+
+ // No error? Get new bytes.
+ bytes = baos.toByteArray();
+
+ } // end try
+ catch( java.io.IOException e )
+ {
+ // Just return originally-decoded bytes
+ } // end catch
+ finally
+ {
+ try{ baos.close(); } catch( Exception e ){}
+ try{ gzis.close(); } catch( Exception e ){}
+ try{ bais.close(); } catch( Exception e ){}
+ } // end finally
+
+ } // end if: gzipped
+ } // end if: bytes.length >= 2
+
+ return bytes;
+ } // end decode
+
+
+
+
+ /**
+ * Attempts to decode Base64 data and deserialize a Java
+ * Object within. Returns null if there was an error.
+ *
+ * @param encodedObject The Base64 data to decode
+ * @return The decoded and deserialized object
+ * @since 1.5
+ */
+ public static Object decodeToObject( String encodedObject )
+ {
+ // Decode and gunzip if necessary
+ byte[] objBytes = decode( encodedObject );
+
+ java.io.ByteArrayInputStream bais = null;
+ java.io.ObjectInputStream ois = null;
+ Object obj = null;
+
+ try
+ {
+ bais = new java.io.ByteArrayInputStream( objBytes );
+ ois = new java.io.ObjectInputStream( bais );
+
+ obj = ois.readObject();
+ } // end try
+ catch( java.io.IOException e )
+ {
+ e.printStackTrace();
+ obj = null;
+ } // end catch
+ catch( java.lang.ClassNotFoundException e )
+ {
+ e.printStackTrace();
+ obj = null;
+ } // end catch
+ finally
+ {
+ try{ bais.close(); } catch( Exception e ){}
+ try{ ois.close(); } catch( Exception e ){}
+ } // end finally
+
+ return obj;
+ } // end decodeObject
+
+
+ /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */
+
+
+
+ /**
+ * It will read data from another
+ * {@link java.io.InputStream}, given in the constructor,
+ * and encode/decode to/from Base64 notation on the fly.
+ *
+ * @see Base64
+ * @see java.io.FilterInputStream
+ * @since 1.3
+ */
+ public static class InputStream extends java.io.FilterInputStream
+ {
+ private int options; // Options specified
+ private boolean encode; // Encoding or decoding
+ private int position; // Current position in the buffer
+ private byte[] buffer; // Small buffer holding converted data
+ private int bufferLength; // Length of buffer (3 or 4)
+ private int numSigBytes; // Number of meaningful bytes in the buffer
+ private int lineLength;
+ private boolean breakLines; // Break lines at less than 80 characters
+
+
+ /**
+ * Constructs a InputStream in DECODE mode.
+ *
+ * @param in the {@link java.io.InputStream} from which to read data.
+ * @since 1.3
+ */
+ public InputStream( java.io.InputStream in )
+ {
+ this( in, DECODE );
+ } // end constructor
+
+
+ /**
+ * Constructs a InputStream in
+ * either ENCODE or DECODE mode.
+ *
+ * Valid options:
+ * ENCODE or DECODE: Encode or Decode as data is read.
+ * DONT_BREAK_LINES: don't break lines at 76 characters
+ * (only meaningful when encoding)
+ * Note: Technically, this makes your encoding non-compliant.
+ *
+ *
+ * Example: new Base64.InputStream( in, Base64.DECODE )
+ *
+ *
+ * @param in the {@link java.io.InputStream} from which to read data.
+ * @param options Specified options
+ * @see Base64#ENCODE
+ * @see Base64#DECODE
+ * @see Base64#DONT_BREAK_LINES
+ * @since 2.0
+ */
+ public InputStream( java.io.InputStream in, int options )
+ {
+ super( in );
+ this.options = options;
+ this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
+ this.encode = (options & ENCODE) == ENCODE;
+ this.bufferLength = encode ? 4 : 3;
+ this.buffer = new byte[ bufferLength ];
+ this.position = -1;
+ this.lineLength = 0;
+ } // end constructor
+
+ /**
+ * Reads enough of the input stream to convert
+ * to/from Base64 and returns the next byte.
+ *
+ * @return next byte
+ * @since 1.3
+ */
+ public int read() throws java.io.IOException
+ {
+ // Do we need to get data?
+ if( position < 0 )
+ {
+ if( encode )
+ {
+ byte[] b3 = new byte[3];
+ int numBinaryBytes = 0;
+ for( int i = 0; i < 3; i++ )
+ {
+ try
+ {
+ int b = in.read();
+
+ // If end of stream, b is -1.
+ if( b >= 0 )
+ {
+ b3[i] = (byte)b;
+ numBinaryBytes++;
+ } // end if: not end of stream
+
+ } // end try: read
+ catch( java.io.IOException e )
+ {
+ // Only a problem if we got no data at all.
+ if( i == 0 )
+ throw e;
+
+ } // end catch
+ } // end for: each needed input byte
+
+ if( numBinaryBytes > 0 )
+ {
+ encode3to4( b3, 0, numBinaryBytes, buffer, 0 );
+ position = 0;
+ numSigBytes = 4;
+ } // end if: got data
+ else
+ {
+ return -1;
+ } // end else
+ } // end if: encoding
+
+ // Else decoding
+ else
+ {
+ byte[] b4 = new byte[4];
+ int i = 0;
+ for( i = 0; i < 4; i++ )
+ {
+ // Read four "meaningful" bytes:
+ int b = 0;
+ do{ b = in.read(); }
+ while( b >= 0 && DECODABET[ b & 0x7f ] <= WHITE_SPACE_ENC );
+
+ if( b < 0 )
+ break; // Reads a -1 if end of stream
+
+ b4[i] = (byte)b;
+ } // end for: each needed input byte
+
+ if( i == 4 )
+ {
+ numSigBytes = decode4to3( b4, 0, buffer, 0 );
+ position = 0;
+ } // end if: got four characters
+ else if( i == 0 ){
+ return -1;
+ } // end else if: also padded correctly
+ else
+ {
+ // Must have broken out from above.
+ throw new java.io.IOException( "Improperly padded Base64 input." );
+ } // end
+
+ } // end else: decode
+ } // end else: get data
+
+ // Got data?
+ if( position >= 0 )
+ {
+ // End of relevant data?
+ if( /*!encode &&*/ position >= numSigBytes )
+ return -1;
+
+ if( encode && breakLines && lineLength >= MAX_LINE_LENGTH )
+ {
+ lineLength = 0;
+ return '\n';
+ } // end if
+ else
+ {
+ lineLength++; // This isn't important when decoding
+ // but throwing an extra "if" seems
+ // just as wasteful.
+
+ int b = buffer[ position++ ];
+
+ if( position >= bufferLength )
+ position = -1;
+
+ return b & 0xFF; // This is how you "cast" a byte that's
+ // intended to be unsigned.
+ } // end else
+ } // end if: position >= 0
+
+ // Else error
+ else
+ {
+ // When JDK1.4 is more accepted, use an assertion here.
+ throw new java.io.IOException( "Error in Base64 code reading stream." );
+ } // end else
+ } // end read
+
+
+ /**
+ * Calls {@link #read} repeatedly until the end of stream
+ * is reached or len bytes are read.
+ * Returns number of bytes read into array or -1 if
+ * end of stream is encountered.
+ *
+ * @param dest array to hold values
+ * @param off offset for array
+ * @param len max number of bytes to read into array
+ * @return bytes read into array or -1 if end of stream is encountered.
+ * @since 1.3
+ */
+ public int read( byte[] dest, int off, int len ) throws java.io.IOException
+ {
+ int i;
+ int b;
+ for( i = 0; i < len; i++ )
+ {
+ b = read();
+
+ //if( b < 0 && i == 0 )
+ // return -1;
+
+ if( b >= 0 )
+ dest[off + i] = (byte)b;
+ else if( i == 0 )
+ return -1;
+ else
+ break; // Out of 'for' loop
+ } // end for: each byte read
+ return i;
+ } // end read
+
+ } // end inner class InputStream
+
+
+
+
+
+
+ /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
+
+
+
+ /**
+ * A OutputStream will write data to another
+ * {@link java.io.OutputStream}, given in the constructor,
+ * and encode/decode to/from Base64 notation on the fly.
+ *
+ * @see Base64
+ * @see java.io.FilterOutputStream
+ * @since 1.3
+ */
+ public static class OutputStream extends java.io.FilterOutputStream
+ {
+ private int options;
+ private boolean encode;
+ private int position;
+ private byte[] buffer;
+ private int bufferLength;
+ private int lineLength;
+ private boolean breakLines;
+ private byte[] b4; // Scratch used in a few places
+ private boolean suspendEncoding;
+
+ /**
+ * Constructs a OutputStream in ENCODE mode.
+ *
+ * @param out the {@link java.io.OutputStream} to which data will be written.
+ * @since 1.3
+ */
+ public OutputStream( java.io.OutputStream out )
+ {
+ this( out, ENCODE );
+ } // end constructor
+
+
+ /**
+ * Constructs a OutputStream in
+ * either ENCODE or DECODE mode.
+ *
+ * Valid options:
+ * ENCODE or DECODE: Encode or Decode as data is read.
+ * DONT_BREAK_LINES: don't break lines at 76 characters
+ * (only meaningful when encoding)
+ * Note: Technically, this makes your encoding non-compliant.
+ *
+ *
+ * Example: new Base64.OutputStream( out, Base64.ENCODE )
+ *
+ * @param out the {@link java.io.OutputStream} to which data will be written.
+ * @param options Specified options.
+ * @see Base64#ENCODE
+ * @see Base64#DECODE
+ * @see Base64#DONT_BREAK_LINES
+ * @since 1.3
+ */
+ public OutputStream( java.io.OutputStream out, int options )
+ {
+ super( out );
+ this.options = options;
+ this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
+ this.encode = (options & ENCODE) == ENCODE;
+ this.bufferLength = encode ? 3 : 4;
+ this.buffer = new byte[ bufferLength ];
+ this.position = 0;
+ this.lineLength = 0;
+ this.suspendEncoding = false;
+ this.b4 = new byte[4];
+ } // end constructor
+
+
+ /**
+ * Writes the byte to the output stream after
+ * converting to/from Base64 notation.
+ * When encoding, bytes are buffered three
+ * at a time before the output stream actually
+ * gets a write() call.
+ * When decoding, bytes are buffered four
+ * at a time.
+ *
+ * @param theByte the byte to write
+ * @since 1.3
+ */
+ public void write(int theByte) throws java.io.IOException
+ {
+ // Encoding suspended?
+ if( suspendEncoding )
+ {
+ super.out.write( theByte );
+ return;
+ } // end if: supsended
+
+ // Encode?
+ if( encode )
+ {
+ buffer[ position++ ] = (byte)theByte;
+ if( position >= bufferLength ) // Enough to encode.
+ {
+ out.write( encode3to4( b4, buffer, bufferLength ) );
+
+ lineLength += 4;
+ if( breakLines && lineLength >= MAX_LINE_LENGTH )
+ {
+ out.write( NEW_LINE );
+ lineLength = 0;
+ } // end if: end of line
+
+ position = 0;
+ } // end if: enough to output
+ } // end if: encoding
+
+ // Else, Decoding
+ else
+ {
+ // Meaningful Base64 character?
+ if( DECODABET[ theByte & 0x7f ] > WHITE_SPACE_ENC )
+ {
+ buffer[ position++ ] = (byte)theByte;
+ if( position >= bufferLength ) // Enough to output.
+ {
+ int len = Base64.decode4to3( buffer, 0, b4, 0 );
+ out.write( b4, 0, len );
+ //out.write( Base64.decode4to3( buffer ) );
+ position = 0;
+ } // end if: enough to output
+ } // end if: meaningful base64 character
+ else if( DECODABET[ theByte & 0x7f ] != WHITE_SPACE_ENC )
+ {
+ throw new java.io.IOException( "Invalid character in Base64 data." );
+ } // end else: not white space either
+ } // end else: decoding
+ } // end write
+
+
+
+ /**
+ * Calls {@link #write} repeatedly until len
+ * bytes are written.
+ *
+ * @param theBytes array from which to read bytes
+ * @param off offset for array
+ * @param len max number of bytes to read into array
+ * @since 1.3
+ */
+ public void write( byte[] theBytes, int off, int len ) throws java.io.IOException
+ {
+ // Encoding suspended?
+ if( suspendEncoding )
+ {
+ super.out.write( theBytes, off, len );
+ return;
+ } // end if: supsended
+
+ for( int i = 0; i < len; i++ )
+ {
+ write( theBytes[ off + i ] );
+ } // end for: each byte written
+
+ } // end write
+
+
+
+ /**
+ * Method added by PHIL. [Thanks, PHIL. -Rob]
+ * This pads the buffer without closing the stream.
+ */
+ public void flushBase64() throws java.io.IOException
+ {
+ if( position > 0 )
+ {
+ if( encode )
+ {
+ out.write( encode3to4( b4, buffer, position ) );
+ position = 0;
+ } // end if: encoding
+ else
+ {
+ throw new java.io.IOException( "Base64 input not properly padded." );
+ } // end else: decoding
+ } // end if: buffer partially full
+
+ } // end flush
+
+
+ /**
+ * Flushes and closes (I think, in the superclass) the stream.
+ *
+ * @since 1.3
+ */
+ public void close() throws java.io.IOException
+ {
+ // 1. Ensure that pending characters are written
+ flushBase64();
+
+ // 2. Actually close the stream
+ // Base class both flushes and closes.
+ super.close();
+
+ buffer = null;
+ out = null;
+ } // end close
+
+
+
+ /**
+ * Suspends encoding of the stream.
+ * May be helpful if you need to embed a piece of
+ * base640-encoded data in a stream.
+ *
+ * @since 1.5.1
+ */
+ public void suspendEncoding() throws java.io.IOException
+ {
+ flushBase64();
+ this.suspendEncoding = true;
+ } // end suspendEncoding
+
+
+ /**
+ * Resumes encoding of the stream.
+ * May be helpful if you need to embed a piece of
+ * base640-encoded data in a stream.
+ *
+ * @since 1.5.1
+ */
+ public void resumeEncoding()
+ {
+ this.suspendEncoding = false;
+ } // end resumeEncoding
+
+
+
+ } // end inner class OutputStream
+
+
+} // end class Base64
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/CombineGraphs.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/CombineGraphs.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/CombineGraphs.java (revision 31635)
@@ -0,0 +1,138 @@
+/** This class is not currently used and therefore will not be compiled by makegli. */
+
+package org.greenstone.gatherer.feedback;
+
+import java.io.*;
+import javax.imageio.*;
+import java.awt.*;
+import java.awt.image.*;
+import com.sun.image.codec.jpeg.*;
+import javax.swing.*;
+
+/**
+ * CombineGraphs class is a class to combine 2 images file into 1 jpeg file
+ * called test.jpg using point by point comparison.
+ *
User need to supplied the valid exist filenames of the 2 images that
+ * they want to combine.If in any one point in 2nd image file is not transparent
+ * then that point will be drawn to the 1st image file.
+ * To run this class properly the format is :
+ * java CombineGraphs filename1 filename2
+ * where filename1 is the names of the first image file.
+ * and filename2 is the names of the second image file. This file must
+ * contains transparent.
+ * For example: If user wants to combine the screenshot image file with the
+ * image file of the scribble lines, then
+ * filename1 will be the filename of the screenshot image file, and
+ * filename2 will be the filename of the scribble lines image file.
+ *
+ * @author Veronica Liesaputra
+ */
+
+public class CombineGraphs extends JPanel
+{
+ /**
+ * This is a variable to stored the image that is stored inside f1.
+ * After the combine method called this variable will store the resulted image of
+ * combining 2 images together.
+ */
+ private BufferedImage bi1;
+
+ /**
+ * This is a variable to stored the image that is stored inside f2.
+ */
+ private BufferedImage bi2;
+
+ /**
+ * This is a variable to stored the first image file that the user supplied.
+ */
+ private File f1;
+
+ /**
+ * This is a variable to stored the second image file that the user supplied.
+ */
+ private File f2;
+
+ /**
+ * It will read the image files user supplied and stored the image
+ * to the appropriate bufferedimage variable.
+ * (Precondition: (file1 != null) && (file2 != null))
+ * @param file1 the name of the first image file.
+ * @param file2 the name of the second image file.
+ */
+ public CombineGraphs(String file1,String file2)
+ {
+ try
+ {
+ f1 = new File(file1);
+ f2 = new File(file2);
+ bi1 = ImageIO.read(f1);
+ bi2 = ImageIO.read(f2);
+ }
+ catch(IOException exp) {}
+ }
+
+ /**
+ * This method will combine the 2 buffered images into 1 buffered image and
+ * saved it to an output file in jpeg format.
+ * (Precondition: (filename != null))
+ * @param filename the name of the output file.
+ * (Postcondition: the combined image is stored in a jpeg file with the name
+ * as specified in filename.)
+ */
+ public void combine(String filename)
+ {
+
+ /*using bi1 as the base layer that we use to compare point by point with bi2.
+ if in one point in bi2 its not transparent then we draw that point to bi1.*/
+ Graphics2D G1;
+
+ G1 = bi1.createGraphics();
+
+ int width = bi2.getWidth();
+ int height = bi2.getHeight();
+
+ for (int y = 0 ; y < height ; y++)
+ {
+ for (int x = 0 ; x < width ; x++)
+ {
+ int rgb = bi2.getRGB(x,y);
+ if (rgb != 0)
+ {
+ G1.setColor(new Color(rgb,true));
+ G1.drawLine(x,y,x,y);
+ }
+ }
+ }
+
+ // Save the combined bufferedimage into a jpeg output file.
+ try {
+ File file = new File(filename);
+ FileOutputStream out = new FileOutputStream(file);
+
+ JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
+ JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(bi1);
+ param.setQuality(1.0f, false);
+ encoder.setJPEGEncodeParam(param);
+ encoder.encode(bi1);
+
+ } catch (Exception ex) {}
+ }
+
+ /**
+ * This the main method that need 2 inputs from the user for it to
+ * work properly.
+ * The inputs are the 2 image filenames that user want to combine.
+ */
+ public static void main (String[] args)
+ {
+ CombineGraphs cb = new CombineGraphs(args[0],args[1]);
+ cb.combine("test.jpg");
+ }
+}
+
+
+
+
+
+
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/CompGroup.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/CompGroup.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/CompGroup.java (revision 31635)
@@ -0,0 +1,60 @@
+package org.greenstone.gatherer.feedback;
+
+import java.io.*;
+
+/**
+ * The instance of this class will hold a UserComponent and the status of the UserComponent.
+ * For each of the CompGroup instance will contains all the information and the status of
+ * a window that is opened by the application the user used.
+ * @author Veronica Liesaputra
+ */
+public class CompGroup implements Serializable
+{
+ /**
+ * This variable will hold the status of the window that is opened by the application.
+ */
+ private String comp_status;
+
+ /**
+ * This variable will hold the information about the window that is opened by the
+ * application.
+ */
+ private UserComponent comp;
+
+ public CompGroup ()
+ {
+
+ }
+
+ /**
+ * It will make CompGroup instance that hold the information and status of the window
+ * thats is opened by the application.
+ * @param curr_comp information about the window opened by the application.
+ * @param curr_status status of the window opened by the application.
+ */
+ public CompGroup (UserComponent curr_comp,String curr_status)
+ {
+ comp_status = curr_status;
+ comp = curr_comp;
+ }
+
+ /**
+ * This method will return the status of the window opened by the application.
+ * @return the status of the window.
+ */
+ public String getStatus ()
+ {
+ return comp_status;
+ }
+
+ /**
+ * This method will return the information about the window opened by the application.
+ * @return the information about the window.
+ */
+ public UserComponent getComponent()
+ {
+ return comp;
+ }
+}
+
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/CompListener.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/CompListener.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/CompListener.java (revision 31635)
@@ -0,0 +1,944 @@
+package org.greenstone.gatherer.feedback;
+
+import org.omg.IOP.ComponentIdHelper;
+import java.awt.*;
+import java.io.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.tree.*;
+import javax.swing.table.*;
+import javax.swing.text.*;
+import java.awt.image.*;
+import javax.imageio.ImageIO;
+import javax.swing.plaf.*;
+import javax.swing.plaf.basic.*;
+import javax.swing.colorchooser.*;
+import java.awt.event.*;
+import javax.swing.event.*;
+import java.text.MessageFormat;
+import javax.swing.plaf.metal.MetalIconFactory.*;
+import javax.swing.plaf.metal.MetalIconFactory;
+import javax.swing.plaf.basic.BasicInternalFrameTitlePane;
+import javax.swing.plaf.metal.MetalInternalFrameTitlePane;
+
+/**
+ * In general, this class will get add special listener
+ * to a Container and of all the components inside it.
+ * This class will also add special listener to the Container and each of the components inside
+ * the Container. So, it will allows special listener to record and listen each of the actions
+ * the user do to the Container or any of the components inside it. The special listener
+ * is an instance of ActionRecorderDialog.
+ * This class will only recognized all the components that is belongs to the javax.swing package
+ * which are all the implementing classes of RootPaneContainer interface and all the direct known
+ * subclasses of JComponent. All the other components will be stated simply as Container
+ * and user cannot really get much information out of it.
+ * The added special listener to this component is as what its allowed in API, if the component
+ * have some modified listener added to it then that action will not be known and recorded by
+ * the special listener.
+ *
+ * @author Veronica Liesaputra
+ */
+public class CompListener
+{
+ /**
+ * This variable will hold the special listener that will listen and record any of the
+ * actions user do to the Container and any of the components inside it.
+ */
+ private ActionRecorderDialog m;
+
+ /**
+ * This variable will hold the pair of the component and the model of the component.
+ * With this variable allows ActionRecorderDialog listener to know which component the model that fires an action
+ * belongs to. In this HashMap the model is the key and the component where the model belongs to is
+ * the value.
+ */
+ private HashMap hash;
+
+ /**
+ * This constructor will set the special listener, the locale and the hashmap to be used in
+ * this Container.
+ * (Precondition: dialog != null)
+ * @param dialog its the special listener that will listen and record all the action
+ * user do to any of the component inside the Container.
+ * @param h its the HashMap that will hold the pair of model with the component it belongs.
+ */
+ public CompListener
+ (ActionRecorderDialog dialog,HashMap h)
+ {
+ m = dialog;
+ hash = h;
+ }
+
+ /**
+ * If the JTextComponent is a JTextField then it will add the special action listener to it.
+ * (Precondition: txt != null)
+ * @param txt its the JTextComponent we want to get all the information from.
+ */
+ public void getTextAreaInfo(JTextComponent txt)
+ {
+ if (txt instanceof JTextField)
+ {
+ ((JTextField)txt).removeActionListener(m);
+ ((JTextField)txt).addActionListener(m);
+ }
+ }
+
+ /**
+ * This method will add the special action listener to the JButton.
+ * (Precondition: button != null)
+ * @param button its the JButton we want to get the information from.
+ */
+ public void getButtonInfo (JButton button)
+ {
+ button.removeActionListener(m);
+ button.addActionListener(m);
+ }
+
+ /**
+ * This method will get add the special change listener to the JProgressBar.
+ * (Precondition: bar != null)
+ * @param bar its the JProgressBar we want to get information from.
+ */
+ public void getProgressBarInfo (JProgressBar bar)
+ {
+ bar.removeChangeListener(m);
+ bar.addChangeListener(m);
+ }
+
+ /**
+ * This method will add the special change listener to the JSlider.
+ * (Precondition: slider != null)
+ * @param slider its the JSlider we want to get information from.
+ */
+ public void getSliderInfo (JSlider slider)
+ {
+ slider.removeChangeListener(m);
+ slider.addChangeListener(m);
+ }
+
+ /**
+ * This method will add the special change listener to the JSpinner.
+ * (Precondition: spinner != null)
+ * @param spinner its the JSpinner we want to the information from.
+ */
+ public void getSpinnerInfo (JSpinner spinner)
+ {
+ spinner.removeChangeListener(m);
+ spinner.addChangeListener(m);
+ }
+
+ /**
+ * Dummy method to get JLabel.
+ */
+ public void getLabelInfo(JLabel lbl)
+ {}
+
+ /**
+ * This method will add the special action listener to the JComboBox.
+ * (Precondition: box != null)
+ * @param box its the JComboBox we want to get information from.
+ */
+ public void getComboBoxInfo(JComboBox box)
+ {
+ ComboBoxModel cm;
+ cm = box.getModel();
+
+ box.removeActionListener(m);
+ box.addActionListener(m);
+ }
+
+ /**
+ * This method will add the special action listener to the JToggleButton.
+ * (Precondition: button != null)
+ * @param button its a JToggleButton inside the window.
+ */
+ public void getToggleButtonInfo (JToggleButton button)
+ {
+ button.removeActionListener(m);
+ button.addActionListener(m);
+ }
+
+ /**
+ * This method will add the special action listener to the JMenuItem.
+ * (Precondition: menuitem != null)
+ * @param menuitem its the JMenuItem we want to get the information from.
+ */
+ public void getMenuItemInfo (JMenuItem menuitem)
+ {
+ menuitem.removeActionListener(m);
+ menuitem.addActionListener(m);
+ }
+
+ /**
+ * This method will get all the components inside JMenuBar.
+ * (Precondition: menubar != null)
+ * @param menubar its the JMenuBar we want to get the information from.
+ */
+ public void getMenuBarInfo (JMenuBar menubar)
+ {
+ int i;
+ for (i= 0 ; i < menubar.getMenuCount() ; i++)
+ {
+ getInside(menubar.getMenu(i));
+ }
+ }
+
+ /**
+ * This method will add the special action and menu listener to the JMenu and
+ * get the components inside JMenu.
+ * (Precondition: menu != null)
+ * @param menu its the JMenu we want to the the information from.
+ */
+ public void getMenuInfo (JMenu menu)
+ {
+ menu.removeActionListener(m);
+ menu.removeMenuListener(m);
+ menu.addActionListener(m);
+ menu.addMenuListener(m);
+
+ int i;
+ for (i= 0 ; i < menu.getMenuComponentCount() ; i++)
+ {
+ getInside((Container)menu.getMenuComponent(i));
+ }
+ }
+
+ /**
+ * This method will get all the components inside the JApplet.
+ * (Precondition: applet != null)
+ * @param applet its the JApplet we want to get the information from.
+ */
+ public void getAppletInfo (JApplet applet)
+ {
+ Component[] group;
+ group = applet.getComponents();
+ int i;
+
+ if (group != null)
+ {
+ for (i = 0; i < group.length ; i++)
+ {
+ getInside((Container)applet.getComponent(i));
+ }
+ }
+ }
+
+ /**
+ * This method will get all the components inside the JWindow.
+ * (Precondition: window != null)
+ * @param window its the JWindow we want to get the information from.
+ */
+ public void getWindowInfo (JWindow window)
+ {
+ Component[] group;
+ group = window.getComponents();
+ int i;
+
+ if (group != null)
+ {
+ for (i = 0; i < group.length ; i++)
+ {
+ getInside((Container)window.getComponent(i));
+ }
+ }
+ }
+
+ /**
+ * This method will get all the components inside the JDialog.
+ * (Precondition: dialog != null)
+ * @param dialog its the JDialog we want to get the information from.
+ */
+ public void getDialogInfo (JDialog dialog)
+ {
+ Component[] group;
+ group = dialog.getComponents();
+ int i;
+
+ if (group != null)
+ {
+ for (i = 0; i < group.length ; i++)
+ {
+ getInside((Container)dialog.getComponent(i));
+ }
+ }
+ }
+
+ /**
+ * This method will get all the components inside the JInternalFrame.
+ * (Precondition: frame != null)
+ * @param frame its the JInternalFrame we want to get the information from.
+ */
+ public void getInternalFrameInfo (JInternalFrame frame)
+ {
+ Component[] group;
+ group = frame.getComponents();
+ int i;
+
+ if (group != null)
+ {
+ for (i = 0; i < group.length ; i++)
+ {
+ getInside((Container)frame.getComponent(i));
+ }
+ }
+ }
+
+ /**
+ * This method will get all the components inside the JFrame.
+ * (Precondition: frame != null)
+ * @param frame its the JFrame we want to get the information from.
+ */
+ public void getFrameInfo (JFrame frame)
+ {
+ Component[] group;
+ group = frame.getComponents();
+ int i;
+
+ if (group != null)
+ {
+ for (i = 0; i < group.length ; i++)
+ {
+ getInside((Container)frame.getComponent(i));
+ }
+ }
+ }
+
+ /**
+ * This method will get all the components inside the JPanel.
+ * (Precondition: pane != null)
+ * @param pane its the JPanel we want to get the information from.
+ */
+ public void getPanelInfo (JPanel pane)
+ {
+ Component[] group;
+ group = pane.getComponents();
+ int i;
+
+ if (group != null)
+ {
+ for (i = 0; i < group.length ; i++)
+ {
+ getInside((Container)pane.getComponent(i));
+ }
+ }
+ }
+
+ /**
+ * This method will get all the components inside the JScrollPane.
+ * (Precondition: scroll != null)
+ * @param scroll its the JScrollPane we want to get the information from.
+ */
+ public void getScrollInfo (JScrollPane scroll)
+ {
+ Component[] group;
+ group = scroll.getComponents();
+ int i;
+
+ if (group != null)
+ {
+ for (i = 0; i < group.length ; i++)
+ {
+ getInside((Container)group[i]);
+ }
+ }
+ }
+
+ /**
+ * This method will add special change listener to the JTabbedPane and get all
+ * the components inside it.
+ * (Precondition: tab != null)
+ * @param tab its the JTabbedPane we want to get the information from.
+ */
+ public void getTabbedPaneInfo (JTabbedPane tab)
+ {
+ int j;
+
+ tab.removeChangeListener(m);
+ tab.addChangeListener(m);
+
+ for (j = 0 ; j < tab.getTabCount(); j++)
+ {
+
+ Component group = tab.getComponentAt(j);
+ getInside((Container)group);
+
+ }
+ }
+
+ /**
+ * This is the dummy method to ge the option pane info.
+ */
+ public void getOptionPaneInfo (JOptionPane op)
+ {}
+
+ /**
+ * This method will get components inside the JViewPort.
+ * (Precondition: vp != null)
+ * @param vp its the JViewPort we want to get the information from.
+ */
+ public void getViewPortInfo (JViewport vp)
+ {
+ Component[] group;
+ group = vp.getComponents();
+ int i;
+
+ if (group != null)
+ {
+ for (i = 0; i < group.length ; i++)
+ {
+ getInside((Container)group[i]);
+ }
+ }
+ }
+
+ /**
+ * This method will get components inside the Box.
+ * (Precondition: box != null)
+ * @param box its the Box we want to get the information from.
+ */
+ public void getBoxInfo (Box box)
+ {
+ Component[] group;
+ group = box.getComponents();
+ int i;
+
+ if (group != null)
+ {
+ for (i = 0; i < group.length ; i++)
+ {
+ getInside((Container)group[i]);
+ }
+ }
+ }
+
+ /**
+ * This method will get components inside the JSplitPane.
+ * (Precondition: split != null)
+ * @param split its the JSplitPane we want to get the information from.
+ */
+ public void getSplitPaneInfo (JSplitPane split)
+ {
+ Component[] group;
+ group = split.getComponents();
+ int i;
+
+ if (group != null)
+ {
+ for (i = 0; i < group.length ; i++)
+ {
+ getInside((Container)group[i]);
+ }
+ }
+ }
+
+ /**
+ * This method will get add special list selection listener to the JList.
+ * (Precondition: list != null)
+ * @param list its the JList we want to get the information from.
+ */
+ public void getListInfo (JList list)
+ {
+ ListModel ls;
+ ls = list.getModel();
+
+ list.removeListSelectionListener(m);
+ list.addListSelectionListener(m);
+
+ if (ls != null)
+ {
+ if (hash.containsKey(ls) == false)
+ hash.put(ls,list);
+ ls.removeListDataListener(m);
+ ls.addListDataListener(m);
+ }
+ }
+
+ /**
+ * This method will add special change listener to the JColorChooser.
+ * (Precondition: color != null)
+ * @param color its the JColorChooser we want to get the information from.
+ */
+ public void getColorInfo (JColorChooser color)
+ {
+ ColorSelectionModel cm;
+ cm = color.getSelectionModel();
+
+ if (cm != null)
+ {
+ if (hash.containsKey(cm) == false)
+ hash.put(cm,color);
+ cm.removeChangeListener(m);
+ cm.addChangeListener(m);
+ }
+
+ getInside((Container) color.getPreviewPanel());
+ }
+
+ /**
+ * This method will add special action listener to the JFileChooser.
+ * (Precondition: fc != null)
+ * @param fc its the JFileChooser we want to get the information from.
+ */
+ public void getFileChooserInfo (JFileChooser fc)
+ {
+ fc.removeActionListener(m);
+ fc.addActionListener(m);
+ }
+
+ /**
+ * This method will add special list selection and column model listener
+ * to the JTable.
+ * (Precondition: table != null)
+ * @param table its the JTable we want to get the information from.
+ */
+ public void getTableInfo (JTable table)
+ {
+ TableModel tm;
+ tm = table.getModel();
+
+ TableColumnModel tcm;
+ tcm = table.getColumnModel();
+ ListSelectionModel lm;
+ lm = table.getSelectionModel();
+
+ if (lm != null)
+ {
+ if (hash.containsKey(lm) == false)
+ hash.put(lm,table);
+ lm.removeListSelectionListener(m);
+ lm.addListSelectionListener(m);
+ }
+
+ if (tcm != null)
+ {
+ if (hash.containsKey(tcm) == false)
+ hash.put(tcm,table);
+ tcm.removeColumnModelListener(m);
+ tcm.addColumnModelListener(m);
+ }
+ }
+
+ /**
+ * This is a dummy method to get the JScrollBar.
+ */
+ public void getScrollBarInfo (JScrollBar scroll)
+ {
+ }
+
+ /**
+ * This method will add special tree selection listener to the JTree.
+ * (Precondition: tree != null)
+ * @param tree its the JTree we want to get the information from.
+ */
+ public void getTreeInfo (JTree tree)
+ {
+ TreeModel tm;
+ tm = tree.getModel();
+
+ tree.removeTreeSelectionListener(m);
+ tree.addTreeSelectionListener(m);
+
+ if (tm != null)
+ {
+ if (hash.containsKey(tm) == false)
+ hash.put(tm,tree);
+ tm.removeTreeModelListener(m);
+ tm.addTreeModelListener(m);
+ }
+ }
+
+ /**
+ * This method is a dummy method to get Container.
+ */
+ public void getCompInfo (Container pane)
+ {
+ }
+
+ /**
+ * This method will get all components inside the JPopupMenu.
+ * (Precondition: popup != null)
+ * @param popup its the JPopUpMenu we want to get the information from.
+ */
+ public void getPopUpMenuInfo (JPopupMenu popup)
+ {
+ Component[] group;
+ group = popup.getComponents();
+ SingleSelectionModel sm;
+ sm = popup.getSelectionModel();
+ int i;
+
+ if (group != null)
+ {
+ for (i = 0; i < group.length ; i++)
+ {
+ getInside((Container)popup.getComponent(i));
+ }
+ }
+ }
+
+ /**
+ * This method will get all the components inside the JRootPane.
+ * (Precondition: pane != null)
+ * @param pane its the JRootPane we want to get the information from.
+ */
+ public void getRootPaneInfo (JRootPane pane)
+ {
+ Component[] group;
+ group = pane.getComponents();
+ int i;
+
+ if (group != null)
+ {
+ for (i = 0; i < group.length ; i++)
+ {
+
+ getInside((Container)pane.getComponent(i));
+ }
+ }
+ }
+
+ /**
+ * This method will get all the components inside the JLayeredPane.
+ * (Precondition: pane != null)
+ * @param pane its the JLayeredPane we want to get the information from.
+ */
+ public void getLayeredPaneInfo (JLayeredPane pane)
+ {
+ Component[] group;
+ group = pane.getComponents();
+ int i;
+
+ if (group != null)
+ {
+ for (i = 0; i < group.length ; i++)
+ {
+ getInside((Container)pane.getComponent(i));
+ }
+ }
+ }
+
+ /**
+ * This is a dummy method to get BasicInternalFrameTitlePane.
+ */
+ public void getInternalFrameTitleInfo (BasicInternalFrameTitlePane pane)
+ {
+ }
+
+ /**
+ * This method will add special column model and list selection
+ * listener to JTableHeader.
+ * (Precondition: header != null)
+ * @param header its the JTableHeader we want to get the information from.
+ */
+ public void getTableHeaderInfo (JTableHeader header)
+ {
+ TableColumnModel tcm;
+ tcm = header.getColumnModel();
+ ListSelectionModel lm;
+ lm = tcm.getSelectionModel();
+
+ if (tcm != null)
+ {
+ if (hash.containsKey(tcm) == false)
+ hash.put(tcm,header);
+ tcm.removeColumnModelListener(m);
+ tcm.addColumnModelListener(m);
+ }
+
+ if (lm != null)
+ {
+ if (hash.containsKey(lm) == false)
+ hash.put(lm,header);
+ lm.removeListSelectionListener(m);
+ lm.addListSelectionListener(m);
+ }
+ }
+
+ /**
+ * This method will get all components inside the JToolBar.
+ * (Precondition: tool != null)
+ * @param tool its the JToolBar we want to get the information from.
+ */
+ public void getToolBarInfo (JToolBar tool)
+ {
+ Component[] group;
+ group = tool.getComponents();
+ int i;
+
+ if (group != null)
+ {
+ for (i = 0; i < group.length ; i++)
+ {
+ getInside((Container)tool.getComponent(i));
+ }
+ }
+ }
+
+ /**
+ * This method will get all the components inside the JToolTip.
+ * (Precondition: tip != null)
+ * @param tip its the JToolTip we want to get the information from.
+ */
+ public void getToolTipInfo (JToolTip tip)
+ {
+ Component group;
+ group = tip.getComponent();
+ int i;
+
+ if (group != null)
+ {
+ getInside((Container) group);
+ }
+ }
+
+ /**
+ * This is the dummy method to get JSeparator.
+ */
+ public void getSeparatorInfo (JSeparator sept)
+ {
+ }
+
+ /**
+ * This method will get all the components inside the JDesktopPane.
+ * (Precondition: pane != null)
+ * @param pane its the JDesktopPane we want to get the information from.
+ */
+ public void getDesktopPaneInfo (JDesktopPane pane)
+ {
+ JInternalFrame[] group;
+ group = pane.getAllFrames();
+
+ if (group != null)
+ {
+ int i;
+
+ for ( i = 0 ; i < group.length ; i++ )
+ {
+ getInside((Container) group[i]);
+ }
+ }
+ }
+
+ /**
+ * This is a dummy method to catch null components.
+ */
+ public void getNullInfo ()
+ {
+ }
+
+ /**
+ * This method will add a special listener to a container and components inside it.
+ * This method is the gateway to all other methods in this class.
+ * @param comp its the Container we want to get the information from.
+ */
+ public void getInside(Container comp)
+ {
+ if (comp == null)
+ {
+ getNullInfo();
+ return;
+ }
+ else if (comp instanceof JApplet)
+ {
+ getAppletInfo ((JApplet) comp);
+ return;
+ }
+ else if (comp instanceof JSeparator)
+ {
+ getSeparatorInfo ((JSeparator) comp);
+ return;
+ }
+ else if (comp instanceof JWindow)
+ {
+ getWindowInfo((JWindow) comp);
+ return;
+ }
+ else if (comp instanceof JDialog)
+ {
+ getDialogInfo((JDialog) comp);
+ return;
+ }
+ else if (comp instanceof JComboBox)
+ {
+ getComboBoxInfo((JComboBox) comp);
+ return;
+ }
+ else if (comp instanceof JLabel)
+ {
+ getLabelInfo((JLabel) comp);
+ return;
+ }
+ else if (comp instanceof JTextComponent)
+ {
+ getTextAreaInfo((JTextComponent) comp);
+ return;
+ }
+ else if (comp instanceof JFrame)
+ {
+ getFrameInfo((JFrame) comp);
+ return;
+ }
+ else if (comp instanceof JPanel)
+ {
+ getPanelInfo((JPanel) comp);
+ return;
+ }
+ else if (comp instanceof JScrollPane)
+ {
+ getScrollInfo((JScrollPane) comp);
+ return;
+ }
+ else if (comp instanceof JScrollBar)
+ {
+ getScrollBarInfo ((JScrollBar) comp);
+ return;
+ }
+ else if (comp instanceof JViewport)
+ {
+ getViewPortInfo((JViewport) comp);
+ return;
+ }
+ else if (comp instanceof JTabbedPane)
+ {
+ getTabbedPaneInfo((JTabbedPane) comp);
+ return;
+ }
+ else if (comp instanceof Box)
+ {
+ getBoxInfo((Box) comp);
+ return;
+ }
+ else if (comp instanceof JToggleButton)
+ {
+ getToggleButtonInfo((JToggleButton) comp);
+ return;
+ }
+ else if (comp instanceof JSplitPane)
+ {
+ getSplitPaneInfo ((JSplitPane) comp);
+ return;
+ }
+ else if (comp instanceof JList)
+ {
+ getListInfo ((JList) comp);
+ return;
+ }
+ else if (comp instanceof JTree)
+ {
+ getTreeInfo ((JTree) comp);
+ return;
+ }
+ else if (comp instanceof JTable)
+ {
+ getTableInfo ((JTable) comp);
+ return;
+ }
+ else if (comp instanceof JSlider)
+ {
+ getSliderInfo ((JSlider) comp);
+ return;
+ }
+ else if (comp instanceof JSpinner)
+ {
+ getSpinnerInfo ((JSpinner) comp);
+ return;
+ }
+ else if (comp instanceof JProgressBar)
+ {
+ getProgressBarInfo ((JProgressBar) comp);
+ return;
+ }
+ else if (comp instanceof JButton)
+ {
+ getButtonInfo ((JButton) comp);
+ return;
+ }
+ else if (comp instanceof JMenu)
+ {
+ getMenuInfo ((JMenu) comp);
+ return;
+ }
+ else if (comp instanceof JMenuBar)
+ {
+ getMenuBarInfo ((JMenuBar) comp);
+ return;
+ }
+ else if (comp instanceof JMenuItem)
+ {
+ getMenuItemInfo ((JMenuItem) comp);
+ return;
+ }
+ else if (comp instanceof JOptionPane)
+ {
+ getOptionPaneInfo ((JOptionPane) comp);
+ return;
+ }
+ else if (comp instanceof BasicInternalFrameTitlePane)
+ {
+ getInternalFrameTitleInfo ((BasicInternalFrameTitlePane) comp);
+ return;
+ }
+ else if (comp instanceof JColorChooser)
+ {
+ getColorInfo ((JColorChooser) comp);
+ return;
+ }
+ else if (comp instanceof JFileChooser)
+ {
+ getFileChooserInfo ((JFileChooser) comp);
+ return;
+ }
+ else if (comp instanceof JDesktopPane)
+ {
+ getDesktopPaneInfo ((JDesktopPane) comp);
+ return;
+ }
+ else if (comp instanceof JInternalFrame)
+ {
+ getInternalFrameInfo ((JInternalFrame) comp);
+ return;
+ }
+ else if (comp instanceof JLayeredPane)
+ {
+ getLayeredPaneInfo ((JLayeredPane) comp);
+ return;
+ }
+ else if (comp instanceof JPopupMenu)
+ {
+ getPopUpMenuInfo ((JPopupMenu) comp);
+ return;
+ }
+ else if (comp instanceof JRootPane)
+ {
+ getRootPaneInfo ((JRootPane) comp);
+ return;
+ }
+ else if (comp instanceof JTableHeader)
+ {
+ getTableHeaderInfo ((JTableHeader) comp);
+ return;
+ }
+ else if (comp instanceof JToolBar)
+ {
+ getToolBarInfo ((JToolBar) comp);
+ return;
+ }
+ else if (comp instanceof JToolTip)
+ {
+ getToolTipInfo ((JToolTip) comp);
+ return;
+ }
+ else
+ {
+ getCompInfo (comp);
+ return;
+ }
+ }
+}
+
+
+
+
+
+
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/ComponentInformation.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/ComponentInformation.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/ComponentInformation.java (revision 31635)
@@ -0,0 +1,2261 @@
+package org.greenstone.gatherer.feedback;
+
+import org.omg.IOP.ComponentIdHelper;
+import java.awt.*;
+import java.io.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.tree.*;
+import javax.swing.table.*;
+import javax.swing.text.*;
+import java.awt.image.*;
+import javax.imageio.ImageIO;
+import javax.swing.plaf.*;
+import javax.swing.plaf.basic.*;
+import javax.swing.colorchooser.*;
+import java.awt.event.*;
+import javax.swing.event.*;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.text.MessageFormat;
+import javax.swing.plaf.metal.MetalIconFactory.*;
+import javax.swing.plaf.metal.MetalIconFactory;
+import javax.swing.plaf.basic.BasicInternalFrameTitlePane;
+import javax.swing.plaf.metal.MetalInternalFrameTitlePane;
+
+/**
+ * In general, this class will get all information and add special listener
+ * to a Container and of all the components inside it.
+ * This class will get the information about the Container and all the components inside it.
+ * The information we get is like whether or not the component is visible and the contents of it.
+ * This class will also add special listener to the Container and each of the components inside
+ * the Container. So, it will allows special listener to record and listen each of the actions
+ * the user do to the Container or any of the components inside it. The special listener
+ * is an instance of ActionRecorderDialog.
+ * This class will only recognized all the components that is belongs to the javax.swing package
+ * which are all the implementing classes of RootPaneContainer interface and all the direct known
+ * subclasses of JComponent. All the other components will be stated simply as Container
+ * and user cannot really get much information out of it.
+ * The added special listener to this component is as what its allowed in API, if the component
+ * have some modified listener added to it then that action will not be known and recorded by
+ * the special listener.
+ *
+ * @author Veronica Liesaputra
+ */
+public class ComponentInformation
+{
+ /**
+ * This variable will hold the special listener that will listen and record any of the
+ * actions user do to the Container and any of the components inside it.
+ */
+ private ActionRecorderDialog m;
+
+ /**
+ * This variable will hold the resource of the words that is stored in Messages.properties file.
+ * The calling using messages.getString(someString) will caused someString to be translated
+ * into some other string that is hold in that file.usually it will caused it to be translated
+ * to the language that the user use or choose to have as stated in Locale.
+ */
+ private static ResourceBundle messages;
+
+ /**
+ * This variable will hold the pair of the component and the model of the component.
+ * With this variable allows ActionRecorderDialog listener to know which component the model that fires an action
+ * belongs to. In this HashMap the model is the key and the component where the model belongs to is
+ * the value.
+ */
+ private HashMap hash;
+
+ /**
+ * This constructor will set the special listener, the locale and the hashmap to be used in
+ * this Container.
+ * (Precondition: dialog != null)
+ * @param dialog its the special listener that will listen and record all the action
+ * user do to any of the component inside the Container.
+ * @param currentLocale its the locale that the user choose all the information to be displayed as.
+ * @param h its the HashMap that will hold the pair of model with the component it belongs.
+ */
+ public ComponentInformation
+ (ActionRecorderDialog dialog,Locale currentLocale,HashMap h)
+ {
+ m = dialog;
+ hash = h;
+ messages = ResourceBundle.getBundle("feedback", currentLocale);
+ }
+
+ /**
+ * This method will get all information about the JTextComponent.
+ * If the JTextComponent is a JTextField then it will add the special action listener to it.
+ * (Precondition: txt != null)
+ * @param txt its the JTextComponent we want to get all the information from.
+ * @return a UserComponent that hold the information about this
+ * JTextComponent.
+ */
+ public UserComponent getTextAreaInfo(JTextComponent txt)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ String text;
+ text = txt.getText();
+
+ file.startContent(1);
+ if (txt instanceof JTextArea)
+ file.saveContent("JTextArea");
+ else if (txt instanceof JTextPane)
+ file.saveContent("JTextPane");
+ else if (txt instanceof JEditorPane)
+ file.saveContent("JEditorPane");
+ else if (txt instanceof JTextField)
+ {
+ ((JTextField)txt).removeActionListener(m);
+ ((JTextField)txt).addActionListener(m);
+
+ if (txt instanceof JFormattedTextField)
+ {
+ file.saveContent("JFormattedTextField");
+ }
+ else if (txt instanceof JPasswordField)
+ {
+ file.saveContent("JPasswordField");
+ }
+ else
+ {
+ file.saveContent("JTextField");
+ }
+ }
+
+ file.startContent(2);
+ file.saveContent(messages.getString("TextComponent"));
+
+ file.startContent(3);
+ if (text != null)
+ file.saveContent(text);
+ else
+ file.saveContent(" ");
+
+ file.startContent(4);
+ file.saveContent(txt.getSelectedText());
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + txt.isVisible()));
+
+ file.startContent(7);
+ file.saveContent(txt.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information and add the special action listener to the JButton.
+ * (Precondition: button != null)
+ * @param button its the JButton we want to get the information from.
+ * @return a UserComponent that will hold the information about this JButton.
+ */
+ public UserComponent getButtonInfo (JButton button)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ String text;
+ text = button.getText();
+
+ button.removeActionListener(m);
+ button.addActionListener(m);
+
+ file.startContent(1);
+ file.saveContent("JButton");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("Button"));
+
+ file.startContent(3);
+ if (text != null)
+ file.saveContent(text);
+ else
+ file.saveContent(" ");
+
+ file.startContent(4);
+ file.saveContent(messages.getString("" + button.isSelected()));
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + button.isVisible()));
+
+ file.startContent(6);
+ Icon img = button.getIcon();
+ if (img != null)
+ {
+ file.saveImage(img,messages.getString("icon")+img.hashCode()+".jpg");
+ }
+ else
+ {
+ file.saveContent(messages.getString("NoImage"));
+ }
+
+ file.startContent(7);
+ file.saveContent(button.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information and add the special change listener to the JProgressBar.
+ * (Precondition: bar != null)
+ * @param bar its the JProgressBar we want to get information from.
+ * @return a UserComponent that will hold the information about this JProgressBar.
+ */
+ public UserComponent getProgressBarInfo (JProgressBar bar)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ String text;
+ text = bar.getString();
+
+ bar.removeChangeListener(m);
+ bar.addChangeListener(m);
+
+ file.startContent(1);
+ file.saveContent("JProgressBar");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("ProgressBar"));
+
+ file.startContent(3);
+ if (text != null)
+ file.saveContent(text);
+ else
+ file.saveContent(" ");
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + bar.isVisible()));
+
+ file.startContent(7);
+ file.saveContent(bar.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information and add the special change listener to the JSlider.
+ * (Precondition: slider != null)
+ * @param slider its the JSlider we want to get information from.
+ * @return a UserComponent that will hold the information about this JSlider.
+ */
+ public UserComponent getSliderInfo (JSlider slider)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ String text;
+ text = "" + slider.getValue();
+
+ slider.removeChangeListener(m);
+ slider.addChangeListener(m);
+
+ file.startContent(1);
+ file.saveContent("JSlider");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("Slider"));
+
+ file.startContent(3);
+ if (text != null)
+ file.saveContent(text);
+ else
+ file.saveContent(" ");
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + slider.isVisible()));
+
+ file.startContent(7);
+ file.saveContent(slider.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information and add the special change listener to the JSpinner.
+ * (Precondition: spinner != null)
+ * @param spinner its the JSpinner we want to the information from.
+ * @return a UserComponent that will hold the information about this JSpinner.
+ */
+ public UserComponent getSpinnerInfo (JSpinner spinner)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ String text;
+ text = "" + spinner.getValue();
+
+ spinner.removeChangeListener(m);
+ spinner.addChangeListener(m);
+
+ file.startContent(1);
+ file.saveContent("JSpinner");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("Spinner"));
+
+ file.startContent(3);
+ if (text != null)
+ file.saveContent(text);
+ else
+ file.saveContent(" ");
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + spinner.isVisible()));
+
+ file.startContent(7);
+ file.saveContent(spinner.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information of the JLabel.
+ * (Precondition: lbl != null)
+ * @param lbl its the JLabel we want to get information from.
+ * @return a UserComponent that will hold the information about this JLabel.
+ */
+ public UserComponent getLabelInfo(JLabel lbl)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ String text;
+ text = lbl.getText();
+
+ file.startContent(1);
+ file.saveContent("JLabel");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("Label"));
+
+ file.startContent(3);
+ if (text != null)
+ file.saveContent(text);
+ else
+ file.saveContent(" ");
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + lbl.isVisible()));
+
+ file.startContent(6);
+ Icon img = lbl.getIcon();
+ if (img != null)
+ {
+ file.saveImage(img,messages.getString("icon")+img.hashCode()+".jpg");
+ }
+ else
+ {
+ file.saveContent(messages.getString("NoImage"));
+ }
+
+ file.startContent(7);
+ file.saveContent(lbl.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information and add the special action listener to the JComboBox.
+ * (Precondition: box != null)
+ * @param box its the JComboBox we want to get information from.
+ * @return a UserComponent that will hold the information about this JComboBox.
+ */
+ public UserComponent getComboBoxInfo(JComboBox box)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ ComboBoxModel cm;
+ cm = box.getModel();
+
+ box.removeActionListener(m);
+ box.addActionListener(m);
+
+ file.startContent(1);
+ file.saveContent("JComboBox");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("ComboBox"));
+
+ file.startContent(3);
+ int i;
+ for (i = 0; i < box.getItemCount() ; i++)
+ {
+ UserComponent file2;
+ file2 = new UserComponent();
+ //Assuming that they don't put any picture or images
+ String text;
+ Object objmsg;
+
+ objmsg = box.getItemAt(i);
+
+ if (objmsg instanceof String)
+ text = (String) objmsg + "\n";
+ else
+ text = objmsg.toString() + "\n" ;
+
+ file2.startContent(3);
+ if (text != null)
+ file2.saveContent(text);
+ else
+ file2.saveContent(" ");
+
+ file2.startContent(4);
+ if (i == box.getSelectedIndex())
+ file2.saveContent(messages.getString("Selected"));
+ else
+ file2.saveContent(messages.getString("NotSelected"));
+
+ file.saveContent(file2);
+ }
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + box.isVisible()));
+
+ file.startContent(7);
+ file.saveContent(box.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information and add the special action listener to the JToggleButton.
+ * (Precondition: button != null)
+ * @param button its a JToggleButton inside the window
+ * @return a UserComponent that will hold the information about this JToggleButton.
+ */
+ public UserComponent getToggleButtonInfo (JToggleButton button)
+ {
+ UserComponent file;
+ file = new UserComponent();
+
+ button.removeActionListener(m);
+ button.addActionListener(m);
+
+ file.startContent(1);
+ if (button instanceof JCheckBox)
+ file.saveContent("JCheckBox");
+ else
+ file.saveContent("JRadioButton");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("ToggleButton"));
+
+ file.startContent(3);
+ String text;
+ text = button.getText();
+ if (text != null)
+ file.saveContent(text);
+ else
+ file.saveContent(" ");
+
+ file.startContent(4);
+ if (button.isSelected() == true)
+ {
+ file.saveContent(messages.getString("Selected"));
+ }
+ else
+ file.saveContent(messages.getString("NotSelected"));
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + button.isVisible()));
+
+ file.startContent(6);
+ Icon img = button.getIcon();
+ if (img != null)
+ {
+ file.saveImage(img,messages.getString("icon")+img.hashCode()+".jpg");
+ }
+ else
+ {
+ file.saveContent(messages.getString("NoImage"));
+ }
+
+ file.startContent(7);
+ file.saveContent(button.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information and add the special action listener to the JMenuItem.
+ * (Precondition: menuitem != null)
+ * @param menuitem its the JMenuItem we want to get the information from.
+ * @return a UserComponent that will hold the information about this JMenuItem.
+ */
+ public UserComponent getMenuItemInfo (JMenuItem menuitem)
+ {
+ UserComponent file;
+ file = new UserComponent();
+
+ menuitem.removeActionListener(m);
+ menuitem.addActionListener(m);
+
+ file.startContent(1);
+ file.saveContent("JMenuItem");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("MenuItem"));
+
+ file.startContent(3);
+ String text;
+ text = menuitem.getText();
+ if (text != null)
+ file.saveContent(text);
+ else
+ file.saveContent(" ");
+
+ file.startContent(4);
+ if (menuitem.isSelected() == true)
+ {
+ file.saveContent(messages.getString("Selected"));
+ }
+ else
+ file.saveContent(messages.getString("NotSelected"));
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + menuitem.isVisible()));
+
+ file.startContent(6);
+ Icon img = menuitem.getIcon();
+ if (img != null)
+ {
+ file.saveImage(img,messages.getString("icon")+img.hashCode()+".jpg");
+ }
+ else
+ {
+ file.saveContent(messages.getString("NoImage"));
+ }
+
+ file.startContent(7);
+ file.saveContent(menuitem.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information of the JMenuBar.
+ * (Precondition: menubar != null)
+ * @param menubar its the JMenuBar we want to get the information from.
+ * @return a UserComponent that will hold the information about this JMenuBar.
+ */
+ public UserComponent getMenuBarInfo (JMenuBar menubar)
+ {
+ UserComponent file;
+ file = new UserComponent();
+
+ file.startContent(1);
+ file.saveContent("JMenuBar");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("MenuBar"));
+
+ file.startContent(3);
+ int i;
+ for (i= 0 ; i < menubar.getMenuCount() ; i++)
+ {
+ file.saveContent(getInside(menubar.getMenu(i)));
+ }
+
+ file.startContent(4);
+ if (menubar.isSelected() == true)
+ {
+ file.saveContent(messages.getString("Selected"));
+ }
+ else
+ file.saveContent(messages.getString("NotSelected"));
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + menubar.isVisible()));
+
+ file.startContent(7);
+ file.saveContent(menubar.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information and add the special action and menu listener to the JMenu.
+ * (Precondition: menu != null)
+ * @param menu its the JMenu we want to the the information from.
+ * @return a UserComponent that will hold the information about this JMenu.
+ */
+ public UserComponent getMenuInfo (JMenu menu)
+ {
+ UserComponent file;
+ file = new UserComponent();
+
+ menu.removeActionListener(m);
+ menu.removeMenuListener(m);
+ menu.addActionListener(m);
+ menu.addMenuListener(m);
+
+ file.startContent(1);
+ file.saveContent("JMenu");
+
+ file.startContent(2);
+ String text;
+ text = menu.getText();
+ if (text != null)
+ file.saveContent(text);
+ else
+ file.saveContent(" ");
+
+ file.startContent(3);
+ int i;
+ for (i= 0 ; i < menu.getMenuComponentCount() ; i++)
+ {
+ file.saveContent(getInside((Container)menu.getMenuComponent(i)));
+ }
+
+ file.startContent(4);
+ if (menu.isSelected() == true)
+ {
+ file.saveContent(messages.getString("Selected"));
+ }
+ else
+ file.saveContent(messages.getString("NotSelected"));
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + menu.isVisible()));
+
+ file.startContent(6);
+ Icon img = menu.getIcon();
+ if (img != null)
+ {
+ file.saveImage(img,messages.getString("icon")+img.hashCode()+".jpg");
+ }
+ else
+ {
+ file.saveContent(messages.getString("NoImage"));
+ }
+
+ file.startContent(7);
+ file.saveContent(menu.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information of the JApplet.
+ * (Precondition: applet != null)
+ * @param applet its the JApplet we want to get the information from.
+ * @return a UserComponent that will hold the information about this JApplet.
+ */
+ public UserComponent getAppletInfo (JApplet applet)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ Component[] group;
+ group = applet.getComponents();
+ int i;
+
+ file.startContent(1);
+ file.saveContent("JApplet");
+
+ file.startContent(2);
+ String text;
+ text = applet.getAppletInfo();
+ if (text != null)
+ file.saveContent(text);
+ else
+ file.saveContent(" ");
+
+ if (group != null)
+ {
+ file.startContent(3);
+ for (i = 0; i < group.length ; i++)
+ {
+ file.saveContent(getInside((Container)applet.getComponent(i)));
+ }
+ }
+
+ file.startContent(4);
+ file.saveContent(messages.getString("" + applet.isActive()));
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + applet.isVisible()));
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information of the JWindow.
+ * (Precondition: window != null)
+ * @param window its the JWindow we want to get the information from.
+ * @return a UserComponent that will hold the information about this JWindow.
+ */
+ public UserComponent getWindowInfo (JWindow window)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ Component[] group;
+ group = window.getComponents();
+ int i;
+
+ file.startContent(1);
+ file.saveContent("JWindow");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("Window"));
+
+ if (group != null)
+ {
+ file.startContent(3);
+ for (i = 0; i < group.length ; i++)
+ {
+ file.saveContent(getInside((Container)window.getComponent(i)));
+ }
+ }
+
+ file.startContent(4);
+ file.saveContent(messages.getString("" + window.isActive()));
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + window.isVisible()));
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information of the JDialog.
+ * (Precondition: dialog != null)
+ * @param dialog its the JDialog we want to get the information from.
+ * @return a UserComponent that will hold the information about this JDialog.
+ */
+ public UserComponent getDialogInfo (JDialog dialog)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ Component[] group;
+ group = dialog.getComponents();
+ int i;
+
+ file.startContent(1);
+ file.saveContent("JDialog");
+
+ file.startContent(2);
+ String text = dialog.getTitle();
+ if (text != null)
+ file.saveContent(text);
+ else
+ file.saveContent(" ");
+
+ if (group != null)
+ {
+ file.startContent(3);
+ for (i = 0; i < group.length ; i++)
+ {
+ file.saveContent(getInside((Container)dialog.getComponent(i)));
+ }
+ }
+
+ file.startContent(4);
+ file.saveContent(messages.getString("" + dialog.isActive()));
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + dialog.isVisible()));
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information of the JInternalFrame.
+ * (Precondition: frame != null)
+ * @param frame its the JInternalFrame we want to get the information from.
+ * @return a UserComponent that will hold the information about this JInternalFrame.
+ */
+ public UserComponent getInternalFrameInfo (JInternalFrame frame)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ Component[] group;
+ group = frame.getComponents();
+ int i;
+
+ file.startContent(1);
+ file.saveContent("JInternalFrame");
+
+ file.startContent(2);
+ String text;
+ text = frame.getTitle();
+ if (text != null)
+ file.saveContent(text);
+ else
+ file.saveContent(" ");
+
+ if (group != null)
+ {
+ file.startContent(3);
+ for (i = 0; i < group.length ; i++)
+ {
+ file.saveContent(getInside((Container)frame.getComponent(i)));
+ }
+ }
+
+ file.startContent(4);
+ file.saveContent(messages.getString("" + frame.isSelected()));
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + frame.isVisible()));
+
+ file.startContent(6);
+ Icon img = frame.getFrameIcon();
+ if (img != null)
+ {
+ file.saveImage(img,messages.getString("icon")+img.hashCode()+".jpg");
+ }
+ else
+ {
+ file.saveContent(messages.getString("NoImage"));
+ }
+
+ file.startContent(7);
+ file.saveContent(frame.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information of the JFrame.
+ * (Precondition: frame != null)
+ * @param frame its the JFrame we want to get the information from.
+ * @return a UserComponent that will hold the information about this JFrame.
+ */
+ public UserComponent getFrameInfo (JFrame frame)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ Component[] group;
+ group = frame.getComponents();
+ int i;
+
+ file.startContent(1);
+ file.saveContent("JFrame");
+
+ file.startContent(2);
+ String text;
+ text = frame.getTitle();
+ if (text != null)
+ file.saveContent(text);
+ else
+ file.saveContent(" ");
+
+ if (group != null)
+ {
+ file.startContent(3);
+ for (i = 0; i < group.length ; i++)
+ {
+ file.saveContent(getInside((Container)frame.getComponent(i)));
+ }
+ }
+
+ file.startContent(4);
+ file.saveContent(messages.getString("" + frame.isActive()));
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + frame.isVisible()));
+
+ file.startContent(6);
+ Image image;
+ image = frame.getIconImage();
+ if (image != null)
+ {
+ ImageIcon img;
+ img = new ImageIcon(image);
+ file.saveImage(img,messages.getString("icon")+img.hashCode()+".jpg");
+ }
+ else
+ {
+ file.saveContent(messages.getString("NoImage"));
+ }
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information of the JPanel.
+ * (Precondition: pane != null)
+ * @param pane its the JPanel we want to get the information from.
+ * @return a UserComponent that will hold the information about this JPanel.
+ */
+ public UserComponent getPanelInfo (JPanel pane)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ Component[] group;
+ group = pane.getComponents();
+ int i;
+
+ file.startContent(1);
+ file.saveContent("JPanel");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("Panel"));
+
+ if (group != null)
+ {
+ file.startContent(3);
+ for (i = 0; i < group.length ; i++)
+ {
+ file.saveContent(getInside((Container)pane.getComponent(i)));
+ }
+ }
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + pane.isVisible()));
+
+ file.startContent(7);
+ file.saveContent(pane.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information of the JScrollPane.
+ * (Precondition: scroll != null)
+ * @param scroll its the JScrollPane we want to get the information from.
+ * @return a UserComponent that will hold the information about this JScrollPane.
+ */
+ public UserComponent getScrollInfo (JScrollPane scroll)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ Component[] group;
+ group = scroll.getComponents();
+ int i;
+
+ file.startContent(1);
+ file.saveContent("JScrollPane");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("Scroller"));
+
+ if (group != null)
+ {
+ file.startContent(3);
+ for (i = 0; i < group.length ; i++)
+ {
+ file.saveContent(getInside((Container)group[i]));
+ }
+ }
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + scroll.isVisible()));
+
+ file.startContent(7);
+ file.saveContent(scroll.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information and add special change listener to the JTabbedPane.
+ * (Precondition: tab != null)
+ * @param tab its the JTabbedPane we want to get the information from.
+ * @return a UserComponent that will hold the information about this JTabbedPane.
+ */
+ public UserComponent getTabbedPaneInfo (JTabbedPane tab)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ int j;
+
+ tab.removeChangeListener(m);
+ tab.addChangeListener(m);
+
+ file.startContent(1);
+ file.saveContent("JTabbedPane");
+
+ file.startContent(3);
+ for (j = 0 ; j < tab.getTabCount(); j++)
+ {
+ UserComponent file2;
+ file2 = new UserComponent();
+
+ file2.startContent(2);
+ String title;
+ title = tab.getTitleAt(j);
+ if (title != null)
+ file2.saveContent(title);
+ else
+ file2.saveContent(" ");
+
+ file2.startContent(6);
+ Icon img;
+ img = tab.getIconAt(j);
+ if (img != null)
+ {
+ file2.saveImage(img,messages.getString("icon")+img.hashCode()+".jpg");
+ }
+ else
+ {
+ file2.saveContent(messages.getString("NoImage"));
+ }
+
+ file2.startContent(3);
+ Component group = tab.getComponentAt(j);
+ file2.saveContent(getInside((Container)group));
+
+ file2.startContent(4);
+ if (j == tab.getSelectedIndex())
+ file2.saveContent(messages.getString("Selected"));
+ else
+ file2.saveContent(messages.getString("NotSelected"));
+
+ file.saveContent(file2);
+ }
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + tab.isVisible()));
+
+ file.startContent(7);
+ file.saveContent(tab.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information of the JOptionPane.
+ * (Precondition: op != null)
+ * @param op its the JOptionPane we want to get the information from.
+ * @return a UserComponent that will hold the information about this JOptionPane.
+ */
+ public UserComponent getOptionPaneInfo (JOptionPane op)
+ {
+ UserComponent file;
+ file = new UserComponent();
+
+ file.startContent(1);
+ file.saveContent("JOptionPane");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("OptionPane"));
+
+ file.startContent(3);
+ Object objmsg;
+ objmsg = op.getMessage();
+
+ if (objmsg != null)
+ {
+ String text;
+ if (objmsg instanceof String)
+ text = (String) objmsg + "\n";
+ else
+ text = objmsg.toString() + "\n" ;
+ file.saveContent(text);
+ }
+ else
+ file.saveContent(" ");
+
+ Object[] obj;
+ int i;
+ obj = op.getOptions();
+ if (obj != null)
+ {
+ for (i = 0; i < obj.length ; i++)
+ {
+ if (obj[i] instanceof String)
+ file.saveContent((String) obj[i]);
+ else
+ file.saveContent(obj[i].toString());
+ }
+ }
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + op.isVisible()));
+
+ file.startContent(6);
+ Icon img = op.getIcon();
+ if (img != null)
+ {
+ file.saveImage(img,messages.getString("icon")+img.hashCode()+".jpg");
+ }
+ else
+ {
+ file.saveContent(messages.getString("NoImage"));
+ }
+
+ file.startContent(7);
+ file.saveContent(op.getToolTipText());
+
+ return file;
+
+ }
+
+ /**
+ * This method will get all the information of the JViewPort.
+ * (Precondition: vp != null)
+ * @param vp its the JViewPort we want to get the information from.
+ * @return a UserComponent that will hold the information about this JViewPort.
+ */
+ public UserComponent getViewPortInfo (JViewport vp)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ Component[] group;
+ group = vp.getComponents();
+ int i;
+
+ file.startContent(1);
+ file.saveContent("JViewPort");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("ViewPort"));
+
+ if (group != null)
+ {
+ file.startContent(3);
+ for (i = 0; i < group.length ; i++)
+ {
+ file.saveContent(getInside((Container)group[i]));
+ }
+ }
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + vp.isVisible()));
+
+ file.startContent(7);
+ file.saveContent(vp.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information of the Box.
+ * (Precondition: box != null)
+ * @param box its the Box we want to get the information from.
+ * @return a UserComponent that will hold the information about this Box.
+ */
+ public UserComponent getBoxInfo (Box box)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ Component[] group;
+ group = box.getComponents();
+ int i;
+
+ file.startContent(1);
+ file.saveContent("Box");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("Box"));
+
+ if (group != null)
+ {
+ file.startContent(3);
+ for (i = 0; i < group.length ; i++)
+ {
+ file.saveContent(getInside((Container)group[i]));
+ }
+ }
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + box.isVisible()));
+
+ file.startContent(7);
+ file.saveContent(box.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information of the JSplitPane.
+ * (Precondition: split != null)
+ * @param split its the JSplitPane we want to get the information from.
+ * @return a UserComponent that will hold the information about this JSplitPane.
+ */
+ public UserComponent getSplitPaneInfo (JSplitPane split)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ Component[] group;
+ group = split.getComponents();
+ int i;
+
+ file.startContent(1);
+ file.saveContent("JSplitPane");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("Splitters"));
+
+ if (group != null)
+ {
+ file.startContent(3);
+ for (i = 0; i < group.length ; i++)
+ {
+ file.saveContent(getInside((Container)group[i]));
+ }
+ }
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + split.isVisible()));
+
+ file.startContent(7);
+ file.saveContent(split.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information and add special list selection listener to the JList.
+ * (Precondition: list != null)
+ * @param list its the JList we want to get the information from.
+ * @return a UserComponent that will hold the information about this JList.
+ */
+ public UserComponent getListInfo (JList list)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ ListModel ls;
+ ls = list.getModel();
+ int i;
+
+ list.removeListSelectionListener(m);
+ list.addListSelectionListener(m);
+
+ if (ls != null)
+ {
+ if (hash.containsKey(ls) == false)
+ hash.put(ls,list);
+ ls.removeListDataListener(m);
+ ls.addListDataListener(m);
+ }
+
+ file.startContent(1);
+ file.saveContent("JList");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("List"));
+
+ if (ls != null)
+ {
+ file.startContent(3);
+ for ( i = 0 ; i < ls.getSize() ; i++)
+ {
+ UserComponent file2;
+ file2 = new UserComponent();
+ //Assume that its all a text
+ file2.startContent(3);
+ String text;
+ Object obj;
+ obj = ls.getElementAt(i);
+
+ if (obj instanceof String)
+ text = (String) obj;
+ else
+ text = obj.toString();
+
+ if (text != null)
+ file2.saveContent(text);
+ else
+ file2.saveContent(" ");
+
+ file2.startContent(4);
+ if ( i == list.getSelectedIndex())
+ {
+ file2.saveContent(messages.getString("Selected"));
+ }
+ else
+ file2.saveContent(messages.getString("NotSelected"));
+
+ file.saveContent(file2);
+ }
+ }
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + list.isVisible()));
+
+ file.startContent(7);
+ file.saveContent(list.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information and add special change listener to the JColorChooser.
+ * (Precondition: color != null)
+ * @param color its the JColorChooser we want to get the information from.
+ * @return a UserComponent that will hold the information about this JColorChooser.
+ */
+ public UserComponent getColorInfo (JColorChooser color)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ ColorSelectionModel cm;
+ cm = color.getSelectionModel();
+ int i;
+
+ if (cm != null)
+ {
+ if (hash.containsKey(cm) == false)
+ hash.put(cm,color);
+ cm.removeChangeListener(m);
+ cm.addChangeListener(m);
+ }
+
+ file.startContent(1);
+ file.saveContent("JColorChooser");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("ColorChooser"));
+
+ file.startContent(3);
+ UserComponent file2;
+ file2 = new UserComponent();
+ file2.startContent(2);
+ file2.saveContent(messages.getString("PreviewPanel"));
+
+ file2.startContent(3);
+ file2.saveContent(getInside((Container) color.getPreviewPanel()));
+
+ file.saveContent(file2);
+
+ file2 = new UserComponent();
+ file2.startContent(2);
+ file2.saveContent(messages.getString("ChooserPanels"));
+
+ AbstractColorChooserPanel[] cp;
+ cp = color.getChooserPanels();
+ if ((cp != null) && (cp.length != 0))
+ {
+ file2.startContent(3);
+ for (i = 0 ; i < cp.length ; i++)
+ {
+ UserComponent file3;
+ file3 = new UserComponent();
+ file3.startContent(2);
+ file3.saveContent(cp[i].getDisplayName());
+
+ ColorSelectionModel cpsm;
+ cpsm = cp[i].getColorSelectionModel();
+
+ if (cpsm != null)
+ {
+ file3.startContent(3);
+ file3.saveContent("" +
+ cpsm.getSelectedColor());
+ }
+
+ file3.startContent(6);
+ Icon img = cp[i].getSmallDisplayIcon();
+ if (img != null)
+ {
+ file3.saveImage(img,messages.getString("icon")+img.hashCode()+".jpg");
+ }
+ else
+ {
+ file3.saveContent(messages.getString("NoImage"));
+ }
+
+ file3.startContent(6);
+ img = cp[i].getLargeDisplayIcon();
+ if (img != null)
+ {
+ file3.saveImage(img,messages.getString("icon")+img.hashCode()+".jpg");
+ }
+ else
+ {
+ file3.saveContent(messages.getString("NoImage"));
+ }
+ file2.saveContent(file3);
+ }
+ }
+
+ file.saveContent(file2);
+
+ file.startContent(4);
+ file.saveContent("" + cm.getSelectedColor().toString());
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + color.isVisible()));
+
+ file.startContent(7);
+ file.saveContent(color.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information and add special action listener to the JFileChooser.
+ * (Precondition: fc != null)
+ * @param fc its the JFileChooser we want to get the information from.
+ * @return a UserComponent that will hold the information about this JFileChooser.
+ */
+ public UserComponent getFileChooserInfo (JFileChooser fc)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ int i;
+
+ fc.removeActionListener(m);
+ fc.addActionListener(m);
+
+ file.startContent(1);
+ file.saveContent("JFileChooser");
+
+ file.startContent(2);
+ file.saveContent(fc.getDialogTitle());
+
+ File[] f;
+ f = fc.getSelectedFiles();
+
+ if (f != null)
+ {
+ file.startContent(3);
+
+ for ( i = 0 ; i < f.length ; i++)
+ {
+ UserComponent file2;
+ file2 = new UserComponent();
+ file2.startContent(1);
+ file2.saveContent(fc.getTypeDescription(f[i]));
+
+ file2.startContent(2);
+ file2.saveContent(fc.getName(f[i]));
+
+ file2.startContent(6);
+ Icon img = fc.getIcon(f[i]);
+ if (img != null)
+ {
+ file2.saveImage(img,messages.getString("icon")+img.hashCode()+".jpg");
+ }
+ else
+ {
+ file2.saveContent(messages.getString("NoImage"));
+ }
+ file.saveContent(file2);
+ }
+ }
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + fc.isVisible()));
+
+ file.startContent(7);
+ file.saveContent(fc.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information and add special list selection and column model listener
+ * to the JTable.
+ * (Precondition: table != null)
+ * @param table its the JTable we want to get the information from.
+ * @return a UserComponent that will hold the information about this JTable.
+ */
+ public UserComponent getTableInfo (JTable table)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ TableModel tm;
+ tm = table.getModel();
+ int i,j;
+ String text;
+
+ TableColumnModel tcm;
+ tcm = table.getColumnModel();
+ ListSelectionModel lm;
+ lm = table.getSelectionModel();
+
+ if (lm != null)
+ {
+ if (hash.containsKey(lm) == false)
+ hash.put(lm,table);
+ lm.removeListSelectionListener(m);
+ lm.addListSelectionListener(m);
+ }
+
+ if (tcm != null)
+ {
+ if (hash.containsKey(tcm) == false)
+ hash.put(tcm,table);
+ tcm.removeColumnModelListener(m);
+ tcm.addColumnModelListener(m);
+ }
+
+ file.startContent(1);
+ file.saveContent("JTable");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("Table"));
+
+ if (tm != null)
+ {
+ file.startContent(3);
+ for ( i = 0 ; i < tm.getRowCount() ; i++)
+ {
+ UserComponent file2;
+ file2 = new UserComponent();
+
+ file2.startContent(2);
+ text = messages.getString("row") +" " + (i + 1);
+ file2.saveContent(text);
+
+ file2.startContent(3);
+ for ( j = 0 ; j < tm.getColumnCount() ; j++)
+ {
+ UserComponent file3;
+ file3 = new UserComponent();
+
+ file3.startContent(2);
+ file3.saveContent(tm.getColumnName(j));
+
+ file3.startContent(3);
+
+ Object obj;
+ obj = tm.getValueAt(i,j);
+
+ if (obj instanceof String)
+ file3.saveContent((String)obj);
+ else
+ file3.saveContent(obj.toString());
+
+ file3.startContent(4);
+ if (table.isCellSelected(i,j) == true)
+ {
+ file3.saveContent(messages.getString("Selected"));
+ }
+ else
+ file3.saveContent(messages.getString("NotSelected"));
+
+ file2.saveContent(file3);
+ }
+
+ file.saveContent(file2);
+ }
+ }
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + table.isVisible()));
+
+ file.startContent(7);
+ file.saveContent(table.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information of the JScrollBar.
+ * (Precondition: scroll != null)
+ * @param scroll its the JScrollBar we want to get the information from.
+ * @return a UserComponent that will hold the information about this JScrollBar.
+ */
+ public UserComponent getScrollBarInfo (JScrollBar scroll)
+ {
+ UserComponent file;
+ file = new UserComponent();
+
+ file.startContent(1);
+ file.saveContent("JScrollBar");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("ScrollerBar"));
+
+ file.startContent(3);
+ file.saveContent(""+ scroll.getOrientation()+ " :"+ scroll.getValue());
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + scroll.isVisible()));
+
+ file.startContent(7);
+ file.saveContent(scroll.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information and add special tree selection listener to the JTree.
+ * (Precondition: tree != null)
+ * @param tree its the JTree we want to get the information from.
+ * @return a UserComponent that will hold the information about this JTree.
+ */
+ public UserComponent getTreeInfo (JTree tree)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ TreeModel tm;
+ tm = tree.getModel();
+ int i;
+
+ tree.removeTreeSelectionListener(m);
+ tree.addTreeSelectionListener(m);
+
+ if (tm != null)
+ {
+ if (hash.containsKey(tm) == false)
+ hash.put(tm,tree);
+ tm.removeTreeModelListener(m);
+ tm.addTreeModelListener(m);
+ }
+
+ file.startContent(1);
+ file.saveContent("JTree");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("Tree"));
+
+ file.startContent(3);
+ Object root;
+ root = tm.getRoot();
+ file.saveContent(getChildInfo(tm,root,tree));
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + tree.isVisible()));
+
+ file.startContent(7);
+ file.saveContent(tree.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information about the parent node and all the childs of it inside the JTree.
+ * (Precondition: (tree != null) && (tm != null) && (parent != null))
+ * @param tm its the tree model of the JTree.
+ * @param parent its the parent node that we want to get all the child information from.
+ * @param tree its the JTree where the parent node belongs to.
+ * @return a UserComponent that will hold the information about this parent node.
+ */
+ private UserComponent getChildInfo (TreeModel tm,Object parent,JTree tree)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ if (tm.isLeaf(parent) == false)
+ {
+ file.startContent(2);
+ String text;
+ text = parent.toString();
+ if (text != null)
+ file.saveContent(text);
+ else
+ file.saveContent(" ");
+
+ int j;
+ for (j = 0 ; j < tm.getChildCount(parent) ; j++)
+ {
+ file.startContent(3);
+ Object child;
+ child = tm.getChild(parent,j);
+ file.saveContent(getChildInfo(tm,child,tree));
+ }
+ }
+ else
+ {
+ file.startContent(2);
+ String text;
+ text = parent.toString();
+ if (text != null)
+ file.saveContent(text);
+ else
+ file.saveContent(" ");
+ }
+ return file;
+ }
+
+ /**
+ * This method will get all the information of a Container that is not belongs to any of the more specified
+ * method stated in this class.
+ * (Precondition: pane != null)
+ * @param pane its the Container component we want to get the information from.
+ * @return a UserComponent that will hold the information about this Container.
+ */
+ public UserComponent getCompInfo (Container pane)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ Component[] group;
+ group = pane.getComponents();
+ int i;
+
+ file.startContent(1);
+ file.saveContent("Container");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("Container"));
+
+ if (group != null)
+ {
+ file.startContent(3);
+ file.saveContent(messages.getString("TitlePane"));
+ file.saveContent(getInside(null));
+ /*for (i = 0; i < group.length ; i++)
+ {
+ file.saveContent(getInside((Container)pane.getComponent(i)));
+ }*/
+ }
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + pane.isVisible()));
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information of the JPopupMenu.
+ * (Precondition: popup != null)
+ * @param popup its the JPopUpMenu we want to get the information from.
+ * @return a UserComponent that will hold the information about this JPopupMenu.
+ */
+ public UserComponent getPopUpMenuInfo (JPopupMenu popup)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ Component[] group;
+ group = popup.getComponents();
+ SingleSelectionModel sm;
+ sm = popup.getSelectionModel();
+ int i;
+
+ file.startContent(1);
+ file.saveContent("JPopUpMenu");
+
+ file.startContent(2);
+ String text;
+ text = popup.getLabel();
+ if (text != null)
+ file.saveContent(popup.getLabel());
+ else
+ file.saveContent(" ");
+
+ if (group != null)
+ {
+ file.startContent(3);
+ for (i = 0; i < group.length ; i++)
+ {
+ file.saveContent(getInside((Container)popup.getComponent(i)));
+ }
+ }
+
+ if (sm != null)
+ {
+ file.startContent(4);
+ file.saveContent("" + sm.getSelectedIndex());
+ }
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + popup.isVisible()));
+
+ file.startContent(7);
+ file.saveContent(popup.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information of the JRootPane.
+ * (Precondition: pane != null)
+ * @param pane its the JRootPane we want to get the information from.
+ * @return a UserComponent that will hold the information about this JRootPane.
+ */
+ public UserComponent getRootPaneInfo (JRootPane pane)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ Component[] group;
+ group = pane.getComponents();
+ int i;
+
+ file.startContent(1);
+ file.saveContent("JRootPane");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("RootPane"));
+
+ if (group != null)
+ {
+ file.startContent(3);
+ for (i = 0; i < group.length ; i++)
+ {
+
+ file.saveContent(getInside((Container)pane.getComponent(i)));
+ }
+ }
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + pane.isVisible()));
+
+ file.startContent(7);
+ file.saveContent(pane.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information of the JLayeredPane.
+ * (Precondition: pane != null)
+ * @param pane its the JLayeredPane we want to get the information from.
+ * @return a UserComponent that will hold the information about this JLayeredPane.
+ */
+ public UserComponent getLayeredPaneInfo (JLayeredPane pane)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ Component[] group;
+ group = pane.getComponents();
+ int i;
+
+ file.startContent(1);
+ file.saveContent("JLayeredPane");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("LayeredPane"));
+
+ if (group != null)
+ {
+ file.startContent(3);
+ for (i = 0; i < group.length ; i++)
+ {
+ file.saveContent(getInside((Container)pane.getComponent(i)));
+ }
+ }
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + pane.isVisible()));
+
+ file.startContent(7);
+ file.saveContent(pane.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information of the BasicInternalFrameTitlePane.
+ * (Precondition: pane != null)
+ * @param pane its the BasicInternalFrameTitlePane we want to get the information from.
+ * @return a UserComponent that will hold the information about this BasicInternalFrameTitlePane.
+ */
+ public UserComponent getInternalFrameTitleInfo (BasicInternalFrameTitlePane pane)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ Component[] group;
+ group = pane.getComponents();
+ int i;
+
+ file.startContent(1);
+ file.saveContent("BasicInternalFrameTitlePane");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("InternalFrameTitlePane"));
+
+ /*if (group != null)
+ {
+ file.startContent(3);
+ for (i = 0; i < group.length ; i++)
+ {
+ Component c;
+ c = pane.getComponent(i);
+
+ else
+ file.saveContent(getInside((Container)pane.getComponent(i)));
+ }
+ }*/
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + pane.isVisible()));
+
+ file.startContent(7);
+ file.saveContent(pane.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information and add special column model and list selection
+ * listener to JTableHeader.
+ * (Precondition: header != null)
+ * @param header its the JTableHeader we want to get the information from.
+ * @return a UserComponent that will hold the information about this JTableHeader.
+ */
+ public UserComponent getTableHeaderInfo (JTableHeader header)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ TableColumnModel tcm;
+ tcm = header.getColumnModel();
+ ListSelectionModel lm;
+ lm = tcm.getSelectionModel();
+
+ if (tcm != null)
+ {
+ if (hash.containsKey(tcm) == false)
+ hash.put(tcm,header);
+ tcm.removeColumnModelListener(m);
+ tcm.addColumnModelListener(m);
+ }
+
+ if (lm != null)
+ {
+ if (hash.containsKey(lm) == false)
+ hash.put(lm,header);
+ lm.removeListSelectionListener(m);
+ lm.addListSelectionListener(m);
+ }
+
+ file.startContent(1);
+ file.saveContent("JTableHeader");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("TableHeader"));
+
+ if (tcm != null)
+ {
+ int[] select = tcm.getSelectedColumns();
+
+ if (select != null)
+ {
+ file.startContent(3);
+ for (int i = 0 ; i < select.length ; i++)
+ {
+ UserComponent file2;
+ file2 = new UserComponent();
+ file2.startContent(4);
+ file2.saveContent("" + select[i]);
+
+ file.saveContent(file2);
+ }
+ }
+ }
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + header.isVisible()));
+
+ file.startContent(7);
+ file.saveContent(header.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information of the JToolBar.
+ * (Precondition: tool != null)
+ * @param tool its the JToolBar we want to get the information from.
+ * @return a UserComponent that will hold the information about this JToolBar.
+ */
+ public UserComponent getToolBarInfo (JToolBar tool)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ Component[] group;
+ group = tool.getComponents();
+ int i;
+
+ file.startContent(1);
+ file.saveContent("JToolBar");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("ToolBar"));
+
+ if (group != null)
+ {
+ file.startContent(3);
+ for (i = 0; i < group.length ; i++)
+ {
+ file.saveContent(getInside((Container)tool.getComponent(i)));
+ }
+ }
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + tool.isVisible()));
+
+ file.startContent(7);
+ file.saveContent(tool.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information of the JToolTip.
+ * (Precondition: tip != null)
+ * @param tip its the JToolTip we want to get the information from.
+ * @return a UserComponent that will hold the information about this JToolTip.
+ */
+ public UserComponent getToolTipInfo (JToolTip tip)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ Component group;
+ group = tip.getComponent();
+ int i;
+
+ file.startContent(1);
+ file.saveContent("JToolTip");
+
+ file.startContent(2);
+ String text;
+ text = tip.getTipText();
+ if (text != null)
+ file.saveContent(tip.getTipText());
+ else
+ file.saveContent(" ");
+
+ if (group != null)
+ {
+ file.startContent(3);
+ file.saveContent(getInside((Container) group));
+ }
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + tip.isVisible()));
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information of the JSeparator.
+ * (Precondition: sept != null)
+ * @param sept its the JSeparator we want to get the information from.
+ * @return a UserComponent that will hold the information about this JSeparator.
+ */
+ public UserComponent getSeparatorInfo (JSeparator sept)
+ {
+ UserComponent file;
+ file = new UserComponent();
+
+ file.startContent(1);
+ file.saveContent("JSeparator");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("Separator"));
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + sept.isVisible()));
+
+ file.startContent(7);
+ file.saveContent(sept.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will get all the information of the JDesktopPane.
+ * (Precondition: pane != null)
+ * @param pane its the JDesktopPane we want to get the information from.
+ * @return a UserComponent that will hold the information about this JDesktopPane.
+ */
+ public UserComponent getDesktopPaneInfo (JDesktopPane pane)
+ {
+ UserComponent file;
+ file = new UserComponent();
+ JInternalFrame[] group;
+ group = pane.getAllFrames();
+
+ file.startContent(1);
+ file.saveContent("JDesktopPane");
+
+ file.startContent(2);
+ file.saveContent(messages.getString("DesktopPane"));
+
+ if (group != null)
+ {
+ int i;
+ file.startContent(3);
+ for ( i = 0 ; i < group.length ; i++ )
+ {
+ file.saveContent (getInside((Container) group[i]));
+ }
+ }
+
+ file.startContent(5);
+ file.saveContent(messages.getString("" + pane.isVisible()));
+
+ file.startContent(7);
+ file.saveContent(pane.getToolTipText());
+
+ return file;
+ }
+
+ /**
+ * This method will give you a UserComponent that hold the information saying
+ * the component is null (no component).
+ * @return a UserComponent that will hold the information saying its a null component.
+ */
+ public UserComponent getNullInfo ()
+ {
+ UserComponent file;
+ file = new UserComponent();
+
+ file.startContent(1);
+ file.saveContent(messages.getString("NoComponent"));
+
+ return file;
+ }
+
+ /**
+ * This method will get all information about any kind of Container.
+ * This method is the gateway to all other methods in this class and will give you all
+ * information about the Container and whats inside it.If you dont know what the specific class
+ * of your container then you go here and it will sort which appropriate method
+ * this container should go to in this class.
+ * @param comp its the Container we want to get the information from.
+ * @return a UserComponent that hold the information about this Container.
+ */
+ public UserComponent getInside(Container comp)
+ {
+ if (comp == null)
+ {
+ return getNullInfo();
+ }
+ else if (comp instanceof JApplet)
+ {
+ return getAppletInfo ((JApplet) comp);
+ }
+ else if (comp instanceof JSeparator)
+ {
+ return getSeparatorInfo ((JSeparator) comp);
+ }
+ else if (comp instanceof JWindow)
+ {
+ return getWindowInfo((JWindow) comp);
+ }
+ else if (comp instanceof JDialog)
+ {
+ return getDialogInfo((JDialog) comp);
+ }
+ else if (comp instanceof JComboBox)
+ {
+ return getComboBoxInfo((JComboBox) comp);
+ }
+ else if (comp instanceof JLabel)
+ {
+ return getLabelInfo((JLabel) comp);
+ }
+ else if (comp instanceof JTextComponent)
+ {
+ return getTextAreaInfo((JTextComponent) comp);
+ }
+ else if (comp instanceof JFrame)
+ {
+ return getFrameInfo((JFrame) comp);
+ }
+ else if (comp instanceof JPanel)
+ {
+ return getPanelInfo((JPanel) comp);
+ }
+ else if (comp instanceof JScrollPane)
+ {
+ return getScrollInfo((JScrollPane) comp);
+ }
+ else if (comp instanceof JScrollBar)
+ {
+ return getScrollBarInfo ((JScrollBar) comp);
+ }
+ else if (comp instanceof JViewport)
+ {
+ return getViewPortInfo((JViewport) comp);
+ }
+ else if (comp instanceof JTabbedPane)
+ {
+ return getTabbedPaneInfo((JTabbedPane) comp);
+ }
+ else if (comp instanceof Box)
+ {
+ return getBoxInfo((Box) comp);
+ }
+ else if (comp instanceof JToggleButton)
+ {
+ return getToggleButtonInfo((JToggleButton) comp);
+ }
+ else if (comp instanceof JSplitPane)
+ {
+ return getSplitPaneInfo ((JSplitPane) comp);
+ }
+ else if (comp instanceof JList)
+ {
+ return getListInfo ((JList) comp);
+ }
+ else if (comp instanceof JTree)
+ {
+ return getTreeInfo ((JTree) comp);
+ }
+ else if (comp instanceof JTable)
+ {
+ return getTableInfo ((JTable) comp);
+ }
+ else if (comp instanceof JSlider)
+ {
+ return getSliderInfo ((JSlider) comp);
+ }
+ else if (comp instanceof JSpinner)
+ {
+ return getSpinnerInfo ((JSpinner) comp);
+ }
+ else if (comp instanceof JProgressBar)
+ {
+ return getProgressBarInfo ((JProgressBar) comp);
+ }
+ else if (comp instanceof JButton)
+ {
+ return getButtonInfo ((JButton) comp);
+ }
+ else if (comp instanceof JMenu)
+ {
+ return getMenuInfo ((JMenu) comp);
+ }
+ else if (comp instanceof JMenuBar)
+ {
+ return getMenuBarInfo ((JMenuBar) comp);
+ }
+ else if (comp instanceof JMenuItem)
+ {
+ return getMenuItemInfo ((JMenuItem) comp);
+ }
+ else if (comp instanceof JOptionPane)
+ {
+ return getOptionPaneInfo ((JOptionPane) comp);
+ }
+ else if (comp instanceof BasicInternalFrameTitlePane)
+ {
+ return getInternalFrameTitleInfo ((BasicInternalFrameTitlePane) comp);
+ }
+ else if (comp instanceof JColorChooser)
+ {
+ return getColorInfo ((JColorChooser) comp);
+ }
+ else if (comp instanceof JFileChooser)
+ {
+ return getFileChooserInfo ((JFileChooser) comp);
+ }
+ else if (comp instanceof JDesktopPane)
+ {
+ return getDesktopPaneInfo ((JDesktopPane) comp);
+ }
+ else if (comp instanceof JInternalFrame)
+ {
+ return getInternalFrameInfo ((JInternalFrame) comp);
+ }
+ else if (comp instanceof JLayeredPane)
+ {
+ return getLayeredPaneInfo ((JLayeredPane) comp);
+ }
+ else if (comp instanceof JPopupMenu)
+ {
+ return getPopUpMenuInfo ((JPopupMenu) comp);
+ }
+ else if (comp instanceof JRootPane)
+ {
+ return getRootPaneInfo ((JRootPane) comp);
+ }
+ else if (comp instanceof JTableHeader)
+ {
+ return getTableHeaderInfo ((JTableHeader) comp);
+ }
+ else if (comp instanceof JToolBar)
+ {
+ return getToolBarInfo ((JToolBar) comp);
+ }
+ else if (comp instanceof JToolTip)
+ {
+ return getToolTipInfo ((JToolTip) comp);
+ }
+ else
+ {
+ return getCompInfo (comp);
+ }
+ }
+}
+
+
+
+
+
+
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/FeedbackInterface.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/FeedbackInterface.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/FeedbackInterface.java (revision 31635)
@@ -0,0 +1,1645 @@
+package org.greenstone.gatherer.feedback;
+
+import java.io.*;
+import java.awt.*;
+import java.awt.image.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.border.*;
+import java.util.*;
+import java.net.*;
+import java.rmi.server.*;
+import javax.imageio.*;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.text.MessageFormat;
+import javax.swing.Timer;
+import org.greenstone.gatherer.Gatherer;
+
+/**
+ * This class will show the FeedbackInterface form.
+ * It will allow user to described about the problem better.Here we will also take some more information available
+ * in the system such as the size of the memory available and the screen size.User also could take more screenshot
+ * so they will be able to say the sequence of events that lead up to the problem.
+ * If the user don't want to send this form, then all the information taken from this form will be deleted
+ * and user will be quitted from the Reporting Feedback sequence.
+ *
+ * @author Veronica Liesaputra
+ */
+public class FeedbackInterface extends JDialog
+ implements ActionListener
+{
+ /**
+ * This is the button user need to pressed if they want to view the files.
+ * This button will be disabled when the timer is running and finish = false.
+ */
+ private JButton view_button;
+
+ /**
+ * This button means that the user finished give the information and
+ * want to send it.
+ */
+ public JButton send_button;
+
+ /**
+ * This button means that the user don't want to send information and
+ * back to normal application.
+ * All the data taken start from user choose to start doing the Feedback sequence
+ * up to this point will be delete.
+ */
+ private JButton notsend_button;
+
+ /**
+ * This is the text area where user can explain more about the problem.
+ */
+ private JTextArea problem_details;
+
+ /**
+ * This is the combo box that allow user to specify the poblem type.
+ */
+ private JComboBox problem_type;
+
+ /**
+ * This is the combo box that allow user to specify the urgency of the problem.
+ */
+ private JComboBox problem_urgency;
+
+ /**
+ * This combo box will ask whether or not user want to send the screenshot.
+ */
+ private JComboBox problem_screenshot;
+
+ /**
+ * This text field allow user to type the smtp address of their email that
+ * they want this application to use to send all the data.
+ * If they don't fill this field then it will set to the same
+ * smtp adress of where the data will be send to.
+ */
+ private JTextField smtp_address;
+
+ /**
+ * This text field allow user to type the email adress that they want this
+ * application to use to send all the data.
+ * If they don't fill this field then it will set to the same email
+ * adress of where the data will be send to.
+ */
+ private JTextField email_address;
+
+ /**
+ * This is the name of the window the user lastly used.
+ * This is the name of the window where the user called to start the Feedback
+ * sequence.
+ */
+ private String frameName;
+
+ /**
+ * This is the unique code generated for each time user start doing the Feedback
+ * sequence.
+ * This code will be used as reference to which feedback that the user send.
+ */
+ private static String code;
+
+ /**
+ * This is the date when the user start open this FeedbackInterface form.
+ */
+ private Date curr_date;
+
+ /**
+ * This is the special listener class that record all the action user did in application.
+ */
+ private static ActionRecorderDialog dialog;
+
+ /**
+ * This variable will hold the resource of the words that is stored in Messages.properties file.
+ * The calling using messages.getString(someString) will caused someString to be translated
+ * into some other string that is hold in that file.usually it will caused it to be translated
+ * to the language that the user use or choose to have as stated in Locale.
+ */
+ private static ResourceBundle messages;
+
+ /**
+ * This is the screen shot instance that will allow the application to take screen shot of the whole
+ * screen or only the window that the user currently doing their action.
+ */
+ private ScreenShot screen;
+
+ /**
+ * This is the thread that allows the encoding of the image file to String and the saving of image to
+ * a jpeg file happens concurrently.
+ */
+ private Thread save;
+
+ /**
+ * This is the hashmap that hold the screen shot and the panel where the screenshot is held.
+ */
+ private HashMap picAndPane;
+
+ /**
+ * This is the panel that hold all the panels containing the screenshots taken.
+ */
+ private JPanel picture_scroll;
+
+ /**
+ * This is how many rows of screenshots panel displayed at the moment.
+ */
+ private int row;
+
+ /**
+ * This is the array list that hold all the screenshots panel displayed.
+ */
+ private ArrayList paneArray;
+
+ /**
+ * This button will remove the last screenshot panels.
+ */
+ private JButton remove;
+
+ /**
+ * This is a reference to feedback interface displayed at the moment.
+ */
+ private FeedbackInterface frame;
+
+ /**
+ * This is the thread to send all the xml files back to the developer.
+ */
+ private Thread sendThread;
+
+ /**
+ * This is the flag to tell whether or not the sending files finished or not.
+ * It is also flag to tell whether or not the viewing files finished or not.
+ */
+ private static boolean finish;
+
+ /**
+ * This is the timer to displayed the progress bar to tell user to wait while
+ * we sending the files or while we generate the xml files to be viewed.
+ * If finish = false then wait cursor,progress bar and wait label will be displayed.
+ * If finish = true then default cursor will be displayed.
+ */
+ private Timer timer;
+
+ /**
+ * Progress bar displayed when user sending or want to view details.
+ */
+ private JProgressBar progressBar;
+
+ /**
+ * Label displayed to tell user to wait for a while.
+ */
+ private JLabel wait_lbl;
+
+ /**
+ * This is the background colour for the window.
+ */
+ private Color bckcolor;
+
+ /**
+ * This is the panel to displayed all descriptions panel.
+ */
+ private JPanel tempPane;
+
+ /**
+ * This is the thread that will be running when user pressed the preview button.
+ * This thread will show the 6 xml files that are going to be send. It will run concurrently
+ * with the timer, if value of finish = true, this thread will be null-ed.
+ */
+ private Thread prevThread;
+
+ /**
+ * This is the flag to say whether or not this window should be dispose when timer is stop
+ * and finish = true.
+ * If selected = true then this window should be dispose, otherwise don't dispose it.
+ */
+ private boolean selected = false;
+
+ /**
+ * This is a blocker to say that user cannot do anything while waiting.
+ */
+ private MouseListener mouse_blocker_listener = new MouseAdapter() {};
+
+ /**
+ * Will make a dialog window that allows people to write more about the problem that want
+ * to send.
+ * This is the GUI for user to write on Feedback form. It is also setting the up the appropriate
+ * data member with the value passed on to this constructor.
+ * (Precondition: (sh != null) && (dlg != null) && (msg != null))
+ * @param sh an instance of ScreenShot that will allow application to take screenshot.
+ * @param frame_name name of the window where user called Feedback form from.
+ * @param msg holding the resource of the words that is stored in Messages.properties file.
+ * @param dlg holding the special listener.
+ */
+ public FeedbackInterface(ScreenShot sh,String frame_name,
+ ResourceBundle msg,ActionRecorderDialog dlg)
+ {
+ super();
+
+ frame = this;
+
+ bckcolor = new Color(176,209,217);
+
+ picAndPane = new HashMap();
+
+ messages = msg;
+ frameName = frame_name;
+ dialog = dlg;
+
+ FeedbackInterface.setDefaultLookAndFeelDecorated(true);
+ setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
+ setTitle(messages.getString("SendFeedbackForm"));
+
+ screen = sh;
+ Container container;
+ container = getContentPane();
+ createUI(container);
+ curr_date = new Date();
+
+ Toolkit kit;
+ kit = Toolkit.getDefaultToolkit();
+ Dimension screenSize;
+ screenSize = kit.getScreenSize();
+ int screenHeight;
+ screenHeight = screenSize.height;
+ int screenWidth;
+ screenWidth = screenSize.width;
+ Dimension size;
+ size = this.getSize();
+ setLocation((screenWidth - size.width - 500) ,
+ (screenHeight - size.height - 700));
+
+ setBackground(bckcolor);
+ pack();
+ setModal(false);
+ setVisible(true);
+ }
+
+ /**
+ * This method is setting up the content pane for the Feedback form window.
+ * (Precondition: (window != null))
+ * @param window the content pane of Feedback form window.
+ */
+ public void createUI(Container window)
+ {
+ JPanel container;
+ container = new JPanel();
+ container.setBackground(bckcolor);
+ container.setLayout(new BoxLayout(container,BoxLayout.PAGE_AXIS));
+
+ String opening_text;
+ opening_text = messages.getString("FeedbackOpeningText");
+ JTextArea textArea;
+ textArea = new JTextArea(opening_text);
+ textArea.setMinimumSize(new Dimension(100,100));
+ textArea.setOpaque(false);
+ textArea.setEditable(false);
+ textArea.setLineWrap(true);
+ textArea.setWrapStyleWord(true);
+ textArea.setBorder(new EmptyBorder(10,10,0,10));
+ textArea.setBackground(bckcolor);
+ container.add(textArea);
+
+ tempPane = new JPanel();
+ tempPane.setLayout(new BorderLayout());
+ tempPane.setBackground(new Color(176,208,176));
+
+ paneArray = new ArrayList();
+ picture_scroll = new JPanel();
+ row = 1;
+ picture_scroll.setLayout(new GridLayout(0,1));
+ picture_scroll.setBackground(new Color(176,208,176));
+ tempPane.add(picture_scroll,BorderLayout.PAGE_START);
+
+ JPanel btnPanel;
+ btnPanel = new JPanel();
+ btnPanel.setBackground(new Color(176,208,176));
+ remove = new JButton("Remove");
+ remove.setActionCommand("Remove");
+ remove.addActionListener(new ActionListener ()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ Object obj;
+ JPanel pane;
+
+ if (paneArray.size() == 0)
+ System.out.println("There is no panel here!");
+ else
+ {
+ pane = (JPanel) paneArray.get((row - 2));
+ obj = picAndPane.get(pane);
+ obj = null;
+ picAndPane.remove(pane);
+ paneArray.remove((row - 2));
+
+ if (paneArray.size() == 0)
+ remove.setVisible(false);
+
+ picture_scroll.remove(pane);
+
+ row--;
+
+ picture_scroll.setSize(picture_scroll.getPreferredSize());
+ picture_scroll.revalidate();
+ picture_scroll.repaint();
+ tempPane.setSize(tempPane.getPreferredSize());
+ tempPane.revalidate();
+ tempPane.repaint();
+ }
+ }
+ });
+ remove.setToolTipText("Remove last thumbnail");
+ remove.setBackground(new Color(176,208,176));
+ remove.setVisible(false);
+ btnPanel.add(remove);
+ tempPane.add(btnPanel,BorderLayout.PAGE_END);
+
+ JScrollPane pict_scroll;
+ pict_scroll = new JScrollPane(tempPane);
+ pict_scroll.setBackground(bckcolor);
+ pict_scroll.setPreferredSize(new Dimension(300,300));
+ pict_scroll.setBorder(new EmptyBorder(0,10,10,10));
+ container.add(pict_scroll);
+
+ JPanel pane;
+ pane = new JPanel();
+ pane.setBackground(bckcolor);
+ pane.setLayout(new GridLayout(0,2));
+ pane.setBorder(new EmptyBorder(10,10,10,10));
+
+ JLabel txt_type;
+ txt_type = new JLabel(messages.getString("Whatkindofproblemisit") + "? ");
+ txt_type.setBackground(bckcolor);
+ pane.add(txt_type);
+
+ String [] type = {" ",messages.getString("ContentError"),
+ messages.getString("ImageNotShow"),
+ messages.getString("StrangeBehaviour"),
+ messages.getString("SomethingUnexpectedHappen"),
+ messages.getString("HardToUse"),
+ messages.getString("Other")};
+ problem_type = new JComboBox(type);
+ problem_type.setOpaque(false);
+ problem_type.addActionListener(this);
+ problem_type.setBackground(bckcolor);
+ pane.add(problem_type);
+
+ JLabel txt_urgent;
+ txt_urgent = new JLabel(messages.getString("Howbadistheproblem")+"? ");
+ txt_urgent.setBackground(bckcolor);
+ pane.add(txt_urgent);
+
+ String[] urgent = {" ",messages.getString("Critical"),
+ messages.getString("Serious"),
+ messages.getString("Medium"),messages.getString("Minor"),
+ messages.getString("Trivial")};
+ problem_urgency = new JComboBox(urgent);
+ problem_urgency.setOpaque(false);
+ problem_urgency.addActionListener(this);
+ problem_urgency.setBackground(bckcolor);
+ pane.add(problem_urgency);
+
+ /*JLabel txt_screenshot;
+ txt_screenshot= new JLabel(messages.getString("Doyouwanttosendthescreenshot") + "? ");
+ txt_screenshot.setBackground(new Color(108,168,108));
+ pane.add(txt_screenshot);
+
+ String[] screenshot = {messages.getString("Yes"),
+ messages.getString("No")};
+ problem_screenshot = new JComboBox(screenshot);
+ problem_screenshot.addActionListener(this);
+ problem_screenshot.setBackground(new Color(108,168,108));
+ pane.add(problem_screenshot);*/
+
+
+ /*JLabel txt_smtp;
+ txt_smtp = new JLabel(messages.getString("SMTPaddress") + "? ");
+ txt_smtp.setBackground(new Color(176,208,176));
+ pane.add(txt_smtp);
+
+ smtp_address = new JTextField();
+ smtp_address.setBackground(new Color(224,240,224));
+ pane.add(smtp_address);*/
+
+
+ /*JLabel txt_email;
+ txt_email = new JLabel(messages.getString("Emailaddress") + "? ");
+ txt_email.setBackground(new Color(176,208,176));
+ pane.add(txt_email);
+
+ email_address = new JTextField();
+ email_address.setBackground(new Color(224,240,224));
+ pane.add(email_address);*/
+
+ JLabel empty;
+ empty = new JLabel (" ");
+ empty.setBackground(bckcolor);
+ pane.add(empty);
+ JLabel empty2;
+ empty2 = new JLabel (" ");
+ empty2.setBackground(bckcolor);
+ pane.add(empty2);
+ container.add(pane);
+
+ JPanel panely;
+ panely = new JPanel();
+ panely.setLayout(new GridLayout(0,3));
+ panely.setBackground(bckcolor);
+ panely.setBorder(new EmptyBorder(new Insets(10,10,10,10)));
+
+ view_button = new JButton("Details . . .");
+ view_button.setActionCommand(messages.getString("Preview"));
+ view_button.addActionListener(this);
+ view_button.setToolTipText(messages.getString("ConformationPreview"));
+ view_button.setBackground(bckcolor);
+ panely.add(view_button);
+
+ send_button = new JButton(messages.getString("Send"));
+ send_button.setActionCommand(messages.getString("Send"));
+ send_button.addActionListener(this);
+ send_button.setDefaultCapable(true);
+ send_button.setToolTipText(messages.getString("FeedbackSendButton"));
+ send_button.setBackground(bckcolor);
+ panely.add(send_button);
+
+ notsend_button = new JButton("Cancel");
+ notsend_button.setActionCommand(messages.getString("NotSend"));
+ notsend_button.addActionListener(this);
+ notsend_button.setToolTipText(messages.getString("FeedbackNotSendButton"));
+ notsend_button.setBackground(bckcolor);
+ panely.add(notsend_button);
+ container.add(panely);
+
+ //Align the left edges of the components.
+ textArea.setAlignmentX(Component.LEFT_ALIGNMENT);
+ pict_scroll.setAlignmentX(Component.LEFT_ALIGNMENT);
+ pane.setAlignmentX(Component.LEFT_ALIGNMENT);
+ panely.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+ String about;
+ about = messages.getString("FeedbackAboutText");
+ JTextArea about_txt;
+ about_txt = new JTextArea(about);
+ about_txt.setEditable(false);
+ about_txt.setLineWrap(true);
+ about_txt.setWrapStyleWord(true);
+ about_txt.setBackground(bckcolor);
+ about_txt.setBorder(new EmptyBorder(10,10,10,10));
+
+ String privacy;
+ privacy = messages.getString("FeedbackPrivacyText");
+ JTextArea privacy_txt;
+ privacy_txt = new JTextArea(privacy);
+ privacy_txt.setEditable(false);
+ privacy_txt.setLineWrap(true);
+ privacy_txt.setWrapStyleWord(true);
+ privacy_txt.setBackground(bckcolor);
+ privacy_txt.setBorder(new EmptyBorder(10,10,10,10));
+
+ JTabbedPane tab;
+ tab = new JTabbedPane();
+ tab.setBackground(bckcolor);
+ tab.addTab(messages.getString("Form"),container);
+ tab.addTab(messages.getString("About"),about_txt);
+ tab.addTab(messages.getString("Privacy"),privacy_txt);
+ tab.setBorder(new EmptyBorder(5,5,5,5));
+
+ JPanel panelx;
+ panelx = new JPanel();
+ panelx.setLayout(new BoxLayout(panelx,BoxLayout.PAGE_AXIS));
+ panelx.setBackground(bckcolor);
+ panelx.setBorder(new EmptyBorder(new Insets(10,10,10,10)));
+
+ wait_lbl = new JLabel(messages.getString("Pleasewait"));
+ wait_lbl.setBackground(bckcolor);
+ wait_lbl.setVisible(false);
+ panelx.add(wait_lbl);
+
+ progressBar = new JProgressBar ();
+ progressBar.setBackground(bckcolor);
+ progressBar.setVisible(false);
+ panelx.add(progressBar);
+
+ JLabel emp2;
+ emp2 = new JLabel (" ");
+ emp2.setBackground(bckcolor);
+ panelx.add(emp2);
+
+ wait_lbl.setAlignmentX(Component.CENTER_ALIGNMENT);
+ progressBar.setAlignmentX(Component.CENTER_ALIGNMENT);
+ emp2.setAlignmentX(Component.CENTER_ALIGNMENT);
+
+ final String p1;
+ final String p2;
+ p1 = "Tab";
+ p2 = "ProgressBar";
+
+ final JPanel cards;
+ cards = new JPanel(new CardLayout());
+ cards.setBackground(bckcolor);
+ cards.add(tab,p1);
+ cards.add(panelx,p2);
+
+ window.add(cards);
+
+
+ timer = new Timer(1000, new ActionListener()
+ {
+ public void actionPerformed(ActionEvent evt)
+ {
+ if (finish == true)
+ {
+ if (sendThread != null)
+ {
+ sendThread.interrupt();
+ sendThread = null;
+ }
+
+ if (prevThread != null)
+ {
+ prevThread.interrupt();
+ prevThread = null;
+ }
+
+ Component glass_pane;
+ glass_pane = frame.getGlassPane();
+
+ glass_pane.setVisible(false);
+ glass_pane.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+ glass_pane.removeMouseListener(mouse_blocker_listener);
+
+ wait_lbl.setVisible(false);
+ progressBar.setIndeterminate(false);
+ progressBar.setVisible(false);
+
+ frame.setTitle(messages.getString("SendFeedbackForm"));
+
+ CardLayout cl = (CardLayout)(cards.getLayout());
+ cl.show(cards,p1);
+
+ if (selected == true)
+ frame.dispose();
+
+ timer.stop();
+ }
+ else
+ {
+ Component glass_pane;
+ glass_pane = frame.getGlassPane();
+
+ glass_pane.addMouseListener(mouse_blocker_listener);
+ glass_pane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+ glass_pane.setVisible(true);
+
+ frame.setTitle("Please wait...");
+
+ CardLayout cl = (CardLayout)(cards.getLayout());
+ cl.show(cards,p2);
+
+ wait_lbl.setVisible(true);
+ progressBar.setIndeterminate(true);
+ progressBar.setVisible(true);
+ }
+ }
+ });
+ }
+
+ /**
+ * This method will set the finish variable values.
+ * When the sending files is finished, the sending application will set the finish value to true.
+ * @param fin whether or not the send files is finished.
+ */
+ public static void setFinish (boolean fin)
+ {
+ finish = fin;
+ }
+
+ /**
+ * This is the class to represent the whole panel that hold the screenshot and the comments
+ * about the screenshot.
+ */
+ private class ThumbnailPic implements Serializable
+ {
+ /**
+ * This is the panel that hold a thumbnail for a screenshot and a comments text area.
+ */
+ private JPanel picpane;
+
+ /**
+ * This hold the BufferedImages for the whole screen version of screenshot.
+ */
+ private BufferedImage[] screenimages;
+
+ /**
+ * This hold the BufferedImages for the window version only.
+ */
+ private BufferedImage[] windowimages;
+
+ /**
+ * This is the text area where user can enter their comments.
+ */
+ private JTextArea problem_details;
+
+ /**
+ * This is a flag to tell us which image user wants to send.Its defaults value is false.
+ * If iswindow = true then it means user wants to send the window version only.
+ * If iswindow = false then it means user wants to send the whole screen version.
+ */
+ private boolean iswindow;
+
+ /**
+ * This is the titles for the panel.
+ * Usually this is the title of the window where user wants to do the screenshot.
+ * If the title is more than 40 characters then we will trim it and add ... at the back of it.
+ */
+ private String titles;
+
+ /**
+ * This is the width of the image user wants to send.
+ */
+ private String iw;
+
+ /**
+ * This is the height of the image user wants to send.
+ */
+ private String ih;
+
+ /**
+ * This is the x-coordinate where the window located inside the whole screen image.
+ */
+ private int xcoord;
+
+ /**
+ * This is the y-coordinate where the window located inside the whole screen image.
+ */
+ private int ycoord;
+
+ /**
+ * This is the width of the window inside the whole screen image.
+ */
+ private int width;
+
+ /**
+ * This is the height of the window inside the whole screen image.
+ */
+ private int height;
+
+ /**
+ * This is the constructor that will setup the panel that contains
+ * a thumbnail screenshot and a comments text area.
+ * @param image the whole screen screenshot image.
+ * @param text the title for this pane.
+ * @param x the x-coordinate of the window inside the whole screen image.
+ * @param y the y-coordinate of the window inside the whole screen image.
+ * @param w the width of the window inside the whole screen image.
+ * @param h the height of the window inside the whole screen image.
+ */
+ //public ThumbnailPic (BufferedImage screen,byte[] image,String text)
+ public ThumbnailPic (BufferedImage image,String text,int x,int y,int w,int h)
+ {
+ xcoord = x;
+ ycoord = y;
+ width = w;
+ height = h;
+ iswindow = false;
+ titles = text;
+ screenimages = new BufferedImage[3];
+ windowimages = new BufferedImage[3];
+ //screenimages[0] = screen;
+ screenimages[0] = image;
+ screenimages[1] = null;
+ screenimages[2] = null;
+ windowimages[0] = null;
+ windowimages[1] = null;
+ windowimages[2] = null;
+ addPanel(image);
+ }
+
+ /**
+ * This method will setup the components inside the panel properly.
+ * @param image the whole screen screenshot image.
+ */
+ //public void addPanel (byte[] image)
+ public void addPanel (BufferedImage image)
+ {
+ picpane = new JPanel();
+ picpane.setLayout(new BorderLayout());
+
+ final JButton picture;
+ picture = new JButton();
+
+ int w;
+ int h;
+
+ w = (int) (150/2);
+ h = (int) (150/2);
+
+ iw = "" + image.getWidth();
+ ih = "" + image.getHeight();
+
+ ImageIcon thumbnail;
+ thumbnail = new ImageIcon(image.getScaledInstance(w,h,Image.SCALE_SMOOTH));
+
+ picture.setIcon(thumbnail);
+ picture.setText("Draw . . .");
+ picture.setActionCommand("Scribble");
+ picture.addActionListener(new ActionListener()
+ {
+ public void actionPerformed (ActionEvent e)
+ {
+ frame.addMouseListener(mouse_blocker_listener);
+ frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+ SelectPicture select;
+ select = new SelectPicture(screenimages,windowimages,iswindow,problem_details.getText(),messages);
+ frame.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+ frame.removeMouseListener(mouse_blocker_listener);
+ select.setWindowBounds(xcoord,ycoord,width,height);
+
+ iswindow = select.getIsWindow();
+
+ screenimages[0] = select.getImage3();
+ screenimages[1] = select.getImage();
+ screenimages[2] = select.getImage2();
+
+ if (iswindow == true)
+ {
+ windowimages[0] = select.getWindowImage3();
+ windowimages[1] = select.getWindowImage();
+ windowimages[2] = select.getWindowImage2();
+ }
+ else
+ {
+ windowimages[0] = null;
+ windowimages[1] = null;
+ windowimages[2] = null;
+ }
+
+ problem_details.setText(select.getDetails());
+
+ int w,h;
+ w = (int) (150/2);
+ h = (int) (150/2);
+
+ ImageIcon thumbnail;
+
+ if (iswindow == false)
+ thumbnail = new ImageIcon(screenimages[1].getScaledInstance(w,h,Image.SCALE_SMOOTH));
+ else
+ thumbnail = new ImageIcon(windowimages[1].getScaledInstance(w,h,Image.SCALE_SMOOTH));
+
+ picture.setIcon(thumbnail);
+
+ if (select.getSendNow() == true)
+ {
+ ActionEvent evt;
+ evt = new ActionEvent(send_button,ActionEvent.ACTION_PERFORMED,messages.getString("Send"));
+
+ frame.actionPerformed(evt);
+ }
+ }
+ });
+ picture.setToolTipText("Click if you want to draw on it");
+ picture.setMargin(new Insets(5,5,5,5));
+ picture.setVerticalTextPosition(AbstractButton.BOTTOM);
+ picture.setHorizontalTextPosition(AbstractButton.CENTER);
+ picture.setBackground(new Color(176,208,176));
+ picpane.add(picture,BorderLayout.LINE_START);
+
+ JPanel txtPane;
+ txtPane = new JPanel(new BorderLayout());
+ txtPane.setBackground(new Color(176,208,176));
+ txtPane.setBorder(new EmptyBorder(5,5,5,5));
+
+ JLabel lbldesc = new JLabel("Comments :");
+ lbldesc.setBackground(new Color(176,208,176));
+ txtPane.add(lbldesc,BorderLayout.PAGE_START);
+
+ problem_details = new JTextArea();
+ problem_details.setWrapStyleWord(true);
+ problem_details.setEditable(true);
+ problem_details.setBackground(new Color(224,240,224));
+
+ JScrollPane scroll;
+ scroll = new JScrollPane(problem_details);
+ scroll.setBackground(new Color(176,208,176));
+ scroll.setPreferredSize(new Dimension(300,150));
+ txtPane.add(scroll,BorderLayout.LINE_START);
+
+ picpane.setSize(new Dimension(150,150));
+ picpane.add(txtPane,BorderLayout.CENTER);
+ }
+
+ /**
+ * This method will get the width of the image user wants to send.
+ * @return the width of the image.
+ */
+ public String getWidth()
+ {
+ if (iswindow == false)
+ return "" + screenimages[0].getWidth();
+ else
+ {
+ return "" + windowimages[0].getWidth();
+ }
+ }
+
+ /**
+ * This method will get the height of the image user wants to send.
+ * @return the height of the image.
+ */
+ public String getHeight()
+ {
+ if (iswindow == false)
+ return "" + screenimages[0].getHeight();
+ else
+ {
+ return "" + windowimages[0].getHeight();
+ }
+ }
+
+ /**
+ * This method will get the titles of this panel.
+ * @return the panel's title.
+ */
+ public String getTitles()
+ {
+ return titles;
+ }
+
+ /**
+ * This method will get user comments inside the text area.
+ * @return the user's comments.
+ */
+ public String getProblemDetails ()
+ {
+ String txt;
+ txt = problem_details.getText();
+
+ if (txt == null)
+ return " ";
+ else
+ return txt;
+ }
+
+ /**
+ * This method will setting up the text inside the text area.
+ * @param comments the text set to the text area.
+ */
+ public void setComments (String comments)
+ {
+ problem_details.setText(comments);
+ }
+
+ /**
+ * This method will get the image that user wants to send.
+ * @return image going to be send.
+ */
+ public BufferedImage[] getImages ()
+ {
+ if (iswindow == false)
+ return screenimages;
+ else
+ return windowimages;
+ }
+
+ /**
+ * This method will get the non-scribble lines image that user wants to send.
+ * @return the string representation of the non-scribble lines image.
+ */
+ public String getScreenImage ()
+ {
+ if (iswindow == false)
+ return convert(screenimages[0]);
+ else
+ return convert(windowimages[1]);
+ }
+
+ /**
+ * This method will get the scribble lines image that user wants to send.
+ * @return the string representation of the scribble lines image.
+ */
+ public String getScreenAndLineImage ()
+ {
+ if (iswindow == false)
+ {
+ if (screenimages[1]!= null)
+ return convert(screenimages[1]);
+ else
+ return convert(screenimages[0]);
+ }
+ else
+ {
+ if (windowimages[1]!= null)
+ return convert(windowimages[1]);
+ else
+ return convert(windowimages[0]);
+ }
+ }
+
+ /**
+ * This method will get only scribble lines image that user wants to send.
+ * @return the string representation of the only scribble lines image.
+ */
+ public String getLineForScreenImage ()
+ {
+ if (iswindow == false)
+ {
+ if (screenimages[2] != null)
+ return convert(screenimages[2]);
+ else
+ return "";
+ }
+ else
+ {
+ if (windowimages[2] != null)
+ return convert(windowimages[2]);
+ else
+ return "";
+ }
+ }
+
+ /**
+ * This method will get the panel that hold a thumbnail and a text area.
+ * @return panel that represent the thumbnail and the text area.
+ */
+ public JPanel getPanel()
+ {
+ return picpane;
+ }
+ }
+
+ /**
+ * This method will setting up the comments to be displayed inside the latest added panel.
+ * So if the window where user wants to take the screenshot at the moment is a modal window
+ * then it will setting the comments to ask user to close the window before they can enter
+ * their comments there.
+ * @param modal the modality of the window where user wants to take the screenshot.
+ */
+ public void setComment (boolean modal)
+ {
+ if (paneArray.size() == 0)
+ System.out.println("There is no panel here!");
+ else
+ {
+ JPanel pane;
+ pane = (JPanel) paneArray.get((row - 2));
+ ThumbnailPic obj;
+ obj = (ThumbnailPic) picAndPane.get(pane);
+ String text;
+ if (modal == true)
+ text = "Please close '" + obj.getTitles() + "' window \nbefore you can enter comments here.";
+ else
+ text = " ";
+ obj.setComments(text);
+ }
+ }
+
+ /**
+ * This method will add a panel that contains the thumbnail button and the text area.
+ * @param title the window's title.
+ * @param image the whole screen image.
+ * @param x the window's x-coordinate in the image.
+ * @param y the window's y-coordinate in the image.
+ * @param width the window's width in the image.
+ * @param height the window's height in the image.
+ */
+ //public void addScreenPanel (String title,BufferedImage screen,byte[] image)
+ public void addScreenPanel (String title,BufferedImage image,int x,int y,int width,int height)
+ {
+ final JPanel pane;
+ pane = new JPanel();
+ pane.setLayout(new BorderLayout());
+ pane.setBackground(new Color(176,208,176));
+
+ Border loweredetched;
+ loweredetched = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED);
+
+ String ttl;
+ if (title.length() > 41)
+ ttl = title.substring(0,40) + " . . . ";
+ else
+ ttl = title;
+
+ TitledBorder border;
+ border = BorderFactory.createTitledBorder(loweredetched, title);
+ border.setTitleJustification(TitledBorder.LEFT);
+
+ pane.setBorder(BorderFactory.createCompoundBorder(new EmptyBorder(5,5,5,5),border));
+
+ ThumbnailPic pic;
+ //pic = new ThumbnailPic(screen,image,title);
+ pic = new ThumbnailPic(image,title,x,y,width,height);
+ picAndPane.put(pane,pic);
+ pane.add(pic.getPanel(),BorderLayout.LINE_START);
+
+ remove.setVisible(true);
+ picture_scroll.add(pane);
+ paneArray.add(pane);
+ row++;
+ picture_scroll.setSize(picture_scroll.getPreferredSize());
+ picture_scroll.revalidate();
+ picture_scroll.repaint();
+ tempPane.setSize(tempPane.getPreferredSize());
+ tempPane.revalidate();
+ tempPane.repaint();
+ }
+
+ /**
+ * This method will generate the unique code that will be used as the
+ * reference number of the feedback that user are about to send.
+ * @return the unique code.
+ */
+ public String getPrevData ()
+ {
+ try
+ {
+ UID id = new UID();
+ code = "" + InetAddress.getLocalHost().hashCode() + id.hashCode();
+ }
+ catch (UnknownHostException exp) {}
+ return code;
+ }
+
+ /**
+ * This method allows another class to get the unique code that can be used as
+ * the reference number of the feedback that user are about to send.
+ * This method should be called only if
+ * getPrevData() method already been called before.Otherwise it will return null.
+ * @return the unique code
+ */
+ public static String getCode()
+ {
+ return code;
+ }
+
+ /**
+ * This method will encode BufferedImage into their String representation using
+ * Base64 encoder.
+ * (Precondition: (image != null))
+ * @param image is the image that we want to encode to String.
+ */
+ public String convert (BufferedImage image)
+ {
+ String txt;
+ txt = null;
+
+ try
+ {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ ImageIO.write(image,"jpg",stream);
+ byte[] bytes = stream.toByteArray();
+
+ txt = Base64.encodeBytes(bytes);
+
+ }
+ catch(IOException ex) {}
+
+ return txt;
+ }
+
+ /**
+ * This method is to make sure that there is no thread running at the moment.
+ */
+ private void ensureEventThread()
+ {
+ if(SwingUtilities.isEventDispatchThread())
+ {
+ return;
+ }
+ throw new RuntimeException(messages.getString("file"));
+ }
+
+ /**
+ * This method will create all 6 xml files, jarred them into one file, deletes all xml files and
+ * sending it.
+ * The xml files for:
+ * 1.The history & information of all the actions done and windows opened by the application
+ * after user choose to start the sequence of sending feedback.
+ * 2.The history of all the commands (actions) done by the user before choosing
+ * after start the sequence of sending feedback.
+ * 3.The informations we get from the feedback interface form period.
+ * will be getting from vector and the temp_feedbackhist.log file.
+ * (Precondition: (sx != null) && (vector != null) && (err_array != null))
+ * @param sx the xml parser.
+ * @param vector the vector that hold the history and information.
+ * @param err_array the arrays of information we take from the feedback interface period.
+ * @param imgFile the arrays of information we take from the panels inside feedback interface form.
+ */
+ private void sendState (SaveToXML sx,Vector vector,String[] err_array,String[] imgFile)
+ {
+ String dirname;
+ dirname = "xmlfeedback/";
+
+ File dir = new File("xmlfeedback");
+ if (dir.isDirectory() == false)
+ dir.mkdir();
+
+ sx.saveFeedback(err_array,imgFile);
+
+ sx.open(dirname + "feedbackcontent" + FeedbackInterface.getCode() + ".xml",
+ "HISTORY");
+ SaveToXML sx2;
+ sx2 = new SaveToXML(messages);
+ sx2.open(dirname + "feedbackcommand" + FeedbackInterface.getCode() + ".xml",
+ "COMMANDS");
+
+ for (int i = 0 ; i < vector.size() ; i++)
+ {
+ History log;
+ log = (History) vector.get(i);
+ log.sendXML(sx);
+ log.sendXMLComm(sx2);
+ }
+
+ Vector stack;
+ stack = getPrev_log();
+ if (stack != null)
+ {
+ for (int i = 0 ; i < stack.size() ; i++)
+ {
+ History log;
+ log = (History) stack.get(i);
+ log.sendXML(sx);
+ log.sendXMLComm(sx2);
+ }
+ stack.removeAllElements();
+ System.gc();
+ stack = null;
+ }
+
+ sx2.close("COMMANDS");
+ sx.close("HISTORY");
+
+ getHistPrev_log(sx,sx2);
+
+ ZipFile zip;
+ zip = new ZipFile();
+ zip.sendZipXMLFile();
+
+ File f;
+ f = new File (dirname + "feedback" + FeedbackInterface.getCode() + ".xml");
+ f.delete();
+ f = new File (dirname + "feedbackcontent" + FeedbackInterface.getCode() + ".xml");
+ f.delete();
+ f = new File (dirname + "feedbackcommand" + FeedbackInterface.getCode() + ".xml");
+ f.delete();
+ /*f = new File (dirname + "content" + FeedbackInterface.getCode() + ".xml");
+ f.delete();*/
+ f = new File (dirname + "command" + FeedbackInterface.getCode() + ".xml");
+ f.delete();
+
+ String[] args = new String[5];
+ args[0] = "vl6@cs.waikato.ac.nz";
+ args[1] = dirname + "Jar" + FeedbackInterface.getCode() + "File.jar";
+ args[4] = "lib/parser.jar";
+ args[2] = err_array[19];
+ args[3] = err_array[20];
+ SendHTTP sm = new SendHTTP();
+ //SendMail sm = new SendMail();
+ sm.sendMail(args,messages,FeedbackInterface.getCode());
+ }
+
+ /**
+ * This method will create the 2 xml files.
+ * The xml files are:
+ * 1.The history & information of all the actions done and windows opened by the application
+ * before user choose to start the sequence of sending feedback.
+ * 2.The history of all the commands (actions) done by the user before choosing
+ * to start the sequence of sending feedback.
+ * The information will be getting from history.log file and the temp_history.log file.
+ * (Precondition: (sx != null) && (sx2 != null))
+ * @param sx the xml parser for the history xml file.
+ * @param sx2 the xml parser for the command xml file.
+ */
+ private void getHistPrev_log(SaveToXML sx,SaveToXML sx2)
+ {
+ Vector stack = null;
+ try
+ {
+ File f = new File("history.log");
+ if (f.exists() == true)
+ {
+ FileInputStream fis = new FileInputStream(f);
+ ObjectInputStream ois = new ObjectInputStream(fis);
+
+ stack = (Vector) ois.readObject();
+
+ String dirname;
+ dirname = "xmlfeedback/";
+
+ /*sx.open(dirname + "content" + FeedbackInterface.getCode() + ".xml",
+ "HISTORY");*/
+ sx2.open(dirname + "command" + FeedbackInterface.getCode() + ".xml",
+ "COMMANDS");
+
+ if (stack != null)
+ {
+ for (int i = 0 ; i < stack.size() ; i++)
+ {
+ History log;
+ log = (History) stack.get(i);
+ //log.sendXML(sx);
+ log.sendXMLComm(sx2);
+ }
+ stack.removeAllElements();
+ System.gc();
+ stack = null;
+ }
+
+ stack = (Vector) ois.readObject();
+
+ if (stack != null)
+ {
+ for (int i = 0 ; i < stack.size() ; i++)
+ {
+ History log;
+ log = (History) stack.get(i);
+ //log.sendXML(sx);
+ log.sendXMLComm(sx2);
+ }
+ stack.removeAllElements();
+ System.gc();
+ stack = null;
+ }
+
+ sx2.close("COMMANDS");
+ //sx.close("HISTORY");
+
+ ois.close();
+ }
+ }
+ catch (IOException exp) {exp.printStackTrace();}
+ catch (ClassNotFoundException exp2)
+ {
+ System.out.println("class exp");}
+ }
+
+ /**
+ * This method will get the Vector stored in temp_feedbackhist.log file.
+ * @return the vector stored in temp_feedbackhist.log file.
+ */
+ public Vector getPrev_log()
+ {
+ Vector stack = null;
+ try
+ {
+ File f = new File("temp_feedbackhist.log");
+ if (f.exists() == true)
+ {
+ FileInputStream fis = new FileInputStream(f);
+ ObjectInputStream ois = new ObjectInputStream(fis);
+ stack = (Vector) ois.readObject();
+ ois.close();
+ return stack;
+ }
+ else
+ stack = null;
+ }
+ catch (IOException exp) {exp.printStackTrace();}
+ catch (ClassNotFoundException exp2)
+ {
+ System.out.println("class exp");}
+
+ return stack;
+ }
+
+ /**
+ * This is the thread that will start when user click send button and it will
+ * make 6 xml files,jarred them together into 1 jar file, deletes the 6 xml files and send it.
+ * @param vector the vector stored the current actions history.
+ * @param err_array the array that stored all the information took during feedback interface period.
+ * @param imgFile the array of that stored all the information from all the panels inside feedback interface form.
+ */
+ public void sendMethod (final Vector vector,final String err_array[], final String imgFile[])
+ {
+ ensureEventThread();
+ Runnable sendRun;
+ sendRun = new Runnable()
+ {
+ public void run ()
+ {
+ finish = false;
+ selected = true;
+ SaveToXML sx;
+ sx = new SaveToXML(messages);
+ sendState(sx,vector,err_array,imgFile);
+ finish = true;
+ }
+ };
+ sendThread = new Thread(sendRun);
+ sendThread.start();
+ }
+
+ /**
+ * This method will called the window that will display the preview of all
+ * the file going to be send.
+ * @param vector the vector stored the current actions history.
+ * @param err_array the array that stored all the information took during feedback interface period.
+ * @param imgFile the array of that stored all the information from all the panels inside feedback interface form.
+ */
+ public void viewMethod (final Vector vector,final String err_array[], final String imgFile[])
+ {
+ ensureEventThread();
+ Runnable prevRun;
+ prevRun = new Runnable()
+ {
+ public void run ()
+ {
+ finish = false;
+ selected = false;
+ ReportDetails rd;
+ rd = new ReportDetails(vector,err_array,imgFile,messages,true);
+ finish = true;
+ }
+ };
+
+ prevThread = new Thread(prevRun);
+ prevThread.start();
+ }
+
+ /**
+ * This method will save a BufferedImage to a jpeg file with the specified filename.
+ * This method is a thread that will caused the thread save to be started and stop
+ * if the saving process is finished.
+ * (Precondition: (I != null))
+ * @param I is the BufferedImage that we want to save.
+ * @param picScreen name of the jpeg file where we want to store the BufferedImage
+ */
+ public void saveGraph(final BufferedImage I,final String picScreen)
+ {
+ ensureEventThread();
+ Runnable saveRun;
+ saveRun = new Runnable ()
+ {
+ public void run()
+ {
+ try
+ {
+ File file;
+ file = new File(picScreen);
+ ImageIO.write(I, "jpg", file);
+ save = null;
+ }
+ catch (Exception ex) {}
+ }
+ };
+
+ save = new Thread(saveRun);
+ save.start();
+ }
+
+ /**
+ * This method will get all the informations from the all the panels inside the feedback interface form.
+ * @return the arrays of information from all the panels.
+ */
+ public String[] getimgFile()
+ {
+ String[] imgFile;
+ imgFile = null;
+
+ /*String answer;
+ answer = (String) problem_screenshot.getSelectedItem();
+ if(answer == messages.getString("Yes"))
+ {*/
+
+ if (paneArray.size() > 0)
+ {
+ imgFile = new String[(paneArray.size() * 10)];
+
+ int step;
+ for (step = 0 ; step < (paneArray.size()*10) ; step=step+10)
+ {
+ JPanel pane;
+ ThumbnailPic obj;
+ pane = (JPanel) paneArray.get((step/10));
+ obj = (ThumbnailPic) picAndPane.get(pane);
+
+ //obj.setSize();
+
+ String title;
+ title = obj.getTitles();
+
+ imgFile[step+0] = title;
+ imgFile[step+1] = obj.getScreenImage();
+ title = "Window" + pane.hashCode() + "File";
+ imgFile[step+2] = messages.getString("screen")+title+step+".jpg";
+ imgFile[step+3] = obj.getScreenAndLineImage();
+ imgFile[step+4] = messages.getString("ErrorLineAnd")+imgFile[step+2];
+ imgFile[step+5] = obj.getLineForScreenImage();
+ imgFile[step+6] = messages.getString("ErrorLineFor")+imgFile[step+2];
+ imgFile[step+7] = obj.getWidth();
+ imgFile[step+8] = obj.getHeight();
+ imgFile[step+9] = obj.getProblemDetails();
+ }
+ }
+
+ return imgFile;
+ }
+
+ /**
+ * This method will get all the informations taken during feedback interface period, such as os version.
+ * @return then arrays of informations.
+ */
+ public String[] geterr_array()
+ {
+ String[] err_array;
+ err_array = null;
+
+ String os_name;
+ os_name = System.getProperty("os.name") + " " + System.getProperty("os.version")
+ + " " + System.getProperty("os.arch");
+
+ Date problem_date;
+ String err_date,java_info,screen_res;
+ problem_date = new Date();
+ err_date = problem_date.toString();
+ java_info = System.getProperty("java.vendor")+ " " + System.getProperty("java.class.version");
+ Dimension dim;
+ dim = Toolkit.getDefaultToolkit().getScreenSize();
+ screen_res = "" + dim.width + "x" + dim.height;
+
+ try
+ {
+ err_array = new String[24];
+
+ err_array[0] = code;
+ err_array[1] = frameName;
+ err_array[2] = "";
+ err_array[3] = (String) problem_type.getSelectedItem();
+ err_array[4] = (String) problem_urgency.getSelectedItem();
+ err_array[5] = System.getProperty("user.name");
+ err_array[6] = System.getProperty("user.home");
+ err_array[7] = System.getProperty("user.dir");
+ err_array[8] = curr_date.toString();
+ err_array[9] = err_date;
+ err_array[10] = os_name;
+ err_array[11] = java_info;
+ err_array[12] = "" + Locale.getDefault();
+ err_array[13] = Gatherer.PROGRAM_NAME + " " + Gatherer.PROGRAM_VERSION;
+ err_array[14] = InetAddress.getLocalHost().getHostName();
+ err_array[15] = InetAddress.getLocalHost().getHostAddress();
+ err_array[16] = screen_res;
+ err_array[17] = "";
+
+ /*String test_txt;
+ test_txt = smtp_address.getText();
+ if ((test_txt == null)||(test_txt.length() < 5))*/
+ err_array[19] = "mail.waikato.ac.nz";
+ /*else
+ err_array[19] = test_txt;
+
+ test_txt = email_address.getText();
+ if ((test_txt == null)||(test_txt.length() < 5))*/
+ err_array[20] = "vl6@cs.waikato.ac.nz";
+ //else
+ //err_array[20] = test_txt;
+
+ Runtime run;
+ run = Runtime.getRuntime();
+ err_array[21] = formatFileSize(run.totalMemory());
+ err_array[22] = formatFileSize(run.maxMemory());
+ err_array[23] = formatFileSize(run.freeMemory());
+ }
+ catch(UnknownHostException exp) {}
+
+ return err_array;
+ }
+
+ /**
+ * This method will format the file size given into more elegant form,e.g. 2KB
+ * @param length the file size.
+ * @return the more elegant string representations of the file size.
+ */
+ public String formatFileSize(long length)
+ {
+ String BYTE_SUFFIX;
+ BYTE_SUFFIX = " B";
+ long GIGABYTE;
+ GIGABYTE = 1024000000l;
+ String GIGABYTE_SUFFIX;
+ GIGABYTE_SUFFIX = " GB";
+ long KILOBYTE;
+ KILOBYTE = 1024l;
+ String KILOBYTE_SUFFIX;
+ KILOBYTE_SUFFIX = " KB";
+ long MEGABYTE;
+ MEGABYTE = 1024000l;
+ String MEGABYTE_SUFFIX;
+ MEGABYTE_SUFFIX = " MB";
+
+ StringBuffer result;
+ result = new StringBuffer("");
+ float number;
+ number = 0f;
+ String suffix;
+ suffix = null;
+
+ if(length >= GIGABYTE)
+ {
+ number = (float) length / (float) GIGABYTE;
+ suffix = GIGABYTE_SUFFIX;
+ }
+ else if(length >= MEGABYTE)
+ {
+ number = (float) length / (float) MEGABYTE;
+ suffix = MEGABYTE_SUFFIX;
+ }
+ else if(length >= KILOBYTE)
+ {
+ number = (float) length / (float) KILOBYTE;
+ suffix = KILOBYTE_SUFFIX;
+ }
+ else
+ {
+ return length + BYTE_SUFFIX;
+ }
+
+ String number_str;
+ number_str = Float.toString(number);
+ char number_char[] = number_str.toCharArray();
+ int pos,i;
+ pos = 0;
+
+ while(number_char != null && pos < number_char.length && number_char[pos] != '.')
+ {
+ result.append(number_char[pos]);
+ pos++;
+ }
+
+ if(pos < number_char.length)
+ {
+ result.append(number_char[pos]);
+ pos++;
+
+ for(i = 0; i < 2 && pos < number_char.length; i++, pos++)
+ result.append(number_char[pos]);
+
+ while(pos < number_char.length && number_char[pos] != 'E')
+ pos++;
+
+ while(pos < number_char.length)
+ {
+ result.append(number_char[pos]);
+ pos++;
+ }
+ }
+
+ result.append(suffix);
+
+ return result.toString();
+ }
+
+ /**
+ * This method will decide what action should be done if the user entered a button in
+ * the Feedback form.
+ * If user click cancel button then all the information taken in this form will be deleted
+ * and user will be quitted from the Reporting Feedback sequence.
+ * If user click the send button then all the information taken will be stored in array which
+ * then will be saved into xml files and sent when user confirm to send all the files.
+ * If user click the details button then they can view all the xml files that are going to be send.
+ * @param e the button that user clicked
+ */
+ public void actionPerformed (ActionEvent e)
+ {
+ getPrevData();
+
+ if(messages.getString("Send").equals(e.getActionCommand()))
+ {
+ String[] imgFile;
+ String[] err_array;
+
+ imgFile = getimgFile();
+ err_array = geterr_array();
+
+ timer.start();
+
+ sendMethod(dialog.getVector(),err_array,imgFile);
+ }
+
+ if(messages.getString("Preview").equals(e.getActionCommand()))
+ {
+ String[] imgFile;
+ String[] err_array;
+
+ imgFile = getimgFile();
+ err_array = geterr_array();
+
+ timer.start();
+
+ viewMethod(dialog.getVector(),err_array,imgFile);
+ }
+
+ if(messages.getString("NotSend").equals(e.getActionCommand()))
+ {
+ dispose();
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/Graphs.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/Graphs.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/Graphs.java (revision 31635)
@@ -0,0 +1,360 @@
+package org.greenstone.gatherer.feedback;
+
+import java.awt.image.*;
+import java.awt.geom.*;
+import java.awt.geom.Line2D.*;
+import java.awt.geom.Line2D.Double;
+import java.io.*;
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * This is the class that will allow user to draw a same line to 2 different BufferedImage
+ * at the same time.
+ * The first BufferedImage is BufferedImage with some Image as the based and then
+ * we draw a line on it, and the second BufferedImage is just a transparent Image and then we
+ * draw a line on it. The line drawn in first BufferedImage is exactly the same with the line
+ * drawn in second BufferedImage.The Image drawn in the first bufferedimage is the
+ * screen shot image of the whole screen.
+ * @author Veronica Liesaputra
+ */
+public class Graphs
+{
+ /**
+ * This is the first buffered image.
+ * It will drawn with an image first to be its based, and a line
+ * that are identical to line drawn the second BufferedImage, will be drawn on it.
+ */
+ private BufferedImage I = null;
+
+ /**
+ * This is the second buffered image.
+ * Its a transaparent buffered image and a line that are indetical to line
+ * drawn in the first BufferedImage, will be drawn on it.
+ */
+ private BufferedImage I2;
+
+ /**
+ * This is the Graphics for the first BufferedImage.
+ * This will allow user to draw a line and an image on the first buffered image.
+ */
+ private Graphics2D G;
+
+ /**
+ * This is the Graphics for the second BufferedImage.
+ * This will allow user to draw a line on the second buffered image.
+ */
+ private Graphics2D G2;
+
+ /**
+ * This is the arrays of the 3 BufferedImage.
+ */
+ private BufferedImage[] screen;
+
+ /**
+ * This is the graphics for screen[0] BufferedImage.
+ * This will allow user to draw something on the screen[0] image.
+ */
+ private Graphics2D G3;
+
+ /**
+ * This is the width of the image.
+ */
+ private double imwidth;
+
+ /**
+ * This is the height of the image.
+ */
+ private double imheight;
+
+ /**
+ * This is the constructor that will initialised the BufferedImage and
+ * its Graphics.
+ * (Precondition: (sh != null))
+ * @param sh is the images.
+ */
+ public Graphs (BufferedImage[] sh)
+ {
+ screen = sh;
+
+ if (screen[0] != null)
+ {
+ imwidth = screen[0].getWidth();
+ imheight = screen[0].getHeight();
+
+
+ if((screen[1] == null)&& (screen[2] == null))
+ {
+ I = new BufferedImage(screen[0].getWidth(),screen[0].getHeight(),
+ BufferedImage.TYPE_INT_RGB);
+ I2 = new BufferedImage(I.getWidth(),I.getHeight(),
+ BufferedImage.TYPE_INT_ARGB);
+ }
+ else
+ {
+ I = screen[1];
+ I2 = screen[2];
+ }
+ start();
+ }
+ else
+ System.out.println("weird");
+ }
+
+ /**
+ * This method will make sure that everything is null-ed and dispose properly.
+ */
+ public void flush()
+ {
+ G.dispose();
+ G = null;
+ G3.dispose();
+ G3 = null;
+ G2.dispose();
+ G2 = null;
+
+ int i;
+ for ( i = 0 ; i < 3; i++)
+ {
+ if (screen[i] != null)
+ {
+ screen[i].flush();
+ screen[i] = null;
+ }
+ }
+
+ I.flush();
+ I = null;
+ I2.flush();
+ I2 = null;
+ System.gc();
+ }
+
+ /**
+ * This method will cleared the scribble lines in the images.
+ */
+ public void reset()
+ {
+ I.flush();
+ I = null;
+ System.gc();
+ I2.flush();
+ I2 = null;
+ G.dispose();
+ System.gc();
+ G2.dispose();
+ G = null;
+ G2 = null;
+ System.gc();
+
+ I = new BufferedImage(screen[0].getWidth(),
+ screen[0].getHeight(),
+ BufferedImage.TYPE_INT_RGB);
+ I2 = new BufferedImage(I.getWidth(),I.getHeight(),
+ BufferedImage.TYPE_INT_ARGB);
+
+ screen[1] = null;
+ screen[2] = null;
+
+ start();
+ }
+ /**
+ * This method will setup the 3 Graphics for the 3 BufferedImage.
+ * Here the graphics for the bufferedimage will be created and the appropriate image
+ * will be drawn to the first bufferedimage.
+ */
+ public void start ()
+ {
+ G3 = screen[0].createGraphics();
+ G3.drawImage((Image) screen[0],0,0,new JLabel());
+
+ G = I.createGraphics();
+ if (screen[1] == null)
+ G.drawImage((Image) screen[0],0,0,new JLabel());
+ else
+ G.drawImage ((Image) screen[1],0,0,new JLabel());
+ G2 = I2.createGraphics();
+ }
+
+ /**
+ * This method will draw a red line on both BufferedImage from the source point
+ * to the destination point.
+ * @param x1 is the x-coordinate of the source point.
+ * @param y1 is the y-coordinate of the source point.
+ * @param x2 is the x-coordinate of the destination point.
+ * @param y2 is the y-coordinate of the destination point.
+ */
+ public void drawLines(int x1,int y1,int x2,int y2)
+ {
+ G2.setColor(Color.red);
+ G.setColor(Color.red);
+
+ G.setStroke(new BasicStroke(2.5f));
+ G2.setStroke(new BasicStroke(2.5f));
+
+ G2.drawLine(x1,y1,x2,y2);
+ G.drawLine(x1,y1,x2,y2);
+ }
+
+
+ /**
+ * This method will draw a white line on all 3 BufferedImage from the source point
+ * to the destination point.
+ * @param x1 is the x-coordinate of the source point.
+ * @param y1 is the y-coordinate of the source point.
+ * @param x2 is the x-coordinate of the destination point.
+ * @param y2 is the y-coordinate of the destination point.
+ */
+ public void eraseLines(int x1,int y1,int x2,int y2)
+ {
+ G3.setColor(Color.white);
+ G2.setColor(Color.white);
+ G.setColor(Color.white);
+
+ G.setStroke(new BasicStroke(25.5f));
+ G2.setStroke(new BasicStroke(25.5f));
+ G3.setStroke(new BasicStroke(25.5f));
+
+ G3.drawLine(x1,y1,x2,y2);
+ G2.drawLine(x1,y1,x2,y2);
+ G.drawLine(x1,y1,x2,y2);
+ }
+
+ /**
+ * This method will get the first BufferedImage.
+ * This is the BufferedImage where it will already be drawn with the
+ * screen shot image and the lines.
+ * @return the first BufferedImage.
+ */
+ public BufferedImage getImage ()
+ {
+ return I;
+ }
+
+ /**
+ * This method will get the second BufferedImage.
+ * This is the BufferedImage where its base is transparent and it will
+ * already be drawn with the lines.
+ * @return the second BufferedImage.
+ */
+ public BufferedImage getImage2()
+ {
+ return I2;
+ }
+
+ /**
+ * This method will get the image without scribble lines only the white lines.
+ * @return the image without scribble lines.
+ */
+ public BufferedImage getScreenImage()
+ {
+ return screen[0];
+ }
+
+ /**
+ * This method will get the window out of the whole screen image.
+ * @param xcoord the xcoordinate of the window in the image.
+ * @param ycoord the ycoordinate of the window in the image.
+ * @param width the width of the window in the image.
+ * @param height the height of the window in the image.
+ * @return the BufferedImage of the window only from the 3 buffered image.
+ */
+ public BufferedImage[] getWindowVersion(int xcoord,int ycoord,int width,int height)
+ {
+ BufferedImage[] window;
+ window = new BufferedImage[3];
+
+ Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
+
+ if (xcoord < 0 )
+ xcoord = 0;
+ if (ycoord < 0)
+ ycoord = 0;
+
+ if (xcoord >= imwidth)
+ xcoord = (int)(imwidth - 1);
+ if (ycoord >= imheight)
+ ycoord = (int)(imheight - 1);
+ if ((xcoord + width) > imwidth)
+ width = (int)(imwidth - xcoord);
+ if ((ycoord + height) > imheight)
+ height = (int)(imheight - ycoord);
+
+ if (width <= 0)
+ width = 1;
+ if (height <= 0)
+ height = 1;
+
+ window[0] = getClipVersion(screen[0],xcoord,ycoord,width,height);
+
+ window[1] = getClipVersion(I,xcoord,ycoord,width,height);
+
+ window[2] = getClipVersion(I2,xcoord,ycoord,width,height);
+
+ return window;
+ }
+
+ /**
+ * This method will return the subimage of the big buffered image.
+ * @param tag the original buffered image.
+ * @param x the x location of subimage.
+ * @param y the y location of subimage.
+ * @param w the width of the subimage.
+ * @param h the height of the subimage.
+ * @return the subimage.
+ */
+ public BufferedImage getClipVersion (BufferedImage tag,int x, int y, int w, int h)
+ {
+ BufferedImage sub;
+ sub = tag.getSubimage(x,y,w,h);
+
+ return sub;
+ }
+
+ /**
+ * This method will return the screen image version of the give buffered image.
+ * @param sh the buffered image that we going to draw in the screen image version.
+ * @param xcoord the x location of subimage.
+ * @param ycoord the y location of subimage.
+ * @param w the width of the subimage.
+ * @param h the height of the subimage.
+ */
+ public void setScreenVersion(BufferedImage[] sh,int xcoord,int ycoord,int w, int h)
+ {
+ if (xcoord < 0 )
+ xcoord = 0;
+ if (ycoord < 0)
+ ycoord = 0;
+
+ if (xcoord >= imwidth)
+ xcoord = (int) (imwidth - 1);
+ if (ycoord >= imheight)
+ ycoord = (int) (imheight - 1);
+ if ((xcoord + w) > imwidth)
+ w = (int) (imwidth - xcoord);
+ if ((ycoord + h) > imheight)
+ h = (int) (imheight - ycoord);
+
+ if (w <= 0)
+ w = 1;
+ if (h <= 0)
+ h = 1;
+
+ G3.drawImage(sh[0], xcoord,ycoord,new JLabel());
+
+
+ G.drawImage(sh[1], xcoord,ycoord,new JLabel());
+
+
+ G2.drawImage(sh[2], xcoord,ycoord,new JLabel());
+ }
+}
+
+
+
+
+
+
+
+
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/History.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/History.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/History.java (revision 31635)
@@ -0,0 +1,284 @@
+package org.greenstone.gatherer.feedback;
+
+import java.util.*;
+import java.awt.image.*;
+import java.io.*;
+import javax.imageio.*;
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * This class is one record of the action user did to any of the component
+ * inside the window that the application open that is recorded by the
+ * special listener class.
+ * An instance of this class will hold all the information about the action
+ * user did at that moment,all information about all the components inside the
+ * window where the action occured and all information about all the components
+ * inside all the windows that were opened when the action occured.
+ * @author Veronica Liesaputra
+ */
+public class History implements Serializable
+{
+ /**
+ * This is the date of when the action is done by the user.
+ */
+ private String date;
+
+ /**
+ * This is the command that user did.
+ */
+ private String command;
+
+ /**
+ * This is the title of the window where the action occured.
+ */
+ private String title;
+
+ /**
+ * This hold all information about all the components inside the
+ * window where the action occured and all information about all the components
+ * inside all the windows that were open when the action occured.
+ */
+ private ArrayList component;
+
+ /**
+ * This hold the image of the window where the action occured.
+ */
+ private String image = null;
+
+ /**
+ * This is the image file name, if then user decide to save the image to a jpeg file.
+ */
+ private String imageName = "";
+
+ /**
+ * This is the height of image of the window where the action occured.
+ */
+ private String height;
+
+ /**
+ * This is the width of image of the window where the action occured.
+ */
+ private String width;
+
+ /**
+ * This constructor will be initializing the date,command and component value.
+ * (Precondition: (curr_date != null))
+ * @param curr_date is the date when the action occured.
+ * @param curr_command is the command the user did.
+ */
+ public History (Date curr_date,String curr_command)
+ {
+ date = curr_date.toString();
+ command = curr_command;
+ component = new ArrayList();
+ }
+
+ /**
+ * This method will be setting up the image attributes to the passed values.
+ * It will setting the image of the window where the action occured and its height
+ * and width.It will also generate the supposed file name for this image.
+ * (Precondition: (imag != null) && (h != null) && (w != null))
+ * @param img the image of the window where the action occured.
+ * @param h image's height.
+ * @param w image's width.
+ */
+ public void setImage(String img,String h,String w)
+ {
+ image = img;
+ height = h;
+ width = w;
+ StringTokenizer st;
+ st = new StringTokenizer(date,": ");
+ while (st.hasMoreTokens())
+ {
+ imageName += st.nextToken();
+ }
+ imageName += ".jpg";
+ }
+
+ /**
+ * This method will add a new CompGroup instance that hold information and
+ * the status of a window that were open when the action occured.
+ * @param comp UserComponent instance that hold information about all
+ * components inside the window.
+ * @param curr_status The current status of the window.
+ */
+ public void addComponent (UserComponent comp,String curr_status)
+ {
+ component.add(new CompGroup(comp,curr_status));
+ }
+
+ /**
+ * This method will get the image's height of the window where the action
+ * occured.
+ * @return image's height.
+ */
+ public String getHeight ()
+ {
+ return height;
+ }
+
+ /**
+ * This method will get the image's width of the window where the action
+ * occured.
+ * @return image's width.
+ */
+ public String getWidth ()
+ {
+ return width;
+ }
+
+ /**
+ * This method will set the title of the window where the action occured.
+ * @param name window's title.
+ */
+ public void setTitle (String name)
+ {
+ title = name;
+ }
+
+ /**
+ * This method will get the title of the window where the action occured.
+ * @return the window's title.
+ */
+ public String getTitle ()
+ {
+ return title;
+ }
+
+ /**
+ * This method will get the date when the action occured.
+ * @return the date when the action occured.
+ */
+ public String getDate()
+ {
+ return date;
+ }
+
+ /**
+ * This method will get the command that user did.
+ * @return the command user did.
+ */
+ public String getCommand()
+ {
+ return command;
+ }
+
+ /**
+ * This method will get the array of CompGroup instances that hold
+ * all the information and status of all the window that were open when
+ * the action occured.
+ * @return the array list of the CompGroup instances.
+ */
+ public ArrayList getArray()
+ {
+ return component;
+ }
+
+ /**
+ * This method will get the image of the window where the action occured.
+ * @return image the window's image.
+ */
+ public String getImage()
+ {
+ return image;
+ }
+
+ /**
+ * This method will get the supposed image's file name.
+ * @return image's file name.
+ */
+ public String getImageName()
+ {
+ return imageName;
+ }
+
+ /**
+ * This method will stored this instance to an xml file.
+ * It will only store the date,title,command,image and all
+ * image attributes of this instance to the xml file.
+ * @param sx is the xml file where we want to store.
+ */
+ public void sendXMLComm (SaveToXML sx)
+ {
+ sx.startContent(10);
+
+ sx.startContent(7);
+ sx.saveContent(date);
+ sx.closeContent(7);
+
+ sx.startContent(2);
+ sx.saveContent(title);
+ sx.closeContent(2);
+
+ sx.startContent(8);
+ sx.saveContent(command);
+ sx.closeContent(8);
+
+ if (image != null)
+ {
+ sx.startContent(6);
+ sx.saveImage(image,imageName,width,height);
+ sx.closeContent(6);
+ }
+
+ sx.closeContent(10);
+ }
+
+ /**
+ * This method will stored this instance to an xml file.
+ * It will store all the data members of this instance to
+ * the xml file.
+ * @param sx is the xml file where we want to store.
+ */
+ public void sendXML (SaveToXML sx)
+ {
+ sx.startContent(10);
+
+ sx.startContent(7);
+ sx.saveContent(date);
+ sx.closeContent(7);
+
+ sx.startContent(2);
+ sx.saveContent(title);
+ sx.closeContent(2);
+
+ sx.startContent(8);
+ sx.saveContent(command);
+ sx.closeContent(8);
+
+ if (image != null)
+ {
+ sx.startContent(6);
+ sx.saveImage(image,imageName,width,height);
+ sx.closeContent(6);
+ }
+
+ int j;
+ for (j = 0 ; j < component.size() ; j++)
+ {
+ sx.startContent(9);
+ CompGroup comp;
+ comp = (CompGroup) component.get(j);
+
+ sx.startContent(8);
+ sx.saveContent(comp.getStatus());
+ sx.closeContent(8);
+
+ sx.startContent(0);
+ comp.getComponent().sendingXML(sx);
+ sx.closeContent(0);
+
+ sx.closeContent(9);
+ }
+ sx.closeContent(10);
+ }
+}
+
+
+
+
+
+
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/Movie.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/Movie.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/Movie.java (revision 31635)
@@ -0,0 +1,375 @@
+package org.greenstone.gatherer.feedback;
+
+import java.awt.image.*;
+import javax.swing.*;
+import java.awt.event.*;
+import java.awt.*;
+import javax.swing.event.*;
+import java.io.*;
+import java.util.ResourceBundle;
+import java.text.MessageFormat;
+import javax.swing.text.NumberFormatter;
+import java.text.NumberFormat;
+import java.beans.*;
+import java.util.ArrayList;
+
+/**
+ * This is the class to generate a animation type from a list of ImageIcon.
+ * It will display a JDialog window and then user can specify the frames
+ * per second rate that the user want to use to view the list of the ImageIcon.
+ * @author Veronica Liesaputra
+ */
+public class Movie extends JDialog
+ implements ActionListener,ChangeListener,PropertyChangeListener
+{
+ /**
+ * This variable will hold the resource of the words that is stored in Messages.properties file.
+ * The calling using messages.getString(someString) will caused someString to be translated
+ * into some other string that is hold in that file.usually it will caused it to be translated
+ * to the language that the user use or choose to have as stated in Locale.
+ */
+ private static ResourceBundle messages;
+
+ /**
+ * This is the label that we use to display the ImageIcon.
+ */
+ private JLabel picture;
+
+ /**
+ * This is a textField where user can set how many frames per second they want to view the
+ * ImageIcon.If user enter value that is bigger than the FPS_MAX then it will beep and user
+ * will need to enter new valid value or it will stay as the previous valid value.
+ * If user enter 0 then it will stop the animation.
+ */
+ private JFormattedTextField textField;
+
+ /**
+ * This is a slider where user can set how many frames per second they want to view the
+ * ImageIcon.If user slide to 0 then animation stop.
+ */
+ private JSlider framesPerSecond;
+
+ /**
+ * This is the minimum frame per second value.
+ */
+ private int FPS_MIN = 0;
+
+ /**
+ * This is the maximum frame per second value. The value is the twice the amount of
+ * ImageIcon in the list.
+ */
+ private int FPS_MAX;
+
+ /**
+ * This is the initial frame per second value to view the ImageIcon.
+ */
+ private int FPS_INIT = 1;
+
+ /**
+ * This is the ImageIcon index that are currently showing in the label.
+ */
+ private int frameNumber;
+
+ /**
+ * This is the total number of ImageIcon in the list.
+ */
+ private int NUM_FRAMES;
+
+ /**
+ * This hold the list of the ImageIcon that are to be displayed.
+ */
+ private ArrayList images;
+
+ /**
+ * This is telling how long is the delay for each frame to be refreshed.
+ */
+ private int delay;
+
+ /**
+ * This is the timer that generate the animation of the list of ImageIcon.
+ * The animation will be paused twice per cycle by restarting the timer.
+ * If the the prame per second chosen by the user is 0 then the timer
+ * will be stop.
+ */
+ private Timer timer;
+
+ /**
+ * This the flag to say when the animation should stop.
+ * If frozen = true then the animation should stop otherwise the animation
+ * should keep running.
+ */
+ private boolean frozen = false;
+
+ /**
+ * This will sho a modal-dialog and set up the value for the animation to run properly
+ * according to the list of ImageIcon passed in.
+ * @param msg hold the resource of the words that is stored in Messages.properties file.
+ * @param img hold the list of ImageIcon to be displayed.
+ */
+ public Movie (ResourceBundle msg,ArrayList img)
+ {
+ super();
+
+ images = img;
+ NUM_FRAMES = img.size();
+ FPS_MAX = img.size() * 2;
+
+ messages = msg;
+ setTitle(messages.getString("GenerateMovie"));
+ setModal(true);
+ setBackground(new Color (176,208,176));
+ }
+
+ /**
+ * This method will seeting up the content pane of the dialog window.
+ * It also setting up the timer and the delay.
+ * @return the content pane of the window.
+ */
+ public JPanel create_UI()
+ {
+ JPanel cont;
+ cont = new JPanel();
+ cont.setOpaque(true);
+ cont.setBackground(new Color (176,208,176));
+
+ JButton cls;
+ cls = new JButton(messages.getString("Close"));
+ cls.addActionListener(new ActionListener ()
+ {
+ public void actionPerformed (ActionEvent e)
+ {
+ dispose();
+ if (timer != null)
+ timer.stop();
+ }
+ });
+ cls.setDefaultCapable(true);
+ cls.setActionCommand(messages.getString("Close"));
+ cls.setBackground(new Color(176,208,176));
+
+
+ if (images.size() <= 0)
+ {
+ JLabel empty_lbl;
+ empty_lbl = new JLabel ("No Image to be displayed ");
+ cont.add(empty_lbl);
+ cont.add(cls);
+ return cont;
+ }
+
+ cont.setLayout(new BoxLayout(cont, BoxLayout.PAGE_AXIS));
+
+ delay = 1000 / FPS_INIT;
+
+ JLabel sliderLabel;
+ sliderLabel = new JLabel(messages.getString("FramesPerSecond") + ": ", JLabel.CENTER);
+ sliderLabel.setBackground(new Color(176,208,176));
+ sliderLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
+
+ NumberFormat numberFormat;
+ numberFormat = NumberFormat.getIntegerInstance();
+ NumberFormatter formatter;
+ formatter = new NumberFormatter(numberFormat);
+ formatter.setMinimum(new Integer(FPS_MIN));
+ formatter.setMaximum(new Integer(FPS_MAX));
+
+ textField = new JFormattedTextField(formatter);
+ textField.setBackground(new Color(224,240,224));
+ textField.setValue(new Integer(FPS_INIT));
+ textField.setColumns(5);
+ textField.addPropertyChangeListener(this);
+
+ textField.getInputMap().put(KeyStroke.getKeyStroke(
+ KeyEvent.VK_ENTER, 0),
+ "check");
+ textField.getActionMap().put("check", new AbstractAction()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ if (!textField.isEditValid())
+ {
+ Toolkit.getDefaultToolkit().beep();
+ textField.selectAll();
+ }
+ else
+ {
+ try
+ {
+ textField.commitEdit();
+ }
+ catch (java.text.ParseException exc) { }
+ }
+ }
+ });
+
+ framesPerSecond = new JSlider(JSlider.HORIZONTAL,
+ FPS_MIN, FPS_MAX, FPS_INIT);
+ framesPerSecond.addChangeListener(this);
+ framesPerSecond.setBackground(new Color(176,208,176));
+ framesPerSecond.setMajorTickSpacing(10);
+ framesPerSecond.setMinorTickSpacing(1);
+ framesPerSecond.setPaintTicks(true);
+ framesPerSecond.setPaintLabels(true);
+ framesPerSecond.setBorder(BorderFactory.createEmptyBorder(0,0,10,0));
+
+ picture = new JLabel();
+ Dimension dim;
+ int iw,ih;
+ dim = Toolkit.getDefaultToolkit().getScreenSize();
+ iw = (int) (dim.width - 30);
+ ih = (int) (dim.height * 0.75);
+ picture.setPreferredSize(new Dimension(iw,ih));
+ picture.setHorizontalAlignment(JLabel.CENTER);
+ picture.setAlignmentX(Component.CENTER_ALIGNMENT);
+ picture.setBorder(BorderFactory.createCompoundBorder(
+ BorderFactory.createLoweredBevelBorder(),
+ BorderFactory.createEmptyBorder(10,10,10,10)));
+ picture.setBackground(new Color(176,208,176));
+ updatePicture(0);
+
+
+ JPanel labelAndTextField;
+ labelAndTextField = new JPanel();
+ labelAndTextField.add(sliderLabel);
+ labelAndTextField.add(textField);
+ labelAndTextField.add(cls);
+ labelAndTextField.setBackground(new Color(176,208,176));
+
+ cont.add(picture);
+ cont.add(framesPerSecond);
+ cont.add(labelAndTextField);
+
+ cont.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+
+ timer = new Timer(delay, this);
+ timer.setInitialDelay(delay * 7);
+ timer.setCoalesce(true);
+ timer.start();
+
+ return cont;
+ }
+
+ /**
+ * This method will tell what should happen when the slider's arrow
+ * change its position.
+ * If user adjusting the frame per second value using the slider
+ * then it will set the Textfield to have the value that slider point
+ * to at the moment.If the value is 0 then it will stop the animation
+ * otherwise it will keep animate with the new frame per second value.
+ * @param e is the slider when the slider's arrow change its position.
+ */
+ public void stateChanged(ChangeEvent e)
+ {
+ JSlider source;
+ source = (JSlider)e.getSource();
+ int fps;
+ fps = (int)source.getValue();
+ if (!source.getValueIsAdjusting())
+ {
+ textField.setValue(new Integer(fps));
+ if (fps == 0)
+ {
+ if (!frozen)
+ stopAnimation();
+ }
+ else
+ {
+ delay = 1000 / fps;
+ timer.setDelay(delay);
+ timer.setInitialDelay(delay * 10);
+ if (frozen)
+ startAnimation();
+ }
+ }
+ else
+ {
+ textField.setText(String.valueOf(fps));
+ }
+ }
+
+ /**
+ * This method will set the slider value when the user adjusting
+ * the frame per second value using the textfield.
+ * If user adjusting the frame per second value using text field, the
+ * slider's arrow position will be changed into that value.
+ * @param e the text field
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if ("value".equals(e.getPropertyName()))
+ {
+ Number value;
+ value = (Number)e.getNewValue();
+ if (framesPerSecond != null && value != null)
+ {
+ framesPerSecond.setValue(value.intValue());
+ }
+ }
+ }
+
+ /**
+ * This method will start the timer.
+ */
+ public void startAnimation()
+ {
+ timer.start();
+ frozen = false;
+ }
+
+ /**
+ * This method will stop the timer.
+ */
+ public void stopAnimation()
+ {
+ timer.stop();
+ frozen = true;
+ }
+
+ /**
+ * This method will tell what should happen when the timer is start.
+ * When the timer start it will increase the framenumber and will
+ * update the label with the new image icon.
+ * If the timer already display all the image icon in the list, it
+ * will restart the timer and reanimate the image icon again.
+ * @param e fires when the timer start.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (frameNumber == (NUM_FRAMES - 1))
+ frameNumber = 0;
+ else
+ frameNumber++;
+
+ updatePicture(frameNumber);
+
+ if ((frameNumber == (NUM_FRAMES - 1)) ||
+ (frameNumber==(NUM_FRAMES/2 - 1)))
+ timer.restart();
+ }
+
+ /**
+ * This will update the label with the new ImageIcon.
+ * If the ImageIcon is null then the label will display an error message
+ * instead of the picture.
+ * @param frameNum this is the index of the ImageIcon to be displayed.
+ */
+ protected void updatePicture(int frameNum)
+ {
+ if (images.get(frameNumber) != null)
+ {
+ picture.setIcon((ImageIcon) images.get(frameNumber));
+ }
+ else
+ {
+ picture.setText(messages.getString("image")+" #"
+ + frameNumber + " " +
+ messages.getString("notfound"));
+ }
+ }
+}
+
+
+
+
+
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/MyTextArea.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/MyTextArea.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/MyTextArea.java (revision 31635)
@@ -0,0 +1,85 @@
+/** This class is not currently used and therefore will not be compiled by makegli. */
+
+package org.greenstone.gatherer.feedback;
+
+import javax.swing.*;
+import javax.swing.text.*;
+import java.util.Date;
+import java.awt.Color;
+
+/**
+ * This class will allow us to changed what should be inputted to the
+ * text area as the user inputted the text to the text area.
+ * @author Veronica Liesaputra
+ */
+public class MyTextArea extends JTextArea
+{
+ /**
+ * Creating an empty JTextArea.
+ */
+ public MyTextArea ()
+ {
+ super();
+ }
+
+ /**
+ * Creating a JTextArea already appended with cols.
+ * (Precondition: (cols != null))
+ * @param cols the text to be appended.
+ */
+ public MyTextArea(String cols)
+ {
+ super(cols);
+ }
+
+ /**
+ * It will give the default model this text area should use.
+ * @return the default model this text area used.
+ */
+ protected Document createDefaultModel()
+ {
+ return new UpperCaseDocument();
+ }
+
+ /**
+ * This class will be used as the default model of MyTextArea.
+ * Here we can control and modified the string that is going to be inserted to
+ * the document.
+ */
+ static class UpperCaseDocument extends PlainDocument
+ {
+ /**
+ * This method will caused the document to be appended with the date followed by newline
+ * everytime user trying to insert a new line to the document.
+ * (Precondition : (offs >= 0) && (str != null))
+ * @param offs is the offset placted to insert the string.
+ * @param str is the string to be inserted.
+ * @param a is the attribute set to be used.
+ */
+ public void insertString(int offs, String str, AttributeSet a)
+ throws BadLocationException
+ {
+ if (str == null)
+ {
+ return;
+ }
+
+ char[] upper;
+ upper = str.toCharArray();
+
+ if (str.endsWith("\n") == true)
+ {
+ String str2;
+ str2 = str + ((new Date()).toString()) + "\n\n>";
+ upper = str2.toCharArray();
+ }
+
+ super.insertString(offs, new String(upper), a);
+ }
+ }
+}
+
+
+
+
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/ReportDetails.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/ReportDetails.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/ReportDetails.java (revision 31635)
@@ -0,0 +1,1304 @@
+package org.greenstone.gatherer.feedback;
+
+import java.io.*;
+import java.awt.image.*;
+import javax.swing.*;
+import java.awt.*;
+import java.util.*;
+import javax.swing.event.MouseInputAdapter;
+import java.awt.event.*;
+import javax.swing.border.*;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.text.MessageFormat;
+import javax.swing.tree.*;
+
+/**
+ * This class can give preview of all the history of actions user did or give preview of all the
+ * xml files that are ready to be send and it also can give preview of all the screen shot of
+ * all window taken when user did the actions.
+ * If user select Details button then they will view all xml files that are going to be send.
+ * If user select View History then they will see the history record that we took so far.
+ * @author Veronica Liesaputra
+ */
+public class ReportDetails extends WindowAdapter
+{
+ /**
+ * This is the tree that will be used to hold the feedback form,the history stored inside the vector
+ * at the moment, and hold the comment user did when they recreating the event.
+ * This tree's data partly will be supplied from whats inside vector.
+ */
+ private JTree tree;
+ /**
+ * This is the root node for tree.
+ */
+ private DefaultMutableTreeNode rootNode;
+ /**
+ * This hold the parent node of the current node that is currently just being added to tree.
+ */
+ private DefaultMutableTreeNode parentNode;
+ /**
+ * This hold the current node that is currently just being added to tree.
+ */
+ private DefaultMutableTreeNode currNode;
+ /**
+ * This is the tree model for tree.
+ */
+ private DefaultTreeModel treeModel;
+
+ /**
+ * This is the tree that will be used to hold the commands (actions) done by the user inside the vector
+ * at the moment.This tree builds from partial data of tree and which data entered to this tree
+ * is signed by the value of comm_bool and comm_start.
+ */
+ private JTree tree2;
+ /**
+ * This is the root node for tree2.
+ */
+ private DefaultMutableTreeNode rootNode2;
+ /**
+ * This is the parent node of the current node that is currently just being added to tree2.
+ */
+ private DefaultMutableTreeNode parentNode2;
+ /**
+ * This is the current node that is currently just being added to tree2.
+ */
+ private DefaultMutableTreeNode currNode2;
+ /**
+ * This is the tree model for tree2.
+ */
+ private DefaultTreeModel treeModel2;
+
+ /**
+ * This is the tree that will be used to hold the history of all the actions
+ * done by the user before they choose to do the Reporting Feedback sequence.
+ */
+ private JTree tree3;
+ /**
+ * This is the root node for tree3.
+ */
+ private DefaultMutableTreeNode rootNode3;
+ /**
+ * This is the parent node for the current node that is currently just being added to tree3.
+ */
+ private DefaultMutableTreeNode parentNode3;
+ /**
+ * This is the current node that is currently just being added to tree3.
+ */
+ private DefaultMutableTreeNode currNode3;
+ /**
+ * This is the tree model for tree3.
+ */
+ private DefaultTreeModel treeModel3;
+
+ /**
+ * This is the tree that will be used to hold the commands (actions) done by the user
+ * before they choose to do the Reporting Feedback sequence.This tree builds from partial data of tree3
+ * and which data entered to this tree2 is signed by the value of comm_bool and comm_start.
+ */
+ private JTree tree4;
+ /**
+ * This is the root node for tree4.
+ */
+ private DefaultMutableTreeNode rootNode4;
+ /**
+ * This is the parent node for the current node that is currently just being added to tree4.
+ */
+ private DefaultMutableTreeNode parentNode4;
+ /**
+ * This is the current node that is currently just being added to tree4.
+ */
+ private DefaultMutableTreeNode currNode4;
+ /**
+ * This is the tree model for tree4.
+ */
+ private DefaultTreeModel treeModel4;
+
+ /**
+ * This is the panel to display tree4.
+ */
+ private JPanel pane4;
+ /**
+ * This is the scroll pane for displaying tree3, so it is scrollable.
+ */
+ private JScrollPane scroll3;
+ /**
+ * This is the scroll pane for displaying tree4, so it is scrollable.
+ */
+ private JScrollPane scroll4;
+
+ /**
+ * This is the panel to display tree2.
+ */
+ private JPanel pane2;
+ /**
+ * This is the scroll pane for displaying tree2, so it is scrollable.
+ */
+ private JScrollPane scroll2;
+ /**
+ * This is the scroll pane for displaying tree, so it is scrollable.
+ */
+ private JScrollPane scroll;
+
+ /**
+ * This is all the information taken from the Feedback form and information that
+ * was taken from the system.
+ */
+ private String[] err_details;
+
+ /**
+ * This is the vector that hold all information about the history of all
+ * the actions user did.
+ * If rec = true, it means it hold the history of all the actions and information
+ * of all the windows were open when user did the actions during reporting feedback
+ * sequence.
+ * If rec = false, it means it hold the history of all the actions and information
+ * of all the windows were open when user did the actions before and after
+ * reporting feedback sequence.
+ */
+ private Vector vector;
+
+ /**
+ * This is a variable to know which tag xml data that wants to be added to the JTree.
+ */
+ private int count;
+
+ /**
+ * This is a flag variable to sign whether or not the next data is to be added to
+ * the command JTree.
+ */
+ private boolean comm_bool = false;
+ /**
+ * This is a flag variable to sign whether or not the next history is to be added to
+ * the command JTree.
+ */
+ private boolean comm_start = false;
+ /**
+ * This is a flag variable to sign whether or not at the moment we building a new tree
+ * or build from the existing tree.
+ */
+ private boolean new_tree = false;
+
+ /**
+ * This is a flag variable to sign which type of preview that user wants at the moment.
+ * If its true, then its means user will view all the 6 xml files that will be send.
+ * If its false, then its means user only wants to view the history and command user just did
+ * before and after the reporting feedback sequence.
+ */
+ private boolean rec = false;
+
+ /**
+ * This is array list of all the image icons that are inside all the command JTree.
+ */
+ private ArrayList movie;
+
+ /**
+ * This variable will hold the resource of the words that is stored in Messages.properties file.
+ * The calling using messages.getString(someString) will caused someString to be translated
+ * into some other string that is hold in that file.usually it will caused it to be translated
+ * to the language that the user use or choose to have as stated in Locale.
+ */
+ private static ResourceBundle messages;
+
+ private String[] img;
+
+ /**
+ * This constructor will be seeting up the data member to its appropriate value.
+ * (Precondition : (msg != null) && (bool != null))
+ * @param stack this is all vector to be supplied to tree.
+ * @param err_array holding all the information taken when user doing the FeedbackInterface form.
+ * @param imgFile holding all the information inside all the panels in the FeedbackInterface form.
+ * @param msg hold the resource of the words that is stored in Messages.properties file.
+ * @param bool hold the falg value of rec.
+ */
+ public ReportDetails(Vector stack,String[] err_array,String[] imgFile,ResourceBundle msg,boolean bool)
+ {
+ img = imgFile;
+ vector = stack;
+ err_details = err_array;
+ messages = msg;
+ rec = bool;
+ movie = new ArrayList();
+ makeNewWindow();
+ }
+
+ /**
+ * This method will make a modal-dialog window that will display all the preview.
+ */
+ public void makeNewWindow()
+ {
+ JDialog frame;
+ frame = new View (this);
+ frame.setModal(true);
+ frame.setVisible(true);
+ }
+
+ /**
+ * This class is the dialog window that will give the preview.
+ */
+ class View extends JDialog implements ActionListener
+ {
+ /**
+ * This variable is holding the current window that display the preview.
+ */
+ private JDialog frame;
+ /**
+ * This variable holding the owner of this window.
+ */
+ private ReportDetails framework = null;
+
+ /**
+ * This constructor will make the dialog window for previewing and setting up the
+ * content pane of this window.
+ * @param controller is the owner of this window.
+ */
+ public View (ReportDetails controller)
+ {
+ super();
+ framework = controller;
+ frame = this;
+
+ setTitle(messages.getString("ViewReport"));
+ setBackground(new Color(176,208,176));
+ setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+ setDefaultLookAndFeelDecorated(true);
+
+ JPanel cont;
+ cont = new JPanel();
+ cont.setLayout(new BoxLayout(cont,BoxLayout.PAGE_AXIS));
+ cont.setBackground(new Color(176,208,176));
+
+ JTabbedPane tab;
+ tab = new JTabbedPane();
+ tab.setBackground(new Color(176,208,176));
+
+ new_tree = false;
+
+ if (err_details != null)
+ {
+ tab.addTab(messages.getString("ViewFeedbackForm"),
+ createFeedback_UI(err_details,img));
+ }
+
+ if (rec == true)
+ {
+ tab.addTab(messages.getString("ViewFeedbackHistoryState"),
+ createFeedbackHistory_UI(vector));
+ tab.addTab(messages.getString("ViewFeedbackCommandState"),pane2);
+ /*tab.addTab(messages.getString("ViewHistoryState"),
+ createHistory_UI());*/
+ createHistory_UI();
+ tab.addTab(messages.getString("ViewCommandState"),pane4);
+ }
+ else
+ {
+ /*tab.addTab(messages.getString("ViewHistoryState"),
+ createFeedbackHistory_UI(vector));*/
+ createFeedbackHistory_UI(vector);
+ tab.addTab(messages.getString("ViewCommandState"),pane2);
+ }
+
+ cont.add(tab);
+
+ JLabel lbl;
+ lbl = new JLabel(" ");
+ lbl.setBackground(new Color(176,208,176));
+
+ cont.add(lbl);
+
+ JPanel pane3;
+ pane3 = new JPanel();
+ pane3.setBackground(new Color(176,208,176));
+ JButton button;
+ button = new JButton(messages.getString("Close"));
+ button.setActionCommand(messages.getString("Close"));
+ button.addActionListener(this);
+ button.setDefaultCapable(true);
+ button.setToolTipText(messages.getString("ClosePreview"));
+ button.setBackground(new Color(176,208,176));
+
+ /*JButton button2;
+ button2 = new JButton(messages.getString("ViewMovie"));
+ button2.setActionCommand(messages.getString("ViewMovie"));
+ button2.addActionListener(new ActionListener()
+ {
+ public void actionPerformed (ActionEvent e)
+ {
+ JDialog.setDefaultLookAndFeelDecorated(true);
+ JDialog frame;
+ frame = new Movie(messages,movie);
+
+ frame.setContentPane(((Movie)frame).create_UI());
+ frame.pack();
+ frame.setVisible(true);
+ }
+ });
+ button2.setToolTipText(messages.getString("ViewMovieText"));
+ button2.setBackground(new Color(176,208,176));
+ pane3.add(button2);*/
+ pane3.add(button);
+ pane3.setBorder(new EmptyBorder(new Insets(0,10,10,10)));
+
+ cont.add(pane3);
+
+ cont.setBorder(new EmptyBorder(new Insets(5,5,5,5)));
+ getContentPane().add(cont);
+
+ tab.setAlignmentX(Component.LEFT_ALIGNMENT);
+ lbl.setAlignmentX(Component.LEFT_ALIGNMENT);
+ pane3.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+ addWindowListener(framework);
+ Toolkit kit;
+ kit = Toolkit.getDefaultToolkit();
+ Dimension screenSize;
+ screenSize = kit.getScreenSize();
+ int screenHeight;
+ screenHeight = screenSize.height;
+ int screenWidth;
+ screenWidth = screenSize.width;
+ Dimension size;
+ size = this.getSize();
+ setLocation((screenWidth -size.width)/ 4,
+ (screenHeight - size.height)/ 4);
+
+ pack();
+ setResizable(false);
+ }
+
+ /**
+ * This method will get the vector that is stored inside temp_feedbackhist.log
+ * if rec is true, and will get the vector that is stored inside temp_history.log
+ * if rec is false. If file is not exist then it will return null.
+ * @return vector that is stored in the file.
+ */
+ public Vector getPrev_log()
+ {
+ Vector stack = null;
+ try
+ {
+ File f;
+ if (rec == true)
+ {
+ f = new File("temp_feedbackhist.log");
+ }
+ else
+ {
+ f = new File("temp_history.log");
+ }
+
+ if (f.exists() == true)
+ {
+ FileInputStream fis = new FileInputStream(f);
+ ObjectInputStream ois = new ObjectInputStream(fis);
+ stack = (Vector) ois.readObject();
+ ois.close();
+ return stack;
+ }
+ else
+ stack = null;
+ }
+ catch (IOException exp) {exp.printStackTrace();}
+ catch (ClassNotFoundException exp2)
+ {
+ System.out.println("class exp");}
+
+ return stack;
+ }
+
+ /**
+ * This method will update the graphics of this window and it will
+ * set variable finish in Conformation to true.
+ * @param g is the graphics for this window.
+ */
+ public void paint (Graphics g)
+ {
+ super.paint(g);
+ FeedbackInterface.setFinish(true);
+ }
+
+ /**
+ * This method will close the preview window if user select close button.
+ * @param e action event user did.
+ */
+ public void actionPerformed (ActionEvent e)
+ {
+ if(messages.getString("Close").equals(e.getActionCommand()))
+ {
+ dispose();
+ }
+ }
+
+ /**
+ * This method will add a record to history tree and the command tree.
+ * @param log is the record to be added.
+ */
+ public void getHist(History log)
+ {
+ comm_bool = true;
+ startContent(10);
+
+ startContent(7);
+ saveContent(log.getDate());
+ closeContent(7);
+
+ startContent(2);
+ saveContent(log.getTitle());
+ closeContent(2);
+
+ startContent(8);
+ saveContent(log.getCommand());
+ closeContent(8);
+
+ String image;
+ image = log.getImage();
+
+ if (image != null)
+ {
+ byte[] img;
+ img = Base64.decode(image);
+
+ String width,height;
+ width = log.getWidth();
+ height = log.getHeight();
+
+ int w,h;
+ w = Integer.parseInt(width);
+ h = Integer.parseInt(height);
+
+ ImageIcon icon,small_icon;
+ small_icon = new ImageIcon(img);
+ icon = new ImageIcon
+ (small_icon.getImage().getScaledInstance(w,h,Image.SCALE_SMOOTH));
+
+ movie.add(0,icon);
+
+ startContent(6);
+ saveImage(log.getImageName(),width,height);
+ closeContent(6);
+ }
+ comm_bool = false;
+
+ ArrayList component;
+ component = log.getArray();
+ int j;
+ for (j = 0 ; j < component.size() ; j++)
+ {
+ startContent(9);
+ CompGroup comp;
+ comp = (CompGroup) component.get(j);
+
+ startContent(8);
+ saveContent(comp.getStatus());
+ closeContent(8);
+
+ startContent(0);
+ UserComponent cmp;
+ cmp = comp.getComponent();
+ showReport(cmp);
+ closeContent(0);
+
+ closeContent(9);
+ }
+
+ comm_bool = true;
+ closeContent(10);
+ comm_bool = false;
+ }
+
+ /**
+ * This method will make the tree that contains the history and the commands of all the actions and window
+ * open when user did the action that stored in vector.
+ * @param vector the vector that contains the information need to be display in tree.
+ * @return the panel that display tree and tree2.
+ */
+ public JPanel createFeedbackHistory_UI(Vector vector)
+ {
+ scroll = null;
+ int k;
+
+ tree = null;
+ rootNode = null;
+ currNode = null;
+ parentNode = null;
+ treeModel = null;
+
+ tree2 = null;
+ rootNode2 = null;
+ currNode2 = null;
+ parentNode2 = null;
+ treeModel2 = null;
+
+ comm_bool = true;
+ comm_start = true;
+ new_tree = false;
+
+ startElement(messages.getString("HISTORY"));
+
+ for (k = 0 ; k < vector.size() ; k++)
+ {
+ History log;
+ log = (History) vector.get(k);
+ getHist(log);
+ }
+
+ Vector stack;
+ stack = getPrev_log();
+ if (stack != null)
+ {
+ for (k = 0 ; k < stack.size() ; k++)
+ {
+ History log;
+ log = (History) stack.get(k);
+ getHist(log);
+ }
+ stack.removeAllElements();
+ System.gc();
+ stack = null;
+ }
+
+ comm_bool = true;
+ endElement(messages.getString("HISTORY"));
+ comm_bool = false;
+
+ comm_start = false;
+
+ pane2 = new JPanel();
+ pane2.add(scroll2);
+ pane2.setBackground(new Color(176,208,176));
+ pane2.setBorder(new EmptyBorder(new Insets(10,10,10,10)));
+
+ JPanel pane;
+ pane = new JPanel();
+ pane.add(scroll);
+ pane.setBackground(new Color(176,208,176));
+ pane.setBorder(new EmptyBorder(new Insets(10,10,10,10)));
+
+ return pane;
+ }
+
+ /**
+ * This method will make the tree that contains the history and the commands of all the actions and window
+ * open when user did the action before user choose the reporting feedback sequence.
+ * @return the panel contains the preview of this history and commands before user choose to start
+ * the reporting feedback sequence.
+ */
+ public JPanel createHistory_UI()
+ {
+ scroll3 = null;
+ int k;
+
+ tree3 = null;
+ rootNode3 = null;
+ currNode3 = null;
+ parentNode3 = null;
+ treeModel3 = null;
+
+ tree4 = null;
+ rootNode4 = null;
+ currNode4 = null;
+ parentNode4 = null;
+ treeModel4 = null;
+
+ comm_bool = true;
+ comm_start = true;
+ new_tree = true;
+
+ startElement(messages.getString("HISTORY"));
+
+ try
+ {
+ File f = new File("history.log");
+ FileInputStream fis = new FileInputStream(f);
+ ObjectInputStream ois = new ObjectInputStream(fis);
+
+ Vector stack;
+ stack = (Vector) ois.readObject();
+
+ if (stack != null)
+ {
+ for (k = 0 ; k < stack.size() ; k++)
+ {
+ History log;
+ log = (History) stack.get(k);
+ getHist(log);
+ }
+ stack.removeAllElements();
+ System.gc();
+ stack = null;
+ }
+
+ stack = (Vector) ois.readObject();
+
+ if (stack != null)
+ {
+ for (k = 0 ; k < stack.size() ; k++)
+ {
+ History log;
+ log = (History) stack.get(k);
+ getHist(log);
+ }
+ stack.removeAllElements();
+ System.gc();
+ stack = null;
+ }
+
+ ois.close();
+ }
+ catch (IOException exp) {exp.printStackTrace();}
+ catch (ClassNotFoundException exp2)
+ {
+ System.out.println("class exp");}
+
+ comm_bool = true;
+ endElement(messages.getString("HISTORY"));
+ comm_bool = false;
+
+ comm_start = false;
+
+ pane4 = new JPanel();
+ pane4.add(scroll4);
+ pane4.setBackground(new Color(176,208,176));
+ pane4.setBorder(new EmptyBorder(new Insets(10,10,10,10)));
+
+ JPanel pane;
+ pane = new JPanel();
+ pane.add(scroll3);
+ pane.setBackground(new Color(176,208,176));
+ pane.setBorder(new EmptyBorder(new Insets(10,10,10,10)));
+
+ return pane;
+ }
+
+ /**
+ * This method display all the information taken when user did the Feedback form.
+ * @param err_details is the information taken when user did the Feedback form.
+ * @param img is the information from all the panels inside the Feedback form.
+ * @return panel displaying the information.
+ */
+ public JPanel createFeedback_UI(String[] err_details,String[] img)
+ {
+ scroll = null;
+
+ tree = null;
+ rootNode = null;
+ currNode = null;
+ parentNode = null;
+ treeModel = null;
+
+ tree2 = null;
+ rootNode2 = null;
+ currNode2 = null;
+ parentNode2 = null;
+ treeModel2 = null;
+
+ startElement(messages.getString("BUGS"));
+
+ startElement(messages.getString("IDCode"));
+ characters(err_details[0].toCharArray(),0,err_details[0].length());
+ endElement(messages.getString("IDCode"));
+
+ startElement(messages.getString("LastViewedWindow"));
+ characters(err_details[1].toCharArray(),0,err_details[1].length());
+ endElement(messages.getString("LastViewedWindow"));
+
+ startElement(messages.getString("Optionals"));
+
+ startElement(messages.getString("Type"));
+ characters(err_details[3].toCharArray(),0,err_details[3].length());
+ endElement(messages.getString("Type"));
+
+ startElement(messages.getString("Urgency"));
+ characters(err_details[4].toCharArray(),0,err_details[4].length());
+ endElement(messages.getString("Urgency"));
+
+ endElement(messages.getString("Optionals"));
+
+ startElement(messages.getString("User"));
+
+ startElement(messages.getString("Name"));
+ characters(err_details[5].toCharArray(),0,err_details[5].length());
+ endElement(messages.getString("Name"));
+
+ startElement(messages.getString("HomeDirectory"));
+ characters(err_details[6].toCharArray(),0,err_details[6].length());
+ endElement(messages.getString("HomeDirectory"));
+
+ startElement(messages.getString("WorkingDirectory"));
+ characters(err_details[7].toCharArray(),0,err_details[7].length());
+ endElement(messages.getString("WorkingDirectory"));
+
+ startElement(messages.getString("SMTP"));
+ characters(err_details[19].toCharArray(),0,err_details[19].length());
+ endElement(messages.getString("SMTP"));
+
+ startElement(messages.getString("Email"));
+ characters(err_details[20].toCharArray(),0,err_details[20].length());
+ endElement(messages.getString("Email"));
+
+ endElement(messages.getString("User"));
+
+ startElement(messages.getString("OpenFormTime"));
+ characters(err_details[8].toCharArray(),0,err_details[8].length());
+ endElement(messages.getString("OpenFormTime"));
+
+ startElement(messages.getString("SendFormTime"));
+ characters(err_details[9].toCharArray(),0,err_details[9].length());
+ endElement(messages.getString("SendFormTime"));
+
+ startElement(messages.getString("OperatingSystem"));
+ characters(err_details[10].toCharArray(),0,err_details[10].length());
+ endElement(messages.getString("OperatingSystem"));
+
+ startElement(messages.getString("Java"));
+
+ startElement(messages.getString("JavaInformation"));
+ characters(err_details[11].toCharArray(),0,err_details[11].length());
+ endElement(messages.getString("JavaInformation"));
+
+ startElement(messages.getString("TotalMemory"));
+ characters(err_details[21].toCharArray(),0,err_details[21].length());
+ endElement(messages.getString("TotalMemory"));
+
+ startElement(messages.getString("MaxMemory"));
+ characters(err_details[22].toCharArray(),0,err_details[22].length());
+ endElement(messages.getString("MaxMemory"));
+
+ startElement(messages.getString("FreeMemory"));
+ characters(err_details[23].toCharArray(),0,err_details[23].length());
+ endElement(messages.getString("FreeMemory"));
+
+ startElement(messages.getString("DefaultLocale"));
+ characters(err_details[12].toCharArray(),0,err_details[12].length());
+ endElement(messages.getString("DefaultLocale"));
+
+
+ endElement(messages.getString("Java"));
+
+ startElement(messages.getString("Browser"));
+ characters(err_details[13].toCharArray(),0,err_details[13].length());
+ endElement(messages.getString("Browser"));
+
+ startElement(messages.getString("LocalHostName"));
+ characters(err_details[14].toCharArray(),0,err_details[14].length());
+ endElement(messages.getString("LocalHostName"));
+
+ startElement(messages.getString("LocalHostAddress"));
+ characters(err_details[15].toCharArray(),0,err_details[15].length());
+ endElement(messages.getString("LocalHostAddress"));
+
+ startElement(messages.getString("ScreenResolution"));
+ characters(err_details[16].toCharArray(),0,err_details[16].length());
+ endElement(messages.getString("ScreenResolution"));
+
+ if (img!=null)
+ {
+ startElement("Sequences");
+ int i;
+ for ( i = 0 ; i < img.length ; i= i + 10)
+ {
+ startElement("Sequence");
+
+ startElement("Title");
+ characters(img[i].toCharArray(),0,img[i].length());
+ endElement("Title");
+
+ startElement("Details");
+ characters(img[i+9].toCharArray(),0,img[i+9].length());
+ endElement("Details");
+
+ startElement("ImageDescription");
+
+ startElement("ScreenShot");
+ startElement("Image");
+ saveImage(img[i+2],img[i+7],img[i+8]);
+ endElement("Image");
+ endElement("ScreenShot");
+
+ startElement("ErrorLineAndScreenShot");
+ startElement("Image");
+ saveImage(img[i+4],img[i+7],img[i+8]);
+ endElement("Image");
+ endElement("ErrorLineAndScreenShot");
+
+ startElement("ErrorLineForScreenShot");
+ startElement("Image");
+ saveImage(img[i+6],img[i+7],img[i+8]);
+ endElement("Image");
+ endElement("ErrorLineForScreenShot");
+
+ endElement("ImageDescription");
+
+ endElement("Sequence");
+ }
+ endElement("Sequences");
+ }
+
+ endElement(messages.getString("BUGS"));
+
+ JPanel pane;
+ pane = new JPanel();
+ pane.add(scroll);
+ pane.setBackground(new Color(176,208,176));
+ pane.setBorder(new EmptyBorder(new Insets(10,10,10,10)));
+
+ return pane;
+ }
+
+ /**
+ * This will add the UserComponent to the tree.
+ * @param sx is the UserComponent to be added.
+ */
+ public void showReport (UserComponent sx)
+ {
+ String type;
+ type = sx.getType();
+ String title;
+ title = sx.getTitle();
+ ArrayList content;
+ content = sx.getContent();
+ String selected;
+ selected = sx.getSelected();
+ String visible;
+ visible = sx.getVisible();
+ String image;
+ image = sx.getImage();
+ String img;
+ img = sx.getImageFileName();
+ String width;
+ width = sx.getWidth();
+ String height;
+ height = sx.getHeight();
+ String tooltip;
+ tooltip = sx.getToolTip();
+
+ if (type != null)
+ {
+ startContent(1);
+ saveContent(type);
+ closeContent(1);
+ }
+
+ if (title != null)
+ {
+ startContent(2);
+ saveContent(title);
+ closeContent(2);
+ }
+
+ if (content != null)
+ {
+ startContent(3);
+ int j;
+ for ( j = 0 ; j < content.size() ; j++)
+ {
+ if (content.get(j) instanceof String)
+ saveContent((String) content.get(j));
+ else if (content.get(j) instanceof UserComponent)
+ {
+ startContent(0);
+ UserComponent cmp;
+ cmp = (UserComponent) content.get(j);
+ showReport(cmp);
+ closeContent(0);
+ }
+ }
+ closeContent(3);
+ }
+
+ if (selected != null)
+ {
+ startContent(4);
+ saveContent(selected);
+ closeContent(4);
+ }
+
+ if (visible != null)
+ {
+ startContent(5);
+ saveContent(visible);
+ closeContent(5);
+ }
+
+ if (img != null)
+ {
+ startContent(6);
+ saveImage(img,width,height);
+ closeContent(6);
+ }
+ else
+ {
+ if (image != null)
+ {
+ startContent(11);
+ saveContent(image);
+ closeContent(11);
+ }
+ }
+
+ if (tooltip != null)
+ {
+ startContent(12);
+ saveContent(tooltip);
+ closeContent(12);
+ }
+ }
+
+ /**
+ * This method will give what type of information this node hold according the number
+ * @param num the number that describe what type of information it hold.
+ */
+ public void startContent (int num)
+ {
+ switch(num)
+ {
+ case 0: startElement(messages.getString("COMPONENT"));
+ break;
+ case 1: startElement(messages.getString("Type"));
+ break;
+ case 2: startElement(messages.getString("Title"));
+ break;
+ case 3: startElement(messages.getString("Content"));
+ break;
+ case 4: startElement(messages.getString("Selected"));
+ break;
+ case 5: startElement(messages.getString("Visible"));
+ break;
+ case 6: startElement(messages.getString("Image"));
+ break;
+ case 7: startElement(messages.getString("Date"));
+ break;
+ case 8: startElement(messages.getString("Command"));
+ break;
+ case 9: startElement(messages.getString("COMPONENTS"));
+ break;
+ case 10:startElement(messages.getString("LOG"));
+ break;
+ case 11:startElement(messages.getString("Icon"));
+ break;
+ case 12:startElement(messages.getString("ToolTipText"));
+ break;
+ case 13:startElement(messages.getString("Comment"));
+ break;
+ }
+ }
+
+ /**
+ * This will add the text content to the current node.
+ * @param text is the text content of the current node.
+ */
+ public void saveContent (String text)
+ {
+ if (text == null)
+ text = "\t";
+ characters(text.toCharArray(),0,text.length());
+ }
+
+ /**
+ * This will say that what type of information that has been finished added
+ * accroding its number.
+ * @param num the number that describe what type of information it hold.
+ */
+ public void closeContent (int num)
+ {
+ switch(num)
+ {
+ case 0: endElement(messages.getString("COMPONENT"));
+ break;
+ case 1: endElement(messages.getString("Type"));
+ break;
+ case 2: endElement(messages.getString("Title"));
+ break;
+ case 3: endElement(messages.getString("Content"));
+ break;
+ case 4: endElement(messages.getString("Selected"));
+ break;
+ case 5: endElement(messages.getString("Visible"));
+ break;
+ case 6: endElement(messages.getString("Image"));
+ break;
+ case 7: endElement(messages.getString("Date"));
+ break;
+ case 8: endElement(messages.getString("Command"));
+ break;
+ case 9: endElement(messages.getString("COMPONENTS"));
+ break;
+ case 10:endElement(messages.getString("LOG"));
+ break;
+ case 11:endElement(messages.getString("Icon"));
+ break;
+ case 12:endElement(messages.getString("ToolTipText"));
+ break;
+ case 13:endElement(messages.getString("Comment"));
+ break;
+ }
+ }
+
+ /**
+ * This method will add the this string to the appropriate place in the tree.
+ * @param type its the type to be added to the tree.
+ */
+ public void startElement (String type)
+ {
+
+ if ((comm_bool == true)&&(comm_start == true))
+ {
+ if (new_tree == true)
+ {
+ if (type == messages.getString("HISTORY"))
+ {
+ rootNode4 = new DefaultMutableTreeNode(messages.getString("HISTORYCOMMANDS"));
+ treeModel4 = new DefaultTreeModel(rootNode4);
+
+ parentNode4 = rootNode4;
+ tree4 = new JTree(treeModel4);
+ tree4.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
+ tree4.setBackground(new Color(224,240,224));
+ tree4.setEditable(false);
+ tree4.setShowsRootHandles(true);
+ }
+ else
+ {
+ currNode4 = new DefaultMutableTreeNode(type + " : ");
+ treeModel4.insertNodeInto(currNode4,parentNode4,parentNode4.getChildCount());
+ parentNode4 = currNode4;
+ }
+ }
+ else
+ {
+ if (type == messages.getString("HISTORY"))
+ {
+ rootNode2 = new DefaultMutableTreeNode(messages.getString("HISTORYCOMMANDS"));
+ treeModel2 = new DefaultTreeModel(rootNode2);
+
+ parentNode2 = rootNode2;
+ tree2 = new JTree(treeModel2);
+ tree2.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
+ tree2.setBackground(new Color(224,240,224));
+ tree2.setEditable(false);
+ tree2.setShowsRootHandles(true);
+ }
+ else
+ {
+ currNode2 = new DefaultMutableTreeNode(type + " : ");
+ treeModel2.insertNodeInto(currNode2,parentNode2,parentNode2.getChildCount());
+ parentNode2 = currNode2;
+ }
+ }
+ }
+
+ if ((type == messages.getString("COMMENTS"))||(type == messages.getString("BUGS"))||
+ (type == messages.getString("HISTORY")))
+ {
+ if (new_tree == true)
+ {
+ rootNode3 = new DefaultMutableTreeNode(type);
+ treeModel3 = new DefaultTreeModel(rootNode3);
+
+ parentNode3 = rootNode3;
+ tree3 = new JTree(treeModel3);
+ tree3.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
+
+ tree3.setBackground(new Color(224,240,224));
+ tree3.setEditable(false);
+ tree3.setShowsRootHandles(true);
+ }
+ else
+ {
+ rootNode = new DefaultMutableTreeNode(type);
+ treeModel = new DefaultTreeModel(rootNode);
+
+ parentNode = rootNode;
+ tree = new JTree(treeModel);
+ tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
+
+ tree.setBackground(new Color(224,240,224));
+ tree.setEditable(false);
+ tree.setShowsRootHandles(true);
+ }
+ }
+ else
+ {
+ if (new_tree == true)
+ {
+ currNode3 = new DefaultMutableTreeNode(type + " : ");
+ treeModel3.insertNodeInto(currNode3,parentNode3,parentNode3.getChildCount());
+ parentNode3 = currNode3;
+ }
+ else
+ {
+ currNode = new DefaultMutableTreeNode(type + " : ");
+ treeModel.insertNodeInto(currNode,parentNode,parentNode.getChildCount());
+ parentNode = currNode;
+ }
+ }
+ }
+
+ /**
+ * This method is to mark that we finished adding the type to the tree.
+ * @param type type that finished added to the tree.
+ */
+ public void endElement (String type)
+ {
+ if ((type == messages.getString("HISTORY"))||(type == messages.getString("BUGS"))||
+ (type == messages.getString("COMMENTS")))
+ {
+ if (new_tree == true)
+ {
+ scroll3 = new JScrollPane(tree3);
+ scroll3.setPreferredSize(new Dimension(600,600));
+ if (comm_bool == true)
+ {
+ scroll4 = new JScrollPane(tree4);
+ scroll4.setPreferredSize(new Dimension(600,600));
+ }
+ }
+ else
+ {
+ scroll = new JScrollPane(tree);
+ scroll.setPreferredSize(new Dimension(600,600));
+ if (comm_bool == true)
+ {
+ scroll2 = new JScrollPane(tree2);
+ scroll2.setPreferredSize(new Dimension(600,600));
+ }
+ }
+ }
+ else
+ {
+ if (new_tree == true)
+ {
+ parentNode3 = (DefaultMutableTreeNode) parentNode3.getParent();
+
+ if ((comm_bool == true)&&(comm_start == true))
+ {
+ if (parentNode4 == null) return;
+ parentNode4 = (DefaultMutableTreeNode) parentNode4.getParent();
+ }
+ }
+ else
+ {
+ parentNode = (DefaultMutableTreeNode) parentNode.getParent();
+
+ if ((comm_bool == true)&&(comm_start == true))
+ {
+ if (parentNode2 == null) return;
+ parentNode2 = (DefaultMutableTreeNode) parentNode2.getParent();
+ }
+ }
+ }
+ }
+
+ /*public BufferedImage iconToImage(Icon icon, Component targetComponent)
+ {
+ int w = icon.getIconWidth();
+ int h = icon.getIconHeight();
+ GraphicsConfiguration gc = targetComponent.getGraphicsConfiguration();
+ BufferedImage image = gc.createCompatibleImage(w, h);
+ Graphics2D g = image.createGraphics();
+ icon.paintIcon(targetComponent,g,0,0);
+ g.dispose();
+ return image;
+ }
+
+ public BufferedImage savingImage (ImageIcon img)
+ {
+ JFrame f = new JFrame();
+ f.setVisible(false);
+ BufferedImage image = iconToImage(img,f);
+ f.dispose();
+ return image;
+ }*/
+
+ /**
+ * This will add the image filename,width and height to the tree.
+ * @param filename image's filename
+ * @param width image's width
+ * @param height image's height
+ */
+ public void saveImage (String filename,String width,String height)
+ {
+ startElement(messages.getString("Title"));
+ characters(filename.toCharArray(),0,filename.length());
+ endElement(messages.getString("Title"));
+
+ startElement(messages.getString("Size"));
+
+ startElement(messages.getString("Width"));
+ characters(width.toCharArray(),0,width.length());
+ endElement(messages.getString("Width"));
+
+ startElement(messages.getString("Height"));
+ characters(height.toCharArray(),0,height.length());
+ endElement(messages.getString("Height"));
+
+ endElement(messages.getString("Size"));
+
+ /*JFrame f = new JFrame();
+ f.setVisible(false);
+ BufferedImage image = savingImage(img);
+ BufferedImage bi = new BufferedImage(400, 400, BufferedImage.TYPE_INT_RGB);
+ Graphics2D big = bi.createGraphics();
+ big.drawImage(image.getScaledInstance(400,400,Image.SCALE_SMOOTH),0,0,f);
+ f.dispose();
+
+
+ JLabel label = new JLabel(new ImageIcon(bi));
+ label.setPreferredSize(new Dimension(400,400));
+ currNode = new DefaultMutableTreeNode(label);
+ treeModel.insertNodeInto(currNode,parentNode,parentNode.getChildCount());*/
+ }
+
+ /**
+ * This method will add the characters to the current node's type.
+ * @param ch is the char[] that is need to be added to the current node's type.
+ * @param start is where we should start taking from ch.
+ * @param len is the length of charcacters we should take from ch
+ */
+ public void characters (char[] ch, int start, int len)
+ {
+ String text;
+ text = new String(ch,start,len);
+ String text1;
+ text1 = text.trim();
+ if (text1.length() > 0)
+ {
+ if ((comm_bool == true)&&(comm_start == true))
+ {
+ if (new_tree == true)
+ {
+ String txt2;
+ txt2 = (String) currNode4.getUserObject();
+ currNode4.setUserObject(txt2 + text1);
+ }
+ else
+ {
+ String txt2;
+ txt2 = (String) currNode2.getUserObject();
+ currNode2.setUserObject(txt2 + text1);
+ }
+ }
+
+ if (new_tree == true)
+ {
+ String txt;
+ txt = (String) currNode3.getUserObject();
+ currNode3.setUserObject(txt + text1);
+ }
+ else
+ {
+ String txt;
+ txt = (String) currNode.getUserObject();
+ currNode.setUserObject(txt + text1);
+ }
+ }
+ }
+ }
+
+}
+
+
+
+
+
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/SaveToXML.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/SaveToXML.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/SaveToXML.java (revision 31635)
@@ -0,0 +1,482 @@
+package org.greenstone.gatherer.feedback;
+
+import java.awt.image.*;
+import java.io.*;// SAX classes.
+import javax.imageio.*;
+import org.xml.sax.*;
+import org.xml.sax.helpers.*;//JAXP 1.1
+import javax.xml.parsers.*;
+import javax.xml.transform.*;
+import javax.xml.transform.stream.*;
+import javax.xml.transform.sax.*;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.text.MessageFormat;
+import java.util.*;
+
+/**
+ * This class will make xml files from the information given.
+ * @author Veronica Liesaputra
+ */
+public class SaveToXML
+{
+ /**
+ * This is the file where the xml file will be saved.
+ */
+ private FileOutputStream fos;
+ /**
+ * This is the transformer handler for the xml file.
+ */
+ private TransformerHandler hd;
+ /**
+ * This is the attribute implementation for the xml file.
+ */
+ private AttributesImpl atts;
+ /**
+ * This variable will hold the resource of the words that is stored in Messages.properties file.
+ * The calling using messages.getString(someString) will caused someString to be translated
+ * into some other string that is hold in that file.usually it will caused it to be translated
+ * to the language that the user use or choose to have as stated in Locale.
+ */
+ private static ResourceBundle messages;
+
+ /**
+ * This constructor will setup what languange should be use.
+ * @param msg will hold the resource of the words that is stored in Messages.properties file.
+ */
+ public SaveToXML(ResourceBundle msg)
+ {
+ messages = msg;
+ }
+
+ /**
+ * This will save the image, image filename,width and height to the xml file.
+ * @param image image
+ * @param filename image's filename
+ * @param width image's width
+ * @param height image's height
+ */
+ public void saveImage (String image,String filename,String width,String height)
+ {
+ try
+ {
+ hd.startElement("","","Title",atts);
+ hd.characters(filename.toCharArray(),0,filename.length());
+ hd.endElement("","","Title");
+ hd.startElement("","","Size",atts);
+ hd.startElement("","","Width",atts);
+ hd.characters(width.toCharArray(),0,width.length());
+ hd.endElement("","","Width");
+ hd.startElement("","","Height",atts);
+ hd.characters(height.toCharArray(),0,height.length());
+ hd.endElement("","","Height");
+ hd.endElement("","","Size");
+ hd.startElement("","","View",atts);
+ hd.characters(image.toCharArray(),0,image.length());
+ hd.endElement("","","View");
+ }
+ catch(SAXException sax) {}
+ }
+
+ /**
+ * This method will create and setup the xml file.
+ * @param xmlname this is the file name for the xml file.
+ * @param type this is the first tag that open the xml file.
+ */
+ public void open (String xmlname,String type)
+ {
+ try
+ {
+ File f;
+ f = new File(xmlname);
+ fos = new FileOutputStream(f);
+ PrintWriter out;
+ out = new PrintWriter(fos);
+ StreamResult streamResult;
+ streamResult = new StreamResult(out);
+ SAXTransformerFactory tf;
+ tf = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
+ hd = tf.newTransformerHandler();
+ Transformer serializer;
+ serializer = hd.getTransformer();
+
+ serializer.setOutputProperty(OutputKeys.ENCODING,"UTF-8");
+ //serializer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM,dtdname);
+ serializer.setOutputProperty(OutputKeys.INDENT,"yes");
+
+ hd.setResult(streamResult);
+ atts = new AttributesImpl();
+
+ hd.startDocument();
+
+ hd.startElement("","",type,atts);
+ }
+ catch (IOException e){
+ System.err.println ("Unable to write to file");
+ System.exit(-1);
+ }catch (TransformerConfigurationException tce){
+ System.err.println("Error in: TransformerConfigurationException");
+ }catch (SAXException spe) {
+ // Error generated by the parser
+ System.err.println("Error in: (SAXException");
+ // Use the contained exception, if any
+ Exception x = spe;
+ if (spe.getException() != null)
+ x = spe.getException();
+ x.printStackTrace();
+ }
+ }
+
+ /**
+ * This method will setup to close the xml file.
+ * @param type this is the last tag that close the xml file.
+ */
+ public void close(String type)
+ {
+ try
+ {
+ hd.endElement("","",type);
+ hd.endDocument();
+ fos.close();
+ }
+ catch (IOException e){
+ System.err.println ("Unable to write to file");
+ System.exit(-1);
+ }catch (SAXException spe) {
+ // Error generated by the parser
+ System.err.println("Error in: (SAXException");
+ // Use the contained exception, if any
+ Exception x = spe;
+ if (spe.getException() != null)
+ x = spe.getException();
+ x.printStackTrace();
+ }
+ }
+
+ /**
+ * This method will add the open tag according to the number.
+ * @param num the number to say which type of tag it is.
+ */
+ public void startContent (int num)
+ {
+ try
+ {
+ switch(num)
+ {
+ case 0: hd.startElement("","","COMPONENT",atts);
+ break;
+ case 1: hd.startElement("","","Type",atts);
+ break;
+ case 2: hd.startElement("","","Title",atts);
+ break;
+ case 3: hd.startElement("","","Content",atts);
+ break;
+ case 4: hd.startElement("","","Selected",atts);
+ break;
+ case 5: hd.startElement("","","Visible",atts);
+ break;
+ case 6: hd.startElement("","","Image",atts);
+ break;
+ case 7: hd.startElement("","","Date",atts);
+ break;
+ case 8: hd.startElement("","","Command",atts);
+ break;
+ case 9: hd.startElement("","","COMPONENTS",atts);
+ break;
+ case 10:hd.startElement("","","LOG",atts);
+ break;
+ case 11:hd.startElement("","","Icon",atts);
+ break;
+ case 12:hd.startElement("","","ToolTipText",atts);
+ break;
+ case 13:hd.startElement("","","Comment",atts);
+ break;
+ }
+ }
+ catch (SAXException spe) {
+ // Error generated by the parser
+ System.err.println("Error in: (SAXException");
+ // Use the contained exception, if any
+ Exception x;
+ x = spe;
+ if (spe.getException() != null)
+ x = spe.getException();
+ x.printStackTrace();
+ }
+ }
+
+ /**
+ * This method will add the text to the tag.
+ * @param text is the text to be saved in xml files.
+ */
+ public void saveContent (String text)
+ {
+ try
+ {
+ if (text == null)
+ text = "\t";
+ hd.characters(text.toCharArray(),0,text.length());
+ }
+ catch (SAXException spe) {
+ // Error generated by the parser
+ System.err.println("Error in: (SAXException");
+ // Use the contained exception, if any
+ Exception x;
+ x = spe;
+ if (spe.getException() != null)
+ x = spe.getException();
+ x.printStackTrace();
+ }
+ }
+
+ /**
+ * This method will add the close tag according to the number.
+ * @param num the number to say which type of tag it is.
+ */
+ public void closeContent (int num)
+ {
+ try
+ {
+ switch(num)
+ {
+ case 0: hd.endElement("","","COMPONENT");
+ break;
+ case 1: hd.endElement("","","Type");
+ break;
+ case 2: hd.endElement("","","Title");
+ break;
+ case 3: hd.endElement("","","Content");
+ break;
+ case 4: hd.endElement("","","Selected");
+ break;
+ case 5: hd.endElement("","","Visible");
+ break;
+ case 6: hd.endElement("","","Image");
+ break;
+ case 7: hd.endElement("","","Date");
+ break;
+ case 8: hd.endElement("","","Command");
+ break;
+ case 9: hd.endElement("","","COMPONENTS");
+ break;
+ case 10:hd.endElement("","","LOG");
+ break;
+ case 11:hd.endElement("","","Icon");
+ break;
+ case 12:hd.endElement("","","ToolTipText");
+ break;
+ case 13:hd.endElement("","","Comment");
+ break;
+ }
+ }
+ catch (SAXException spe) {
+ // Error generated by the parser
+ System.err.println("Error in: (SAXException");
+ // Use the contained exception, if any
+ Exception x;
+ x = spe;
+ if (spe.getException() != null)
+ x = spe.getException();
+ x.printStackTrace();
+ }
+ }
+
+ /**
+ * This method save to xml all the information taken when user did the Feedback form.
+ * @param err_details is the information taken when user did the Feedback form.
+ * @param img is the images taken when user did the Feedback form
+ */
+ public void saveFeedback (String[] err_details,String[] img)
+ {
+ try
+ {
+ String dirname;
+ dirname = "xmlfeedback/";
+ FileOutputStream fos;
+ fos = new FileOutputStream(dirname + "feedback" + err_details[0] +
+ ".xml");
+ PrintWriter out;
+ out = new PrintWriter(fos);
+ StreamResult streamResult;
+ streamResult = new StreamResult(out);
+ SAXTransformerFactory tf;
+ tf = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
+ hd = tf.newTransformerHandler();
+ Transformer serializer;
+ serializer = hd.getTransformer();
+ serializer.setOutputProperty(OutputKeys.ENCODING,"UTF-8");
+ serializer.setOutputProperty(OutputKeys.INDENT,"yes");
+ hd.setResult(streamResult);
+
+
+ hd.startDocument();
+ atts = new AttributesImpl();
+ hd.startElement("","","BUGS",atts);
+
+ hd.startElement("","","IDCode",atts);
+ hd.characters(err_details[0].toCharArray(),0,err_details[0].length());
+ hd.endElement("","","IDCode");
+
+ hd.startElement("","","LastViewedWindow",atts);
+ hd.characters(err_details[1].toCharArray(),0,err_details[1].length());
+ hd.endElement("","","LastViewedWindow");
+
+ hd.startElement("","","Optionals",atts);
+
+ hd.startElement("","","Type",atts);
+ hd.characters(err_details[3].toCharArray(),0,err_details[3].length());
+ hd.endElement("","","Type");
+
+ hd.startElement("","","Urgency",atts);
+ hd.characters(err_details[4].toCharArray(),0,err_details[4].length());
+ hd.endElement("","","Urgency");
+ hd.endElement("","","Optionals");
+
+ hd.startElement("","","User",atts);
+ hd.startElement("","","Name",atts);
+ hd.characters(err_details[5].toCharArray(),0,err_details[5].length());
+ hd.endElement("","","Name");
+
+ hd.startElement("","","HomeDirectory",atts);
+ hd.characters(err_details[6].toCharArray(),0,err_details[6].length());
+ hd.endElement("","","HomeDirectory");
+
+ hd.startElement("","","WorkingDirectory",atts);
+ hd.characters(err_details[7].toCharArray(),0,err_details[7].length());
+ hd.endElement("","","WorkingDirectory");
+
+ hd.startElement("","","SMTP",atts);
+ hd.characters(err_details[19].toCharArray(),0,err_details[19].length());
+ hd.endElement("","","SMTP");
+
+ hd.startElement("","","Email",atts);
+ hd.characters(err_details[20].toCharArray(),0,err_details[20].length());
+ hd.endElement("","","Email");
+
+ hd.endElement("","","User");
+
+ hd.startElement("","","OpenFormTime",atts);
+ hd.characters(err_details[8].toCharArray(),0,err_details[8].length());
+ hd.endElement("","","OpenFormTime");
+
+ hd.startElement("","","SendFormTime",atts);
+ hd.characters(err_details[9].toCharArray(),0,err_details[9].length());
+ hd.endElement("","","SendFormTime");
+
+ hd.startElement("","","OperatingSystem",atts);
+ hd.characters(err_details[10].toCharArray(),0,err_details[10].length());
+ hd.endElement("","","OperatingSystem");
+
+ hd.startElement("","","Java",atts);
+
+ hd.startElement("","","JavaInformation",atts);
+ hd.characters(err_details[11].toCharArray(),0,err_details[11].length());
+ hd.endElement("","","JavaInformation");
+
+ hd.startElement("","","TotalMemory",atts);
+ hd.characters(err_details[21].toCharArray(),0,err_details[21].length());
+ hd.endElement("","","TotalMemory");
+
+ hd.startElement("","","MaxMemory",atts);
+ hd.characters(err_details[22].toCharArray(),0,err_details[22].length());
+ hd.endElement("","","MaxMemory");
+
+ hd.startElement("","","FreeMemory",atts);
+ hd.characters(err_details[23].toCharArray(),0,err_details[23].length());
+ hd.endElement("","","FreeMemory");
+
+ hd.startElement("","","DefaultLocale",atts);
+ hd.characters(err_details[12].toCharArray(),0,err_details[12].length());
+ hd.endElement("","","DefaultLocale");
+
+ hd.endElement("","","Java");
+
+ hd.startElement("","","Browser",atts);
+ hd.characters(err_details[13].toCharArray(),0,err_details[13].length());
+ hd.endElement("","","Browser");
+
+ hd.startElement("","","LocalHostName",atts);
+ hd.characters(err_details[14].toCharArray(),0,err_details[14].length());
+ hd.endElement("","","LocalHostName");
+
+ hd.startElement("","","LocalHostAddress",atts);
+ hd.characters(err_details[15].toCharArray(),0,err_details[15].length());
+ hd.endElement("","","LocalHostAddress");
+
+ hd.startElement("","","ScreenResolution",atts);
+ hd.characters(err_details[16].toCharArray(),0,err_details[16].length());
+ hd.endElement("","","ScreenResolution");
+
+ if (img!= null)
+ {
+ hd.startElement("","","Sequences",atts);
+ int i;
+ for ( i = 0 ; i < img.length ; i= i + 10)
+ {
+ hd.startElement("","","Sequence",atts);
+
+ hd.startElement("","","Title",atts);
+ hd.characters(img[i].toCharArray(),0,img[i].length());
+ hd.endElement("","","Title");
+
+ hd.startElement("","","Details",atts);
+ hd.characters(img[i+9].toCharArray(),0,img[i+9].length());
+ hd.endElement("","","Details");
+
+ hd.startElement("","","ImageDescription",atts);
+
+ hd.startElement("","","ScreenShot",atts);
+ hd.startElement("","","Image",atts);
+ saveImage(img[i+1],img[i+2],img[i+7],img[i+8]);
+ hd.endElement("","","Image");
+ hd.endElement("","","ScreenShot");
+
+ hd.startElement("","","ErrorLineAndScreenShot",atts);
+ hd.startElement("","","Image",atts);
+ saveImage(img[i+3],img[i+4],img[i+7],img[i+8]);
+ hd.endElement("","","Image");
+ hd.endElement("","","ErrorLineAndScreenShot");
+
+ hd.startElement("","","ErrorLineForScreenShot",atts);
+ hd.startElement("","","Image",atts);
+ saveImage(img[i+5],img[i+6],img[i+7],img[i+8]);
+ hd.endElement("","","Image");
+ hd.endElement("","","ErrorLineForScreenShot");
+
+ hd.endElement("","","ImageDescription");
+
+ hd.endElement("","","Sequence");
+ }
+ hd.endElement("","","Sequences");
+ }
+
+ hd.endElement("","","BUGS");
+ hd.endDocument();
+ fos.close();
+ }
+ catch (IOException e){
+ System.err.println ("Unable to write to file");
+ System.exit(-1);
+ }catch (TransformerConfigurationException tce){
+ System.err.println("Error in: TransformerConfigurationException");
+ }catch (SAXException spe) {
+ // Error generated by the parser
+ System.err.println("Error in: (SAXException");
+ // Use the contained exception, if any
+ Exception x;
+ x = spe;
+ if (spe.getException() != null)
+ x = spe.getException();
+ x.printStackTrace();
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/ScreenShot.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/ScreenShot.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/ScreenShot.java (revision 31635)
@@ -0,0 +1,272 @@
+package org.greenstone.gatherer.feedback;
+
+import java.awt.*;
+import java.applet.Applet;
+import java.awt.image.*;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.*;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBuffer;
+import java.awt.geom.GeneralPath;
+import java.io.*;
+import javax.imageio.*;
+import javax.swing.*;
+import java.awt.geom.*;
+
+/**
+ * This class can take screen shot of the whole screen size or screen shot of a specified
+ * rectangle only.
+ * @author Veronica Liesaputra
+ */
+public class ScreenShot
+{
+ /**
+ * This is the buffered image that hold the screen shot.
+ */
+ private BufferedImage img;
+
+ /**
+ * The dimension of the whole screen size.
+ */
+ private final static Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
+
+ /**
+ * Rectangle with a dimension of a whole screen size.
+ */
+ private Rectangle mRect = new Rectangle(dim);
+
+ /**
+ * The desired width of the image.
+ */
+ private int iw;
+
+ /**
+ * The desired height of the image.
+ */
+ private int ih;
+
+ /**
+ * BufferedImage that hold the screen shot image with the desired height and width.
+ */
+ private BufferedImage bi;
+
+ /**
+ * This is the robot that allows to take screen shot.
+ */
+ private Robot mRobot;
+
+ /**
+ * This constructor will setup the robot to alow taking a screen shot.
+ */
+ public ScreenShot ()
+ {
+ try
+ {
+ mRobot = new Robot();
+ iw = (int) (dim.getWidth() - 60);
+ ih = (int) (dim.getHeight() * 0.75) - 50;
+ }
+ catch (AWTException exp) {}
+ }
+
+ /**
+ * This method will only take screen shot of the particular rectangle.
+ * @param rect is the specified rectangle that we want to take the screen shot of.
+ * @return the image taken.
+ */
+ //public byte[] getImageIcon (Rectangle rect)
+ public BufferedImage getImageIcon (Rectangle rect)
+ {
+ BufferedImage image;
+ image = null;
+
+ //try
+ // {
+ img = mRobot.createScreenCapture(rect);
+
+ int w,h;
+ boolean rescale;
+
+ rescale = false;
+ w = rect.width;
+
+ if (w >= iw)
+ {
+ w = iw - 50;
+ rescale = true;
+ }
+
+ h = rect.height;
+
+ if ( h >= ih)
+ {
+ h = ih - 50;
+ rescale = true;
+ }
+
+ Graphics2D big;
+ big = null;
+
+ if (rescale == true)
+ {
+ image = new BufferedImage(w,h, BufferedImage.TYPE_INT_RGB);
+ big = image.createGraphics();
+ big.drawImage(img.getScaledInstance(w,h,Image.SCALE_SMOOTH),0,0,null);
+ }
+ else
+ {
+ image = img;
+ }
+
+
+ /*ByteArrayOutputStream stream;
+ stream = new ByteArrayOutputStream();
+ ImageIO.write(image,"jpeg",stream);
+ byte[] bytes;
+ bytes = stream.toByteArray();*/
+
+ if (big != null)
+ big.dispose();
+ //image = null;
+ //img = null;
+ ActionRecorderDialog.setSavefinish(true);
+ return image;
+ //return bytes;
+ // }
+ //catch(IOException ex) {ex.printStackTrace();}
+
+ //return null;
+ }
+
+ /**
+ * This method will only take screen shot of the particular rectangle and encode
+ * the image to its string representation.
+ * @param rect is the specified rectangle that we want to take the screen shot of.
+ * @return the string representation of the image taken.
+ */
+ public String getImage (Rectangle rect)
+ {
+ String txt;
+ txt = null;
+
+ BufferedImage image;
+ image = null;
+
+ try
+ {
+ img = mRobot.createScreenCapture(rect);
+
+ int w,h;
+ boolean rescale;
+
+ rescale = false;
+ w = rect.width;
+
+ if (w >= iw)
+ {
+ w = iw - 50;
+ rescale = true;
+ }
+
+ h = rect.height;
+
+ if ( h >= ih)
+ {
+ h = ih - 50;
+ rescale = true;
+ }
+
+ Graphics2D big;
+ big = null;
+
+ if (rescale == true)
+ {
+ image = new BufferedImage(w,h, BufferedImage.TYPE_INT_RGB);
+ big = image.createGraphics();
+ big.drawImage(img.getScaledInstance(w,h,Image.SCALE_SMOOTH),0,0,null);
+ }
+ else
+ {
+ image = img;
+ }
+
+ ByteArrayOutputStream stream;
+ stream = new ByteArrayOutputStream();
+ ImageIO.write(image,"gif",stream);
+ byte[] bytes;
+ bytes = stream.toByteArray();
+ txt = Base64.encodeBytes(bytes);
+
+ bytes = null;
+ if (big != null)
+ big.dispose();
+ image = null;
+ img = null;
+ }
+ catch(IOException ex) {ex.printStackTrace();}
+
+ ActionRecorderDialog.setSavefinish(true);
+
+ return txt;
+ }
+
+ /**
+ * This method will return the buffered image of the whole screen size screen shot
+ * with the desired width and height.
+ * @return the buffered image with the desired height and width.
+ */
+ public BufferedImage getBuffImage()
+ {
+ return bi;
+ }
+
+ /**
+ * This method will take screen shot of the whole screen and resize it to the
+ * desired width and height.
+ */
+ public BufferedImage captureScreen()
+ {
+ img = mRobot.createScreenCapture(mRect);
+
+ Graphics2D big;
+ bi = new BufferedImage(iw,ih, BufferedImage.TYPE_INT_RGB);
+ big = bi.createGraphics();
+ big.drawImage(img.getScaledInstance(iw,ih,Image.SCALE_SMOOTH),0,0,null);
+ big.dispose();
+ big = null;
+
+ return bi;
+ }
+
+ /**
+ * This method will return the desired width.
+ * @return the desired width.
+ */
+ public int getWidth()
+ {
+ return iw;
+ }
+
+ /**
+ * This method will return the desired height.
+ * @return the desired height.
+ */
+ public int getHeight()
+ {
+ return ih;
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/SelectPicture.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/SelectPicture.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/SelectPicture.java (revision 31635)
@@ -0,0 +1,819 @@
+package org.greenstone.gatherer.feedback;
+
+import java.awt.image.*;
+import javax.swing.BoxLayout;
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import javax.swing.*;
+import javax.swing.event.MouseInputAdapter;
+import java.awt.*;
+import java.awt.geom.*;
+import java.awt.geom.Line2D.*;
+import java.awt.geom.Line2D.Double;
+import java.awt.event.*;
+import java.awt.event.MouseEvent;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.text.MessageFormat;
+import javax.swing.border.*;
+import javax.swing.BorderFactory;
+
+import org.greenstone.gatherer.util.JarTools;
+
+/**
+ * This class allows user to make scribble lines in the image.
+ * This class will display an image to a label and if the user drag inside the label
+ * then it will draw coloured line as the user drag the mouse.
+ * Image here is the screen shot of the whole screen size.
+ * @author Veronica Liesaputra
+ */
+public class SelectPicture extends WindowAdapter
+{
+ /**
+ * This is the images that stored all the images for the whole screen size image.
+ */
+ private BufferedImage[] screen;
+
+ /**
+ * This is the images that stored all the images for the window image.
+ */
+ private BufferedImage[] window;
+
+ /**
+ * This is the buffered image that contains the line that are drawn all
+ * over the screen image.
+ */
+ private BufferedImage bi;
+
+ /**
+ * This is the buffered image of the line that are drawn all over the screen image.
+ */
+ private BufferedImage bi2;
+
+ /**
+ * This is the buffered image of the screen image.
+ */
+ private BufferedImage bi3;
+
+ /**
+ * This is the buffered image that contains the line that are drawn all over the window image.
+ */
+ private BufferedImage bi4;
+
+ /**
+ * This is the buffered image of the line that are drawn all over the window image.
+ */
+ private BufferedImage bi5;
+
+ /**
+ * This is the buffered image of the window image.
+ */
+ private BufferedImage bi6;
+
+ /**
+ * This is the window's x-coord in the screen image.
+ */
+ private int xcoord;
+
+ /**
+ * This is the window's y-coord in the screen image.
+ */
+ private int ycoord;
+
+ /**
+ * This is the window's width in the screen image.
+ */
+ private int width;
+
+ /**
+ * This is the window's height in the screen image.
+ */
+ private int height;
+
+ /**
+ * This is to block all the input in this window while waiting.
+ */
+ private MouseListener mouse_blocker_listener = new MouseAdapter() {};
+
+ /**
+ * This variable will hold the resource of the words that is stored in Messages.properties file.
+ * The calling using messages.getString(someString) will caused someString to be translated
+ * into some other string that is hold in that file.usually it will caused it to be translated
+ * to the language that the user use or choose to have as stated in Locale.
+ */
+ private static ResourceBundle messages;
+
+ /**
+ * This variable hold the comments that user entered when they are drawing lines into the image.
+ */
+ private String details;
+
+ /**
+ * This variable is a flag to say whether or not user wants to send the feedback straight away.
+ */
+ private boolean sendNow;
+
+ /**
+ * This variable is a flag to say whether the user wants to draw the scribble lines or
+ * to erase the image.
+ */
+ private boolean drawNow;
+
+ /**
+ * This variable is a flag to say whether or not the image user wants to send is window
+ * or the whole screen image.
+ */
+ private boolean windowNow;
+
+ /**
+ * This method will make a modal-dialog that will allow user to draw a line all over the image.
+ * @param sh the buffered images of the screen image.
+ * @param wh the buffered images of the window image.
+ * @param iswindow the flag to say whether user wants to send window or whole screen image.
+ * @param screen_details the comments that user typed for this screenshot.
+ * @param msg hold the resource of the words that is stored in Messages.properties file.
+ */
+ public SelectPicture(BufferedImage[] sh,BufferedImage[] wh,boolean iswindow,String screen_details,ResourceBundle msg)
+ {
+ windowNow = iswindow;
+ drawNow = true;
+ details = screen_details;
+ messages = msg;
+ window = wh;
+ screen = sh;
+ }
+
+ /**
+ * This method setting up the window's bounds inside the screen image.
+ * @param x the window's x-coordinate in the screen image.
+ * @param y the window's y-coordniate in the screen image.
+ * @param w the window's width in the screen image.
+ * @param h the window's height in the screen image.
+ */
+ public void setWindowBounds (int x,int y,int w,int h)
+ {
+ xcoord = x;
+ ycoord = y;
+ width = w;
+ height = h;
+ makeNewWindow();
+ }
+
+ /**
+ * This method will get which one user wants to send the window or screen image.
+ * @return the flag whether user wants to send window or screen image.
+ */
+ public boolean getIsWindow()
+ {
+ return windowNow;
+ }
+
+ /**
+ * This method will get the screen image of the screenshot with the line drawn all over it.
+ * @return this is the screen image with the line drawn all over it.
+ */
+ public BufferedImage getImage()
+ {
+ return bi;
+ }
+
+ /**
+ * This method will get the screen image of the screenshot.
+ * @return this is the screen image.
+ */
+ public BufferedImage getImage3()
+ {
+ return bi5;
+ }
+
+ /**
+ * This method will get the screen image of only the line that drawn all over the image of
+ * the screenshot.
+ * @return this is the screen image of only the line.
+ */
+ public BufferedImage getImage2()
+ {
+ return bi2;
+ }
+
+ /**
+ * This method will get the window image of the screenshot with the line drawn all over it.
+ * @return this is the image with the line drawn all over it.
+ */
+ public BufferedImage getWindowImage()
+ {
+ return bi3;
+ }
+
+ /**
+ * This method will get the window image of the screenshot.
+ * @return this is the window image.
+ */
+ public BufferedImage getWindowImage3()
+ {
+ return bi6;
+ }
+
+ /**
+ * This method will get the window image of only the line that drawn all over the image of
+ * the screenshot.
+ * @return this is the window image of only the line.
+ */
+ public BufferedImage getWindowImage2()
+ {
+ return bi4;
+ }
+
+ /**
+ * This method will give the comments user made when they are drawing line over the picture.
+ * @return this is the comment user made.
+ */
+ public String getDetails ()
+ {
+ return details;
+ }
+
+ public boolean getSendNow()
+ {
+ return sendNow;
+ }
+
+ /**
+ * This method will make a modal-dialog that will have the image of the screen shot in the label,
+ * allow user to draw a line all over the image and comments it as well.
+ */
+ public void makeNewWindow()
+ {
+ JDialog f;
+ f = new Selecting(this);
+ f.setLocation(0,0);
+ }
+
+ /**
+ * This class will make a modal-dialog that will have the image of the screen shot in the label,
+ * allow user to draw a line all over the image and comments it as well.
+ */
+ class Selecting extends JDialog implements ActionListener
+ {
+ /**
+ * This is the modal-dialog.
+ */
+ private JDialog frame;
+
+ /**
+ * This is the special label that allows user to draw a line all over the icon set in the label.
+ */
+ private SelectionArea area;
+
+ /**
+ * This is the text area where user can enter the comments they want to add.
+ */
+ private JTextArea problem_details;
+
+ /**
+ * This is the owner of this modal-dialog.
+ */
+ private SelectPicture framework = null;
+
+ /**
+ * This is the graphs that will allow to draw the line all over the screen image in the label and getting
+ * the image of it.
+ */
+ private Graphs gp;
+
+ /**
+ * This is the graphs that will allow to draw the line all over the window image in the label and getting
+ * the image of it.
+ */
+ private Graphs gp2;
+
+ /**
+ * This constructor will make a modal-dialog that will have the image of the screen shot in the label,
+ * allow user to draw a line all over the image and comments it as well.
+ * @param controller this is the owner of the modal-dialog.
+ */
+ public Selecting(SelectPicture controller)
+ {
+ Container contentPane;
+
+ contentPane = getContentPane();
+
+ gp = new Graphs(screen);
+
+ window = gp.getWindowVersion(xcoord,ycoord,width,height);
+
+ gp2 = new Graphs(window);
+
+ buildUI(contentPane);
+
+ framework = controller;
+
+ setTitle(messages.getString("Showwhereistheproblem"));
+ setDefaultLookAndFeelDecorated(true);
+ setModal(true);
+
+ frame = this;
+ addWindowListener(framework);
+ setBackground(new Color(176,208,176));
+
+ setLocation(0,0);
+
+ pack();
+ setVisible(true);
+ }
+
+ /**
+ * This method will setup the contentpane of the modal-dialog.It will set the cursor for
+ * the label to be from the file pen.gif and set the image icon for the label.
+ * @param content this is the contentPane of the modal-dialog.
+ */
+ private void buildUI(Container content)
+ {
+ final JPanel container;
+ container = new JPanel();
+ container.setLayout(new BoxLayout(container, BoxLayout.PAGE_AXIS));
+ container.setBackground(new Color(176,208,176));
+ container.setBorder(new EmptyBorder(5,5,5,5));
+
+
+ ImageIcon image = createImageIcon(windowNow);
+ area = new SelectionArea(image, this);
+ area.setBackground(new Color(224,240,224));
+ area.setBorder(BorderFactory.createCompoundBorder
+ (BorderFactory.createLineBorder(Color.blue,4),
+ new EmptyBorder(10,10,10,10)));
+
+ ImageIcon imgicon,imgicon2;
+ Image img,img2;
+ imgicon = JarTools.getImage("pen.gif");
+ img = imgicon.getImage();
+ img = img.getScaledInstance(24,24,Image.SCALE_SMOOTH);
+ imgicon2 = JarTools.getImage("eraserpen.gif");
+ img2 = imgicon2.getImage();
+ final Cursor cursor;
+ final Cursor cursor2;
+ cursor = Toolkit.getDefaultToolkit().createCustomCursor(img,new Point(0,0),"crayon");
+ cursor2 = Toolkit.getDefaultToolkit().createCustomCursor(img2,new Point(0,0),"eraser");
+
+ area.setCursor(cursor);
+
+ Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
+ final JPanel iconarea = new JPanel();
+ iconarea.setBackground(new Color(176,208,176));
+ iconarea.add(area);
+ container.add(iconarea);
+
+ String screen_label;
+ screen_label = " " + messages.getString("ScreenShotLabel");
+ JLabel label;
+ label = new JLabel(screen_label);
+ label.setBackground(new Color(176,208,176));
+ container.add(label);
+
+ JPanel panes;
+ panes = new JPanel();
+ panes.setLayout(new GridLayout(0,2));
+ panes.setBackground(new Color(176,208,176));
+
+ problem_details = new JTextArea(details);
+ problem_details.setWrapStyleWord(true);
+ problem_details.setEditable(true);
+ problem_details.setBackground(new Color(224,240,224));
+ JScrollPane scroll;
+ scroll = new JScrollPane(problem_details);
+ scroll.setBackground(new Color(176,208,176));
+ scroll.setPreferredSize(new Dimension(50,100));
+ scroll.setBorder(BorderFactory.createTitledBorder(
+ new EmptyBorder(0,0,0,0),
+ "Screen shot " +
+ messages.getString("ProblemDetails")));
+
+ panes.add(scroll);
+
+ JPanel pane;
+ pane = new JPanel();
+ pane.setLayout(new GridLayout(2,3));
+ pane.setBackground(new Color(176,208,176));
+ pane.setBorder(new EmptyBorder(15,15,15,15));
+
+ if ((window == null) || (screen == null)) {System.out.println("How come!");}
+ else
+ {
+ final JButton toggle;
+
+ if (windowNow == false)
+ {
+ toggle = new JButton("GLI window.");
+ toggle.setToolTipText("Showing only GLI window.");
+ }
+ else
+ {
+ toggle = new JButton("Whole screen.");
+ toggle.setToolTipText("Showing whole screen.");
+ }
+ toggle.setActionCommand("Toggle");
+ toggle.addActionListener(new ActionListener ()
+ {
+ public void actionPerformed (ActionEvent e)
+ {
+ frame.addMouseListener(mouse_blocker_listener);
+ frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+ if (windowNow == true)
+ {
+ windowNow = false;
+ toggle.setText("GLI window.");
+ toggle.setToolTipText("Showing only GLI window.");
+ window[0] = gp2.getScreenImage();
+ window[1] = gp2.getImage();
+ window[2] = gp2.getImage2();
+ gp.setScreenVersion(window,xcoord,ycoord,width,height);
+ area.setIcon(new ImageIcon(gp.getImage()));
+ }
+ else
+ {
+ windowNow = true;
+ toggle.setText("Whole screen.");
+ toggle.setToolTipText("Showing whole screen.");
+
+ window[0].flush();
+ window[0] = null;
+ window[1].flush();
+ window[1] = null;
+ window[2].flush();
+ window[2] = null;
+ window = null;
+ BufferedImage tmp;
+ gp2.getImage().flush();
+ tmp = gp2.getImage();
+ tmp.flush();
+ tmp = null;
+ gp2.getImage2().flush();
+ tmp = gp2.getImage2();
+ tmp.flush();
+ tmp = null;
+ gp2.flush();
+ gp2 = null;
+ System.gc();
+
+ window = gp.getWindowVersion(xcoord,ycoord,width,height);
+ gp2 = new Graphs(window);
+ area.setIcon(new ImageIcon(gp2.getImage()));
+ }
+
+ frame.pack();
+ frame.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+ frame.removeMouseListener(mouse_blocker_listener);
+ }
+ });
+ toggle.setBackground(new Color(176,208,176));
+ pane.add(toggle);
+ }
+
+
+ JButton pen;
+ pen = new JButton(JarTools.getImage("penicon.gif"));
+ pen.setText("Pen");
+ pen.setPreferredSize(new Dimension(30,20));
+ pen.setActionCommand("Pen");
+ pen.setToolTipText("Draw.");
+ pen.addActionListener(new ActionListener ()
+ {
+ public void actionPerformed (ActionEvent e)
+ {
+ area.setCursor(cursor);
+ drawNow = true;
+ }
+ });
+ pen.setBackground(new Color(176,208,176));
+ pane.add(pen);
+
+ JButton eraser;
+ eraser = new JButton(JarTools.getImage("eraser.gif"));
+ eraser.setText("Eraser");
+ eraser.setActionCommand("Eraser");
+ eraser.setPreferredSize(new Dimension(30,20));
+ eraser.setToolTipText("Erase.");
+ eraser.addActionListener(new ActionListener ()
+ {
+ public void actionPerformed (ActionEvent e)
+ {
+ area.setCursor(cursor2);
+ drawNow = false;
+ }
+ });
+ eraser.setBackground(new Color(176,208,176));
+ pane.add(eraser);
+
+ if ((window == null) || (screen == null))
+ {
+ JLabel empty = new JLabel(" ");
+ empty.setBackground(new Color(176,208,176));
+ pane.add(empty);
+ }
+
+ JButton button;
+ button = new JButton("Clear Scribble");
+ button.setActionCommand("Clear");
+ button.setToolTipText("Clear all the scribble lines.");
+ button.addActionListener(this);
+ button.setBackground(new Color(176,208,176));
+ pane.add(button);
+
+ JButton button2;
+ button2 = new JButton("Finish Scribble");
+ button2.setActionCommand(messages.getString("Send"));
+ button2.setToolTipText("Back to Feedback form.");
+ button2.addActionListener(this);
+ button2.setBackground(new Color(176,208,176));
+ pane.add(button2);
+
+ JButton button3;
+ button3 = new JButton("Send");
+ button3.setActionCommand("SendNow");
+ button3.setToolTipText("Finish reporting feedback and send all information now.");
+ button3.addActionListener(this);
+ button3.setBackground(new Color(176,208,176));
+ pane.add(button3);
+
+ panes.add(pane);
+
+ container.add(panes);
+
+ panes.setAlignmentX(Component.LEFT_ALIGNMENT);
+ label.setAlignmentX(Component.LEFT_ALIGNMENT);
+ iconarea.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+ container.setSize(container.getPreferredSize());
+ content.add(container);
+ }
+
+ /**
+ * This method will tell what the program should do when user click the buttons.
+ * @param e this is the event when user click button.
+ */
+ public void actionPerformed (ActionEvent e)
+ {
+ if(messages.getString("Send").equals(e.getActionCommand()))
+ {
+ frame.addMouseListener(mouse_blocker_listener);
+ frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+ sendNow = false;
+ if (windowNow == false)
+ {
+ window = gp.getWindowVersion(xcoord,ycoord,width,height);
+ }
+ else
+ {
+ window[0] = gp2.getScreenImage();
+ window[1] = gp2.getImage();
+ window[2] = gp2.getImage2();
+ gp.setScreenVersion(window,xcoord,ycoord,width,height);
+ }
+
+ bi5 = gp.getScreenImage();
+ bi = gp.getImage();
+ bi2 = gp.getImage2();
+ bi3 = window[1];
+ bi4 = window[2];
+ bi6 = window[0];
+
+ details = problem_details.getText();
+ frame.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+ frame.removeMouseListener(mouse_blocker_listener);
+ dispose();
+ }
+ if ("Clear".equals(e.getActionCommand()))
+ {
+ if (windowNow == false)
+ {
+ gp.reset();
+ area.setIcon(new ImageIcon(gp.getScreenImage()));
+ }
+ else
+ {
+ gp2.reset();
+ area.setIcon(new ImageIcon(gp2.getScreenImage()));
+ }
+ }
+
+ if ("SendNow".equals(e.getActionCommand()))
+ {
+ frame.addMouseListener(mouse_blocker_listener);
+ frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+ sendNow = true;
+
+ if (windowNow == false)
+ {
+ window = gp.getWindowVersion(xcoord,ycoord,width,height);
+ }
+ else
+ {
+ window[0] = gp2.getScreenImage();
+ window[1] = gp2.getImage();
+ window[2] = gp2.getImage2();
+ gp.setScreenVersion(window,xcoord,ycoord,width,height);
+ }
+
+ bi5 = gp.getScreenImage();
+ bi = gp.getImage();
+ bi2 = gp.getImage2();
+ bi3 = window[1];
+ bi4 = window[2];
+ bi6 = window[0];
+
+ details = problem_details.getText();
+ frame.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+ frame.removeMouseListener(mouse_blocker_listener);
+ dispose();
+ }
+ }
+
+ /**
+ * This method will create ImageIcon of the buffered image get from the screen shot instance.
+ * The image is screen shot whole screen size image.
+ * @return ImageIcon of the screen shot image.
+ */
+ protected ImageIcon createImageIcon(boolean iswindow)
+ {
+ if (iswindow == false)
+ {
+ if (screen[1] == null)
+ return new ImageIcon(screen[0]);
+ else
+ return new ImageIcon(screen[1]);
+ }
+ else
+ {
+ if (window[1] == null)
+ return new ImageIcon(window[0]);
+ else
+ return new ImageIcon(window[1]);
+ }
+ }
+
+ /**
+ * This is the special class, that will allows user to draw a line all over the label when
+ * user dragged the mouse inside the area.
+ */
+ private class SelectionArea extends JLabel
+ {
+ /**
+ * This is the owner of this label.
+ */
+ private Selecting controller;
+ /**
+ * This is the x coordinate of the first point.
+ */
+ private int x1;
+ /**
+ * This is the x coordinate of the second point.
+ */
+ private int x2;
+ /**
+ * This is the y coordinate of the first point.
+ */
+ private int y1;
+ /**
+ * This is the y coordinate of the second point.
+ */
+ private int y2;
+ /**
+ * This is the flag to say that user is over the label of the screen image
+ * and they are doing the mouse input from there.
+ */
+ private boolean ismouse;
+
+ /**
+ * This method will create a label with the specified image
+ * icon and it will added with mouse listener that will
+ * allow user to draw a line all over the image.
+ * @param image image icon to be added to the label.
+ * @param controller this is the owner of this label.
+ */
+ public SelectionArea(ImageIcon image, Selecting controller)
+ {
+ super(image);
+ this.controller = controller;
+ setOpaque(true);
+
+ MyListener myListener;
+ myListener = new MyListener();
+ addMouseListener(myListener);
+ addMouseMotionListener(myListener);
+ }
+
+ /**
+ * This is the modified mouse listener that will allow user to draw
+ * a line all over the label while user dragging the mouse.
+ */
+ private class MyListener extends MouseInputAdapter
+ {
+ /**
+ * This method will make a red dot in the place where user pressed the mouse.
+ * @param e the mouse event
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ int x;
+ x = e.getX();
+ int y;
+ y = e.getY();
+
+ x2 = x;
+ y2 = y;
+
+ x1 = x2;
+ y1 = y2;
+ }
+
+ /**
+ * This method will make a red line from (x1,y1) to the coordinate where
+ * the mouse is located now.
+ * @param e the mouse event.
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ ismouse = true;
+ updateSize(e);
+ }
+
+
+ /**
+ * This method will make a red line from (x1,y1) to the coordinate where
+ * the mouse is located now.
+ * @param e the mouse event.
+ */
+ public void updateSize(MouseEvent e)
+ {
+ int x;
+ x = e.getX();
+ int y;
+ y = e.getY();
+
+ x2 = x;
+ y2 = y;
+
+ repaint();
+ }
+ }
+
+ /**
+ * This method will repaint all the component inside the dialog-window.
+ * It will draw a red line from (x1,y1) to (x2,y2) coordinates and
+ * updates the value of x1,y1 and the icon for the label.
+ * @param g the graphics of this dialog-window.
+ */
+ public void paintComponent(Graphics g)
+ {
+ super.paintComponent(g);
+
+ if (ismouse == true)
+ {
+ if (windowNow == false)
+ {
+ if (drawNow == true)
+ gp.drawLines(x1,y1,x2,y2);
+ else
+ gp.eraseLines(x1,y1,x2,y2);
+ }
+ else
+ {
+ if (drawNow == true)
+ gp2.drawLines(x1,y1,x2,y2);
+ else
+ gp2.eraseLines(x1,y1,x2,y2);
+ }
+
+ x1 = x2;
+ y1 = y2;
+
+ if (windowNow == false)
+ {
+ area.setIcon(new ImageIcon(gp.getImage()));
+ }
+ else
+ {
+ area.setIcon(new ImageIcon(gp2.getImage()));
+ }
+
+ ismouse = false;
+ }
+ }
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/SendHTTP.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/SendHTTP.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/SendHTTP.java (revision 31635)
@@ -0,0 +1,421 @@
+package org.greenstone.gatherer.feedback;
+
+import java.util.Properties;
+import java.net.*;
+import java.io.*;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.text.MessageFormat;
+import javax.swing.*;
+import java.awt.Dimension;
+
+/**
+ * This class will send the 2 jar files to the server using HTTP Post.
+ */
+public class SendHTTP
+{
+ /**
+ * This is variable will hold the arguments provided by user in order to send
+ */
+ private String[] argument;
+ /**
+ * This variable will hold the resource of the words that is stored in Messages.properties file.
+ * The calling using messages.getString(someString) will caused someString to be translated
+ * into some other string that is hold in that file.usually it will caused it to be translated
+ * to the language that the user use or choose to have as stated in Locale.
+ */
+ private static ResourceBundle msg;
+ /**
+ * This variable will hold the jar file unique code.
+ * For example: Jar123File.jar then file_code = 123
+ */
+ private String file_code;
+ /**
+ * This is the flag that will say whether or not the sending is successful.
+ * If failed = false means its succesful otherwise its failed.
+ */
+ private boolean failed = false;
+
+ /**
+ * Crates instance of SendHTTP
+ */
+ public SendHTTP () {}
+
+ /**
+ * This method will send 2 files back to the server.
+ * It will also send the code if the sending is failed then it will give user a message saying whats
+ * the possible error of it, and it will make the jar file changed into
+ * Unsend_Jar123File.jar file.
+ * If the sending is succesful then it will try to send all the other jar files
+ * that were failed to send before.And then it will deletes all the jar files
+ * that are send successfully.
+ * @param args the argument provided to send the email.
+ * @param messages hold the resource of the words that is stored in Messages.properties file.
+ * @param code the code of the email to be send.
+ */
+ public void sendMail(String[] args,ResourceBundle messages,String code)
+ {
+ argument = args;
+ msg = messages;
+ file_code = code;
+
+ try
+ {
+ String BND = "---------------------------7d021a37605f0";
+ String hostURL = "http://kanuka.cs.waikato.ac.nz:8090/glifeedback/Upload";
+
+ URL mURL = new URL(hostURL+"?name=posting");
+ URLConnection conn = mURL.openConnection();
+
+ if (conn instanceof HttpURLConnection)
+ ((HttpURLConnection) conn).setRequestMethod("POST");
+
+ conn.setDoInput(true);
+ conn.setDoOutput(true);
+ conn.setUseCaches(false);
+ conn.setDefaultUseCaches(false);
+ conn.setRequestProperty("Accept","*/*");
+ conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BND);
+ conn.setRequestProperty("Connection","Keep-Alive");
+ conn.setRequestProperty("Cache-Control","no-cache");
+
+ DataOutputStream out;
+ out = new DataOutputStream(conn.getOutputStream());
+
+ //--- Sending subject -------
+ String txt;
+ txt = "--" + BND + "\r\n";
+ out.writeBytes(txt);
+
+ txt = "Content-Disposition: form-data; ";
+ out.writeBytes(txt);
+
+ String subject;
+ subject = "Send Feedback" + code;
+ txt = "name=\"" + "Subject" + "\"\r\n";
+ out.writeBytes(txt);
+
+ txt = "\r\n";
+ out.writeBytes(txt);
+
+ txt = subject + "\r\n";
+ out.writeBytes(txt);
+
+ out.flush();
+
+ //--- Sending text ------
+ txt = "--" + BND + "\r\n";
+ out.writeBytes(txt);
+
+ txt = "Content-Disposition: form-data; ";
+ out.writeBytes(txt);
+
+ String attmnt;
+ attmnt = "We have succesfully received your feedback report.\n" +
+ "To check what files we have received go to this link: " + hostURL +
+ "/?name=" + code + "\n" +
+ "Please keep referencing to this ID Code for this feedback report.\n" +
+ "ID Code: " + code;
+
+ txt = "name=\"" + "Text" + "\"\r\n";
+ out.writeBytes(txt);
+
+ txt = "\r\n";
+ out.writeBytes(txt);
+
+ txt = attmnt + "\r\n";
+ out.writeBytes(txt);
+
+ out.flush();
+
+ //--------- Sending xml-jar file
+ FileInputStream fis;
+ File f;
+ String name,path;
+
+ f = new File(args[1]);
+
+ txt = "--" + BND + "\r\n";
+ out.writeBytes(txt);
+
+ txt = "Content-Disposition: form-data; ";
+ out.writeBytes(txt);
+
+ name = f.getName();
+ txt = "name=\"" + name + "\"; ";
+ out.writeBytes(txt);
+
+ path = f.getAbsolutePath();
+ txt = "filename=\"" + path + "\"\r\n";
+ out.writeBytes(txt);
+
+ txt = "Content-Type: aplication/octet-stream\r\n";
+ out.writeBytes(txt);
+
+ txt = "\r\n";
+ out.writeBytes(txt);
+
+ fis = new FileInputStream(f);
+ byte[] data = new byte[1024];
+ int r = 0;
+ while((r = fis.read(data, 0, data.length)) != -1)
+ {
+ out.write(data, 0, r);
+ }
+ fis.close();
+
+ txt = "\r\n";
+ out.writeBytes(txt);
+
+ out.flush();
+
+ //--- Sending parser.jar ----
+ txt = "--" + BND + "\r\n";
+ out.writeBytes(txt);
+
+ txt = "Content-Disposition: form-data; ";
+ out.writeBytes(txt);
+
+ f = new File(args[4]);
+
+ name = f.getName();
+ txt = "name=\"" + name + "\"; ";
+ out.writeBytes(txt);
+
+ path = f.getAbsolutePath();
+ txt = "filename=\"" + path + "\"\r\n";
+ out.writeBytes(txt);
+
+ txt = "Content-Type: application/octet-stream\r\n";
+ out.writeBytes(txt);
+
+ txt = "\r\n";
+ out.writeBytes(txt);
+
+ fis = new FileInputStream(f);
+ data = new byte[1024];
+ r = 0;
+ while((r = fis.read(data, 0, data.length)) != -1)
+ {
+ out.write(data, 0, r);
+ }
+ fis.close();
+
+ txt = "\r\n";
+ out.writeBytes(txt);
+
+ out.flush();
+
+ //----- finished sending -----
+ txt = "--" + BND + "--";
+ out.writeBytes(txt);
+
+ out.flush();
+ out.close();
+
+ //----- getting the response ------
+ BufferedReader reader;
+ reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
+ String responseLine;
+ responseLine = null;
+
+ StringBuffer response;
+ response = new StringBuffer();
+ while ((responseLine = reader.readLine()) != null)
+ response.append(responseLine+"\n");
+
+ reader.close();
+
+
+ //------ our application's response to server's response
+ /*if (response.equals("failed"))
+ {
+ failed = true;
+
+ JOptionPane.showMessageDialog(null,messages.getString("InternalError")+ "\n" +
+ messages.getString("Emaillater"),messages.getString("Error"),
+ JOptionPane.ERROR_MESSAGE);
+ //Conformation.setFinish(true);
+ }
+ else if (response.equals("success"))
+ failed = false;
+ else
+ {*/
+ failed = false;
+ JDialog.setDefaultLookAndFeelDecorated(false);
+ JDialog dlg = new JDialog();
+ JLabel lbl = new JLabel (response.toString());
+
+ dlg.getContentPane().add(lbl);
+ dlg.pack();
+ dlg.setVisible(true);
+ // }
+
+ //Conformation.setFinish(true);
+ }
+ catch (MalformedURLException exp)
+ {
+ failed = true;
+
+ JOptionPane.showMessageDialog(null,"malformed URL" + "\n" +
+ messages.getString("Emaillater"),messages.getString("Error"),
+ JOptionPane.ERROR_MESSAGE);
+ System.out.println("malformed URL");
+ }
+ catch (IOException e)
+ {
+ failed = true;
+
+ JOptionPane.showMessageDialog(null,messages.getString("InternalError")+ "\n" +
+ messages.getString("Emaillater"),messages.getString("Error"),
+ JOptionPane.ERROR_MESSAGE);e.printStackTrace();
+ }
+
+ String dirname;
+ dirname = "xmlfeedback/";
+
+ if (failed == false)
+ {
+ File f;
+ f = new File(dirname + "Jar" + file_code + "File.jar");
+ f.delete();
+
+ File file;
+ file = new File(dirname + ".");
+ File[] fileList;
+ fileList = file.listFiles();
+
+ int y;
+ for (y = 0 ; y < fileList.length ; y++)
+ {
+ String filename;
+ filename = isRightJarFile(fileList[y]);
+ if (filename != null)
+ {
+ renameTo(fileList[y],new File(filename));
+ argument[1] = filename;
+ sendMail(argument,msg,getCode(filename));
+ }
+ }
+ }
+ else
+ {
+ File f2;
+ f2 = new File(dirname + "Jar" + file_code + "File.jar");
+ renameTo(f2,new File(dirname + "Unsend_Jar" + file_code + "File.jar"));
+ }
+
+ }
+
+ /**
+ * This method will rename file.
+ * @param src is the name of the file to be renamed.
+ * @param dest is the desired file name.
+ */
+ private void renameTo (File src, File dest)
+ {
+ if (src.renameTo(dest) == false)
+ {
+ try
+ {
+ FileInputStream in;
+ in = new FileInputStream(src);
+ FileOutputStream out;
+ out = new FileOutputStream(dest);
+ int next;
+
+ while (true)
+ {
+ next = in.read();
+ if (next == -1)
+ break;
+ else
+ out.write((byte) next);
+ }
+
+ in.close();
+ out.close();
+ }
+ catch (IOException exp) {exp.printStackTrace();}
+ }
+ }
+
+ /**
+ * This method will get the extension of the file.
+ * @param f is the file that we want to get the extension.
+ * @return the file extension
+ */
+ private String getExtension(File f)
+ {
+ String ext;
+ ext = null;
+ String s;
+ s = f.getName();
+ int i;
+ i = s.lastIndexOf('.');
+
+ if (i > 0 && i < s.length() - 1)
+ {
+ ext = s.substring(i+1).toLowerCase();
+ }
+ return ext;
+ }
+
+ /**
+ * This method will get all the name of the file after "_" if the file its a jar file.
+ * For example Unsend_Jar123File.jar then this method will return Jar123File, otherwise
+ * file with any other format will return null.
+ * @param f is the file that we want to get the name.
+ */
+ private String isRightJarFile(File f)
+ {
+ String ext;
+ ext = null;
+
+ if (f.isDirectory() == true)
+ return null;
+
+ if (getExtension(f).compareTo("jar") == 0)
+ {
+ String s;
+ s = f.getName();
+ int i;
+ i = s.lastIndexOf("_");
+
+ if (i > 0 && i < s.length() - 1)
+ {
+ ext = s.substring(i+1,s.length());
+ }
+ else
+ ext = null;
+
+ return ext;
+ }
+ else
+ return null;
+ }
+
+ /**
+ * This method will getting the code that inside the Jar filename.
+ * For example Jar123File then this method will return 123.
+ */
+ private String getCode (String s)
+ {
+ int i;
+ i = s.lastIndexOf("Jar");
+ int j;
+ j = s.indexOf("File");
+ String code;
+ code = s.substring((i+3),j);
+ return code;
+ }
+}
+
+
+
+
+
+
+
+
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/SeparateGraphs.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/SeparateGraphs.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/SeparateGraphs.java (revision 31635)
@@ -0,0 +1,105 @@
+/** This class is not currently used and therefore will not be compiled by makegli. */
+
+package org.greenstone.gatherer.feedback;
+
+import java.io.*;
+import javax.imageio.*;
+import java.awt.*;
+import java.awt.image.*;
+import com.sun.image.codec.jpeg.*;
+import javax.swing.*;
+
+/**
+ * This class will separate a image file into 2 jpeg image files by taking
+ * all the point in that image file that has the color red and save all that
+ * points to another image file, while all the other point to another image
+ * file.
+ * @author Veronica Liesaputra
+ */
+public class SeparateGraphs
+{
+ /**
+ * This is the BufferedImage that we want to separate.
+ */
+ private BufferedImage bi;
+
+ /**
+ * This constructor will save the image inside the file with the specified
+ * filename to the BufferedImage.
+ */
+ public SeparateGraphs(String filename)
+ {
+ try
+ {
+ File f = new File(filename);
+ bi = ImageIO.read(f);
+ }
+ catch(IOException exp) {}
+ }
+
+ /**
+ * This method will separate bi into 2 image and save it to
+ * jpeg files with the specified filename.
+ * The 2 image separated will be, 1 image is the image of all the points in bi that
+ * have the color red and another image is the image of all other points in bi that
+ * do not have the color red.
+ * @param file1 this is the filename for the image of all other points that do not have color red.
+ * @param file2 this is the filename for the image of all points that have the color red.
+ */
+ public void separate(String file1,String file2)
+ {
+ BufferedImage bi1,bi2;
+
+ int width = bi.getWidth();
+ int height = bi.getHeight();
+
+ bi1 = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
+ bi2 = new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);
+
+ for (int y = 0 ; y < height ; y++)
+ {
+ for (int x = 0 ; x < width ; x++)
+ {
+ int rgb = bi.getRGB(x,y);
+ Color c = new Color(rgb,true);
+ if (c != Color.RED)
+ {
+ bi1.setRGB(x,y,rgb);
+ }
+ else
+ {
+ bi2.setRGB(x,y,rgb);
+ }
+ }
+ }
+
+ save(bi1,file1);
+ save(bi2,file2);
+ }
+
+ /**
+ * This method will save the BufferedImage into a jpeg file with the specified filenames.
+ * @param b is the BufferedImage to be saved.
+ * @param filename is the name of the jpeg files.
+ */
+ public void save(BufferedImage b,String filename)
+ {
+ try
+ {
+ File file = new File(filename);
+ FileOutputStream out = new FileOutputStream(file);
+
+ JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
+ JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(b);
+ param.setQuality(1.0f, false);
+ encoder.setJPEGEncodeParam(param);
+ encoder.encode(b);
+
+ } catch (Exception ex) {}
+ }
+}
+
+
+
+
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/Separating.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/Separating.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/Separating.java (revision 31635)
@@ -0,0 +1,108 @@
+/** This class is not currently used and therefore will not be compiled by makegli. */
+
+package org.greenstone.gatherer.feedback;
+
+import java.io.*;
+import javax.imageio.*;
+import java.awt.*;
+import java.awt.image.*;
+import com.sun.image.codec.jpeg.*;
+import javax.swing.*;
+
+/**
+ * This method will separate 1 image file into 2 image with the help of
+ * another file and sace the difference into a jpeg files.
+ * For example : We have 1 image file which is a screen shot and another image file
+ * is the screenshot with some line in it.
+ * This class will separate the screen shot and the line into 2 separates
+ * images.And then it will save the line into a jpeg files.
+ *
+ * @author Veronica Liesaputra
+ */
+public class Separating
+{
+ /**
+ * This is the BufferedImage of image inside the reference image file.
+ */
+ private BufferedImage bi;
+ /**
+ * This is the BufferedImage of image that we want to separate into
+ * 2 different image using the reference file.
+ */
+ private BufferedImage src_bi;
+
+ /**
+ * It will read whats inside the 2 given filenames and put it in the
+ * right BufferedImage.
+ * @param filename1 this is the file name of the reference image file.
+ * @param filename2 this is the file name of the file we want to separate into 2.
+ */
+ public Separating(String filename1,String filename2)
+ {
+ try
+ {
+ File f1 = new File(filename1);
+ File f2 = new File(filename2);
+ bi = ImageIO.read(f1);
+ src_bi = ImageIO.read(f2);
+ }
+ catch(IOException exp) {}
+ }
+
+ /**
+ * This method will get the difference that exist between bi and src_bi and
+ * will save the difference inside a jpeg file with the specified file name.
+ * @param file1 is the name of the file where we want to save the image.
+ */
+ public void separate(String file1)
+ {
+ BufferedImage bi1;
+
+ int width = bi.getWidth();
+ int height = bi.getHeight();
+
+ bi1 = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
+
+ for (int y = 0 ; y < height ; y++)
+ {
+ for (int x = 0 ; x < width ; x++)
+ {
+ int rgb = bi.getRGB(x,y);
+ int src_rgb = src_bi.getRGB(x,y);
+
+ if (src_rgb != rgb)
+ {
+ bi1.setRGB(x,y,rgb);
+ }
+ }
+ }
+
+ save(bi1,file1);
+ }
+
+ /**
+ * This method will save a BufferedImage to a jpeg file with the specified filename.
+ * @param b is the BufferedImage to be saved.
+ * @param filename is the name of the jpeg file.
+ */
+ public void save(BufferedImage b,String filename)
+ {
+ try
+ {
+ File file = new File(filename);
+ FileOutputStream out = new FileOutputStream(file);
+
+ JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
+ JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(b);
+ param.setQuality(1.0f, false);
+ encoder.setJPEGEncodeParam(param);
+ encoder.encode(b);
+
+ } catch (Exception ex) {}
+ }
+}
+
+
+
+
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/UserComponent.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/UserComponent.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/UserComponent.java (revision 31635)
@@ -0,0 +1,439 @@
+package org.greenstone.gatherer.feedback;
+
+import java.util.*;
+import java.awt.image.*;
+import java.io.*;
+import javax.imageio.*;
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * This class willl hold the information about a component and the contents inside the component.
+ * @author Veronica Liesaputra
+ */
+public class UserComponent implements Serializable
+{
+ /**
+ * This variable will hold what is the type of instance this component is.
+ */
+ private String type = null;
+
+ /**
+ * This variable will store the title of this component.
+ */
+ private String title = null;
+
+ /**
+ * This varaible will store the array list of the contents of this component.
+ */
+ private ArrayList content = null;
+
+ /**
+ * This variable will store the status of this component, whether or not
+ * this component is selected by the user.
+ */
+ private String selected = null;
+
+ /**
+ * This variable will store the status of this component, whether or not
+ * this component is visible.
+ */
+ private String visible = null;
+
+ /**
+ * This variable will hold the descrption text saying that there is no
+ * image in this component.
+ */
+ private String image = null;
+
+ /**
+ * This is the supposed filename of the image that is in this component should use if
+ * we want to save it to a jpeg file.
+ */
+ private String img = null;
+
+ /**
+ * This is the string representation of the image that is in this component.
+ */
+ private String imgFile = null;
+
+ /**
+ * This is the index that will help storing the data to the correct data member.
+ */
+ private int index;
+
+ /**
+ * This is the height of the image that is in this component.
+ */
+ private String height;
+
+ /**
+ * This is the width of the image that is in this component.
+ */
+ private String width;
+
+ /**
+ * This variable will hold the tooltiptext set for this component.
+ */
+ private String tooltip;
+
+ /**
+ * This will initialized content to a new empty Arrray list.
+ */
+ public UserComponent()
+ {
+ content = new ArrayList();
+ }
+
+ /**
+ * This method will get the width of the image that is inside this component.
+ * @return image's width.
+ */
+ public String getWidth()
+ {
+ return width;
+ }
+
+ /**
+ * This method will get the height of the image that is inside this component.
+ * @return image's height.
+ */
+ public String getHeight()
+ {
+ return height;
+ }
+
+ /**
+ * This method will get the type of instance this component is.
+ * @return component's type.
+ */
+ public String getType()
+ {
+ return type;
+ }
+
+ /**
+ * This method will get the tooltiptext set for this component.
+ * @return component's tooltiptext.
+ */
+ public String getToolTip()
+ {
+ return tooltip;
+ }
+
+ /**
+ * This method will get the title for this component.
+ * @return component's title.
+ */
+ public String getTitle()
+ {
+ return title;
+ }
+
+ /**
+ * This method will get the content inside this component.
+ * @return component's content.
+ */
+ public ArrayList getContent()
+ {
+ return content;
+ }
+
+ /**
+ * This method will get whether or not this component is
+ * selected by the user.
+ * @return component selected or not.
+ */
+ public String getSelected()
+ {
+ return selected;
+ }
+
+ /**
+ * This method will get whether or not this component is
+ * visible.
+ * @return component visible or not.
+ */
+ public String getVisible()
+ {
+ return visible;
+ }
+
+ /**
+ * This method will get the descrption text saying that there is no
+ * image in this component.
+ * @return the description text.
+ */
+ public String getImage()
+ {
+ return image;
+ }
+
+ /**
+ * This method will get the supposed filename of the image that is in this component should use if
+ * we want to save it to a jpeg file.
+ * @return the image file name.
+ */
+ public String getImageFileName()
+ {
+ return img;
+ }
+
+ /**
+ * This method will get the string representation of the image that is in this component.
+ * @return the image.
+ */
+ public String getImageFile()
+ {
+ return imgFile;
+ }
+
+ /**
+ * This method will set the index value.
+ * (Precondition: (num > 0))
+ * @param num the new index value.
+ */
+ public void startContent (int num)
+ {
+ index = num;
+ }
+
+ /**
+ * This method will add the UserComponent to the content array list.
+ * @param comp the content of the component.
+ */
+ public void saveContent (UserComponent comp)
+ {
+ content.add(comp);
+ }
+
+ /**
+ * This method will set the data member value to the text passed
+ * to this method depending on the index value.
+ * @param text the value to be set to the data member.
+ */
+ public void saveContent (String text)
+ {
+ if (text == null)
+ text = " ";
+
+ switch (index)
+ {
+ case 1 : type = text; break;
+ case 2 : title = text; break;
+ case 3 : content.add(text); break;
+ case 4 : selected = text;break;
+ case 5 : visible = text;break;
+ case 6 : image = text; break;
+ case 7 : tooltip = text;break;
+ }
+ }
+
+ /**
+ * This method will encode the ImageIcon into its String representation using Based64encoder and
+ * it will also set the image's filename,height and width.
+ * @param image is the image to be converted.
+ * @param filename is the file name of the image.
+ */
+ public void saveImage (ImageIcon image,String filename)
+ {
+ /*int w;
+ w = image.getIconWidth();
+ int h;
+ h = image.getIconHeight();
+
+ height = "" + h;
+ width = "" + w;
+ img = filename;
+
+ if (w > 50)
+ w = w - 50;
+
+ if (h > 50)
+ h = h - 50;
+
+ ImageIcon icon;
+ icon = new ImageIcon(image.getImage().getScaledInstance(w,h,Image.SCALE_SMOOTH));
+
+ if ( w < 0 || h < 0)
+ {
+ System.out.println(img);
+ return;
+ }
+
+ BufferedImage bi;
+ bi =new BufferedImage(w,h, BufferedImage.TYPE_INT_RGB);
+ Graphics2D big;
+ big = bi.createGraphics();
+
+ big.drawImage(icon.getImage(),0,0,null);
+
+ try
+ {
+ ByteArrayOutputStream stream;
+ stream = new ByteArrayOutputStream();
+ ImageIO.write(bi,"jpeg",stream);
+ byte[] bytes;
+ bytes = stream.toByteArray();
+ imgFile = Base64.encodeBytes(bytes);
+ }
+ catch (IOException exp) {}
+
+ big.dispose();
+ image = null;
+ bi = null;
+ icon = null;*/
+ }
+
+ /**
+ * This method will encode the Icon into its String representation using Based64encoder and
+ * it will also set the image's filename,height and width.
+ * @param image is the image to be converted.
+ * @param filename is the file name of the image
+ */
+ public void saveImage (Icon image,String filename)
+ {
+ if (image == null)
+ System.out.println("null image??");
+ else if (image instanceof ImageIcon)
+ saveImage((ImageIcon)image, filename);
+ else
+ System.out.println("Its a icon");
+ //saveIcon (image,filename);
+ }
+
+ /**
+ * This method will encode the Icon into its String representation using Based64encoder and
+ * it will also set the image's filename,height and width.
+ * @param image is the image to be converted.
+ * @param filename is the file name of the image.
+ */
+ private void saveIcon (Icon image,String filename)
+ {
+ JFrame lbl = new JFrame();
+ lbl.setVisible(false);
+
+ ImageIcon icon;
+ Image img;
+ img = iconToImage(image,lbl);
+ icon = new ImageIcon(img);
+ lbl.dispose();
+ lbl = null;
+ saveImage(icon,filename);
+ }
+
+ /**
+ * This method will save to content of this UserComponent to an xml file.
+ * The xml file should already have an opening tag and closing tag first before calling
+ * this method or it wont make a valid xml file.
+ * @param sx this is the xml file where we want to save it.
+ */
+ public void sendingXML (SaveToXML sx)
+ {
+ if (type != null)
+ {
+ sx.startContent(1);
+ sx.saveContent(type);
+ sx.closeContent(1);
+ }
+
+ if (title != null)
+ {
+ sx.startContent(2);
+ sx.saveContent(title);
+ sx.closeContent(2);
+ }
+
+ if (content != null)
+ {
+ sx.startContent(3);
+ for (int j = 0 ; j < content.size() ; j++)
+ {
+ if (content.get(j) instanceof String)
+ sx.saveContent((String) content.get(j));
+ else
+ {
+ sx.startContent(0);
+ UserComponent cmp;
+ cmp = (UserComponent) content.get(j);
+ cmp.sendingXML(sx);
+ sx.closeContent(0);
+ }
+ }
+ sx.closeContent(3);
+ }
+
+ if (selected != null)
+ {
+ sx.startContent(4);
+ sx.saveContent(selected);
+ sx.closeContent(4);
+ }
+
+ if (visible != null)
+ {
+ sx.startContent(5);
+ sx.saveContent(visible);
+ sx.closeContent(5);
+ }
+
+ if (img != null)
+ {
+ sx.startContent(6);
+ sx.saveImage(imgFile,img,width,height);
+ sx.closeContent(6);
+ }
+ else
+ {
+ if (image != null)
+ {
+ sx.startContent(11);
+ sx.saveContent(image);
+ sx.closeContent(11);
+ }
+ }
+
+ if (tooltip != null)
+ {
+ sx.startContent(12);
+ sx.saveContent(tooltip);
+ sx.closeContent(12);
+ }
+ }
+
+ /**
+ * This method will convert an Icon to a BufferedImage.
+ * @param icon icon to be converted.
+ * @param targetComponent is the component where we want to draw this BufferedImage.
+ * @return is the BufferedImage of the icon.
+ */
+ public BufferedImage iconToImage(Icon icon, Component targetComponent)
+ {
+ int w,h;
+ w = icon.getIconWidth();
+ h = icon.getIconHeight();
+ GraphicsConfiguration gc;
+ gc = targetComponent.getGraphicsConfiguration();
+ BufferedImage image;
+ image = gc.createCompatibleImage(w, h,Transparency.TRANSLUCENT);
+
+ Graphics2D g;
+ g = image.createGraphics();
+ icon.paintIcon(targetComponent,g,0,0);
+ g.dispose();
+ return image;
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/ZipFile.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/ZipFile.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/feedback/ZipFile.java (revision 31635)
@@ -0,0 +1,266 @@
+package org.greenstone.gatherer.feedback;
+
+import java.io.*;
+import java.util.jar.*;
+import java.util.zip.*;
+
+/**
+ * This class will help to zip and unzip files.
+ * @author Veronica Liesaputra
+ */
+public class ZipFile
+{
+ /**
+ * This is the specified buffer size.
+ */
+ private static final int BUFFER = 2048;
+
+ public ZipFile ()
+ {}
+
+ /**
+ * This method will unzip the MyZipFile.zip file and delete the file.
+ */
+ public void unZipFile ()
+ {
+ try
+ {
+ BufferedOutputStream dest;
+ dest = null;
+ FileInputStream fis;
+ fis = new FileInputStream("MyZipFile.zip");
+ ZipInputStream zis;
+ zis = new ZipInputStream(new BufferedInputStream(fis));
+ ZipEntry entry;
+ while((entry = zis.getNextEntry()) != null)
+ {
+ int count;
+ byte data[];
+ data = new byte[BUFFER];
+ // write the files to the disk
+ File f;
+ f = new File(entry.getName());
+ if (isRightLogExtension(f) == true)
+ {
+ FileOutputStream fos;
+ fos = new FileOutputStream(entry.getName());
+ dest = new BufferedOutputStream(fos, BUFFER);
+ while ((count = zis.read(data, 0, BUFFER)) != -1)
+ {
+ dest.write(data, 0, count);
+ }
+ dest.flush();
+ dest.close();
+ }
+ }
+ zis.close();
+ File f3;
+ f3 = new File("MyZipFile.zip");
+ f3.delete();
+ }
+ catch (FileNotFoundException fexp) {}
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * This method will zip all the xml files into one Jar file.
+ */
+ public void sendZipXMLFile ()
+ {
+ try
+ {
+ String dirname;
+ dirname = "xmlfeedback/";
+ BufferedInputStream origin;
+ origin = null;
+ FileOutputStream dest;
+ dest = new FileOutputStream(dirname + "Jar" +
+ FeedbackInterface.getCode() +
+ "File.jar");
+ JarOutputStream out;
+ out = new JarOutputStream(new BufferedOutputStream(dest));
+ out.setMethod(ZipOutputStream.DEFLATED);
+ byte data[];
+ data = new byte[BUFFER];
+
+ File f;
+ f = new File(dirname + ".");
+ File[] fileList;
+ fileList = f.listFiles();
+ File[] files;
+ files = new File[fileList.length + 8];
+
+ int index,j,i;
+ index = 0;
+ for (j = 0 ; j < fileList.length ; j++)
+ {
+ String name;
+ name = fileList[j].getName();
+
+ if (isRightXMLExtension(fileList[j]) == true)
+ {
+ files[index] = fileList[j];
+ index++;
+ }
+ }
+
+ for ( i = 0; i < index; i++)
+ {
+ FileInputStream fi;
+ fi = new FileInputStream(files[i]);
+ origin = new BufferedInputStream(fi, BUFFER);
+ ZipEntry entry;
+ entry = new ZipEntry(files[i].getName());
+ out.putNextEntry(entry);
+ int count;
+ while((count = origin.read(data, 0,BUFFER)) != -1)
+ {
+ out.write(data, 0, count);
+ }
+ origin.close();
+ }
+
+ out.close();
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * This method will zip all .log file to MyZipFile.zip file and it will
+ * delete the history.log file.
+ */
+ public void sendZipFile ()
+ {
+ try
+ {
+ BufferedInputStream origin;
+ origin = null;
+ FileOutputStream dest;
+ dest = new FileOutputStream("MyZipFile.zip");
+ ZipOutputStream out;
+ out = new ZipOutputStream(new BufferedOutputStream(dest));
+ out.setMethod(ZipOutputStream.DEFLATED);
+ byte data[];
+ data = new byte[BUFFER];
+
+ File f;
+ f = new File(".");
+ File[] fileList;
+ fileList = f.listFiles();
+ File[] files;
+ files = new File[fileList.length];
+
+ int index,i,j;
+ index = 0;
+ for ( j = 0 ; j < fileList.length ; j++)
+ {
+ if (isRightLogExtension(fileList[j]) == true)
+ {
+ files[index] = fileList[j];
+ index++;
+ }
+ }
+
+ for (i = 0; i < index; i++)
+ {
+ FileInputStream fi;
+ fi = new FileInputStream(files[i]);
+ origin = new BufferedInputStream(fi, BUFFER);
+ ZipEntry entry;
+ entry = new ZipEntry(files[i].getName());
+ out.putNextEntry(entry);
+ int count;
+ while((count = origin.read(data, 0,BUFFER)) != -1)
+ {
+ out.write(data, 0, count);
+ }
+ origin.close();
+ }
+ out.close();
+ File f2;
+ f2 = new File("history.log");
+ f2.delete();
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * This method will get the extension of the given file.
+ * @param f the file we want to know the extension.
+ * @return the file extension.
+ */
+ public String getExtension(File f)
+ {
+ String ext;
+ ext = null;
+ String s;
+ s = f.getName();
+ int i;
+ i = s.lastIndexOf('.');
+
+ if (i > 0 && i < s.length() - 1)
+ {
+ ext = s.substring(i+1).toLowerCase();
+ }
+ return ext;
+ }
+
+ /**
+ * This method will check whether or not the give file has
+ * an .xml extension.
+ * @param f the file we want to check the extension.
+ * @return whether or not the file has xml as its file extension.
+ */
+ public boolean isRightXMLExtension(File f)
+ {
+ if (f.isDirectory() == true)
+ {
+ return false;
+ }
+ String ext;
+ ext = getExtension(f);
+ if ((ext != null) && (ext.compareTo("xml") == 0))
+ return true;
+ else
+ return false;
+ }
+
+ /**
+ * This method will check whether or not the give file has
+ * an .log extension.
+ * @param f the file we want to check the extension.
+ * @return whether or not the file has log as its file extension.
+ */
+ public boolean isRightLogExtension(File f)
+ {
+ if (f.isDirectory() == true)
+ return false;
+
+ String ext;
+ ext = getExtension(f);
+
+ if ((ext != null) && (ext.compareTo("log") == 0))
+ return true;
+ else
+ return false;
+ }
+}
+
+
+
+
+
+
+
+
+
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/FileAlreadyExistsException.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/FileAlreadyExistsException.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/FileAlreadyExistsException.java (revision 31635)
@@ -0,0 +1,11 @@
+package org.greenstone.gatherer.file;
+
+import java.lang.Exception;
+
+public class FileAlreadyExistsException
+ extends Exception {
+
+ public FileAlreadyExistsException() {
+ super();
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/FileAssociationManager.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/FileAssociationManager.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/FileAssociationManager.java (revision 31635)
@@ -0,0 +1,367 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.file;
+
+import java.io.*;
+import java.util.regex.*;
+import javax.swing.table.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.gui.FileAssociationDialog;
+import org.greenstone.gatherer.gui.PreviewCommandDialog;
+import org.greenstone.gatherer.util.ExternalProgram;
+import org.greenstone.gatherer.util.StaticStrings;
+import org.greenstone.gatherer.util.Utility;
+import org.greenstone.gatherer.util.XMLTools;
+import org.w3c.dom.*;
+
+public class FileAssociationManager
+ extends AbstractTableModel {
+ static final public String FILENAME_ARG = "%1";
+ static final private String ESCAPE = "\\\\"; // '\'
+ static final private String ESCAPED_ESCAPE = "\\\\\\\\"; // '\\'
+ static final private String SPACE = " ";
+ static final private String ESCAPED_SPACE = "\\\\ ";
+ private Element associations_element;
+ private File data_file;
+
+ public FileAssociationManager() {
+ // Retrieve the associations_element from the config
+ associations_element = Configuration.getFileAssociations();
+ // Initialize the associations. This involves looking through all current associations searching for those with a command of "".
+ if(associations_element != null) {
+ NodeList entries = associations_element.getElementsByTagName(StaticStrings.ENTRY_ELEMENT);
+ for(int i = 0; i < entries.getLength(); i++) {
+ Element entry = (Element) entries.item(i);
+ String command = XMLTools.getValue(entry);
+ // If we encounter a command of ""...
+ if(command.length() == 0) {
+ // if we are on windows, we default to the start command
+ if(Utility.isWindows()) {
+ if (Utility.isWindows9x()) {
+ XMLTools.setValue(entry, StaticStrings.WIN_9X_OPEN_COMMAND);
+ } else {
+ XMLTools.setValue(entry, StaticStrings.WIN_OPEN_COMMAND);
+ }
+ }
+ // and if we are on mac, we default to the open program
+ else if(Utility.isMac()) {
+ XMLTools.setValue(entry, StaticStrings.MAC_OPEN_COMMAND);
+ }
+ //else { XMLTools.setValue(entry, StaticStrings.LINUX_OPEN_COMMAND); } // assume linux?
+ }
+ command = null;
+ entry = null;
+ }
+ entries = null;
+ }
+ else {
+ DebugStream.println("Didn't parse anything. About to crash.");
+ }
+ }
+
+ public void edit() {
+ FileAssociationDialog dialog = new FileAssociationDialog(this);
+ dialog.display(null);
+ dialog = null;
+ }
+
+ public String getBrowserCommand(String url) {
+ DebugStream.println("Get browser command: " + url);
+ // First off we try to retrieve one from the configuration
+ String command = Configuration.getPreviewCommand();
+ // If that worked, substitute in the url and return
+ if(command != null && command.length() > 0) {
+ command = command.replaceAll("%1", url);
+ DebugStream.println("Result = " + command);
+ return command;
+ }
+ command = null;
+ // Failing that we have a guess at a sensible default
+ if(Utility.isWindows()) {
+ // we use cmd and start
+ if (Utility.isWindows9x()) {
+ command = StaticStrings.WIN_9X_OPEN_COMMAND;//"command.com /c start \""+url+"\"";
+ } else {
+ command = StaticStrings.WIN_OPEN_COMMAND;//"cmd.exe /c start \"\" \""+url+"\"";
+ }
+ } else if (Utility.isMac()) {
+ command = StaticStrings.MAC_OPEN_COMMAND; // "open %1"
+ } else {
+ // we try to look for a browser
+ String [] browsers = new String [] {"mozilla", "netscape"};
+ for (int i=0; i does not work if there are spaces in the
+ // file path %1, when running the Process with a command array.
+
+ // Need <"start \"window\" \"%1\""> to be an element in the command array:
+ String[] tmp = commands;
+ int index = 0;
+
+ for(int i = 0; i < commands.length; i++) {
+ if(commands[i].indexOf("start") != -1) {
+ index = i;
+ }
+ }
+
+ commands = new String[index+1];
+ for(int i = 0; i < index; i++) {
+ commands[i] = tmp[i];
+ }
+
+ commands[index] = tmp[index];
+ for(int i = index+1; i < tmp.length; i++) {
+ commands[index] = commands[index] + " " + tmp[i];
+ }
+
+ }
+
+ for (int i=0; i ");
+ FileNode destination = getDestination();
+ if(destination != null) {
+ text.append(destination.getFile().getAbsolutePath());
+ }
+ else {
+ text.append("Recycle Bin");
+ }
+ }
+ return text.toString();
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/FileManager.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/FileManager.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/FileManager.java (revision 31635)
@@ -0,0 +1,561 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, NZDL Project, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 2005 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.file;
+
+import java.io.File;
+import javax.swing.*;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.collection.CollectionManager;
+import org.greenstone.gatherer.collection.CollectionTree;
+import org.greenstone.gatherer.collection.CollectionTreeNode;
+import org.greenstone.gatherer.gui.ExplodeMetadataDatabasePrompt;
+import org.greenstone.gatherer.gui.ReplaceSrcDocWithHtmlPrompt;
+import org.greenstone.gatherer.gui.GProgressBar;
+import org.greenstone.gatherer.gui.NewFolderOrFilePrompt;
+import org.greenstone.gatherer.gui.RenamePrompt;
+import org.greenstone.gatherer.gui.tree.DragTree;
+import org.greenstone.gatherer.remote.RemoteGreenstoneServer;
+import org.greenstone.gatherer.util.DragComponent;
+import org.greenstone.gatherer.util.Utility;
+
+/** Manages the moving of files within a separate thread.
+ * @author John Thompson, NZDL Project, University of Waikato
+ */
+public class FileManager
+{
+ /** Not only the queue of files to be moved, but also the object that moves them. */
+ static private FileQueue file_queue = null;
+
+ public static final int COPY = 0;
+ public static final int MOVE = 1;
+
+ public static final int FILE_TYPE = 0;
+ public static final int FOLDER_TYPE = 1;
+ protected static File startup_directory = null;
+
+
+ /** Constructor.
+ * @see org.greenstone.gatherer.file.FileQueue
+ */
+ public FileManager()
+ {
+ file_queue = new FileQueue();
+ file_queue.start();
+ }
+
+
+ /** Determine what action should be carried out by the file queue, and add all of the necessary file jobs. */
+ public void action(DragComponent source, FileNode[] source_nodes, DragComponent target, FileNode target_node)
+ {
+ // Check there is something to do
+ if (source_nodes == null || source_nodes.length == 0) {
+ return;
+ }
+
+ // We need a unique ID for each file task
+ long id = System.currentTimeMillis();
+
+ // If source and target are the same we're moving
+ if (source == target) {
+ // Start a new move FileTask and we're done
+ (new FileTask(id, source, source_nodes, target, target_node, FileJob.MOVE)).start();
+ return;
+ }
+
+ // If target isn't the RecycleBin, we're copying
+ if (!(target instanceof RecycleBin)) {
+ // Start a new copy FileTask and we're done
+ (new FileTask(id, source, source_nodes, target, target_node, FileJob.COPY)).start();
+ return;
+ }
+
+ // We're deleting... but first make sure source isn't read-only
+ boolean read_only_source = false;
+
+ // The workspace tree is read-only...
+ if (source.toString().equals("Workspace")) {
+ read_only_source = true;
+
+ // ...except for files from the "Downloaded Files" folder
+ String downloaded_files_folder_path = Gatherer.getGLIUserCacheDirectoryPath();
+ for (int i = 0; i < source_nodes.length; i++) {
+ // Is this the "Downloaded Files" folder?
+ if (source_nodes[i].getFile().getAbsolutePath().startsWith(downloaded_files_folder_path)) {
+ read_only_source = false;
+ }
+ }
+ }
+
+ // The source is read-only, so tell the user and abort
+ if (read_only_source) {
+ JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("FileActions.Read_Only"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ // Start a new delete FileTask and we're done
+ (new FileTask(id, source, source_nodes, target, target_node, FileJob.DELETE)).start();
+ }
+
+ /** For moving and copying of folders. */
+ public void action(File sourceFolder, File targetFolder, int operation) {
+ (new SimpleFileTask(sourceFolder, targetFolder, operation)).start();
+ }
+
+
+ /** Retrieves the file queue object. */
+ public FileQueue getQueue()
+ {
+ return file_queue;
+ }
+
+ /** Performs the simple file task of moving or copying folders. */
+ private class SimpleFileTask
+ extends Thread
+ {
+ private File sourceFolder;
+ private File targetFolder;
+ int operation; // MOVE or COPY
+
+ public SimpleFileTask(File sourceFolder, File targetFolder, int operation)
+ {
+ this.sourceFolder = sourceFolder;
+ this.targetFolder = targetFolder;
+ this.operation = operation;
+ }
+
+
+ public void run()
+ {
+ // check if we're moving or overwriting the current collection
+ String currentColPath = Gatherer.getCollectDirectoryPath()+CollectionManager.getLoadedCollectionName();
+ if(currentColPath.equals(sourceFolder.getAbsolutePath())
+ || currentColPath.equals(targetFolder.getAbsolutePath())) {
+ Gatherer.g_man.saveThenCloseCurrentCollection();
+ }
+
+ // if moving, try a simple move operation (if it works, it
+ // shouldn't take long at all and doesn't need a progress bar)
+ if(operation == MOVE && sourceFolder.renameTo(targetFolder)) {
+ //System.err.println("**** A simple renameTo() worked.");
+ WorkspaceTreeModel.refreshGreenstoneCollectionsNode();
+ return;
+ }
+
+ // Reset the progress bar and set it to indeterminate while calculating its size
+ GProgressBar progress_bar = file_queue.getProgressBar();
+ progress_bar.reset();
+ progress_bar.setIndeterminate(true);
+
+ String status = "FileActions.Moving";
+ if(operation == COPY) {
+ status = "FileActions.Copying";
+ }
+ progress_bar.setString(Dictionary.get(status));
+ file_queue.getFileStatus().setText(Dictionary.get(status,
+ file_queue.formatPath(status,
+ sourceFolder.getAbsolutePath(),
+ file_queue.getFileStatus().getSize().width)));
+
+ // do the move or copy operation
+ try {
+ //System.err.println("**** Copying " + sourceFolder + " to: " + targetFolder);
+ file_queue.copyDirectoryContents(sourceFolder, targetFolder);
+ } catch(Exception e) {
+ JOptionPane.showMessageDialog(Gatherer.g_man, e.getMessage(),
+ "Can't perform file operation", JOptionPane.ERROR_MESSAGE);
+
+ progress_bar.setIndeterminate(false);
+ progress_bar.clear();
+ return;
+ }
+
+ // if moving, delete the original source folder and
+ // update the docs in GS collections node in the workspace tree
+
+ if(operation == MOVE) {
+ Utility.delete(sourceFolder);
+ WorkspaceTreeModel.refreshGreenstoneCollectionsNode();
+ }
+
+
+ progress_bar.setIndeterminate(false);
+ progress_bar.clear();
+ file_queue.getFileStatus().setText(Dictionary.get("FileActions.No_Activity"));
+ progress_bar.setString(Dictionary.get("FileActions.No_Activity"));
+ }
+ }
+
+ private class FileTask
+ extends Thread
+ {
+ private long id;
+ private DragComponent source;
+ private FileNode[] source_nodes;
+ private DragComponent target;
+ private FileNode target_node;
+ private byte type;
+
+
+ public FileTask(long id, DragComponent source, FileNode[] source_nodes, DragComponent target, FileNode target_node, byte type)
+ {
+ this.id = id;
+ this.source = source;
+ this.source_nodes = source_nodes;
+ this.target = target;
+ this.target_node = target_node;
+ this.type = type;
+ }
+
+
+ public void run()
+ {
+ // Reset the progress bar and set it to indeterminate while calculating its size
+ GProgressBar progress_bar = file_queue.getProgressBar();
+ progress_bar.reset();
+ progress_bar.setIndeterminate(true);
+
+ // Calculate the progress bar size
+ boolean cancelled = file_queue.calculateSize(source_nodes);
+ if (!cancelled) {
+ file_queue.addJob(id, source, source_nodes, target, target_node, type);
+ if (Gatherer.isGsdlRemote) {
+ String collection_name = CollectionManager.getLoadedCollectionName();
+
+ // Perform the appropriate action based on the job type (RemoteGreenstoneServer will queue)
+ if (type == FileJob.COPY) {
+ // Copies: upload all the files at once in one zip file
+ File[] source_files = new File[source_nodes.length];
+ for (int i = 0; i < source_nodes.length; i++) {
+ source_files[i] = source_nodes[i].getFile();
+ }
+ Gatherer.remoteGreenstoneServer.uploadFilesIntoCollection(collection_name, source_files, target_node.getFile());
+ }
+ else if (type == FileJob.DELETE) {
+ // Deletes: delete each top-level file/directory one at a time
+ for (int i = 0; i < source_nodes.length; i++) {
+ Gatherer.remoteGreenstoneServer.deleteCollectionFile(collection_name, source_nodes[i].getFile());
+ }
+ }
+ else if (type == FileJob.MOVE) {
+ // Moves: move each top-level file/directory one at a time
+ for (int i = 0; i < source_nodes.length; i++) {
+ Gatherer.remoteGreenstoneServer.moveCollectionFile(
+ collection_name, source_nodes[i].getFile(), target_node.getFile());
+ }
+ }
+ }
+ }
+
+ progress_bar.setIndeterminate(false);
+ progress_bar.clear();
+ }
+ }
+
+ public void explodeMetadataDatabase(File file)
+ {
+ // This must go in a separate thread because we need the progress bar to work (remote Greenstone server)
+ new ExplodeMetadataDatabasePromptTask(file).start();
+ }
+
+ // Works with replace_srcdoc_with_html.pl
+ public void replaceSrcDocWithHtml(File[] files)
+ {
+ // This must go in a separate thread because we need the progress bar to work (remote Greenstone server)
+ new ReplaceSrcDocWithHtmlPromptTask(files).start();
+ }
+
+ private class ExplodeMetadataDatabasePromptTask
+ extends Thread
+ {
+ private File metadata_database_file = null;
+
+ public ExplodeMetadataDatabasePromptTask(File metadata_database_file)
+ {
+ this.metadata_database_file = metadata_database_file;
+ }
+
+ public void run()
+ {
+ ExplodeMetadataDatabasePrompt emp = new ExplodeMetadataDatabasePrompt(metadata_database_file);
+ }
+ }
+
+ // Works with replace_srcdoc_with_html.pl
+ private class ReplaceSrcDocWithHtmlPromptTask
+ extends Thread
+ {
+ private File[] replace_these_srcdoc_files = null;
+
+ public ReplaceSrcDocWithHtmlPromptTask(File[] replace_these_srcdoc_files)
+ {
+ this.replace_these_srcdoc_files = replace_these_srcdoc_files;
+ }
+
+ public void run()
+ {
+ ReplaceSrcDocWithHtmlPrompt prompt = new ReplaceSrcDocWithHtmlPrompt(replace_these_srcdoc_files);
+ }
+ }
+
+
+ public void openFileInExternalApplication(File file)
+ {
+ // This must go in a separate thread because we need the progress bar to work (remote Greenstone server)
+ new OpenFileInExternalApplicationTask(file).start();
+ }
+
+
+ private class OpenFileInExternalApplicationTask
+ extends Thread
+ {
+ private File file = null;
+
+ public OpenFileInExternalApplicationTask(File file)
+ {
+ this.file = file;
+ }
+
+ public void run()
+ {
+ // If we're using a remote Greenstone server, we need to download the file before viewing it...
+ if (Gatherer.isGsdlRemote) {
+ // ... but only if it is inside the collection and we haven't already downloaded it
+ if (file.getAbsolutePath().startsWith(Gatherer.getCollectDirectoryPath()) && file.length() == 0) {
+ if (Gatherer.remoteGreenstoneServer.downloadCollectionFile(
+ CollectionManager.getLoadedCollectionName(), file).equals("")) {
+ // Something has gone wrong downloading the file
+ return;
+ }
+ }
+ }
+
+ // View the file in an external application
+ Gatherer.spawnApplication(file);
+ }
+ }
+
+
+ public void newDummyDoc(DragTree tree, CollectionTreeNode parent_node){
+ newFolderOrDummyDoc(tree, parent_node, FILE_TYPE);
+ }
+
+
+ public void newFolder(DragTree tree, CollectionTreeNode parent_node) {
+ newFolderOrDummyDoc(tree, parent_node, FOLDER_TYPE);
+ }
+
+
+ protected void newFolderOrDummyDoc(DragTree tree, CollectionTreeNode parent_node, int type) {
+ (new NewFolderOrDummyDocumentTask(tree, parent_node, type)).start();
+ }
+
+
+ private class NewFolderOrDummyDocumentTask
+ extends Thread
+ {
+ private DragTree tree = null;
+ private CollectionTreeNode parent_node = null;
+ private int type;
+
+ public NewFolderOrDummyDocumentTask(DragTree tree, CollectionTreeNode parent_node, int type)
+ {
+ this.tree = tree;
+ this.parent_node = parent_node;
+ this.type = type;
+ }
+
+ public void run()
+ {
+ // Ask the user for the directories name.
+ String extension = "";
+ if (type == FILE_TYPE) {
+ extension = ".nul";
+ }
+
+ NewFolderOrFilePrompt new_folder_prompt = new NewFolderOrFilePrompt(parent_node, type, extension);
+ String name = new_folder_prompt.display();
+ new_folder_prompt.dispose();
+ new_folder_prompt = null;
+
+ // And if the name is non-null...
+ if (name != null) {
+ FileSystemModel model = (FileSystemModel) tree.getModel();
+ File folder_file = new File(parent_node.getFile(), name);
+
+ //... check if it already exists.
+ if (folder_file.exists()) {
+ if (type == FILE_TYPE) {
+ JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("FileActions.File_Already_Exists_No_Create", name), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
+ }
+ else {
+ JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("FileActions.Folder_Already_Exists", name), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ // Otherwise create it.
+ else {
+ try {
+ if (type == FILE_TYPE) {
+ folder_file.createNewFile();
+ if (Gatherer.isGsdlRemote) {
+ Gatherer.remoteGreenstoneServer.uploadCollectionFile(
+ CollectionManager.getLoadedCollectionName(), folder_file);
+ }
+ }
+ else {
+ folder_file.mkdirs();
+ if (Gatherer.isGsdlRemote) {
+ Gatherer.remoteGreenstoneServer.newCollectionDirectory(
+ CollectionManager.getLoadedCollectionName(), folder_file);
+ }
+ }
+
+ // Update the parent node to show the new folder
+ parent_node.refresh();
+
+ // Refresh workspace tree (collection tree is done automatically)
+ Gatherer.g_man.refreshWorkspaceTree(DragTree.COLLECTION_CONTENTS_CHANGED);
+ }
+ catch (Exception exception) {
+ if (type == FILE_TYPE) {
+ JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("FileActions.File_Create_Error", name), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
+ }
+ else {
+ JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("FileActions.Folder_Create_Error", name), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ }
+
+ folder_file = null;
+ model = null;
+ }
+ name = null;
+ }
+ }
+
+
+ public void renameCollectionFile(CollectionTree collection_tree, CollectionTreeNode collection_tree_node)
+ {
+ // This must go in a separate thread because we need the progress bar to work (remote Greenstone server)
+ new RenameTask(collection_tree, collection_tree_node).start();
+ }
+
+
+ private class RenameTask
+ extends Thread
+ {
+ private CollectionTree collection_tree = null;
+ private CollectionTreeNode collection_tree_node = null;
+
+ public RenameTask(CollectionTree collection_tree, CollectionTreeNode collection_tree_node)
+ {
+ this.collection_tree = collection_tree;
+ this.collection_tree_node = collection_tree_node;
+ }
+
+ public void run()
+ {
+ RenamePrompt rename_prompt = new RenamePrompt(collection_tree_node);
+ String new_collection_file_name = rename_prompt.display();
+ rename_prompt.dispose();
+ rename_prompt = null;
+
+ if (new_collection_file_name != null) {
+ File collection_file = collection_tree_node.getFile();
+ File new_collection_file = new File(collection_file.getParentFile(), new_collection_file_name);
+ CollectionTreeNode new_collection_tree_node = new CollectionTreeNode(new_collection_file);
+ file_queue.addJob(System.currentTimeMillis(), collection_tree, new FileNode[] { collection_tree_node }, collection_tree, new_collection_tree_node, FileJob.RENAME);
+ if (Gatherer.isGsdlRemote) {
+ Gatherer.remoteGreenstoneServer.moveCollectionFile(
+ CollectionManager.getLoadedCollectionName(), collection_file, new_collection_file);
+ }
+ }
+ }
+ }
+
+ public void replaceCollectionFile(CollectionTree collection_tree, CollectionTreeNode collection_tree_node)
+ {
+ // This must go in a separate thread because we need the progress bar to work (remote Greenstone server)
+ new ReplaceTask(collection_tree, collection_tree_node).start();
+ }
+
+
+ private class ReplaceTask
+ extends Thread
+ {
+ private CollectionTree collection_tree = null;
+ private CollectionTreeNode collection_tree_node = null;
+
+ public ReplaceTask(CollectionTree collection_tree, CollectionTreeNode collection_tree_node)
+ {
+ this.collection_tree = collection_tree;
+ this.collection_tree_node = collection_tree_node;
+ }
+
+ public void run()
+ {
+ JFileChooser file_chooser = new JFileChooser(startup_directory);
+ file_chooser.setDialogTitle(Dictionary.get("ReplacePrompt.Title"));
+ File new_file = null;
+ int return_val = file_chooser.showOpenDialog(null);
+ if(return_val == JFileChooser.APPROVE_OPTION) {
+ new_file = file_chooser.getSelectedFile();
+ }
+
+ if (new_file == null) {
+ return;
+ }
+
+ // save the search path for next time
+ startup_directory = new_file.getParentFile();
+ // make up a node for the file to bring in
+ WorkspaceTreeNode source_node = new WorkspaceTreeNode(new_file);
+
+ File target_directory = collection_tree_node.getFile().getParentFile();
+ CollectionTreeNode new_collection_tree_node = new CollectionTreeNode(new File(target_directory, new_file.getName()));
+ // copy the new file in - but don't bring metadata
+ file_queue.addJob(System.currentTimeMillis(), Gatherer.g_man.gather_pane.workspace_tree, new FileNode[] { source_node }, collection_tree, (FileNode)collection_tree_node.getParent(), FileJob.COPY_FILE_ONLY);
+ if (Gatherer.isGsdlRemote) {
+ String collection_name = CollectionManager.getLoadedCollectionName();
+ Gatherer.remoteGreenstoneServer.deleteCollectionFile(collection_name, collection_tree_node.getFile());
+ Gatherer.remoteGreenstoneServer.uploadFilesIntoCollection(collection_name, new File[] { new_file }, target_directory);
+ }
+ // do a replace of old file with new file
+ file_queue.addJob(System.currentTimeMillis(), collection_tree, new FileNode[] { collection_tree_node }, collection_tree, new_collection_tree_node, FileJob.REPLACE);
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/FileNode.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/FileNode.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/FileNode.java (revision 31635)
@@ -0,0 +1,393 @@
+package org.greenstone.gatherer.file;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import javax.swing.*;
+import javax.swing.filechooser.*;
+import javax.swing.tree.*;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.metadata.FilenameEncoding;
+import org.greenstone.gatherer.util.ArrayTools;
+
+
+public abstract class FileNode
+ implements MutableTreeNode
+{
+ protected boolean allows_children = true;
+ protected ArrayList child_nodes = null;
+ protected ArrayList child_nodes_unfiltered = null;
+ protected File file = null;
+ protected FileSystemModel model = null;
+ protected MutableTreeNode parent = null;
+
+ protected String urlEncodedFileName = "";
+ protected String urlEncodedFilePath = "";
+ protected String filenameEncoding = "";
+ /** The string that is displayed as the filename. Attempts to be in the correct encoding. */
+ protected String displayFileName = null;
+
+
+ public FileNode(File file)
+ {
+ this.file = file;
+
+ if (file != null) {
+ // Files cannot have children
+ if(file.isFile()) {
+ // Cache this result to prevent unceasing missing disk messages being thrown if the
+ // removable media was, um, removed after directory mapped
+ this.allows_children = false;
+ }
+ filenameEncoding = "";
+ urlEncodedFilePath = FilenameEncoding.calcURLEncodedFilePath(file);
+ urlEncodedFileName = FilenameEncoding.calcURLEncodedFileName(urlEncodedFilePath);
+
+ // work out the display string (extra special processing for CollectionTreeNodes)
+ displayFileName = calcDisplayString();
+ }
+ }
+
+ public String getURLEncodedFileName() { return urlEncodedFileName; }
+
+ public String getURLEncodedFilePath() { return urlEncodedFilePath; }
+
+ public String getFilenameEncoding() { return filenameEncoding; }
+
+
+ /** This method returns a string representation of the filenodes in the tree,
+ * that can then be displayed in the tree. Overridden in subclass CollectionTreeNode.
+ * Turn FilenameEncoding.DEBUGGING on to see URLEncoded filenames.
+ */
+ protected String calcDisplayString() {
+ if(FilenameEncoding.DEBUGGING) {
+ return getURLEncodedFileName();
+ } else {
+ return file.getName();
+ }
+ }
+
+ /** Returns the children of the node as an Enumeration. */
+ public Enumeration children()
+ {
+ return new FileEnumeration();
+ }
+
+
+ /** Returns true if the receiver allows children. */
+ public boolean getAllowsChildren()
+ {
+ return allows_children;
+ }
+
+
+ /** Returns the child TreeNode at index childIndex. */
+ public TreeNode getChildAt(int index)
+ {
+ return (TreeNode) child_nodes.get(index);
+ }
+
+
+ /** Returns the number of children TreeNodes the receiver contains. */
+ public int getChildCount()
+ {
+ map();
+
+ // Use the number of (filtered) child nodes
+ if (child_nodes != null) {
+ return child_nodes.size();
+ }
+
+ return 0;
+ }
+
+
+ /** Returns the index of node in the receivers children. */
+ public int getIndex(TreeNode node)
+ {
+ if (child_nodes != null) {
+ return child_nodes.indexOf(node);
+ }
+
+ return -1;
+ }
+
+
+ /** Returns the parent TreeNode of the receiver. */
+ public TreeNode getParent()
+ {
+ return parent;
+ }
+
+
+ /** Adds child to the receiver at index. */
+ public void insert(MutableTreeNode child, int index)
+ {
+ DebugStream.println("Insert " + child + " in " + this + " at index " + index + " [Model: " + model + "]");
+ if (child == null) {
+ return;
+ }
+
+ try {
+ FileNode child_node = (FileNode) child;
+ child_nodes.add(index, child_node);
+ child_node.model = model;
+ child_node.parent = this;
+ }
+ catch (Exception exception) {
+ DebugStream.printStackTrace(exception);
+ }
+ }
+
+
+ /** Returns true if the receiver is a leaf. */
+ public boolean isLeaf()
+ {
+ return (allows_children == false);
+ }
+
+
+ /** Removes the child at index from the receiver. */
+ public void remove(int index)
+ {
+ if (index >= 0 && index < child_nodes.size()) {
+ child_nodes.remove(index);
+ }
+ }
+
+
+ /** Removes node from the receiver. */
+ public void remove(MutableTreeNode node)
+ {
+ remove(getIndex(node));
+ }
+
+
+ /** Removes the receiver from its parent. */
+ public void removeFromParent()
+ {
+ parent.remove(this);
+ parent = null;
+ }
+
+
+ /** Resets the user object of the receiver to object. */
+ public void setUserObject(Object object) {
+ try {
+ file = (File) object;
+ }
+ catch (Exception exception) {
+ DebugStream.printStackTrace(exception);
+ }
+ }
+
+
+ // -------------------------------------------------------------------------------
+
+
+ public void add(MutableTreeNode child)
+ {
+ insert(child, child_nodes.size());
+ }
+
+
+ protected abstract FileNode addChildNode(File file);
+
+
+ /** Compare two FileNodes for equality. */
+ public boolean equals(FileNode node)
+ {
+ if (node == null) {
+ // Definitely not a match
+ return false;
+ }
+
+ if (file != null) {
+ return file.equals(node.getFile());
+ }
+ else {
+ return toString().equals(node.toString());
+ }
+ }
+
+
+ /** Retrieve the file node at the given index, regardless of filters set. */
+ public FileNode getChildAtUnfiltered(int index)
+ {
+ if (index >= 0 && index < size()) {
+ return (FileNode) child_nodes_unfiltered.get(index);
+ }
+ return null;
+ }
+
+
+ public File getFile() {
+ return file;
+ }
+
+
+ /** Retrieves the tree path from the root node to this node. */
+ public TreeNode[] getPath() {
+ int count = 0;
+ TreeNode current = this;
+ while(current != null) {
+ count++;
+ current = current.getParent();
+ }
+ TreeNode[] path = new TreeNode[count];
+ current = this;
+ while(current != null) {
+ path[count - 1] = current;
+ count--;
+ current = current.getParent();
+ }
+ return path;
+ }
+
+
+ public boolean isFileSystemRoot() {
+ if (file != null) {
+ return FileSystemView.getFileSystemView().isFileSystemRoot(file);
+ }
+ else {
+ return false;
+ }
+ }
+
+
+ /** Overridden if necessary by subclasses. */
+ public boolean isInLoadedCollection()
+ {
+ return false;
+ }
+
+
+ /** Overridden if necessary by subclasses. */
+ public boolean isReadOnly()
+ {
+ return false;
+ }
+
+
+ public void map()
+ {
+ // If this node has already been mapped, don't bother doing it again
+ if (child_nodes != null) {
+ return;
+ }
+ child_nodes = new ArrayList();
+
+ // General case, only map if children are allowed
+ if (file != null && getAllowsChildren()) {
+ File[] files = file.listFiles();
+ if (files != null && files.length > 0) {
+ // Sort the child files
+ ArrayTools.sort(files);
+
+ // Now add them to child_nodes_unfiltered
+ child_nodes_unfiltered = new ArrayList();
+ for (int i = 0; i < files.length; i++) {
+ FileNode child_node = this.addChildNode(files[i]);
+ child_nodes_unfiltered.add(child_node);
+ }
+
+ // Apply the filters set in the model
+ FileFilter[] filters = model.getFilters();
+ for (int i = 0; filters != null && i < filters.length; i++) {
+ files = ArrayTools.filter(files, filters[i].filter, filters[i].exclude);
+ }
+
+ // Add the files left after filtering to child_nodes
+ for (int i = 0, j = 0; (i < child_nodes_unfiltered.size() && j < files.length); i++) {
+ // Use the FileNode object in child_nodes_unfiltered rather than creating another
+ FileNode file_node = (FileNode) child_nodes_unfiltered.get(i);
+ if (file_node.getFile().equals(files[j])) {
+ child_nodes.add(file_node);
+ j++;
+ }
+ }
+
+ // in case any filename encodings had gone stale,
+ // (recalculate these and) refresh the display name
+ refreshDescendantEncodings();
+
+ }
+
+ model.nodeStructureChanged(this);
+ }
+ }
+
+
+ public void refresh()
+ {
+ unmap();
+ map();
+ }
+
+ // overridden in subclass CollectionTreeNode to reset and reencode display strings
+ public void resetDescendantEncodings() {}
+ public void refreshDescendantEncodings() {}
+
+
+ public void setModel(FileSystemModel model) {
+ this.model = model;
+ }
+
+ public void setParent(MutableTreeNode parent) {
+ this.parent = parent;
+ }
+
+
+ /** Return the total number of child files for the file this node represents, irrespective of filters set. */
+ public int size() {
+ if (child_nodes_unfiltered != null) {
+ return child_nodes_unfiltered.size();
+ }
+ return 0;
+ }
+
+
+ public String toString()
+ {
+ if (isFileSystemRoot()) {
+ return file.getAbsolutePath();
+ }
+ else {
+ if(displayFileName == null) {
+ displayFileName = calcDisplayString();
+ }
+ return displayFileName; //return file.getName();
+ }
+ }
+
+
+ /** Unmap this node's children. */
+ public void unmap()
+ {
+ DebugStream.println("Unmapping " + this + "...");
+ child_nodes_unfiltered = null;
+ child_nodes = null;
+ }
+
+
+ private class FileEnumeration
+ implements Enumeration
+ {
+ private int index = 0;
+
+ /** Tests if this enumeration contains more elements. */
+ public boolean hasMoreElements() {
+ return (index < child_nodes.size());
+ }
+
+ /** Returns the next element of this enumeration if this enumeration object has at least one more element to provide. */
+ public Object nextElement() {
+ Object result = null;
+ if (index < child_nodes.size()) {
+ result = child_nodes.get(index);
+ index++;
+ }
+ return result;
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/FileOpenActionListener.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/FileOpenActionListener.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/FileOpenActionListener.java (revision 31635)
@@ -0,0 +1,101 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.file;
+
+import java.awt.event.*;
+import java.io.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.tree.*;
+import org.greenstone.gatherer.Gatherer;
+
+/** This class listens for mouse clicks and responds to double-clicks (spawn a new application to view the selected file) and right mouse button clicks (popup menu).
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.3b
+ */
+public class FileOpenActionListener
+ extends MouseAdapter
+ implements TreeExpansionListener {
+ /** This flag gets toggled to true if the listener determines that the next mouse clicked event it will recieve is actually caused by the tree expanding or collapsing. */
+ private boolean ignore = false;
+ /** The constructor. */
+ public FileOpenActionListener() {
+ }
+ /** Any subclass of MouseAdapter can override this method to respond to mouse click events. In this case we want to start an external application if someone double clicks on an appropriate file record.
+ * @param event A MouseEvent containing further information about the mouse click performed.
+ * @see org.greenstone.gatherer.Gatherer
+ * @see org.greenstone.gatherer.file.FileNode
+ * @see org.greenstone.gatherer.gui.tree.DragTree
+ */
+ public void mouseClicked(MouseEvent event) {
+ ///ystem.err.println("Mouse clicked");
+ if(!ignore) {
+ if(event.getClickCount() >= 2) {
+ // Find the file we're clicking on.
+ JTree tree = (JTree)event.getSource();
+ TreePath path = tree.getClosestPathForLocation(event.getX(), event.getY());
+ if(path != null) {
+ FileNode record = (FileNode)path.getLastPathComponent();
+ ///ystem.err.println("Double clicked on " + record);
+ File file = record.getFile();
+ if (file != null && file.isFile()) {
+ ///ystem.err.println("Running " + file);
+ Gatherer.f_man.openFileInExternalApplication(file);
+ }
+ }
+ }
+ }
+ else {
+ ///ystem.err.println("Caused by tree expansion / collapse. Ignoring.");
+ ignore = false;
+ }
+ }
+ /** Any implementation of TreeExpansionListener must include this method so that we can be informed when a tree node has been collapsed, thus indicating that any mouse click events about to be recieved are most likely related to this event.
+ * @param event A TreeExpansionEvent containing information about the node collapsed.
+ */
+ public void treeCollapsed(TreeExpansionEvent event) {
+ ///ystem.err.println("Tree Collapsed");
+ ignore = true;
+ }
+ /** Any implementation of TreeExpansionListener must include this method so that we can be informed when a tree node has been expanded, thus indicating that any mouse click events about to be recieved are most likely related to this event.
+ * @param event A TreeExpansionEvent containing information about the node expanded.
+ */
+ public void treeExpanded(TreeExpansionEvent event) {
+ ///ystem.err.println("Tree Expanded");
+ ignore = true;
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/FileQueue.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/FileQueue.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/FileQueue.java (revision 31635)
@@ -0,0 +1,846 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.file;
+
+import java.io.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.tree.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.collection.CollectionTreeNode;
+import org.greenstone.gatherer.gui.GProgressBar;
+import org.greenstone.gatherer.gui.tree.DragTree;
+import org.greenstone.gatherer.metadata.MetadataValue;
+import org.greenstone.gatherer.metadata.MetadataXMLFileManager;
+import org.greenstone.gatherer.util.DragComponent;
+import org.greenstone.gatherer.util.StaticStrings;
+import org.greenstone.gatherer.util.SynchronizedTreeModelTools;
+import org.greenstone.gatherer.util.Utility;
+
+/** A threaded object which processes a queue of file actions such as copying and movement. It also handles updating the various trees involved so they are an accurate representation of the file system they are meant to match.
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.3
+ */
+public class FileQueue
+ extends Thread
+{
+ /** The size of the io buffer, in bytes. */
+ static final private int BUFFER_SIZE = 1024;
+
+ /** When someone requests the movement queue to be dumped this cancel flag is set to true. */
+ private boolean cancel_action = false;
+ /** The button which controls the stopping of the file queue. */
+ private JButton stop_button = null;
+ /** true if the user has selected yes to all from a file 'clash' dialog. */
+ private boolean yes_to_all = false;
+ /** A label explaining the current moving files status. */
+ private JLabel file_status = null;
+ /** A list containing a queue of waiting movement jobs. */
+ private ArrayList queue = null;
+ /** A progress bar which shows how many bytes, out of the total size of bytes, has been moved. */
+ private GProgressBar progress = null;
+
+
+ /** Constructor.
+ */
+ public FileQueue() {
+ DebugStream.println("FileQueue started.");
+ this.queue = new ArrayList();
+ file_status = new JLabel(Dictionary.get("FileActions.No_Activity"));
+ progress = new GProgressBar();
+ progress.setBackground(Configuration.getColor("coloring.collection_tree_background", false));
+ progress.setForeground(Configuration.getColor("coloring.collection_tree_foreground", false));
+ progress.setString(Dictionary.get("FileActions.No_Activity"));
+ progress.setStringPainted(true);
+ }
+
+
+ /** Add a new job to the queue, specifiying as many arguments as is necessary to complete this type of job (ie delete needs no target information).
+ * @param id A long id unique to all jobs created by a single action.
+ * @param source The DragComponent source of this file, most likely a DragTree.
+ * @param child The FileNode you wish to mode.
+ * @param target The DragComponent to move the file to, again most likely a DragTree.
+ * @param parent The files new FileNode parent within the target.
+ * @param type The type of this movement as an int, either COPY or DELETE.
+ */
+ public void addJob(long id, DragComponent source, FileNode[] children, DragComponent target, FileNode parent, byte type)
+ {
+ // Queue the sub-job(s) (this may fail if we are asked to delete a read only file)
+ for (int i = 0; i < children.length; i++) {
+ addJob(id, source, children[i], target, parent, type, -1);
+ }
+ }
+
+ synchronized private void addJob(long id, DragComponent source, FileNode child, DragComponent target, FileNode parent, byte type, int position) {
+ FileJob job = new FileJob(id, source, child, target, parent, type);
+ DebugStream.println("Adding job: " + job);
+ if(position != -1 && position <= queue.size() + 1) {
+ queue.add(position, job);
+ }
+ else {
+ queue.add(job);
+ }
+ notify();
+ }
+
+ /** Calculates the total deep file size of the selected file nodes.
+ * @param files a FileNode[] of selected files
+ * @return true if a cancel was signalled, false otherwise
+ * @see org.greenstone.gatherer.file.FileManager.Task#run()
+ */
+ public boolean calculateSize(FileNode[] files)
+ {
+ file_status.setText(Dictionary.get("FileActions.Calculating_Size"));
+ progress.setString(Dictionary.get("FileActions.Calculating_Size"));
+
+ // Calculate the total file size of all the selected file nodes
+ Vector remaining = new Vector();
+ for (int i = 0; !cancel_action && i < files.length; i++) {
+ remaining.add(files[i]);
+ }
+ while (!cancel_action && remaining.size() > 0) {
+ FileNode node = (FileNode) remaining.remove(0);
+ if (node.isLeaf()) {
+ progress.addMaximum(node.getFile().length());
+ }
+ else {
+ for (int i = 0; !cancel_action && i < node.getChildCount(); i++) {
+ remaining.add(node.getChildAt(i));
+ }
+ }
+ }
+
+ // Now we return if calculation was cancelled so that the FileManagers Task can skip the addJob phase correctly.
+ if (cancel_action) {
+ cancel_action = false; // reset
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ /** This method is called to cancel the job queue at the next available moment. */
+ public void cancelAction() {
+ cancel_action = true;
+ clearJobs();
+ }
+
+
+ /** Format the given filename path string so that it is no longer than the given width. If it is wider replace starting directories with ...
+ * @param key The key String used to retrieve a phrase from the dictionary for this item.
+ * @param raw The raw filename path String .
+ * @param width The maximum width as an int .
+ * @return A path String no longer than width.
+ */
+ String formatPath(String key, String raw, int width) // package access
+ {
+ JLabel label = new JLabel(Dictionary.get(key, raw));
+ int position = -1;
+ while(label.getPreferredSize().width > width && (position = raw.indexOf(File.separator)) != -1) {
+ raw = "..." + raw.substring(position + 1);
+ label.setText(Dictionary.get(key, raw));
+ }
+ if(raw.indexOf(File.separator) == -1 && raw.startsWith("...")) {
+ raw = raw.substring(3);
+ }
+ return raw;
+ }
+
+
+ /** Access to the file state label. */
+ public JLabel getFileStatus() {
+ return file_status;
+ }
+
+ /** Access to the progress bar. */
+ public GProgressBar getProgressBar() {
+ return progress;
+ }
+
+
+ synchronized private void addFileJob(long id, DragComponent source, FileNode child, DragComponent target, FileNode parent, byte type)
+ {
+ queue.add(new FileJob(id, source, child, target, parent, type));
+ notify();
+ }
+
+
+ private void doEmptyDirectoryDelete(FileJob file_job)
+ {
+ FileNode source_node = file_job.getOrigin();
+ File source_directory = source_node.getFile();
+
+ // If the directory isn't empty then this will fail
+ if (source_directory.delete() == false) {
+ // The source directory couldn't be deleted, so give the user the option of continuing or cancelling
+ if (showErrorDialog(Dictionary.get("FileActions.Could_Not_Delete", source_directory.getAbsolutePath())) == JOptionPane.CANCEL_OPTION) {
+ clearJobs(); // Aborting action
+ }
+ return;
+ }
+
+ // Remove the node from the model
+ SynchronizedTreeModelTools.removeNodeFromParent(file_job.source.getTreeModel(), source_node);
+ }
+
+
+ private void doDirectoryDelete(FileJob file_job)
+ {
+ FileNode source_node = file_job.getOrigin();
+ File source_directory = source_node.getFile();
+
+ // The last thing we will do is delete this directory (which should be empty by then)
+ addFileJob(file_job.ID(), file_job.source, source_node, null, null, FileJob.DELETE_EMPTY_DIRECTORY);
+
+ // Add a new Delete job for each child of this directory (except metadata.xml files)
+ source_node.refresh();
+ for (int i = 0; i < source_node.size(); i++) {
+ FileNode child_file_node = (FileNode) source_node.getChildAtUnfiltered(i);
+ if (!child_file_node.getFile().getName().equals(StaticStrings.METADATA_XML)) {
+ addFileJob(file_job.ID(), file_job.source, child_file_node, null, null, FileJob.DELETE);
+ }
+ }
+
+ // Treat metadata.xml files specially: delete them first
+ for (int i = 0; i < source_node.size(); i++) {
+ FileNode child_file_node = (FileNode) source_node.getChildAtUnfiltered(i);
+ if (child_file_node.getFile().getName().equals(StaticStrings.METADATA_XML)) {
+ addFileJob(file_job.ID(), file_job.source, child_file_node, null, null, FileJob.DELETE);
+ break;
+ }
+ }
+ }
+
+
+ private void doDirectoryCopy(FileJob file_job)
+ {
+ FileNode source_node = file_job.getOrigin();
+ FileNode target_node = file_job.getDestination();
+
+ File source_directory = source_node.getFile();
+ File target_directory = new File(target_node.getFile(), source_directory.getName());
+
+ // Check that the source directory doesn't contain the target directory (will create a cyclic loop)
+ if (target_directory.getAbsolutePath().startsWith(source_directory.getAbsolutePath())) {
+ if (showErrorDialog(Dictionary.get("FileActions.Cyclic_Path", source_directory.getName())) == JOptionPane.CANCEL_OPTION) {
+ clearJobs(); // Aborting action
+ }
+ return;
+ }
+
+ // The target directory shouldn't already exist
+ if (target_directory.exists()) {
+ if (showErrorDialog(Dictionary.get("FileActions.Folder_Already_Exists", target_directory.getAbsolutePath())) == JOptionPane.CANCEL_OPTION) {
+ clearJobs(); // Aborting action
+ }
+ return;
+ }
+ target_directory.mkdirs();
+
+ // Create a node for the new directory in the collection tree
+ FileSystemModel target_model = (FileSystemModel) file_job.target.getTreeModel();
+ CollectionTreeNode new_target_node = new CollectionTreeNode(target_directory);
+ SynchronizedTreeModelTools.insertNodeInto(target_model, target_node, new_target_node);
+ new_target_node.setParent(target_node);
+
+ // Copy the non-folder level metadata assigned to the original directory to the new directory
+ ArrayList assigned_metadata = MetadataXMLFileManager.getMetadataAssignedDirectlyToExternalFile(source_directory);
+ MetadataXMLFileManager.addMetadata((CollectionTreeNode) new_target_node, assigned_metadata);
+
+ // Add a new Copy job for each child of this directory (except metadata.xml files)
+ source_node.refresh();
+ for (int i = 0; i < source_node.size(); i++) {
+ FileNode child_file_node = (FileNode) source_node.getChildAtUnfiltered(i);
+ if (!child_file_node.getFile().getName().equals(StaticStrings.METADATA_XML)) {
+ addFileJob(file_job.ID(), file_job.source, child_file_node, file_job.target, new_target_node, FileJob.COPY);
+ }
+ }
+ }
+
+
+ private void doDirectoryMove(FileJob file_job)
+ {
+ FileNode source_node = file_job.getOrigin();
+ FileNode target_node = file_job.getDestination();
+
+ File source_directory = source_node.getFile();
+ File target_directory = new File(target_node.getFile(), source_directory.getName());
+ if (file_job.type == FileJob.RENAME) {
+ // This is the only difference between moves and renames
+ target_directory = target_node.getFile();
+ target_node = (FileNode) source_node.getParent();
+ }
+
+ // Check the target directory isn't the source directory
+ if (target_directory.equals(source_directory)) {
+ DebugStream.println("Target directory is the source directory!");
+ return;
+ }
+
+ // The target directory shouldn't already exist
+ if (target_directory.exists()) {
+ if (showErrorDialog(Dictionary.get("FileActions.Folder_Already_Exists", target_directory.getAbsolutePath())) == JOptionPane.CANCEL_OPTION) {
+ clearJobs(); // Aborting action
+ }
+ return;
+ }
+ target_directory.mkdirs();
+
+ // Create a node for the new directory in the collection tree
+ FileSystemModel target_model = (FileSystemModel) file_job.target.getTreeModel();
+ CollectionTreeNode new_target_node = new CollectionTreeNode(target_directory);
+ SynchronizedTreeModelTools.insertNodeInto(target_model, target_node, new_target_node);
+ new_target_node.setParent(target_node);
+
+ // Move the folder level metadata assigned to the original directory to the new directory
+ ArrayList assigned_metadata = MetadataXMLFileManager.getMetadataAssignedDirectlyToFile(source_directory);
+ MetadataXMLFileManager.removeMetadata((CollectionTreeNode) source_node, assigned_metadata);
+ MetadataXMLFileManager.addMetadata((CollectionTreeNode) new_target_node, assigned_metadata);
+
+ // The last thing we will do is delete this directory
+ addFileJob(file_job.ID(), file_job.source, source_node, null, null, FileJob.DELETE);
+
+ // Treat metadata.xml files specially: delete them last
+ source_node.refresh();
+ for (int i = 0; i < source_node.size(); i++) {
+ FileNode child_file_node = (FileNode) source_node.getChildAtUnfiltered(i);
+ if (child_file_node.getFile().getName().equals(StaticStrings.METADATA_XML)) {
+ addFileJob(file_job.ID(), file_job.source, child_file_node, null, null, FileJob.DELETE);
+ break;
+ }
+ }
+
+ // Add a new Move job for each child of this directory (except metadata.xml files)
+ for (int i = 0; i < source_node.size(); i++) {
+ FileNode child_file_node = (FileNode) source_node.getChildAtUnfiltered(i);
+ if (!child_file_node.getFile().getName().equals(StaticStrings.METADATA_XML)) {
+ addFileJob(file_job.ID(), file_job.source, child_file_node, file_job.target, new_target_node, FileJob.MOVE);
+ }
+ }
+ }
+
+
+ private void doFileDelete(FileJob file_job)
+ {
+ FileNode source_node = file_job.getOrigin();
+ File source_file = source_node.getFile();
+
+ // Almost all files will be deleted from the collection tree (exception: files in "Downloaded Files")
+ if (source_node instanceof CollectionTreeNode) {
+ // If we're deleting a metadata.xml file we must unload it
+ boolean metadata_xml_file = source_file.getName().equals(StaticStrings.METADATA_XML);
+ if (metadata_xml_file) {
+ MetadataXMLFileManager.unloadMetadataXMLFile(source_file);
+ }
+ // Otherwise remove any metadata assigned directly to the file
+ else {
+ ArrayList assigned_metadata = MetadataXMLFileManager.getMetadataAssignedDirectlyToFile(source_file);
+ MetadataXMLFileManager.removeMetadata((CollectionTreeNode) source_node, assigned_metadata);
+ }
+ }
+
+ // Delete the source file
+ if (!Utility.delete(source_file)) {
+ // The source file couldn't be deleted, so give the user the option of continuing or cancelling
+ if (showErrorDialog(Dictionary.get("FileActions.File_Not_Deleted_Message", source_file.getAbsolutePath())) == JOptionPane.CANCEL_OPTION) {
+ clearJobs(); // Aborting action
+ }
+ return;
+ }
+
+ // Remove the node from the model
+ SynchronizedTreeModelTools.removeNodeFromParent(file_job.source.getTreeModel(), source_node);
+ }
+
+
+ private void doFileCopy(FileJob file_job)
+ {
+ FileNode source_node = file_job.getOrigin();
+ FileNode target_node = file_job.getDestination();
+
+ File source_file = source_node.getFile();
+ File target_file = new File(target_node.getFile(), source_file.getName());
+
+ // The target file shouldn't already exist -- if it does ask the user whether they want to overwrite
+ boolean overwrite_file = false;
+ if (target_file.exists()) {
+ int result = showOverwriteDialog(target_file.getName());
+ if (result == JOptionPane.NO_OPTION) {
+ // Don't overwrite
+ return;
+ }
+ if (result == JOptionPane.CANCEL_OPTION) {
+ clearJobs(); // Aborting action
+ return;
+ }
+
+ overwrite_file = true;
+ }
+
+ // Copy the file
+ try {
+ copyFile(source_file, target_file, true);
+ }
+ catch (FileAlreadyExistsException exception) {
+ // This should not ever happen, since we've called copyFile with overwrite set
+ DebugStream.printStackTrace(exception);
+ return;
+ }
+ catch (FileNotFoundException exception) {
+ DebugStream.printStackTrace(exception);
+ if (showErrorDialog(Dictionary.get("FileActions.File_Not_Found_Message", source_file.getAbsolutePath())) == JOptionPane.CANCEL_OPTION) {
+ clearJobs(); // Aborting action
+ }
+ // Refresh the source tree model
+ FileSystemModel source_model = file_job.source.getTreeModel();
+ source_model.refresh(new TreePath(((FileNode) file_job.getOrigin().getParent()).getPath()));
+ return;
+ }
+ catch (InsufficientSpaceException exception) {
+ DebugStream.printStackTrace(exception);
+ if (showErrorDialog(Dictionary.get("FileActions.Insufficient_Space_Message", exception.getMessage())) == JOptionPane.CANCEL_OPTION) {
+ clearJobs(); // Aborting action
+ }
+ return;
+ }
+ catch (IOException exception) {
+ DebugStream.printStackTrace(exception);
+ if (showErrorDialog(exception.getMessage()) == JOptionPane.CANCEL_OPTION) {
+ clearJobs(); // Aborting action
+ }
+ return;
+ }
+ catch (ReadNotPermittedException exception) {
+ DebugStream.printStackTrace(exception);
+ if (showErrorDialog(Dictionary.get("FileActions.Read_Not_Permitted_Message", source_file.getAbsolutePath())) == JOptionPane.CANCEL_OPTION) {
+ clearJobs(); // Aborting action
+ }
+ return;
+ }
+ catch (UnknownFileErrorException exception) {
+ DebugStream.printStackTrace(exception);
+ if (showErrorDialog(Dictionary.get("FileActions.Unknown_File_Error_Message")) == JOptionPane.CANCEL_OPTION) {
+ clearJobs(); // Aborting action
+ }
+ return;
+ }
+ catch (WriteNotPermittedException exception) {
+ DebugStream.printStackTrace(exception);
+ if (showErrorDialog(Dictionary.get("FileActions.Write_Not_Permitted_Message", target_file.getAbsolutePath())) == JOptionPane.CANCEL_OPTION) {
+ clearJobs(); // Aborting action
+ }
+ return;
+ }
+
+ CollectionTreeNode new_target_node = new CollectionTreeNode(target_file);
+ if (overwrite_file == false) {
+ // Add the new node into the tree
+ FileSystemModel target_model = file_job.target.getTreeModel();
+ SynchronizedTreeModelTools.insertNodeInto(target_model, target_node, new_target_node);
+ }
+ Gatherer.c_man.fireFileAddedToCollection(target_file);
+
+ // Copy the non-folder level metadata assigned to the original file to the new file
+ if (file_job.type == FileJob.COPY) {
+ // do metadata too
+ ArrayList assigned_metadata = MetadataXMLFileManager.getMetadataAssignedDirectlyToExternalFile(source_file);
+ MetadataXMLFileManager.addMetadata((CollectionTreeNode) new_target_node, assigned_metadata);
+ }
+ }
+
+
+ private void doFileMove(FileJob file_job)
+ {
+ FileNode source_node = file_job.getOrigin();
+ FileNode target_node = file_job.getDestination();
+
+ File source_file = source_node.getFile();
+ File target_file = new File(target_node.getFile(), source_file.getName());
+ if (file_job.type == FileJob.RENAME) {
+ // This is the only difference between moves and renames
+ target_file = target_node.getFile();
+ target_node = (FileNode) source_node.getParent();
+ }
+
+ // Check the target file isn't the source file
+ if (target_file.equals(source_file)) {
+ DebugStream.println("Target file is the source file!");
+ return;
+ }
+
+ // The target file shouldn't already exist
+ if (target_file.exists()) {
+ int result = showOverwriteDialog(target_file.getName());
+ if (result == JOptionPane.NO_OPTION) {
+ // Don't overwrite
+ return;
+ }
+ if (result == JOptionPane.CANCEL_OPTION) {
+ clearJobs(); // Aborting action
+ return;
+ }
+ }
+
+ // Move the file by renaming it
+ if (!source_file.renameTo(target_file)) {
+ String args[] = { source_file.getName(), target_file.getAbsolutePath() };
+ if (showErrorDialog(Dictionary.get("FileActions.File_Move_Error_Message", args)) == JOptionPane.CANCEL_OPTION) {
+ clearJobs(); // Aborting action
+ }
+ return;
+ }
+
+ // Remove the node from the source model and add it to the target model
+ SynchronizedTreeModelTools.removeNodeFromParent(file_job.source.getTreeModel(), source_node);
+ CollectionTreeNode new_target_node = new CollectionTreeNode(target_file);
+ FileSystemModel target_model = file_job.target.getTreeModel();
+ SynchronizedTreeModelTools.insertNodeInto(target_model, target_node, new_target_node);
+
+ // Move the non-folder level metadata assigned to the original file to the new file
+ ArrayList assigned_metadata = MetadataXMLFileManager.getMetadataAssignedDirectlyToFile(source_file);
+ MetadataXMLFileManager.removeMetadata((CollectionTreeNode) source_node, assigned_metadata);
+ MetadataXMLFileManager.addMetadata((CollectionTreeNode) new_target_node, assigned_metadata);
+ }
+
+
+ /** all this does is move the metadata, and delete the source */
+ private void doFileReplace(FileJob file_job)
+ {
+ FileNode source_node = file_job.getOrigin();
+ FileNode target_node = file_job.getDestination();
+
+ File source_file = source_node.getFile();
+ File target_file = target_node.getFile();
+
+ // Move the non-folder level metadata assigned to the original file to the new file
+ CollectionTreeNode new_target_node = new CollectionTreeNode(target_file);
+ ArrayList assigned_metadata = MetadataXMLFileManager.getMetadataAssignedDirectlyToFile(source_file);
+ MetadataXMLFileManager.removeMetadata((CollectionTreeNode) source_node, assigned_metadata);
+ MetadataXMLFileManager.addMetadata((CollectionTreeNode) new_target_node, assigned_metadata);
+
+ // now delete the original
+ doFileDelete(file_job);
+ }
+
+
+ private void processFileJob(FileJob file_job)
+ {
+ DebugStream.println("Processing file job " + file_job + "...");
+
+ // Ensure that the source file exists
+ File source_file = file_job.getOrigin().getFile();
+ if (!source_file.exists()) {
+ // The source file doesn't exist, so give the user the option of continuing or cancelling
+ if (showErrorDialog(Dictionary.get("FileActions.File_Not_Found_Message", source_file.getAbsolutePath())) == JOptionPane.CANCEL_OPTION) {
+ clearJobs(); // Aborting action
+ }
+ // Refresh the source tree model
+ FileSystemModel source_model = file_job.source.getTreeModel();
+ source_model.refresh(new TreePath(((FileNode) file_job.getOrigin().getParent()).getPath()));
+ return;
+ }
+
+ // Enable the "Stop" button
+ stop_button.setEnabled(true);
+
+ // Delete empty directory job
+ if (file_job.type == FileJob.DELETE_EMPTY_DIRECTORY) {
+ file_status.setText(Dictionary.get("FileActions.Deleting", formatPath("FileActions.Deleting", source_file.getAbsolutePath(), file_status.getSize().width)));
+ doEmptyDirectoryDelete(file_job);
+ return;
+ }
+
+ // Delete job
+ if (file_job.type == FileJob.DELETE) {
+ file_status.setText(Dictionary.get("FileActions.Deleting", formatPath("FileActions.Deleting", source_file.getAbsolutePath(), file_status.getSize().width)));
+ if (source_file.isFile()) {
+ long source_file_size = source_file.length();
+ doFileDelete(file_job);
+ progress.addValue(source_file_size); // Update progress bar
+ }
+ else {
+ doDirectoryDelete(file_job);
+ }
+ return;
+ }
+
+ // Copy job
+ if (file_job.type == FileJob.COPY || file_job.type == FileJob.COPY_FILE_ONLY) {
+ file_status.setText(Dictionary.get("FileActions.Copying", formatPath("FileActions.Copying", source_file.getAbsolutePath(), file_status.getSize().width)));
+ if (source_file.isFile()) {
+ long source_file_size = source_file.length();
+ doFileCopy(file_job);
+ progress.addValue(source_file_size); // Update progress bar
+ }
+ else {
+ doDirectoryCopy(file_job);
+ }
+ return;
+ }
+
+ // Move (or rename) job
+ if (file_job.type == FileJob.MOVE || file_job.type == FileJob.RENAME) {
+ file_status.setText(Dictionary.get("FileActions.Moving", formatPath("FileActions.Moving", source_file.getAbsolutePath(), file_status.getSize().width)));
+ if (source_file.isFile()) {
+ long source_file_size = source_file.length();
+ doFileMove(file_job);
+ progress.addValue(source_file_size); // Update progress bar
+ }
+ else {
+ doDirectoryMove(file_job);
+ }
+ return;
+ }
+
+ // Replace job
+ if (file_job.type == FileJob.REPLACE) {
+ file_status.setText(Dictionary.get("FileActions.Replacing", formatPath("FileActions.Replacing", source_file.getAbsolutePath(), file_status.getSize().width)));
+ doFileReplace(file_job);
+ return;
+ }
+ }
+
+
+ private int showErrorDialog(String error_message)
+ {
+ Object[] options = { Dictionary.get("General.OK"), Dictionary.get("General.Cancel") };
+ int result = JOptionPane.showOptionDialog(Gatherer.g_man, error_message, Dictionary.get("General.Error"), JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE, null, options, options[0]);
+ if (result == 0) {
+ return JOptionPane.OK_OPTION;
+ }
+ else {
+ return JOptionPane.CANCEL_OPTION;
+ }
+ }
+
+
+ private int showOverwriteDialog(String target_file_name)
+ {
+ // Has "yes to all" been set?
+ if (yes_to_all) {
+ return JOptionPane.YES_OPTION;
+ }
+
+ Object[] options = { Dictionary.get("General.Yes"), Dictionary.get("FileActions.Yes_To_All"), Dictionary.get("General.No"), Dictionary.get("General.Cancel") };
+ int result = JOptionPane.showOptionDialog(Gatherer.g_man, Dictionary.get("FileActions.File_Exists", target_file_name), Dictionary.get("General.Warning"), JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]);
+ if (result == 0) {
+ return JOptionPane.YES_OPTION;
+ }
+ else if (result == 1) {
+ yes_to_all = true;
+ return JOptionPane.YES_OPTION;
+ }
+ else if (result == 2) {
+ return JOptionPane.NO_OPTION;
+ }
+ else {
+ return JOptionPane.CANCEL_OPTION;
+ }
+ }
+
+
+ public void run()
+ {
+ super.setName("FileQueue");
+
+ while (!Gatherer.exit) {
+ // Retrieve the next job
+ int position = queue.size() - 1;
+ if (position >= 0) {
+ // We have a file job, so process it
+ processFileJob((FileJob) queue.remove(position));
+ }
+ else {
+ // No jobs, so reset and wait until we are notified of one
+ synchronized(this) {
+ // Force both workspace and collection trees to refresh
+ if (Gatherer.g_man != null && Gatherer.c_man.ready()) {
+ Gatherer.g_man.refreshWorkspaceTree(DragTree.COLLECTION_CONTENTS_CHANGED);
+ // Gatherer.g_man.refreshCollectionTree(DragTree.COLLECTION_CONTENTS_CHANGED);
+ }
+
+ // Reset status area
+ file_status.setText(Dictionary.get("FileActions.No_Activity"));
+ progress.reset();
+ progress.setString(Dictionary.get("FileActions.No_Activity"));
+
+ // Reset "yes to all" and "cancel" flags
+ yes_to_all = false;
+ cancel_action = false;
+
+ // Wait for a new file job
+ try {
+ wait();
+ }
+ catch (InterruptedException exception) {}
+ }
+ }
+ }
+ }
+
+
+ /** Register the button that will be responsible for stopping executing file actions.
+ * @param stop_button a JButton
+ */
+ public void registerStopButton(JButton stop_button) {
+ this.stop_button = stop_button;
+ }
+
+
+ synchronized private void clearJobs() {
+ queue.clear();
+ }
+
+ /** Copy the contents from the source directory to the destination
+ * directory.
+ * @param source The source directory
+ * @param destination The destination directory
+ * @see org.greenstone.gatherer.Gatherer
+ */
+ public void copyDirectoryContents(File source, File destination, boolean overwrite)
+ throws FileAlreadyExistsException, FileNotFoundException, InsufficientSpaceException, IOException, ReadNotPermittedException, UnknownFileErrorException, WriteNotPermittedException
+ {
+ if (!source.isDirectory()) return;
+ // check that dest dirs exist
+ destination.mkdirs();
+
+ File [] src_files = source.listFiles();
+ if (src_files.length == 0) return; // nothing to copy
+ for (int i=0; i destination.length()) {
+ // Determine the difference (which I guess is in bytes).
+ long difference = (destination_size + (long) data_size) - destination.length();
+ // Transform that into a human readable string.
+ String message = Utility.formatFileLength(difference);
+ throw new InsufficientSpaceException(message);
+ }
+ else {
+ throw(io_exception);
+ }
+ }
+ }
+
+ // Flush and close the streams to ensure all bytes are written.
+ f_in.close();
+ f_out.close();
+
+ // We have now, in theory, produced an exact copy of the source file. Check this by comparing sizes.
+ if(!destination.exists() || (!cancel_action && source.length() != destination.length())) {
+ throw new UnknownFileErrorException();
+ }
+
+ // If we were cancelled, ensure that none of the destination file exists.
+ if (cancel_action) {
+ destination.delete();
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/FileSystem.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/FileSystem.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/FileSystem.java (revision 31635)
@@ -0,0 +1,63 @@
+package org.greenstone.gatherer.file;
+
+import java.io.File;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+
+
+public class FileSystem
+{
+ static public WorkspaceTreeNode getLocalFilespaceNode(FileSystemModel model)
+ {
+ // Get all the available roots mounted on the filesystem
+ File[] roots = File.listRoots();
+ if (roots == null) {
+ return null;
+ }
+
+ // If there is just one root use it as the tree root (Unix)
+ if (roots.length == 1) {
+ return new WorkspaceTreeNode(roots[0], Dictionary.get("Tree.Root"));
+ }
+
+ // Otherwise build a dummy node which has the roots as children (Windows)
+ else {
+ return new WorkspaceTreeNode(null, Dictionary.get("Tree.Root"));
+ }
+ }
+
+
+ static public WorkspaceTreeNode getHomeFolderNode()
+ {
+ // Get the name of the OS we're running on
+ String os_name = System.getProperty("os.name");
+ if (os_name == null) {
+ return null;
+ }
+
+ // Make sure we're running on a multiuser OS where the idea of a "user home" is valid
+ DebugStream.println("Property os.name: " + os_name);
+ String os_name_lc = os_name.toLowerCase();
+ if (os_name_lc.equals("windows 95") || os_name_lc.equals("windows 98") || os_name_lc.equals("windows me")) {
+ return null;
+ }
+
+ // Get the user's home folder
+ String home_folder_str = System.getProperty("user.home");
+ if (home_folder_str == null || home_folder_str.length() == 0) {
+ return null;
+ }
+
+ // Generate a special mapping to the user home folder
+ File home_folder = new File(home_folder_str);
+ return new WorkspaceTreeNode(home_folder, Dictionary.get("Tree.Home", home_folder.getName()));
+ }
+
+
+ static public WorkspaceTreeNode getDownloadedFilesNode()
+ {
+ // Return a mapping to the downloaded files folder
+ return new WorkspaceTreeNode(new File(Gatherer.getGLIUserCacheDirectoryPath()), Dictionary.get("Tree.DownloadedFiles"));
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/FileSystemModel.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/FileSystemModel.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/FileSystemModel.java (revision 31635)
@@ -0,0 +1,278 @@
+package org.greenstone.gatherer.file;
+
+import java.io.File;
+import java.util.*;
+import java.util.regex.*;
+import javax.swing.event.*;
+import javax.swing.tree.*;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.gui.tree.DragTree;
+
+
+public class FileSystemModel
+ extends DefaultTreeModel
+ implements TreeExpansionListener, TreeWillExpandListener
+{
+ private DragTree tree = null;
+ private FileFilter current_filter = null;
+ private FileFilter[] filters = null;
+ /** The filters in place for any file system model. */
+ private FileFilter[] default_filters = { new FileFilter("\\..*", true), new FileFilter("metadata\\.xml", true) };
+
+ public FileSystemModel(FileNode root) {
+ super(root);
+ root.setModel(this);
+ root.refresh();
+ }
+
+ public int getChildCount(Object parent) {
+ return ((TreeNode)parent).getChildCount();
+ }
+
+ public FileFilter[] getFilters() {
+ if(filters == null) {
+ if(current_filter != null) {
+ filters = new FileFilter[default_filters.length + 1];
+ filters[default_filters.length] = current_filter;
+ }
+ else {
+ filters = new FileFilter[default_filters.length];
+ }
+ System.arraycopy(default_filters, 0, filters, 0, default_filters.length);
+ }
+ return filters;
+ }
+
+ /** Retrieve the node denoted by the given tree path. Note that this isn't equivelent to saying path.lastPathComponent, as the references within the path may be stale. */
+ public FileNode getNode(TreePath path) {
+ FileNode last = (FileNode)path.getLastPathComponent();
+ DebugStream.println("Last Path Component = " + last);
+ return last;
+ /*
+ ///atherer.println("**** getNode(" + path + ") ****");
+ FileNode current = (FileNode)root;
+ // Special case for the root node. Check the first path component is the root node.
+ FileNode first_node = (FileNode)path.getPathComponent(0);
+ if(current.equals(first_node)) {
+ DebugStream.println("First path component matches root node.");
+ // For each path with this tree path
+ for(int i = 1; current != null && i < path.getPathCount(); i++) {
+ // Retrieve the stale path
+ Object stale_object = path.getPathComponent(i);
+ FileNode stale_node = null;
+ if(stale_object instanceof FileNode) {
+ stale_node = (FileNode) stale_object;
+ }
+ DebugStream.print("Searching for '" + stale_object + "': ");
+ // Locate the fresh node by searching current's children. Remember to ensure that current is mapped.
+ boolean found = false;
+
+ // First we search through the mapped children
+ for(int j = 0; !found && j < current.getChildCount(); j++) {
+ FileNode child_node = (FileNode) current.getChildAt(j);
+ DebugStream.print(child_node + " ");
+ if((stale_node != null && stale_node.equals(child_node)) || stale_object.toString().equals(child_node.toString())) {
+ found = true;
+ current = child_node;
+ DebugStream.println("Found!");
+ }
+ child_node = null;
+ }
+ // Failing that we search through all the children, including filtered files
+ for(int j = 0; !found && j < current.size(); j++) {
+ FileNode child_node = (FileNode) current.getChildAtUnfiltered(j);
+ DebugStream.print(child_node + " ");
+ if((stale_node != null && stale_node.equals(child_node)) || stale_object.toString().equals(child_node.toString())) {
+ found = true;
+ current = child_node;
+ DebugStream.println("Found!");
+ }
+ child_node = null;
+ }
+ // If no match is found, then set current to null and exit.
+ if(!found) {
+ current = null;
+ DebugStream.println("Not Found!");
+ }
+ else {
+ DebugStream.println("Returning node: " + new TreePath(current.getPath()));
+ }
+ // Repeat as necessary
+ }
+ }
+ return current;
+ */
+ }
+
+ public void insertNodeInto(MutableTreeNode newChild, MutableTreeNode parent, int index) {
+ ///ystem.err.println("insertNodeInto(" + newChild + ", " + parent + ", " + index + ")");
+ super.insertNodeInto(newChild, parent, index);
+ }
+
+
+ public void refresh(TreePath path)
+ {
+ // Can only refresh if the model is currently being displayed in a tree
+ if (tree == null) {
+ return;
+ }
+
+ // If no path is set, take the path to the root node (ie. update the whole tree)
+ if (path == null) {
+ // System.err.println("\nFileSystemModel.refresh(entire tree).");
+ path = new TreePath(((FileNode) root).getPath());
+
+ // Make sure the root node is expanded
+ tree.expandPath(path);
+ }
+ // else {
+ // System.err.println("\nFileSystemModel.refresh(" + path + ").");
+ // }
+
+ // Record all the expanded paths under this node
+ Enumeration old_expanded_paths_enumeration = tree.getExpandedDescendants(path);
+ if (old_expanded_paths_enumeration == null) {
+ return;
+ }
+
+ // Map and unmap the node to refresh its contents
+ FileNode node = (FileNode) path.getLastPathComponent();
+ node.refresh();
+
+ // Fire the appropriate event
+ nodeStructureChanged(node);
+
+ // Sort the old expanded paths by length, smallest first
+ ArrayList old_expanded_paths_list = Collections.list(old_expanded_paths_enumeration);
+ Collections.sort(old_expanded_paths_list, new TreePathComparator());
+
+ // Restore each of the expanded paths
+ for (int i = 0; i < old_expanded_paths_list.size(); i++) {
+ TreePath old_expanded_path = (TreePath) old_expanded_paths_list.get(i);
+ // System.err.println("Expanded path: " + old_expanded_path);
+
+ // Build up the new path in the tree
+ TreePath current_path = new TreePath(path.getPath());
+ FileNode current_node = node;
+
+ // Traverse the tree to find the node to expand (or find it no longer exists)
+ while (!current_path.toString().equals(old_expanded_path.toString())) {
+ // System.err.println("Current path: " + current_path);
+
+ FileNode old_expanded_node =
+ (FileNode) old_expanded_path.getPathComponent(current_path.getPathCount());
+ // System.err.println("Looking for: " + old_expanded_node);
+
+ // Find the child node that matches the next element in the path
+ boolean found = false;
+ for (int j = 0; j < current_node.getChildCount(); j++) {
+ FileNode child_node = (FileNode) current_node.getChildAt(j);
+ // System.err.println("Child node: " + child_node);
+ if (child_node.equals(old_expanded_node)) {
+ // System.err.println("Found!");
+ current_path = current_path.pathByAddingChild(child_node);
+ current_node = child_node;
+ found = true;
+ break;
+ }
+ }
+
+ // The node was not found, so we cannot expand this path
+ if (!found) {
+ // System.err.println("Not found...");
+ break;
+ }
+ }
+
+ // If we have built up the correct path, expand it
+ if (current_path.toString().equals(old_expanded_path.toString())) {
+ tree.expandPath(current_path);
+ }
+ }
+ }
+
+
+ // The file tree can be filtered, so check that the node to be removed is actually part of the model
+ public void removeNodeFromParent(MutableTreeNode node)
+ {
+ TreeNode parent_node = ((FileNode) node).getParent();
+ if (parent_node != null && parent_node.getIndex((FileNode) node) != -1) {
+ super.removeNodeFromParent(node);
+ }
+ }
+
+
+ private class TreePathComparator
+ implements Comparator {
+
+ public int compare(Object o1, Object o2)
+ {
+ return (((TreePath) o1).getPathCount() - ((TreePath) o2).getPathCount());
+ }
+ }
+
+
+ public void setFilter(String pattern) {
+ if(pattern != null) {
+ current_filter = new FileFilter(pattern, false);
+ }
+ else {
+ current_filter = null;
+ }
+ filters = null;
+ }
+
+ public void setTree(DragTree tree) {
+ this.tree = tree;
+ }
+
+ public String toString() {
+ if(tree != null) {
+ return tree.toString();
+ }
+ return "FileSystemModel";
+ }
+
+ /** Called whenever an item in the tree has been collapsed. */
+ public void treeCollapsed(TreeExpansionEvent event) {
+ // Deallocate the affected nodes children. Don't need to do this in a swing worker, as the nodes children are currently not visable.
+ TreePath path = event.getPath();
+ FileNode node = (FileNode) path.getLastPathComponent();
+ node.unmap();
+ // Fire the appropriate event.
+ nodeStructureChanged(node);
+ }
+
+ /** Called whenever an item in the tree has been expanded. */
+ public void treeExpanded(TreeExpansionEvent event) {
+ }
+
+ /** Invoked whenever a node in the tree is about to be collapsed. */
+ public void treeWillCollapse(TreeExpansionEvent event)
+ throws ExpandVetoException {
+ // Veto the event if the user is attempting to collapse the root node (regardless of whether it is visible).
+ TreePath path = event.getPath();
+ if(path.getPathCount() == 1) {
+ throw new ExpandVetoException(event, "Cannot collapse root node!");
+ }
+ }
+
+
+ /** Invoked whenever a node in the tree is about to be expanded. */
+ public void treeWillExpand(TreeExpansionEvent event)
+ throws ExpandVetoException
+ {
+ // Set the wait cursor
+ Gatherer.g_man.wait(true);
+
+ // Allocate the children (don't need a swing worker since the node's children are currently not visible)
+ TreePath path = event.getPath();
+ FileNode node = (FileNode) path.getLastPathComponent();
+ node.refresh();
+ nodeStructureChanged(node);
+
+ // Restore the cursor
+ Gatherer.g_man.wait(false);
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/InsufficientSpaceException.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/InsufficientSpaceException.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/InsufficientSpaceException.java (revision 31635)
@@ -0,0 +1,11 @@
+package org.greenstone.gatherer.file;
+
+import java.lang.Exception;
+
+public class InsufficientSpaceException
+ extends Exception {
+
+ public InsufficientSpaceException(String message) {
+ super(message);
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/ReadNotPermittedException.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/ReadNotPermittedException.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/ReadNotPermittedException.java (revision 31635)
@@ -0,0 +1,11 @@
+package org.greenstone.gatherer.file;
+
+import java.lang.Exception;
+
+public class ReadNotPermittedException
+ extends Exception
+{
+ public ReadNotPermittedException(String message) {
+ super(message);
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/RecycleBin.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/RecycleBin.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/RecycleBin.java (revision 31635)
@@ -0,0 +1,188 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.file;
+
+import java.awt.*;
+import java.awt.dnd.*;
+import java.awt.event.*;
+import java.awt.geom.AffineTransform;
+import java.io.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.tree.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.gui.GLIButton;
+import org.greenstone.gatherer.util.DragComponent;
+import org.greenstone.gatherer.util.DragGroup;
+import org.greenstone.gatherer.util.JarTools;
+
+
+public class RecycleBin
+ extends GLIButton
+ implements DragComponent, DropTargetListener {
+
+ private boolean ignore = false;
+ /** The group encompasses all of the objects you plan to drag and drop within, and ensures that only one has focus (as clearly identified by the colour of the selection field or, in this particular case, the background) and that actions only occur between components in the same group. */
+ private DragGroup group;
+ /** In order to make this button a drop target we have to create a DropTarget instance with the button as its target. */
+ private DropTarget drop_target;
+ private FileSystemModel model;
+ /** What sort of action should a drag resemble. Not really used as we override with custom drag icon. */
+ private int drag_action = DnDConstants.ACTION_MOVE;
+ /** The last point the mouse was at. Used to repaint 'spoilt' area. */
+ private Point pt_last = null;
+ /** The area covered by the drag ghost, our custom drag icon. */
+ private Rectangle ra_ghost = new Rectangle();
+
+
+ public RecycleBin()
+ {
+ super(JarTools.getImage("bin.gif"));
+ this.drop_target = new DropTarget(this, drag_action, this, true);
+
+ setBackground(Configuration.getColor("coloring.button_background", true));
+ setForeground(Configuration.getColor("coloring.button_foreground", true));
+ setOpaque(true);
+
+ this.model = new FileSystemModel(new RecycleBinNode(new File(Gatherer.getGLIUserDirectoryPath() + "recycle")));
+ }
+
+
+ /** In order for the appearance to be consistant, given we may be in the situation where the pointer has left our focus but the ghost remains, this method allows other members of the GGroup to tell this component to repair the 'spoilt' region left by its ghost. */
+ public void clearGhost(){
+ }
+
+
+ /** Any implementation of DropTargetListener must include this method so we can be notified when the drag focus enters this component. We want to provide some sort of indication whether the current component is an acceptable drop target as well as indicating focus. */
+ public void dragEnter(DropTargetDragEvent event) {
+ group.grabFocus(this);
+ setBackground(Configuration.getColor("coloring.button_selected_background", true));
+ setForeground(Configuration.getColor("coloring.button_selected_foreground", true));
+ }
+
+ /** Any implementation of DropTargetListener must include this method so we can be notified when the drag focus leaves this component. We need to indicate that we have lost focus. */
+ public void dragExit(DropTargetEvent event) {
+ setBackground(Configuration.getColor("coloring.button_background", true));
+ setForeground(Configuration.getColor("coloring.button_foreground", true));
+ }
+
+ /** Any implementation of DropTargetListener must include this method so we can be notified when the drag moves in this component. This is where we repaint our ghost icon at the tip of the mouse pointer. */
+ public void dragOver(DropTargetDragEvent event) {
+ Graphics2D g2 = (Graphics2D) getGraphics();
+ Point pt = event.getLocation();
+ if(pt_last != null && pt.equals(pt_last)) {
+ return;
+ }
+ pt_last = pt;
+ if(!DragSource.isDragImageSupported()) {
+ // Erase the last ghost image and or cue line
+ paintImmediately(ra_ghost.getBounds());
+ // Remember where you are about to draw the new ghost image
+ ra_ghost.setRect(pt.x - group.mouse_offset.x, pt.y - group.mouse_offset.y, group.image_ghost.getWidth(), group.image_ghost.getHeight());
+ // Draw the ghost image
+ g2.drawImage(group.image_ghost, AffineTransform.getTranslateInstance(ra_ghost.getX(), ra_ghost.getY()), null);
+ }
+ }
+
+ /** Any implementation of DropTargetListener must include this method so we can be notified when the drag ends, ie the transferable is dropped. This in turn triggers a series of add events preceded by a pre() and followed by a post(). */
+ public void drop(DropTargetDropEvent event) {
+ ignore = true;
+ group.grabFocus(this);
+ setBackground(Configuration.getColor("coloring.button_background", true));
+ setForeground(Configuration.getColor("coloring.button_foreground", true));
+
+ try {
+ DragComponent source = group.getSource();
+ TreePath[] selection = group.getSelection();
+ FileNode[] source_nodes = new FileNode[selection.length];
+ for(int i = 0; i < source_nodes.length; i++) {
+ source_nodes[i] = (FileNode) selection[i].getLastPathComponent();
+ }
+ event.acceptDrop(drag_action);
+ // Action delete
+ Gatherer.f_man.action(source, source_nodes, this, null);
+ group.setSource(null);
+ group.setSelection(null);
+ }
+ catch(Exception error) {
+ error.printStackTrace();
+ event.rejectDrop();
+ }
+ ignore = false;
+ // Clear up the group.image_ghost
+ paintImmediately(ra_ghost.getBounds());
+ event.getDropTargetContext().dropComplete(true);
+ }
+
+ /** Any implementation of DropTargetListener must include this method so we can be notified when the action to be taken upon drop changes. We never change so we don't do anything here. */
+ public void dropActionChanged(DropTargetDragEvent event) {
+ }
+
+ /** Used to notify this component that it has gained focus by some method other that mouse focus. */
+ public void gainFocus() {
+ }
+
+ /** Any implementation of DragComponent must include this method so that a outsider can get at the underlying tree model behind the component. */
+ public FileSystemModel getTreeModel(){
+ return (FileSystemModel) model;
+ }
+
+ public boolean ignore() {
+ return ignore;
+ }
+
+ /** This method is used to inform this component when it loses focus by means other than a drag mouse event, and should indicate this somehow. */
+ public void loseFocus() {
+ }
+
+ public void setGroup(DragGroup group) {
+ this.group = group;
+ }
+
+
+ public class RecycleBinNode
+ extends FileNode
+ {
+ public RecycleBinNode(File file)
+ {
+ super(file);
+ }
+
+ public FileNode addChildNode(File file) { return null; }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/UnknownFileErrorException.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/UnknownFileErrorException.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/UnknownFileErrorException.java (revision 31635)
@@ -0,0 +1,11 @@
+package org.greenstone.gatherer.file;
+
+import java.lang.Exception;
+
+public class UnknownFileErrorException
+ extends Exception {
+
+ public UnknownFileErrorException() {
+ super();
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/WorkspaceTree.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/WorkspaceTree.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/WorkspaceTree.java (revision 31635)
@@ -0,0 +1,450 @@
+/**
+ *############################################################################
+ * A component of the Greenstone Librarian Interface, part of the Greenstone
+ * digital library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################################################
+ */
+
+package org.greenstone.gatherer.file;
+
+import java.awt.event.*;
+import java.io.File;
+import java.util.Enumeration;
+import javax.swing.*;
+import javax.swing.tree.TreePath;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.collection.CollectionManager;
+import org.greenstone.gatherer.gui.CreateShortcutPrompt;
+import org.greenstone.gatherer.gui.tree.DragTree;
+import org.greenstone.gatherer.util.Utility;
+
+
+public class WorkspaceTree
+ extends DragTree
+ implements MouseListener
+{
+ static public final int LIBRARY_CONTENTS_CHANGED = 10;
+ static public final int DOWNLOADED_FILES_CHANGED = 11;
+ static public final int FOLDER_SHORTCUTS_CHANGED = 12;
+
+
+ public WorkspaceTree()
+ {
+ super(WorkspaceTreeModel.getWorkspaceTreeModel(), true);
+ addMouseListener(this);
+
+ setBackgroundNonSelectionColor(Configuration.getColor("coloring.workspace_tree_background", false));
+ setBackgroundSelectionColor(Configuration.getColor("coloring.workspace_selection_background", false));
+ setTextNonSelectionColor(Configuration.getColor("coloring.workspace_tree_foreground", false));
+ setTextSelectionColor(Configuration.getColor("coloring.workspace_selection_foreground", false));
+
+ filter.setBackground(Configuration.getColor("coloring.workspace_heading_background", false));
+ filter.setEditable(Configuration.getMode() >= Configuration.LIBRARIAN_MODE);
+ }
+
+
+ public void addDirectoryMapping(String name, File file)
+ {
+ // Update the information stored in the user's configuration
+ Configuration.addDirectoryMapping(name, file);
+
+ // Now update the tree
+ refresh(FOLDER_SHORTCUTS_CHANGED);
+ }
+
+
+ public boolean isDraggable()
+ {
+ return true;
+ }
+
+
+ public boolean isDroppable()
+ {
+ return false;
+ }
+
+
+ public void mouseClicked(MouseEvent event)
+ {
+ if (SwingUtilities.isRightMouseButton(event)) {
+ new WorkspaceTreeRightClickMenu(this, event);
+ }
+ }
+
+ public void mouseEntered(MouseEvent event) { }
+
+ public void mouseExited(MouseEvent event) { }
+
+ public void mousePressed(MouseEvent event) { }
+
+ public void mouseReleased(MouseEvent event) { }
+
+
+ public void refresh(int refresh_reason)
+ {
+ DebugStream.println("WorkspaceTree::refresh()... ");
+
+ // The method for displaying the tree has changed - redraw the tree
+ if (refresh_reason == DragTree.TREE_DISPLAY_CHANGED) {
+ DebugStream.println("...Reason: tree display changed.");
+ updateUI();
+ }
+
+ // The loaded collection has changed - currently do nothing
+ if (refresh_reason == DragTree.LOADED_COLLECTION_CHANGED) {
+ DebugStream.println("...Reason: loaded collection changed.");
+ }
+
+ // The collection's contents have changed - refresh collection's import folder
+ if (refresh_reason == DragTree.COLLECTION_CONTENTS_CHANGED) {
+ DebugStream.println("...Reason: collection contents changed.");
+ String collection_import_directory_path = CollectionManager.getLoadedCollectionImportDirectoryPath();
+ refreshEveryNodeShowingFolder(collection_import_directory_path);
+ }
+
+ // The collections in the library have changed - refresh the collect directory
+ if (refresh_reason == WorkspaceTree.LIBRARY_CONTENTS_CHANGED) {
+ DebugStream.println("...Reason: library contents changed.");
+ String collect_directory_path = Gatherer.getCollectDirectoryPath();
+ refreshEveryNodeShowingFolder(collect_directory_path);
+ WorkspaceTreeModel.refreshGreenstoneCollectionsNode();
+ }
+
+ // The downloaded files have changed - refresh that node
+ if (refresh_reason == WorkspaceTree.DOWNLOADED_FILES_CHANGED) {
+ DebugStream.println("...Reason: downloaded files changed.");
+ WorkspaceTreeModel.refreshDownloadedFilesNode();
+ }
+
+ // The folder shortcuts have changed - refresh only them
+ if (refresh_reason == WorkspaceTree.FOLDER_SHORTCUTS_CHANGED) {
+ DebugStream.println("...Reason: folder shortcuts changed.");
+ WorkspaceTreeModel.refreshFolderShortcuts();
+ }
+ }
+
+
+ private void refreshEveryNodeShowingFolder(String folder_path_str)
+ {
+ // Search through the expanded tree paths, checking if any show the given folder
+ TreePath tree_root_path = new TreePath(getModel().getRoot());
+ Enumeration expanded_tree_paths = getExpandedDescendants(tree_root_path);
+ while (expanded_tree_paths.hasMoreElements()) {
+ TreePath expanded_tree_path = (TreePath) expanded_tree_paths.nextElement();
+ WorkspaceTreeNode tree_node = (WorkspaceTreeNode) expanded_tree_path.getLastPathComponent();
+
+ File tree_node_file = tree_node.getFile();
+ if (tree_node_file == null) {
+ continue;
+ }
+
+ // Get the path of the open tree node
+ String tree_node_path_str = tree_node_file.toString();
+ if (!tree_node_path_str.endsWith(File.separator)) {
+ tree_node_path_str = tree_node_path_str + File.separator;
+ }
+
+ // If the specified folder is open, it must be refreshed
+ if (tree_node_path_str.equals(folder_path_str)) {
+ //System.err.println("Must refresh node " + tree_node_path_str + "!");
+ ((WorkspaceTreeModel) getModel()).refresh(expanded_tree_path);
+ }
+ }
+ }
+
+
+ public void removeDirectoryMapping(WorkspaceTreeNode target)
+ {
+ // Update the information stored in the user's configuration
+ Configuration.removeDirectoryMapping(target.toString());
+
+ // Update tree
+ refresh(FOLDER_SHORTCUTS_CHANGED);
+ }
+
+
+ public String toString()
+ {
+ return "Workspace";
+ }
+
+
+ /** When a user right-clicks within the workspace and collection trees they are presented with a small popup menu of context based options. This class provides such functionality.
+ */
+ private class WorkspaceTreeRightClickMenu
+ extends JPopupMenu
+ implements ActionListener
+ {
+ /** The tree over which the right click action occurred. */
+ private WorkspaceTree workspace_tree = null;
+ /** The tree nodes selected when the right click action occurred. */
+ private TreePath[] selection_paths = null;
+ /** The file record over which the right click action occurred. */
+ private WorkspaceTreeNode node = null;
+
+ private JMenuItem create_shortcut = null;
+ private JMenuItem delete_shortcut = null;
+ private JMenuItem collapse_folder = null;
+ private JMenuItem expand_folder = null;
+ private JMenuItem explode_metadata_database = null;
+ private JMenuItem delete = null;
+ private JMenuItem metaaudit = null;
+ private JMenuItem new_folder = null;
+ private JMenuItem new_dummy_doc = null;
+ private JMenuItem open_externally = null;
+ private JMenuItem rename = null;
+ private JMenuItem replace = null;
+ private JMenuItem copy_collection = null;
+ private JMenuItem move_collection = null;
+
+
+ private WorkspaceTreeRightClickMenu(WorkspaceTree workspace_tree, MouseEvent event)
+ {
+ super();
+ this.workspace_tree = workspace_tree;
+
+ // Note we have to use setImmediate() with the set selction paths
+ // otherwise the selection doesn't get updated until after the
+ // popup comes up.
+
+ // the right click position
+ TreePath right_click_path = workspace_tree.getPathForLocation(event.getX(), event.getY());
+ if (right_click_path == null) {
+ // user has clicked outside of the tree, clear the selection
+ selection_paths = null;
+ workspace_tree.setImmediate(true);
+ workspace_tree.clearSelection();
+ workspace_tree.setImmediate(false);
+ }
+ else {
+ // Get the paths currently selected in the tree
+ selection_paths = workspace_tree.getSelectionPaths();
+ if (selection_paths == null) {
+ // nothing currently selected - we shift the selection to
+ // the node that was right clicked on
+ selection_paths = new TreePath[1];
+ selection_paths[0] = right_click_path;
+ workspace_tree.setImmediate(true);
+ workspace_tree.setSelectionPath(right_click_path);
+ workspace_tree.setImmediate(false);
+ }
+ else if (selection_paths.length == 1 && ! selection_paths[0].equals( right_click_path)) {
+ workspace_tree.setImmediate(true);
+ workspace_tree.clearSelection();
+ workspace_tree.setSelectionPath(right_click_path);
+ workspace_tree.setImmediate(false);
+ selection_paths[0] = right_click_path;
+ }
+ else {
+ // we had multiply selected paths in the tree.
+ // if we clicked on one of those paths, then use all the
+ // current selection, otherwise clear the selection and
+ // select the one we right clicked on
+ boolean clicked_in_selection = false;
+ for (int i = 0; i < selection_paths.length; i++) {
+ if (selection_paths[i].equals(right_click_path)) {
+ clicked_in_selection = true;
+ break;
+ }
+ }
+ if (!clicked_in_selection) {
+ // want the tree to update right away
+ workspace_tree.setImmediate(true);
+ workspace_tree.clearSelection();
+ workspace_tree.setSelectionPath(right_click_path);
+ workspace_tree.setImmediate(false);
+ selection_paths = new TreePath[1];
+ selection_paths[0] = right_click_path;
+ }
+ }
+ }
+
+ // Create an appropriate context menu, based on what is selected
+ buildContextMenu(selection_paths);
+
+ // Show the popup menu on screen
+ show(workspace_tree, event.getX(), event.getY());
+ }
+
+
+ private void buildContextMenu(TreePath[] selection_paths)
+ {
+ // If nothing is selected, no options are available...
+ if (selection_paths == null) {
+ return;
+ }
+
+ // Only meta-audit and delete are available if multiple items are selected...
+ if (selection_paths.length > 1) {
+ return;
+ }
+
+ TreePath path = selection_paths[0];
+ node = (WorkspaceTreeNode) path.getLastPathComponent();
+
+ // ---- Options for file nodes ----
+ if (node.isLeaf()) {
+ // Open the file in an external program
+ open_externally = new JMenuItem(Dictionary.get("Menu.Open_Externally"), KeyEvent.VK_O);
+ open_externally.addActionListener(this);
+ add(open_externally);
+
+ return;
+ }
+
+ // ---- Options for folder nodes ----
+ // Collapse or expand, depending on current status
+ if (workspace_tree.isExpanded(path)) {
+ collapse_folder = new JMenuItem(Dictionary.get("Menu.Collapse"), KeyEvent.VK_C);
+ collapse_folder.addActionListener(this);
+ add(collapse_folder);
+ }
+ else {
+ expand_folder = new JMenuItem(Dictionary.get("Menu.Expand"), KeyEvent.VK_O);
+ expand_folder.addActionListener(this);
+ add(expand_folder);
+ }
+
+ // Create/remove shortcut option, for workspace tree only
+ if (workspace_tree == workspace_tree) {
+ // The "built-in" folders can't be modified
+ String node_name = node.toString();
+ if (node_name.equals(Dictionary.get("Tree.World")) || node_name.equals(Dictionary.get("Tree.Root")) || node_name.equals(Dictionary.get("Tree.DownloadedFiles"))) {
+ return;
+ }
+
+ // You can unmap 1st level nodes
+ WorkspaceTreeNode root = (WorkspaceTreeNode) workspace_tree.getModel().getRoot();
+ if (root.getIndex(node) != -1) {
+ delete_shortcut = new JMenuItem(Dictionary.get("MappingPrompt.Unmap"), KeyEvent.VK_R);
+ delete_shortcut.addActionListener(this);
+ add(delete_shortcut);
+ }
+ // Or map any other level directories
+ else {
+ // --- Options for Documents In Greenstone Collections (greenstone_collections_node) ---
+ // all subfolder of Documents In Greenstone Collections can be copied and moved
+ WorkspaceTreeNode secondLevelNode = (WorkspaceTreeNode) path.getPathComponent(1);
+
+ if (secondLevelNode.toString().equals(Dictionary.get("Tree.World"))) {
+ // Can move and copy the collection folders across
+ copy_collection = new JMenuItem(Dictionary.get("Menu.Copy_Collection"), KeyEvent.VK_P);
+ copy_collection.addActionListener(this);
+ add(copy_collection);
+
+ move_collection = new JMenuItem(Dictionary.get("Menu.Move_Collection"), KeyEvent.VK_M);
+ move_collection.addActionListener(this);
+ add(move_collection);
+ }
+
+ create_shortcut = new JMenuItem(Dictionary.get("MappingPrompt.Map"), KeyEvent.VK_S);
+ create_shortcut.addActionListener(this);
+ add(create_shortcut);
+ }
+ }
+ }
+
+
+ /** Called whenever one of the menu items is clicked, this method then causes the appropriate effect. */
+ public void actionPerformed(ActionEvent event)
+ {
+ Object source = event.getSource();
+
+ // Create shortcut
+ if (source == create_shortcut) {
+ //Make sure the file exists
+ if(!node.getFile().exists()){
+ JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("FileActions.Folder_Does_Not_Exist"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ CreateShortcutPrompt csp = new CreateShortcutPrompt(workspace_tree, node.getFile());
+ csp.destroy();
+ }
+
+ // Delete shortcut
+ else if (source == delete_shortcut) {
+ workspace_tree.removeDirectoryMapping(node);
+ }
+
+ // Collapse folder
+ else if (source == collapse_folder) {
+ workspace_tree.collapsePath(selection_paths[0]);
+ }
+
+ // Expand folder
+ else if (source == expand_folder) {
+ workspace_tree.expandPath(selection_paths[0]);
+ }
+
+ // Open in external program
+ else if (source == open_externally) {
+ Gatherer.f_man.openFileInExternalApplication(node.getFile());
+ }
+
+ // Copy or move a collection from Documents in Greenstone Collections
+ else if (source == move_collection || source == copy_collection) {
+
+ JFileChooser chooser = new JFileChooser(Gatherer.getCollectDirectoryPath());
+ chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+ chooser.setDialogTitle(Dictionary.get("FileActions.ChooseDestinationDirectory"));
+ int returnVal = chooser.showOpenDialog(Gatherer.g_man.gather_pane);
+
+ if(returnVal == JFileChooser.APPROVE_OPTION) {
+
+ // the node that the user rightclicked on ends up
+ // being the col's import folder
+ File sourceFolder = node.getFile();
+ if(sourceFolder.getName().equals("import")) {
+ sourceFolder = sourceFolder.getParentFile();
+ }
+ String target = chooser.getSelectedFile().getAbsolutePath();
+ File targetFolder = new File(target+File.separator+sourceFolder.getName());
+
+ // some sanity checks
+ if(targetFolder.equals(sourceFolder)) { // directory has not changed. No copy/move performed
+ JOptionPane.showMessageDialog(Gatherer.g_man,
+ "Can't move " + sourceFolder + " " + "to itself\n(" + targetFolder + ").",
+ "Source and destination directories are the same",
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ } else if(targetFolder.exists()) { // option to overwrite or not
+ if(JOptionPane.showConfirmDialog(Gatherer.g_man,
+ "Directory " + targetFolder + " already exists. Overwrite?",
+ "Destination directory already exists",
+ JOptionPane.YES_NO_OPTION) == JOptionPane.NO_OPTION) {
+ return;
+ }
+ // else can overwrite: delete the existing targetfolder before copying/moving
+ Utility.delete(targetFolder);
+ }
+
+ int operation = (source == move_collection) ? FileManager.MOVE : FileManager.COPY;
+ Gatherer.f_man.action(sourceFolder, targetFolder, operation);
+ }
+ }
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/WorkspaceTreeModel.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/WorkspaceTreeModel.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/WorkspaceTreeModel.java (revision 31635)
@@ -0,0 +1,173 @@
+/**
+ *############################################################################
+ * A component of the Greenstone Librarian Interface, part of the Greenstone
+ * digital library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
+ *
+ * Copyright (C) 2004 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################################################
+ */
+
+package org.greenstone.gatherer.file;
+
+import java.io.*;
+import java.util.*;
+import javax.swing.tree.TreePath;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.util.SynchronizedTreeModelTools;
+
+
+public class WorkspaceTreeModel
+ extends FileSystemModel
+{
+ static private WorkspaceTreeModel workspace_tree_model = null;
+ static private WorkspaceTreeNode workspace_tree_root = null;
+
+ static private WorkspaceTreeNode greenstone_collections_node = null;
+ static private WorkspaceTreeNode local_filespace_node = null;
+ static private WorkspaceTreeNode downloaded_files_node = null;
+ static private WorkspaceTreeNode[] folder_shortcuts = null;
+
+
+ public WorkspaceTreeModel(WorkspaceTreeNode root_node)
+ {
+ super(root_node);
+ }
+
+
+ static public WorkspaceTreeNode[] getFolderShortcuts()
+ {
+ // Return any predefined special directories
+ HashMap mappings = Configuration.getDirectoryMappings();
+ WorkspaceTreeNode[] mapping_nodes = new WorkspaceTreeNode[mappings.size()];
+ Iterator mappings_iterator = mappings.keySet().iterator();
+ for (int i = 0; mappings_iterator.hasNext(); i++) {
+ String mapping_name = (String) mappings_iterator.next();
+ File mapping_file = (File) mappings.get(mapping_name);
+ mapping_nodes[i] = new WorkspaceTreeNode(mapping_file, mapping_name);
+ }
+ return mapping_nodes;
+ }
+
+
+ static public WorkspaceTreeModel getWorkspaceTreeModel()
+ {
+ // Create a root node to contain the various nodes in the workspace tree
+ workspace_tree_root = new WorkspaceTreeNode(null, "ABS_ROOT");
+ workspace_tree_model = new WorkspaceTreeModel(workspace_tree_root);
+
+ // Add the "Documents in Greenstone Collections" node
+ greenstone_collections_node = new WorkspaceTreeNode(null, Dictionary.get("Tree.World"));
+ workspace_tree_root.add(greenstone_collections_node);
+
+ // Add the "Local Filespace" node
+ local_filespace_node = FileSystem.getLocalFilespaceNode(workspace_tree_model);
+ workspace_tree_root.add(local_filespace_node);
+
+ // Add the "Home Folder" node
+ workspace_tree_root.add(FileSystem.getHomeFolderNode());
+
+ // Add the "Downloaded Files" node, if the Download pane is enabled
+ if (Gatherer.g_man.download_pane != null) {
+ downloaded_files_node = FileSystem.getDownloadedFilesNode();
+ workspace_tree_root.add(downloaded_files_node);
+ }
+
+ // Add any folder shortcuts the user has created
+ refreshFolderShortcuts();
+
+ return workspace_tree_model;
+ }
+
+
+ static public void refreshGreenstoneCollectionsNode()
+ {
+ greenstone_collections_node.refresh();
+ }
+
+
+ static public void refreshDownloadedFilesNode()
+ {
+ downloaded_files_node.refresh();
+ }
+
+
+ static public void refreshFolderShortcuts()
+ {
+ // Check for new/deleted folder shortcuts
+ WorkspaceTreeNode[] old_folder_shortcuts = folder_shortcuts;
+ folder_shortcuts = getFolderShortcuts();
+
+ // Remove any deleted shortcuts from the tree
+ if (old_folder_shortcuts != null) {
+ for (int i = 0; i < old_folder_shortcuts.length; i++) {
+ if (!doesArrayContain(folder_shortcuts, old_folder_shortcuts[i])) {
+ DebugStream.println("Deleted shortcut: " + old_folder_shortcuts[i]);
+ SynchronizedTreeModelTools.removeNodeFromParent(workspace_tree_model, old_folder_shortcuts[i]);
+ }
+ }
+ }
+
+ // Add any new shortcuts to the tree
+ if (folder_shortcuts != null) {
+ for (int i = 0; i < folder_shortcuts.length; i++) {
+ if (!doesArrayContain(old_folder_shortcuts, folder_shortcuts[i])) {
+ DebugStream.println("Added shortcut: " + folder_shortcuts[i]);
+ SynchronizedTreeModelTools.insertNodeInto(workspace_tree_model, workspace_tree_root, folder_shortcuts[i], false);
+ }
+ }
+ }
+ }
+
+
+ // -- This code is necessary to support the workspace tree file filter --
+ public void refresh(TreePath path)
+ {
+ // If we're not refreshing the whole tree just refresh a certain path
+ if (path != null) {
+ super.refresh(path);
+ return;
+ }
+
+ // Refresh each of the nodes in the workspace tree
+ for (int i = 0; i < workspace_tree_root.getChildCount(); i++) {
+ WorkspaceTreeNode child_node = (WorkspaceTreeNode) workspace_tree_root.getChildAt(i);
+ super.refresh(new TreePath(child_node.getPath()));
+ }
+ }
+
+
+ static private boolean doesArrayContain(Object[] array, Object item)
+ {
+ if (array == null) {
+ return false;
+ }
+
+ for (int i = 0; i < array.length; i++) {
+ if (item.toString().equals(array[i].toString())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/WorkspaceTreeNode.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/WorkspaceTreeNode.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/WorkspaceTreeNode.java (revision 31635)
@@ -0,0 +1,214 @@
+/**
+ *############################################################################
+ * A component of the Greenstone Librarian Interface, part of the Greenstone
+ * digital library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
+ *
+ * Copyright (C) 2004 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################################################
+ */
+
+package org.greenstone.gatherer.file;
+
+
+import java.io.*;
+import java.util.*;
+import javax.swing.filechooser.FileSystemView;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.collection.BasicCollectionConfiguration; // !!! Don't like this here
+import org.greenstone.gatherer.collection.CollectionManager; // !!! Don't like this here
+import org.greenstone.gatherer.util.ArrayTools;
+import org.greenstone.gatherer.util.StaticStrings;
+import org.greenstone.gatherer.util.Utility;
+
+
+/** This class represents one node in the workspace tree. */
+public class WorkspaceTreeNode
+ extends FileNode
+{
+ private boolean is_gs3_site_node = false;
+ private boolean is_group_node = false;
+ private boolean is_in_loaded_collection = false;
+ private String title = null;
+
+
+ public WorkspaceTreeNode(File file)
+ {
+ super(file);
+ }
+
+
+ public WorkspaceTreeNode(File file, String title)
+ {
+ this(file);
+ this.title = title;
+ }
+
+
+ public FileNode addChildNode(File file)
+ {
+ WorkspaceTreeNode child_node = new WorkspaceTreeNode(file);
+ child_node.setModel(model);
+ child_node.setParent(this);
+ return child_node;
+ }
+
+
+ /** Is this file node within the currently loaded collection? */
+ public boolean isInLoadedCollection()
+ {
+ if (is_in_loaded_collection) {
+ return true;
+ }
+ else {
+ FileNode parent = (FileNode) getParent();
+ if (parent != null) {
+ return parent.isInLoadedCollection();
+ }
+ }
+ return false;
+ }
+
+
+ public boolean isReadOnly()
+ {
+ // The workspace tree is read only
+ return true;
+ }
+
+
+ public void map()
+ {
+ // Special Case: "Documents in Greenstone Collections" -- map the collections installed in Greenstone
+ if (file == null) { // a special mapping folder
+ if (child_nodes != null) {
+ // don't bother mapping again if we already have children
+ return;
+ }
+ child_nodes = new ArrayList();
+ if (title.equals(Dictionary.get("Tree.World")) && Gatherer.GS3) {
+ // the Greenstone collections folder for GS3 - this contains a
+ // folder for each site
+
+ File start = new File(Utility.getSitesDir(Configuration.gsdl3_path));
+ File sites[] = start.listFiles();
+ ArrayTools.sort(sites);
+ for (int i = 0; sites != null && i < sites.length; i++) {
+ File collect_dir = new File(sites[i], "collect");
+ if (!collect_dir.exists()) {
+ continue;
+ }
+
+ WorkspaceTreeNode child = new WorkspaceTreeNode(null, sites[i].getName());
+ child.is_gs3_site_node = true;
+ child.unmap();
+ child.setModel(model);
+ child.setParent(this);
+ child.map();
+ child_nodes.add(child);
+ }
+ model.nodeStructureChanged(this);
+
+ } else if (title.equals(Dictionary.get("Tree.World")) || is_gs3_site_node || is_group_node) {
+ // For each of the children directories, which are collections...
+ File start;
+ if (is_gs3_site_node ) {
+ start = new File(Gatherer.getSitesDirectoryPath() + title + File.separator + "collect" + File.separator);
+ }
+ else if (is_group_node) {
+ start = new File(Gatherer.getCollectDirectoryPath() + title);
+ }
+ else {
+ start = new File(Gatherer.getCollectDirectoryPath());
+ }
+ File cols[] = start.listFiles();
+ ArrayTools.sort(cols);
+
+ // We add their import directories, except for the model collection
+ for (int i = 0; cols != null && i < cols.length; i++) {
+ if (!cols[i].getName().equals(StaticStrings.MODEL_COLLECTION_NAME)) {
+ // check for groups
+ String file_name = (Gatherer.GS3)? Utility.CONFIG_GS3_FILE : Utility.CONFIG_FILE;
+ BasicCollectionConfiguration collect_cfg = new BasicCollectionConfiguration(new File(cols[i], file_name));
+ if (collect_cfg.getCollectGroup().equals("true")) {
+ WorkspaceTreeNode child = new WorkspaceTreeNode(null, cols[i].getName());
+ child.is_group_node = true;
+ child.unmap();
+ child.setModel(model);
+ child.setParent(this);
+ child.map();
+ child_nodes.add(child);
+ } else {
+ File import_dir = new File(cols[i], "import");
+ if (!import_dir.exists()) {
+ continue;
+ }
+
+ WorkspaceTreeNode collection_root = new WorkspaceTreeNode(import_dir, collect_cfg.toString());
+ collection_root.setParent(this);
+ collection_root.setModel(model);
+
+ // One last piece of magic so we can determine the current collection
+ // Now made to work with collection groups
+ String groupQualifiedColName = cols[i].getName();
+ if(is_group_node) { // then title would have stored the colgroup name
+ groupQualifiedColName = title + File.separator + cols[i].getName();
+ }
+ collection_root.is_in_loaded_collection = groupQualifiedColName.equals(CollectionManager.getLoadedCollectionName());
+ child_nodes.add(collection_root);
+ }
+ }
+ }
+ model.nodeStructureChanged(this);
+ }
+ else if (title.equals(Dictionary.get("Tree.Root"))) {
+ // Special Case: "Local Filespace" on Windows -- create a node for each filesystem root (drive letter)
+
+ // Sort the roots into alphabetical order
+ File[] roots = File.listRoots();
+ ArrayTools.sort(roots);
+ for (int i = 0; i < roots.length; i++) {
+ // Only add root if it isn't a floppy drive
+ // this used to cause problems, I don't think it does now...
+ //if (!FileSystemView.getFileSystemView().isFloppyDrive(roots[i])) {
+ child_nodes.add(addChildNode(roots[i]));
+ // }
+ }
+ }
+ } // if file == null
+
+ // General case
+ else {
+ super.map();
+ }
+ }
+
+
+ public String toString()
+ {
+ if (title != null) {
+ return title;
+ }
+
+ return super.toString();
+ }
+
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/WriteNotPermittedException.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/WriteNotPermittedException.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/file/WriteNotPermittedException.java (revision 31635)
@@ -0,0 +1,11 @@
+package org.greenstone.gatherer.file;
+
+import java.lang.Exception;
+
+public class WriteNotPermittedException
+ extends Exception {
+
+ public WriteNotPermittedException(String message) {
+ super(message);
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/Attribute.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/Attribute.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/Attribute.java (revision 31635)
@@ -0,0 +1,122 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: Shaoqun Wu, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gems;
+
+
+/**
+ * Wrapper class for attributes displayed in the table
+ * @author Shaoqun Wu, Greenstone Digital Library, University of Waikato
+ */
+public class Attribute
+{
+ private String name="";
+ private String value="";
+ private String lang="";
+ private boolean isRequired;
+ //private boolean isLanguageDependent;
+
+ public Attribute() {}
+
+ public Attribute(String name, String value, String lang, boolean isRequired /*, boolean isLangDep*/){
+ this.name = name;
+ this.value = value;
+ this.lang = lang;
+ this.isRequired = isRequired;
+ //this.isLanguageDependent = isLangDep;
+ }
+
+ public Attribute(String name, String value, boolean isRequired/*, boolean isLangDep*/){
+ this.name = name;
+ this.value = value;
+ //this.lang = lang;
+ this.isRequired = isRequired;
+ //this.isLanguageDependent = isLangDep;
+ }
+
+ public Attribute(String name, String value){
+ this.name = name;
+ this.value = value;
+ }
+
+ public String getName(){
+ return name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public String getLanguage() {
+ return lang;
+ }
+
+ public boolean isRequired(){
+ return this.isRequired;
+ }
+
+// public boolean isLanguageDependent(){
+// return this.isLanguageDependent;
+// }
+
+ public void setName(String name) {
+ if (name.trim().equals(this.name)) return;
+ this.name = name.trim();
+
+ }
+
+ public void setValue(String value) {
+ if (value.trim().equals(this.value)) return;
+ this.value = value.trim();
+ }
+
+ public void setLanguage(String lang) {
+ if (lang.trim().equals(this.lang)) return;
+ this.lang = lang;
+ }
+
+ public void setRequired(boolean isRequired) {
+ this.isRequired = isRequired;
+ }
+
+// public void setLanguageDependent(boolean isLangDep) {
+// this.isLanguageDependent = isLangDep;
+// }
+
+ public String toString() {
+ return (this.name + " " + this.value +" " + this.lang);
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/AttributeContainer.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/AttributeContainer.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/AttributeContainer.java (revision 31635)
@@ -0,0 +1,183 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: Katherine Don, New Zealand Digital Library Project,
+ * University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gems;
+
+import org.greenstone.gatherer.Configuration;
+
+import java.util.ArrayList;
+
+
+/** this is used as a base class for MetadataSetInfo and MetadataElementModel
+ * Both of thesee have some attributes which are language independent, and
+ * some which are language dependent
+ */
+public class AttributeContainer {
+
+ protected ArrayList attributes;
+ protected ArrayList language_dependent_attributes;
+
+ String [] required_attribute_names = null;
+ String [] language_dependent_attribute_names = null;
+
+ public AttributeContainer() {}
+ public AttributeContainer(String [] required_atts, String []lang_dependent_atts) {
+
+ attributes = new ArrayList();
+ language_dependent_attributes = new ArrayList();
+
+ if (required_atts != null) {
+ required_attribute_names = required_atts;
+ for(int i=0;i
+ *
+ * Author: Shaoqun Wu, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################## ##########################
+ */
+package org.greenstone.gatherer.gems;
+
+public class AttributeEvent{
+ Attribute attribute;
+
+ public AttributeEvent(Attribute attr){
+ attribute = attr;
+
+ }
+
+ public Attribute getSource(){
+ return attribute;
+ }
+
+ //add get and set method
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/AttributeListener.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/AttributeListener.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/AttributeListener.java (revision 31635)
@@ -0,0 +1,43 @@
+
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at th * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: Shaoqun Wu, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################## ##########################
+ */
+package org.greenstone.gatherer.gems;
+
+public interface AttributeListener{
+
+ public void attributeChanged(AttributeEvent ae);
+
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/AttributeTable.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/AttributeTable.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/AttributeTable.java (revision 31635)
@@ -0,0 +1,325 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: Shaoqun Wu, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gems;
+
+import javax.swing.table.*;
+import javax.swing.JTable;
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+import java.awt.Rectangle;
+import javax.swing.JOptionPane;
+import javax.swing.DefaultCellEditor;
+import javax.swing.JTextField;
+import javax.swing.ListSelectionModel;
+
+import org.greenstone.gatherer.Dictionary;
+
+public class AttributeTable
+ extends JTable
+ implements MetadataElementListener, MetadataSetListener, ActionListener {
+
+ // self reference
+ JTable this_table;
+
+ private ArrayList listeners = new ArrayList();
+ private JTextField metadata_value_text_field = new JTextField();
+ private boolean language_dependent;
+
+ public AttributeTable(boolean lang_dependent){
+ super();
+ setRowHeight(20);
+ this.setComponentOrientation(Dictionary.getOrientation());
+ metadata_value_text_field.setComponentOrientation(Dictionary.getOrientation());
+ language_dependent = lang_dependent;
+
+ metadata_value_text_field.setBorder(null);
+ DefaultCellEditor cellEditor = new DefaultCellEditor(metadata_value_text_field);
+ cellEditor.setClickCountToStart(1);
+ setDefaultEditor(String.class,cellEditor);
+ this_table = this;
+ setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ }
+
+ /* not currently used */
+ public void actionPerformed(ActionEvent e){
+ String command = e.getActionCommand();
+
+ if (command.equals(GEMSConstants.ADD_ATTRIBUTE)){
+ addAttribute();
+ return ;
+ }
+
+ if (command.equals(GEMSConstants.DELETE_ATTRIBUTE)){
+ deleteAttribute();
+ }
+
+ }
+
+ public void addNewLanguage(String lang) {
+ AttributeTableModel model = (AttributeTableModel)getModel();
+ model.addNewLanguage(lang);
+ }
+
+ public void metadataElementChanged(MetadataElementEvent mee){
+ MetadataElementModel model = mee.getMetadataElementModel();
+ if (model == null){ //this model has been deleted
+ setModel(new DefaultTableModel());
+ return;
+ }
+ populateAttributeTable(model);
+ }
+
+ public void metadataSetChanged(MetadataSetEvent mse){
+ MetadataSetInfo info = mse.getMetadataSetInfo();
+ if (info == null){
+ setModel(new DefaultTableModel());
+ return;
+ }
+ populateAttributeTable(info);
+ }
+
+ public void addAttributeListener(AttributeListener al){
+ if (!listeners.contains(al)){
+ listeners.add(al);
+ }
+ }
+
+ public void removeAttributeListener(AttributeListener al){
+ listeners.remove(al);
+ }
+
+ private void addAttribute(){
+ AttributeTableModel model = (AttributeTableModel)getModel();
+ model.addNewRow();
+ }
+
+
+ private void deleteAttribute(){
+ int index = getSelectedRow();
+ AttributeTableModel model = (AttributeTableModel)getModel();
+ model.deleteRow(index);
+ }
+
+ private void populateAttributeTable(AttributeContainer container){
+ AttributeTableModel model = new AttributeTableModel(container, language_dependent);
+ setModel(model);
+ TableColumnModel tcm = getColumnModel();
+ if (language_dependent) {
+ tcm.getColumn(0).setPreferredWidth(100);
+ tcm.getColumn(1).setPreferredWidth(50);
+ tcm.getColumn(2).setPreferredWidth(350);
+ }
+ else {
+ tcm.getColumn(0).setPreferredWidth(100);
+ tcm.getColumn(1).setPreferredWidth(400);
+ }
+ }
+
+
+
+ private void notifyListeners(Attribute attr){
+ AttributeEvent ae = new AttributeEvent(attr);
+ for(int i=0;i";
+ // }
+
+ if (column == 0) return attr.getName();
+ if (language_dependent) {
+ if (column == 1) return attr.getLanguage();
+ if (column == 2) return attr.getValue();
+ }
+ else {
+ if (column == 1) return attr.getValue();
+ }
+ }
+ return null;
+ }
+
+ public void setValueAt(Object value, int row, int column){
+ if (row < attributes.size() && attributes.size()>0 ){
+ Attribute attr = (Attribute)attributes.get(row);
+ if (attr.isRequired() && ((String)value).trim().equals("") && !getValueAt(row,column).equals("")) {
+ JOptionPane.showMessageDialog(null,Dictionary.get("GEMS.Attribute_Edition_Error_Message"), Dictionary.get("GEMS.Attribute_Edition_Error"),JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ // TODO: check namespace conflict
+ if (column == 0) attr.setName((String)value);
+ else {
+ if (language_dependent) {
+ if (column == 1) attr.setLanguage((String)value);
+ else if (column == 2) attr.setValue((String)value);
+ }
+ else {
+ if (column == 1) attr.setValue((String)value);
+ }
+ }
+ notifyListeners(attr);
+
+ /// check this stuff
+ if (attribute_container instanceof MetadataElementModel) {
+ if (attr.getName().trim().equals(GEMSConstants.NAME_ATTRIBUTE)) {
+ ((MetadataElementModel)attribute_container).setName((String)value);
+ }
+ } else if (attribute_container instanceof MetadataSetInfo) {
+ ((MetadataSetInfo)attribute_container).infoChanged();
+ }
+ }
+ }
+
+ /** used only for language dependent attribute tables */
+ public void addNewLanguage(String lang)
+ {
+ if (!language_dependent) return;
+ int index = attribute_container.languageExists(lang);
+ if (index >=0){
+ this_table.getSelectionModel().setSelectionInterval(index,index);
+ Rectangle rect = this_table.getCellRect(index, 0, true);
+ this_table.scrollRectToVisible(rect);
+ }
+ else {
+ String [] attrNames = attribute_container.getLanguageDependentAttributeNames();
+ if (attrNames == null){
+ JOptionPane.showMessageDialog(null,Dictionary.get("GEMS.Add_Lang_Depend_Attr_Error_Message"),Dictionary.get("GEMS.Add_Lang_Depend_Attr_Error"),JOptionPane.ERROR_MESSAGE);
+ return ;
+ }
+ for(int i=0;i=0){
+ Attribute attr = (Attribute)attributes.get(index);
+ if (attr.isRequired()){
+ JOptionPane.showMessageDialog(null,Dictionary.get("GEMS.Attribute_Deletion_Error_Message"), Dictionary.get("GEMS.Attribute_Deletion_Error"),JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ // int result = JOptionPane.showOptionDialog(null, Dictionary.get("GEMS.Confirm_Removal", Dictionary.get("GEMS.Attribute") + " " + Dictionary.get("GEMS.Cannot_Undo")), Dictionary.get("GEMS.Confirm_Removal_Title"), JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE,null,GEMSConstants.DIALOG_OPTIONS,GEMSConstants.DIALOG_OPTIONS[0] );
+
+ //if (result !=JOptionPane.OK_OPTION) return;
+ notifyListeners(attr);
+ attributes.remove(index);
+ fireTableDataChanged();
+ }
+
+ }
+
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/DeleteMetadataSetPrompt.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/DeleteMetadataSetPrompt.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/DeleteMetadataSetPrompt.java (revision 31635)
@@ -0,0 +1,281 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: Shaoqun Wu, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gems;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Vector;
+import java.util.HashMap;
+
+
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.gui.ModalDialog;
+import org.greenstone.gatherer.gui.GLIButton;
+
+public class DeleteMetadataSetPrompt
+ extends ModalDialog {
+
+ static private Dimension SIZE = new Dimension(500, 500);
+
+ private ArrayList available_metadata_sets;
+ private ArrayList listeners;
+
+ private JCheckBox confirmation = null;
+ private JButton delete_button = null;
+ private JButton close_button = null;
+ private JList available_set_list = null;
+ /** The model behind the list. */
+ private DefaultListModel list_model = null;
+ private JTextArea description_textarea = null;
+ private DeleteMetadataSetPrompt self;
+ private MetadataSetManager meta_manager;
+
+ public DeleteMetadataSetPrompt(Frame parent,MetadataSetManager msm) {
+ super(parent, true);
+ self = this;
+ meta_manager = msm;
+ listeners = new ArrayList();
+
+ setSize(SIZE);
+ setTitle(Dictionary.get("GEMS.DeleteMetadataSetPrompt.Title"));
+
+ JPanel content_pane = (JPanel) getContentPane();
+ content_pane.setOpaque(true);
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JLabel available_metadata_sets_label = new JLabel(Dictionary.get("GEMS.DeleteMetadataSetPrompt.Available_Sets"));
+ available_metadata_sets_label.setOpaque(true);
+ available_metadata_sets_label.setComponentOrientation(Dictionary.getOrientation());
+
+ list_model = new DefaultListModel();
+
+ available_set_list = new JList();
+ available_set_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ available_set_list.setModel(list_model);
+ available_set_list.setCellRenderer(new MetadatSetListCellRenderer());
+ available_set_list.setFixedCellHeight(20);
+ available_set_list.addListSelectionListener(new MetadataSetListSelectionListener());
+ available_set_list.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel set_pane = new JPanel();
+ set_pane.setComponentOrientation(Dictionary.getOrientation());
+ set_pane.setLayout(new BorderLayout());
+ set_pane.add(available_metadata_sets_label,BorderLayout.NORTH);
+ set_pane.add(new JScrollPane(available_set_list),BorderLayout.CENTER);
+
+
+ JLabel metadata_set_des_label = new JLabel(Dictionary.get("GEMS.Set_Description"));
+ metadata_set_des_label.setOpaque(true);
+
+ description_textarea = new JTextArea();
+ description_textarea.setOpaque(true);
+ description_textarea.setEditable(false);
+ description_textarea.setLineWrap(true);
+ description_textarea.setWrapStyleWord(true);
+ description_textarea.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel des_pane = new JPanel();
+ des_pane.setComponentOrientation(Dictionary.getOrientation());
+ des_pane.setLayout(new BorderLayout());
+ des_pane.add(metadata_set_des_label,BorderLayout.NORTH);
+ des_pane.add(new JScrollPane(description_textarea),BorderLayout.CENTER);
+
+ JPanel button_pane = new JPanel();
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+ delete_button = new GLIButton(Dictionary.get("GEMS.DeleteMetadataSetPrompt.Delete"), Dictionary.get("GEMS.DeleteMetadataSetPrompt.Delete_Tooltip"));
+ delete_button.setEnabled(false);
+
+
+ confirmation = new JCheckBox(Dictionary.get("GEMS.DeleteMetadataSetPrompt.Confirm_Delete"));
+ confirmation.setEnabled(false);
+ confirmation.setSelected(false);
+ confirmation.setComponentOrientation(Dictionary.getOrientation());
+
+ close_button = new GLIButton(Dictionary.get("General.Close"), Dictionary.get("General.Close_Tooltip"));
+ close_button.setEnabled(true);
+
+ confirmation.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent event) {
+ delete_button.setEnabled(confirmation.isSelected());
+ //confirmation.setEnabled(false);
+ //confirmation.setSelected(false);
+ }
+ });
+
+ // Add listeners
+ delete_button.addActionListener(new DeleteButtonListener());
+
+ close_button.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent event) {
+ self.dispose();
+ }
+ });
+
+
+ button_pane.setLayout(new GridLayout(1,2));
+ button_pane.add(delete_button);
+ button_pane.add(close_button);
+
+ JPanel bottom_pane = new JPanel();
+ bottom_pane.setComponentOrientation(Dictionary.getOrientation());
+ bottom_pane.setLayout(new BorderLayout());
+ bottom_pane.add(confirmation,BorderLayout.NORTH);
+ bottom_pane.add(button_pane, BorderLayout.CENTER);
+
+
+ content_pane.setLayout(new BorderLayout());
+ content_pane.add(set_pane, BorderLayout.NORTH);
+ content_pane.add(des_pane, BorderLayout.CENTER);
+ content_pane.add(bottom_pane, BorderLayout.SOUTH);
+
+ // Show
+ Dimension screen_size = Configuration.screen_size;
+ setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
+ setVisible(false);
+ }
+
+
+ public void display() {
+ available_metadata_sets = meta_manager.getAvailableMetadataSets();
+ for (int i=0; iActionEvent containing all the relevant information garnered from the event itself.
+ * @see org.greenstone.gatherer.Configuration
+ * @see org.greenstone.gatherer.Gatherer
+ * @see org.greenstone.gatherer.util.Utility
+ */
+ public void actionPerformed(ActionEvent event) {
+ // Delete the selected MetadataSet.
+ Object selectedValue = available_set_list.getSelectedValue();
+
+ if (selectedValue !=null && (selectedValue instanceof MetadataSetInfo)){
+ ((MetadataSetInfo)selectedValue).deleteMetadataSet();
+ list_model.removeElement(selectedValue);
+ }
+ delete_button.setEnabled(false);
+ confirmation.setEnabled(false);
+ confirmation.setSelected(false);
+ description_textarea.setText(Dictionary.get("GEMS.DeleteMetadataSetPrompt.No_Set"));
+ //self.dispose();
+ }
+
+ }
+
+
+ private class MetadatSetListCellRenderer extends JLabel implements ListCellRenderer {
+ public MetadatSetListCellRenderer() {
+ setOpaque(true);
+ this.setComponentOrientation(Dictionary.getOrientation());
+ }
+
+ public Component getListCellRendererComponent(JList list,
+ Object value,
+ int index,
+ boolean isSelected,
+ boolean cellHasFocus)
+ {
+ String name= "unknown";
+
+ if (value instanceof MetadataSetInfo){
+ MetadataSetInfo meta_info = (MetadataSetInfo) value;
+ name = meta_info.getMetadataSetName();
+ }
+
+ setText(name);
+ if (isSelected) {
+ setBackground(list.getSelectionBackground());
+ setForeground(list.getSelectionForeground());
+ }
+ else {
+ setBackground(list.getBackground());
+ setForeground(list.getForeground());
+ }
+
+ return this;
+ }
+ }
+
+
+ private class MetadataSetListSelectionListener implements ListSelectionListener {
+ public void valueChanged(ListSelectionEvent lse){
+ if (lse.getValueIsAdjusting()) return;
+
+ delete_button.setEnabled(false);
+
+ Object selectedValue = available_set_list.getSelectedValue();
+
+ if (selectedValue !=null && (selectedValue instanceof MetadataSetInfo)){
+ MetadataSetInfo meta_info = (MetadataSetInfo)selectedValue;
+ description_textarea.setText(meta_info.getMetadataSetDescription());
+ confirmation.setEnabled(true);
+ confirmation.setSelected(false);
+ }
+ else {
+ confirmation.setEnabled(false);
+ description_textarea.setText(Dictionary.get("GEMS.DeleteMetadataSetPrompt.No_Set"));
+ }
+
+ }
+ }
+
+
+
+ public void addMetadataSetListener(MetadataSetListener msl){
+ listeners.add(msl);
+ }
+
+
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/GEMS.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/GEMS.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/GEMS.java (revision 31635)
@@ -0,0 +1,591 @@
+ /**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at th * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: Shaoqun Wu, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################## ##########################
+ */
+package org.greenstone.gatherer.gems;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import javax.swing.*;
+import java.lang.String;
+import javax.swing.event.*;
+import javax.swing.filechooser.*;
+import javax.swing.text.*;
+import javax.swing.tree.*;
+import java.awt.*;
+import java.awt.event.*;
+
+import java.io.File;
+
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.GetOpt;
+import org.greenstone.gatherer.gui.GComboBox;
+import org.greenstone.gatherer.gui.GLIButton;
+import org.greenstone.gatherer.gui.HelpFrame;
+import org.greenstone.gatherer.gui.ModalDialog;
+import org.greenstone.gatherer.gui.NonWhitespaceField;
+import org.greenstone.gatherer.util.Codec;
+import org.greenstone.gatherer.util.JarTools;
+import org.greenstone.gatherer.util.Utility;
+import org.greenstone.gatherer.util.XMLTools;
+import org.greenstone.gatherer.Dictionary;
+import org.w3c.dom.*;
+
+
+/*
+ * @author Shaoqun Wu, Greenstone Digital Library, University of Waikato
+ * @version 2.4
+ */
+public class GEMS
+ extends JFrame implements WindowFocusListener
+{
+
+ static final private Dimension SIZE = new Dimension(800,550);
+
+ // we have a card pane to switch between the no set loaded view, and the
+ // set loaded view
+ private CardLayout card_layout = null;
+ private JPanel card_pane = null;
+ /** The name of the panel containing the "set loaded" card. */
+ private String SET_LOADED_CARD = "";
+ /** The name of the panel containing the "no set loaded" placeholder */
+ private String NO_SET_LOADED_CARD = "No set loaded";
+
+ private GEMS self = null;
+
+ private OpenMetadataSetPrompt open_prompt;
+ private DeleteMetadataSetPrompt delete_prompt;
+ private NewMetadataSetPrompt new_prompt;
+
+ // this is the main panel that gets switched in once a set is loaded
+ private JSplitPane metadata_set_details_split_pane = null;
+ // this is the split with the attribute tables in it
+ private JSplitPane attribute_tables_split_pane = null;
+
+ /** Um, the size of the screen I'd guess. */
+ private Dimension screen_size = null;
+
+ private MetadataSetManager msm;
+
+ private AttributeTable attribute_table; // meta_element_table
+ private AttributeTable language_dependent_attribute_table; //lang_element_table;
+
+ private MetadataSetTree meta_set_tree;
+
+ private MetadataSetModel metadata_set_model;
+
+ private boolean stand_alone = true;
+
+ private ArrayList listeners;
+
+ static public void main(String[] args)
+ {
+ //TODO: add an option to open a paticular metadata set
+
+ // Parse arguments
+ GetOpt go = new GetOpt(args);
+
+ if (go.debug) {
+ DebugStream.enableDebugging();
+
+ Calendar now = Calendar.getInstance();
+ String debug_file_path = "debug" + now.get(Calendar.DATE) + "-" + now.get(Calendar.MONTH) + "-" + now.get(Calendar.YEAR) + ".txt";
+
+ // Debug file is created in the GLI directory
+ DebugStream.println("Debug file path: " + debug_file_path);
+ DebugStream.setDebugFile(debug_file_path);
+
+ }
+
+ new GEMS(go.gsdl_path,go.gsdl3_path,go.metadata_path,true,go.new_set);
+
+ }
+
+
+ /** Constructor.
+ */
+ public GEMS(String gsdl_path, String gsdl3_path, String metadata_path, boolean standalone, boolean new_set)
+ {
+ self = this;
+ JarTools.initialise(this);
+ screen_size = Configuration.screen_size;
+ msm = new MetadataSetManager(gsdl_path,gsdl3_path);
+ stand_alone = standalone;
+ listeners = new ArrayList();
+
+ addWindowListener(new WindowAdapter(){
+ public void windowClosing(WindowEvent e){
+ metadata_set_model.save(true);
+ if (stand_alone){
+ System.exit(0);
+ }
+ else {
+ notifyListeners();
+ setVisible(false);
+ }
+ }
+ });
+
+ setSize(SIZE);
+ setTitle(Dictionary.get("GEMS.Title"));
+ setJMenuBar(new GEMSMenuBar());
+
+ addWindowFocusListener(this);
+
+ card_layout = new CardLayout();
+ card_pane = new JPanel();
+
+ metadata_set_model = new MetadataSetModel(msm);
+ msm.setMetadataSetModel(metadata_set_model);
+ meta_set_tree = new MetadataSetTree(self);
+ metadata_set_model.addObserver(meta_set_tree);
+
+ attribute_table = new AttributeTable(false);
+ MetadataElementModel mem = new MetadataElementModel();
+ mem.addMetadataElementListener((MetadataElementListener)attribute_table);
+ attribute_table.addAttributeListener(metadata_set_model);
+
+ language_dependent_attribute_table = new AttributeTable(true);
+ mem.addMetadataElementListener((MetadataElementListener)language_dependent_attribute_table);
+ language_dependent_attribute_table.addAttributeListener(metadata_set_model);
+
+ MetadataSetInfo msti= new MetadataSetInfo();
+ msti.addMetadataSetListener((MetadataSetListener)attribute_table);
+ msti.addMetadataSetListener((MetadataSetListener)language_dependent_attribute_table);
+ msti.setMetadataSetModel(metadata_set_model);
+
+ open_prompt = new OpenMetadataSetPrompt(self,msm);
+ open_prompt.addMetadataSetListener((MetadataSetListener)metadata_set_model);
+ open_prompt.addMetadataSetListener((MetadataSetListener)attribute_table);
+ open_prompt.addMetadataSetListener((MetadataSetListener)language_dependent_attribute_table);
+
+ delete_prompt = new DeleteMetadataSetPrompt(self,msm);
+ delete_prompt.addMetadataSetListener((MetadataSetListener)metadata_set_model);
+ delete_prompt.addMetadataSetListener((MetadataSetListener)attribute_table);
+ delete_prompt.addMetadataSetListener((MetadataSetListener)language_dependent_attribute_table);
+
+ new_prompt = new NewMetadataSetPrompt(self,msm);
+ new_prompt.addMetadataSetListener((MetadataSetListener)metadata_set_model);
+ new_prompt.addMetadataSetListener((MetadataSetListener)attribute_table);
+ new_prompt.addMetadataSetListener((MetadataSetListener)language_dependent_attribute_table);
+
+
+ // load the initial metadataset
+ if (metadata_path !=null && !metadata_path.equals("")){
+ open_prompt.openMetadataSet(metadata_path);
+ }
+ else{
+ if (new_set) new_prompt.display();
+ }
+
+ // the set tree
+ JScrollPane treePane = new JScrollPane(meta_set_tree);
+ // the language independent attributes
+ JScrollPane tablePane = new JScrollPane(attribute_table);
+ // the language dependent attributes
+ JScrollPane langTablePane = new JScrollPane(language_dependent_attribute_table);
+
+ // no set loaded pane
+ JPanel no_set_loaded_pane = new JPanel();
+ no_set_loaded_pane.setBackground(Color.lightGray);
+ JLabel no_set_loaded_label = new JLabel(Dictionary.get("GEMS.No_Set_Loaded"));
+ no_set_loaded_label.setHorizontalAlignment(JLabel.CENTER);
+ no_set_loaded_label.setVerticalAlignment(JLabel.CENTER);
+
+ no_set_loaded_pane.setLayout(new BorderLayout());
+ no_set_loaded_pane.add(no_set_loaded_label, BorderLayout.CENTER);
+
+ JPanel buttonPane = new JPanel(new GridLayout(1,2));
+
+ JButton arrow_up_button = new GLIButton(Dictionary.get("GEMS.Move_Up"));
+ arrow_up_button.setIcon(JarTools.getImage("arrow-up.gif"));
+
+ JButton arrow_down_button = new GLIButton(Dictionary.get("GEMS.Move_Down"));
+ arrow_down_button.setIcon(JarTools.getImage("arrow-down.gif"));
+
+ arrow_up_button.setActionCommand(GEMSConstants.MOVE_UP);
+ arrow_up_button.addActionListener(meta_set_tree);
+ arrow_down_button.addActionListener(meta_set_tree);
+ arrow_down_button.setActionCommand(GEMSConstants.MOVE_DOWN);
+
+ buttonPane.add(arrow_up_button);
+ buttonPane.add(arrow_down_button);
+
+ JPanel leftPane = new JPanel(new BorderLayout());
+ leftPane.add(treePane,BorderLayout.CENTER);
+ leftPane.add(buttonPane,BorderLayout.SOUTH);
+
+ JLabel selected_language_label = new JLabel(Dictionary.get("GEMS.SelectedLanguage"));
+ selected_language_label.setOpaque(true);
+
+ Vector language_vector = new Vector(msm.getLanguageList());
+ language_vector.add(0, Dictionary.get("GEMS.Language"));
+ JComboBox language_combo = new JComboBox(language_vector);
+
+ language_combo.addActionListener(new ActionListener(){
+ public void actionPerformed(ActionEvent e){
+ JComboBox language_combo = (JComboBox) e.getSource();
+ String langInfo = (String)language_combo.getSelectedItem();
+ if (!langInfo.equals(Dictionary.get("GEMS.Language"))) {
+ String lang = langInfo.split("\\s")[0];
+ language_dependent_attribute_table.addNewLanguage(lang);
+ }
+ }
+
+ });
+
+ JLabel language_label = new JLabel(Dictionary.get("GEMS.LanguageDependent"));
+ language_label.setOpaque(true);
+
+
+ JPanel selectLangPane = new JPanel(new BorderLayout(5,5));
+ selectLangPane.add(selected_language_label,BorderLayout.WEST);
+ selectLangPane.add(language_combo, BorderLayout.CENTER);
+
+
+ JPanel languagePane = new JPanel(new BorderLayout(5,5));
+ languagePane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ languagePane.add(language_label,BorderLayout.NORTH);
+ languagePane.add(selectLangPane,BorderLayout.CENTER);
+
+
+ JPanel languageAttributePane = new JPanel(new BorderLayout());
+ languageAttributePane.add(languagePane,BorderLayout.NORTH);
+ languageAttributePane.add(langTablePane,BorderLayout.CENTER);
+
+ JLabel attribute_label = new JLabel(Dictionary.get("GEMS.Attribute_Table"));
+ attribute_label.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ JPanel mainAttributePane = new JPanel(new BorderLayout());
+ mainAttributePane.add(attribute_label, BorderLayout.NORTH);
+ mainAttributePane.add(tablePane, BorderLayout.CENTER);
+
+ attribute_tables_split_pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,mainAttributePane,languageAttributePane);
+
+ metadata_set_details_split_pane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,leftPane,attribute_tables_split_pane);
+
+ card_pane.setLayout(card_layout);
+ card_pane.add(no_set_loaded_pane, NO_SET_LOADED_CARD);
+ card_pane.add(metadata_set_details_split_pane, SET_LOADED_CARD);
+
+ getContentPane().add(card_pane,BorderLayout.CENTER);
+ setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
+ if (stand_alone)
+ setVisible(true);
+ }
+
+ public void setVisible(boolean visible) {
+ super.setVisible(visible);
+ if (visible) {
+ attribute_tables_split_pane.setDividerLocation(0.3);
+ }
+ }
+
+ // called from GLI
+ public void displayMetadataSet(String metadata_path){
+ open_prompt.openMetadataSet(metadata_path);
+ updateCardLayout(true);
+ setVisible(true);
+ }
+
+ // called from GLI
+ public void newMetadataSet(){
+ new_prompt.display();
+ if (!new_prompt.isCancelled()) {
+ updateCardLayout(true);
+ setVisible(true);
+ }
+
+ }
+
+ public void addGEMSListener(GEMSListener listener){
+ listeners.add(listener);
+ }
+
+ public void removeGEMSListener(GEMSListener listener){
+ listeners.remove(listener);
+ }
+
+ public void notifyListeners(){
+ for (int i=0;i New
+ if (event_source == file_new) {
+ new_prompt.display();
+ if (!new_prompt.isCancelled()) {
+ updateCardLayout(true);
+ }
+ return;
+ }
+
+ // File -> Open
+ if (event_source == file_open) {
+ open_prompt.display();
+ if (!open_prompt.isCancelled()) {
+ updateCardLayout(true);
+ }
+ return;
+ }
+
+ // File -> Close
+ if (event_source == file_close) {
+ metadata_set_model.save(true);
+ updateCardLayout(false);
+ return;
+ }
+ // File -> Delete
+ if (event_source == file_delete) {
+ delete_prompt.display();
+ return;
+ }
+
+
+ // File -> Save
+ if (event_source == file_save) {
+ metadata_set_model.save(false);
+ return;
+ }
+
+ // File -> Exit
+ if (event_source == file_exit) {
+ metadata_set_model.save(true);
+ if (stand_alone){
+ System.exit(0);
+ }
+ else {
+ self.notifyListeners();
+ self.setVisible(false);
+ return;
+ }
+ }
+
+ /// File -> Preferences
+ // if(event_source == file_preferences){
+ // GEMSPreferences GemsPreferences = new GEMSPreferences();
+ //}
+
+ // Edit -> Cut
+ if (event_source == edit_cut) {
+ try {
+ KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
+ // Get the component with selected text as a JTextComponent
+ JTextComponent text = (JTextComponent) kfm.getPermanentFocusOwner();
+ // Cut the text to the clipboard
+ text.cut();
+ }
+ catch (ClassCastException cce) {
+ // If the component is not a text component ignore the cut command
+ DebugStream.println(cce.toString());
+ }
+ return;
+ }
+
+ // Edit -> Copy
+ if (event_source == edit_copy) {
+ try {
+ KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
+ // Get the component with selected text as a JTextComponent
+ JTextComponent text = (JTextComponent) kfm.getPermanentFocusOwner();//getFocusOwner();
+ text.copy();
+ }
+ catch (Exception cce) {
+ // If the component is not a text component ignore the copy command
+ DebugStream.println(cce.toString());
+ }
+ return;
+ }
+
+ // Edit -> Paste
+ if (event_source == edit_paste) {
+ try {
+ KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
+ // Get the component with selected text as a JTextComponent
+ JTextComponent text = (JTextComponent) kfm.getPermanentFocusOwner();
+ // Cut the text to the clipboard
+ text.paste();
+ }
+ catch (ClassCastException cce) {
+ // If the component is not a text component ignore the paste command
+ DebugStream.println(cce.toString());
+ }
+ return;
+ }
+
+ }
+ }
+
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/GEMSConstants.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/GEMSConstants.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/GEMSConstants.java (revision 31635)
@@ -0,0 +1,94 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: Shaoqun Wu, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gems;
+
+import org.greenstone.gatherer.Dictionary;
+
+public class GEMSConstants
+{
+
+ static String NAMESPACE_ATTRIBUTE = "namespace";
+ static String NAME_ATTRIBUTE = "name";
+ static String DESCRIPTION_ATTRIBUTE = "description";
+ static String CODE_ATTRIBUTE = "code";
+ static String LANGUAGE_ATTRIBUTE = "language";
+ static String SET_LANGUAGE_ELEMENT = "SetLanguage";
+ static String LANGUAGE_ELEMENT = "Language";
+ static String NAME_ELEMENT = "Name";
+ static String DESCRIPTION_ELEMENT = "Description";
+ static String DEFAULT_LANGUAGE ="en";
+ static String ATTRIBUTE_ELEMENT="Attribute";
+ static String ELEMENT_ELEMENT="Element";
+ static String METADATASET_ELEMENT="MetadataSet";
+ static String ADD_ATTRIBUTE = "addAttribute";
+ static String DELETE_ATTRIBUTE = "deleteAttribute";
+ static String ADD_ELEMENT = "addElement";
+ static String ADD_SUBELEMENT = "addSubElement";
+ static String DELETE_ELEMENT = "deleteElement";
+ static int METADATA_INFO = 0;
+ static int METADATA_ELEMENT = 0;
+ static String NEW_ELEMENT = "ELEMENT";
+ static String MOVE_UP = "moveUp";
+ static String MOVE_DOWN = "moveDown";
+ static String EXTRACTED_METADATA_NAMESPACE = "ex";
+
+ static String[] SET_REQUIRED_ATTRIBUTES = new String[]{"contact","creator","family","lastchanged","namespace"};
+ static String[] SET_LANG_DEPEND_ATTR_NAMES = new String[]{"name","description"};
+
+ static String UNKNOWN_NAME="unknown";
+
+ static String MATADATASET_NAME="MetadataSet";
+
+ static String SYSTEM_ID = "http://www.greenstone.org/dtd/MetadataSet/1.0/MetadataSet.dtd";
+
+ static String[] DIALOG_OPTIONS = new String[]{Dictionary.get("General.OK"),Dictionary.get("General.Cancel")};
+
+ static String LABEL_ATTRIBUTE = "label";
+ static String DEFINITION_ATTRIBUTE = "definition";
+ static String COMMENT_ATTRIBUTE = "comment";
+
+ static String [] ELEMENT_REQUIRED_ATTRIBUTES = new String[] {"name"};
+
+ static String [] ELEMENT_LANG_DEPEND_ATTR_NAMES = new String[] {LABEL_ATTRIBUTE, DEFINITION_ATTRIBUTE, COMMENT_ATTRIBUTE};
+
+ static String [] ATTRIBUTES_COLUMN_NAMES = new String [] {
+ Dictionary.get("GEMS.AttributeTable.Name"), Dictionary.get("GEMS.AttributeTable.Value")};
+ static String [] LANG_DEPENDENT_ATTRIBUTES_COLUMN_NAMES = new String [] {
+ Dictionary.get("GEMS.AttributeTable.Name"), Dictionary.get("GEMS.AttributeTable.Language"), Dictionary.get("GEMS.AttributeTable.Value")};
+
+ static String METADATA_SET_FILE_EXTENSION = ".mds";
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/GEMSListener.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/GEMSListener.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/GEMSListener.java (revision 31635)
@@ -0,0 +1,43 @@
+
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at th * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: Shaoqun Wu, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################## ##########################
+ */
+package org.greenstone.gatherer.gems;
+
+public interface GEMSListener{
+
+ public void gemsIsClosed();
+
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/MetadataElementEvent.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/MetadataElementEvent.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/MetadataElementEvent.java (revision 31635)
@@ -0,0 +1,50 @@
+
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at th * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: Shaoqun Wu, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################## ##########################
+ */
+package org.greenstone.gatherer.gems;
+
+public class MetadataElementEvent{
+ private MetadataElementModel meta_element;
+
+ public MetadataElementEvent(MetadataElementModel mee){
+ meta_element = mee;
+
+ }
+
+ public MetadataElementModel getMetadataElementModel(){
+ return meta_element;
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/MetadataElementListener.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/MetadataElementListener.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/MetadataElementListener.java (revision 31635)
@@ -0,0 +1,43 @@
+
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at th * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: Shaoqun Wu, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################## ##########################
+ */
+package org.greenstone.gatherer.gems;
+
+public interface MetadataElementListener{
+
+ public void metadataElementChanged(MetadataElementEvent mse);
+
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/MetadataElementModel.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/MetadataElementModel.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/MetadataElementModel.java (revision 31635)
@@ -0,0 +1,350 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: Shaoqun Wu, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gems;
+
+import org.greenstone.gatherer.util.XMLTools;
+import org.greenstone.gatherer.Configuration;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Text;
+import org.w3c.dom.Node;
+
+import java.util.Observable ;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * @author Shaoqun Wu, Greenstone Digital Library, University of Waikato
+ * @version 2.4
+ */
+public class MetadataElementModel
+ extends AttributeContainer
+{
+ private Element metadata_element;
+ private ArrayList children;
+ private MetadataElementModel parent;
+ private static ArrayList listeners = new ArrayList();
+ private MetadataSetInfo metadataSetInfo;
+ private String name;
+ private static int count = 0;
+
+ public MetadataElementModel(){}
+
+ // do we need this one?
+ public MetadataElementModel(MetadataSetInfo info){
+ this(info, null);
+ }
+
+ public MetadataElementModel(MetadataSetInfo info, String new_name){
+ super(null, GEMSConstants.ELEMENT_LANG_DEPEND_ATTR_NAMES);
+ required_attribute_names = GEMSConstants.ELEMENT_REQUIRED_ATTRIBUTES;
+ metadataSetInfo = info;
+ children = new ArrayList();
+ if (new_name != null) {
+ name = new_name;
+ } else {
+ name = GEMSConstants.NEW_ELEMENT+(count++);
+ }
+ // add name in as an attribute - lang independent
+ Attribute attr = new Attribute(GEMSConstants.NAME_ATTRIBUTE, name, true);
+ attributes.add(attr);
+ // also add in a default label attribute
+ attr = getAttributeByNameAndLanguage(GEMSConstants.LABEL_ATTRIBUTE, Configuration.getLanguage());
+ attr.setValue(name);
+
+ }
+
+ public MetadataElementModel(Element metadataElement, MetadataSetInfo info){
+ super(null, null);
+ required_attribute_names = GEMSConstants.ELEMENT_REQUIRED_ATTRIBUTES;
+ language_dependent_attribute_names = GEMSConstants.ELEMENT_LANG_DEPEND_ATTR_NAMES;
+ metadataSetInfo = info;
+ metadata_element = metadataElement;
+
+ children = new ArrayList();
+
+ constructModel(metadata_element);
+
+ ArrayList child_elements =XMLTools.getChildElementsByTagName(metadata_element,GEMSConstants.ELEMENT_ELEMENT);
+
+ for(int i=0;i=0){
+ children.remove(child);
+ children.add(index-1,child);
+ return true;
+ }
+ return false;
+ }
+
+ public boolean moveDown(MetadataElementModel child){
+ metadataSetInfo.getMetadataSetModel().setChanged(true);
+ int index = children.indexOf(child);
+ if (index+1 < children.size()){
+ children.remove(child);
+ children.add(index+1,child);
+ return true;
+ }
+
+ return false;
+ }
+
+ public void save(){
+ saveElement();
+
+ for(int i=0;i
+ *
+ * Author: Shaoqun Wu, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################## ##########################
+ */
+package org.greenstone.gatherer.gems;
+
+public class MetadataSetEvent{
+ private MetadataSetInfo meta_info;
+
+ public MetadataSetEvent(MetadataSetInfo info){
+ meta_info = info;
+
+ }
+
+ public MetadataSetInfo getMetadataSetInfo(){
+ return meta_info;
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/MetadataSetInfo.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/MetadataSetInfo.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/MetadataSetInfo.java (revision 31635)
@@ -0,0 +1,333 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: Shaoqun Wu, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gems;
+
+import org.apache.xerces.dom.*;// for new Documents
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.Attr;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.NodeList;
+
+import java.util.HashMap;
+import java.util.ArrayList;
+
+import org.greenstone.gatherer.util.XMLTools;
+
+/**
+ * @author Shaoqun Wu, Greenstone Digital Library, University of Waikato
+ */
+public class MetadataSetInfo
+ extends AttributeContainer
+{
+
+ private String file_path="";
+
+ private static ArrayList listeners = new ArrayList();
+
+ private static MetadataSetModel metadata_model;
+
+ private String current_language = GEMSConstants.DEFAULT_LANGUAGE;
+
+ private boolean isNew = false;
+
+ public MetadataSetInfo(){
+ super(GEMSConstants.SET_REQUIRED_ATTRIBUTES, GEMSConstants.SET_LANG_DEPEND_ATTR_NAMES);
+ }
+
+ public boolean isNamespaceAlreadyUsed(String namespace){
+ if(metadata_model != null) {
+ return metadata_model.getMetadataSetManager().isNamespaceAlreadyUsed(namespace);
+ }
+ return false;
+ }
+
+ public void setNew(boolean isNew){
+ this.isNew = isNew;
+ }
+
+ public boolean isNew(){
+ return isNew;
+ }
+
+ public Document getMetadataSetDocument(){
+ return metadata_model.getMetadataSetDocument();
+ }
+
+ public MetadataSetModel getMetadataSetModel(){
+ return metadata_model;
+ }
+
+ public void setMetadataSetModel(MetadataSetModel model){
+ metadata_model = model;
+ }
+
+
+ public String getMetadataSetName(){
+ return getMetadataSetName(getCurrentLanguage());
+ }
+
+ public String getMetadataSetName(String lang){
+ Attribute attr = getAttributeByNameAndLanguage(GEMSConstants.NAME_ATTRIBUTE,lang);
+ if (attr != null) {
+ return attr.getValue();
+ }
+ else {
+ return GEMSConstants.UNKNOWN_NAME;
+ }
+ }
+
+ public void setMetadataSetName(String name){
+ setMetadataSetName(name, getCurrentLanguage());
+ }
+
+ public void setMetadataSetName(String name,String lang){
+ Attribute attr = getAttributeByNameAndLanguage(GEMSConstants.NAME_ATTRIBUTE,lang);
+ if (attr != null){
+ attr.setValue(name);
+ }
+ else {
+ attr = new Attribute(GEMSConstants.NAME_ATTRIBUTE, name, lang, true);
+ language_dependent_attributes.add(attr);
+ }
+ }
+
+
+
+ public String getMetadataSetDescription(){
+ return getMetadataSetDescription(getCurrentLanguage());
+ }
+
+ public String getMetadataSetDescription(String lang){
+ Attribute attr = getAttributeByNameAndLanguage(GEMSConstants.DESCRIPTION_ATTRIBUTE,lang);
+ if (attr != null) {
+ return attr.getValue();
+ }
+ else {
+ return "";
+ }
+ }
+
+ public void setMetadataSetDescription(String desc) {
+ setMetadataSetDescription(desc, getCurrentLanguage());
+ }
+
+ public void setMetadataSetDescription(String desc, String lang) {
+ Attribute attr = getAttributeByNameAndLanguage(GEMSConstants.DESCRIPTION_ATTRIBUTE,lang);
+ if (attr != null){
+ attr.setValue(desc);
+ }
+ else {
+ attr = new Attribute(GEMSConstants.DESCRIPTION_ATTRIBUTE, desc, lang, true);
+ language_dependent_attributes.add(attr);
+ }
+ }
+
+ public String getCurrentLanguage(){
+ return current_language;
+ }
+
+ public void setCurrentLanguage(String lang){
+ current_language = lang;
+ }
+
+ public String getNamespace(){
+ Attribute attr = getAttributeByName(GEMSConstants.NAMESPACE_ATTRIBUTE);
+ if (attr != null){
+ return attr.getValue();
+ }
+ else{
+ return "";
+ }
+ }
+
+ public void setNamespace(String namespace){
+ Attribute attr = getAttributeByName(GEMSConstants.NAMESPACE_ATTRIBUTE);
+ if (attr != null){
+ attr.setValue(namespace);
+ }
+ else {
+ attr = new Attribute(GEMSConstants.NAMESPACE_ATTRIBUTE, namespace, true);
+ attributes.add(attr);
+ }
+
+ }
+
+
+ public String getFilePath(){
+ return file_path;
+ }
+
+
+ public void infoChanged(){
+ metadata_model.valueChanged();
+ }
+
+ public void setFilePath(String path){
+ file_path = path;
+ }
+
+
+ public void addMetadataSetListener(MetadataSetListener msl){
+ if (!listeners.contains(msl))
+ listeners.add(msl);
+ }
+
+ public void removeMetadataSetListener(MetadataSetListener msl){
+ listeners.remove(msl);
+ }
+
+ public void removeAllMetadataSetListeners(){
+ listeners.clear();
+ }
+
+ public void deleteMetadataSet(){
+ metadata_model.getMetadataSetManager().deleteMetadataSet(this);
+
+ if (metadata_model.getMetadataSetInfo() != null
+ && metadata_model.getMetadataSetInfo().getFilePath().equals(this.getFilePath())){
+ metadata_model.metadataSetDeleted();
+ MetadataSetEvent mse = new MetadataSetEvent(null);
+ for(int i=0;i=0;--i){
+ Node set_language = setLanguages.item(i);
+ doc_element.removeChild(set_language);
+ }
+
+ ArrayList names = XMLTools.getChildElementsByTagName(doc_element,GEMSConstants.NAME_ELEMENT);
+ for(int i=0;i< names.size();i++){
+ Element name_element = (Element)names.get(i);
+ doc_element.removeChild(name_element);
+ }
+
+ ArrayList deses = XMLTools.getChildElementsByTagName(doc_element,GEMSConstants.DESCRIPTION_ELEMENT);
+ for(int i=0;i< deses.size();i++){
+ Element des_element = (Element)deses.get(i);
+ doc_element.removeChild(des_element);
+ }
+
+ Node first_child = doc_element.getFirstChild();
+
+ Node previous_element = null;
+
+ //add setLanguage elements
+ ArrayList langs = getAllLanguages();
+ for (int i=0;i
+ *
+ * Author: Shaoqun Wu, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################## ##########################
+ */
+package org.greenstone.gatherer.gems;
+
+public interface MetadataSetListener{
+
+ public void metadataSetChanged(MetadataSetEvent mse);
+
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/MetadataSetManager.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/MetadataSetManager.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/MetadataSetManager.java (revision 31635)
@@ -0,0 +1,385 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: Shaoqun Wu, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gems;
+
+import java.io.*;
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+import java.util.ArrayList;
+
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.util.Utility;
+import org.greenstone.gatherer.file.*;
+import org.greenstone.gatherer.util.XMLTools;
+import org.greenstone.gatherer.Dictionary;
+
+import org.apache.xerces.parsers.*;
+import org.apache.xml.serialize.*;
+import org.w3c.dom.*;
+import org.xml.sax.*;
+
+
+
+/** This class is responsible for managing all the metadata sets in the collection and for providing methods for manipulating the aforementioned sets contents.
+ * @author Shaoqun Wu, Greenstone Digital Library, University of Waikato
+ * @version 2.4
+ */
+public class MetadataSetManager
+{
+
+ private ArrayList mds_list = new ArrayList();
+
+ private MetadataSetModel metadata_set_model;
+
+ private ArrayList languageList;
+
+ private String current_language = GEMSConstants.DEFAULT_LANGUAGE;
+
+ /** Constructor. */
+ public MetadataSetManager(String gsdl_path, String gsdl3_path) {
+ // Determine the GLI user directory path
+ String gli_user_directory_path = System.getProperty("user.home") + File.separator;
+
+ if (Utility.isWindows()) {
+ gli_user_directory_path += "Application Data" + File.separator + "Greenstone" + File.separator + "GLI" + File.separator;
+ }
+ else {
+ gli_user_directory_path += ".gli" + File.separator;
+ }
+
+ new Configuration(gli_user_directory_path, gsdl_path, gsdl3_path, null, null, null);
+ current_language = Configuration.getLanguage();
+
+ languageList = new ArrayList();
+ }
+
+ public ArrayList getLanguageList(){
+ //already loaded
+ if (languageList.size() > 0 ) return languageList;
+
+ Document document = XMLTools.parseXMLFile(getLanguageXMLPath(), true);
+
+ NodeList languages = document.getDocumentElement().getElementsByTagName(GEMSConstants.LANGUAGE_ELEMENT);
+ for(int i=0; i< languages.getLength();i++){
+ Element language_element = (Element)languages.item(i);
+ languageList.add(language_element.getAttribute(GEMSConstants.CODE_ATTRIBUTE).toLowerCase()+" "+language_element.getAttribute(GEMSConstants.NAME_ATTRIBUTE));
+ }
+
+ return languageList;
+ }
+
+
+ public boolean isAttributeRequired(String name){
+ String tmp_name = name.trim();
+ for(int i=0;i0){
+ info.setCurrentLanguage(((Attribute)attrs.get(0)).getLanguage());
+ }
+
+ info.setFilePath(metadata_path);
+ return info;
+
+ }
+
+ public void deleteMetadataSet(MetadataSetInfo info){
+ String filepath = info.getFilePath();
+
+ if(filepath != null && !filepath.trim().equals("")){
+ File file = new File(filepath);
+
+ boolean deleted = file.delete();
+ if (!deleted){
+ JOptionPane.showMessageDialog(null,Dictionary.get("GEMS.File_Deletion_Error_Message"), Dictionary.get("GEMS.File_Deletion_Error"),JOptionPane.ERROR_MESSAGE);
+ }
+ else{
+ for(int i=0;i elements
+ NodeList setLanguages = document.getDocumentElement().getElementsByTagName(GEMSConstants.SET_LANGUAGE_ELEMENT);
+
+ if (setLanguages.getLength() >0){
+ for(int i=0;i 0){
+ Element name_element = (Element)names.get(0);
+ Attribute attr = new Attribute(GEMSConstants.NAME_ATTRIBUTE,XMLTools.getElementTextValue(name_element), true);
+ if (lang == null){
+ attr.setLanguage(name_element.getAttribute(GEMSConstants.LANGUAGE_ATTRIBUTE));
+ name_lang = attr.getLanguage();
+ }
+ else{
+ attr.setLanguage(lang);
+ }
+ languageList.add(attr);
+// if (current_language.equals(attr.getLanguage())){
+// attr.setRequired(true);
+// languageList.add(0,attr);
+// }
+// else{
+// languageList.add(attr);
+// }
+
+ }
+
+
+ ArrayList des = XMLTools.getChildElementsByTagName(parent,GEMSConstants.DESCRIPTION_ELEMENT);
+ if (des.size() > 0){
+ Element des_element = (Element)des.get(0);
+ Attribute attr = new Attribute(GEMSConstants.DESCRIPTION_ATTRIBUTE,XMLTools.getElementTextValue(des_element), true);
+ if (lang == null){
+ attr.setLanguage(des_element.getAttribute(GEMSConstants.LANGUAGE_ATTRIBUTE));
+ }
+ else{
+ attr.setLanguage(lang);
+ }
+ languageList.add(attr);
+ }
+ else{
+ Attribute attr = new Attribute(GEMSConstants.DESCRIPTION_ATTRIBUTE,"", true);
+ if (lang == null){
+ attr.setLanguage(name_lang);
+ }
+ else{
+ attr.setLanguage(lang);
+ }
+
+ languageList.add(attr);
+ }
+
+ }
+
+
+ static public String getGLIDirectoryPath()
+ {
+ return System.getProperty("user.dir") + File.separator;
+ }
+
+
+ static public String getGLIMetadataDirectoryPath()
+ {
+ return getGLIDirectoryPath() + "metadata" + File.separator;
+ }
+
+ static public String getLanguageXMLPath()
+ {
+ return getGLIDirectoryPath() + "classes" + File.separator+"xml"+ File.separator+ "languages.xml";
+ }
+
+ static public String getCollectionPath()
+ {
+ if (Configuration.gsdl3_path != null){
+ return Configuration.gsdl3_path + File.separator + "sites";
+
+ }
+ else{
+ return Configuration.gsdl_path + File.separator + "collect";
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/MetadataSetModel.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/MetadataSetModel.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/MetadataSetModel.java (revision 31635)
@@ -0,0 +1,216 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: Shaoqun Wu, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gems;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.greenstone.gatherer.util.XMLTools;
+
+
+import java.util.Observable;
+import java.util.ArrayList;
+
+/**
+ * @author Shaoqun Wu, Greenstone Digital Library, University of Waikato
+ * @version 2.4
+ */
+public class MetadataSetModel
+ extends Observable implements MetadataSetListener, AttributeListener{
+
+ private Document metadata_doc;
+
+ private MetadataSetManager meta_manager;
+
+ private ArrayList model;
+
+ private MetadataSetInfo meta_info;
+
+ private boolean changed = false;
+
+
+ //construt a new MetadataSet
+ public MetadataSetModel(MetadataSetManager msm){
+ meta_manager = msm;
+ model = new ArrayList();
+ }
+
+ public String getCurrentLanguage(){
+ return meta_manager.getCurrentLanguage();
+ }
+
+ public void metadataSetChanged(MetadataSetEvent mse){
+ if (meta_info !=null) save(true); //save the last metadata set
+
+ model = new ArrayList();
+ meta_info = mse.getMetadataSetInfo();
+ meta_info.setMetadataSetModel(this);
+ metadata_doc = meta_manager.getMetadataSetDocument(meta_info.getFilePath());
+
+ constructModel();
+
+ setChanged();
+ notifyObservers();
+ clearChanged();
+ changed = false;
+
+ }
+
+ public void valueChanged(){
+ setChanged();
+ notifyObservers(new Boolean(false));
+ clearChanged();
+ }
+
+
+ private void constructModel(){
+ if (metadata_doc == null) return;
+ ArrayList elements =XMLTools.getChildElementsByTagName(metadata_doc.getDocumentElement(),GEMSConstants.ELEMENT_ELEMENT);
+ for(int i=0;i=0){
+ changed = true;
+ model.remove(child);
+ model.add(index-1,child);
+ return true;
+ }
+
+ return false;
+ }
+
+ public boolean moveDown(MetadataElementModel child){
+ int index = model.indexOf(child);
+ if (index+1 < model.size()){
+ changed = true;
+ model.remove(child);
+ model.add(index+1,child);
+ return true;
+ }
+
+ return false;
+ }
+
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/MetadataSetTree.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/MetadataSetTree.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/MetadataSetTree.java (revision 31635)
@@ -0,0 +1,435 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: Shaoqun Wu, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gems;
+
+import org.w3c.dom.Document;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.tree.*;
+
+import java.util.Observer;
+import java.util.Observable;
+import java.util.ArrayList;
+
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+
+/**
+ * @author Shaoqun Wu, Greenstone Digital Library, University of Waikato
+ * @version 2.4
+ */
+public class MetadataSetTree
+ extends JTree
+ implements Observer, ActionListener, MouseListener {
+
+ private MetadataSetTree self;
+ private MetadataSetModel metadata_model;
+ protected JFrame parent_frame;
+ public MetadataSetTree(JFrame parent){
+ this.setComponentOrientation(Dictionary.getOrientation());
+ setCellRenderer(new MetadataSetCellRenderer());
+ setRowHeight(20);
+ setEditable(false);
+ getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
+ setModel(null);
+ self = this;
+ parent_frame = parent;
+ addMouseListener(this);
+ }
+
+ public void mouseClicked(MouseEvent event) {
+ // right click
+ if (SwingUtilities.isRightMouseButton(event)) {
+ new MetadataSetTreeRightClickMenu(this, event);
+ } else {
+ TreePath tse = self.getSelectionPath();
+ if (tse!=null){
+ DefaultMutableTreeNode snode= (DefaultMutableTreeNode)tse.getLastPathComponent();
+
+ Object data = snode.getUserObject();
+
+ if (data instanceof MetadataElementModel){
+
+ ((MetadataElementModel)data).notifyListeners(false);
+ }
+ else{
+ if (data instanceof MetadataSetInfo){
+ ((MetadataSetInfo)data).notifyListeners();
+ }
+ }
+ }
+ }
+
+ }
+
+ public void mouseEntered(MouseEvent event) { }
+
+ public void mouseExited(MouseEvent event) { }
+
+ public void mousePressed(MouseEvent event) { }
+
+ public void mouseReleased(MouseEvent event) { }
+
+ public void update(Observable o,Object arg){
+ if (arg !=null){
+ Boolean replaceModel = (Boolean)arg;
+ if (!replaceModel.booleanValue()){
+ repaint();
+ expandAll();
+ }
+ }
+ metadata_model = (MetadataSetModel)o;
+
+ populateMetadataSetTree(metadata_model);
+
+ }
+
+ private void expandAll() {
+ int row = 0;
+ while (row < getRowCount()) {
+ expandRow(row);
+ row++;
+ }
+ }
+
+
+
+ public void actionPerformed(ActionEvent e){
+ String command = e.getActionCommand();
+
+ if (command.equals(GEMSConstants.ADD_ELEMENT)){
+ addElement(false);
+ return ;
+ }
+ if (command.equals(GEMSConstants.ADD_SUBELEMENT)){
+ addElement(true);
+ return ;
+ }
+
+ if (command.equals(GEMSConstants.DELETE_ELEMENT)){
+ deleteElement();
+ }
+
+
+ if (command.equals(GEMSConstants.MOVE_UP)){
+ moveUpElement();
+ }
+
+ if (command.equals(GEMSConstants.MOVE_DOWN)){
+ moveDownElement();
+ }
+
+ }
+
+
+ public void addElement(boolean subelement){
+ TreePath tse = self.getSelectionPath();
+ if (tse == null) return;
+
+ DefaultTreeModel dtm = (DefaultTreeModel)getModel();
+ DefaultMutableTreeNode snode=(DefaultMutableTreeNode)tse.getLastPathComponent();
+ Object data = snode.getUserObject();
+ if (data instanceof MetadataSetInfo){
+ data = metadata_model;
+ }
+ NewMetadataElementNamePrompt name_prompt = new NewMetadataElementNamePrompt(parent_frame, subelement, data);
+ if (name_prompt.isCancelled()) {
+ return;
+ }
+ String new_name = name_prompt.getName();
+
+ MetadataElementModel element_model = new MetadataElementModel(metadata_model.getMetadataSetInfo(), new_name);
+ DefaultMutableTreeNode new_node = new DefaultMutableTreeNode(element_model);
+ dtm.insertNodeInto(new_node,snode,snode.getChildCount());
+ //update model
+ if (data instanceof MetadataElementModel){
+ ((MetadataElementModel) data).addChild(element_model);
+ }
+ else if(data instanceof MetadataSetModel){
+ ((MetadataSetModel)data).addChild(element_model);
+
+ }
+
+
+ dtm.reload(snode);
+ expandPath(tse);
+ tse = tse.pathByAddingChild(new_node);
+ self.setSelectionPath(tse);
+ element_model.notifyListeners(false);
+ }
+
+
+
+ public void deleteElement(){
+
+ //int result = JOptionPane.showConfirmDialog(null, Dictionary.get("GEMS.Confirm_DeleteElement", "(Put name here)"), Dictionary.get("GEMS.Confirm_DeleteElement_Title"), JOptionPane.OK_CANCEL_OPTION);
+ //if (result != JOptionPane.OK_OPTION) return;
+
+ TreePath tse = self.getSelectionPath();
+
+ if (tse!=null){
+ DefaultTreeModel dtm = (DefaultTreeModel)getModel();
+ DefaultMutableTreeNode snode=(DefaultMutableTreeNode)tse.getLastPathComponent();
+ DefaultMutableTreeNode parent = (DefaultMutableTreeNode) snode.getParent();
+ snode.removeFromParent();
+
+ Object parent_data = parent.getUserObject();
+ Object data = snode.getUserObject();
+ MetadataElementModel selected_model = null;
+ if (parent_data instanceof MetadataElementModel){
+ MetadataElementModel parent_model = (MetadataElementModel) parent_data;
+ selected_model = (MetadataElementModel) data;
+ parent_model.removeChild(selected_model);
+ parent_model.notifyListeners(false);
+ }
+ else{
+ if(parent_data instanceof MetadataSetInfo){
+ selected_model = (MetadataElementModel) data;
+ metadata_model.removeChild(selected_model);
+ ((MetadataSetInfo)parent_data).notifyListeners();
+
+ }
+ }
+
+ dtm.reload(parent);
+ self.setSelectionPath(tse.getParentPath());
+ //selected_model.notifyListeners(true);// notify its listeners that it's been deleted
+ }
+
+ }
+
+ private void moveUpElement(){
+ TreePath tse = self.getSelectionPath();
+
+ if (tse!=null){
+ DefaultMutableTreeNode snode= (DefaultMutableTreeNode)tse.getLastPathComponent();
+ int row = getSelectionRows()[0];
+ boolean moved = false;
+
+ Object data = snode.getUserObject();
+
+ if (data instanceof MetadataElementModel){
+ MetadataElementModel element_model = (MetadataElementModel)data;
+ //top level element
+ if (element_model.getParent() == null){
+ moved = metadata_model.moveUp(element_model);
+
+ }
+ else{
+ MetadataElementModel parent = element_model.getParent();
+ moved = parent.moveUp(element_model);
+ }
+ if (moved){
+ DefaultTreeModel dtm = (DefaultTreeModel)getModel();
+ DefaultMutableTreeNode pnode= (DefaultMutableTreeNode) snode.getParent();
+ int index = pnode.getIndex(snode);
+ dtm.removeNodeFromParent(snode);
+ dtm.insertNodeInto(snode,pnode,--index);
+ dtm.reload(pnode);
+ setSelectionRow(--row);
+ }
+ }
+ }
+
+ }
+
+ private void moveDownElement(){
+ TreePath tse = self.getSelectionPath();
+
+ if (tse!=null){
+ boolean moved = false;
+ DefaultMutableTreeNode snode= (DefaultMutableTreeNode)tse.getLastPathComponent();
+ int row = getSelectionRows()[0];
+ Object data = snode.getUserObject();
+
+ if (data instanceof MetadataElementModel){
+ MetadataElementModel element_model = (MetadataElementModel)data;
+ //top level element
+ if (element_model.getParent() == null){
+ moved = metadata_model.moveDown(element_model);
+ }
+ else{
+ MetadataElementModel parent = element_model.getParent();
+ moved = parent.moveDown(element_model);
+ }
+ // move the node down the tree
+ if (moved){
+ DefaultTreeModel dtm = (DefaultTreeModel)getModel();
+ DefaultMutableTreeNode pnode= (DefaultMutableTreeNode) snode.getParent();
+ int index = pnode.getIndex(snode);
+ dtm.removeNodeFromParent(snode);
+ dtm.insertNodeInto(snode,pnode,++index);
+ dtm.reload(pnode);
+ setSelectionRow(++row);
+ }
+ }
+ }
+ }
+
+
+ private void populateMetadataSetTree(MetadataSetModel metadata_model){
+ setModel(null);
+ MetadataSetInfo info= metadata_model.getMetadataSetInfo();
+ if (info == null) {
+ repaint();
+
+ return;
+ }
+
+ ArrayList metadata_elements = metadata_model.getMetadataSetModel();
+
+ DefaultTreeModel dtm = new DefaultTreeModel(new DefaultMutableTreeNode(info));
+ DefaultMutableTreeNode root = (DefaultMutableTreeNode)dtm.getRoot();
+
+ for(int i=0;i
+ *
+ * Author: Katherine Don, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gems;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.gui.GLIButton;
+import org.greenstone.gatherer.gui.ModalDialog;
+
+public class NewMetadataElementNamePrompt
+ extends ModalDialog {
+ private Dimension SIZE = new Dimension(350, 120);
+
+ private boolean cancelled = false;
+ private String element_name = null;
+ private JTextField name_textfield = null;
+ private JDialog prompt;
+ private Object model;
+ public NewMetadataElementNamePrompt(Frame parent, boolean subelement, Object model) {
+ super(parent, true);
+ setSize(SIZE);
+ prompt = this;
+ this.model = model;
+ JPanel content_pane = (JPanel) getContentPane();
+ content_pane.setOpaque(true);
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JLabel name_label = new JLabel();
+ name_label.setComponentOrientation(Dictionary.getOrientation());
+ if (subelement) {
+ setTitle(Dictionary.get("GEMS.NewMetadataElementNamePrompt.SubTitle"));
+ name_label.setText(Dictionary.get("GEMS.NewMetadataElementNamePrompt.SubName"));
+ } else {
+ setTitle(Dictionary.get("GEMS.NewMetadataElementNamePrompt.Title"));
+ name_label.setText(Dictionary.get("GEMS.NewMetadataElementNamePrompt.Name"));
+ }
+
+ name_textfield = new JTextField();
+ name_textfield.setComponentOrientation(Dictionary.getOrientation());
+ name_textfield.addKeyListener(new KeyAdapter() {
+ public void keyPressed(KeyEvent e) {
+ if (e.getKeyCode() == KeyEvent.VK_ENTER) {
+ //same as clicking OK button
+ validateElementName();
+ }
+ }
+ });
+
+ JPanel details_pane = new JPanel();
+ details_pane.setComponentOrientation(Dictionary.getOrientation());
+ details_pane.setLayout(new GridLayout(2,1));
+ details_pane.add(name_label);
+ details_pane.add(name_textfield);
+
+ JPanel button_pane = new JPanel();
+ button_pane.setLayout(new GridLayout(1,2));
+ GLIButton ok_button = new GLIButton(Dictionary.get("General.OK"), Dictionary.get("General.OK_Tooltip"));
+ GLIButton cancel_button = new GLIButton(Dictionary.get("General.Cancel"), Dictionary.get("General.Cancel_Tooltip"));
+ button_pane.add(ok_button);
+ button_pane.add(cancel_button);
+
+ // Add listeners
+ ok_button.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent event) {
+ validateElementName();
+ }
+ });
+ cancel_button.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent event) {
+ cancelled = true;
+ prompt.dispose();
+ }
+ });
+
+ content_pane.setLayout(new BorderLayout(5,5));
+ content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ content_pane.add(details_pane, BorderLayout.CENTER);
+ content_pane.add(button_pane, BorderLayout.SOUTH);
+
+ Dimension screen_size = Configuration.screen_size;
+ setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
+ setVisible(true);
+
+ }
+
+ public String getName() {
+ return element_name;
+ }
+
+ public boolean isCancelled() {
+ return cancelled;
+ }
+
+ private void validateElementName() {
+ String name = name_textfield.getText();
+ if (name.equals("")) {
+ JOptionPane.showMessageDialog(prompt,Dictionary.get("GEMS.NewMetadataElementNamePrompt.EmptyName_Error_Message"), Dictionary.get("GEMS.NewMetadataElementNamePrompt.Name_Error"), JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ boolean already_used = false;
+ if (model instanceof MetadataSetModel) {
+ already_used = ((MetadataSetModel)model).doesChildWithThisNameExist(name);
+ } else if (model instanceof MetadataElementModel) {
+ already_used = ((MetadataElementModel)model).doesChildWithThisNameExist(name);
+ }
+ if (!already_used) {
+ element_name = name;
+ prompt.dispose();
+ } else {
+ JOptionPane.showMessageDialog(prompt,Dictionary.get("GEMS.NewMetadataElementNamePrompt.Name_Error_Message"), Dictionary.get("GEMS.NewMetadataElementNamePrompt.Name_Error"), JOptionPane.ERROR_MESSAGE);
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/NewMetadataSetPrompt.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/NewMetadataSetPrompt.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gems/NewMetadataSetPrompt.java (revision 31635)
@@ -0,0 +1,315 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: Shaoqun Wu, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gems;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Collection;
+import java.util.Vector;
+
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.gui.ModalDialog;
+import org.greenstone.gatherer.gui.GLIButton;
+
+public class NewMetadataSetPrompt
+ extends ModalDialog {
+
+ static private Dimension SIZE = new Dimension(500, 320);
+
+ private ArrayList available_metadata_sets;
+ private ArrayList listeners;
+
+ private JButton ok_button = null;
+ private JButton cancel_button = null;
+ private JComboBox base_metadata_combo;
+ private NewMetadataSetPrompt self;
+ private MetadataSetManager meta_manager;
+ private JTextArea description_textarea = null;
+ private JTextField title_field;
+ private JTextField namespace_field;
+
+ private boolean cancelled = false;
+
+ public NewMetadataSetPrompt(Frame parent,MetadataSetManager msm) {
+ super(parent, true);
+ self = this;
+ listeners = new ArrayList();
+ meta_manager = msm;
+
+ setSize(SIZE);
+ setTitle(Dictionary.get("GEMS.NewMetadataSetPrompt.Title"));
+
+ JPanel content_pane = (JPanel) getContentPane();
+ content_pane.setOpaque(true);
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JLabel instruction_label = new JLabel(Dictionary.get("GEMS.NewMetadataSetPrompt.Instructions"));
+ instruction_label.setOpaque(true);
+ instruction_label.setComponentOrientation(Dictionary.getOrientation());
+
+ JLabel title_label = new JLabel(Dictionary.get("GEMS.NewMetadataSetPrompt.Metadata_Title"));
+ title_label.setOpaque(true);
+ title_label.setComponentOrientation(Dictionary.getOrientation());
+
+ title_field = new JTextField();
+ title_field.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel title_pane = new JPanel(new BorderLayout(5,5));
+ title_pane.setComponentOrientation(Dictionary.getOrientation());
+ title_pane.add(title_label,BorderLayout.LINE_START);
+ title_pane.add(title_field, BorderLayout.CENTER);
+
+
+ JLabel namespace_label = new JLabel(Dictionary.get("GEMS.NewMetadataSetPrompt.Metadata_Namespace"));
+ namespace_label.setComponentOrientation(Dictionary.getOrientation());
+ namespace_label.setOpaque(true);
+
+ namespace_field = new JTextField();
+ namespace_field.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel namespace_pane = new JPanel(new BorderLayout(5,5));
+ namespace_pane.setComponentOrientation(Dictionary.getOrientation());
+ namespace_pane.add(namespace_label,BorderLayout.LINE_START);
+ namespace_pane.add(namespace_field, BorderLayout.CENTER);
+
+
+ JPanel info_pane = new JPanel();
+ info_pane.setComponentOrientation(Dictionary.getOrientation());
+ info_pane.setLayout(new BorderLayout(5,5));
+ info_pane.add(instruction_label,BorderLayout.NORTH);
+ info_pane.add(title_pane,BorderLayout.CENTER);
+ info_pane.add(namespace_pane,BorderLayout.SOUTH);
+
+
+ JLabel description_label = new JLabel(Dictionary.get("GEMS.Set_Description"));
+ description_label.setOpaque(true);
+ description_label.setComponentOrientation(Dictionary.getOrientation());
+
+ description_textarea = new JTextArea();
+ description_textarea.setComponentOrientation(Dictionary.getOrientation());
+ description_textarea.setLineWrap(true);
+ description_textarea.setWrapStyleWord(true);
+
+ JPanel description_pane = new JPanel();
+ description_pane.setComponentOrientation(Dictionary.getOrientation());
+ description_pane.setLayout(new BorderLayout());
+ description_pane.add(description_label,BorderLayout.NORTH);
+ description_pane.add(new JScrollPane(description_textarea),BorderLayout.CENTER);
+
+
+ JLabel base_label = new JLabel(Dictionary.get("GEMS.NewMetadataSetPrompt.Base_MetadataSet"));
+ base_label.setOpaque(true);
+ base_label.setComponentOrientation(Dictionary.getOrientation());
+
+ base_metadata_combo = new JComboBox();
+ base_metadata_combo.setRenderer(new MetadatSetListCellRenderer());
+ base_metadata_combo.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel base_pane = new JPanel(new BorderLayout(5,5));
+ base_pane.setComponentOrientation(Dictionary.getOrientation());
+ base_pane.add(base_label,BorderLayout.LINE_START);
+ base_pane.add(base_metadata_combo, BorderLayout.CENTER);
+
+
+ JPanel button_pane = new JPanel();
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+ ok_button = new GLIButton(Dictionary.get("General.OK"), Dictionary.get("General.OK_Tooltip"));
+ cancel_button = new GLIButton(Dictionary.get("General.Cancel"), Dictionary.get("General.Cancel_Tooltip"));
+
+
+ // Add listeners
+ ok_button.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent event) {
+ if (createNewSet()) {
+ self.dispose();
+ } // else if that returned false, then we leave the
+ // prompt there for them to change their input
+ }
+ });
+
+
+ cancel_button.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent event) {
+ cancelled = true;
+ self.dispose();
+ }
+ });
+
+
+ button_pane.setLayout(new GridLayout(1,2));
+ button_pane.add(ok_button);
+ button_pane.add(cancel_button);
+
+ JPanel bottom_pane = new JPanel(new GridLayout(2,1,5,5));
+ bottom_pane.add(base_pane);
+ bottom_pane.add(button_pane);
+
+ content_pane.setLayout(new BorderLayout(5,5));
+ content_pane.add(info_pane, BorderLayout.NORTH);
+ content_pane.add(description_pane, BorderLayout.CENTER);
+ content_pane.add(bottom_pane, BorderLayout.SOUTH);
+ content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+
+ // Show
+ Dimension screen_size = Configuration.screen_size;
+ setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
+ setVisible(false);
+
+ }
+
+
+ public void display(){
+ cancelled = false;
+ available_metadata_sets = meta_manager.getAvailableMetadataSets();
+ Vector data = new Vector((Collection)available_metadata_sets);
+ data.add(0,Dictionary.get("GEMS.NewMetadataSetPrompt.New_Metadata"));
+ DefaultComboBoxModel model = new DefaultComboBoxModel(data);
+ title_field.setText("");
+ namespace_field.setText("");
+ description_textarea.setText("");
+ base_metadata_combo.setModel(model);
+ setVisible(true);
+ }
+
+ public boolean isCancelled() {
+ return cancelled;
+ }
+
+ public void addMetadataSetListener(MetadataSetListener msl){
+ listeners.add(msl);
+
+ }
+
+ private boolean createNewSet() {
+
+ String title = title_field.getText();
+ String namespace = namespace_field.getText();
+ String description = description_textarea.getText();
+
+ if (title == null || title.trim().equals("")){
+ JOptionPane.showMessageDialog(self, Dictionary.get("GEMS.NewMetadataSetPrompt.Title_Error_Message"), Dictionary.get("GEMS.NewMetadataSetPrompt.Title_Error"), JOptionPane.ERROR_MESSAGE);
+
+ return false;
+ }
+
+ if (namespace == null || namespace.trim().equals("")){
+ JOptionPane.showMessageDialog(self, Dictionary.get("GEMS.NewMetadataSetPrompt.Namespace_Error_Message"), Dictionary.get("GEMS.NewMetadataSetPrompt.Namespace_Error"), JOptionPane.ERROR_MESSAGE);
+ return false;
+ }
+
+ //check namespace conflict
+ if (meta_manager.isNamespaceAlreadyUsed(namespace)) {
+ int result = JOptionPane.showOptionDialog(null,Dictionary.get("GEMS.Namespace_Conflict_Message"), Dictionary.get("GEMS.Namespace_Conflict"),JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE,null,GEMSConstants.DIALOG_OPTIONS,GEMSConstants.DIALOG_OPTIONS[0] );
+
+ if (result != JOptionPane.OK_OPTION) return false;
+ }
+
+ Object selectedValue = base_metadata_combo.getSelectedItem();
+
+ if (selectedValue != null ){
+ MetadataSetInfo meta_info = null;
+
+ if ((selectedValue instanceof MetadataSetInfo)){
+ meta_info = (MetadataSetInfo)selectedValue;
+ }
+ else{
+ meta_info = new MetadataSetInfo();
+ }
+
+ meta_info.setNew(true);
+ // clear all the language dependent attributes
+ meta_info.setLanguageDependentAttributes(new ArrayList());
+ meta_info.setMetadataSetName(title);
+ meta_info.setMetadataSetDescription(description);
+ meta_info.setNamespace(namespace);
+ meta_info.setCurrentLanguage(meta_manager.getCurrentLanguage());
+ notifyListeners(meta_info);
+ }
+ return true;
+ }
+
+
+ private void notifyListeners(MetadataSetInfo set_info){
+ MetadataSetEvent mse = new MetadataSetEvent(set_info);
+ for(int i=0;i
+ *
+ * Author: Shaoqun Wu, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gems;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Collection;
+import java.util.Vector;
+import java.io.File;
+
+
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.gui.ModalDialog;
+import org.greenstone.gatherer.gui.GLIButton;
+
+public class OpenMetadataSetPrompt
+ extends ModalDialog {
+
+ static private Dimension SIZE = new Dimension(500, 500);
+
+ private ArrayList available_metadata_sets;
+ private ArrayList listeners;
+
+ private JButton open_button = null;
+ private JButton cancel_button = null;
+ private JButton browse_button = null;
+ private JList available_set_list = null;
+ private OpenMetadataSetPrompt self;
+ private MetadataSetManager meta_manager;
+ private JTextArea description_textarea = null;
+
+ private boolean cancelled = false;
+
+ public OpenMetadataSetPrompt(Frame parent,MetadataSetManager msm) {
+ super(parent, true);
+
+ self = this;
+ meta_manager = msm;
+ setSize(SIZE);
+ setTitle(Dictionary.get("GEMS.OpenMetadataSetPrompt.Title"));
+ listeners = new ArrayList();
+
+ JPanel content_pane = (JPanel) getContentPane();
+ content_pane.setOpaque(true);
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JLabel available_metadata_sets_label = new JLabel(Dictionary.get("GEMS.OpenMetadataSetPrompt.Available_Sets"));
+ available_metadata_sets_label.setOpaque(true);
+ available_metadata_sets_label.setComponentOrientation(Dictionary.getOrientation());
+
+ available_set_list = new JList();
+ available_set_list.setCellRenderer(new MetadatSetListCellRenderer());
+ available_set_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ available_set_list.setFixedCellHeight(20);
+ available_set_list.addListSelectionListener(new MetadataSetListSelectionListener());
+ available_set_list.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel set_pane = new JPanel();
+ set_pane.setComponentOrientation(Dictionary.getOrientation());
+ set_pane.setLayout(new BorderLayout());
+ set_pane.add(available_metadata_sets_label,BorderLayout.NORTH);
+ set_pane.add(new JScrollPane(available_set_list),BorderLayout.CENTER);
+
+ JLabel metadata_set_des_label = new JLabel(Dictionary.get("GEMS.Set_Description"));
+ metadata_set_des_label.setOpaque(true);
+ metadata_set_des_label.setComponentOrientation(Dictionary.getOrientation());
+
+ description_textarea = new JTextArea();
+ description_textarea.setOpaque(true);
+ description_textarea.setEditable(false);
+ description_textarea.setLineWrap(true);
+ description_textarea.setWrapStyleWord(true);
+ description_textarea.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel des_pane = new JPanel();
+ des_pane.setLayout(new BorderLayout());
+ des_pane.add(metadata_set_des_label,BorderLayout.NORTH);
+ des_pane.add(new JScrollPane(description_textarea),BorderLayout.CENTER);
+ des_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel button_pane = new JPanel();
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ open_button = new GLIButton(Dictionary.get("GEMS.OpenMetadataSetPrompt.Open"), Dictionary.get("GEMS.OpenMetadataSetPrompt.Open_Tooltip"));
+ open_button.setEnabled(true);
+
+ browse_button = new GLIButton(Dictionary.get("General.Browse"));
+ browse_button.setEnabled(true);
+
+ cancel_button = new GLIButton(Dictionary.get("General.Cancel"), Dictionary.get("General.Cancel_Tooltip"));
+ cancel_button.setEnabled(true);
+
+
+ // Add listeners
+ open_button.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent event) {
+ notifyListeners();
+ self.dispose();
+ }
+ });
+
+ browse_button.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent event) {
+ String collect_dir = meta_manager.getCollectionPath();
+ JFileChooser file_chooser = new JFileChooser(new File(collect_dir));
+ file_chooser.setMultiSelectionEnabled(false);
+ int result = file_chooser.showOpenDialog(null);
+ if (result == JFileChooser.APPROVE_OPTION){
+ String file_path = file_chooser.getSelectedFile().toString();
+ MetadataSetInfo meta_info = meta_manager.getMetadataSet(file_path);
+ meta_info.setNew(false);
+ MetadataSetEvent mse = new MetadataSetEvent(meta_info);
+ for(int i=0;i= HttpURLConnection.HTTP_OK && response_code < HttpURLConnection.HTTP_MULT_CHOICE) {
+ DebugStream.println("200 - Complete.");
+ }
+ else {
+ DebugStream.println("404 - Failed.");
+ }
+ }
+ catch (Exception exception) {
+ DebugStream.printStackTrace(exception);
+ }
+ }
+
+ // Used to send messages to the local library server wrapper to the Apache web server (server.jar)
+ static private boolean sendMessageToServer(String message) {
+ if(isPersistentServer) {
+ return false;
+ }
+
+ if(port == -1) {
+ return false;
+ }
+
+ try {
+ if(clientSocket == null) {
+ clientSocket = new Socket("localhost", port);
+ }
+ if(clientSocketWriter == null) {
+ clientSocketWriter = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));
+ }
+ clientSocketWriter.write(message);
+ clientSocketWriter.flush();
+
+ } catch (Exception e) {
+ System.err.println("An exception occurred when trying to send the message: " + message
+ + "\nto the LocalLibraryServer.\n" + e);
+ return false;
+ }
+ return true;
+ }
+
+ static public boolean isRunning()
+ {
+ if (!running) return false; // if the url is pending, then running would also be false (server not started up yet)
+
+ llssite_cfg_file.load(true);
+ String url = llssite_cfg_file.getURL();
+ if (url == null) return false;
+
+ // Called by Gatherer to check whether we need to stop the server.
+ // if the url is pending, then the GSI hasn't started the server up yet
+ // Already covered in !running
+ //if (url.equals(LLSSiteConfig.URL_PENDING)) return false;
+
+ return true;
+ }
+
+ /** collection name can be a group-qualified collect name */
+ static public void releaseCollection(String collection_name)
+ {
+ if (isPersistentServer) {
+ config(RELEASE_COMMAND + collection_name);
+ }
+ }
+
+ static public boolean start(String gsdl_path, String local_library_server_file_path)
+ {
+ // Check the local library server file (server.exe or gs2-server.sh) exists
+ if(local_library_server_file_path == null) {
+ return false;
+ }
+ local_library_server_file = new File(local_library_server_file_path);
+ if (local_library_server_file.exists()) {
+ if(local_library_server_file.getName().equals("server.exe")) {
+ isPersistentServer = true;
+ } // else it may be gs2-web-server.bat
+ }
+ else { // local_library_server_file does not exist
+ DebugStream.println("No local library at given file path.");
+
+ if(Utility.isWindows()) { // test for server.exe and gs2-web-server.bat
+ local_library_server_file = new File(gsdl_path + "server.exe");
+ if (local_library_server_file.exists()) {
+ isPersistentServer = true;
+ } else {
+ local_library_server_file = new File(gsdl_path + "gs2-web-server.bat");
+ if (!local_library_server_file.exists()) {
+ DebugStream.println("No local library at all.");
+ return false;
+ }
+ }
+ } else { // linux
+ local_library_server_file = new File(gsdl_path + "gs2-server.sh");
+ if (!local_library_server_file.exists()) {
+ DebugStream.println("No local library at all.");
+ return false;
+ }
+ }
+ }
+
+ if(!isPersistentServer) {
+ // In the case of the Local Library Server on Linux or on Win where there's no server.exe, do an extra test:
+ // If GS2 was not configured with --enable-apache-httpd, then there is no apache webserver folder,
+ // even though the gs2-server.sh file would still be there. That means if the folder is absent
+ // we still have no local library server.
+
+ File localServerFolder = new File(gsdl_path, "apache-httpd");
+ if (!localServerFolder.exists() && !localServerFolder.isDirectory()) {
+ DebugStream.println("The web server does not exist at "
+ + localServerFolder.getAbsolutePath() + "\nNo local library at all. Trying web library");
+ return false;
+ } // else apache-httpd folder exists
+ }
+
+ llssite_cfg_file = new LLSSiteConfig(local_library_server_file);
+ if(!llssite_cfg_file.isConfigFileSet()) {
+ return false;
+ }
+
+ // from now on return true: we're in local_library_mode (even if the server is not running)
+
+
+ // If the user launched the GSI independent of GLI, but user has not pressed
+ // Enter Library yet, then we will obtain the previewURL later.
+ if(LocalLibraryServer.isURLPending()) {
+ // running is still false when the URL is pending because only GSI is running, not the server
+ return true;
+ } else if(llssite_cfg_file.isIndependentGSI()) {
+ // There is already a url and it's not pending: meaning the server
+ // has started up and that GSI was launched outside of GLI
+ running = true;
+ return true;
+ }
+
+ // Spawn local library server process
+ final String QUOTES = Utility.isWindows() ? "\"" : ""; // need to embed path in quotes on Windows for spaces (interferes on Linux)
+ String local_library_server_command = QUOTES+local_library_server_file.getAbsolutePath()+QUOTES + getExtraLaunchArguments(llssite_cfg_file);
+ if(Utility.isWindows() && !isPersistentServer) { // launching gs2-web-server.bat (Windows) needs cmd start
+ local_library_server_command = "cmd.exe /c start \"GSI\" " + local_library_server_command;
+ }
+
+ // Check if the server is already running
+ String url = llssite_cfg_file.getURL();
+ if (url != null) {
+ // If it is already running then set the Greenstone web server address and we're done
+ // E.g. if previously GLI was not properly shut down, the URL property (signifying
+ // the server is still running) would still be in the config file.
+ try {
+ Configuration.library_url = new URL(url);
+ running = true;
+
+ // Run the server interface
+ Gatherer.spawnApplication(local_library_server_command, ID);
+ return true;
+ }
+ catch (MalformedURLException exception) {
+ DebugStream.printStackTrace(exception);
+ }
+ }
+
+ // Configure the server for immediate entry
+ //llssite_cfg_file.set();
+
+ // Spawn local library server process
+ Gatherer.spawnApplication(local_library_server_command, ID);
+
+ // Wait until program has started
+ try {
+ //System.err.println("**** testing server...");
+ testServerRunning(); // will set running = true when the server is up and running successfully
+ //System.err.println("**** Is server running: " + running);
+ } catch (IOException bad_url_connection) {
+ try {
+ // If this fails then we try changing the url to be localhost
+ Configuration.library_url = new URL(llssite_cfg_file.getLocalHostURL());
+ DebugStream.println("Try connecting to server on local host: '" + Configuration.library_url + "'");
+ URLConnection connection = Configuration.library_url.openConnection();
+ connection.getContent();
+ running = true;
+
+ } catch (IOException worse_url_connection) {
+ DebugStream.println("Can't connect to server on either address.");
+ Configuration.library_url = null;
+ running = false;
+ }
+ }
+
+ return true;
+ }
+
+ /** Call this when the collect directory has changed. Only works when using
+ * the web server launched by GLI. */
+ static public void reconfigure() {
+ if(isPersistentServer) {
+ // 1st: server may have nulled URL if server was stopped inbetween,
+ // so load that new URL, if GLI conf file modified
+ // Then put the new collectDir into the GLI configfile being used
+ llssite_cfg_file.load(true);
+
+ String collectDir = Gatherer.getCollectDirectoryPath();
+ collectDir = collectDir.substring(0, collectDir.length()-1); // remove file separator at end
+ llssite_cfg_file.put(LLSSiteConfig.COLLECTHOME, collectDir);
+ llssite_cfg_file.save();
+
+ // tell the server to restart, so it will read the new configuration
+
+ // (1) need a server to be running in order for us to send a restart message to it
+ if(checkServerRunning()) { // if true, it wasn't running before, but has now restarted the server. We're done
+ return;
+ }
+ // (2) otherwise the server may already have been running, need to tell it to restart after setting the collect dir
+ if(running) {
+ config(RESTART_COMMAND);
+ }
+ return;
+ }
+
+ // can't control the GSI/server if it was launched independent of GLI
+ if(llssite_cfg_file.isIndependentGSI()) {
+ JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("Server.Reconfigure"), Dictionary.get("General.Reconfigure"), JOptionPane.INFORMATION_MESSAGE);
+ return;
+ }
+
+ // someone may have closed the web server
+ if(checkServerRunning()) { // need a server to be running in order for us to send a reconfigure message to it
+ return; // it has reconfigured and restarted the server
+ }
+
+ // If it was already running, tell the server a reconfigure is required on next restart
+ // and then restart
+ if(running && sendMessageToServer("RECONFIGURE\n")) {
+ // restart
+ if(running) {
+ running = false;
+
+ if(sendMessageToServer("RESTART\n")) { //if(sendMessageToServer("RECONFIGURE\n")) {
+ // wait for the server to update the URL in the gli config file
+ llssite_cfg_file.setLastModified();
+
+ int attempt_count = 0;
+ while (!llssite_cfg_file.isModified()) {
+ new OneSecondWait(); // Wait one second (give or take)
+ attempt_count++;
+
+ // After waiting for the specified time, ask the user whether they want to wait for that long again
+ if (attempt_count == WAITING_TIME) {
+ break; // can't be waiting forever, we'll be waiting again below
+ }
+ }
+
+ try {
+ testServerRunning(); // will set running = true when the server is up and running successfully
+ } catch (IOException bad_url_connection) {
+ try {
+ // If this fails then we try changing the url to be localhost
+ Configuration.library_url = new URL(llssite_cfg_file.getLocalHostURL());
+ DebugStream.println("Try connecting to server on local host: '" + Configuration.library_url + "'");
+ URLConnection connection = Configuration.library_url.openConnection();
+ connection.getContent();
+ running = true;
+
+ } catch (IOException worse_url_connection) {
+ DebugStream.println("Can't connect to server on either address.");
+ Configuration.library_url = null;
+ running = false;
+ }
+ }
+ }
+ }
+ } else {
+ System.err.println("GLI was unable to send a reconfigure request to the local library server."
+ + "\nPlease reconfigure and restart the local Greenstone server manually.");
+ }
+ }
+
+
+ static public void stop()
+ {
+ if (!running) {
+ // also the case if the URL is pending in an independently launched GSI
+ return;
+ }
+
+ // don't (can't) shutdown the GSI/server if it was launched independent of GLI
+ if(llssite_cfg_file.isIndependentGSI()) {
+ return;
+ }
+
+ // Send the command for it to exit.
+ if (isPersistentServer) {
+ config(QUIT_COMMAND);
+ } else {
+ boolean success = sendMessageToServer("QUIT\n");
+ try {
+ if(clientSocketWriter != null) {
+ clientSocketWriter.close();
+ clientSocketWriter = null;
+ }
+ clientSocket = null;
+ } catch(Exception e) {
+ System.err.println("An exception occurred when trying to close the socket"
+ + "\nto the LocalLibraryServer.\n" + e);
+ }
+ if(success) {
+ Gatherer.terminateApplication(ID);
+ } else {
+ System.err.println("Unable to stop the server, since there's no communication port to send the quit msg over."
+ + "\nPlease stop the local Greenstone server manually.");
+ }
+ }
+
+ // Wait until program has stopped, by reloading and checking the URL field
+ llssite_cfg_file.load(false);
+ int attempt_count = 0;
+ String url = llssite_cfg_file.getURL();
+ while (url != null && !url.equals(LLSSiteConfig.URL_PENDING)) { // if pending, the server is already stopped (not running)
+ new OneSecondWait(); // Wait one second (give or take)
+ llssite_cfg_file.load(false);
+ attempt_count++;
+
+ // After waiting for the specified time, ask the user whether they want to wait for that long again
+ if (attempt_count == WAITING_TIME) {
+ int try_again = JOptionPane.showConfirmDialog(Gatherer.g_man, Dictionary.get("Server.QuitTimeOut", Integer.toString(WAITING_TIME)),
+ Dictionary.get("General.Warning"), JOptionPane.YES_NO_OPTION);
+ if (try_again == JOptionPane.NO_OPTION) {
+ return;
+ }
+ attempt_count = 0;
+ }
+ // read the url again to see if it's updated
+ url = llssite_cfg_file.getURL();
+ }
+
+ // Restore the llssite_cfg.
+ llssite_cfg_file.restore();
+
+ // If the local server is still running then our changed values will get overwritten.
+ url = llssite_cfg_file.getURL();
+ if (url != null && !url.equals(LLSSiteConfig.URL_PENDING)) {
+ JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("Server.QuitManual"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
+ }
+
+ running = false;
+ }
+
+ static private String getExtraLaunchArguments(LLSSiteConfig site_cfg_file) {
+ String args = " " + LLSSiteConfig.GLIMODE + " " + site_cfg_file.getSiteConfigFilename();
+
+ if(isPersistentServer) {
+ return args;
+ }
+
+ // Else, when running the Local Apache Library Server on Linux/Win, need to provide a port argument
+ try {
+ PortFinder portFinder = new PortFinder(50100, 100);
+ port = portFinder.findPortInRange(false); // silent mode
+ } catch(Exception e) {
+ System.err.println("Exception when trying to find an available port: " + e);
+ port = -1;
+ }
+
+ return args + " --quitport=" + port;
+ }
+
+
+ // This method first tests whether there is a URL in the llssite_cfg_file
+ // and after that appears, it tests whether the URL is functional.
+ static private void testServerRunning() throws IOException {
+ // Wait until program has started, by reloading and checking the URL field
+ llssite_cfg_file.load(false);
+ int attempt_count = 0;
+ while (llssite_cfg_file.getURL() == null) {
+ new OneSecondWait(); // Wait one second (give or take)
+ //System.err.println("**** One Second Wait");
+ llssite_cfg_file.load(false);
+ attempt_count++;
+
+ // After waiting for the specified time, ask the user whether they want to wait for that long again
+ if (attempt_count == WAITING_TIME) {
+ int try_again = JOptionPane.showConfirmDialog(Gatherer.g_man, Dictionary.get("Server.StartUpTimeOut", Integer.toString(WAITING_TIME)),
+ Dictionary.get("General.Warning"), JOptionPane.YES_NO_OPTION);
+ if (try_again == JOptionPane.NO_OPTION) {
+ return;
+ }
+ attempt_count = 0;
+ }
+ }
+
+ // Ta-da. Now the url should be available
+ try {
+ Configuration.library_url = new URL(llssite_cfg_file.getURL());
+ }
+ catch (MalformedURLException exception) {
+ DebugStream.printStackTrace(exception);
+ }
+
+ // A quick test involves opening a connection to get the home page for this collection
+ try {
+ DebugStream.println("Try connecting to server on config url: '" + Configuration.library_url + "'");
+ URLConnection connection = Configuration.library_url.openConnection();
+ connection.getContent();
+ running = true;
+ }
+ catch (IOException bad_url_connection) {
+ System.err.println("Unable to open a connection to " + Configuration.library_url
+ + "\nCheck whether the port may already be in use. If so, change your\n"
+ + "Greenstone port number in the Greenstone Server Interface(GSI).");
+ throw bad_url_connection;
+ }
+
+
+ }
+
+ /** Returns true if we're waiting on the user to click on Enter Library button
+ * in an independently launched GSI (one not launched by GLI). */
+ static public boolean isURLPending() {
+ //if(Configuration.library_url != null) {
+ //System.err.println("**** Configuration.library_url: " + Configuration.library_url );
+ //return false; // don't need to do anything as we already have the url
+ //}
+
+ llssite_cfg_file.load(true); // don't force reload, load only if modified
+
+ String url = llssite_cfg_file.getURL();
+
+ if(url != null) {
+ if(url.equals(LLSSiteConfig.URL_PENDING)) {
+ running = false; // imagine if they restarted an external GSI
+ return true;
+ } else {
+ // a valid URL at last
+ try {
+ Configuration.library_url = new URL(url);
+ running = true;
+ }
+ catch (MalformedURLException exception) {
+ running = false;
+ exception.printStackTrace();
+ DebugStream.printStackTrace(exception);
+ }
+ return false;
+ }
+ }
+
+ // If either the URL is null--which means no independent GSI (Greenstone server interface
+ // app) was launched--or if the 'URL' doesn't say it's pending, then we're not waiting
+ return false;
+ }
+
+ /** @returns true if it had to restart the server. Returns false if it didn't restart it. */
+ static public boolean checkServerRunning() {
+ boolean serverRestarted = false;
+
+ if (!running) return false; // don't worry about it if it's not supposed to be running
+ llssite_cfg_file.load(true); // don't force reload, load only if modified
+
+ String url = llssite_cfg_file.getURL();
+ if(url != null) {
+ if(url.equals(LLSSiteConfig.URL_PENDING)) {
+ running = false;
+ return false;
+ }
+
+ // else, valid URL:
+ try {
+ Configuration.library_url = new URL(url);
+ running = true;
+ }
+ catch (MalformedURLException exception) {
+ running = false;
+ DebugStream.printStackTrace(exception);
+ exception.printStackTrace();
+ }
+ }
+ else { // NO URL in current mode of configFile, try other mode (e.g. "gli.url")
+
+ if(llssite_cfg_file.usingGSImode()) { // if a GSI is already using llssite_cfg, this would have loaded it in
+ url = llssite_cfg_file.getURL(); //if(isURLPending()) {
+ if(url.equals(LLSSiteConfig.URL_PENDING)) {
+ running = false;
+ } else {
+ running = true;
+ }
+ return false; // don't need to launch the server, one has been independently launched
+ } else {
+ // else we try using the "gli." prefix to access the url property in the config file
+ llssite_cfg_file.useGLImode();
+ //llssite_cfg_file.set();
+
+ // since we're going to restart the server, make sure to reset all
+ // the client socket and its writer (for communicating with the web server)
+ // will only be reinstantiated if they are first nulled
+ if(clientSocket != null) {
+ clientSocket = null;
+ }
+ if(clientSocketWriter != null) {
+ try{
+ clientSocketWriter.close();
+ clientSocketWriter = null;
+ } catch(Exception e) {
+ System.err.println("Unable to close the client socket outputstream.");
+ } finally {
+ clientSocketWriter = null;
+ }
+ }
+
+ // Spawn local library server process
+ running = false;
+ final String QUOTES = Utility.isWindows() ? "\"" : ""; // need to embed path in quotes on Windows for spaces (interferes on Linux)
+ String local_library_server_command = QUOTES+local_library_server_file.getAbsolutePath()+QUOTES + getExtraLaunchArguments(llssite_cfg_file);
+ if(Utility.isWindows() && !isPersistentServer) { // launching gs2-web-server.bat (Windows) needs cmd start
+ local_library_server_command = "cmd.exe /c start \"GSI\" " + local_library_server_command;
+ }
+ Gatherer.spawnApplication(local_library_server_command, ID);
+ try {
+ testServerRunning(); // don't return until the webserver is up and running
+ serverRestarted = true;
+ } catch (IOException bad_url_connection) {
+ DebugStream.println("Can't connect to server on address " + Configuration.library_url);
+ running = false;
+ }
+ }
+ }
+ return serverRestarted;
+ }
+
+ static private class OneSecondWait
+ {
+ public OneSecondWait()
+ {
+ synchronized(this) {
+ try {
+ wait(1000);
+ }
+ catch (InterruptedException exception) {
+ }
+ }
+ }
+ }
+
+
+ static public class LLSSiteConfig
+ extends LinkedHashMap
+ {
+ private File configFile;
+
+ private String autoenter_initial;
+ private String start_browser_initial;
+
+ private long lastModified = 0;
+ private boolean isIndependentGSI;
+
+ static final private String GLI_PREFIX = "gli";
+ static final private String GSI_AUTOENTER = "autoenter";
+ static final private String AUTOENTER = GLI_PREFIX+"."+"autoenter";
+ static final private String COLON = ":";
+ static final private String ENTERLIB = "enterlib";
+ static final private String FALSE = "0";
+ static final private String GSDL = "greenstone"; // httpprefix is no longer /gsdl but /greenstone
+ static final private String LLSSITE_CFG = "llssite.cfg";
+ static final private String LOCAL_HOST = "http://localhost";
+ static final private String PORTNUMBER = "portnumber";
+ static final private String SEPARATOR = "/";
+ static final private String SPECIFIC_CONFIG = "--config=";
+ static final private String STARTBROWSER = GLI_PREFIX+"."+"start_browser";
+ static final private String GSI_STARTBROWSER = "start_browser";
+ static final private String TRUE = "1";
+ static final private String URL = GLI_PREFIX+"."+"url";
+ static final private String GSI_URL = "url";
+ static final private String COLLECTHOME = "collecthome";
+ static final private String GLIMODE = "--mode="+GLI_PREFIX;
+
+ static final public String URL_PENDING = "URL_pending";
+
+
+ public LLSSiteConfig(File server_exe) {
+ debug("New LLSSiteConfig for: " + server_exe.getAbsolutePath());
+
+ configFile = new File(server_exe.getParentFile(), LLSSITE_CFG);
+ if(!configFile.exists()) { // create it from the template
+ File llssite_cfg_in = new File(server_exe.getParentFile(), LLSSITE_CFG+".in");
+
+ // need to generate llssite_cfg from llssite_cfg_in
+ if(llssite_cfg_in.exists()) {
+ copyConfigFile(llssite_cfg_in, configFile, true); // adjust for gli
+ }
+ else {
+ debug("File llssite.cfg.in can't be found. Can't create llssite.cfg frrom llssite.cfg.in.");
+ }
+ }
+
+ // use the config file now
+ if(configFile.exists()) {
+ // first test if server was started independently of GLI
+ // if so, the config file we'd be using would be llssite.cfg
+ if(!usingGSImode()) { // this step will try to access the url and other
+ // special properties in the config file using the "gli." prefix.
+ useGLImode();
+ }
+
+ } else {
+ System.err.println("**** ERROR. Configfile is null.");
+ configFile = null;
+ }
+
+ autoenter_initial = null;
+ start_browser_initial = null;
+
+ // GS2. We're in the constructor, so we'll be running the LLS the first time
+ // Set the correct collectdir (from last time) before the server is started up
+
+ String orig_collection_path = Configuration.getString("general.open_collection_gs2", true);
+ String collectDir = Gatherer.getCollectDirectoryPath(); // Gatherer would've set this up
+ String defaultColDir = Gatherer.getDefaultGSCollectDirectoryPath(true); // with file separator at end
+
+ if(orig_collection_path.equals("")) { // default GS collect dir path
+ return;
+ }
+
+ if (!orig_collection_path.startsWith(collectDir) // if coldir would've been changed on startup OR if coldir is non-standard collect folder
+ || !collectDir.equals(defaultColDir))
+ {
+ // if we're running *server.exe* and if the current collect dir at Local Lib's startup is
+ // anything other than the default GS collect dir or if we changed back to the default collect
+ // dir because the non-standard collectDir didn't exist/wasn't specified in the GLI config
+ // file, then write out the new collectDir (minus file separator at end) to the lls site conf in use
+ // Regardless of what collecthome value was in the llssite file, we end up resetting it here
+ if (isPersistentServer) { // server.exe, so we're dealing with a local GS2
+ put(COLLECTHOME, collectDir);
+ save();
+ lastModified = configFile.lastModified();
+ }
+ } // now the correct current collect dir will get loaded when the server is started up hereafter
+ }
+
+ /** Changes the mode to GLI mode (since GSI was not launched independently but by GLI): in this
+ * mode, the "gli." prefix is used to access GLI specific properties from the config file */
+ public void useGLImode()
+ {
+ isIndependentGSI = false;
+ load(true); // reload if modified
+ }
+
+ /** Tests whether the server interface is up, running independently of GLI
+ * If so, we don't need to launch the server interface.
+ * The server interface may not have started up the server itself though
+ * (in which case the server URL would be URL_PENDING).
+ * This method returns true if the server interface has already started
+ * and, if so, it would have loaded in the configFile. It sets the mode to
+ * GSI Mode (meaning GSI was launched independently): no prefix is used to
+ * access properties from the config file.
+ */
+ public boolean usingGSImode() {
+ // Now to check if the configfile contains the GSI_URL line
+ load(false); // force load
+ isIndependentGSI = true;
+ if(getURL() == null) {
+ isIndependentGSI = false;
+ return false;
+ }
+
+ lastModified = configFile.lastModified();
+ return true;
+ }
+
+ /** To test we've actually instantiated this object meaningfully. If so, then configFile is set */
+ public boolean isConfigFileSet() {
+ return (configFile != null && configFile.exists());
+ }
+
+ /** @return true if GSI was started up independently and outside of GLI.
+ * In such a case, GLI would be using llssite_cfg. */
+ public boolean isIndependentGSI() {
+ return isIndependentGSI;
+ }
+
+ public boolean exists() {
+ return configFile.exists();
+ }
+
+ public String getLocalHostURL() {
+ StringBuffer url = new StringBuffer(LOCAL_HOST);
+ url.append(COLON);
+ url.append((String)get(PORTNUMBER));
+ String enterlib = (String)get(ENTERLIB);
+ if(!isPersistentServer || enterlib == null || enterlib.length() == 0) {
+ // Use the default /gsdl and hope for the best.
+ url.append(SEPARATOR);
+ url.append(GSDL);
+ }
+ else {
+ if(!enterlib.startsWith(SEPARATOR)) {
+ url.append(SEPARATOR);
+ }
+ url.append(enterlib);
+ }
+ enterlib = null;
+ debug("Found Local Library Address: " + url.toString());
+ return url.toString();
+ }
+
+ /** @return the cmd-line parameter for the configfile used to launch
+ * the server through GLI: --config=. */
+ public String getSiteConfigFilename() {
+ return SPECIFIC_CONFIG + configFile.getAbsolutePath();
+ }
+
+ public String getURL() {
+ String urlPropertyName = isIndependentGSI() ? GSI_URL : URL;
+ // URL is made from url and portnumber
+ String url = (String) get(urlPropertyName);
+
+ // server interface is already up, independent of GLI
+ // but it has not started the server (hence URL is pending)
+ if(url != null && url.equals(URL_PENDING)) {
+ return url;
+ }
+
+ if(!isPersistentServer) {
+ return url;
+ }
+
+ if(url != null) {
+ StringBuffer temp = new StringBuffer(url);
+ temp.append(COLON);
+ temp.append((String)get(PORTNUMBER));
+ String enterlib = (String)get(ENTERLIB);
+ if(enterlib == null || enterlib.length() == 0) {
+ // Use the default /greenstone prefix and hope for the best.
+ temp.append(SEPARATOR);
+ temp.append(GSDL);
+ }
+ else {
+ if(!enterlib.startsWith(SEPARATOR)) {
+ temp.append(SEPARATOR);
+ }
+ temp.append(enterlib);
+ }
+ enterlib = null;
+ url = temp.toString();
+ }
+ debug("Found Local Library Address: " + url);
+ return url;
+ }
+
+ public void setLastModified() {
+ if(isModified()) {
+ lastModified = configFile.lastModified();
+ }
+ }
+
+ public boolean isModified() {
+ return (lastModified != configFile.lastModified());
+ }
+
+ public void load(boolean reloadOnlyIfModified) {
+
+ if(configFile == null) {
+ debug(configFile.getAbsolutePath() + " cannot be found!");
+ }
+
+ if(isModified()) {
+ lastModified = configFile.lastModified();
+ } else if(reloadOnlyIfModified) {
+ return; // asked to reload only if modified. Don't reload since not modified
+ }
+
+ if(configFile.exists()) {
+ debug("Load: " + configFile.getAbsolutePath());
+ clear();
+ try {
+ BufferedReader in = new BufferedReader(new FileReader(configFile));
+ String line = null;
+ while((line = in.readLine()) != null) {
+ String key = null;
+ String value = null;
+ int index = -1;
+ if((index = line.indexOf("=")) != -1 && line.length() >= index + 1) {
+ key = line.substring(0, index);
+ value = line.substring(index + 1);
+ }
+ else {
+ key = line;
+ }
+ put(key, value);
+ }
+
+ in.close();
+ }
+ catch (Exception error) {
+ error.printStackTrace();
+ }
+ }
+ else {
+ debug(configFile.getAbsolutePath() + " cannot be found!");
+ }
+ }
+
+ /** Restore the autoenter value to its initial value, and remove url if present. */
+ public void restore() {
+ if(configFile != null) {
+ // Delete the file
+ //configFile.delete();
+
+ // Not restoring nor deleting here. Just removing the URL (which indicates a running server),
+ // so that the file's state indicates the server has stopped, and then saving the property file.
+ // Should all properties not defined in the llssite.cfg.in template be removed below?
+ String urlPropertyName = isIndependentGSI() ? GSI_URL : URL;
+ remove(urlPropertyName);
+
+ remove("gsdlhome");
+ remove("collecthome");
+ remove("gdbmhome");
+ remove("logfilename");
+
+ // For some reason launching GLI with server.exe clobbers autoenter and startbrowser
+ // values for independent GSI in llssite.cfg file, and gli.autoenter and gli.startbrowser.
+ // So set them here.
+ if(get(GSI_AUTOENTER) == null) {
+ put(GSI_AUTOENTER, "0");
+ }
+ if(get(GSI_STARTBROWSER) == null) {
+ put(GSI_STARTBROWSER, "1");
+ }
+ if(get(AUTOENTER) == null) {
+ put(AUTOENTER, "1");
+ }
+ if(get(STARTBROWSER) == null) {
+ put(STARTBROWSER, "0");
+ }
+ save();
+ }
+ else {
+ String urlPropertyName = isIndependentGSI() ? GSI_URL : URL;
+ debug("Restore Initial Settings");
+ put(AUTOENTER, autoenter_initial);
+ put(STARTBROWSER, start_browser_initial);
+ remove(urlPropertyName);
+ save();
+ }
+ }
+
+ public void set() {
+ debug("Set Session Settings");
+ if(autoenter_initial == null) {
+ autoenter_initial = (String) get(AUTOENTER);
+ debug("Remember autoenter was: " + autoenter_initial);
+ }
+ put(AUTOENTER, TRUE);
+ if(start_browser_initial == null) {
+ start_browser_initial = (String) get(STARTBROWSER);
+ debug("Remember start_browser was: " + start_browser_initial);
+ }
+ put(STARTBROWSER, FALSE);
+ save();
+ }
+
+ private void debug(String message) {
+ ///ystem.err.println(message);
+ }
+
+ private void save() {
+ debug("Save: " + configFile.getAbsolutePath());
+ try {
+ BufferedWriter out = new BufferedWriter(new FileWriter(configFile, false));
+ for(Iterator keys = keySet().iterator(); keys.hasNext(); ) {
+ String key = (String) keys.next();
+ String value = (String) get(key);
+ out.write(key, 0, key.length());
+ if(value != null) {
+ out.write('=');
+
+ // if the GSI was launched outside of GLI, don't overwrite its default
+ // autoenter and startbrowser values
+ if(isIndependentGSI && (key == GSI_AUTOENTER || key == GSI_STARTBROWSER)) {
+ if(key == GSI_AUTOENTER) {
+ out.write(autoenter_initial, 0, autoenter_initial.length());
+ } else { // GSI_STARTBROWSER
+ out.write(start_browser_initial, 0, start_browser_initial.length());
+ }
+ } else {
+ out.write(value, 0, value.length());
+ }
+ }
+ out.newLine();
+ }
+ out.flush();
+ out.close();
+ }
+ catch (Exception error) {
+ error.printStackTrace();
+ }
+ }
+
+ private static void copyConfigFile(File source_cfg, File dest_cfg, boolean setToGliSiteDefaults) {
+ // source_cfg file should exist
+ // dest_cfg file should not yet exist
+ // If setToGliSiteDefaults is true, then GLIsite.cfg's default configuration
+ // is applied to concerned lines: gli.autoenter=1, and gli.startbrowser=0
+
+ try {
+ BufferedReader in = new BufferedReader(new FileReader(source_cfg));
+ BufferedWriter out = new BufferedWriter(new FileWriter(dest_cfg, false));
+
+ String line = null;
+ while((line = in.readLine()) != null) {
+
+ if(setToGliSiteDefaults) {
+ if(line.startsWith(AUTOENTER)) {
+ line = AUTOENTER+"=1";
+ }
+ else if(line.startsWith(STARTBROWSER)) {
+ line = STARTBROWSER+"=0";
+ }
+ }
+
+ // write out the line
+ out.write(line);
+ out.newLine();
+ }
+
+ out.flush();
+ in.close();
+ out.close();
+ } catch(Exception e) {
+ System.err.println("Exception occurred when trying to copy the config file "
+ + source_cfg.getName() + " to " + dest_cfg.getName() + ": " + e);
+ e.printStackTrace();
+ }
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/greenstone/Plugins.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/greenstone/Plugins.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/greenstone/Plugins.java (revision 31635)
@@ -0,0 +1,338 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, NZDL Project, University of Waikato
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.greenstone;
+
+import java.io.*;
+import java.util.*;
+import javax.swing.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.cdm.Argument;
+import org.greenstone.gatherer.cdm.Plugin;
+import org.greenstone.gatherer.remote.RemoteGreenstoneServer;
+import org.greenstone.gatherer.util.StaticStrings;
+import org.greenstone.gatherer.util.Utility;
+import org.greenstone.gatherer.util.XMLTools;
+import org.w3c.dom.*;
+import org.xml.sax.*;
+
+
+/** This class is for maintaining a list of known plug-ins, and importing new plugins using the parser. */
+public class Plugins
+{
+ // A list of all the plugins in the core Greenstone "perllib/plugins" folder (arguments may not be loaded)
+ static private ArrayList core_greenstone_plugins_list = null;
+ // The name of the loaded collection
+ static private String collection_name = null;
+ // A list of all the plugins in the loaded collection's "perllib/plugins" folder (arguments may not be loaded)
+ static private ArrayList collection_specific_plugins_list = new ArrayList();
+
+
+ static public Plugin getPlugin(String plugin_name, boolean arguments_required)
+ {
+ Plugin plugin = null;
+ boolean collection_specific = false;
+
+ // Check the collection-specific plugins first
+ for (int i = 0; i < collection_specific_plugins_list.size(); i++) {
+ Plugin collection_specific_plugin = (Plugin) collection_specific_plugins_list.get(i);
+ if (collection_specific_plugin.getName().equals(plugin_name)) {
+ plugin = collection_specific_plugin;
+ collection_specific = true;
+ break;
+ }
+ }
+
+ // Try the core Greenstone plugins if necessary
+ if (plugin == null) {
+ for (int i = 0; i < core_greenstone_plugins_list.size(); i++) {
+ Plugin core_greenstone_plugin = (Plugin) core_greenstone_plugins_list.get(i);
+ if (core_greenstone_plugin.getName().equals(plugin_name)) {
+ plugin = core_greenstone_plugin;
+ break;
+ }
+ }
+ }
+
+ // If we've found the plugin, load its arguments now, if required
+ if (plugin != null && arguments_required) {
+ if (!plugin.hasLoadedOptions()) {
+ loadPluginInfo(plugin, collection_specific);
+ }
+ else {
+ DebugStream.println("Already loaded arguments for " + plugin_name + "!");
+ }
+ }
+
+ return plugin;
+ }
+
+
+ /** Returns a new list from merging the collection-specific and the core Greenstone plugins. */
+ static public ArrayList getPluginsList()
+ {
+ ArrayList plugins_list = new ArrayList();
+ plugins_list.addAll(collection_specific_plugins_list);
+
+ // Add in the core Greenstone plugins, taking care not to overwrite any collection-specific ones
+ for (int i = 0; i < core_greenstone_plugins_list.size(); i++) {
+ Plugin core_greenstone_plugin = (Plugin) core_greenstone_plugins_list.get(i);
+
+ boolean found = false;
+ for (int j = 0; j < collection_specific_plugins_list.size(); j++) {
+ Plugin collection_specific_plugin = (Plugin) collection_specific_plugins_list.get(j);
+ if (core_greenstone_plugin.getName().equals(collection_specific_plugin.getName())) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ plugins_list.add(core_greenstone_plugin);
+ }
+ }
+
+ return plugins_list;
+ }
+
+
+ static private void loadPluginInfo(Plugin plugin, boolean collection_specific)
+ {
+ DebugStream.println("Loading arguments for " + plugin.getName() + "...");
+
+ // Run pluginfo.pl to get the list of plugins
+ try {
+ String pluginfo_xml = null;
+ if (Gatherer.isGsdlRemote) {
+ String pluginfo_options = "&plugin=" + plugin;
+ if (collection_specific) {
+ pluginfo_options += "&collection=" + collection_name;
+ }
+ pluginfo_xml = Gatherer.remoteGreenstoneServer.getScriptOptions("pluginfo.pl", pluginfo_options);
+ }
+ else {
+ ArrayList args = new ArrayList();
+ args.add(Configuration.perl_path);
+ args.add("-S");
+ args.add(LocalGreenstone.getBinScriptDirectoryPath() + "pluginfo.pl");
+ args.add("-gs_version");
+ if (Gatherer.GS3) {
+ args.add("3");
+ } else {
+ args.add("2");
+ }
+ if (collection_specific) {
+ args.add("-collection");
+ args.add(collection_name);
+ if (Gatherer.GS3) {
+ args.add("-site");
+ args.add(Configuration.site_name);
+ }
+ }
+ args.add("-xml");
+ args.add("-language");
+ args.add(Configuration.getLanguage());
+ args.add(plugin.getName());
+ // Run the pluginfo.pl process
+ Runtime runtime = Runtime.getRuntime();
+ Process process = runtime.exec((String[]) args.toArray(new String[] { }));
+ InputStream input_stream = process.getErrorStream();
+ StringBuffer pluginfo_xml_buffer = XMLTools.readXMLStream(input_stream);
+ if (pluginfo_xml_buffer != null) {
+ pluginfo_xml = pluginfo_xml_buffer.toString();
+ }
+ }
+
+ // Check the XML output was obtained successfully
+ if (pluginfo_xml == null || pluginfo_xml.length() == 0) {
+ plugin.setHasLoadedOptions(false); // failure to load options
+ JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CDM.PlugInManager.PlugIn_XML_Parse_Failed", plugin.getName()), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
+ return;
+ } else {
+ plugin.setHasLoadedOptions(true);
+ }
+
+ parsePluginInfoXML(plugin, pluginfo_xml);
+ }
+ catch (Exception exception) {
+ DebugStream.printStackTrace(exception);
+ }
+ }
+
+
+ static public void loadPluginsList(String collection_name_arg)
+ {
+ DebugStream.println("In loadPluginsList()...");
+
+ // If we're getting the collection-specific plugins, clear the old list no matter what
+ if (collection_name_arg != null) {
+ collection_name = collection_name_arg;
+ collection_specific_plugins_list = new ArrayList();
+ }
+
+ // Run pluginfo.pl to get the list of plugins
+ try {
+ StringBuffer xml = null;
+ if (Gatherer.isGsdlRemote) {
+ String pluginfo_options = "&listall";
+ if (collection_name != null) {
+ pluginfo_options += "&collection=" + collection_name;
+ }
+ String pluginfo_output = Gatherer.remoteGreenstoneServer.getScriptOptions("pluginfo.pl", pluginfo_options);
+ xml = new StringBuffer(pluginfo_output);
+ }
+ else {
+ ArrayList args = new ArrayList();
+ args.add(Configuration.perl_path);
+ args.add("-S");
+ args.add(LocalGreenstone.getBinScriptDirectoryPath() + "pluginfo.pl");
+ args.add("-gs_version");
+ if (Gatherer.GS3) {
+ args.add("3");
+ } else {
+ args.add("2");
+ }
+ if (collection_name != null) {
+ args.add("-collection");
+ args.add(collection_name);
+ if (Gatherer.GS3) {
+ args.add("-site");
+ args.add(Configuration.site_name);
+ }
+ }
+ args.add("-listall");
+ args.add("-xml");
+
+ // Run the pluginfo.pl process
+ Runtime runtime = Runtime.getRuntime();
+ Process process = runtime.exec((String[]) args.toArray(new String[] { }));
+ InputStream input_stream = process.getErrorStream();
+ xml = XMLTools.readXMLStream(input_stream);
+ }
+
+ // Check the XML output was obtained successfully
+ if (xml == null || xml.length() == 0) {
+ JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CDM.PluginManager.Plugin_List_XML_Parse_Failed"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ if (collection_name != null) {
+ collection_specific_plugins_list = parsePluginsListXML(xml.toString());
+ }
+ else {
+ core_greenstone_plugins_list = parsePluginsListXML(xml.toString());
+ }
+ }
+ catch (Exception exception) {
+ DebugStream.printStackTrace(exception);
+ }
+ }
+
+
+ static private void parsePluginInfoXML(Plugin plugin, String xml)
+ {
+ Document document = XMLTools.parseXML(new StringReader(xml));
+ if (document == null) {
+ plugin.setHasLoadedOptions(false); // failure to load the options/failed plugin
+ JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CDM.PlugInManager.PlugIn_XML_Parse_Failed", plugin.getName()), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
+ return;
+ } else {
+ plugin.setHasLoadedOptions(true);
+ }
+
+ parsePluginInfoXMLNode(plugin, document.getDocumentElement());
+ }
+
+
+ static private void parsePluginInfoXMLNode(Plugin plugin, Node root_node)
+ {
+ for (Node node = root_node.getFirstChild(); node != null; node = node.getNextSibling()) {
+ String node_name = node.getNodeName();
+
+ if (node_name.equalsIgnoreCase("Name")) {
+ plugin.setName(XMLTools.getValue(node));
+ }
+ else if (node_name.equals("Desc")) {
+ plugin.setDescription(XMLTools.getValue(node));
+ }
+ else if (node_name.equals("Abstract")) {
+ plugin.setIsAbstract(XMLTools.getValue(node).equalsIgnoreCase(StaticStrings.YES_STR));
+ }
+ else if (node_name.equalsIgnoreCase("Explodes")) {
+ plugin.setDoesExplodeMetadataDatabases(XMLTools.getValue(node).equalsIgnoreCase(StaticStrings.YES_STR));
+ }
+ else if (node_name.equalsIgnoreCase("SourceReplaceable")) { // looking for tag
+ plugin.setDoesReplaceSrcDocsWithHtml(XMLTools.getValue(node).equalsIgnoreCase(StaticStrings.YES_STR));
+ }
+ else if (node_name.equalsIgnoreCase("Processes")) {
+ plugin.setDefaultProcessExpression(XMLTools.getValue(node));
+ }
+ else if (node_name.equalsIgnoreCase("Blocks")) {
+ plugin.setDefaultBlockExpression(XMLTools.getValue(node));
+ }
+ // Parse the plugin arguments
+ else if (node_name.equalsIgnoreCase("Arguments")) {
+ for (Node argument_node = node.getFirstChild(); argument_node != null; argument_node = argument_node.getNextSibling()) {
+ // An option
+ if (argument_node.getNodeName().equalsIgnoreCase("Option")) {
+ Argument argument = new Argument();
+ argument.parseXML((Element) argument_node);
+ plugin.addArgument(argument);
+ }
+ }
+ }
+ // A super plugin class
+ else if (node_name.equalsIgnoreCase("PlugInfo")) {
+ Plugin super_plugin = new Plugin();
+ parsePluginInfoXMLNode(super_plugin, node);
+ plugin.setSuper(super_plugin);
+ }
+ }
+ }
+
+
+ static private ArrayList parsePluginsListXML(String xml)
+ {
+ ArrayList plugins_list = new ArrayList();
+
+ Document document = XMLTools.parseXML(new StringReader(xml));
+ Node root = document.getDocumentElement();
+ for (Node node = root.getFirstChild(); node != null; node = node.getNextSibling()) {
+ String node_name = node.getNodeName();
+
+ if (node_name.equals("PlugInfo")) {
+ Plugin plugin = new Plugin();
+ parsePluginInfoXMLNode(plugin, node);
+ plugins_list.add(plugin);
+ }
+ }
+
+ return plugins_list;
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/greenstone3/ServletConfiguration.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/greenstone3/ServletConfiguration.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/greenstone3/ServletConfiguration.java (revision 31635)
@@ -0,0 +1,189 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.greenstone3;
+
+import java.awt.*;
+import java.io.*;
+import java.lang.ref.*;
+import java.net.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.plaf.*;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.util.ArrayTools;
+import org.greenstone.gatherer.util.StaticStrings;
+import org.greenstone.gatherer.util.Utility;
+import org.greenstone.gatherer.util.XMLTools;
+import org.greenstone.gatherer.remote.RemoteGreenstoneServer;
+import org.w3c.dom.*;
+
+/** This class stores the mapping between sites and servlets available for them. The info is read in from the web.xml file.
+ * @author Katherine Don, New Zealand Digital Library, University of Waikato
+ * @version
+ */
+public class ServletConfiguration {
+
+ static public String ADD_COMMAND = "?a=s&sa=a&st=collection&sn=";
+ static public String DEACTIVATE_COMMAND = "?a=s&sa=d&st=collection&sn=";
+ private String gsdl3_path = "";
+ private ArrayList sites = null;
+ private HashMap mappings = null;
+
+ public ServletConfiguration(String gsdl3_path) {
+
+ this.gsdl3_path = gsdl3_path;
+
+ if (Gatherer.isGsdlRemote){
+ if (Gatherer.remoteGreenstoneServer.downloadWebXMLFile().equals("")) {
+ System.err.println("Error: Could not download web.xml.");
+ System.exit(0);
+ }
+ }
+ //String web_xml_path = gsdl3_path + File.separator + "web" + File.separator + "WEB-INF"+ File.separator + "web.xml";
+ File web_xml;
+ if (Gatherer.isGsdlRemote){
+ web_xml = new File(gsdl3_path + "web.xml");
+ }else{
+ web_xml = new File(gsdl3_path + "WEB-INF"+ File.separator + "web.xml");
+ }
+ if (!web_xml.exists()) {
+ DebugStream.println("Error: no web.xml found at "+web_xml.toString());
+ return;
+ }
+
+ this.sites = new ArrayList();
+ if (Gatherer.isGsdlRemote){
+ String sites_on_server = Gatherer.remoteGreenstoneServer.getSiteNames();
+ if (sites_on_server.equals("")) {
+ // !! Something went wrong : could not get names of the sites
+ System.err.println("Error: Could not get names of the sites.");
+ System.exit(0);
+ }
+
+ String[] sites_arr=sites_on_server.split("-----");
+ for (int i=0; i
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.io.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.tree.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.cdm.CollectionDesignManager;
+import org.greenstone.gatherer.cdm.Control;
+
+/** This class is responsible for generating the necessary GUI components. It does this by calling the getEditControls() method of the appropriate class (i.e. IndexManager for index related edits). It also is in charge of correctly adding (and removing) listeners, using phrases from the Dictionary rather than the text keys inserted in the component classes, and several other aspects of the design page, including the config file section tree.
+
+/** This is a base class for Design and Format panes, which are very similar except that they have different components. */
+public abstract class BaseConfigPane
+ extends JPanel {
+
+ /* This should list the contents of the contents */
+ protected String contents[];
+
+ /** The preferred size of the contents tree. */
+ static final private Dimension TREE_SIZE = new Dimension(200, 500);
+
+ /** The panel apon which is rendered the currently selected section screen. */
+ protected Control view = null;
+ protected String view_type = null;
+ /** The collection manager is responsible for parsing in, and allowing the editing of, a single collections configuration file. */
+ protected CollectionDesignManager cdm = null;
+ /** A tree to serve as a 'table of contents' for this design tool. We decided on a tree rather than a list, as it allows us to break sections into subsections if they become to complicated. */
+ protected DesignTree tree;
+ protected JPanel tree_pane;
+ /** The constructor. */
+ public BaseConfigPane() {
+ super();
+ DebugStream.println("BaseConfigPane: Main GUI components created.");
+ // Creation
+ this.setComponentOrientation(Dictionary.getOrientation());
+ tree_pane = new JPanel();
+ tree_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ tree = new DesignTree();
+ tree.setComponentOrientation(Dictionary.getOrientation());
+ // Connect
+ tree.addTreeSelectionListener(new TreeListener());
+
+ // Layout
+ tree_pane.setLayout(new BorderLayout());
+ tree_pane.setPreferredSize(TREE_SIZE);
+ JScrollPane scrol_tmp;
+ scrol_tmp = new JScrollPane(tree);
+ scrol_tmp.setComponentOrientation(Dictionary.getOrientation());
+ tree_pane.add(scrol_tmp, BorderLayout.CENTER);
+ setLayout(new BorderLayout());
+ setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+
+ add(tree_pane, BorderLayout.LINE_START);
+
+
+ }
+
+ abstract protected Control getSubControls(String type);
+
+ /** Called to cause the components to lay themselves out and be displayed.
+ */
+ public void display() {
+ }
+
+
+ public void gainFocus()
+ {
+ if (cdm == null && Gatherer.c_man.ready()) {
+ // Retrieve the new config manager.
+ cdm = Gatherer.c_man.getCollection().cdm;
+ }
+ if (view != null) {
+ view.gainFocus();
+ }
+ }
+
+
+ public void loseFocus()
+ {
+ if (view != null) {
+ view.loseFocus();
+ }
+ if (cdm != null) {
+ Gatherer.c_man.saveCollection();
+ }
+ }
+
+
+ public void destroy() {
+ tree = null;
+ view = null;
+ }
+
+ /** Called whenever the detail mode changes to ensure the filters are at an appropriate level (ie only editable by those that understand regular expression matching)
+ * @param mode the mode level as an int
+ */
+ public void modeChanged(int mode) {
+ if (cdm != null) {
+ cdm.modeChanged(mode);
+ }
+ }
+
+ /** This method is called whenever the state of the current collection changes.
+ */
+ public void refresh(int refresh_reason, boolean ready)
+ {
+ if (ready) {
+ // Retrieve the new config manager.
+ cdm = Gatherer.c_man.getCollection().cdm;
+ tree.resetModel(Configuration.getMode()); // does this set view??
+ }
+ }
+
+
+ /** Force the display to show a certain pane of controls.
+ * @param type a String giving the name of the information manager or view we wish to display
+ */
+ public void setSelectedView(String type) {
+ tree.setSelectedView(type);
+ }
+
+ /** This tree provides a 'table of contents' for the various components of the design process (collection configuration in more technical terms). */
+ private class DesignTree
+ extends JTree {
+ private DesignNode root = null;
+ /** Constructor. Automatically generates all of the nodes, in the order of contents. */
+ public DesignTree() {
+ super();
+ }
+
+ /** Reset the model used by the design page contents tree. This is necessary to hide the partitions entry when in lower detail modes
+ * @param mode the current detail mode as an int
+ */
+ public void resetModel(int mode) {
+ root = new DesignNode("CDM.GUI.Root");
+ // Now add the design categories.
+ for(int i = 0; i < contents.length; i++) {
+ root.add(new DesignNode(contents[i]));
+ }
+ this.setModel(new DefaultTreeModel(root));
+ expandRow(0);
+ setRootVisible(false);
+ setSelectionRow(0);
+ updateUI();
+ }
+ /** Set the current view to the one specified.
+ * @param type the name of the desired view as a String
+ */
+ public void setSelectedView(String type) {
+ type = Dictionary.get(type);
+ for(int i = 0; i < root.getChildCount(); i++) {
+ DesignNode child = (DesignNode) root.getChildAt(i);
+ if(child.toString().equals(type)) {
+ TreePath path = new TreePath(child.getPath());
+ setSelectionPath(path);
+ }
+ }
+ }
+ }
+ /** A tree node that retains a reference to one of the possible design sub-views relating to the different sub-managers. */
+ private class DesignNode
+ extends DefaultMutableTreeNode {
+ /** Constructor.
+ * @param object The Object assigned to this node.
+ */
+ public DesignNode(String object) {
+ super(object);
+ }
+ /** Retrieve a textual representation of the object.
+ * @return a String
+ */
+ public String toString() {
+ // return Dictionary.get("CDM.GUI." + (String)getUserObject());
+ return Dictionary.get((String) getUserObject());
+ }
+ }
+ /** Listens for selection changes in the 'contents' tree, and switches to the appropriate view. */
+ private class TreeListener
+ implements TreeSelectionListener {
+ /** Called whenever the selection changes, we must update the view so it matches the node selected.
+ * @param event A TreeSelectionEvent containing more information about the tree selection.
+ */
+ public void valueChanged(TreeSelectionEvent event) {
+ if(!tree.isSelectionEmpty()) {
+ TreePath path = tree.getSelectionPath();
+ DesignNode node = (DesignNode)path.getLastPathComponent();
+ String type = (String)node.getUserObject();
+ // Wait begins
+ Gatherer.g_man.wait(true);
+ // Save information in current view
+ if (view != null) { // can we get rid of this??
+ view.loseFocus();
+ remove((JPanel)view);
+ }
+ view_type = type;
+ // Change panes.
+ view = getSubControls(type);
+ if (view != null) {
+ add((JPanel)view, BorderLayout.CENTER);
+ // Update information on visible pane
+ view.gainFocus();
+ }
+ repaint();
+ // Wait ends
+ Gatherer.g_man.wait(false);
+ }
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/ConfigFileEditor.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/ConfigFileEditor.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/ConfigFileEditor.java (revision 31635)
@@ -0,0 +1,271 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+
+package org.greenstone.gatherer.gui;
+
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.util.StaticStrings;
+import org.greenstone.gatherer.util.Utility;
+import org.greenstone.gatherer.util.XMLTools;
+
+import org.w3c.dom.*;
+
+
+import java.io.File;
+import java.io.IOException;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.event.*;
+
+
+public class ConfigFileEditor extends ModalDialog
+ implements ActionListener, DocumentListener
+{
+ public final File config_file;
+
+ private GLIButton cancel_button = null;
+ private GLIButton save_button = null;
+ private NumberedJTextArea editor = null;
+ private JTextArea editor_msgarea;
+
+ //private final String DEFAULT_PROCESSING_INSTRUCTION = "";
+
+ private static final Dimension SIZE = new Dimension(850,550);
+
+ public ConfigFileEditor() {
+
+ super(Gatherer.g_man, true);
+ setModal(true);
+ setSize(SIZE);
+
+ String collection_folder_path = Gatherer.getCollectDirectoryPath();
+ String collectionName = Gatherer.c_man.getCollection().getName(); // we won't be in this constructor if collection is null.
+ this.config_file = new File(collection_folder_path + File.separator + collectionName, Utility.CONFIG_GS3_FILE); // col config editing is a GS3 feature
+
+
+ // Most of the validation_msg_panel code is from Format4gs3Manager.java
+ JPanel validation_msg_panel = new JPanel();
+ JLabel validation_label = new JLabel(Dictionary.get("CDM.FormatManager.MessageBox"));
+ validation_label.setBorder(new EmptyBorder(10,10,10,10)); // add some margin
+ editor_msgarea = new JTextArea();
+
+ editor_msgarea.setCaretPosition(0);
+ editor_msgarea.setLineWrap(true);
+ editor_msgarea.setRows(3);
+ editor_msgarea.setWrapStyleWord(false);
+ editor_msgarea.setEditable(false);
+ editor_msgarea.setToolTipText(Dictionary.get("CDM.FormatManager.MessageBox_Tooltip"));
+ validation_msg_panel.setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0));
+ validation_msg_panel.setLayout(new BorderLayout(5, 0));
+ validation_msg_panel.add(validation_label, BorderLayout.WEST);
+ validation_msg_panel.add(new JScrollPane(editor_msgarea), BorderLayout.CENTER);
+
+ // the all important XML editor
+ editor = new NumberedJTextArea(Dictionary.get("ConfigFileEditor.Tooltip"));
+ editor.getDocument().addDocumentListener(this);
+
+ save_button = new GLIButton(Dictionary.get("General.Save"), Dictionary.get("ConfigFileEditor.Save_Tooltip"));
+ cancel_button = new GLIButton(Dictionary.get("General.Cancel"), Dictionary.get("ConfigFileEditor.Cancel_Tooltip")); // tooltip is the same
+ cancel_button.addActionListener(this);
+ save_button.addActionListener(this);
+
+ // LAYOUT
+ JPanel button_panel = new JPanel(new FlowLayout());
+ button_panel.setComponentOrientation(Dictionary.getOrientation());
+ button_panel.add(editor.undoButton);
+ button_panel.add(editor.redoButton);
+ button_panel.add(cancel_button);
+ button_panel.add(save_button);
+
+ JPanel content_pane = (JPanel) getContentPane();
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+ content_pane.setLayout(new BorderLayout());
+ content_pane.add(validation_msg_panel, BorderLayout.NORTH);
+ content_pane.add(new JScrollPane(editor), BorderLayout.CENTER); // make editor scrollable
+ content_pane.add(button_panel, BorderLayout.SOUTH);
+
+
+ // NOW WE CAN PREPARE THE ACTUAL CONTENT TO GO INTO THE XML EDITOR TEXTAREA
+ Document xmlDoc = null;
+ xmlDoc = XMLTools.parseXMLFile(config_file);
+
+ if(xmlDoc != null) {
+
+ // Save the collectionConfig.xml before loading it.
+ String[] nonEscapingTagNames = { StaticStrings.FORMAT_STR, StaticStrings.DISPLAYITEM_STR };
+ XMLTools.writeXMLFile(this.config_file, xmlDoc, nonEscapingTagNames); //prepends proc instruction
+
+ // now load the contents
+ editor_msgarea.setBackground(Color.white);
+ StringBuffer sb = new StringBuffer();
+ XMLTools.xmlNodeToString(sb, xmlDoc.getDocumentElement(), true, " ", 0);
+ editor.setText(sb.toString());
+ //editor.setText(elementToString(xmlDoc.getDocumentElement(), true)); // reading in file this way adds processing instruction, which the parser doesn't like.
+
+ } else { // parsing failed
+ // so read in file as text and display as text and show the validation box in red
+
+ // Won't re-save file that is already invalid before it's even been edited through this Editor
+ System.err.println("*** Warning: Parsing error in file " + config_file + ".");
+ System.err.println("*** Not saving ahead of editing.");
+
+ String xml_str = Utility.readFile(config_file);
+
+ // re-parse to get error msg to display in validation field
+
+ // but first get rid of the preprocessing instruction, as this will fail parsing
+ String processingInstruction = getProcessingInstruction(xml_str);
+ xml_str = xml_str.replace(processingInstruction, "").trim(); // also trim newline at start
+
+ if(!xml_str.equals("")) {
+
+ String msg = XMLTools.parse(xml_str); // re-parse
+ editor_msgarea.setBackground(Color.red);
+ editor_msgarea.setText(msg); // display the parsing error
+ save_button.setEnabled(false);
+
+ editor.setText(xml_str); // display the xml contents with error and all
+
+ } else {
+ editor.setText("");
+ editor_msgarea.setText("Empty collection config file");
+ }
+ }
+
+ // Final dialog setup & positioning.
+ setDefaultCloseOperation(DISPOSE_ON_CLOSE); // get rid of this dialog when it's closed (on dispose())
+ getRootPane().setDefaultButton(save_button);
+ Dimension screen_size = Configuration.screen_size;
+ setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+
+ if(e.getSource() == cancel_button) {
+ this.dispose(); // get rid of this dialog, we're done
+ }
+ else if(e.getSource() == save_button) {
+ // write out the XML to the collectionConfig.xml file and we're done.
+
+ // already know the xml in the textarea is valid, else the save_button would have been inactive
+ Document xml_file_doc = XMLTools.getDOM(editor.getText());
+ // This code from FormatConversionDialog.dispose()
+ // saves the XML, prepending the processing instruction ()
+ String[] nonEscapingTagNames = { StaticStrings.FORMAT_STR, StaticStrings.DISPLAYITEM_STR };
+ XMLTools.writeXMLFile(this.config_file, xml_file_doc, nonEscapingTagNames);
+
+ this.dispose(); // get rid of this dialog, we're done
+
+ // close and reopen the collection in GLI
+ String current_collection_filepath = Gatherer.c_man.getCollection().getCollectionPath();
+ Gatherer.c_man.closeCollection();
+ Gatherer.c_man.loadCollection(current_collection_filepath);
+ }
+ }
+
+
+
+ // This method returns a proper processing instruction (starts with )
+ // else the empty string is returned
+ public String getProcessingInstruction(String xmlStr) {
+ String processingInstruction = "";
+
+ xmlStr = xmlStr.trim();
+ if(xmlStr.startsWith(""); // end of processing instruction
+
+ if(endIndex != -1) {
+ endIndex += 2;
+ processingInstruction = xmlStr.substring(0, endIndex);
+ }
+ }
+ return processingInstruction;
+ }
+
+
+ // THE FOLLOWING FUNCTIONS ARE LARGELY FROM Format4gs3Manager.java.EditorListener
+ public void changedUpdate(DocumentEvent e)
+ {
+ update();
+ }
+
+ public void insertUpdate(DocumentEvent e)
+ {
+ update();
+ updateUndo("insert");
+
+ }
+
+ public void removeUpdate(DocumentEvent e)
+ {
+ update();
+ updateUndo("remove");
+
+ }
+
+ private void updateUndo(String from)
+ {
+
+ editor.undoButton.setEnabled(true);
+ }
+
+ public void update()
+ {
+
+ String xml_str = editor.getText();
+
+ // Processing instructions "" are no longer displayed in the editor (but
+ // they are written out to the file on save) so we don't need to deal with them here.
+
+ // can't parse with processing instruction , so remove that
+ ///String processingInstruction = getProcessingInstruction(xml_str);
+ ///xml_str = xml_str.replace(processingInstruction, "");
+ // now can parse the XML portion without the processing instruction,
+ // while the original XML remains unchanged in the editor area
+ // If the processing instruction was incomplete, then xml_str would still
+ // include the incomplete processing instruction, and parsing below will catch the error.
+
+ String msg = XMLTools.parse(xml_str);
+ editor_msgarea.setText(msg);
+
+ if (msg.startsWith(XMLTools.WELLFORMED))
+ {
+ editor_msgarea.setBackground(Color.white);
+ save_button.setEnabled(true);
+ }
+ else
+ {
+ editor_msgarea.setBackground(Color.red);
+ save_button.setEnabled(false);
+ }
+ }
+
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/CreatePane.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/CreatePane.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/CreatePane.java (revision 31635)
@@ -0,0 +1,1097 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.text.*;
+import javax.swing.tree.*;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.cdm.BuildTypeManager;
+import org.greenstone.gatherer.cdm.CollectionDesignManager;
+import org.greenstone.gatherer.collection.Collection;
+import org.greenstone.gatherer.shell.GBuildProgressMonitor;
+import org.greenstone.gatherer.shell.GImportProgressMonitor;
+import org.greenstone.gatherer.shell.GScheduleProgressMonitor;
+import org.greenstone.gatherer.shell.GShell;
+import org.greenstone.gatherer.shell.GShellEvent;
+import org.greenstone.gatherer.shell.GShellListener;
+import org.greenstone.gatherer.shell.GShellProgressMonitor;
+import org.greenstone.gatherer.util.AppendLineOnlyFileDocument;
+import org.greenstone.gatherer.util.StaticStrings;
+import org.greenstone.gatherer.util.Utility;
+
+import org.greenstone.gatherer.collection.CollectionManager;
+import org.greenstone.gatherer.util.GS3ServerThread;
+
+/** This class provides a GUI view showing some statistics on your current collection, and options for building it. As the collection is built this initial view is replaced with one showing progress bars and a message log of the creation process. This log can be later accessed via the options tree located in the center of the initial pane. This class is also responsible for creating the GShellProgressMonitors that are then attatched to external shell processes, and calling the methods in the CollectionManager for actually importing and building the collection.
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.3
+ */
+public class CreatePane
+ extends JPanel
+ implements GShellListener {
+
+ static private Dimension ARGUMENT_SIZE = new Dimension(800,90);
+ /** The threshold for when the simple view is replaced by the complex one. */
+ static private final int THRESHOLD = Configuration.EXPERT_MODE;
+ /** An identifier for the control panel within the card layout. */
+ static private String CONTROL = "Control";
+ /** An identifier for the progress panel within the card layout. */
+ static private String PROGRESS = "Progress";
+ /** An identifier for the simple panel within the card layout. */
+ static private String SIMPLE = "Simple";
+
+ /** Determines the current view that should be shown for this pane. */
+ public boolean processing = false;
+ /** The options pane generates all the various option sheet configuations. */
+ public OptionsPane options_pane = null;
+ private AppendLineOnlyFileDocument document;
+ /** A card layout is used to store the separate configuration and progress panes. */
+ private CardLayout card_layout = null;
+ /** Stores a reference to the current collection. */
+ private Collection previous_collection = null;
+ /** This monitor tracks the build processes progress. */
+ private GShellProgressMonitor build_monitor = null;
+ /** This monitor tracks the import processes progress. */
+ private GShellProgressMonitor import_monitor = null;
+ /** This monitor tracks the schedule processes progress. */
+ private GShellProgressMonitor schedule_monitor = null;
+ /** The button for begining the building processes. */
+ private JButton build_button = null;
+ /** The button for stopping the building processes. */
+ private JButton cancel_button = null;
+ /** The button for setting up scheduling */
+ private JButton schedule_button = null;
+ /** The button for viewing the collection. */
+ private JButton preview_button = null;
+ private JButton simple_build_button;
+ private JButton simple_cancel_button;
+ private JButton simple_preview_button;
+ /** The radio buttons for chnaging the build type (incremental/full) */
+ private JRadioButton full_build_radio_button;
+ private JRadioButton incremental_build_radio_button;
+ private JRadioButton sfull_build_radio_button;
+ private JRadioButton sincremental_build_radio_button;
+ /** The label displaying the number of documents in this collection. */
+ private JLabel document_count = null;
+ /** The label alongside the build progress bar gets some special treatment so... */
+ private JLabel progress_build_label = null;
+ /** The label alongside the import progress bar gets some special treatment so... */
+ private JLabel progress_import_label = null;
+ private JPanel bar_area;
+ /** The panel on which buttons are rendered on higher detail modes. */
+ private JPanel button_pane;
+ /** The pane which contains the controls for configuration. */
+ private JPanel control_pane = null;
+ /** The pane which has the card layout as its manager. */
+ private JPanel main_pane = null;
+ /** The pane which contains the progress information. */
+ private JPanel progress_pane = null;
+ /** The pane on the right-hand side - shows the requested options view */
+ private JPanel right = null;
+ /** The simple panel on which all of the available arguments are rendered. */
+ private JPanel sargument_configuration_panel;
+ /** The panel on which buttons are rendered on lower details modes. */
+ private JPanel sbutton_panel;
+ /** A simplified version for lower detail modes containing both the control and progress panes smooshed together. */
+ private JSplitPane simple_panel;
+ /** The lower part of the panel which is global so the log pane can be added and removed from it */
+ private JPanel slower_panel;
+ /** The inner panel of the simple pane which is global so that the bar_area can be added and removed from it. */
+ private JPanel sinner_panel;
+ /** The scrolling pane for the log. */
+ private JScrollPane log_scroll;
+ /** The scrolling pane the simple arguments are rendered within. */
+ private JScrollPane sargument_configuration_scroll;
+ /** the scrolling pane the righthand side is inside - only used for import and build options. the log and log list are not inside a scrollpane */
+ private JScrollPane scroll_pane;
+ /** The message log for the entire session. */
+ private JTextArea log_textarea;
+ /** A tree used to display the currently available option views. */
+ private OptionTree tree = null;
+ /** An array used to pass arguments with dictionary calls. */
+ private String args[] = null;
+ /** The homepage address of the current collection */
+ public String homepage = null;
+
+ /** The constructor creates important helper classes, and initializes all the components.
+ * @see org.greenstone.gatherer.collection.CollectionManager
+ * @see org.greenstone.gatherer.gui.BuildOptions
+ * @see org.greenstone.gatherer.gui.CreatePane.BuildButtonListener
+ * @see org.greenstone.gatherer.gui.CreatePane.CancelButtonListener
+ * @see org.greenstone.gatherer.gui.CreatePane.OptionTree
+ * @see org.greenstone.gatherer.gui.OptionsPane
+ * @see org.greenstone.gatherer.shell.GBuildProgressMonitor
+ * @see org.greenstone.gatherer.shell.GImportProgressMonitor
+ * @see org.greenstone.gatherer.shell.GShellProgressMonitor
+ */
+ public CreatePane() {
+ this.setComponentOrientation(Dictionary.getOrientation());
+ log_textarea = new JTextArea();
+ log_scroll = new JScrollPane(log_textarea);
+
+ // Create components
+ card_layout = new CardLayout();
+ // Control Pane
+ control_pane = new JPanel();
+ control_pane.setComponentOrientation(Dictionary.getOrientation());
+ tree = new OptionTree();
+ tree.setComponentOrientation(Dictionary.getOrientation());
+ button_pane = new JPanel();
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ // Progress Pane
+ progress_pane = new JPanel(); // One owner of the bar component
+ progress_pane.setComponentOrientation(Dictionary.getOrientation());
+ bar_area = new JPanel(); // This component will be shared about
+ bar_area.setComponentOrientation(Dictionary.getOrientation());
+
+ progress_import_label = new JLabel(Dictionary.get("CreatePane.Import_Progress"));
+ progress_import_label.setComponentOrientation(Dictionary.getOrientation());
+
+ import_monitor = new GImportProgressMonitor();
+ Gatherer.c_man.registerImportMonitor(import_monitor);
+
+ progress_build_label = new JLabel(Dictionary.get("CreatePane.Build_Progress"));
+ progress_build_label.setComponentOrientation(Dictionary.getOrientation());
+
+ build_monitor = new GBuildProgressMonitor(import_monitor.getSharedProgress());
+ Gatherer.c_man.registerBuildMonitor(build_monitor);
+
+ //We only need a schedule monitor for text output. No use for a progress bar!
+
+ //progress_schedule_label = new JLabel(Dictionary.get("CreatePane.Schedule_Progress"));
+
+ schedule_monitor = new GScheduleProgressMonitor();
+ Gatherer.c_man.registerScheduleMonitor(schedule_monitor);
+
+ // And the simple panel
+ slower_panel = new JPanel();
+ sbutton_panel = new JPanel();
+ sinner_panel = new JPanel();
+
+ slower_panel.setComponentOrientation(Dictionary.getOrientation());
+ sbutton_panel.setComponentOrientation(Dictionary.getOrientation());
+ sinner_panel.setComponentOrientation(Dictionary.getOrientation());
+
+ //Radio buttons
+ incremental_build_radio_button = new JRadioButton(Dictionary.get("CreatePane.Minimal_Build"));
+ incremental_build_radio_button.setComponentOrientation(Dictionary.getOrientation());
+ incremental_build_radio_button.setToolTipText(Dictionary.get("CreatePane.Minimal_Build_Tooltip"));
+ full_build_radio_button = new JRadioButton(Dictionary.get("CreatePane.Full_Build"));
+ full_build_radio_button.setComponentOrientation(Dictionary.getOrientation());
+ full_build_radio_button.setToolTipText(Dictionary.get("CreatePane.Full_Build_Tooltip"));
+ sincremental_build_radio_button = new JRadioButton(Dictionary.get("CreatePane.Minimal_Build"));
+ sincremental_build_radio_button.setComponentOrientation(Dictionary.getOrientation());
+ sincremental_build_radio_button.setToolTipText(Dictionary.get("CreatePane.Minimal_Build_Tooltip"));
+ sfull_build_radio_button = new JRadioButton(Dictionary.get("CreatePane.Full_Build"));
+ sfull_build_radio_button.setComponentOrientation(Dictionary.getOrientation());
+ sfull_build_radio_button.setToolTipText(Dictionary.get("CreatePane.Full_Build_Tooltip"));
+
+ //keep them in sync
+ full_build_radio_button.addActionListener(
+ new ActionListener() { public void actionPerformed(ActionEvent e) {
+ sfull_build_radio_button.setSelected( full_build_radio_button.isSelected() );
+ }});
+ sfull_build_radio_button.addActionListener(
+ new ActionListener() { public void actionPerformed(ActionEvent e) {
+ full_build_radio_button.setSelected( sfull_build_radio_button.isSelected() );
+ }});
+ incremental_build_radio_button.addActionListener(
+ new ActionListener() { public void actionPerformed(ActionEvent e) {
+ sincremental_build_radio_button.setSelected( incremental_build_radio_button.isSelected() );
+ }});
+ sincremental_build_radio_button.addActionListener(
+ new ActionListener() { public void actionPerformed(ActionEvent e) {
+ incremental_build_radio_button.setSelected( sincremental_build_radio_button.isSelected() );
+ }});
+
+ // Buttons
+ BuildButtonListener bbl = new BuildButtonListener();
+ CancelButtonListener cbl = new CancelButtonListener();
+
+ build_button = new GLIButton(Dictionary.get("CreatePane.Build_Collection"), Dictionary.get("CreatePane.Build_Collection_Tooltip"));
+ build_button.addActionListener(bbl);
+
+ cancel_button = new GLIButton(Dictionary.get("CreatePane.Cancel_Build"), Dictionary.get("CreatePane.Cancel_Build_Tooltip"));
+ cancel_button.addActionListener(cbl);
+ cancel_button.setEnabled(false);
+
+ preview_button = new PreviewButton(Dictionary.get("CreatePane.Preview_Collection"), Dictionary.get("CreatePane.Preview_Collection_Tooltip"));
+ //preview_button.addActionListener(pbl);
+ if(Gatherer.c_man != null) {
+ preview_button.setEnabled(Gatherer.c_man.built());
+ }
+ else {
+ preview_button.setEnabled(false);
+ }
+
+ BuildSimpleButtonListener bsbl = new BuildSimpleButtonListener();
+ simple_build_button = new GLIButton(Dictionary.get("CreatePane.Build_Collection"), Dictionary.get("CreatePane.Build_Collection_Tooltip"));
+ simple_build_button.addActionListener(bsbl);
+
+ simple_cancel_button = new GLIButton(Dictionary.get("CreatePane.Cancel_Build"), Dictionary.get("CreatePane.Cancel_Build_Tooltip"));
+ simple_cancel_button.addActionListener(cbl);
+ simple_cancel_button.setEnabled(false);
+
+ simple_preview_button = new PreviewButton(Dictionary.get("CreatePane.Preview_Collection"), Dictionary.get("CreatePane.Preview_Collection_Tooltip"));
+ //simple_preview_button.addActionListener(pbl);
+ if(Gatherer.c_man != null) {
+ simple_preview_button.setEnabled(Gatherer.c_man.built());
+ }
+ else {
+ simple_preview_button.setEnabled(false);
+ }
+
+ bbl = null;
+ bsbl = null;
+ cbl = null;
+ //pbl = null;
+ }
+
+
+ public void destroy() {
+ if(document != null) {
+ document.destroy();
+ }
+ }
+
+ /** This method is called to actually layout the components.
+ * @see org.greenstone.gatherer.Configuration
+ * @see org.greenstone.gatherer.Gatherer
+ */
+ public void display() {
+
+ int current_mode = Configuration.getMode();
+
+ //Complete/incremental build options panel
+ ButtonGroup build_type_group = new ButtonGroup();
+ build_type_group.add(incremental_build_radio_button);
+ build_type_group.add(full_build_radio_button);
+ full_build_radio_button.setSelected(true);
+
+ ButtonGroup sbuild_type_group = new ButtonGroup();
+ sbuild_type_group.add(sincremental_build_radio_button);
+ sbuild_type_group.add(sfull_build_radio_button);
+ sfull_build_radio_button.setSelected(true);
+
+ JPanel build_type_pane = new JPanel(new GridLayout(2,1));
+ build_type_pane.setComponentOrientation(Dictionary.getOrientation());
+ build_type_pane.add(full_build_radio_button);
+ build_type_pane.add(incremental_build_radio_button);
+
+ JPanel sbuild_type_pane = new JPanel(new GridLayout(2,1));
+ sbuild_type_pane.setComponentOrientation(Dictionary.getOrientation());
+ sbuild_type_pane.add(sfull_build_radio_button);
+ sbuild_type_pane.add(sincremental_build_radio_button);
+
+
+ // Build control_pane
+ JPanel left = new JPanel();
+ left.setComponentOrientation(Dictionary.getOrientation());
+ left.setBorder(BorderFactory.createEmptyBorder(0,5,5,5));
+ left.setLayout(new BorderLayout());
+ left.add(tree, BorderLayout.CENTER);
+ left.add(build_type_pane, BorderLayout.SOUTH);
+ //left.add(full_build_radio_button, BorderLayout.SOUTH);
+
+ right = new JPanel();
+ right.setComponentOrientation(Dictionary.getOrientation());
+ right.setBorder(BorderFactory.createEmptyBorder(0,0,5,5));
+ right.setLayout(new BorderLayout());
+
+ JPanel options_area = new JPanel();
+ options_area.setComponentOrientation(Dictionary.getOrientation());
+ options_area.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(5,5,5,5), BorderFactory.createTitledBorder(Dictionary.get("CreatePane.Options_Title"))));
+ options_area.setLayout(new BorderLayout());
+ options_area.add(left, BorderLayout.LINE_START);
+ options_area.add(right, BorderLayout.CENTER);
+
+ button_pane = new JPanel();
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+ button_pane.setBorder(BorderFactory.createEmptyBorder(5,10,10,10));
+ button_pane.setLayout(new GridLayout(1,4));
+ button_pane.add(build_button);
+ button_pane.add(cancel_button);
+ button_pane.add(preview_button);
+
+ control_pane.setLayout(new BorderLayout());
+ control_pane.add(options_area, BorderLayout.CENTER);
+ control_pane.add(button_pane, BorderLayout.SOUTH);
+
+ // Build progress_pane
+ JPanel labels_pane = new JPanel();
+ labels_pane.setComponentOrientation(Dictionary.getOrientation());
+ labels_pane.setLayout(new GridLayout(2,1,0,5));
+ labels_pane.add(progress_import_label);
+ labels_pane.add(progress_build_label);
+
+ JPanel monitors_pane = new JPanel();
+ monitors_pane.setComponentOrientation(Dictionary.getOrientation());
+ monitors_pane.setLayout(new GridLayout(2,1,0,5));
+ monitors_pane.add(import_monitor.getProgress());
+ monitors_pane.add(build_monitor.getProgress());
+
+ bar_area.setBorder(BorderFactory.createEmptyBorder(10,10,5,10));
+ bar_area.setLayout(new BorderLayout(5,5));
+ bar_area.add(labels_pane, BorderLayout.LINE_START);
+ bar_area.add(monitors_pane, BorderLayout.CENTER);
+
+ progress_pane.setBorder(BorderFactory.createEmptyBorder(20,20,20,20));
+ progress_pane.setLayout(new BorderLayout());
+ progress_pane.add(bar_area, BorderLayout.NORTH);
+ if(current_mode >= THRESHOLD) {
+ progress_pane.add(log_scroll, BorderLayout.CENTER);
+ }
+
+ // Simple panel
+ sbutton_panel.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
+ sbutton_panel.setLayout(new GridLayout(1,3));
+ sbutton_panel.add(simple_build_button);
+ sbutton_panel.add(simple_cancel_button);
+ sbutton_panel.add(simple_preview_button);
+
+ JPanel simple_bar_area = new JPanel(new GridLayout(2,1));
+ simple_bar_area.setComponentOrientation(Dictionary.getOrientation());
+ simple_bar_area.setBorder(BorderFactory.createEmptyBorder(0,5,0,0));
+ simple_bar_area.add(import_monitor.getSharedProgress());
+ simple_bar_area.add(sbutton_panel);
+
+ sinner_panel.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
+ sinner_panel.setLayout(new BorderLayout());
+ sinner_panel.add(sbuild_type_pane, BorderLayout.LINE_START);
+ //sinner_panel.add(full_build_radio_button, BorderLayout.WEST);
+ sinner_panel.add(simple_bar_area, BorderLayout.CENTER);
+
+ if(options_pane != null) {
+ sargument_configuration_panel = options_pane.buildImport(null);
+ sargument_configuration_panel = options_pane.buildBuild(sargument_configuration_panel);
+ }
+ else {
+ sargument_configuration_panel = new JPanel();
+ }
+ sargument_configuration_panel.setComponentOrientation(Dictionary.getOrientation());
+ sargument_configuration_scroll = new JScrollPane(sargument_configuration_panel);
+ sargument_configuration_scroll.setPreferredSize(ARGUMENT_SIZE);
+ sargument_configuration_scroll.setComponentOrientation(Dictionary.getOrientation());
+
+ slower_panel.setLayout(new BorderLayout());
+ slower_panel.add(sinner_panel, BorderLayout.NORTH);
+ if(current_mode < THRESHOLD) {
+ slower_panel.add(log_scroll, BorderLayout.CENTER);
+ }
+
+ simple_panel = new JSplitPane(JSplitPane.VERTICAL_SPLIT, sargument_configuration_scroll, slower_panel);
+ simple_panel.setComponentOrientation(Dictionary.getOrientation());
+ // Main pane
+ main_pane = new JPanel(card_layout);
+ main_pane.setComponentOrientation(Dictionary.getOrientation());
+ if(current_mode < THRESHOLD) { // Simple mode - add first
+ main_pane.add(simple_panel, SIMPLE);
+ }
+ main_pane.add(control_pane, CONTROL);
+ main_pane.add(progress_pane, PROGRESS);
+ if(current_mode >= THRESHOLD) { // Expert mode - add last
+ main_pane.add(simple_panel, SIMPLE);
+ }
+
+ this.setLayout(new BorderLayout());
+ this.add(main_pane, BorderLayout.CENTER);
+ }
+
+ /** This method is called whenever the 'Create' tab is selected from the view bar. It allows this view to perform any preactions required prior to display. In this case this entails gathering up to date information about the status of the collection including number of documents etc.
+ * @see org.greenstone.gatherer.Gatherer
+ * @see org.greenstone.gatherer.collection.CollectionManager
+ * @see org.greenstone.gatherer.gui.CreatePane.OptionTree
+ */
+ public void gainFocus() {
+ if(Configuration.getMode() < THRESHOLD) {
+ card_layout.show(main_pane, SIMPLE);
+ }
+ else if(!processing) {
+ // Move the buttons to control
+ control_pane.add(button_pane, BorderLayout.SOUTH);
+ card_layout.show(main_pane, CONTROL);
+ }
+ else {
+ // Move the buttons to progress
+ progress_pane.add(button_pane, BorderLayout.SOUTH);
+ card_layout.show(main_pane, PROGRESS);
+ }
+ // Refresh the set of controls.
+ TreePath path = tree.getSelectionPath();
+ tree.clearSelection();
+ tree.setSelectionPath(path);
+
+ }
+
+ /** We are informed when this view pane loses focus so we can update build options. */
+ public void loseFocus() {
+ tree.valueChanged(null);
+ }
+
+ /** All implementation of GShellListener must include this method so the listener can be informed of messages from the GShell.
+ * @param event A GShellEvent that contains, amoung other things, the message.
+ */
+ public synchronized void message(GShellEvent event) {
+ // Ignore the messages from RecPlug (used for progress bars)
+ if (event.getMessage().startsWith("import.pl> RecPlug - ")) {
+ return;
+ }
+ document.appendLine(event.getMessage());
+ log_textarea.setCaretPosition(document.getLengthToNearestLine());
+ }
+
+ /** Called when the detail mode has changed which in turn may cause several import/build configuration arguments to be available/hidden
+ * @param mode the new mode as an int
+ */
+ public void modeChanged(int mode) {
+ // If we are below the complexity threshold ensure the simple controls are being shown
+ if(Configuration.getMode() < THRESHOLD) {
+ // Update the arguments
+ simple_panel.remove(sargument_configuration_scroll);
+ if(options_pane != null) {
+ sargument_configuration_panel = options_pane.buildImport(null);
+ sargument_configuration_panel = options_pane.buildBuild(sargument_configuration_panel);
+ }
+ else {
+ sargument_configuration_panel = new JPanel();
+ }
+ sargument_configuration_scroll = new JScrollPane(sargument_configuration_panel);
+ sargument_configuration_panel.setComponentOrientation(Dictionary.getOrientation());
+ sargument_configuration_scroll.setComponentOrientation(Dictionary.getOrientation());
+ sargument_configuration_scroll.setPreferredSize(ARGUMENT_SIZE);
+ simple_panel.setTopComponent(sargument_configuration_scroll);
+ // Remove the shared components from the expert panels
+ progress_pane.remove(log_scroll);
+ // And add to simple one
+ slower_panel.add(log_scroll, BorderLayout.CENTER);
+ // And bring the card to the front
+ card_layout.show(main_pane, SIMPLE);
+ }
+ // And if we are above the threshold change to the complex controls
+ else {
+ // Remove the shared components from the simple panel
+ slower_panel.remove(log_scroll);
+ // And add then to the expert ones
+ progress_pane.add(log_scroll, BorderLayout.CENTER);
+ // And bring the appropriate card to the front
+ if(!processing) {
+ control_pane.add(button_pane, BorderLayout.SOUTH);
+ card_layout.show(main_pane, CONTROL);
+ }
+ else {
+ progress_pane.add(button_pane, BorderLayout.SOUTH);
+ card_layout.show(main_pane, PROGRESS);
+ }
+ }
+ tree.valueChanged(null); // Ensure tree argument panels are rebuilt
+ }
+
+ /** All implementation of GShellListener must include this method so the listener can be informed when a GShell begins its task. Implementation side-effect, not actually used.
+ * @param event A GShellEvent that contains details of the initial state of the GShell before task comencement.
+ */
+ public synchronized void processBegun(GShellEvent event) {
+ // We don't care. We'll get a more acurate start from the progress monitors.
+ }
+ /** All implementation of GShellListener must include this method so the listener can be informed when a GShell completes its task.
+ * @param event A GShellEvent that contains details of the final state of the GShell after task completion.
+ */
+ public synchronized void processComplete(GShellEvent event) {
+ DebugStream.println("In CreatePane::processComplete(" + event.getType() + ")...");
+ if(event.getStatus() == GShell.OK) {
+ if(event.getType() == GShell.SCHEDULE) {
+
+ processing = false;
+ build_button.setEnabled(true);
+ cancel_button.setEnabled(false);
+ //preview_button.setEnabled(true);
+ //only enable preview if the collection has been built.
+ preview_button.setEnabled(Gatherer.c_man.built());
+ simple_build_button.setEnabled(true);
+ simple_cancel_button.setEnabled(false);
+ simple_preview_button.setEnabled(Gatherer.c_man.built());
+ int status = event.getStatus();
+ document.setSpecialCharacter(OptionsPane.SCHEDULED);
+ options_pane.resetFileEntry();
+ build_monitor.reset();
+ import_monitor.reset();
+ if(Configuration.getMode() >= THRESHOLD) {
+ control_pane.add(button_pane, BorderLayout.SOUTH);
+ card_layout.show(main_pane, CONTROL);
+ }
+ }
+ else if(event.getType() == GShell.BUILD) {
+ processing = false;
+ build_button.setEnabled(true);
+ cancel_button.setEnabled(false);
+ preview_button.setEnabled(Gatherer.c_man.built());
+ simple_build_button.setEnabled(true);
+ simple_cancel_button.setEnabled(false);
+ simple_preview_button.setEnabled(Gatherer.c_man.built());
+ int status = event.getStatus();
+ document.setSpecialCharacter(OptionsPane.SUCCESSFUL);
+ options_pane.resetFileEntry();
+ build_monitor.reset();
+ import_monitor.reset();
+ if(Configuration.getMode() >= THRESHOLD) {
+ control_pane.add(button_pane, BorderLayout.SOUTH);
+ card_layout.show(main_pane, CONTROL);
+ }
+ }
+ // Otherwise its completed import but still got build to go
+ }
+ else {
+ processing = false;
+ cancel_button.setEnabled(false);
+ build_button.setEnabled(true);
+ // The build may have failed, but a previous index may still be in place
+ preview_button.setEnabled(Gatherer.c_man.built());
+
+ simple_build_button.setEnabled(true);
+ simple_cancel_button.setEnabled(false);
+ simple_preview_button.setEnabled(Gatherer.c_man.built());
+ if(event.getStatus() == GShell.CANCELLED) {
+ document.setSpecialCharacter(OptionsPane.CANCELLED);
+ }
+ else {
+ document.setSpecialCharacter(OptionsPane.UNSUCCESSFUL);
+ }
+ options_pane.resetFileEntry();
+ if(Configuration.getMode() >= THRESHOLD) {
+ control_pane.add(button_pane, BorderLayout.SOUTH);
+ card_layout.show(main_pane, CONTROL);
+ }
+ }
+ }
+
+
+ /** This method is invoked at any time there has been a significant change in the collection, such as saving, loading or creating.
+ * @param ready A boolean indicating if a collection is currently available.
+ * @see org.greenstone.gatherer.Gatherer
+ * @see org.greenstone.gatherer.collection.CollectionManager
+ * @see org.greenstone.gatherer.gui.BuildOptions
+ * @see org.greenstone.gatherer.gui.OptionsPane
+ */
+ public void refresh(int refresh_reason, boolean ready)
+ {
+ if (Gatherer.c_man == null || !ready) {
+ return;
+ }
+ Collection current_collection = Gatherer.c_man.getCollection();
+ if (current_collection != previous_collection && !Gatherer.c_man.isImporting()) {
+ this.options_pane = new OptionsPane(current_collection.import_options, current_collection.build_options, current_collection.schedule_options);
+ if (previous_collection != null) {
+ // clear the log
+ Document log_document = log_textarea.getDocument();
+ if (log_document instanceof AppendLineOnlyFileDocument) {
+ ((AppendLineOnlyFileDocument) log_document).destroy();
+ }
+ }
+ previous_collection = current_collection;
+
+ }
+ // If we are in simple mode, we have to rebuild the simple arguments list
+ if(Configuration.getMode() < THRESHOLD) {
+ simple_panel.remove(sargument_configuration_scroll);
+ sargument_configuration_panel = options_pane.buildImport(null);
+ sargument_configuration_panel = options_pane.buildBuild(sargument_configuration_panel);
+ //if(CollectionManager.canDoScheduling()) {
+ //sargument_configuration_panel = options_pane.buildSchedule(sargument_configuration_panel);
+ //}
+ sargument_configuration_scroll = new JScrollPane(sargument_configuration_panel);
+ sargument_configuration_scroll.setComponentOrientation(Dictionary.getOrientation());
+ sargument_configuration_scroll.setPreferredSize(ARGUMENT_SIZE);
+ simple_panel.setTopComponent(sargument_configuration_scroll);
+ }
+ tree.valueChanged(null);
+
+ // Validate the preview button
+ if (Gatherer.c_man.built() && Configuration.library_url != null) {
+ preview_button.setEnabled(true);
+ simple_preview_button.setEnabled(true);
+ }
+ else {
+ preview_button.setEnabled(false);
+ simple_preview_button.setEnabled(false);
+ }
+ }
+
+ /** This class serves as the listener for actions on the build button. */
+ private class BuildButtonListener
+ implements ActionListener {
+ /**
+ * This method checks to see what needs to be done for a build, and starts the process off.
+ * A complete build proccess is started by {@link CollectionManager.importCollection()}, which then imports and builds the collection.
+ * However, this doesn't always happen, as sometimes an incremental build will do :-)
+ * @param event An ActionEvent who, thanks to the power of object oriented programming, we don't give two hoots about.
+ * @see org.greenstone.gatherer.Gatherer
+ * @see org.greenstone.gatherer.collection.CollectionManager
+ * @see org.greenstone.gatherer.gui.BuildOptions
+ * @see org.greenstone.gatherer.shell.GShellProgressMonitor
+ */
+ public void actionPerformed(ActionEvent event) {
+ Collection collection = Gatherer.c_man.getCollection();
+ String collection_name = collection.getName();
+
+ //if button is labelled for Building
+ if(event.getActionCommand().equals(Dictionary.get("CreatePane.Build_Collection"))) {
+
+ //set a static variable marking whether this is a "Complete" or "Minimal" build
+ CollectionDesignManager.setCompleteBuild( full_build_radio_button.isSelected() );
+
+ //do the import and build, skipping import if possible
+ /*if( !CollectionDesignManager.isCompleteBuild() &&
+ !collection.getMetadataChanged() && !collection.getFilesChanged() && Gatherer.c_man.imported() &&
+ CollectionDesignManager.getRebuildTypeRequired() <= CollectionDesignManager.BUILDCOL
+ ) {
+ // Just do build (no import)
+ DebugStream.println("Just want to run buildcol.pl");
+ prepareForBuild();
+ Gatherer.c_man.buildCollection();
+ }
+ else {
+ */
+ prepareForBuild();
+
+ // Starting building,
+ // For now, for a GS3 solr collection, we need to stop the GS3 server before building
+ // and restart it after building is complete. Restart is done in CollectionManager.processComplete()
+ if(CollectionManager.isSolrCollection()) {
+ /*GS3ServerThread thread = new GS3ServerThread(Configuration.gsdl3_src_path, "stop");
+ thread.start();*/
+ }
+
+ Gatherer.c_man.importCollection(); //This starts the building process.
+ /*
+ }
+ */
+
+ } else { //button is labelled for setting up scheduling
+
+ //this needs to be called to configure buttons... but no building is done
+ prepareForBuild();
+ Gatherer.c_man.scheduleBuild();
+ }
+ //Re-setting the rebuildTypeRequired is handled by CollectionManager.processComplete(GShellEvent)
+ }
+
+
+ /**
+ * This does some stuff that is needed before a collection can be built.
+ * For example, buttons are enabled/disabled, and certain flags are set.
+ * This is called from {@link actionPerformed(ActionEvent)}
+ */
+ private void prepareForBuild() {
+ // First we force the build options to be updated if we haven't already.
+ tree.valueChanged(null);
+
+ // Remember that for lower thresholds the above doesn't work, so try this instead
+ if(Configuration.getMode() < THRESHOLD) {
+ options_pane.update(sargument_configuration_panel);
+ }
+
+ // Now go about building.
+ build_button.setEnabled(false);
+ cancel_button.setEnabled(true);
+ preview_button.setEnabled(false);
+
+ simple_build_button.setEnabled(false);
+ simple_cancel_button.setEnabled(true);
+ simple_preview_button.setEnabled(false);
+
+ document = options_pane.createNewLogDocument();
+ log_textarea.setDocument(document);
+ options_pane.log_textarea.setDocument(document);
+ // Change the view.
+ processing = true;
+ if(Configuration.getMode() >= THRESHOLD) {
+ progress_pane.add(button_pane, BorderLayout.SOUTH);
+ card_layout.show(main_pane, PROGRESS);
+ }
+ // Reset the stop flag in all the process monitors, just incase.
+ ((GBuildProgressMonitor)build_monitor).reset();
+ import_monitor.setStop(false);
+ }
+ }
+
+ /** I hope this works. Wendy */
+ private class BuildSimpleButtonListener
+ implements ActionListener {
+ /**
+ * This method checks to see what needs to be done for a build, and starts the process off.
+ * A complete build proccess is started by {@link CollectionManager.importCollection()}, which then imports and builds the collection.
+ * However, this doesn't always happen, as sometimes an incremental build will do :-)
+ * @param event An ActionEvent who, thanks to the power of object oriented programming, we don't give two hoots about.
+ * @see org.greenstone.gatherer.Gatherer
+ * @see org.greenstone.gatherer.collection.CollectionManager
+ * @see org.greenstone.gatherer.gui.BuildOptions
+ * @see org.greenstone.gatherer.shell.GShellProgressMonitor
+ */
+ public void actionPerformed(ActionEvent event) {
+ Collection collection = Gatherer.c_man.getCollection();
+ String collection_name = collection.getName();
+
+ //set a static variable marking whether this is a "Complete" or "Minimal" build
+ CollectionDesignManager.setCompleteBuild( full_build_radio_button.isSelected() );
+
+ //do the import and build, skipping import if possible
+ /*if( !CollectionDesignManager.isCompleteBuild() &&
+ !collection.getMetadataChanged() && !collection.getFilesChanged() && Gatherer.c_man.imported() &&
+ CollectionDesignManager.getRebuildTypeRequired() <= CollectionDesignManager.BUILDCOL
+ ) {
+ // Just do build (no import)
+ DebugStream.println("Just want to run buildcol.pl");
+ prepareForBuild();
+ Gatherer.c_man.buildCollection();
+ }
+ else {
+ */
+ prepareForBuild();
+ Gatherer.c_man.importCollection(); //This starts the building process.
+ /*
+ }
+ */
+
+ //Re-setting the rebuildTypeRequired is handled by CollectionManager.processComplete(GShellEvent)
+ }
+
+ /**
+ * This does some stuff that is needed before a collection can be built.
+ * For example, buttons are enabled/disabled, and certain flags are set.
+ * This is called from {@link actionPerformed(ActionEvent)}
+ */
+ private void prepareForBuild() {
+ // First we force the build options to be updated if we haven't already.
+ tree.valueChanged(null);
+
+ // Remember that for lower thresholds the above doesn't work, so try this instead
+ if(Configuration.getMode() < THRESHOLD) {
+ options_pane.update(sargument_configuration_panel);
+ }
+
+ // Now go about building.
+ build_button.setEnabled(false);
+ cancel_button.setEnabled(true);
+ preview_button.setEnabled(false);
+
+ simple_build_button.setEnabled(false);
+ simple_cancel_button.setEnabled(true);
+ simple_preview_button.setEnabled(false);
+
+ document = options_pane.createNewLogDocument();
+ log_textarea.setDocument(document);
+ options_pane.log_textarea.setDocument(document);
+ // Change the view.
+ processing = true;
+ if(Configuration.getMode() >= THRESHOLD) {
+ progress_pane.add(button_pane, BorderLayout.SOUTH);
+ card_layout.show(main_pane, PROGRESS);
+ }
+ // Reset the stop flag in all the process monitors, just incase.
+ ((GBuildProgressMonitor)build_monitor).reset();
+ import_monitor.setStop(false);
+ }
+ }
+
+
+ /** This class serves as the listener for actions on the cancel button. */
+ private class CancelButtonListener
+ implements ActionListener {
+ /** This method attempts to cancel the current GShell task. It does this by first telling CollectionManager not to carry out any further action. This it turn tells the GShell to break from the current job immediately, without waiting for the processEnded message, and then kills the thread in an attempt to stop the process. The results of such an action are debatable.
+ * @param event An ActionEvent who, thanks to the power of object oriented programming, we don't give two hoots about.
+ * @see org.greenstone.gatherer.Gatherer
+ * @see org.greenstone.gatherer.collection.CollectionManager
+ * @see org.greenstone.gatherer.gui.BuildOptions
+ * @see org.greenstone.gatherer.shell.GShellProgressMonitor
+ */
+ public void actionPerformed(ActionEvent event) {
+ build_button.setEnabled(false);
+ cancel_button.setEnabled(false);
+ preview_button.setEnabled(false);
+
+ simple_build_button.setEnabled(false);
+ simple_cancel_button.setEnabled(false);
+ simple_preview_button.setEnabled(false);
+
+ processing = false;
+ document.setSpecialCharacter(OptionsPane.CANCELLED);
+ if(Configuration.getMode() >= THRESHOLD) {
+ control_pane.add(button_pane, BorderLayout.SOUTH);
+ card_layout.show(main_pane, CONTROL);
+ }
+ // Set the stop flag in all the process monitor.
+ import_monitor.setStop(true);
+ import_monitor.reset();
+ build_monitor.setStop(true);
+ build_monitor.reset();
+
+ // Remove the collection lock.
+ //Gatherer.g_man.lockCollection(false, false);
+
+ // Cancelling building:
+ // For now, for a GS3 solr collection, we need to stop the GS3 server before building
+ // and restart it after building is complete. If a cancel is pressed during build
+ // then we also restart the GS3 server
+ if(CollectionManager.isSolrCollection()) {
+ /*GS3ServerThread thread = new GS3ServerThread(Configuration.gsdl3_src_path, "restart");
+ thread.start();*/
+ }
+
+
+ }
+ }
+
+
+ /** The OptionTree is simply a tree structure that has a root node labelled "Options" and then has a child node for each available options screen. These screens are either combinations of the available import and build arguments, or a message log detailing the shell processes progress. */
+ private class OptionTree
+ extends JTree
+ implements TreeSelectionListener {
+ /** The model behind the tree. */
+ private DefaultTreeModel model = null;
+ /** The previous options view displayed, which is sometimes needed to refresh properly. */
+ private JPanel previous_pane = null;
+ /** The node corresponding to the building options view. */
+ private OptionTreeNode building = null;
+ /** The node corresponding to the importing options view. */
+ private OptionTreeNode importing = null;
+ /** The node corresponding to the scheduling options view. */
+ private OptionTreeNode scheduling = null;
+ /** The node corresponding to the log view. */
+ private OptionTreeNode log = null;
+ /** The root node of the options tree, which has no associated options view. */
+ private OptionTreeNode options = null;
+ /** Constructor.
+ * @see org.greenstone.gatherer.gui.CreatePane.OptionTreeNode
+ */
+ public OptionTree() {
+ super();
+ this.setComponentOrientation(Dictionary.getOrientation());
+ ToolTipManager.sharedInstance().registerComponent(this);
+ addTreeSelectionListener(this);
+ getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
+ setCellRenderer(new ToolTipTreeCellRenderer());
+ setRootVisible(false);
+ setToggleClickCount(1);
+
+ // Create tree.
+ building = new OptionTreeNode(Dictionary.get("CreatePane.Build"));
+ building.setToolTipText(Dictionary.get("CreatePane.Build_Tooltip"));
+ importing = new OptionTreeNode(Dictionary.get("CreatePane.Import"));
+ importing.setToolTipText(Dictionary.get("CreatePane.Import_Tooltip"));
+ if (CollectionManager.canDoScheduling()) {
+ scheduling = new OptionTreeNode(Dictionary.get("CreatePane.Schedule"));
+ scheduling.setToolTipText(Dictionary.get("CreatePane.Schedule_Tooltip"));
+
+ }
+ log = new OptionTreeNode(Dictionary.get("CreatePane.Log"));
+ log.setToolTipText(Dictionary.get("CreatePane.Log_Tooltip"));
+ options = new OptionTreeNode(Dictionary.get("CreatePane.Options"));
+
+ model = new DefaultTreeModel(options);
+ setModel(model);
+ model.insertNodeInto(importing, options, 0);
+ model.insertNodeInto(building, options, 1);
+ if (CollectionManager.canDoScheduling()) {
+ model.insertNodeInto(scheduling, options, 2);
+ model.insertNodeInto(log, options, 3);
+ } else {
+ model.insertNodeInto(log, options, 2);
+ }
+ // Expand the root node
+ expandPath(new TreePath(options));
+ }
+ /** Any implementation of TreeSelectionListener must include this method to allow this listener to know when the selection has changed. Here we swap the options view depending on the selected OptionTreeNode.
+ * @param event A TreeSelectionEvent which contains all the information garnered when the event occured.
+ * @see org.greenstone.gatherer.gui.CreatePane.OptionTreeNode
+ */
+ public void valueChanged(TreeSelectionEvent event) {
+ TreePath path = null;
+ OptionTreeNode node = null;
+ //if(event != null) {
+ //path = event.getPath();
+ path = getSelectionPath();
+ if(path != null) {
+ node = (OptionTreeNode)path.getLastPathComponent();
+ }
+ //}
+ if(previous_pane != null) {
+ //target_pane.remove(previous_pane);
+ options_pane.update(previous_pane);
+ if(scroll_pane != null) {
+ right.remove(scroll_pane);
+ }
+ else {
+ right.remove(previous_pane);
+ }
+ previous_pane = null;
+ scroll_pane = null;
+ }
+ if(node != null && node.equals(log)) {
+ build_button.setActionCommand(Dictionary.get("CreatePane.Build_Collection"));
+ build_button.setText(Dictionary.get("CreatePane.Build_Collection"));
+ }
+ if(node != null && node.equals(building)) {
+ build_button.setActionCommand(Dictionary.get("CreatePane.Build_Collection"));
+ build_button.setText(Dictionary.get("CreatePane.Build_Collection"));
+
+
+ previous_pane = options_pane.buildBuild(null);
+ scroll_pane = new JScrollPane(previous_pane);
+ right.add(scroll_pane, BorderLayout.CENTER);
+ //target_pane.add(previous_pane, BorderLayout.CENTER);
+ }
+ else if(node != null && node.equals(importing)) {
+ build_button.setActionCommand(Dictionary.get("CreatePane.Build_Collection"));
+ build_button.setText(Dictionary.get("CreatePane.Build_Collection"));
+
+ previous_pane = options_pane.buildImport(null);
+ scroll_pane = new JScrollPane(previous_pane);
+ right.add(scroll_pane, BorderLayout.CENTER);
+ //target_pane.add(previous_pane, BorderLayout.CENTER);
+ }
+ else if(node != null && CollectionManager.canDoScheduling() && node.equals(scheduling)) {
+ build_button.setActionCommand(Dictionary.get("CreatePane.Schedule_Build"));
+ build_button.setText(Dictionary.get("CreatePane.Schedule_Build"));
+
+ previous_pane = options_pane.buildSchedule(null);
+ scroll_pane = new JScrollPane(previous_pane);
+ right.add(scroll_pane, BorderLayout.CENTER);
+ }
+ else {
+ if (options_pane != null) {
+ previous_pane = options_pane.buildLog();
+ right.add(previous_pane, BorderLayout.CENTER);
+ right.updateUI(); // we need to repaint the log pane, cos it hasn't changed since last time
+ ///ystem.err.println("I've added the log back to the right pane again.");
+ //target_pane.add(previous_pane, BorderLayout.CENTER);
+ }
+ }
+ //scroll_pane.setViewportView(previous_pane);
+ //previous_pane.validate();
+ right.validate();
+ //System.err.println("Current pane size: " + previous_pane.getSize());
+ //System.err.println("While its preferred size is: " + previous_pane.getPreferredSize());
+ }
+ }
+
+ /** The OptionTree is built from these nodes, each of which has several methods used in creating the option panes.
+ */
+ private class OptionTreeNode
+ extends DefaultMutableTreeNode
+ implements Comparable {
+ /** The text label given to this node in the tree. */
+ private String title = null;
+ /** a tool tip to be used for this node in the tree */
+ private String tool_tip = null;
+ /** Constructor.
+ * @param title The String which serves as this nodes title.
+ */
+ public OptionTreeNode(String title) {
+ super();
+ this.title = title;
+ }
+
+ /** This method compares two nodes for ordering.
+ * @param obj The Object to compare to.
+ * @return An int of one of the possible values -1, 0 or 1 indicating if this node is less than, equal to or greater than the target node respectively.
+ */
+ public int compareTo(Object obj) {
+ return title.compareTo(obj.toString());
+ }
+
+ /** This method checks if two nodes are equivalent.
+ * @param obj The Object to be tested against.
+ * @return A boolean which is true if the objects are equal, false otherwise.
+ */
+ public boolean equals(Object obj) {
+ if(compareTo(obj) == 0) {
+ return true;
+ }
+ return false;
+ }
+
+ /** get the tool tip */
+ public String getToolTipText() {
+ return tool_tip;
+ }
+
+ /** set the tool tip */
+ public void setToolTipText(String tip) {
+ tool_tip = tip;
+ }
+
+ /** Method to translate this node into a textual representation.
+ * @return A String which in this case is the title.
+ */
+ public String toString() {
+ return title;
+ }
+ }
+
+ // Adds tooltips to the tree nodes
+ private class ToolTipTreeCellRenderer
+ extends DefaultTreeCellRenderer {
+
+ public Component getTreeCellRendererComponent(JTree tree,
+ Object value,
+ boolean sel,
+ boolean expanded,
+ boolean leaf,
+ int row,
+ boolean hasFocus) {
+
+ super.getTreeCellRendererComponent(tree, value, sel,
+ expanded, leaf, row,
+ hasFocus);
+ if (value instanceof OptionTreeNode) {
+ String tip = ((OptionTreeNode) value).getToolTipText();
+ if (tip != null) {
+ setToolTipText(tip);
+ }
+ }
+ return this;
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/CreateShortcutPrompt.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/CreateShortcutPrompt.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/CreateShortcutPrompt.java (revision 31635)
@@ -0,0 +1,151 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, Greenstone Digital Library, University of Waikato
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import java.io.*;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.file.WorkspaceTree;
+
+
+public class CreateShortcutPrompt
+ extends JDialog
+ implements ActionListener, KeyListener
+{
+ /** The default size of a special mapping dialog. */
+ static final Dimension DIALOG_SIZE = new Dimension(400, 120);
+
+ private boolean cancelled = false;
+ private JButton cancel_button = null;
+ private JButton ok_button = null;
+ private JTextField name_field = null;
+
+
+ public CreateShortcutPrompt(WorkspaceTree workspace_tree, File file)
+ {
+ super(Gatherer.g_man);
+ this.setComponentOrientation(Dictionary.getOrientation());
+ setModal(true);
+ setSize(DIALOG_SIZE);
+ setTitle(Dictionary.get("MappingPrompt.Title"));
+
+ // Creation
+ JPanel content_pane = (JPanel) getContentPane();
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+ JPanel center_pane = new JPanel();
+ center_pane.setComponentOrientation(Dictionary.getOrientation());
+ JPanel labels_pane = new JPanel();
+ labels_pane.setComponentOrientation(Dictionary.getOrientation());
+ JPanel fields_pane = new JPanel();
+ fields_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JLabel file_label = new JLabel(Dictionary.get("MappingPrompt.File"));
+ file_label.setComponentOrientation(Dictionary.getOrientation());
+ JLabel file_field = new JLabel(file.getAbsolutePath());
+ file_field.setComponentOrientation(Dictionary.getOrientation());
+
+ JLabel name_label = new JLabel(Dictionary.get("MappingPrompt.Name"));
+ name_label.setComponentOrientation(Dictionary.getOrientation());
+ name_field = new JTextField(file.getName());
+ name_field.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel button_pane = new JPanel();
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+ ok_button = new GLIButton(Dictionary.get("General.OK"), Dictionary.get("General.OK_Tooltip"));
+ ok_button.setEnabled(name_field.getText().length() > 0);
+ cancel_button = new GLIButton(Dictionary.get("General.Cancel"), Dictionary.get("General.Cancel_Tooltip"));
+
+ // Connection
+ cancel_button.addActionListener(this);
+ ok_button.addActionListener(this);
+ name_field.addKeyListener(this);
+
+ // Layout
+ labels_pane.setLayout(new GridLayout(2,1,5,0));
+ labels_pane.add(file_label);
+ labels_pane.add(name_label);
+
+ fields_pane.setLayout(new GridLayout(2,1,5,0));
+ fields_pane.add(file_field);
+ fields_pane.add(name_field);
+
+ center_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
+ center_pane.setLayout(new BorderLayout(5,0));
+ center_pane.add(labels_pane, BorderLayout.LINE_START);
+ center_pane.add(fields_pane, BorderLayout.CENTER);
+
+ button_pane.setLayout(new GridLayout(1,2,5,5));
+ button_pane.add(ok_button);
+ button_pane.add(cancel_button);
+
+ content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ content_pane.setLayout(new BorderLayout());
+ content_pane.add(center_pane, BorderLayout.CENTER);
+ content_pane.add(button_pane, BorderLayout.SOUTH);
+
+ // Display
+ Dimension screen_size = Configuration.screen_size;
+ setLocation((screen_size.width - DIALOG_SIZE.width) / 2, (screen_size.height - DIALOG_SIZE.height) / 2);
+ setVisible(true);
+
+ // If not cancelled create mapping.
+ if (!cancelled) {
+ workspace_tree.addDirectoryMapping(name_field.getText(), file);
+ }
+ }
+
+
+ public void actionPerformed(ActionEvent event)
+ {
+ if (event.getSource() == cancel_button) {
+ cancelled = true;
+ }
+ dispose();
+ }
+
+
+ public void destroy()
+ {
+ cancel_button = null;
+ ok_button = null;
+ name_field = null;
+ }
+
+
+ public void keyPressed(KeyEvent event) { }
+
+ public void keyReleased(KeyEvent event)
+ {
+ ok_button.setEnabled(name_field.getText().length() > 0);
+ }
+
+ public void keyTyped(KeyEvent event) { }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/DeleteCollectionPrompt.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/DeleteCollectionPrompt.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/DeleteCollectionPrompt.java (revision 31635)
@@ -0,0 +1,360 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.net.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.collection.BasicCollectionConfiguration;
+import org.greenstone.gatherer.collection.CollectionManager;
+import org.greenstone.gatherer.file.WorkspaceTree; // !!! Don't like this here
+import org.greenstone.gatherer.util.ArrayTools;
+import org.greenstone.gatherer.util.StaticStrings;
+import org.greenstone.gatherer.util.Utility;
+
+/** This class provides the functionality to delete current collections from the GSDLHOME/collect/ directory. The user chooses the collection from a list, where each entry also displays details about itself, confirms the delete of a collection by checking a checkbox then presses the ok button to actually delete the collection.
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.3
+ */
+public class DeleteCollectionPrompt
+ extends ModalDialog {
+ /** The currently selected collection for deletion. */
+ private BasicCollectionConfiguration collection = null;
+ /** The model behind the list. */
+ private DefaultListModel list_model = null;
+ /** A reference to ourself so any inner-classes can dispose of us. */
+ private DeleteCollectionPrompt prompt = null;
+ /** The close button, which exits the prompt without deleting anything. */
+ private JButton close_button = null;
+ /** The ok button which causes the selected collection to be deleted. */
+ private JButton ok_button = null;
+ /** The confirmation check box. */
+ private JCheckBox confirmation = null;
+ /** The label above details. */
+ private JLabel details_label = null;
+ /** The label above the list. */
+ private JLabel list_label = null;
+ /** The list of available collections. */
+ private JList list = null;
+ /** The text area used to display details about the collection selected. */
+ private JTextArea details = null;
+ /** A string array used to pass arguments to the phrase retrieval method. */
+ private String args[] = null;
+
+ private boolean current_coll_deleted = false;
+ /** The size of the delete prompt screen. */
+ public static final Dimension SIZE = new Dimension(500, 500);
+
+ /** Constructor.
+ */
+ public DeleteCollectionPrompt() {
+ super(Gatherer.g_man);
+ close_button = new GLIButton(Dictionary.get("General.Close"), Dictionary.get("General.Close_Tooltip"));
+ this.setComponentOrientation(Dictionary.getOrientation());
+ confirmation = new JCheckBox(Dictionary.get("DeleteCollectionPrompt.Confirm_Delete"));
+ confirmation.setComponentOrientation(Dictionary.getOrientation());
+ details = new JTextArea(Dictionary.get("DeleteCollectionPrompt.No_Collection"));
+ details.setComponentOrientation(Dictionary.getOrientation());
+ details.setEditable(false);
+ details_label = new JLabel(Dictionary.get("DeleteCollectionPrompt.Collection_Details"));
+ details_label.setComponentOrientation(Dictionary.getOrientation());
+
+ list = new JList();
+ list.setComponentOrientation(Dictionary.getOrientation());
+ list_label = new JLabel(Dictionary.get("DeleteCollectionPrompt.Collection_List"));
+ list_label.setComponentOrientation(Dictionary.getOrientation());
+
+ list_model = new DefaultListModel();
+ ok_button = new GLIButton(Dictionary.get("DeleteCollectionPrompt.Delete"), Dictionary.get("DeleteCollectionPrompt.Delete_Tooltip"));
+
+ prompt = this;
+ setModal(true);
+ setSize(SIZE);
+ setTitle(Dictionary.get("DeleteCollectionPrompt.Title"));
+
+ setJMenuBar(new SimpleMenuBar("deletingcollections"));
+ close_button.addActionListener(new CloseButtonListener());
+ confirmation.addActionListener(new ConfirmationCheckBoxListener());
+ confirmation.setEnabled(false);
+ confirmation.setSelected(false);
+ list.addListSelectionListener(new CollectionListListener());
+ list.clearSelection();
+ list.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ list.setModel(list_model);
+ ok_button.addActionListener(new OKButtonListener());
+ ok_button.setEnabled(false);
+ scanForCollections();
+
+ }
+
+ /** Destructor. */
+ public void destroy() {
+ list_model.clear();
+ list_model = null;
+ close_button = null;
+ confirmation = null;
+ details = null;
+ details_label = null;
+ list = null;
+ ok_button = null;
+ prompt = null;
+ }
+
+ /** This method causes the modal prompt to be displayed.
+ * returns true if it has deleted the collection that is currently open */
+ public boolean display() {
+ // Central pane
+ JScrollPane scrol_tmp;
+ JPanel list_pane = new JPanel(new BorderLayout());
+ list_pane.setComponentOrientation(Dictionary.getOrientation());
+ list_pane.add(list_label, BorderLayout.NORTH);
+ scrol_tmp = new JScrollPane(list);
+ scrol_tmp.setComponentOrientation(Dictionary.getOrientation());
+ list_pane.add(scrol_tmp, BorderLayout.CENTER);
+ list_pane.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0));
+
+ JPanel details_pane = new JPanel(new BorderLayout());
+ details_pane.setComponentOrientation(Dictionary.getOrientation());
+ details_pane.add(details_label, BorderLayout.NORTH);
+ scrol_tmp = new JScrollPane(details);
+ scrol_tmp.setComponentOrientation(Dictionary.getOrientation());
+ details_pane.add(scrol_tmp, BorderLayout.CENTER);
+ details_pane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
+
+ JPanel central_pane = new JPanel(new GridLayout(2, 1));
+ central_pane.setComponentOrientation(Dictionary.getOrientation());
+ central_pane.add(list_pane);
+ central_pane.add(details_pane);
+ central_pane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+
+ // Lower pane
+ JPanel confirmation_pane = new JPanel(new BorderLayout());
+ confirmation_pane.setComponentOrientation(Dictionary.getOrientation());
+ confirmation_pane.add(confirmation, BorderLayout.CENTER);
+ confirmation_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
+
+ JPanel button_pane = new JPanel(new GridLayout(1, 2));
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+ button_pane.add(ok_button);
+ button_pane.add(close_button);
+ button_pane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
+
+ JPanel lower_pane = new JPanel(new BorderLayout());
+ lower_pane.setComponentOrientation(Dictionary.getOrientation());
+ lower_pane.add(confirmation_pane, BorderLayout.NORTH);
+ lower_pane.add(button_pane, BorderLayout.SOUTH);
+ lower_pane.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5));
+
+ // Final.
+ JPanel content_pane = (JPanel)this.getContentPane();
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+ content_pane.setLayout(new BorderLayout());
+ content_pane.add(central_pane, BorderLayout.CENTER);
+ content_pane.add(lower_pane, BorderLayout.SOUTH);
+
+ // Center and display.
+ Dimension screen_size = Configuration.screen_size;
+ this.setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
+ this.setVisible(true); // blocks until the dialog is killed
+ return current_coll_deleted;
+ }
+
+
+ /** Shows a delete complete prompt.
+ * @param success A boolean indicating if the collection was successfully deleted.
+ * @see org.greenstone.gatherer.collection.Collection
+ */
+ public void resultPrompt(boolean success) {
+ args = new String[1];
+ args[0] = collection.getName();
+ if (success) {
+ JOptionPane.showMessageDialog(prompt,Dictionary.get("DeleteCollectionPrompt.Successful_Delete", args),Dictionary.get("DeleteCollectionPrompt.Successful_Title"),JOptionPane.INFORMATION_MESSAGE);
+ }
+ else {
+ JOptionPane.showMessageDialog(prompt,Dictionary.get("DeleteCollectionPrompt.Failed_Delete", args),Dictionary.get("DeleteCollectionPrompt.Failed_Title"),JOptionPane.WARNING_MESSAGE);
+ }
+ }
+
+ /** Method to scan the collect directory retrieving and reloading each collection it finds, while building the list of known collections.
+ * @see org.greenstone.gatherer.Configuration
+ * @see org.greenstone.gatherer.Gatherer
+ * @see org.greenstone.gatherer.util.ArrayTools
+ * @see org.greenstone.gatherer.util.Utility
+ */
+ private void scanForCollections() {
+ // Start at the collect dir.
+ File collect_directory = new File(Gatherer.getCollectDirectoryPath());
+ if (collect_directory.exists()) {
+ loadCollectionConfigs(collect_directory);
+ }
+ }
+
+ private void loadCollectionConfigs(File directory) {
+ // For each child directory see if it contains an
+ // etc/collect.cfg file and if so try to load it..
+ File collections[] = directory.listFiles();
+ String file_name = (Gatherer.GS3)? Utility.CONFIG_GS3_FILE : Utility.CONFIG_FILE;
+ ArrayTools.sort(collections);
+ for(int i = 0; collections != null && i < collections.length; i++) {
+ if(collections[i].isDirectory() && !collections[i].getName().equals(StaticStrings.MODEL_COLLECTION_NAME)) {
+ File config_file = new File(collections[i], file_name);
+ if (config_file.exists()) {
+ BasicCollectionConfiguration config = new BasicCollectionConfiguration(config_file);
+ if (config.getCollectGroup().equals("true")) {
+ loadCollectionConfigs(collections[i]);
+ } else {
+ list_model.addElement(config);
+ config = null;
+ }
+ }
+ }
+ }
+ }
+
+
+ /** A button listener implementation, which listens for actions on the close button and disposes of the dialog when detected. */
+ private class CloseButtonListener
+ implements ActionListener {
+ /** Any implementation of ActionListener must include this method so we can be informed when the button is actioned.
+ * @param event An ActionEvent containing all the relevant information garnered from the event itself.
+ */
+ public void actionPerformed(ActionEvent event) {
+ // Done
+ prompt.dispose();
+ }
+ }
+
+ /** This private class listens for selection events in from the list and then displays the appropriate details for that collection.
+ */
+ private class CollectionListListener
+ implements ListSelectionListener {
+ /** Any implementation of ListSelectionListener must include this method so we can be informed when the list selection changes.
+ * @param event a ListSelectionEvent containing all the relevant information garnered from the event itself
+ */
+ public void valueChanged(ListSelectionEvent event) {
+ ok_button.setEnabled(false);
+ confirmation.setSelected(false);
+ if(!list.isSelectionEmpty()) {
+ collection = (BasicCollectionConfiguration) list.getSelectedValue();
+
+ String currentColName = (Gatherer.c_man.getCollection() == null) ? "" : Gatherer.c_man.getCollection().getName();
+ if(!collection.getShortName().equals(currentColName)) {
+ // the selected collection can only be deleted if it is NOT the collection that is currently open
+ confirmation.setEnabled(true);
+ confirmation.setToolTipText(null); // no tooltip
+ ok_button.setToolTipText(null);
+
+ args = new String[3];
+ args[0] = collection.getCreator();
+ args[1] = collection.getMaintainer();
+ args[2] = collection.getDescription();
+ details.setText(Dictionary.get("DeleteCollectionPrompt.Details", args));
+ details.setCaretPosition(0);
+ } else { // trying to delete the collection that is open at present
+ // add a tooltip saying to the confirmation checkbox saying that the current collection can't be deleted
+ String tooltip = Dictionary.get("DeleteCollectionPrompt.Cannot_Delete_Open_Collection_Tooltip",
+ collection.getName());
+ confirmation.setToolTipText(tooltip);
+ ok_button.setToolTipText(tooltip);
+ // delete/ok button and confirmation checkbox should be disabled
+ ok_button.setEnabled(false);
+ confirmation.setEnabled(false);
+ confirmation.setSelected(false);
+ }
+ }
+ else {
+ confirmation.setEnabled(false);
+ details.setText(Dictionary.get("DeleteCollectionPrompt.No_Collection"));
+ }
+ }
+ }
+
+ /** A check box listener so we can tell if the user has confirmed the deletion */
+ private class ConfirmationCheckBoxListener
+ implements ActionListener {
+ /** Any implementation of ActionListener must include this method so we can be informed when the button is actioned.
+ * @param event An ActionEvent containing all the relevant information garnered from the event itself.
+ */
+ public void actionPerformed(ActionEvent event) {
+ // OK button is only enabled if the confirmation check box is ticked
+ ok_button.setEnabled(confirmation.isSelected());
+ }
+ }
+
+ /** The OK button listener implementation. */
+ private class OKButtonListener
+ implements ActionListener {
+ /** Any implementation of ActionListener must include this method so we can be informed when the button is actioned.
+ * @param event An ActionEvent containing all the relevant information garnered from the event itself.
+ * @see org.greenstone.gatherer.Configuration
+ * @see org.greenstone.gatherer.Gatherer
+ * @see org.greenstone.gatherer.util.Utility
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ // Delete the selected collection.
+ if (Gatherer.c_man.deleteCollection(collection.getShortName())) {
+ // Refresh the collections shown in the workspace tree
+ Gatherer.g_man.refreshWorkspaceTree(WorkspaceTree.LIBRARY_CONTENTS_CHANGED);
+
+ if (collection.getShortName().equals(CollectionManager.getLoadedCollectionName())) {
+ current_coll_deleted = true;
+ }
+ list_model.removeElement(collection);
+
+ if (!Configuration.fedora_info.isActive()) { // for fedora collections, delete in background
+ resultPrompt(true);
+ }
+ details.setText(Dictionary.get("DeleteCollectionPrompt.No_Collection"));
+ confirmation.setEnabled(false);
+ confirmation.setSelected(false);
+ ok_button.setEnabled(false);
+ collection = null;
+ }
+ else {
+ resultPrompt(false);
+ }
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/DesignPane.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/DesignPane.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/DesignPane.java (revision 31635)
@@ -0,0 +1,77 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.cdm.CollectionDesignManager;
+import org.greenstone.gatherer.cdm.Control;
+
+import org.greenstone.gatherer.Configuration;
+
+public class DesignPane
+ extends BaseConfigPane {
+
+ /** The constructor. */
+ public DesignPane() {
+ super();
+ if (Configuration.fedora_info.isActive()) {
+ contents = new String [] { "CDM.GUI.Plugins" };
+ }
+ else {
+ contents = new String [] { "CDM.GUI.Plugins", "CDM.GUI.Indexes", "CDM.GUI.Subcollections", "CDM.GUI.Classifiers" };
+ }
+
+ }
+
+ protected Control getSubControls(String type) {
+ if(type.equals("CDM.GUI.Plugins")) {
+ return CollectionDesignManager.plugin_manager.getControls();
+ }
+ if(type.equals("CDM.GUI.Indexes")) {
+ return CollectionDesignManager.index_manager.getControls();
+ }
+ if(type.equals("CDM.GUI.Subcollections")) {
+ return CollectionDesignManager.subcollection_manager.getControls();
+ }
+ if(type.equals("CDM.GUI.Classifiers")) {
+ return CollectionDesignManager.classifier_manager.getControls();
+ }
+
+ return null;
+ }
+
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/DesignPaneHeader.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/DesignPaneHeader.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/DesignPaneHeader.java (revision 31635)
@@ -0,0 +1,82 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: Katherine Don, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.gui.HelpFrame;
+import org.greenstone.gatherer.gui.MenuBar;
+
+/** creates a header pane containing the title of the screen, and a help button which links to the appropriate help page */
+public class DesignPaneHeader
+ extends JPanel
+{
+ protected String help_key_name;
+
+
+ public DesignPaneHeader(String label_key, String help_key)
+ {
+ this.setComponentOrientation(Dictionary.getOrientation());
+ JLabel title_label = new JLabel("" + Dictionary.get(label_key) + " ");
+ title_label.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel title_pane = new JPanel();
+ title_pane.setBorder(BorderFactory.createEmptyBorder(0,0,0,5));
+ title_pane.setLayout(new BorderLayout());
+ title_pane.add(title_label, BorderLayout.CENTER);
+ title_pane.add(new JSeparator(), BorderLayout.SOUTH);
+ title_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ GLIButton help_button = new GLIButton(Dictionary.get("CDM.HelpButton"), Dictionary.get("CDM.HelpButton_Tooltip"));
+ this.help_key_name = help_key;
+ help_button.setIcon(MenuBar.HELP_ICON);
+ help_button.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent event) {
+ HelpFrame.setView(help_key_name);
+ }
+ });
+
+ setLayout(new BorderLayout());
+ add(title_pane, BorderLayout.CENTER);
+ add(help_button, BorderLayout.LINE_END);
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/DownloadPane.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/DownloadPane.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/DownloadPane.java (revision 31635)
@@ -0,0 +1,799 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.event.*;
+import javax.swing.tree.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.file.WorkspaceTree;
+import org.greenstone.gatherer.greenstone.LocalGreenstone;
+import org.greenstone.gatherer.util.SafeProcess;
+import org.greenstone.gatherer.util.StaticStrings;
+import org.greenstone.gatherer.util.Utility;
+import org.greenstone.gatherer.download.Download;
+import org.greenstone.gatherer.download.DownloadScrollPane;
+import org.greenstone.gatherer.download.ServerInfoDialog;
+import org.greenstone.gatherer.util.XMLTools;
+import org.greenstone.gatherer.cdm.*;
+import org.greenstone.gatherer.gui.*;
+import org.w3c.dom.*;
+import org.xml.sax.*;
+import org.greenstone.gatherer.GAuthenticator;
+
+/**
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.1
+ */
+public class DownloadPane
+ extends JPanel {
+
+ static final private Dimension LABEL_SIZE = new Dimension(225, 25);
+ static final private Dimension TREE_SIZE = new Dimension(150, 500);
+ //static final private String CONTENTS[] = { "DOWNLOAD.MODE.WebDownload", "DOWNLOAD.MODE.MediaWikiDownload", "DOWNLOAD.MODE.OAIDownload", "DOWNLOAD.MODE.ZDownload" , "DOWNLOAD.MODE.SRWDownload"};
+ private String CONTENTS[] = null;
+
+ private boolean download_button_enabled = false;
+ private boolean ready = false;
+
+ private JPanel options_pane;
+ // TODO should use Vector to store all loaded downloads!!
+
+ private DesignTree tree;
+ private HashMap download_map;
+ private ServerInfoDialog server_info;
+ private JScrollPane list_scroll;
+ private DownloadScrollPane getter;
+ private String mode = null;
+ private TreePath previous_path;
+ private String proxy_url = "";
+
+ /** Main System code */
+ public DownloadPane() {
+ super();
+ JScrollPane scrol_tmp;
+ this.setComponentOrientation(Dictionary.getOrientation());
+ // TODO: Download the WDownload and the download panel fixed!!
+ getter = new DownloadScrollPane();
+ getter.start();
+ list_scroll = getter.getDownloadJobList();
+ list_scroll.setComponentOrientation(Dictionary.getOrientation());
+
+ // TODO should use Vector to store all loaded downloads!!
+ String lang = Configuration.getLanguage();
+ download_map = new HashMap();
+
+ // run downloadinfo.pl -describeall, load the downloaders into the download_map,
+ // and get back their list of names, which are of the form "Download".
+ // Store these names in the CONTENTS[] array as "DOWNLOAD.MODE.Download",
+ // with z3950 as a minor exception: DOWNLOAD.MODE.ZDownload.
+ ArrayList downloaderNamesList = loadDownloadersInfo(lang);
+ int size = downloaderNamesList.size();
+ CONTENTS = new String[size];
+ for(int i = 0; i < size; i++) {
+ String downloadName = downloaderNamesList.get(i); // e.g. "WebDownload"
+ CONTENTS[i] = "DOWNLOAD.MODE."+downloadName.replace("3950", ""); // A special case is Z3950Download,
+ // which has to be stored in CONTENTS array as DOWNLOAD.MODE.ZDownload
+ }
+
+ // Creation
+ tree = new DesignTree();
+ tree.setComponentOrientation(Dictionary.getOrientation());
+ options_pane = new JPanel();
+ options_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JButton clear_cache_button = new GLIButton(Dictionary.get("Mirroring.ClearCache"), Dictionary.get("Mirroring.ClearCache_Tooltip"));
+ clear_cache_button.setEnabled(true);
+ clear_cache_button.setMnemonic(KeyEvent.VK_C);
+
+ JButton download_button = new GLIButton(Dictionary.get("Mirroring.Download"), Dictionary.get("Mirroring.Download_Tooltip"));
+ download_button.setEnabled(true);
+ download_button.setMnemonic(KeyEvent.VK_D);
+
+ JButton information_button = new GLIButton(Dictionary.get("Download.ServerInformation"), Dictionary.get("Download.ServerInformation_Tooltip"));
+ information_button.setEnabled(true);
+ information_button.setMnemonic(KeyEvent.VK_S);
+
+
+ JButton preferences_button = new GLIButton(Dictionary.get("Mirroring.Preferences"), Dictionary.get("Mirroring.Preferences_Tooltip"));
+ preferences_button.setEnabled(true);
+ preferences_button.setMnemonic(KeyEvent.VK_P);
+
+ // Connect
+ clear_cache_button.addActionListener(new ClearCacheListener());
+ download_button.addActionListener(new DownloadButtonListener());
+ preferences_button.addActionListener(new PreferencesButtonActionListener());
+ information_button.addActionListener(new InformationButtonActionListener());
+ tree.addTreeSelectionListener(new TreeListener());
+
+ // Add to Panel
+ JPanel button_pane = new JPanel();
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+ button_pane.setLayout(new GridLayout(1,4)); // GridLayout so button pane resizes with window-width
+ button_pane.setBorder(BorderFactory.createEtchedBorder());
+ button_pane.add(clear_cache_button);
+ button_pane.add(download_button);
+ button_pane.add(information_button);
+ button_pane.add(preferences_button);
+
+ JPanel tree_pane = new JPanel();
+ tree_pane.setComponentOrientation(Dictionary.getOrientation());
+ tree_pane.setLayout(new BorderLayout());
+ scrol_tmp = new JScrollPane(tree);
+ scrol_tmp.setComponentOrientation(Dictionary.getOrientation());
+ tree_pane.add(scrol_tmp, BorderLayout.CENTER);
+ tree_pane.setPreferredSize(TREE_SIZE);
+
+
+ Color colour_two = Configuration.getColor("coloring.collection_tree_background", false);
+ options_pane.setBackground(colour_two);
+ options_pane.setBorder(BorderFactory.createEtchedBorder());
+
+
+ JScrollPane options_scroll_pane = new JScrollPane(options_pane);
+ options_scroll_pane.setComponentOrientation(Dictionary.getOrientation());
+ JSplitPane mode_pane = new JSplitPane();
+ mode_pane.setComponentOrientation(Dictionary.getOrientation());
+ mode_pane.setBorder(BorderFactory.createEmptyBorder(0,0,0,0));
+ if (Dictionary.getOrientation().isLeftToRight()){
+ mode_pane.add(tree_pane,JSplitPane.LEFT);
+ mode_pane.add(options_scroll_pane,JSplitPane.RIGHT);
+ mode_pane.setDividerLocation(TREE_SIZE.width);
+ }else{
+ mode_pane.add(tree_pane,JSplitPane.RIGHT);
+ mode_pane.add(options_scroll_pane,JSplitPane.LEFT);
+ mode_pane.setDividerLocation(1-TREE_SIZE.width);
+ }
+
+
+ JPanel edit_pane = new JPanel();
+ edit_pane.setComponentOrientation(Dictionary.getOrientation());
+ edit_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(2,0,0,0), BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Download Setting"), BorderFactory.createEmptyBorder(2,2,2,2))));
+ edit_pane.setLayout(new BorderLayout());
+ edit_pane.add(mode_pane,BorderLayout.CENTER);
+ edit_pane.add(button_pane,BorderLayout.PAGE_END);
+
+ // Add to "this"
+ setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ setLayout(new GridLayout(2,1));
+ add(edit_pane);
+ add(list_scroll);
+
+ //set the mode to the first downloader in the list
+ mode = convertCONTENTStoMode(CONTENTS[0]); // e.g. Web
+ generateOptions(options_pane,(Download)download_map.get(mode));
+ previous_path = tree.getSelectionPath();
+ }
+
+ /** System Utilities */
+ public void modeChanged(int gli_mode) {
+ // do nothing at this stage - should we be renewing download options??
+ }
+
+ private void addHeader(String name, Color color, JPanel target_pane) {
+ JPanel header = new JPanel();
+ header.setComponentOrientation(Dictionary.getOrientation());
+ header.setBackground(color);
+ JPanel inner_pane = new JPanel();
+ inner_pane.setComponentOrientation(Dictionary.getOrientation());
+ inner_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(5,5,5,5), BorderFactory.createRaisedBevelBorder()));
+ inner_pane.setBackground(color);
+ JLabel header_label = new JLabel("" + name + " ");
+ header_label.setComponentOrientation(Dictionary.getOrientation());
+ header_label.setBackground(Configuration.getColor("coloring.collection_heading_background", false));
+ header_label.setHorizontalAlignment(JLabel.CENTER);
+ header_label.setOpaque(true);
+
+ // Layout
+ inner_pane.setLayout(new BorderLayout());
+ inner_pane.add(header_label, BorderLayout.CENTER);
+
+ header.setLayout(new BorderLayout());
+ header.add(inner_pane, BorderLayout.CENTER);
+ target_pane.add(header);
+ }
+
+ /** Supporting Functions */
+ private ArrayList loadDownloadersInfo(String lang) {
+ Document document = null;
+
+ try {
+ if (Gatherer.isGsdlRemote) {
+ String output = Gatherer.remoteGreenstoneServer.getScriptOptions("downloadinfo.pl", "&describeall");
+ Reader reader = new StringReader(output);
+ document = XMLTools.parseXML(reader);
+ }
+ else {
+ ArrayList args_list = new ArrayList();
+ String args[] = null;
+ if(Configuration.perl_path != null) {
+ args_list.add(Configuration.perl_path);
+ } else if(Utility.isWindows()) {
+ args_list.add("Perl.exe");
+ } else {
+ args_list.add("perl");
+ }
+ args_list.add("-S");
+ args_list.add(LocalGreenstone.getBinScriptDirectoryPath()+"downloadinfo.pl");
+ args_list.add("-describeall");
+ args_list.add("-xml");
+ args_list.add("-language");
+ args_list.add(lang);
+
+ // Create the process.
+ args = (String []) args_list.toArray(new String[0]);
+
+ SafeProcess process = new SafeProcess(args);
+ DebugStream.println("Getting Download Info: "+args_list);
+
+ // run the SafeProcess
+ int exitVal = process.runProcess();
+ if(exitVal != 0) {
+ throw new Exception("*** Error running Download Info process, process exited with: "
+ + exitVal);
+ }
+
+ // get the result and process it.
+ // This time we expect XML to have come out of the process std error stream.
+ String errStreamOutput = process.getStdError();
+ ///System.err.println("*********\nDownload Pane data, got:\n" + errStreamOutput + "\n**********\n");
+ StringReader xmlStrReader = new StringReader(errStreamOutput);
+ document = XMLTools.parseXML(xmlStrReader);
+ xmlStrReader.close();
+
+ }
+
+
+ }
+ catch (Exception error) {
+ System.err.println("Failed when trying to parse downloadinfo.pl -describeall");
+ error.printStackTrace();
+ }
+
+ if(document != null) {
+ return parseXML(document.getDocumentElement());
+ }
+
+ return null;
+ }
+
+ private ArrayList parseXML(Node root) {
+ ArrayList downloaders = null;
+ Element downloadList = (Element)root;
+ int length = -1;
+ if(downloadList.hasAttribute("length")) {
+ length = Integer.parseInt(downloadList.getAttribute("length"));
+ downloaders = new ArrayList(length);
+
+ for (Node node = downloadList.getFirstChild(); node != null; node = node.getNextSibling()) {
+ // goes through each of describeAll
+
+ String download_name = null;
+
+ for(Node infoNode = node.getFirstChild();
+ infoNode != null; infoNode = infoNode.getNextSibling()) {
+
+ String node_name = infoNode.getNodeName();
+ if(node_name.equalsIgnoreCase("Name")) { // WebDownload
+ download_name = XMLTools.getValue(infoNode); // e.g. WebDownload
+ }
+
+ // At this top level of elements,
+ // skip all the downloaders that are Abstract, as these are pure superclasses
+ else if(node_name.equalsIgnoreCase("Abstract")) {
+ String isAbstract = XMLTools.getValue(infoNode);
+
+ if(isAbstract.equalsIgnoreCase("no") && download_name != null) {
+ downloaders.add(download_name);
+ Download downloader = parseDownloadInfoXML(node); // parse the node properly
+ // now embedded references to abstract superclasses (embedded nodes)
+ // will be handled
+
+ String shortName = download_name.replace("Download", ""); // e.g. "Web"
+ download_map.put(shortName, downloader);
+ }
+ }
+ }
+ }
+ }
+
+ return downloaders;
+ }
+
+ private Download parseDownloadInfoXML(Node root) {
+
+ Download download = new Download();
+ String node_name = null;
+ for (Node node = root.getFirstChild(); node != null; node = node.getNextSibling()) {
+ node_name = node.getNodeName();
+ if(node_name.equalsIgnoreCase("Name")) {
+ String name = XMLTools.getValue(node);
+ download.setName(name);
+ }
+ else if (node_name.equalsIgnoreCase("Desc")) {
+ download.setDescription(XMLTools.getValue(node));
+ }
+ else if (node_name.equalsIgnoreCase("Abstract")) {
+ download.setIsAbstract(XMLTools.getValue(node).equalsIgnoreCase(StaticStrings.YES_STR));
+ }
+ else if(node_name.equalsIgnoreCase("Arguments")) {
+ for(Node arg = node.getFirstChild(); arg != null; arg = arg.getNextSibling()) {
+ node_name = arg.getNodeName();
+ if(node_name.equalsIgnoreCase("Option")) {
+ Argument argument = new Argument((Element)arg);
+ argument.parseXML((Element)arg);
+ argument.setValue(argument.getDefaultValue());
+ download.addArgument(argument);
+ }
+
+ }
+ }
+ else if(node_name.equalsIgnoreCase("DownloadInfo")) {
+ Download super_download = parseDownloadInfoXML(node);
+ download.setSuper(super_download);
+ }
+ }
+
+ if(download.getName() != null) {
+ return download;
+ }
+ return null;
+ }
+
+ /** Update the previous setup */
+ private boolean updateArguments(boolean checkRequired)
+ {
+ boolean cont = true;
+ for(int i = 0; i < options_pane.getComponentCount(); i++) {
+
+ Component component = options_pane.getComponent(i);
+ if(component instanceof ArgumentControl) {
+ cont = cont && ((ArgumentControl)component).updateArgument(checkRequired);
+ }
+ }
+
+ if(cont){return true; }
+
+ return false;
+ }
+
+ /** Generate Controls for Options */
+ /* at some stage we should think about which options should be shown for
+ * different modes. Currently, always show all options (unless hidden)*/
+ private void generateOptions(JPanel options_pane, ArgumentContainer data) {
+ options_pane.removeAll();
+ /** Create the current option panel */
+
+ ArrayList arguments = data.getArguments(true, false);
+ int mode = Configuration.getMode();
+ ArrayList added_arguments = new ArrayList();
+
+ for(int i = 0; i < arguments.size(); i++) {
+ Argument argument = (Argument) arguments.get(i);
+
+ if (argument.isHiddenGLI()) continue;
+ ArgumentControl argument_control = new ArgumentControl(argument,false,null);
+ added_arguments.add(argument_control);
+ }
+
+
+ options_pane.setLayout(new GridLayout(added_arguments.size(),1));
+ for(int i = 0; i < added_arguments.size(); i++) {
+ options_pane.add((ArgumentControl)added_arguments.get(i));
+
+ }
+ }
+
+ /** Behaviour Functions */
+ public void afterDisplay() {
+ ready = true;
+ }
+
+
+ public void gainFocus() {
+ if(!ready) {
+ return;
+ }
+
+ // It is also a good time to determine if the download should be enabled - ie if its allowed to be enabled and a valid URL is present in the field.
+ download_button_enabled = true;
+ //download_button.setEnabled(download_button_enabled);
+ }
+
+
+
+ public void refresh(int refresh_reason, boolean ready)
+ {
+ }
+
+ /** Private classes */
+ /** This tree provides a 'table of contents' for the various components of the design process (collection configuration in more technical terms). */
+ private class DesignTree extends JTree {
+
+ private DesignNode root = null;
+ /** Constructor. Automatically generates all of the nodes, in the order of CONTENTS. */
+ public DesignTree() {
+ super();
+ this.setComponentOrientation(Dictionary.getOrientation());
+ resetModel(Configuration.getMode());
+ expandRow(0);
+ setRootVisible(false);
+ setSelectionRow(0);
+ }
+
+ /** Reset the model used by the design page contents tree. This is necessary to hide the partitions entry when in lower detail modes
+ * @param mode the current detail mode as an int
+ */
+ public void resetModel(int mode) {
+ root = new DesignNode("DOWNLOAD.MODE.Root");
+ // Now add the design categories.
+ for(int i = 0; i < CONTENTS.length; i++) {
+ root.add(new DesignNode(CONTENTS[i]));
+ }
+ this.setModel(new DefaultTreeModel(root));
+ updateUI();
+ }
+ /** Set the current view to the one specified.
+ * @param type the name of the desired view as a String
+ */
+ public void setSelectedView(String type) {
+ type = Dictionary.get(type);
+ for(int i = 0; i < root.getChildCount(); i++) {
+ DesignNode child = (DesignNode) root.getChildAt(i);
+ if(child.toString().equals(type)) {
+ TreePath path = new TreePath(child.getPath());
+ setSelectionPath(path);
+ }
+ }
+ }
+ }
+
+ /** A tree node that retains a reference to one of the possible design sub-views relating to the different sub-managers. */
+ private class DesignNode extends DefaultMutableTreeNode {
+ /** Constructor.
+ * @param object The Object assigned to this node.
+ */
+ public DesignNode(String object) {
+ super(object);
+ }
+ /** Retrieve a textual representation of the object.
+ * @return a String
+ */
+ public String toString() {
+ // return Dictionary.get("CDM.GUI." + (String)getUserObject());
+ return Dictionary.get((String) getUserObject());
+ }
+ }
+
+ /** Listens for selection changes in the 'contents' tree, and switches to the appropriate view. */
+ private class TreeListener
+ implements TreeSelectionListener {
+ /** Called whenever the selection changes, we must update the view so it matches the node selected.
+ * @param event A TreeSelectionEvent containing more information about the tree selection.
+ * @see org.greenstone.gatherer.cdm.ClassifierManager
+ * @see org.greenstone.gatherer.cdm.CollectionDesignManager
+ * @see org.greenstone.gatherer.cdm.CollectionMetaManager
+ * @see org.greenstone.gatherer.cdm.FormatManager
+ * @see org.greenstone.gatherer.cdm.LanguageManager
+ * @see org.greenstone.gatherer.cdm.MetadataSetView
+ * @see org.greenstone.gatherer.cdm.SubcollectionManager
+ * @see org.greenstone.gatherer.cdm.TranslationView
+ * @see org.greenstone.gatherer.cdm.PlugInManager
+ */
+ public void valueChanged(TreeSelectionEvent event) {
+ if(!tree.isSelectionEmpty()) {
+ TreePath path = tree.getSelectionPath();
+
+ DesignNode node = (DesignNode)path.getLastPathComponent();
+ String type = (String)node.getUserObject();
+ Gatherer.g_man.wait(true);
+
+ // type has the value DOWNLOAD.MODE.Download,
+ // mode should then be of the form
+ mode = convertCONTENTStoMode(type);
+ generateOptions(options_pane,(Download)download_map.get(mode));
+
+ tree.setSelectionPath(path);
+ previous_path = path;
+ repaint();
+
+ Gatherer.g_man.wait(false);
+ }
+ }
+ }
+
+ private String convertCONTENTStoMode(String content) {
+ return content.replace("DOWNLOAD.MODE.", "").replace("ZDownload", "Z3950").replace("Download", "");
+ }
+
+ private class ClearCacheListener
+ implements ActionListener {
+ public void actionPerformed(ActionEvent event) {
+ // Retrieve the cache folder and delete it.
+ Utility.delete(Utility.getCacheDir());
+ // ...and refresh the node in the workspace tree to show it's all gone
+ Gatherer.g_man.refreshWorkspaceTree(WorkspaceTree.DOWNLOADED_FILES_CHANGED);
+ }
+ }
+
+ private class DownloadButtonListener
+ implements ActionListener {
+ public void actionPerformed(ActionEvent event) {
+
+ if(checkURL(true) && checkProxy() == true) {
+
+ // Proxy settings are now set. Check that the url is not a redirect, else get
+ // redirect url (we do this step in order to avoid some unintuitive behaviour from wget)
+ Download current_download = (Download)download_map.get(mode);
+ Argument arg_url = current_download.getArgument("url");
+ if(arg_url != null) { // it's null for z3950 and possibly for other downloaders
+ String url_str = arg_url.getValue();
+ String redirect_url_str = getRedirectURL(url_str);
+
+ // only update the Argument and its GUI ArgumentControl if the URL
+ // has in fact changed
+ if(!url_str.equals(redirect_url_str)) {
+ arg_url.setValue(redirect_url_str);
+ updateArgument(arg_url, redirect_url_str);
+ }
+ }
+ getter.newDownloadJob((Download)download_map.get(mode) ,mode,proxy_url);
+ }
+ }
+ }
+
+ /**
+ * The Java code here will retrieve the page at the given url. If the response code is
+ * a redirect, it will get the redirect url so that wget may be called with the proper url.
+ * This preprocessing of the URL is necessary because:
+ * Wget does not behave the way the browser does when faced with urls of the form
+ * http://www.englishhistory.net/tudor/citizens and if that page does not exist.
+ * The directory listing with a slash at the end (http://www.englishhistory.net/tudor/citizens/)
+ * does exist, however. In order to prevent wget from assuming that the root URL
+ * to traverse is http://www.englishhistory.net/tudor/ instead of the intended
+ * http://www.englishhistory.net/tudor/citizens/, we need give wget the redirect location
+ * that's returned when we initially make a request for http://www.englishhistory.net/tudor/citizens
+ * The proper url is sent back in the Location header, allowing us to bypass wget's
+ * unexpected behaviour.
+ * This method ensures that urls like http://www.nzdl.org/niupepa also continue to work:
+ * there is no http://www.nzdl.org/niupepa/ page, because this url actually redirects to an
+ * entirely different URL.
+ * @return the redirect url for the given url if any redirection is involved, or the
+ * url_str.
+ */
+ private String getRedirectURL(String url_str) {
+ HttpURLConnection connection = null;
+ if(url_str.startsWith("http:")) { // only test http urls
+ try {
+ URL url = new URL(url_str);
+ connection = (HttpURLConnection)url.openConnection(); //new HttpURLConnection(url);
+ // don't let it automatically follow redirects, since we want to
+ // find out whether we are dealing with redirects in the first place
+ connection.setInstanceFollowRedirects(false);
+
+ // now check for whether we get a redirect response
+ // HTTP Codes 3xx are redirects, http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
+ int responseCode = connection.getResponseCode();
+ if(responseCode >= 300 && responseCode < 400) {
+ String responseMsg = connection.getResponseMessage();
+
+ // Get the Location header since this specifies the new location of the resource
+ String location = connection.getHeaderField("Location");
+
+ // this becomes the url that wget should download from
+ url_str = location.trim();
+ }
+
+ connection.disconnect();
+ } catch(Exception e) {
+ if(connection != null) {
+ connection.disconnect();
+ }
+ System.err.println("Checking redirection. Tried to connect to "
+ + url_str + ",\nbut got exception: " + e);
+ }
+ }
+
+ return url_str;
+ }
+
+
+ /** For a string-based Argument whose value has changed, this method
+ * updates the GUI ArgumentControl's value correspondingly. */
+ private void updateArgument(Argument arg, String value) {
+ for(int i = 0; i < options_pane.getComponentCount(); i++) {
+ Component component = options_pane.getComponent(i);
+ if(component instanceof ArgumentControl) {
+ ArgumentControl control = (ArgumentControl)component;
+ if(control.getArgument() == arg) {
+ control.setValue(value);
+ control.repaint();
+ }
+ }
+ }
+ }
+
+ private boolean checkURL(boolean checkRequired){
+
+ if (!updateArguments(checkRequired)){
+ return false;
+ }
+
+ Download current_download = (Download)download_map.get(mode);
+ Argument arg_url = current_download.getArgument("url");
+
+ if (arg_url == null) return true;
+
+ String url_str = arg_url.getValue();
+ URL url = null;
+ try {
+ url = new URL(url_str);
+ }
+ catch(MalformedURLException error) {
+ JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("Mirroring.Invalid_URL"), Dictionary.get("Mirroring.Invalid_URL_Title"), JOptionPane.ERROR_MESSAGE);
+ return false;
+ }
+
+ return true;
+ }
+
+
+ private boolean checkProxy(){
+
+ proxy_url = null;
+
+ Download current_download = (Download)download_map.get(mode);
+
+ Argument arg = current_download.getArgument("proxy_on");
+
+ if (arg == null) return true;
+
+ // Determine if we have to use a proxy.
+ if(Configuration.get("general.use_proxy", true)) {
+
+ String proxy_host = Configuration.getString("general.proxy_host", true);
+ String proxy_port = Configuration.getString("general.proxy_port", true);
+ // Find out whether the user has already authenticated themselves
+ String user_pass = "";
+ String address = proxy_host + ":" + proxy_port;
+
+ int count = 0;
+ // Only for wget, need to avoid a second automatic authentication popup (first asks
+ // the proxy authentication for wget, and the second will ask the same for the realm)
+ // Once the authentication has been reused, it will set the GAuthenticator state back to REGULAR
+ GAuthenticator.setMode(GAuthenticator.DOWNLOAD);
+ while(count < 3 && (user_pass = (String) GAuthenticator.authentications.get(address)) == null) {
+ Authenticator.requestPasswordAuthentication(proxy_host, null, Integer.parseInt(proxy_port), "http://", Dictionary.get("WGet.Prompt"), "HTTP");
+ count++;
+ }
+ if(count >= 3) {
+ return false;
+ }
+
+ if(user_pass.indexOf("@") != -1) {
+
+ // Write the use proxy command - we don't do this anymore, instead we set environment variables - hopefully these can't be spied on like the following can (using ps) - actually the environment stuff didn't work for windows, so lets go back to this
+ if (Utility.isWindows()) {
+
+ arg.setValue("true");
+ arg.setAssigned(true);
+
+ arg = current_download.getArgument("proxy_host");
+ arg.setValue(proxy_host);
+ arg.setAssigned(true);
+
+ arg = current_download.getArgument("proxy_port");
+ arg.setValue(proxy_port);
+ arg.setAssigned(true);
+
+ arg = current_download.getArgument("user_name");
+ arg.setValue(user_pass.substring(0, user_pass.indexOf("@")));
+ arg.setAssigned(true);
+
+ arg = current_download.getArgument("user_password");
+ arg.setValue(user_pass.substring(user_pass.indexOf("@") + 1));
+ arg.setAssigned(true);
+ }
+ else{
+ String user_name = user_pass.substring(0, user_pass.indexOf("@"));
+ String user_pwd = user_pass.substring(user_pass.indexOf("@") + 1);
+ proxy_url = user_name+":"+user_pwd+"@"+proxy_host+":"+proxy_port+"/";
+
+ }
+
+ return true;
+ }
+ else{
+ return false;
+ }
+
+ }
+
+ return true;
+ }
+
+ /*
+ private class PreferencesButtonActionListener
+ implements ActionListener {
+ public void actionPerformed(ActionEvent event) {
+ new Preferences(Preferences.CONNECTION_PREFS);
+ }
+ }*/
+
+ private class InformationButtonActionListener
+ implements ActionListener {
+ public void actionPerformed(ActionEvent event) {
+ //turn off the check for find argument
+ Download current_download = (Download)download_map.get(mode);
+
+ if (!checkProxy() || !checkURL(false) )return;
+
+
+ if(server_info != null) {
+ server_info.dispose();
+ }
+
+
+ Argument arg_url = current_download.getArgument("url");
+ String str_url = "";
+
+ if( arg_url!= null && arg_url.isAssigned()) {
+ str_url = arg_url.getValue();
+ }
+
+
+ server_info = new ServerInfoDialog(str_url ,proxy_url, mode,(Download)download_map.get(mode));
+
+ }
+ }
+
+ private class PreferencesButtonActionListener
+ implements ActionListener {
+ public void actionPerformed(ActionEvent event) {
+ new Preferences(Preferences.CONNECTION_PREFS);
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/EditorDialog.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/EditorDialog.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/EditorDialog.java (revision 31635)
@@ -0,0 +1,156 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+
+/** A class that extends a JDialog into a editor for editing large block of text for the metadata value.
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.3
+ */
+final public class EditorDialog
+ extends ModalDialog
+ implements ActionListener {
+ /** Is this dialog editable? */
+ private boolean editable = true;
+ /** The cancel, and I don't want the text I've typed, button. */
+ private JButton cancel = null;
+ /** The ok, I'll save what I've just typed in, button. */
+ private JButton ok = null;
+ /** The area in which we type. */
+ private JTextArea text = null;
+ /** And what result should be passed back to our caller. */
+ private String result = null;
+ /** The size of the edit pop-up. */
+ final static private Dimension SIZE = new Dimension(400, 300);
+
+ /** Constructor */
+ public EditorDialog() {
+ super(Gatherer.g_man);
+ this.setComponentOrientation(Dictionary.getOrientation());
+ }
+
+ /** Any implementation of ActionListener must include this method so we can be informed when an action has been performed on one of our target controls. In this case we generate a pop-up window to edit in.
+ * @param event An ActionEvent containing information about the event.
+ */
+ public void actionPerformed(ActionEvent event) {
+ if (event.getSource() == ok) {
+ result = text.getText();
+ }
+ dispose();
+ }
+
+ /** Method to display the editing box on screen.
+ * @param value The initial text to be displayed in the editing area, as a String .
+ * @return The new value for the metadata value as a String or null if the user has pressed cancel.
+ */
+ public String display(String value) {
+ setModal(true);
+ setSize(SIZE);
+ setJMenuBar(new SimpleMenuBar("theenrichview"));
+ if (editable) {
+ setTitle(Dictionary.get("General.Edit"));
+ } else {
+ setTitle(Dictionary.get("General.View"));
+ }
+ // Create
+ text = new JTextArea(value);
+ text.setComponentOrientation(Dictionary.getOrientation());
+ text.setCaretPosition(value.length());
+ text.setEditable(editable);
+ text.setLineWrap(true);
+ text.setWrapStyleWord(false);
+ if (editable) {
+ text.setToolTipText(Dictionary.get("EnrichPane.Value_Field_Tooltip"));
+ } else {
+ text.setToolTipText(Dictionary.get("EnrichPane.Value_Field_Tooltip_Uneditable"));
+ }
+
+ cancel = new GLIButton(Dictionary.get("General.Cancel"), Dictionary.get("General.Pure_Cancel_Tooltip"));
+
+ ok = new GLIButton(Dictionary.get("General.OK"), Dictionary.get("General.OK_Tooltip"));
+
+
+ // Listeners
+ cancel.addActionListener(this);
+ ok.addActionListener(this);
+
+ // Layout
+ JPanel button_pane = new JPanel();
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+ button_pane.setLayout(new GridLayout(1,2));
+ if(editable) {
+ button_pane.add(ok);
+ button_pane.add(cancel);
+ }
+ else {
+ button_pane.add(new JPanel());
+ button_pane.add(ok);
+ }
+
+ JPanel content_pane = (JPanel) getContentPane();
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+ content_pane.setLayout(new BorderLayout());
+ JScrollPane scrol_tmp;
+ scrol_tmp = new JScrollPane(text);
+ scrol_tmp.setComponentOrientation(Dictionary.getOrientation());
+ content_pane.add(scrol_tmp, BorderLayout.CENTER);
+ content_pane.add(button_pane, BorderLayout.SOUTH);
+
+ Dimension screen_size = Configuration.screen_size;
+ setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
+ setVisible(true);
+ return result;
+ }
+
+ /** Specify if this text dialog should be editable or readonly
+ * @param editable true to allow editing, false otherwise
+ */
+ public void setEditable(boolean editable) {
+ if(text == null) {
+ this.editable = editable;
+ }
+ else {
+ text.setEditable(editable);
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/EmailField.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/EmailField.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/EmailField.java (revision 31635)
@@ -0,0 +1,69 @@
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.util.regex.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.text.*;
+import org.greenstone.gatherer.Dictionary;
+
+public class EmailField
+ extends JTextField
+ implements DocumentListener {
+
+ static final public Pattern EMAIL_PATTERN = Pattern.compile("([^()\\-<> @,;:\"][^()<> @,;:\"]*|\"[^()<>@,;:\"]+\")@([A-Za-z](-*[A-Za-z0-9])*(\\.[A-Za-z](-*[A-Za-z0-9])*)*)");
+
+ private boolean invalid = false;
+ private Color background;
+ private Color invalid_background;
+
+ public EmailField(Color invalid_background) {
+ super();
+ this.setComponentOrientation(Dictionary.getOrientation());
+ this.invalid_background = invalid_background;
+ }
+
+ public EmailField(String email, Color invalid_background) {
+ super(email);
+ this.invalid_background = invalid_background;
+ }
+
+ /** Gives notification that an attribute or set of attributes changed.
+ * @param e
+ */
+ public void changedUpdate(DocumentEvent e) {
+ validateEmail();
+ }
+
+ /** Gives notification that there was an insert into the document.
+ * @param e
+ */
+ public void insertUpdate(DocumentEvent e) {
+ validateEmail();
+ }
+
+ /** Gives notification that a portion of the document has been removed.
+ * @param e
+ */
+ public void removeUpdate(DocumentEvent e) {
+ validateEmail();
+ }
+
+ private void validateEmail() {
+ Matcher m = EMAIL_PATTERN.matcher(getText());
+ if(m.matches()) {
+ // It was invalid, but now its valid again
+ if(invalid) {
+ setBackground(background);
+ invalid = false;
+ }
+ // Otherwise nothings changed so why do anything
+ }
+ else {
+ background = getBackground();
+ setBackground(invalid_background);
+ invalid = true;
+ }
+ m = null;
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/EnrichPane.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/EnrichPane.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/EnrichPane.java (revision 31635)
@@ -0,0 +1,430 @@
+/**
+ *############################################################################
+ * A component of the Greenstone Librarian Interface, part of the Greenstone
+ * digital library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
+ * Based on code by John Thompson
+ *
+ * Copyright (C) 2005 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################################################
+ */
+
+package org.greenstone.gatherer.gui;
+
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.text.*;
+import javax.swing.tree.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.collection.CollectionTree;
+import org.greenstone.gatherer.collection.CollectionTreeNode;
+import org.greenstone.gatherer.gui.tree.DragTree;
+import org.greenstone.gatherer.metadata.FilenameEncoding;
+import org.greenstone.gatherer.metadata.MetadataElement;
+import org.greenstone.gatherer.metadata.MetadataValue;
+import org.greenstone.gatherer.metadata.MetadataValueTableEntry;
+import org.greenstone.gatherer.metadata.MetadataValueTreeNode;
+import org.greenstone.gatherer.metadata.MetadataXMLFileManager;
+
+
+/** Provides a view of controls for the editing of metadata.
+ */
+public class EnrichPane
+ extends JPanel
+ implements TreeSelectionListener
+{
+ static private Dimension MINIMUM_SIZE = new Dimension(100, 100);
+ static private Dimension COLLECTION_TREE_SIZE = new Dimension(250, 500);
+
+ private boolean has_focus = false;
+ /** The collection tree. */
+ private CollectionTree collection_tree = null;
+ /** The currently reported selection. */
+ private CollectionTreeNode[] file_nodes = null;
+ /** Used to dynamically filter the collection tree. */
+ private Filter collection_filter = null;
+ /** The label at the top of the collection tree, which shows the collection name. */
+ private JLabel collection_label;
+ /** The panel that contains the collection tree. */
+ private JPanel collection_pane = null;
+ /** The scrollable area into which the collection tree is placed. */
+ private JScrollPane collection_scroll = null;
+ /** The splitpane dividing the collection tree and the metadata editing controls. */
+ private JSplitPane external_split;
+ /** The metadata value table shows the metadata values that are currently assigned to a file. */
+ private MetadataValueTablePane metadata_value_table_pane = null;
+ /** The metadata value tree shows the metadata values that are currently assigned to a metadata element. */
+ private MetadataValueTreePane metadata_value_tree_pane = null;
+
+
+ public EnrichPane()
+ {
+ this.setComponentOrientation(Dictionary.getOrientation());
+ // Create the metadata value tree pane
+ metadata_value_tree_pane = new MetadataValueTreePane();
+ metadata_value_tree_pane.addMetadataValueTreeSelectionListener(new MetadataValueTreeSelectionListener());
+
+ // Create metadata value table pane
+ metadata_value_table_pane = new MetadataValueTablePane();
+ metadata_value_table_pane.addMetadataValueTableListSelectionListener(new MetadataValueTableListSelectionListener());
+ metadata_value_table_pane.addMetadataValueTableMouseListener(new MetadataValueTableMouseListener());
+ metadata_value_table_pane.addMetadataValueTextFieldDocumentListener(new MetadataValueTextFieldDocumentListener());
+ metadata_value_table_pane.addMetadataValueTextFieldKeyListener(new MetadataValueTextFieldKeyListener());
+ }
+
+
+ /** Some actions can only occur after this panel has been displayed on-screen, so this method is provided to do exactly that. Such actions include the proportioning of the split panes and the setting of table column widths.
+ */
+ public void afterDisplay()
+ {
+ if (Dictionary.getOrientation().isLeftToRight()){
+ external_split.setDividerLocation(0.3);
+ }else{
+ external_split.setDividerLocation(0.7);
+ }
+ }
+
+
+ /** Used to create, connect and layout the components to be shown on this control panel.
+ * @see org.greenstone.gatherer.Gatherer
+ * @see org.greenstone.gatherer.file.FileOpenActionListener
+ * @see org.greenstone.gatherer.gui.Filter
+ */
+ public void display()
+ {
+ JPanel left_hand_pane = new JPanel(new BorderLayout());
+ left_hand_pane.setComponentOrientation(Dictionary.getOrientation());
+ GLIButton metadata_set_button = new GLIButton(Dictionary.get("EnrichPane.ManageMetadataSets"), Dictionary.get("EnrichPane.ManageMetadataSets_Tooltip"));
+ metadata_set_button.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent event) {
+ MetadataSetDialog msd = new MetadataSetDialog();
+ if (msd.setsChanged()) {
+ valueChanged(null);
+ }
+ }
+ });
+
+ // Collection Tree
+ collection_pane = new JPanel(new BorderLayout());
+ // collection_pane.setComponentOrientation(Dictionary.getOrientation());
+ collection_pane.setMinimumSize(MINIMUM_SIZE);
+ collection_pane.setPreferredSize(COLLECTION_TREE_SIZE);
+
+ collection_label = new JLabel(Dictionary.get("Collection.No_Collection"));
+ collection_label.setComponentOrientation(Dictionary.getOrientation());
+ collection_label.setOpaque(true);
+
+ collection_tree = Gatherer.c_man.getCollectionTree();
+ collection_tree.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
+ collection_tree.addTreeSelectionListener(this);
+ collection_tree.setComponentOrientation(Dictionary.getOrientation());
+ collection_filter = collection_tree.getFilter();
+ external_split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
+ external_split.setComponentOrientation(Dictionary.getOrientation());
+ // Layout
+ collection_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(3,3,3,3), BorderFactory.createLoweredBevelBorder()));
+ collection_pane.setMinimumSize(MINIMUM_SIZE);
+ collection_pane.setPreferredSize(new Dimension(Gatherer.g_man.getSize().width / 3, Gatherer.g_man.getSize().height));
+ collection_pane.setComponentOrientation(Dictionary.getOrientation());
+ // Collection Pane
+ collection_pane.add(collection_label, BorderLayout.NORTH);
+
+ JSplitPane metadata_editing_pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
+ metadata_editing_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5));
+ metadata_editing_pane.setDividerSize(8);
+ metadata_editing_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ metadata_editing_pane.add(metadata_value_table_pane, JSplitPane.TOP);
+ metadata_editing_pane.add(metadata_value_tree_pane, JSplitPane.BOTTOM);
+ metadata_editing_pane.setDividerLocation(250);
+
+ left_hand_pane.add(collection_pane, BorderLayout.CENTER);
+ left_hand_pane.add(metadata_set_button, BorderLayout.SOUTH);
+ if (Dictionary.getOrientation().isLeftToRight()){
+ external_split.add(left_hand_pane, JSplitPane.LEFT);
+ external_split.add(metadata_editing_pane, JSplitPane.RIGHT);
+ }else{
+ external_split.add(left_hand_pane, JSplitPane.RIGHT);
+ external_split.add(metadata_editing_pane, JSplitPane.LEFT);
+ }
+
+
+
+ this.setLayout(new BorderLayout());
+ this.add(external_split, BorderLayout.CENTER);
+ }
+
+
+ /** Called to inform this pane that it has just gained focus as an effect of the user clicking on its tab
+ */
+ public void gainFocus()
+ {
+ // Remember that we currently have focus
+ has_focus = true;
+
+ // Add the collection tree and filter back into this pane
+ collection_scroll = new JScrollPane(collection_tree);
+ collection_pane.add(collection_scroll, BorderLayout.CENTER);
+ collection_pane.add(collection_filter, BorderLayout.SOUTH);
+
+ // Select the first node in the tree if nothing is already selected
+ if (collection_tree.getSelectionPaths() == null && collection_tree.getRowCount() > 0) {
+ collection_tree.setImmediate(true);
+ collection_tree.setSelectionRow(0);
+ collection_tree.setImmediate(false);
+ return;
+ }
+
+ // Force all of the controls to be updated
+ valueChanged(null);
+ }
+
+
+ /** Called to inform this pane that it has just lost focus as an effect of the user clicking on another tab
+ */
+ public void loseFocus()
+ {
+ // Very important: make sure metadata value is saved before leaving the pane
+ metadata_value_table_pane.stopEditingAndRebuild(file_nodes);
+
+ // Make sure all the metadata has been saved to file
+ MetadataXMLFileManager.saveMetadataXMLFiles();
+
+ // Remove the collection tree and filter from this pane so it can be added to the Gather pane
+ collection_pane.remove(collection_scroll);
+ collection_pane.remove(collection_filter);
+
+ // Remember that we no longer have focus
+ has_focus = false;
+ }
+
+
+ /** Called whenever the detail mode changes to ensure the filters are at an appropriate level (ie only editable by those that understand regular expression matching)
+ * @param mode the mode level as an int
+ */
+ public void modeChanged(int mode)
+ {
+ collection_filter.setEditable(mode >= Configuration.LIBRARIAN_MODE);
+ }
+
+
+ /** Refresh this pane, depending on what has just happened (refresh_reason). */
+ public void refresh(int refresh_reason, boolean collection_loaded)
+ {
+ if (collection_loaded) {
+ // Update collection label
+ collection_label.setText(Dictionary.get("Collection.Collection"));
+ collection_label.setBackground(Configuration.getColor("coloring.collection_heading_background", false));
+ collection_label.setForeground(Configuration.getColor("coloring.collection_heading_foreground", false));
+
+ // Update collection tree
+ if (refresh_reason == Gatherer.COLLECTION_OPENED) {
+ collection_tree.setModel(Gatherer.c_man.getCollectionTreeModel());
+ }
+
+ // Update collection filter
+ collection_filter.setBackground(Configuration.getColor("coloring.collection_heading_background", false));
+ }
+ else {
+ // Update collection label
+ collection_label.setText(Dictionary.get("Collection.No_Collection"));
+ collection_label.setBackground(Color.lightGray);
+ collection_label.setForeground(Color.black);
+
+ // Update collection tree
+ collection_tree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode("Error")));
+
+ // Update collection filter
+ collection_filter.setBackground(Color.lightGray);
+ }
+
+ // Enable or disable the controls
+ collection_tree.setEnabled(collection_loaded);
+ collection_filter.setEnabled(collection_loaded);
+
+ // Force the metadata table to be rebuilt (for switching extracted metadata on or off)
+ if (refresh_reason == Gatherer.PREFERENCES_CHANGED) {
+ metadata_value_table_pane.stopEditingAndRebuild(file_nodes);
+ }
+ }
+
+
+ /** Allows other classes to request the enrich pane to prepare for metadata
+ * saving.
+ * @author John Thompson, DL Consulting Ltd
+ */
+ public void stopEditingAndRebuild()
+ {
+ // Update the metadata value table (and consequently, the metadata value tree)
+ metadata_value_table_pane.stopEditingAndRebuild(file_nodes);
+ }
+ /** stopEditingAndRebuild() **/
+
+ /** Called whenever the collection tree selection changes. This causes the metadata value table to be rebuilt. */
+ public void valueChanged(TreeSelectionEvent event)
+ {
+ if(FilenameEncoding.isRefreshRequired()) {
+ // The CollectionTree is in the process of being re-built to deal with filename-encodings,
+ // so don't mess up the table during that time.
+ // (CollectionTreeNode.refreshDescendantEncodings() doesn't cope well if this method
+ // responds on Tree selection changes while the tree is updating its encodings, resulting in lost
+ // and misplaced metadata values in the table and thereby in the metadata.xml files themselves).
+ return;
+ }
+
+
+ // If we haven't got focus then it must have been a selection in the Gather pane, so don't bother rebuilding
+ if (has_focus == false) {
+ return;
+ }
+
+ // Nothing selected in the collection tree
+ if (collection_tree.getSelectionCount() == 0) {
+ file_nodes = null;
+ }
+
+ // Some files selected in the collection tree
+ else {
+ TreePath paths[] = collection_tree.getSelectionPaths();
+ file_nodes = new CollectionTreeNode[paths.length];
+ for (int i = 0; i < paths.length; i++) {
+ file_nodes[i] = (CollectionTreeNode) paths[i].getLastPathComponent();
+ }
+ }
+
+ // Update the metadata value table (and consequently, the metadata value tree)
+ metadata_value_table_pane.stopEditingAndRebuild(file_nodes);
+ }
+
+
+ private class MetadataValueTableListSelectionListener
+ implements ListSelectionListener
+ {
+ public void valueChanged(ListSelectionEvent list_selection_event)
+ {
+ // We only want to handle one event per selection, so wait for the value to stabilise
+ if (list_selection_event.getValueIsAdjusting()) {
+ return;
+ }
+
+ // Update the metadata value tree for the current table selection
+ metadata_value_tree_pane.rebuild(metadata_value_table_pane.getSelectedMetadataValueTableEntry());
+ }
+ }
+
+
+ private class MetadataValueTableMouseListener
+ extends MouseAdapter
+ {
+ public void mouseClicked(MouseEvent mouse_event)
+ {
+ // We're only interested in clicks on the inherited column
+ if (metadata_value_table_pane.isMouseEventForInheritedMetadataValueTableColumn(mouse_event) == false) {
+ return;
+ }
+
+ // If the selected metadata is inherited, switch to the folder it came from
+ MetadataValueTableEntry selected_metadata_value_table_entry = metadata_value_table_pane.getSelectedMetadataValueTableEntry();
+ if (selected_metadata_value_table_entry.isInheritedMetadata()) {
+ collection_tree.setSelection(selected_metadata_value_table_entry.getFolderMetadataInheritedFrom());
+ }
+ }
+ }
+
+
+ private class MetadataValueTextFieldDocumentListener
+ implements DocumentListener
+ {
+ /** Gives notification that an attribute or set of attributes changed */
+ public void changedUpdate(DocumentEvent document_event) {
+ validate(document_event);
+ }
+
+ /** Gives notification that there was an insert into the document */
+ public void insertUpdate(DocumentEvent document_event) {
+ validate(document_event);
+ }
+
+ /** Gives notification that a portion of the document has been removed */
+ public void removeUpdate(DocumentEvent document_event) {
+ validate(document_event);
+ }
+
+
+ /** Ensures that the value tree is updated in response to changes in the value text field */
+ private void validate(DocumentEvent document_event)
+ {
+ try {
+ Document document = document_event.getDocument();
+ String metadata_value_string = document.getText(0, document.getLength());
+ metadata_value_tree_pane.selectBestPathForMetadataValue(metadata_value_string);
+ }
+ catch (Exception exception) {
+ DebugStream.printStackTrace(exception);
+ }
+ }
+ }
+
+
+ private class MetadataValueTextFieldKeyListener
+ extends KeyAdapter
+ {
+ /** Gives notification of key events on the text field */
+ public void keyPressed(KeyEvent key_event)
+ {
+ // Tab: Auto-complete what is selected in the metadata value tree
+ if (key_event.getKeyCode() == KeyEvent.VK_TAB) {
+ MetadataValueTreeNode selected_metadata_value_tree_node = metadata_value_tree_pane.getSelectedMetadataValueTreeNode();
+ if (selected_metadata_value_tree_node != null) {
+ metadata_value_table_pane.setMetadataValueTextFieldValue(selected_metadata_value_tree_node.getFullValue());
+ }
+
+ // We do not want this event to be processed by the table also
+ key_event.consume();
+ }
+
+ // Enter: save the current value then add a blank row for the selected metadata element
+ if (key_event.getKeyCode() == KeyEvent.VK_ENTER) {
+ metadata_value_table_pane.stopEditingAndAddBlankRowForSelectedMetadataElement();
+ }
+ }
+ }
+
+
+ private class MetadataValueTreeSelectionListener
+ implements TreeSelectionListener
+ {
+ public void valueChanged(TreeSelectionEvent tree_selection_event)
+ {
+ // When a node is selected in the tree, fill the metadata value text field with the selected node's value
+ MetadataValueTreeNode selected_metadata_value_tree_node = metadata_value_tree_pane.getSelectedMetadataValueTreeNode();
+ if (selected_metadata_value_tree_node != null) {
+ metadata_value_table_pane.setMetadataValueTextFieldValue(selected_metadata_value_tree_node.getFullValue());
+ }
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/ExplodeMetadataDatabasePrompt.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/ExplodeMetadataDatabasePrompt.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/ExplodeMetadataDatabasePrompt.java (revision 31635)
@@ -0,0 +1,378 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Katherine Don, Greenstone Digital Library, University of Waikato
+ *
+ * Copyright (C) 2005 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.text.*;
+import java.io.*;
+import java.util.ArrayList;
+
+import org.w3c.dom.Document;
+
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.cdm.Argument;
+import org.greenstone.gatherer.cdm.ArgumentControl;
+import org.greenstone.gatherer.cdm.CollectionDesignManager;
+import org.greenstone.gatherer.cdm.Plugin;
+import org.greenstone.gatherer.collection.CollectionManager;
+import org.greenstone.gatherer.collection.ScriptOptions;
+import org.greenstone.gatherer.collection.Collection;
+import org.greenstone.gatherer.feedback.Base64;
+import org.greenstone.gatherer.greenstone.LocalGreenstone;
+import org.greenstone.gatherer.gui.tree.DragTree;
+import org.greenstone.gatherer.metadata.MetadataXMLFileManager;
+import org.greenstone.gatherer.metadata.MetadataElement;
+import org.greenstone.gatherer.metadata.MetadataSet;
+import org.greenstone.gatherer.metadata.MetadataSetManager;
+import org.greenstone.gatherer.metadata.MetadataTools;
+import org.greenstone.gatherer.remote.RemoteGreenstoneServer;
+import org.greenstone.gatherer.shell.GShell;
+import org.greenstone.gatherer.shell.GShellEvent;
+import org.greenstone.gatherer.shell.GShellListener;
+import org.greenstone.gatherer.util.Utility;
+import org.greenstone.gatherer.util.XMLTools;
+
+public class ExplodeMetadataDatabasePrompt
+ extends ModalDialog
+ implements GShellListener
+{
+ /** The size of this new collection dialog box. */
+ static private Dimension SIZE = new Dimension(675, 350);
+ private JDialog self;
+
+ /** the file we wil be exploding */
+ private File metadata_file = null;
+ /** the list of potential plugins to be used */
+ private Argument plugin_arg = null;
+ /** holds all the available options for the exploding script */
+ private ScriptOptions options = null;
+ /** the pane containing the options */
+ private JPanel options_pane = null;
+ /** the error message if any */
+ private StringBuffer error_message = null;
+ /** whether we were successful or not */
+ private boolean successful;
+
+
+ public ExplodeMetadataDatabasePrompt(File source_file) {
+ super(Gatherer.g_man, true);
+ this.self = this;
+ this.metadata_file = source_file;
+ this.setComponentOrientation(Dictionary.getOrientation());
+ // check that we actually have an explodable file
+ ArrayList exp_plugins = CollectionDesignManager.plugin_manager.getExploderPlugins(source_file);
+ if (exp_plugins.size() == 0) {
+ JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("ExplodeMetadataPrompt.NotExplodable"), Dictionary.get("ExplodeMetadataPrompt.Title"), JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ plugin_arg = createPluginArgument(exp_plugins);
+ setJMenuBar(new SimpleMenuBar("explodingmetadata"));
+ setSize(SIZE);
+ setTitle(Dictionary.get("ExplodeMetadataPrompt.Title"));
+
+ // set up the script options
+ // we have empty initial values
+ String dom_string = "\n ";
+ Document doc = XMLTools.parseXML(new StringReader(dom_string));
+ options = new ScriptOptions(doc.getDocumentElement(), "explode_metadata_database.pl");
+
+ // Creation
+ JPanel content_pane = (JPanel) getContentPane();
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+ content_pane.setOpaque(true);
+
+ options_pane = new JPanel();
+ options_pane.setComponentOrientation(Dictionary.getOrientation());
+ addScriptOptions(options_pane);
+ JScrollPane middle_pane = new JScrollPane(options_pane);
+ middle_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JTextArea instructions_area = new JTextArea(Dictionary.get("ExplodeMetadataPrompt.Instructions"));
+ instructions_area.setComponentOrientation(Dictionary.getOrientation());
+ instructions_area.setEditable(false);
+ instructions_area.setLineWrap(true);
+ instructions_area.setRows(5);
+ instructions_area.setWrapStyleWord(true);
+
+ JPanel button_pane = new JPanel();
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+ JButton explode_button = new GLIButton(Dictionary.get("ExplodeMetadataPrompt.Explode"), Dictionary.get("ExplodeMetadataPrompt.Explode_Tooltip"));
+ JButton cancel_button = new GLIButton(Dictionary.get("General.Cancel"), Dictionary.get("General.Cancel_Tooltip"));
+
+ // Connection
+ cancel_button.addActionListener(new CancelListener());
+ explode_button.addActionListener(new ExplodeListener());
+
+ // Layout
+
+ button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
+ button_pane.setLayout(new GridLayout(1,2));
+ button_pane.add(explode_button);
+ button_pane.add(cancel_button);
+
+ content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ content_pane.setLayout(new BorderLayout());
+ content_pane.add(instructions_area, BorderLayout.NORTH);
+ content_pane.add(middle_pane, BorderLayout.CENTER);
+ content_pane.add(button_pane, BorderLayout.SOUTH);
+
+ // Final dialog setup & positioning.
+ Dimension screen_size = Configuration.screen_size;
+ setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
+ setVisible(true);
+ }
+
+ public void destroy()
+ {
+
+ }
+
+ /** All implementation of GShellListener must include this method so the listener can be informed of messages from the GShell.
+ * @param event A GShellEvent that contains, amoung other things, the message.
+ */
+ public synchronized void message(GShellEvent event) {
+ String message = event.getMessage();
+ if (message.startsWith("explode_metadata_database.pl>")) {
+ message = message.substring(29);
+ error_message.append(message);
+ error_message.append("\n");
+ }
+ }
+
+ /** All implementation of GShellListener must include this method so the listener can be informed when a GShell begins its task. Implementation side-effect, not actually used.
+ * @param event A GShellEvent that contains details of the initial state of the GShell before task comencement.
+ */
+ public synchronized void processBegun(GShellEvent event) {
+ // We don't care.
+ }
+
+ /** All implementation of GShellListener must include this method so the listener can be informed when a GShell completes its task.
+ * @param event A GShellEvent that contains details of the final state of the GShell after task completion.
+ */
+ public synchronized void processComplete(GShellEvent event) {
+ successful = false;
+ if(event.getStatus() == GShell.OK) {
+ successful = true;
+ }
+ }
+
+ private Argument createPluginArgument(ArrayList plugin_list) {
+ Argument arg = new Argument();
+ arg.setName("plugin");
+ arg.setDescription(Dictionary.get("ExplodeMetadataPrompt.Plugin"));
+ arg.setRequired(true);
+ arg.setType(Argument.ENUM);
+ for (int i=0; i
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import javax.swing.*;
+import javax.swing.event.*;
+
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.collection.BasicCollectionConfiguration;
+import org.greenstone.gatherer.greenstone.LocalGreenstone;
+import org.greenstone.gatherer.shell.GShell;
+import org.greenstone.gatherer.shell.GShellEvent;
+import org.greenstone.gatherer.shell.GShellListener;
+import org.greenstone.gatherer.shell.GDefaultProgressMonitor;
+import org.greenstone.gatherer.util.ArrayTools;
+import org.greenstone.gatherer.util.CheckList;
+import org.greenstone.gatherer.util.CheckListEntry;
+import org.greenstone.gatherer.util.StaticStrings;
+import org.greenstone.gatherer.util.Utility;
+
+/** This class provides the functionality to export a set of current
+ * collections from the GSDLHOME/collect/ directory to various formats
+ * (hence ExportAs) using export.pl. The user chooses the collection from a
+ * list, where each entry also displays details about itself, confirms the
+ * delete of a collection by checking a checkbox then presses the ok button
+ * to actually delete the collection.
+ * Copied from WriteCDImagePrompt
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.3
+ */
+public class ExportAsPrompt
+ extends ModalDialog
+ implements GShellListener {
+
+ static final private Dimension LABEL_SIZE = new Dimension(120, 25);
+
+ private OKButtonListener ok_button_listener;
+
+ private JLabel saveas_label = null;
+ private JComboBox saveas_combobox = null;
+
+ private ArrayList all_collections = null;
+ private BasicCollectionConfiguration selected_collection = null;
+ /** The list of collections to export */
+ private JList list = null;
+ /** The currently selected collection for deletion. */
+ private BasicCollectionConfiguration collection = null;
+ /** A reference to ourself so any inner-classes can dispose of us. */
+ private ExportAsPrompt prompt = null;
+ /** The close button, which exits the prompt without deleting anything. */
+ private JButton cancel_button = null;
+ /** The ok button which causes the selected collection to be deleted. */
+ private JButton ok_button = null;
+ /** The label above details. */
+ private JLabel details_label = null;
+ /** The label above the list. */
+ private JLabel list_label = null;
+ /** The text area used to display details about the collection selected. */
+ private JTextArea details_textarea = null;
+ /** The text area used to display instructions for the cd-rom/dvd export */
+ private JTextArea instructions_textarea;
+ /** A string array used to pass arguments to the phrase retrieval method. */
+ private JTextField title_field = null;
+ private JLabel title_label = null;
+ private String args[] = null;
+ private String cd_title = null;
+ /** whether the exporting was successful or not */
+ private boolean successful = false;
+ /** whether we are trying to export or not */
+ private boolean exporting = false;
+ /** the error message if any */
+ private StringBuffer error_message = null;
+ /** The size of the export prompt screen. */
+ public static final Dimension SIZE = new Dimension(500, 540);
+ private GDefaultProgressMonitor progress_monitor;
+
+ private JButton convert_xml_button1 = null;
+ private JButton convert_xml_button2 = null;
+ private JButton convert_xml_button3 = null;
+ private JButton folder_button = null;
+
+ private JPanel instructions_pane = null;
+
+ private JPanel convert_xml_pane1 = null;
+ private JPanel convert_xml_pane2 = null;
+ private JPanel mapping_xml_pane = null;
+
+ private JCheckBox convert_xml_checkbox1 = null;
+ private JCheckBox convert_xml_checkbox2 = null;
+ private JCheckBox mapping_xml_checkbox = null;
+
+ private JCheckBox output_single_checkbox = null;
+
+ private JTextField convert_xml_field1 = null;
+ private JTextField convert_xml_field2 = null;
+ private JTextField mapping_xml_field = null;
+
+
+ private File xsl_file1 = null;
+ private File xsl_file2 = null;
+ private File mapping_file = null;
+
+ private JPanel convert_xml_pane = null;
+
+ private HashMap plugoutMap = new HashMap();
+
+
+ /** Constructor.
+ * @see org.greenstone.gatherer.collection.ExportAsPrompt.CancelButtonListener
+ * @see org.greenstone.gatherer.collection.ExportAsPrompt.CollectionListListener
+ * @see org.greenstone.gatherer.collection.ExportAsPrompt.OKButtonListener
+ */
+ public ExportAsPrompt() {
+ super(Gatherer.g_man, true);
+ this.setComponentOrientation(Dictionary.getOrientation());
+ plugoutMap.clear();
+ plugoutMap.put("DSpace","dublin-core.xml");
+ plugoutMap.put("MARCXML","doc.xml");
+ plugoutMap.put("GreenstoneMETS","doctxt.xml,docmets.xml");
+ plugoutMap.put("FedoraMETS","doctxt.xml,docmets.xml");
+
+ // this is the order we want them to appear in the list
+ String [] saveas_formats = {"GreenstoneMETS", "FedoraMETS", "MARCXML", "DSpace"};
+ cancel_button = new GLIButton(Dictionary.get("General.Close"), Dictionary.get("General.Close_Tooltip"));
+
+ details_textarea = new JTextArea(Dictionary.get("DeleteCollectionPrompt.No_Collection"));
+ details_textarea.setEditable(false);
+ details_textarea.setComponentOrientation(Dictionary.getOrientation());
+
+ details_label = new JLabel(Dictionary.get("DeleteCollectionPrompt.Collection_Details"));
+ details_label.setComponentOrientation(Dictionary.getOrientation());
+
+ instructions_textarea = new JTextArea(Dictionary.get("ExportAsPrompt.Instructions"));
+ instructions_textarea.setCaretPosition(0);
+ instructions_textarea.setEditable(false);
+ instructions_textarea.setLineWrap(true);
+ instructions_textarea.setRows(4);
+ instructions_textarea.setWrapStyleWord(true);
+ instructions_textarea.setComponentOrientation(Dictionary.getOrientation());
+
+
+ saveas_label = new JLabel(Dictionary.get("ExportAsPrompt.SaveAs"));
+ saveas_label.setComponentOrientation(Dictionary.getOrientation());
+ //saveas_label.setPreferredSize(LABEL_SIZE);
+
+ saveas_combobox = new JComboBox(saveas_formats);
+ saveas_combobox.setOpaque(false);
+ saveas_combobox.setToolTipText(Dictionary.get("ExportAsPrompt.SaveAs_Tooltip"));
+ saveas_combobox.setComponentOrientation(Dictionary.getOrientation());
+
+ // Add xml conversion feature
+ convert_xml_button1 = new GLIButton(Dictionary.get("ExportAsPrompt.Browse"),Dictionary.get("ExportAsPrompt.Browse_Tooltip"));
+ convert_xml_button1.setEnabled(false);
+
+ convert_xml_button2 = new GLIButton(Dictionary.get("ExportAsPrompt.Browse"),Dictionary.get("ExportAsPrompt.Browse_Tooltip"));
+ convert_xml_button2.setEnabled(false);
+
+ convert_xml_button3 = new GLIButton(Dictionary.get("ExportAsPrompt.Browse"),Dictionary.get("ExportAsPrompt.Browse_Tooltip"));
+ convert_xml_button3.setEnabled(false);
+
+ convert_xml_checkbox1 = new JCheckBox();
+ convert_xml_checkbox1.setText(Dictionary.get("ExportAsPrompt.ApplyXSL","doc.xml"));
+ convert_xml_checkbox1.setToolTipText(Dictionary.get("ExportAsPrompt.ApplyXSL_Tooltip"));
+ convert_xml_checkbox1.setComponentOrientation(Dictionary.getOrientation());
+
+ convert_xml_checkbox2 = new JCheckBox();
+ convert_xml_checkbox2.setToolTipText(Dictionary.get("ExportAsPrompt.ApplyXSL_Tooltip"));
+ convert_xml_checkbox2.setComponentOrientation(Dictionary.getOrientation());
+
+ output_single_checkbox = new JCheckBox();
+ output_single_checkbox.setComponentOrientation(Dictionary.getOrientation());
+ output_single_checkbox.setText(Dictionary.get("ExportAsPrompt.MARCXMLGroup"));
+ output_single_checkbox.setToolTipText(Dictionary.get("ExportAsPrompt.MARCXMLGroup_Tooltip"));
+
+
+ mapping_xml_checkbox = new JCheckBox();
+ mapping_xml_checkbox.setComponentOrientation(Dictionary.getOrientation());
+ mapping_xml_checkbox.setText(Dictionary.get("ExportAsPrompt.MappingXML"));
+ mapping_xml_checkbox.setToolTipText(Dictionary.get("ExportAsPrompt.MappingXML_Tooltip"));
+
+ convert_xml_field1 = new JTextField();
+ convert_xml_field1.setComponentOrientation(Dictionary.getOrientation());
+ convert_xml_field1.setEnabled(false);
+
+ convert_xml_field2 = new JTextField();
+ convert_xml_field2.setComponentOrientation(Dictionary.getOrientation());
+ convert_xml_field2.setEnabled(false);
+
+ mapping_xml_field = new JTextField();
+ mapping_xml_field.setComponentOrientation(Dictionary.getOrientation());
+ mapping_xml_field.setEnabled(false);
+
+ convert_xml_pane1 = new JPanel(new BorderLayout());
+ convert_xml_pane1.setComponentOrientation(Dictionary.getOrientation());
+
+ convert_xml_pane2 = new JPanel(new BorderLayout());
+ convert_xml_pane2.setComponentOrientation(Dictionary.getOrientation());
+
+ mapping_xml_pane = new JPanel(new BorderLayout());
+ mapping_xml_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ convert_xml_pane = new JPanel(new GridLayout(3,1));
+ convert_xml_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ all_collections = new ArrayList();
+ //list = new CheckList(true);
+ list = new JList(getCollectionListModel());
+ list.setComponentOrientation(Dictionary.getOrientation());
+ list_label = new JLabel(Dictionary.get("DeleteCollectionPrompt.Collection_List"));
+ list_label.setComponentOrientation(Dictionary.getOrientation());
+
+ ok_button = new GLIButton(Dictionary.get("ExportAsPrompt.Export"), Dictionary.get("ExportAsPrompt.Export_Tooltip"));
+
+ title_field = new JTextField();
+ title_field.setComponentOrientation(Dictionary.getOrientation());
+ title_field.setToolTipText(Dictionary.get("ExportAsPrompt.Export_Name_Tooltip"));
+ title_label = new JLabel(Dictionary.get("ExportAsPrompt.Export_Name"));
+ title_label.setComponentOrientation(Dictionary.getOrientation());
+
+ folder_button = new GLIButton(Dictionary.get("ExportAsPrompt.Browse"),Dictionary.get("ExportAsPrompt.Browse_Tooltip"));
+ folder_button.addActionListener(new FolderButtonListener());
+
+
+ prompt = this;
+ setSize(SIZE);
+ setTitle(Dictionary.get("ExportAsPrompt.Title"));
+
+ setJMenuBar(new SimpleMenuBar("exporting"));
+ cancel_button.addActionListener(new CancelButtonListener());
+ list.addListSelectionListener(new CollectionListListener());
+ list.clearSelection();
+ list.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ ok_button_listener = new OKButtonListener();
+ ok_button.addActionListener(ok_button_listener);
+ ok_button.setEnabled(false);
+ //title.getDocument().addDocumentListener(new DocumentListener());
+
+ convert_xml_button1.addActionListener(new ConvertXMLButtonListener());
+ convert_xml_checkbox1.addActionListener(new ConvertXMLCheckboxListener());
+ mapping_xml_checkbox.addActionListener(new ConvertXMLCheckboxListener());
+ convert_xml_button3.addActionListener(new ConvertXMLButtonListener());
+
+ convert_xml_button2.addActionListener(new ConvertXMLButtonListener());
+ convert_xml_checkbox2.addActionListener(new ConvertXMLCheckboxListener());
+
+ saveas_combobox.addActionListener(new SaveasListener());
+
+ }
+
+ /** Destructor. */
+ public void destroy() {
+ saveas_label = null;
+ saveas_combobox = null;
+ all_collections.clear();
+ all_collections = null;
+ cancel_button = null;
+ details_textarea = null;
+ details_label = null;
+ list = null;
+ ok_button = null;
+ prompt = null;
+ selected_collection = null;
+ title_field = null;
+ title_label = null;
+ }
+
+ /** This method causes the modal prompt to be displayed.
+ * returns true if it has exported the collections that are currently selected */
+ public boolean display() {
+ JScrollPane scrol_tmp;
+ // Top pane
+ instructions_pane = new JPanel(new BorderLayout());
+ instructions_pane.setComponentOrientation(Dictionary.getOrientation());
+ instructions_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,5));
+ scrol_tmp = new JScrollPane(instructions_textarea);
+ scrol_tmp.setComponentOrientation(Dictionary.getOrientation());
+ instructions_pane.add(scrol_tmp, BorderLayout.CENTER);
+
+ title_label.setBorder(BorderFactory.createEmptyBorder(0,5,0,15));
+
+ JPanel title_pane = new JPanel(new BorderLayout());
+ title_pane.setComponentOrientation(Dictionary.getOrientation());
+ title_pane.add(title_label, BorderLayout.LINE_START);
+ title_pane.add(title_field, BorderLayout.CENTER);
+ title_pane.add(folder_button, BorderLayout.LINE_END);
+ //apply xsl pane
+
+ convert_xml_pane1.removeAll();
+ convert_xml_pane2.removeAll();
+ mapping_xml_pane.removeAll();
+ convert_xml_pane.removeAll();
+
+ convert_xml_pane1.add(convert_xml_checkbox1, BorderLayout.LINE_START);
+ convert_xml_pane1.add(convert_xml_field1, BorderLayout.CENTER);
+ convert_xml_pane1.add(convert_xml_button1, BorderLayout.LINE_END);
+
+ convert_xml_pane2.add(convert_xml_checkbox2, BorderLayout.LINE_START);
+ convert_xml_pane2.add(convert_xml_field2, BorderLayout.CENTER);
+ convert_xml_pane2.add(convert_xml_button2, BorderLayout.LINE_END);
+
+ mapping_xml_pane.add(mapping_xml_checkbox, BorderLayout.LINE_START);
+ mapping_xml_pane.add(mapping_xml_field, BorderLayout.CENTER);
+ mapping_xml_pane.add(convert_xml_button3, BorderLayout.LINE_END);
+
+ convert_xml_pane.add(convert_xml_pane1);
+
+ String saveas = (String)saveas_combobox.getSelectedItem();
+ // force the updating of which fields we are displaying
+ saveas_combobox.setSelectedIndex(0);
+
+ // Save as pane
+ JPanel saveas_pane = new JPanel(new BorderLayout());
+ saveas_pane.setComponentOrientation(Dictionary.getOrientation());
+ saveas_label.setBorder(BorderFactory.createEmptyBorder(0,5,0,15));
+ saveas_pane.add(saveas_label, BorderLayout.LINE_START);
+ saveas_pane.add(saveas_combobox, BorderLayout.CENTER);
+
+ JPanel tmp_pane = new JPanel(new BorderLayout());
+ tmp_pane.setComponentOrientation(Dictionary.getOrientation());
+ tmp_pane.add(saveas_pane, BorderLayout.NORTH);
+ tmp_pane.add(title_pane, BorderLayout.CENTER);
+
+ instructions_pane.add(tmp_pane, BorderLayout.NORTH);
+
+ instructions_pane.add(convert_xml_pane, BorderLayout.CENTER);
+
+ // Central pane
+ JPanel list_pane = new JPanel(new BorderLayout());
+ list_pane.setComponentOrientation(Dictionary.getOrientation());
+ list_pane.add(list_label, BorderLayout.NORTH);
+ scrol_tmp = new JScrollPane(list);
+ scrol_tmp.setComponentOrientation(Dictionary.getOrientation());
+ list_pane.add(scrol_tmp, BorderLayout.CENTER);
+ list_pane.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0));
+
+ JPanel details_pane = new JPanel(new BorderLayout());
+ details_pane.setComponentOrientation(Dictionary.getOrientation());
+ details_pane.add(details_label, BorderLayout.NORTH);
+ scrol_tmp = new JScrollPane(details_textarea);
+ scrol_tmp.setComponentOrientation(Dictionary.getOrientation());
+ details_pane.add(scrol_tmp, BorderLayout.CENTER);
+ details_pane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
+
+ JPanel central_pane = new JPanel(new GridLayout(2, 1));
+ central_pane.setComponentOrientation(Dictionary.getOrientation());
+ central_pane.add(list_pane);
+ central_pane.add(details_pane);
+ central_pane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+
+ // Lower pane
+ JPanel button_pane = new JPanel(new GridLayout(1, 2));
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+ button_pane.add(ok_button);
+ button_pane.add(cancel_button);
+ button_pane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
+
+ JPanel lower_pane = new JPanel(new BorderLayout());
+ lower_pane.setComponentOrientation(Dictionary.getOrientation());
+ lower_pane.add(button_pane, BorderLayout.SOUTH);
+ lower_pane.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5));
+
+ // Final.
+ JPanel content_pane = (JPanel)this.getContentPane();
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+ content_pane.setLayout(new BorderLayout());
+ content_pane.add(instructions_pane, BorderLayout.NORTH);
+ content_pane.add(central_pane, BorderLayout.CENTER);
+ content_pane.add(lower_pane, BorderLayout.SOUTH);
+
+ // Center and display.
+ Dimension screen_size = Configuration.screen_size;
+ this.setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
+ this.setVisible(true); // blocks until the dialog is killed
+ return true;
+
+ }
+
+
+ /** This method calls the builcol.pl scripts via a GShell so as to not lock up the processor.
+ * @see org.greenstone.gatherer.Configuration
+ * @see org.greenstone.gatherer.Gatherer
+ * @see org.greenstone.gatherer.collection.Collection
+ * @see org.greenstone.gatherer.gui.BuildOptions
+ * @see org.greenstone.gatherer.shell.GShell
+ * @see org.greenstone.gatherer.shell.GShellListener
+ * @see org.greenstone.gatherer.shell.GShellProgressMonitor
+ * @see org.greenstone.gatherer.util.Utility
+ */
+ public void exportAsCollections() {
+ DebugStream.println("ExportAsPrompt.exportAsCollections()");
+
+ if (selected_collection == null) return;
+
+ cd_title = title_field.getText();
+
+ String export_type = (String) saveas_combobox.getSelectedItem();
+
+ // Generate the export.pl command
+ ArrayList command_parts_list = new ArrayList();
+ if (!Gatherer.isGsdlRemote) {
+ command_parts_list.add(Configuration.perl_path);
+ command_parts_list.add("-S");
+ }
+ command_parts_list.add(LocalGreenstone.getBinScriptDirectoryPath() + "export.pl");
+ command_parts_list.add("-gli");
+ command_parts_list.add("-language");
+ command_parts_list.add(Configuration.getLanguage());
+ command_parts_list.add("-removeold");
+ command_parts_list.add("-saveas");
+ command_parts_list.add(export_type);
+ // we want to be able to export items from other collect directories too
+ String collectDir = Gatherer.getCollectDirectoryPath();
+
+ if(Gatherer.GS3) {
+ command_parts_list.add("-site");
+ command_parts_list.add(Configuration.site_name);
+ }
+
+ if(collectDir != Gatherer.getDefaultGSCollectDirectoryPath(true)) {
+ command_parts_list.add("-collectdir");
+ command_parts_list.add(collectDir);
+ }
+ command_parts_list.add("-exportdir");
+ String export_dir = LocalGreenstone.getTmpDirectoryPath();
+ if (cd_title.equals("")) {
+ export_dir += "exported_" + selected_collection.getShortName()+ "_" +export_type;
+ }
+ else {
+ File cd_file = new File(cd_title);
+ if (cd_file.isAbsolute())
+ export_dir = cd_title + File.separator + "exported_" + selected_collection.getShortName()+ "_"+ export_type;
+ else{
+ cd_title = cd_title.replaceAll("\\s+","");
+ cd_title = cd_title.replaceAll("\\\\+","/");
+ cd_title = cd_title.replaceAll("/+","/");
+ export_dir +=cd_title;
+ }
+ }
+
+ command_parts_list.add(export_dir);
+
+ // check the xslt/mapping file values.
+
+ // now we build up the saveas options
+ StringBuffer saveas_options = new StringBuffer();
+
+ if (export_type.equals("MARCXML")){
+ if (!convert_xml_field1.getText().equals("")) {
+ saveas_options.append("-xslt_file ");
+ saveas_options.append(convert_xml_field1.getText()+" ");
+ }
+ if (!mapping_xml_field.getText().equals("")) {
+ saveas_options.append("-mapping_file ");
+ saveas_options.append(mapping_xml_field.getText()+" ");
+ }
+ if (output_single_checkbox.isSelected()){
+ saveas_options.append("-group ");
+ }
+ }
+ else if (export_type.endsWith("METS")) {
+ if (!convert_xml_field1.getText().equals("")) {
+ saveas_options.append("-xslt_txt ");
+ saveas_options.append(convert_xml_field1.getText()+" ");
+ }
+ if (!convert_xml_field2.getText().equals("")) {
+ saveas_options.append("-xslt_mets ");
+ saveas_options.append(convert_xml_field2.getText()+" ");
+ }
+ }
+ else if (export_type.equals("DSpace")) {
+ if (!convert_xml_field1.getText().equals("")) {
+ saveas_options.append("-xslt_file ");
+ saveas_options.append(convert_xml_field1.getText()+" ");
+ }
+ }
+
+ if (saveas_options.length()>0) {
+ command_parts_list.add("-saveas_options");
+ command_parts_list.add(saveas_options.toString());
+ }
+
+ command_parts_list.add( selected_collection.getShortName());
+
+
+ DebugStream.print("export command = ");
+ for (int i = 0; i < command_parts_list.size(); i++) {
+ DebugStream.print(command_parts_list.get(i) + " ");
+ //System.err.print("'"+command_parts_list.get(i)+"'" + " ");
+ }
+ DebugStream.println("");
+
+ // Run the export.pl command
+ String[] command_parts = (String[]) command_parts_list.toArray(new String[0]);
+
+ progress_monitor = new GDefaultProgressMonitor();
+
+ GShell process = new GShell(command_parts, GShell.EXPORTAS, 3, this,progress_monitor , GShell.GSHELL_EXPORTAS);
+ process.start();
+ //process.run();
+ DebugStream.println("ExportAsPrompt.exportAsCollections().return");
+
+ }
+
+
+ public void cancelExporting(){
+ progress_monitor.setStop(true);
+ }
+
+
+ /** Shows an export complete prompt.
+ * @param success A boolean indicating if the collection was successfully deleted.
+ * @see org.greenstone.gatherer.collection.Collection
+ */
+ public void resultPrompt(boolean success, String extra) {
+ args = new String[2];
+
+ // coll name
+ args[0] = selected_collection.getName() + StaticStrings.SPACE_CHARACTER + StaticStrings.OPEN_PARENTHESIS_CHARACTER + selected_collection.getShortName() + StaticStrings.CLOSE_PARENTHESIS_CHARACTER;
+
+ String export_type = (String) saveas_combobox.getSelectedItem();
+ args[1] = LocalGreenstone.getTmpDirectoryPath();
+ if (cd_title.equals("")) {
+ args[1] += "exported_" + selected_collection.getShortName()+"_"+export_type;
+ }
+ else {
+ File cd_file = new File(cd_title);
+ if (cd_file.isAbsolute())
+ args[1] = cd_title + File.separator + "exported_" + selected_collection.getShortName()+"_"+export_type;
+ else{
+ cd_title = cd_title.replaceAll("\\s+","");
+ cd_title = cd_title.replaceAll("\\\\+","/");
+ cd_title = cd_title.replaceAll("/+","/");
+ args[1] +=cd_title;
+ }
+ }
+
+
+ String title;
+ String label;
+ String details;
+
+ if (success) {
+ String successMessage = "ExportAsPrompt.Successful_ExportOne";
+ title = Dictionary.get("ExportAsPrompt.Successful_Title");
+ label = Dictionary.get(successMessage, args);
+ details = Dictionary.get("ExportAsPrompt.Successful_Details", args);
+ } else {
+ String failedMessage = "ExportAsPrompt.Failed_ExportOne";
+ title = Dictionary.get("ExportAsPrompt.Failed_Title");
+ label = Dictionary.get(failedMessage, args);
+ details = Dictionary.get("ExportAsPrompt.Failed_Details", args);
+ }
+ SimpleResultDialog result_dialog = new SimpleResultDialog(this, title, label, details);
+ result_dialog.setVisible(true); // Blocks
+ result_dialog.dispose();
+ result_dialog = null;
+ }
+
+ /** Method to scan the collect directory retrieving and reloading each collection it finds, while building the list of known collections.
+ * @see org.greenstone.gatherer.Configuration
+ * @see org.greenstone.gatherer.Gatherer
+ * @see org.greenstone.gatherer.util.ArrayTools
+ * @see org.greenstone.gatherer.util.Utility
+ */
+ private ListModel getCollectionListModel() {
+ DefaultListModel model = new DefaultListModel();
+ // Start at the collect dir.
+ File collect_directory = new File(Gatherer.getCollectDirectoryPath());
+ String file_name = (Gatherer.GS3)? Utility.CONFIG_GS3_FILE : Utility.CONFIG_FILE;
+ if (collect_directory.exists()) {
+ // Now for each child directory see if it contains a .col file and
+ // if so try to load it..
+ File collections[] = collect_directory.listFiles();
+ ArrayTools.sort(collections);
+ for(int i = 0; collections != null && i < collections.length; i++) {
+ if(collections[i].isDirectory() && !collections[i].getName().equals(StaticStrings.MODEL_COLLECTION_NAME)) {
+ File config_file = new File(collections[i], file_name);
+ if (config_file.exists()) {
+ BasicCollectionConfiguration config = new BasicCollectionConfiguration(config_file);
+ model.addElement(config);
+ config = null;
+ }
+ }
+ }
+ }
+ return model;
+ // Otherwise the collect directory doesn't actually exist, so there ain't much we can do.
+ }
+
+
+ /** All implementation of GShellListener must include this method so the listener can be informed of messages from the GShell.
+ * @param event A GShellEvent that contains, amoung other things, the message.
+ */
+ public synchronized void message(GShellEvent event) {
+ // Ignore the messages from RecPlug with 'show_progress' set (used for progress bars)
+ String message = event.getMessage();
+ if (message.startsWith("export.pl>")) {
+ message = message.substring(13);
+ //DebugStream.println("message = "+event.getMessage());
+ error_message.append(message);
+ error_message.append("\n");
+ }
+ }
+
+ /** All implementation of GShellListener must include this method so the listener can be informed when a GShell begins its task. Implementation side-effect, not actually used.
+ * @param event A GShellEvent that contains details of the initial state of the GShell before task comencement.
+ */
+ public synchronized void processBegun(GShellEvent event) {
+ // We don't care.
+ }
+ /** All implementation of GShellListener must include this method so the listener can be informed when a GShell completes its task.
+ * @param event A GShellEvent that contains details of the final state of the GShell after task completion.
+ */
+ public synchronized void processComplete(GShellEvent event) {
+ successful = false;
+ if(event.getStatus() == GShell.OK) {
+ if(event.getType() == GShell.EXPORTAS) {
+ successful = true;
+ }
+ }
+ ok_button_listener.processComplete();
+ }
+
+ /** A button listener implementation, which listens for actions on the close button and disposes of the dialog when detected. */
+ private class CancelButtonListener
+ implements ActionListener {
+ /** Any implementation of ActionListener must include this method so we can be informed when the button is actioned.
+ * @param event An ActionEvent containing all the relevant information garnered from the event itself.
+ */
+ public void actionPerformed(ActionEvent event) {
+ prompt.dispose();
+ }
+ }
+
+ /** This private class listens for selection events in from the list and then displays the appropriate details for that collection.
+ */
+ private class CollectionListListener
+ implements ListSelectionListener
+ {
+ /** Any implementation of ListSelectionListener must include this method so we can be informed when the list selection changes.
+ * @param event a ListSelectionEvent containing all the relevant information garnered from the event itself
+ */
+ public void valueChanged(ListSelectionEvent event)
+ {
+ // Can only export when something is ticked
+ //ok_button.setEnabled(!list.isNothingTicked());
+
+ if (list.isSelectionEmpty()) {
+ details_textarea.setText(Dictionary.get("DeleteCollectionPrompt.No_Collection"));
+ ok_button.setEnabled(false);
+ }
+ else {
+ BasicCollectionConfiguration collection = (BasicCollectionConfiguration) list.getSelectedValue();
+ args = new String[3];
+ args[0] = collection.getCreator();
+ args[1] = collection.getMaintainer();
+ args[2] = collection.getDescription();
+ details_textarea.setText(Dictionary.get("DeleteCollectionPrompt.Details", args));
+ details_textarea.setCaretPosition(0);
+ ok_button.setEnabled(true);
+ }
+ }
+
+ }
+ /** The OK button listener implementation. */
+ private class OKButtonListener
+ implements ActionListener {
+ private Component glass_pane;
+ private MouseListener mouse_blocker_listener;
+ private ProgressDialog progress_dialog;
+
+ /** Any implementation of ActionListener must include this method so we can be informed when the button is actioned.
+ * @param event An ActionEvent containing all the relevant information garnered from the event itself.
+ * @see org.greenstone.gatherer.Configuration
+ * @see org.greenstone.gatherer.Gatherer
+ * @see org.greenstone.gatherer.util.Utility
+ */
+ public void actionPerformed(ActionEvent event) {
+ ///ystem.err.println("OK Clicked");
+ // Make sure there are some colls specified
+ selected_collection = (BasicCollectionConfiguration)list.getSelectedValue();
+ error_message = new StringBuffer();
+
+ // Set the cursor to hourglass
+ glass_pane = getGlassPane();
+ mouse_blocker_listener = new MouseAdapter() {};
+ glass_pane.addMouseListener(mouse_blocker_listener);
+ glass_pane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+ glass_pane.setVisible(true);
+
+ // Export the selected collection.
+ ///ystem.err.println("Exporting As for named collections");
+ exportAsCollections();
+
+ // Show progress dialog
+ ///ystem.err.println("Showing progress dialog");
+ progress_dialog = new ProgressDialog();
+ progress_dialog.setVisible(true);
+ }
+
+ public void processComplete() {
+ ///ystem.err.println("Process complete");
+ // Dispose of progress dialog
+
+
+ progress_dialog.setVisible(false);
+ progress_dialog.dispose();
+ progress_dialog = null;
+
+ // unset the cursor
+ glass_pane.setVisible(false);
+ glass_pane.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+ glass_pane.removeMouseListener(mouse_blocker_listener);
+ glass_pane = null;
+ mouse_blocker_listener= null;
+
+ if (successful) {
+ resultPrompt(true, error_message.toString());
+ } else {
+ resultPrompt(false, error_message.toString());
+ }
+ error_message = null;
+
+ convert_xml_button1.setEnabled(false);
+ xsl_file1 = null;
+ convert_xml_field1.setText("");
+ convert_xml_checkbox1.setSelected(false);
+
+ convert_xml_button2.setEnabled(false);
+ xsl_file2 = null;
+ convert_xml_field2.setText("");
+ convert_xml_checkbox2.setSelected(false);
+
+ mapping_xml_checkbox.setSelected(false);
+ output_single_checkbox.setSelected(false);
+ convert_xml_button3.setEnabled(false);
+ mapping_xml_field.setText("");
+ mapping_file = null;
+
+ }
+
+ private class ProgressDialog
+ extends ModalDialog {
+
+ private Dimension size = new Dimension(400,110);
+
+ public ProgressDialog() {
+ super(Gatherer.g_man, Dictionary.get("ExportAsPrompt.Title"), true);
+ setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
+ setSize(size);
+ JPanel content_pane = (JPanel) getContentPane();
+ JLabel progress_label = new JLabel(Dictionary.get("ExportAsPrompt.Progress_Label"));
+
+ JProgressBar progress_bar = new JProgressBar();
+ progress_bar.setIndeterminate(true);
+ content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ GLIButton cancel_button = new GLIButton(Dictionary.get("ExportAsPrompt.Cancel"),Dictionary.get("ExportAsPrompt.Cancel_Tooltip"));
+ cancel_button.setMnemonic(KeyEvent.VK_C);
+
+ cancel_button.addActionListener(new ActionListener(){
+ public void actionPerformed(ActionEvent e){
+ cancelExporting();
+ }
+
+ });
+
+ JPanel cancel_panel = new JPanel(new FlowLayout(FlowLayout.CENTER,0,0));
+ cancel_panel.add(cancel_button);
+
+ content_pane.setLayout(new BorderLayout(0,5));
+ content_pane.add(progress_label, BorderLayout.NORTH);
+ content_pane.add(progress_bar, BorderLayout.CENTER);
+ content_pane.add(cancel_panel, BorderLayout.SOUTH);
+
+ // Position
+ Rectangle frame_bounds = Gatherer.g_man.getBounds();
+ setLocation(frame_bounds.x + (frame_bounds.width - size.width) / 2, frame_bounds.y + (frame_bounds.height - size.height) / 2);
+ }
+ }
+ }
+
+ private class ConvertXMLCheckboxListener implements ActionListener {
+ public void actionPerformed(ActionEvent event) {
+
+ convert_xml_button1.setEnabled(convert_xml_checkbox1.isSelected());
+ convert_xml_field1.setEnabled(convert_xml_checkbox1.isSelected());
+ convert_xml_button2.setEnabled(convert_xml_checkbox2.isSelected());
+ convert_xml_field2.setEnabled(convert_xml_checkbox2.isSelected());
+ convert_xml_button3.setEnabled(mapping_xml_checkbox.isSelected());
+ mapping_xml_field.setEnabled(mapping_xml_checkbox.isSelected());
+
+ }
+ }
+
+
+
+ private class SaveasListener implements ActionListener {
+
+ public void actionPerformed(ActionEvent event) {
+
+ convert_xml_checkbox1.setSelected(false);
+ convert_xml_checkbox2.setSelected(false);
+ mapping_xml_checkbox.setSelected(false);
+ output_single_checkbox.setSelected(false);
+
+ convert_xml_button1.setEnabled(false);
+ convert_xml_button2.setEnabled(false);
+ convert_xml_button3.setEnabled(false);
+
+ convert_xml_field1.setText("");
+ convert_xml_field2.setText("");
+ mapping_xml_field.setText("");
+
+ String saveas = (String)saveas_combobox.getSelectedItem();
+
+ if (convert_xml_pane.getComponentCount() > 1){
+ convert_xml_pane.remove(1);
+ if (convert_xml_pane.getComponentCount() > 1){
+ convert_xml_pane.remove(1);
+ }
+ }
+
+ if (!saveas.endsWith("METS")){
+
+ convert_xml_checkbox1.setText(Dictionary.get("ExportAsPrompt.ApplyXSL",(String)plugoutMap.get(saveas)));
+ if (saveas.equals("MARCXML")){
+ convert_xml_pane.add(mapping_xml_pane);
+ convert_xml_pane.add(output_single_checkbox);
+ }
+ }
+ else{
+ String[] docs = ((String)plugoutMap.get(saveas)).split(",");
+ convert_xml_checkbox1.setText(Dictionary.get("ExportAsPrompt.ApplyXSL",docs[0]));
+ convert_xml_checkbox2.setText(Dictionary.get("ExportAsPrompt.ApplyXSL",docs[1]));
+ convert_xml_pane.add(convert_xml_pane2);
+ }
+
+ convert_xml_pane.revalidate();
+ convert_xml_pane.repaint();
+ instructions_pane.revalidate();
+ instructions_pane.repaint();
+
+ }
+
+ }
+
+ private class ConvertXMLButtonListener implements ActionListener {
+ public void actionPerformed(ActionEvent event) {
+ JFileChooser chooser = new JFileChooser();
+ // Note: source for ExampleFileFilter can be found in FileChooserDemo,
+ // under the demo/jfc directory in the Java 2 SDK, Standard Edition.
+ javax.swing.filechooser.FileFilter filter = new javax.swing.filechooser.FileFilter(){
+ public boolean accept(File f){
+ return f.getPath().endsWith(".xsl")||f.isDirectory()||f.getPath().endsWith(".xml");
+ }
+
+ public String getDescription(){
+ return "XSL or XML file";
+ }
+ };
+
+ chooser.setFileFilter(filter);
+ int returnVal = chooser.showOpenDialog(prompt);
+
+ if(returnVal == JFileChooser.APPROVE_OPTION) {
+ if (event.getSource() == convert_xml_button1){
+
+ xsl_file1 = chooser.getSelectedFile();
+ convert_xml_field1.setText(xsl_file1.getPath());
+ }
+ else if (event.getSource() == convert_xml_button2){
+ xsl_file2 = chooser.getSelectedFile();
+ convert_xml_field2.setText(xsl_file2.getPath());
+
+ }
+ else {
+ mapping_file = chooser.getSelectedFile();
+ mapping_xml_field.setText(mapping_file.getPath());
+ }
+ }
+ }
+ }
+
+ private class FolderButtonListener implements ActionListener {
+ public void actionPerformed(ActionEvent event) {
+ JFileChooser chooser = new JFileChooser();
+ chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+
+ int returnVal = chooser.showOpenDialog(prompt);
+
+ if(returnVal == JFileChooser.APPROVE_OPTION) {
+ File folder_name = chooser.getSelectedFile();
+ title_field.setText(folder_name.getPath());
+ }
+
+ }
+ }
+}
+
+
+
+
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/FedoraLogin.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/FedoraLogin.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/FedoraLogin.java (revision 31635)
@@ -0,0 +1,466 @@
+/*
+ * -----------------------------------------------------------------------------
+ *
+ * License and Copyright: The contents of this file are subject
+ * to the Educational Community License (the "License"); you may not use
+ * this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ * http://www.opensource.org/licenses/ecl1.txt.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific language governing rights and limitations under
+ * the License.
+ *
+ * The entire file consists of original code. Copyright ©
+ * 2002-2007 by The Rector and Visitors of the University of Virginia and
+ * Cornell University. All rights reserved.
+ *
+ * -----------------------------------------------------------------------------
+ */
+
+
+/* Based on Fedora's Client LoginDialog.java code */
+/* Modified to work with Greenstone: 22 Jan 2008 */
+
+package org.greenstone.gatherer.gui;
+
+import java.awt.BorderLayout;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+
+import javax.swing.AbstractAction;
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JPasswordField;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+
+import java.awt.Rectangle;
+import java.awt.Toolkit;
+import org.greenstone.gatherer.Dictionary;
+
+
+
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.Configuration;
+
+public class FedoraLogin extends JDialog
+{
+ private static final int MAX_ITEMS = 5;
+
+ private static final java.awt.Color LIGHT = new java.awt.Color(244, 244, 224);
+ private static final java.awt.Color TRANSPARENT = new java.awt.Color(0,0,0,0);
+
+ private JPanel m_infoPane;
+ private JComboBox m_serverComboBox;
+ private JComboBox m_protocolComboBox;
+ private JComboBox m_usernameComboBox;
+ private JPasswordField m_passwordField;
+
+ private String m_lastUsername="fedoraAdmin";
+ private String m_lastServer="localhost:8080";
+ private String m_lastProtocol="http";
+ private String m_lastPassword="";
+
+ private boolean login_requested = false;
+
+ public FedoraLogin(String title, boolean can_cancel)
+ {
+ super(Gatherer.g_man, "Login", true);
+ this.setComponentOrientation(Dictionary.getOrientation());
+ this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+
+ JLabel serverLabel=new JLabel("Fedora Server");
+ JLabel protocolLabel=new JLabel("Protocol");
+ JLabel usernameLabel=new JLabel("Username");
+ JLabel passwordLabel=new JLabel("Password");
+
+ serverLabel.setComponentOrientation(Dictionary.getOrientation());
+ protocolLabel.setComponentOrientation(Dictionary.getOrientation());
+ usernameLabel.setComponentOrientation(Dictionary.getOrientation());
+ passwordLabel.setComponentOrientation(Dictionary.getOrientation());
+
+ m_serverComboBox=new JComboBox(new String[]{m_lastServer});
+ m_serverComboBox.setComponentOrientation(Dictionary.getOrientation());
+ m_serverComboBox.setEditable(true);
+ m_protocolComboBox=new JComboBox(new String[]{m_lastProtocol, "https"}); // http and https
+ m_protocolComboBox.setComponentOrientation(Dictionary.getOrientation());
+ m_protocolComboBox.setEditable(true);
+ m_usernameComboBox=new JComboBox(new String[]{m_lastUsername});
+ m_usernameComboBox.setComponentOrientation(Dictionary.getOrientation());
+ m_usernameComboBox.setEditable(true);
+ m_passwordField=new JPasswordField();
+
+ setComboBoxValues();
+
+
+ LoginAction loginAction=new LoginAction(this);
+ JButton loginButton=new JButton(loginAction);
+
+ loginAction.setButton(loginButton);
+ loginButton.setEnabled(false);
+ this.getRootPane().setDefaultButton(loginButton);
+
+ m_passwordField.getDocument().addDocumentListener(
+ new PasswordChangeListener(loginButton, m_passwordField));
+ m_passwordField.setAction(loginAction);
+
+ JPanel inputPane=new JPanel();
+ inputPane.setComponentOrientation(Dictionary.getOrientation());
+ inputPane.setBorder(BorderFactory.createCompoundBorder(
+ BorderFactory.createCompoundBorder(
+ BorderFactory.createEmptyBorder(6, 6, 6, 6),
+ BorderFactory.createEtchedBorder()
+ ),
+ BorderFactory.createEmptyBorder(6,6,6,6)
+ ));
+ GridBagLayout gridBag=new GridBagLayout();
+ inputPane.setLayout(gridBag);
+ addLabelValueRows(new JLabel[] {serverLabel, protocolLabel, usernameLabel, passwordLabel},
+ new JComponent[] {m_serverComboBox, m_protocolComboBox, m_usernameComboBox, m_passwordField},
+ gridBag, inputPane);
+
+ // handling Closing and cancelling events
+ this.addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent e){
+ // this is important, if we want to stop looping on displaying the dialog:
+ login_requested = false;
+ //dispose(); // defaultCloseOperation of this dialog already set to dispose it
+ }
+ });
+
+ JButton cancelButton=new JButton(new AbstractAction() {
+ private static final long serialVersionUID = 1L;
+ public void actionPerformed(ActionEvent evt) {
+ // this is important, if we want to stop looping on displaying the dialog:
+ login_requested = false;
+ dispose();
+ }
+ });
+
+ cancelButton.setText("Exit"); // if haven't logged in yet
+
+ JPanel buttonPane=new JPanel();
+ buttonPane.setComponentOrientation(Dictionary.getOrientation());
+ buttonPane.add(loginButton);
+ buttonPane.add(cancelButton);
+ Container contentPane=getContentPane();
+ contentPane.setComponentOrientation(Dictionary.getOrientation());
+
+ contentPane.setLayout(new BorderLayout());
+ m_infoPane = new JPanel();
+ m_infoPane.setComponentOrientation(Dictionary.getOrientation());
+ m_infoPane.setBackground(TRANSPARENT);
+ contentPane.add(m_infoPane, BorderLayout.NORTH);
+ contentPane.add(inputPane, BorderLayout.CENTER);
+ contentPane.add(buttonPane, BorderLayout.SOUTH);
+ addWindowListener(new WindowAdapter() {
+ public void windowOpened(WindowEvent evt) {
+ m_passwordField.requestFocus();
+ }
+ });
+ pack();
+
+ // Position
+ Dimension size = getSize();
+ if (Gatherer.g_man != null) {
+ Rectangle frame_bounds = Gatherer.g_man.getBounds();
+ setLocation(frame_bounds.x + (frame_bounds.width - size.width) / 2, frame_bounds.y + (frame_bounds.height - size.height) / 2);
+ }
+ else {
+ Dimension screen_size = Toolkit.getDefaultToolkit().getScreenSize();
+ setLocation((screen_size.width - size.width) / 2, (screen_size.height - size.height) / 2);
+ }
+
+
+ setVisible(true);
+ }
+
+
+ public void saveProperties(JComboBox control, String affectedProperty, String editedValue) {
+ //String editedValue = (String)control.getSelectedItem();
+ // Edited value is the value in the combobox editfield, it has not yet been added to the combobox
+ control.insertItemAt(editedValue, 0);
+
+ if(editedValue == null) {
+ editedValue = (String)control.getItemAt(0); // else reuse the first default
+ }
+ Configuration.setString(affectedProperty, true, editedValue);
+
+ // Add the values in the combobox as well.
+ int value_count = 1;
+ for(int i = 0; value_count < MAX_ITEMS && i < control.getItemCount(); i++, value_count++) {
+ String value = (String)control.getItemAt(i);
+ if(value == null || value.equals(editedValue) || value.equals("")) {
+ // skip duplicates of just-edited comboBox value and empty values
+ value_count--;
+ } else {
+ Configuration.setString(affectedProperty+value_count, true, value);
+ } // else retain old value for this affectedProperty (e.g. general.gliserver_url)
+ }
+ }
+
+ private void setComboBoxValues() {
+ // All comboboxes are of the same size
+ Dimension newSize=new Dimension(m_serverComboBox.getPreferredSize().width+20, m_serverComboBox.getPreferredSize().height);
+ m_passwordField.setPreferredSize(newSize);
+
+ setComboBoxValues(m_serverComboBox, "fedora.server", newSize);
+ setComboBoxValues(m_protocolComboBox, "fedora.protocol", newSize);
+ setComboBoxValues(m_usernameComboBox, "fedora.username", newSize);
+ newSize = null;
+ }
+
+ private void setComboBoxValues(JComboBox control, String affectedProperty, Dimension newSize) {
+ // get values from Configuration file, if none, it will go back to using defaultvalues
+ // put them into the combobox
+
+ String value = Configuration.getString(affectedProperty, true);
+ boolean finished = value.equals("");
+ if(!finished) {
+ control.removeAllItems(); // remove defaults, since config file now contains init values
+ control.addItem(value);
+ } // else value and combobox already contains the defaults
+
+
+ for(int i = 1; !finished; i++) {
+ value = Configuration.getString(affectedProperty+i, true);
+ if(value.equals("")) {
+ finished = true;
+ }
+ else { // value is not empty
+ control.addItem(value);
+ }
+ }
+
+ // setting the size of the dropdown control
+ control.setPreferredSize(newSize);
+ }
+
+ public void addLabelValueRows(JLabel[] labels, JComponent[] values,
+ GridBagLayout gridBag, Container container) {
+ GridBagConstraints c=new GridBagConstraints();
+ c.insets=new Insets(0, 6, 6, 6);
+ for (int i=0; i
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.File;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.filechooser.*;
+import javax.swing.table.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.file.FileAssociationManager;
+import org.greenstone.gatherer.util.TableUtils;
+import org.greenstone.gatherer.util.Utility;
+
+/** The file association allows the entry of new file associations and the modification of existing ones.
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.3
+ */
+public class FileAssociationDialog
+ extends ModalDialog {
+
+ /** The default size for the dialog. */
+ static final private Dimension SIZE = new Dimension(600, 450);
+
+ private boolean ignore = false;
+ private FileAssociationDialog self;
+ private FileAssociationManager file_association_manager;
+ private JButton add_button;
+ private JButton browse_button;
+ private JButton close_button;
+ private JButton remove_button;
+ private JButton replace_button;
+ private JTable existing_associations_table;
+ private JTextField command_field;
+ private JTextField extension_field;
+
+ /** Create a new file association dialog.
+ * @param file_association_manager A reference to the FileAssociationManager so we can determine what extensions are already associated.
+ */
+ public FileAssociationDialog(FileAssociationManager file_association_manager) {
+ super(Gatherer.g_man);
+ this.file_association_manager = file_association_manager;
+ this.self = this;
+ this.setComponentOrientation(Dictionary.getOrientation());
+ // Creation
+ setModal(true);
+ setSize(SIZE);
+ setTitle(Dictionary.get("FileAssociationDialog.Title"));
+ setJMenuBar(new SimpleMenuBar("fileassociations"));
+
+ JPanel content_pane = (JPanel) getContentPane();
+ content_pane.setBackground(Configuration.getColor("coloring.collection_heading_background", false));
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JTextArea instructions_area = new JTextArea(Dictionary.get("FileAssociationDialog.Instructions"));
+ instructions_area.setEditable(false);
+ instructions_area.setLineWrap(true);
+ instructions_area.setRows(5);
+ instructions_area.setWrapStyleWord(true);
+ instructions_area.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel control_pane = new JPanel();
+ control_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ existing_associations_table = new JTable(file_association_manager);
+ existing_associations_table.setComponentOrientation(Dictionary.getOrientation());
+ existing_associations_table.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN);
+ existing_associations_table.setColumnSelectionAllowed(false);
+ existing_associations_table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ JPanel lower_pane = new JPanel();
+ lower_pane.setComponentOrientation(Dictionary.getOrientation());
+ JPanel details_pane = new JPanel();
+ details_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel extension_pane = new JPanel();
+ extension_pane.setComponentOrientation(Dictionary.getOrientation());
+ JLabel extension_label = new JLabel(Dictionary.get("FileAssociationDialog.Extension"));
+ extension_label.setComponentOrientation(Dictionary.getOrientation());
+ extension_field = new NonWhitespaceField();
+ extension_field.setToolTipText(Dictionary.get("FileAssociationDialog.Extension_Tooltip"));
+
+ JLabel command_label = new JLabel(Dictionary.get("FileAssociationDialog.Command"));
+ command_label.setComponentOrientation(Dictionary.getOrientation());
+ JPanel command_pane = new JPanel();
+ command_pane.setComponentOrientation(Dictionary.getOrientation());
+ command_field = new JTextField();
+ command_field.setComponentOrientation(Dictionary.getOrientation());
+ command_field.setToolTipText(Dictionary.get("FileAssociationDialog.Command_Tooltip"));
+ browse_button = new GLIButton(Dictionary.get("FileAssociationDialog.Browse"));
+ browse_button.setEnabled(!Utility.isMac());
+ if (Utility.isMac()) {
+ browse_button.setToolTipText(Dictionary.get("FileAssociationDialog.Browse_Tooltip_Mac"));
+ } else {
+ browse_button.setToolTipText(Dictionary.get("FileAssociationDialog.Browse_Tooltip"));
+ }
+
+
+ JPanel button_pane = new JPanel();
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ add_button = new GLIButton(Dictionary.get("FileAssociationDialog.Add"), Dictionary.get("FileAssociationDialog.Add_Tooltip"));
+ add_button.setEnabled(false);
+
+ replace_button = new GLIButton(Dictionary.get("FileAssociationDialog.Replace"), Dictionary.get("FileAssociationDialog.Replace_Tooltip"));
+ replace_button.setEnabled(false);
+
+ remove_button = new GLIButton(Dictionary.get("FileAssociationDialog.Remove"), Dictionary.get("FileAssociationDialog.Remove_Tooltip"));
+ remove_button.setEnabled(false);
+
+ close_button = new GLIButton(Dictionary.get("FileAssociationDialog.Close"), Dictionary.get("FileAssociationDialog.Close_Tooltip"));
+
+ // Connection
+ add_button.addActionListener(new AddButtonListener());
+ browse_button.addActionListener(new BrowseButtonListener());
+ close_button.addActionListener(new CloseButtonListener());
+ remove_button.addActionListener(new RemoveButtonListener());
+ replace_button.addActionListener(new ReplaceButtonListener());
+ CommandOrExtensionFieldListener coefl = new CommandOrExtensionFieldListener();
+ command_field.getDocument().addDocumentListener(coefl);
+ extension_field.getDocument().addDocumentListener(coefl);
+ coefl = null;
+ existing_associations_table.getSelectionModel().addListSelectionListener(new ExistingAssociationsTableListener());
+
+ // Layout
+ extension_pane.setBorder(BorderFactory.createEmptyBorder(2,0,2,0));
+ extension_pane.setLayout(new BorderLayout(5,0));
+ extension_pane.add(extension_label, BorderLayout.LINE_START);
+ extension_pane.add(extension_field, BorderLayout.CENTER);
+
+ command_pane.setBorder(BorderFactory.createEmptyBorder(2,0,2,0));
+ command_pane.setLayout(new BorderLayout());
+ command_pane.add(command_field, BorderLayout.CENTER);
+ command_pane.add(browse_button, BorderLayout.LINE_END);
+
+ details_pane.setBorder(BorderFactory.createTitledBorder(Dictionary.get("FileAssociationDialog.Details")));
+ details_pane.setLayout(new GridLayout(3,1));
+ details_pane.add(extension_pane);
+ details_pane.add(command_label);
+ details_pane.add(command_pane);
+
+ lower_pane.setBorder(BorderFactory.createEmptyBorder(2,0,0,0));
+ button_pane.setLayout(new GridLayout(2,3));
+ button_pane.add(add_button);
+ button_pane.add(replace_button);
+ button_pane.add(remove_button);
+ JPanel tmp ;
+ tmp = new JPanel();
+ tmp.setComponentOrientation(Dictionary.getOrientation());
+ button_pane.add(tmp);
+ tmp = new JPanel();
+ tmp.setComponentOrientation(Dictionary.getOrientation());
+ button_pane.add(tmp);
+ button_pane.add(close_button);
+
+ lower_pane.setBorder(BorderFactory.createEmptyBorder(2,0,0,0));
+ lower_pane.setLayout(new BorderLayout());
+ lower_pane.add(details_pane, BorderLayout.CENTER);
+ lower_pane.add(button_pane, BorderLayout.SOUTH);
+
+ control_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
+ control_pane.setLayout(new BorderLayout());
+ JScrollPane scrol_tmp;
+ scrol_tmp = new JScrollPane(existing_associations_table);
+ scrol_tmp.setComponentOrientation(Dictionary.getOrientation());
+ control_pane.add(scrol_tmp, BorderLayout.CENTER);
+ control_pane.add(lower_pane, BorderLayout.SOUTH);
+
+ content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ content_pane.setLayout(new BorderLayout());
+ scrol_tmp = new JScrollPane(instructions_area);
+ scrol_tmp.setComponentOrientation(Dictionary.getOrientation());
+ content_pane.add(scrol_tmp, BorderLayout.NORTH);
+ content_pane.add(control_pane, BorderLayout.CENTER);
+
+ Rectangle screen = Gatherer.g_man.getBounds(null);
+ setLocation((int)(screen.getX() + (screen.getWidth() - SIZE.width) / 2), (int)(screen.getY() + (screen.getHeight() - SIZE.height) / 2));
+ screen = null;
+
+ }
+
+ public void destroy() {
+ // Disconnect
+ // Clean up
+ file_association_manager = null;
+ self = null;
+ }
+
+ /** Redisplay the dialog, ensuring that the details (or lack thereof) for a certain extension is apparent. In fact if an extension is provided force the user to add it or cancel.
+ * @param new_extension The extension code as a String .
+ */
+ public String display(String new_extension) {
+ // Clear the current selection
+ existing_associations_table.clearSelection();
+ if(new_extension != null) {
+ extension_field.setText(new_extension);
+ }
+ setVisible(true);
+ if(new_extension != null) {
+ return file_association_manager.getCommandString(new_extension);
+ }
+ return null;
+ }
+
+ protected void processWindowEvent(WindowEvent event) {
+ if(event.getID() == WindowEvent.WINDOW_ACTIVATED) {
+ TableUtils.fixColumnToPreferredWidth(existing_associations_table, 0);
+ }
+ else {
+ super.processWindowEvent(event);
+ }
+ }
+
+ private class AddButtonListener
+ implements ActionListener {
+
+ public void actionPerformed(ActionEvent event) {
+ String extension_str = extension_field.getText();
+ file_association_manager.setCommand(extension_str, command_field.getText());
+ // Select the appropriate entry in the table
+ int index = -1;
+ for(int i = 0; index == -1 && i < file_association_manager.size(); i++) {
+ if(file_association_manager.getExtension(i).equals(extension_str)) {
+ index = i;
+ }
+ }
+ if(index > -1) {
+ existing_associations_table.setRowSelectionInterval(index, index);
+ }
+ // And update buttons
+ add_button.setEnabled(false);
+ remove_button.setEnabled(true);
+ }
+ }
+
+ /** Batch filter shows only files ending in bat. Based on ImageFilter.java which is a 1.4 example used by FileChooserDemo2.java. */
+ private class BatchFileFilter
+ extends FileFilter {
+
+ /** Accept all .exe files
+ * @param file a File
+ * @return true is this file should be shown, false otherwise
+ */
+ public boolean accept(File file) {
+ return file.getName().toLowerCase().endsWith(".bat");
+ }
+
+ /** The description of this filter
+ * @return a String
+ */
+ public String getDescription() {
+ return Dictionary.get("FileAssociationDialog.Batch_File");
+ }
+ }
+
+ /** Whenever the user clicks the browse button, we should open up a file browser to allow them to select an executable file from somewhere in the file system. */
+ private class BrowseButtonListener
+ implements ActionListener {
+ /** Open up a simple JFileChooser when the user clicks the button.
+ * @param event An ActionEvent containing information about the event.
+ */
+ public void actionPerformed(ActionEvent event) {
+ JFileChooser chooser = new JFileChooser(new File(Gatherer.getGLIUserDirectoryPath()));
+ GUIUtils.disableRename(chooser);
+ chooser.setDialogTitle(Dictionary.get("FileAssociationDialog.Browse_Title"));
+ chooser.setFileFilter(new BatchFileFilter());
+ chooser.setFileFilter(new CoreObjectModelFileFilter());
+ chooser.setFileFilter(new ExecutableFileFilter());
+ chooser.setAcceptAllFileFilterUsed(true);
+ if(chooser.showOpenDialog(Gatherer.g_man) == JFileChooser.APPROVE_OPTION) {
+ command_field.setText(chooser.getSelectedFile().getAbsolutePath());
+ }
+ }
+ }
+
+ private class CloseButtonListener
+ implements ActionListener {
+ public void actionPerformed(ActionEvent event) {
+ existing_associations_table.clearSelection();
+ add_button.setEnabled(false);
+ remove_button.setEnabled(false);
+ replace_button.setEnabled(false);
+ self.dispose();
+ }
+ }
+
+ /** Command filter shows only files ending in com. Based on ImageFilter.java which is a 1.4 example used by FileChooserDemo2.java. */
+ private class CoreObjectModelFileFilter
+ extends FileFilter {
+
+ /** Accept all .exe files
+ * @param file a File
+ * @return true is this file should be shown, false otherwise
+ */
+ public boolean accept(File file) {
+ return file.getName().toLowerCase().endsWith(".com");
+ }
+
+ /** The description of this filter
+ * @return a String
+ */
+ public String getDescription() {
+ return Dictionary.get("FileAssociationDialog.Command_File");
+ }
+ }
+
+ private class CommandOrExtensionFieldListener
+ implements DocumentListener {
+ /** Gives notification that an attribute or set of attributes changed. */
+ public void changedUpdate(DocumentEvent e) {
+ changed();
+ }
+ /** Gives notification that there was an insert into the document. */
+ public void insertUpdate(DocumentEvent e) {
+ changed();
+ }
+ /** Gives notification that a portion of the document has been removed. */
+ public void removeUpdate(DocumentEvent e) {
+ changed();
+ }
+
+ private void changed() {
+ ignore = true;
+ String extension_str = extension_field.getText();
+ String command_str = command_field.getText();
+ // Determine if there is currently an association selected
+ int selected_index = -1;
+ if((selected_index = existing_associations_table.getSelectionModel().getMinSelectionIndex()) != -1) {
+ String current_extension_str = file_association_manager.getExtension(selected_index);
+ String current_command_str = file_association_manager.getCommandString(current_extension_str);
+ // What buttons are enabled depends a lot on the file extension, taken to be the 'key'
+ if(extension_str.equals(current_extension_str)) {
+ // If the extensions are the same then remove is enabled
+ remove_button.setEnabled(true);
+ // But if the command is different, then enable replace
+ if(current_command_str.length() == 0) {
+ add_button.setEnabled(true);
+ replace_button.setEnabled(false);
+ }
+ else if(!command_str.equals(current_command_str)) {
+ add_button.setEnabled(false);
+ replace_button.setEnabled(true);
+ }
+ else {
+ add_button.setEnabled(false);
+ replace_button.setEnabled(false);
+ }
+ }
+ // We can add it, but we first clear the selection
+ else {
+ existing_associations_table.clearSelection();
+ add_button.setEnabled(true);
+ remove_button.setEnabled(false);
+ replace_button.setEnabled(false);
+ }
+ }
+ // If not, we must first test if the extension is in use
+ else {
+ int index = -1;
+ for(int i = 0; index == -1 && i < file_association_manager.size(); i++) {
+ String existing_extension_str = file_association_manager.getExtension(i);
+ if(existing_extension_str.equals(extension_str)) {
+ index = i;
+ }
+ }
+ if(index != -1) {
+ // Selection that index
+ existing_associations_table.setRowSelectionInterval(index, index);
+ // Set replace and remove enabled
+ add_button.setEnabled(false);
+ remove_button.setEnabled(true);
+ replace_button.setEnabled(true);
+ }
+ // Otherwise if there is some text in both extension and command, enable add only
+ else if(extension_str.length() > 0 && command_str.length() > 0) {
+ add_button.setEnabled(true);
+ remove_button.setEnabled(false);
+ replace_button.setEnabled(false);
+ }
+ // Otherwise disable everything
+ else {
+ add_button.setEnabled(false);
+ remove_button.setEnabled(false);
+ replace_button.setEnabled(false);
+ }
+ }
+ ignore = false;
+ }
+ }
+
+ /** Executable filter shows only files ending in exe. Based on ImageFilter.java which is a 1.4 example used by FileChooserDemo2.java. */
+ private class ExecutableFileFilter
+ extends FileFilter {
+
+ /** Accept all .exe files
+ * @param file a File
+ * @return true is this file should be shown, false otherwise
+ */
+ public boolean accept(File file) {
+ return file.getName().toLowerCase().endsWith(".exe");
+ }
+
+ /** The description of this filter
+ * @return a String
+ */
+ public String getDescription() {
+ return Dictionary.get("FileAssociationDialog.Executable_File");
+ }
+ }
+
+ private class ExistingAssociationsTableListener
+ implements ListSelectionListener {
+
+ public void valueChanged(ListSelectionEvent event) {
+ if(!event.getValueIsAdjusting() && !ignore) {
+ int selected_index = -1;
+ if((selected_index = existing_associations_table.getSelectionModel().getMinSelectionIndex()) != -1) {
+ // Propagate the details of the selection down into the fields
+ String extension_str = file_association_manager.getExtension(selected_index);
+ extension_field.setText(extension_str);
+ command_field.setText(file_association_manager.getCommandString(extension_str));
+ extension_str = null;
+ remove_button.setEnabled(true);
+ }
+ else {
+ // Clear the fields
+ extension_field.setText("");
+ command_field.setText("");
+ remove_button.setEnabled(false);
+ }
+ add_button.setEnabled(false);
+ replace_button.setEnabled(false);
+ }
+ }
+ }
+
+ private class RemoveButtonListener
+ implements ActionListener {
+ public void actionPerformed(ActionEvent event) {
+ file_association_manager.setCommand(extension_field.getText(), null);
+ // And update buttons
+ add_button.setEnabled(true);
+ remove_button.setEnabled(false);
+ }
+ }
+
+ private class ReplaceButtonListener
+ implements ActionListener {
+ public void actionPerformed(ActionEvent event) {
+ file_association_manager.setCommand(extension_field.getText(), command_field.getText());
+ // And update buttons
+ replace_button.setEnabled(false);
+ }
+ }
+}
+
+
+
+
+
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/Filter.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/Filter.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/Filter.java (revision 31635)
@@ -0,0 +1,336 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.util.regex.*;
+import javax.swing.*;
+import javax.swing.tree.*;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.file.FileSystemModel;
+import org.greenstone.gatherer.gui.tree.DragTree;
+
+/** This object allows the user to set a filter on one of the workspace trees, specifying a preset type, or a regular expression that a files must match to be in the tree. Note that all directories are included. This class includes the controls for editing the filter. The trick is that several instances of the Filter class can share the same internal data (termed a 'run' of filters), so that the filter set on the GatherPane and the EnrichPane are virtually the same.
+ * The regular expression typed uses '*' as a wildcard character (equivalent to '.*'), and does not use '.' to match any single character (use '?' instead).
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.3
+ */
+public class Filter
+ extends JPanel {
+ /** The other filters in this run of filters, used to ensure they all show the same thing. */
+ private ArrayList others = null;
+ /** Is this the first filter of this run of filters created (later filters will share the same information). */
+ private boolean first = true;
+ /** Prevent any changes we make in the class from causing events which we then process causing events... */
+ private boolean ignore = false;
+ /** A reference to ourselves so inner classes can refer to us. */
+ private Filter this_filter = null;
+ /** The check box to enable/disable filter. */
+ private JCheckBox checkbox = null;
+ /** The editable combobox where you either choose a predefined filter, or type a new pseudo-regular expression. */
+ private GComboBox combobox = null;
+ /** The label shown on the filter controls. */
+ private JLabel label = null;
+ /** A reference to the tree this filter is being applied to. */
+ private DragTree tree = null;
+ /** The default size for the label. */
+ static final private Dimension SIZE = new Dimension(100,30);
+ /** Preprogrammed default filters. */
+ static final private String DEFAULTS[]
+ = {"^.*\\.html?$", "^.*\\.xml$", "^.*\\.txt$",
+ "(^.*\\.jpe?g$)|(^.*\\.png$)|(^.*\\.gif$)|(^.*\\.bmp$)|(^.*\\.tiff?$)",
+ "^.*\\.pdf$",
+ "(^.*\\.docx?$)|(^.*\\.pptx?$)|(^.*\\.xlsx?$)|(^.*\\.od(t|s|p)$)"};
+
+ /** Constructor.
+ * @param tree A reference to the JTree being affected.
+ */
+ public Filter(DragTree tree) {
+ this(tree, null);
+ }
+
+ /** Constructor.
+ * @param tree A reference to the JTree being affected.
+ * @param others An ArrayList of the other Filters already in this run.
+ */
+ public Filter(DragTree tree, ArrayList others) {
+ super();
+ this.setComponentOrientation(Dictionary.getOrientation());
+ if (others == null) {
+ others = new ArrayList();
+ }
+ this.others = others;
+ this.others.add(this);
+ this.this_filter = this;
+ this.tree = tree;
+ // Create components.
+ combobox = new GComboBox(true);
+ try {
+ combobox.add(new Entry());
+ }
+ catch (Exception error) {
+ error.printStackTrace();
+ }
+ for(int i = 0; i < DEFAULTS.length; i++) {
+ try {
+ Entry entry = new Entry(Dictionary.get("Filter." + i), Pattern.compile(DEFAULTS[i]));
+ combobox.add(entry);
+ }
+ catch (Exception error) {
+ error.printStackTrace();
+ }
+ }
+ label = new JLabel(Dictionary.get("Filter.Filter_Tree"));
+ label.setComponentOrientation(Dictionary.getOrientation());
+ label.setComponentOrientation(org.greenstone.gatherer.Dictionary.getOrientation());
+ combobox.setToolTipText(Dictionary.get("Collection.Filter_Tooltip"));
+
+ // Add listeners.
+ combobox.addActionListener(new ComboBoxListener());
+ // Layout.
+ label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5));
+
+ setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
+ setLayout(new BorderLayout());
+ add(label, BorderLayout.LINE_START);
+ add(combobox, BorderLayout.CENTER);
+ }
+
+ /** Used to restore the filter state to enabled, the normal state during collection editing.
+ * @param state The new state for the filter. true for enabled, false otherwise.
+ */
+ public void setEnabled(boolean state) {
+ ignore = true;
+ combobox.setEnabled(state);
+ ignore = false;
+ }
+
+ /** Set the combobox model for this filter.
+ * @param model The new ComboBoxModel to use.
+ */
+ public void setComboBoxModel(ComboBoxModel model) {
+ combobox.setModel(model);
+ }
+
+ /** Ensure that a certain entry is selected from the combobox.
+ * @param selection The Entry that should be selected.
+ */
+ public void setComboBoxSelection(Entry selection) {
+ ignore = true;
+ combobox.setSelectedItem(selection);
+ ignore = false;
+ }
+
+ /** Sets whether this combobox is editable or not
+ * @param value true to make the control editable, false otherwise
+ */
+ public void setEditable(boolean value) {
+ combobox.setEditable(value);
+ }
+
+ /** Set to signify that this filter is the first in a new run of filters.
+ * @param first true if this is the first filter in a run, false if it will just be added to the current run.
+ */
+ public void setFirst(boolean first) {
+ this.first = first;
+ }
+
+ /** Encode an expression in pseudo-regular expression into regular expression.
+ * @param raw The pseudo-regular expression String which includes several characters which differ in meaning from regular expression queries.
+ * @return A proper regular expression as a String .
+ */
+ private String encode(String raw) {
+ StringBuffer working = new StringBuffer();
+ for(int i = 0; i < raw.length(); i++) {
+ char c = raw.charAt(i);
+ switch(c) {
+ case '.':
+ working.append("\\.");
+ break;
+ case '*':
+ working.append(".*");
+ break;
+ case '?':
+ working.append(".");
+ break;
+ default:
+ working.append(Character.toLowerCase(c));
+ }
+ }
+ return working.toString();
+ }
+ /** This method applies the given pattern to the tree registered as belonging to this filter.*/
+ private void setFilter(Pattern pattern) {
+ // Show busy cursor.
+ Gatherer.g_man.wait(true);
+ FileSystemModel model = (FileSystemModel) tree.getModel();
+ // Apply filter
+ if(pattern != null) {
+ model.setFilter(pattern.pattern());
+ }
+ else {
+ model.setFilter(null);
+ }
+ // Ask tree to completely refresh
+ tree.refresh(null);
+ // Restore cursor
+ Gatherer.g_man.wait(false);
+ }
+
+ /** Listens for changes in the combobox as when one is detected attempts to compile a regular expression from whatever text was entered. If successful, or if the item chosen was a predefined filter, it then applies the filter to the target tree. */
+ private class ComboBoxListener
+ implements ActionListener {
+ /** Called when a new item is selected from the filter combobox, we treat the new entry as a pseudo-regular expression, compile it and then apply it to the tree.
+ * @param event An ActionEvent containing more information about the change performed.
+ * @see org.greenstone.gatherer.gui.Filter.Entry
+ */
+ public void actionPerformed(ActionEvent event) {
+ try {
+ Object temp = combobox.getSelectedItem();
+ Entry entry = null;
+ if(temp instanceof String) {
+ String temp_str = (String) temp;
+ ///ystem.err.println("Filter = " + temp_str);
+
+ // Ignore any string which matches a predefined filter
+ if(temp_str.equals(Dictionary.get("Filter.All_Files"))) {
+ }
+ // HTM & HTML
+ else if(temp_str.equals(Dictionary.get("Filter.0"))) {
+ }
+ // XML
+ else if(temp_str.equals(Dictionary.get("Filter.1"))) {
+ }
+ // Text files
+ else if(temp_str.equals(Dictionary.get("Filter.2"))) {
+ }
+ // Images
+ else if(temp_str.equals(Dictionary.get("Filter.3"))) {
+ }
+ // PDF
+ else if(temp_str.equals(Dictionary.get("Filter.4"))) {
+ }
+ // Office Docs
+ else if(temp_str.equals(Dictionary.get("Filter.5"))) {
+ }
+ else {
+ // Make sure the filter isn't already in the list
+ boolean already_exists = false;
+ for (int i = 0; i < combobox.getItemCount(); i++) {
+ if (temp_str.equals(combobox.getItemAt(i).toString())) {
+ already_exists = true;
+ entry = (Entry) combobox.getItemAt(i);
+ break;
+ }
+ }
+
+ if (already_exists == false) {
+ entry = new Entry(temp_str, Pattern.compile(encode(temp_str)));
+ int position = combobox.getItemCount();
+ combobox.insertItemAt(entry, position);
+ combobox.setSelectedIndex(position);
+ }
+ }
+ }
+ else {
+ ///ystem.err.println("Custom Filter");
+ entry = (Entry) temp;
+ }
+ if(entry != null) {
+ setFilter(entry.getPattern());
+ }
+ // Else we ignore this event as being one of the painfully erroneous events we receive because we've been silly enough to have an editable combobox.
+ }
+ catch (PatternSyntaxException error) {
+ if(first) {
+ JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("Filter.Invalid_Pattern"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ }
+ }
+ /** An object that holds a filter entry. This is string used for the filter pattern and, if not custom built, its name. */
+ private class Entry
+ implements Comparable {
+ /** The compiled pattern created from a regular expression. */
+ private Pattern pattern = null;
+ /** The name of this filter entry. */
+ private String name = null;
+ /** Constructor. */
+ public Entry() {
+ }
+ /** Constructor.
+ * @param name The name of this entry as a String .
+ * @param pattern The compiled regular expression as a Pattern .
+ */
+ public Entry(String name, Pattern pattern) {
+ this.name = name;
+ this.pattern = pattern;
+ }
+ /** Compare two Entrys for ordering.
+ * @param object The other Entry to compare to, as an Object .
+ * @return An int indicating the respective ordering, as defined in java.lang.String#compareTo
+ */
+ public int compareTo(Object object) {
+ return toString().compareTo(object.toString());
+ }
+ /** Retrieve the pattern associated with this entry.
+ * @return The Pattern .
+ */
+ public Pattern getPattern() {
+ return pattern;
+ }
+ /** Translate this entry into a textual representation.
+ * @return A String containing the representation.
+ */
+ public String toString() {
+ String result = null;
+ if (name != null) {
+ result = name;
+ }
+ else if (pattern == null) {
+ result = Dictionary.get("Filter.All_Files");
+ }
+ else {
+ result = pattern.pattern();
+ }
+ return result;
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/FormatConversionDialog.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/FormatConversionDialog.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/FormatConversionDialog.java (revision 31635)
@@ -0,0 +1,908 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+
+package org.greenstone.gatherer.gui;
+
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.cdm.CollectionConfigXMLReadWrite;
+import org.greenstone.gatherer.util.Codec;
+import org.greenstone.gatherer.util.SafeProcess;
+import org.greenstone.gatherer.util.StaticStrings;
+import org.greenstone.gatherer.util.XMLTools;
+
+import org.w3c.dom.*;
+
+import java.io.File;
+import java.io.IOException;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.event.*;
+
+
+// TO DO:
+// + StreamGobblers: http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html
+// 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
+// + Need to use dictionary for labels
+// X convertFormat() for remote GS. Remote does not display Format Conversion wizard
+// + HTML tidy (put all " " back to " " in luce/etc/collectionConfig.xml and run again)
+// + Help tooltips on buttons
+// + Undo, Redo buttons
+// X Split class into dialog/widgets and data processing?
+// http://www.java2s.com/Tutorial/Java/0240__Swing/SettingJOptionPanebuttonlabelstoFrench.htm
+
+public class FormatConversionDialog extends ModalDialog
+{
+ public static final String GSF_FORMAT_GS2_TAG = "gsf:format-gs2";
+ private static final String GSF_GS3_ROOT_TAG = "gsf:gs3-root";
+
+ // The cmdline programs launched by this dialog
+ private static final int XMLTIDY = 0;
+ private static final int FORMATCONVERTER = 1;
+ private static final int FORMATCONVERTER_DOCUMENTNODE = 2;
+ private static final int FORMATCONVERTER_CLASSIFIERNODE = 3;
+
+ // Online HTML tidy for learning usage: http://infohound.net/tidy/
+ private static final String[] xmltidy_cmd_args = {"tidy", "-config", Configuration.getGS3BinPath() + "tidyconfig.cfg", "-utf8", "-wrap", "0", "-raw", "-q"}; // {"tidy", "-xml", "-utf8"};
+ private static final String[] formatconverter_cmd_base_args = {Configuration.getGS3BinOSPath() + "formatconverter", "--silent"};
+
+ private static final Dimension SIZE = new Dimension(640,480);
+
+ // the important member variables
+ private File collect_cfg_file = null;
+ private Document xml_file_doc = null;
+ private NodeList gsf_format_gs2_list = null;
+
+ private int current_index = -1;
+ private int dlgResult = OpenCollectionDialog.OK_OPTION;
+ private int process_exitValue = -1;
+
+ // widgets
+ final String WARNING_TITLE = Dictionary.get("General.Warning")
+ + ": " + Dictionary.get("FormatConversionDialog.Invalid_XML")
+ + " " + Dictionary.get("FormatConversionDialog.Invalid_XML_Warning_Title");
+
+ private static final Border EMPTYBORDER = new EmptyBorder(5, 5, 5, 5);
+ private JLabel section_label = null;
+ private GLIButton cancel_button = null;
+ private GLIButton next_button = null;
+ private GLIButton accept_all_button = null;
+ private GLIButton htmltidy_button = null;
+ private GLIButton xmltidy_button = null;
+ // the 2 NumberedJTextAreas. Each provide their own undo and redo buttons already hooked up to listeners
+ private NumberedJTextArea gs2_textarea = null;
+ private NumberedJTextArea gs3_textarea = null;
+ private JLabel count_label = null;
+ private JLabel statusbar = null;
+
+ public FormatConversionDialog (File collect_cfg_file, Document xml_file_doc, NodeList gsf_format_gs2_list) {
+ super(Gatherer.g_man, "", true);
+
+ this.collect_cfg_file = collect_cfg_file;
+ this.xml_file_doc = xml_file_doc;
+ this.gsf_format_gs2_list = gsf_format_gs2_list;
+
+ this.setComponentOrientation(Dictionary.getOrientation());
+ setSize(SIZE);
+ setTitle(Dictionary.get("FormatConversionDialog.Title"));
+ this.addWindowListener(new WindowClosingListener()); // the dialog's top right close button should behave as a Cancel press
+ setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+
+ // The NumberedJTextArea provides its own undo and redo buttons, already hooked up to listeners
+ // Just need to place these buttons in the dialog
+ gs2_textarea = new NumberedJTextArea("gs2", Dictionary.get("FormatConversionDialog.GS2_Text_Tooltip"));
+ gs3_textarea = new NumberedJTextArea("gs3", Dictionary.get("FormatConversionDialog.GS3_Text_Tooltip"));
+
+
+ JPanel midbutton_panel = new JPanel(); // FlowLayout by default in a JPanel
+ midbutton_panel.setComponentOrientation(Dictionary.getOrientation());
+
+ JButton reconvert_button = new GLIButton(Dictionary.get("FormatConversionDialog.Reconvert"), Dictionary.get("FormatConversionDialog.Reconvert_Tooltip"));
+
+ midbutton_panel.add(gs2_textarea.undoButton);
+ midbutton_panel.add(gs2_textarea.redoButton);
+ midbutton_panel.add(reconvert_button);
+ reconvert_button.addActionListener(new ReconvertListener());
+ reconvert_button.setAlignmentY(Component.CENTER_ALIGNMENT);
+
+ section_label = new JLabel("Section Label Goes Here"); // title will be replaced, don't need to translate
+ section_label.setBorder(EMPTYBORDER);
+ section_label.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel button1_panel = new JPanel();
+ button1_panel.setComponentOrientation(Dictionary.getOrientation());
+
+ xmltidy_button = new GLIButton(Dictionary.get("FormatConversionDialog.XHTML_Tidy"), Dictionary.get("FormatConversionDialog.XHTML_Tidy_Tooltip"));
+ xmltidy_button.addActionListener(new XMLTidyButtonListener());
+
+ button1_panel.add(gs3_textarea.undoButton);
+ button1_panel.add(gs3_textarea.redoButton);
+ button1_panel.add(xmltidy_button);
+
+ JPanel button_panel = new JPanel(new FlowLayout());
+ button_panel.setComponentOrientation(Dictionary.getOrientation());
+ count_label = new JLabel(""); // placeholder text for numbers, no need to translate
+ cancel_button = new GLIButton(Dictionary.get("General.Cancel"), Dictionary.get("FormatConversionDialog.Cancel_Tooltip"));
+ next_button = new GLIButton(Dictionary.get("FormatConversionDialog.Next"), Dictionary.get("FormatConversionDialog.Next_Tooltip"));
+ accept_all_button = new GLIButton(Dictionary.get("FormatConversionDialog.Accept_All"), Dictionary.get("FormatConversionDialog.Accept_All_Tooltip"));
+ cancel_button.addActionListener(new CancelButtonListener());
+ next_button.addActionListener(new NextButtonListener());
+ accept_all_button.addActionListener(new AcceptAllButtonListener());
+ button_panel.add(cancel_button);
+ button_panel.add(next_button);
+ button_panel.add(accept_all_button);
+ button_panel.add(count_label);
+
+
+ JPanel centre_panel = new JPanel();
+ centre_panel.setLayout(new BoxLayout(centre_panel, BoxLayout.Y_AXIS));
+ centre_panel.setComponentOrientation(Dictionary.getOrientation());
+ centre_panel.setBorder(EMPTYBORDER);
+ centre_panel.add(new JScrollPane(gs2_textarea));
+ centre_panel.add(midbutton_panel);
+ centre_panel.add(new JScrollPane(gs3_textarea));
+
+ JPanel bottom_panel = new JPanel();
+ bottom_panel.setLayout(new BoxLayout(bottom_panel, BoxLayout.Y_AXIS));
+ bottom_panel.setComponentOrientation(Dictionary.getOrientation());
+ bottom_panel.setBorder(EMPTYBORDER);
+
+ bottom_panel.add(button1_panel);
+ bottom_panel.add(button_panel);
+ statusbar = new JLabel("");
+ statusbar.setBorder(EMPTYBORDER);
+ // http://stackoverflow.com/questions/2560784/how-to-center-elements-in-the-boxlayout-using-center-of-the-element
+ statusbar.setAlignmentX(Component.CENTER_ALIGNMENT);
+ bottom_panel.add(statusbar);
+
+ // add all the widgets to the contentpane
+ JPanel content_pane = (JPanel) getContentPane();
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+ content_pane.setLayout(new BorderLayout());
+ //content_pane.setBorder(EMPTYBORDER);
+
+ content_pane.add(section_label, BorderLayout.NORTH);
+ content_pane.add(centre_panel, BorderLayout.CENTER);
+ content_pane.add(bottom_panel, BorderLayout.SOUTH);
+
+
+ // Final dialog setup & positioning.
+ getRootPane().setDefaultButton(next_button);
+ Dimension screen_size = Configuration.screen_size;
+ setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
+ screen_size = null;
+ //setVisible(true);
+ }
+
+
+ public int getDialogResult() {
+ return dlgResult; // OK_OPTION or CANCEL_OPTION
+ }
+
+ //*************************PROCESSING FUNCTIONS***************************//
+ public static int checkForGS2FormatStatements(File collect_cfg_file) {
+
+ if(Gatherer.GS3 && collect_cfg_file.getAbsolutePath().endsWith(".xml")) {
+
+ //System.err.println("*** Opening an xml config file");
+
+ Document xml_file_doc = XMLTools.parseXMLFile(collect_cfg_file);
+ Element root = xml_file_doc.getDocumentElement();
+
+ // check if there are any elements. If there are, then may need to process them
+
+ //NodeList gsf_format_gs2_list = root.getElementsByTagNameNS("gsf", "format-gs2");
+ NodeList gsf_format_gs2_list = root.getElementsByTagName(FormatConversionDialog.GSF_FORMAT_GS2_TAG);
+ if(gsf_format_gs2_list != null && gsf_format_gs2_list.getLength() > 0) {
+
+ // Sample the first of the elements to
+ // check we don't have any CDataSections in the elements
+ // If the first has a CDataSection, it means we've already
+ // converted it to GS3 format statements during an earlier GLI session.
+
+ Node gs2format = gsf_format_gs2_list.item(0);
+ //gs2format.normalize();
+ NodeList children = gs2format.getChildNodes();
+
+ for(int i = 0; i < children.getLength(); i++) {
+ Node child = children.item(i);
+ if(child.getNodeType() == Node.CDATA_SECTION_NODE) {
+ // there are GS2 statements in col config, but they've already been converted to GS3
+ // can open the collection without going through the FormatConversionDialog
+ return OpenCollectionDialog.OK_OPTION;
+ }
+ }
+ System.err.println("*** Found GS2 format statements in config file to be converted to GS3.");
+
+
+ // if remote GS3, do we open the collection with the html-encoded GS2 format statements
+ // or do we not allow the remote user to open such a collection at all?
+ // For now we allow them to open it, but print a warning that conversions are not possible.
+
+ if(Gatherer.isGsdlRemote) { // remote GS3
+ System.err.println("*** Cannot convert GS2 collections from a remote GS3 server.");
+ return OpenCollectionDialog.OK_OPTION;
+ }
+
+ // If we get here, it means there were no CDataSections in the first (any)
+ // This means it's the first time the GS2 collection is being opened in GLI
+ // Open the FormatCconversionDialog and convert the gs2 statements to gs3:
+ FormatConversionDialog formatconversionDlg = new FormatConversionDialog(collect_cfg_file, xml_file_doc, gsf_format_gs2_list);
+ formatconversionDlg.convertGS2FormatStatements();
+ return formatconversionDlg.getDialogResult();
+
+ }
+ }
+ return OpenCollectionDialog.OK_OPTION; // no GS2 statements in col config, and can open the collection
+ }
+
+
+ /**
+ * runInteractiveProgram() runs a cmdline program that reads from stdinput
+ * until Ctrl-D is encountered. It outputs the result to stdout.
+ *
+ * The cmdline programs HTML Tidy and FormatConverter both behave the same way:
+ * When the formatconverter binary is run in silent mode, it expects input
+ * followed by a newline and then EOF (or if no newline, then 2 EOFs)
+ * which is Ctrl-D on Linux/Mac and Ctrl-Z on Windows.
+ * Then the cmdline program exits by printing the result of the conversion.
+ *
+ * Explicitly sending EOFs from java is no longer necessary, as the OutputStreamGobbler
+ * Thread takes care of closing the process stream.
+ *
+ * HTMLTidy returns 0 if no warnings or errors, 1 if just Warnings, 2 if Errors (failure)
+ * http://sourceforge.net/p/tidy/mailman/tidy-develop/thread/20020811204058.GD1137@shaw.ca/
+ *
+ * This code uses the StreamGobbler classes based on
+ * http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2
+ */
+ public String runInteractiveProgram(int program, String inputstr) {
+ String outputstr = "";
+ String[] command_args;
+ process_exitValue = -1;
+
+ switch(program) {
+ case XMLTIDY:
+ command_args = xmltidy_cmd_args;
+ break;
+ case FORMATCONVERTER:
+ command_args = formatconverter_cmd_base_args;
+ break;
+ case FORMATCONVERTER_DOCUMENTNODE:
+ case FORMATCONVERTER_CLASSIFIERNODE:
+ command_args = new String[formatconverter_cmd_base_args.length+1];
+ System.arraycopy(formatconverter_cmd_base_args, 0, command_args, 0, formatconverter_cmd_base_args.length);
+ if(program == FORMATCONVERTER_DOCUMENTNODE) {
+ command_args[command_args.length-1] = "--documentNode";
+ } else if(program == FORMATCONVERTER_CLASSIFIERNODE) {
+ command_args[command_args.length-1] = "--classifierNode";
+ }
+ break;
+ default:
+ System.err.println("*** Unrecognised program code: " + program);
+ return outputstr;
+ }
+
+ // Generate the formatconverter command
+ /*if (Gatherer.isGsdlRemote) {
+
+ }*/
+
+ SafeProcess prcs = new SafeProcess(command_args);
+ prcs.setInputString(inputstr);
+ prcs.setSplitStdErrorNewLines(true);
+ prcs.runProcess();
+ // process done, now get process output
+ outputstr = prcs.getStdOutput();
+ String errmsg = prcs.getStdError();
+ if(!errmsg.equals("")) {
+ System.err.println("*** Process errorstream: \n" + errmsg + "\n****");
+ }
+
+ ///System.err.println("#### Got output: " + outputstr);
+
+ /*
+ try {
+
+ Runtime rt = Runtime.getRuntime();
+ Process prcs = null;
+
+ prcs = rt.exec(command_args);
+
+ // send inputstr to process
+ OutputStreamGobbler inputGobbler = new OutputStreamGobbler(prcs.getOutputStream(), inputstr);
+
+ // monitor for any error messages
+ InputStreamGobbler errorGobbler = new InputStreamGobbler(prcs.getErrorStream(), true);
+
+ // monitor for the expected output line(s)
+ InputStreamGobbler outputGobbler = new InputStreamGobbler(prcs.getInputStream());
+
+ // kick them off
+ inputGobbler.start();
+ errorGobbler.start();
+ outputGobbler.start();
+
+ // any error???
+ process_exitValue = prcs.waitFor();
+ //System.out.println("ExitValue: " + exitVal);
+
+ // From the comments of
+ // http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2
+ // To avoid running into nondeterministic failures to get the process output
+ // if there's no waiting for the threads, call join() on each Thread (StreamGobbler) object:
+ outputGobbler.join();
+ errorGobbler.join();
+ inputGobbler.join();
+
+ outputstr = outputGobbler.getOutput();
+ String errmsg = errorGobbler.getOutput();
+ if(!errmsg.equals("")) {
+ System.err.println("*** Process errorstream: \n" + errmsg + "\n****");
+ }
+
+ } catch(IOException ioe) {
+ System.err.println("IOexception " + ioe.getMessage());
+ //ioe.printStackTrace();
+ } catch(InterruptedException ie) {
+ System.err.println("Process InterruptedException " + ie.getMessage());
+ //ie.printStackTrace();
+ }
+ */
+ return outputstr;
+ }
+
+ public void convertGS2FormatStatements() {
+
+ // at this point, we know there are one or more elements
+ // process each of them as follows: unescape, then call formatconverter, then call html tidy on it
+ //NodeList gsf_format_gs2_list = root.getElementsByTagNameNS("gsf", "format-gs2");
+
+ int len = gsf_format_gs2_list.getLength();
+
+ for(int i = 0; i < len; i++) {
+
+ Element gs2format = (Element)gsf_format_gs2_list.item(i);
+ String gs2formatstr = XMLTools.getElementTextValue(gs2format); // seems to already unescape the html entities
+ //gs2formatstr = Codec.transform(gs2formatstr, Codec.ESCAPEDHTML_TO_UNESCAPED);
+
+ processFormatStatement(i, gs2formatstr);
+ }
+
+ increment(); // modifies the textareas to initialise them
+ setVisible(true);
+ }
+
+
+ private String processFormatStatement(int i, String gs2formatstr) {
+ String errorMsg = "";
+
+ boolean startsWithTableCell = (gs2formatstr.toLowerCase().startsWith("\\s+<", "><");
+
+ //System.err.println("*** Format is now: " + gs3formatstr);
+
+ String gs3formatstr_notags = gs3formatstr;
+ gs3formatstr = addSurroundingTags(gs3formatstr);
+
+
+ String validationMsg = XMLTools.parseDOM(gs3formatstr);
+ if(!validationMsg.startsWith(XMLTools.WELLFORMED)) {
+ // Run Html Tidy in XML mode
+ //System.err.println("*** Needing to run HTML Tidy on: ");
+ //System.err.println(gs3formatstr_notags);
+
+
+ // HTMLTidy returns 0 if no warnings or errors, 1 if just Warnings, 2 if Errors (failure)
+ // http://sourceforge.net/p/tidy/mailman/tidy-develop/thread/20020811204058.GD1137@shaw.ca/
+ String htmltidy_string = runInteractiveProgram(XMLTIDY, gs3formatstr_notags);
+
+ if(process_exitValue >= 2) {
+ // System.err.println("@@@ Process exit value: " + process_exitValue);
+ errorMsg = Dictionary.get("FormatConversionDialog.Tidy_Failed")
+ + " " + Dictionary.get("FormatConversionDialog.XML_Still_Invalid");
+ } else {
+ errorMsg = "";
+
+ gs3formatstr_notags = htmltidy_string;
+
+ gs3formatstr_notags = removeHTMLTags(i, gs3formatstr_notags, startsWithTableCell);
+ gs3formatstr = addSurroundingTags(gs3formatstr_notags);
+
+ // Having applied html-tidy, setGS3Format() will return true if it finally parsed
+ }
+ }
+
+ // For now, assume HTML Tidy has worked and that the gs3format has now been parsed successfully
+
+ boolean parsed = setGS3Format(i, gs3formatstr); // will parse the gs3formatstr into a DOM object
+ if(!parsed && errorMsg.equals("")) {
+ errorMsg = Dictionary.get("FormatConversionDialog.Tidy_Done")
+ + " " + Dictionary.get("FormatConversionDialog.XML_Still_Invalid");
+ }
+ return errorMsg;
+ //return gs3formatstr;
+ }
+
+ // HTML tidy adds entire HTML tags around a single format statement. This method removes it.
+ private String removeHTMLTags(int i, String gs3formatstr_notags, boolean startsWithTD) {
+
+ // if it's a VList classifier
+ // and the gs2 format statement starts with a ,
+ // then remove up to and including the outermost ,
+ // else remove up to and including the tag
+
+ String removeOuterTag = "body>";
+ Element parent = (Element)getParentNode(i); // gets parent of GS2format:
+ if(parent.hasAttribute("match") && (!parent.hasAttribute("mode") || !parent.getAttribute("mode").equals("horizontal"))) {
+ if(startsWithTD) {
+ removeOuterTag = "tr>"; // remove the outermost cell HTML tidy added around the tablecell
+ }
+ }
+
+ //
+ //
+ // lines we want
+ //
+ //
+
+ int end = gs3formatstr_notags.indexOf(removeOuterTag);
+ if(end != -1) {
+ gs3formatstr_notags = gs3formatstr_notags.substring(end+removeOuterTag.length());
+ }
+ int start = gs3formatstr_notags.lastIndexOf(""+removeOuterTag); //closing tag
+ if(start != -1) {
+ gs3formatstr_notags = gs3formatstr_notags.substring(0, start);
+ }
+
+ //System.err.println("@@@@ " + startsWithTD + " - TAG: " + removeOuterTag + " - AFTER REMOVING TAGS:\n" + gs3formatstr_notags);
+
+ return gs3formatstr_notags;
+ }
+
+
+ //**************** ACCESS FUNCTIONS ***************//
+
+ // gs2 format text is the text string that goes into : text
+ private void setGS2Format(int i, String text) {
+ XMLTools.setElementTextValue(getGS2Format(i), text);
+ }
+
+ // gs3FormatStr represents DOM, and must be parsed and appended as sibling to the gs2format element
+ // as . If it fails to parse, nest it in a CDATA element of
+ private boolean setGS3Format(int i, String gs3formatstr) {
+
+ Document ownerDoc = getGS2Format(i).getOwnerDocument();
+ Node gs3format = null;
+ boolean parse_success = false;
+
+
+ String validationMsg = XMLTools.parseDOM(gs3formatstr);
+ if(!validationMsg.startsWith(XMLTools.WELLFORMED)) {
+ // silently add the gs3formatstr in a CDATA block into the root
+
+ gs3formatstr = removeSurroundingTags(gs3formatstr);
+ gs3format = ownerDoc.createElement(GSF_GS3_ROOT_TAG);
+ Node cdataSection = ownerDoc.createCDATASection(gs3formatstr);
+ gs3format.appendChild(cdataSection);
+ parse_success = false;
+ }
+ else {
+
+ // parse DOM into XML
+ Document doc = XMLTools.getDOM(gs3formatstr);
+ Element root = doc.getDocumentElement();
+ gs3format = ownerDoc.importNode(root, true); // an element
+ parse_success = true;
+
+ // System.err.println("@@@@ ROOT:\n" + XMLTools.xmlNodeToString(root));
+ }
+
+ // add gs3format element as sibling to gs2format element
+ Element oldgs3format = getGS3Format(i);
+ if(oldgs3format == null) {
+ getParentNode(i).appendChild(gs3format);
+ // System.err.println("@@@ APPEND");
+ } else {
+ // replace the existing
+ getParentNode(i).replaceChild(gs3format, oldgs3format);
+ // System.err.println("@@@ REPLACE");
+ }
+
+ //http://stackoverflow.com/questions/12524727/remove-empty-nodes-from-a-xml-recursively
+
+ return parse_success;
+ }
+
+ private Node getParentNode(int i) {
+ return gsf_format_gs2_list.item(i).getParentNode();
+ }
+
+ private Element getGS2Format(int i) {
+ return (Element)gsf_format_gs2_list.item(i);
+ }
+
+ private String getGS2FormatString(int i) {
+ return XMLTools.getElementTextValue(getGS2Format(i));
+ }
+
+ private Element getGS3Format(int i) {
+ Element parent = (Element)getParentNode(i);
+ NodeList nl = parent.getElementsByTagName(GSF_GS3_ROOT_TAG);
+ if(nl.getLength() > 0) {
+ return (Element)nl.item(0);
+ }
+ return null;
+ }
+
+ private String getGS3FormatString(int i) {
+ Element gs3format = getGS3Format(i);
+ if(gs3format == null) {
+ return "";
+ } else {
+
+ // if any child is a CData section, return its text as-is. There will be no indenting
+ NodeList children = gs3format.getChildNodes();
+ for(int j = 0 ; j < children.getLength(); j++) {
+ if(children.item(j).getNodeType() == Node.CDATA_SECTION_NODE) {
+ return children.item(j).getNodeValue(); // content of CDataSection
+ }
+ }
+
+ // else we have proper nodes, return indented string
+ StringBuffer sb = new StringBuffer();
+ XMLTools.xmlNodeToString(sb, gs3format, true, " ", 0);
+ return sb.toString();
+ }
+ }
+
+ private int formatConverterMode(int i) {
+ String docOrClassNodeType = "";
+
+ // Given XML of the form:
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+
+ Element parent = (Element)getParentNode(i); // gets parent of GS2format:
+ String nodeType = parent.getAttribute("match"); //e.g. documentNode, classifierNode, or "" if no match attr
+
+ if(nodeType.equals("documentNode")) {
+ return FORMATCONVERTER_DOCUMENTNODE; // convert just the part of the format stmt applicable to documentNodes
+ } else if(nodeType.equals("classifierNode")) {
+ return FORMATCONVERTER_CLASSIFIERNODE; // convert just the part of the format stmt applicable to classifierNodes
+ }
+ return FORMATCONVERTER; // convert the entire format statement
+ }
+
+ private String getLabel(int i) {
+ String label = "";
+
+ // Given XML of the form:
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+
+ // Want to return the label: "browse|search > documentNode|classifierNode [> horizontal]"
+
+ Element parent = (Element)getParentNode(i); // gets parent of GS2format:
+ label = parent.getAttribute("match"); //e.g. documentNode, classifierNode
+
+ if(parent.hasAttribute("mode")) { // e.g. classifierNode mode=horizontal
+ label = label + " > " + parent.getAttribute("mode");
+ }
+
+ Element ancestor = (Element) parent.getParentNode().getParentNode(); // ancestor: |
+ label = ancestor.getTagName() + " > " + label;
+
+ return label;
+ }
+
+ // gs2 format statements have spaces instead of newlines. For display, replace with newlines
+ private String makeLines(String gs2formatstr) {
+ return gs2formatstr.replaceAll(">\\s+<", ">\n<");
+ }
+
+ private String singleLine(String gs2formatstr) {
+ return gs2formatstr.replace(">\n<", "> <"); // put the spaces back
+ }
+
+ private String removeSurroundingTags(String xml)
+ {
+ return xml.replaceAll("<"+GSF_GS3_ROOT_TAG+" xmlns:gsf=(\"|\')http://www.greenstone.org/greenstone3/schema/ConfigFormat(\"|\') xmlns:gslib=(\"|\')http://www.greenstone.org/skinning(\"|\') xmlns:xsl=(\"|\')http://www.w3.org/1999/XSL/Transform(\"|\')>\n?", "").replaceAll("\n?"+GSF_GS3_ROOT_TAG+">", "");//.trim();
+ }
+
+ private String addSurroundingTags(String gs3formatstr) {
+ return "<"+GSF_GS3_ROOT_TAG+" xmlns:gsf='http://www.greenstone.org/greenstone3/schema/ConfigFormat' xmlns:gslib='http://www.greenstone.org/skinning' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>"+gs3formatstr+""+GSF_GS3_ROOT_TAG+">";
+ }
+
+ //*************************FUNCTIONS THAT INTERACT WITH WIDGETS************************//
+ // increment() loads the next values into the dialog
+ private boolean increment() {
+ current_index++;
+ section_label.setText ( getLabel(current_index) );
+ gs2_textarea.setText( makeLines(getGS2FormatString(current_index)) );
+ gs3_textarea.setText( removeSurroundingTags(getGS3FormatString(current_index)) );
+ statusbar.setText("");
+
+ // as we're on a new screen of dialog, need to clear all undo/redo history
+ gs2_textarea.discardAllEdits(); // will disable redo/undo buttons
+ gs3_textarea.discardAllEdits();
+
+ int len = gsf_format_gs2_list.getLength();
+ count_label.setText((current_index+1) + " / " + len);
+ if((current_index+1) == len) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+
+ private void setStatus(String msg) {
+ statusbar.setText(msg);
+ }
+ private void setErrorStatus(String msg) {
+ statusbar.setBackground(Color.red);
+ statusbar.setText(msg);
+ }
+
+
+ public void dispose() {
+ //System.err.println("@@@@ DIALOG CLOSING!");
+ if(dlgResult != OpenCollectionDialog.CANCEL_OPTION) {
+ // Need to remove the siblings of all
+ // Then, get the children of each and add these as siblings of
+
+
+ int len = gsf_format_gs2_list.getLength();
+ for(int k=len-1; k >= 0; k--) {
+ Element parent = (Element)getParentNode(k);
+
+ //parent.normalize();
+
+ NodeList children = parent.getChildNodes();
+
+ // now have to loop/remove nodes in reverse order, since the following loop
+ // modifies the very nodelist we're looping over by removing nodes from it
+
+ int numChildren = children.getLength()-1;
+
+ for(int i=numChildren; i >= 0; i--) {
+ //for(int i = 0; i < children.getLength(); i++) {
+ Node child = children.item(i);
+
+ if(child.getNodeName().equals(GSF_FORMAT_GS2_TAG)) {
+ // if we're dealing with gs2-format-stmts, put their textnode contents in CData sections
+ // http://www.w3schools.com/xml/xml_cdata.asp
+ // This avoids having to look at html-encoded gs2-format tags in the Format pane
+
+ Element gs2format = (Element)child;
+ String gs2formatstr = XMLTools.getElementTextValue(gs2format);
+ gs2formatstr = Codec.transform(gs2formatstr, Codec.ESCAPEDHTML_TO_UNESCAPED);
+
+ Node textNode = XMLTools.getNodeTextNode(gs2format);
+ Node cdataSection = gs2format.getOwnerDocument().createCDATASection(gs2formatstr);
+ gs2format.replaceChild(cdataSection, textNode);
+ }
+ else if(child.getNodeName().equals(GSF_GS3_ROOT_TAG)) {
+
+ // remove GS3 node and append its children to the parent in its place
+ // the elements wouldn't be in the xml_file_doc DOM tree
+ // unless they were valid XML, so don't need to check for validity here
+
+ Node gs3root = child;
+ NodeList gs3_format_lines = gs3root.getChildNodes();
+
+ for(int j = 0; j < gs3_format_lines.getLength(); j++) {
+ Node duplicate = gs3_format_lines.item(j).cloneNode(true);
+ parent.appendChild(duplicate);
+ }
+ gs3root = parent.removeChild(gs3root);
+ gs3root = null; // finished processing
+
+ } // else - skip all nodes other than and
+ }
+ }
+
+ Element root = xml_file_doc.getDocumentElement();
+ //System.err.println("### XML file to write out:\n" + XMLTools.xmlNodeToString(root));
+
+ // Finally, write out the collection xml file
+ String[] nonEscapingTagNames = { StaticStrings.FORMAT_STR, StaticStrings.DISPLAYITEM_STR };
+ XMLTools.writeXMLFile(collect_cfg_file, xml_file_doc, nonEscapingTagNames);
+
+ }
+ super.dispose();
+
+ }
+
+ //******************INNER CLASSES including LISTENERS and STREAMGOBBLERS****************//
+
+ // windowClosing() is called when the user presses the top-right close button the dialog
+ // this means the user wanted to cancel out of the entire Format Conversion Wizard.
+ private class WindowClosingListener extends WindowAdapter {
+ public void windowClosing(WindowEvent e) {
+ dlgResult = OpenCollectionDialog.CANCEL_OPTION;
+ }
+ }
+
+ private class ReconvertListener implements ActionListener {
+ public void actionPerformed(ActionEvent e) {
+ String gs2formatstr = singleLine(gs2_textarea.getText());
+ String errorMsg = processFormatStatement(current_index, gs2formatstr);
+ gs3_textarea.setText( removeSurroundingTags(getGS3FormatString(current_index)) );
+ if(!errorMsg.equals("")) {
+ setErrorStatus(errorMsg);
+ } else {
+ statusbar.setText("");
+ }
+ }
+ }
+
+
+ private class NextButtonListener implements ActionListener {
+ public void actionPerformed(ActionEvent e) {
+ //statusbar.setText("");
+
+ // check if the GS3 format statement is valid XML before storing. If not, let user to decide
+ // whether they want to re-edit it or if they want to keep it as-is, in which case it needs
+ // to be stored as CDATA, which will make it an inactive format statement.
+ // See http://www.w3schools.com/xml/xml_cdata.asp
+ // setGS3Format() already stores invalidXML as CDATA.
+
+ // Check if the GS3 format statement is valid XML before storing. If not, let the
+ // user to decide whether they want to re-edit it or store it as-is and continue
+
+ // user okay-ed the lines currently displayed, store them
+ setGS2Format( current_index, singleLine(gs2_textarea.getText()) );
+ boolean parse_success = setGS3Format( current_index, addSurroundingTags(gs3_textarea.getText()) );
+
+ if(!parse_success) { // invalid XML, warn the user
+ String message = Dictionary.get("FormatConversionDialog.Invalid_XML") + " " + Dictionary.get("FormatConversionDialog.Cancel_Or_Continue_Next");
+ int user_choice = JOptionPane.showConfirmDialog(FormatConversionDialog.this, message, WARNING_TITLE, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
+
+ if(user_choice == JOptionPane.CANCEL_OPTION) {
+ return; // do nothing on this NextButton press. Don't increment. Let user re-adjust invalid XML for GS3 statement.
+ }
+ }
+
+ if(increment()) {
+ repaint();
+ } else {
+ next_button.setEnabled(false);
+ getRootPane().setDefaultButton(accept_all_button);
+
+ }
+
+ }
+ }
+
+ private class CancelButtonListener implements ActionListener {
+ public void actionPerformed(ActionEvent e) {
+ dlgResult = OpenCollectionDialog.CANCEL_OPTION;
+ FormatConversionDialog.this.dispose(); // close dialog
+ }
+ }
+
+
+ private class AcceptAllButtonListener implements ActionListener {
+ public void actionPerformed(ActionEvent e) {
+
+ // user okay-ed the lines, store them
+ setGS2Format(current_index, gs2_textarea.getText());
+ String gs3formatstr = gs3_textarea.getText();
+ boolean parse_success = setGS3Format(current_index, addSurroundingTags(gs3formatstr));
+ String message = "";
+
+ if(!parse_success) { // invalid XML for current format statement, warn the user
+ setErrorStatus(Dictionary.get("FormatConversionDialog.Invalid_XML"));
+
+ message = Dictionary.get("FormatConversionDialog.Invalid_XML") + " " + Dictionary.get("FormatConversionDialog.Cancel_Or_Continue_Next");
+ }
+
+ // Even if the current GS3 format statement is valid XML, the user could have pressed
+ // Accept All at the very start of the FormatConversion dialog too. Check all the
+ // subsequent format statements, and if any have invalid XML, warn user.
+ for(int i = current_index+1; parse_success && i < gsf_format_gs2_list.getLength(); i++) {
+ gs3formatstr = getGS3FormatString(i);
+ String validationMsg = XMLTools.parseDOM(gs3formatstr);
+ if(!validationMsg.startsWith(XMLTools.WELLFORMED)) {
+ parse_success = false;
+ message = Dictionary.get("FormatConversionDialog.Cancel_Or_Accept_All");
+ }
+ }
+
+ if(!parse_success) {
+ int user_choice = JOptionPane.showConfirmDialog(FormatConversionDialog.this, message, WARNING_TITLE, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
+ if(user_choice == JOptionPane.CANCEL_OPTION) {
+ return; // Don't close the dialog. Let the user continue looking at this or subsequent GS3 format statements.
+ }
+ }
+
+ // If we're here, then either the format statements parsed, or the user accepted them anyway
+ FormatConversionDialog.this.dispose(); // close dialog
+ }
+ }
+
+ private class XMLTidyButtonListener implements ActionListener {
+ // run HTML tidy taking input from stdin
+ // http://www.w3.org/People/Raggett/tidy/
+ public void actionPerformed(ActionEvent e) {
+ String gs3formatstr_notags = gs3_textarea.getText();
+
+ // Flag to determine which tags that HTML tidy adds need to be removed
+ boolean startsWithTableCell = (gs3formatstr_notags.trim().toLowerCase().startsWith("= 2) {
+ //System.err.println("@@@ Process exit value: " + process_exitValue);
+ setErrorStatus(Dictionary.get("FormatConversionDialog.Tidy_Failed"));
+ } else {
+ gs3formatstr_notags = htmltidy_string;
+
+ // HTML tidy adds extra HTML markup around the formatstring, so will need to remove it:
+ gs3formatstr_notags = removeHTMLTags(current_index, gs3formatstr_notags, startsWithTableCell);
+
+ // put the XML tags around the gs3 format string again so we can convert it to a DOM object
+ String gs3formatstr = addSurroundingTags(gs3formatstr_notags);
+
+ boolean parse_success = setGS3Format(current_index, gs3formatstr); // converts to DOM object
+
+ // Get indented GS3 format string from DOM object and display it in the text area
+ gs3_textarea.setText( removeSurroundingTags(getGS3FormatString(current_index)) );
+
+ if(parse_success) {
+ statusbar.setText(Dictionary.get("FormatConversionDialog.Tidy_Done"));
+ } else {
+ setErrorStatus(Dictionary.get("FormatConversionDialog.Tidy_Done")
+ + " " + Dictionary.get("FormatConversionDialog.Error_GS3_Format")
+ + " " + Dictionary.get("FormatConversionDialog.Invalid_XML"));
+ }
+ }
+ }
+ }
+
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/FormatPane.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/FormatPane.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/FormatPane.java (revision 31635)
@@ -0,0 +1,247 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.cdm.CollectionDesignManager;
+import org.greenstone.gatherer.cdm.FormatManager;
+import org.greenstone.gatherer.cdm.Format;
+import org.greenstone.gatherer.cdm.Format4gs3Manager;
+import org.greenstone.gatherer.cdm.Format4gs3;
+import org.greenstone.gatherer.cdm.DOMProxyListEntry;
+import org.greenstone.gatherer.cdm.Control;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.shell.GShell;
+import org.greenstone.gatherer.shell.GShellEvent;
+import org.greenstone.gatherer.shell.GShellListener;
+
+public class FormatPane
+extends BaseConfigPane
+implements PreviewButtonOwner,GShellListener{
+
+
+ /** The button for viewing the collection. */
+ private static PreviewButton preview_button = null;
+ private boolean buildCanceled = false;
+
+ /** The constructor. */
+ public FormatPane () {
+ super ();
+ if (Gatherer.GS3) {
+ contents = new String []{ "CDM.GUI.General", "CDM.GUI.SearchMetadata", "CDM.GUI.Formats", "CDM.GUI.Translation" };
+ } else {
+ contents = new String []{ "CDM.GUI.General", "CDM.GUI.SearchMetadata", "CDM.GUI.Formats", "CDM.GUI.Translation", "CDM.GUI.SuperCollection","CDM.GUI.Macros", "CDM.GUI.DepositorMetadata" };
+ }
+ JPanel side_panel = new JPanel ();
+ side_panel.setComponentOrientation(Dictionary.getOrientation());
+ side_panel.setLayout (new BorderLayout ());
+ remove (tree_pane);
+ side_panel.add (tree_pane, BorderLayout.CENTER);
+ JPanel preview_pane = new JPanel ();
+ preview_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ preview_pane.setLayout (new BorderLayout ());
+ preview_pane.setBorder (BorderFactory.createEmptyBorder (5,0,0,0));
+
+ preview_button = new PreviewButton (Dictionary.get ("CreatePane.Preview_Collection"), Dictionary.get ("CreatePane.Preview_Collection_Tooltip"));
+ preview_button.setEnabled (true);
+ preview_button.setVariablePreview (true);
+ preview_button.setOwner (this);
+ preview_pane.add (preview_button, BorderLayout.CENTER);
+
+
+ preview_button.addActionListener (new ActionListener () {
+ public void actionPerformed (ActionEvent event) {
+ if (view != null) {
+ view.loseFocus ();
+ }
+
+ if (buildCanceled){
+ WarningDialog dialog = new WarningDialog ("warning.ShowPreviousCollection", Dictionary.get ("ShowPreviousCollection.Title"), Dictionary.get ("ShowPreviousCollection.Message"), null, false);
+ dialog.display ();
+ dialog.dispose ();
+ }
+ }
+
+ });
+ side_panel.add (preview_pane, BorderLayout.SOUTH);
+
+ add (side_panel, BorderLayout.LINE_START);
+ }
+
+ public void gainFocus (){
+
+ if (Gatherer.c_man.built () && Configuration.library_url != null) {
+ preview_button.setEnabled (true);
+ }
+ else{
+ preview_button.setEnabled (false);
+ }
+
+ }
+ public static void setPreviewButton (boolean ready_to_preview) {
+ // if we're asked to enable the preview button, yet the collection is still
+ // unbuilt (as could happen when someone clicks on a format statement before
+ // building the collection), then shouldn't enable previewing.
+ if(ready_to_preview && !Gatherer.c_man.built()) {
+ ready_to_preview = false;
+ }
+ preview_button.setEnabled (ready_to_preview);
+ }
+
+ /** All implementation of GShellListener must include this method so the listener can be informed of messages from the GShell.
+ * @param event A GShellEvent that contains, amoung other things, the message.
+ */
+ public synchronized void message (GShellEvent event) {
+ // We don't care.
+ }
+
+
+ /** All implementation of GShellListener must include this method so the listener can be informed when a GShell begins its task. Implementation side-effect, not actually used.
+ * @param event A GShellEvent that contains details of the initial state of the GShell before task comencement.
+ */
+ public synchronized void processBegun (GShellEvent event) {
+ buildCanceled = false;
+ }
+
+ /** All implementation of GShellListener must include this method so the listener can be informed when a GShell completes its task.
+ * @param event A GShellEvent that contains details of the final state of the GShell after task completion.
+ */
+ public synchronized void processComplete (GShellEvent event) {
+ if(event.getStatus () == GShell.OK) {
+ if(event.getType () == GShell.BUILD) {
+ buildCanceled = false;
+ }
+ }
+ else {
+ buildCanceled = true;
+ }
+ }
+
+
+
+ protected Control getSubControls (String type) {
+ if(type.equals ("CDM.GUI.General")) {
+ return CollectionDesignManager.general_manager.getControls ();
+ }
+ if (type.equals ("CDM.GUI.SearchMetadata")) {
+ return CollectionDesignManager.searchmetadata_manager.getControls ();
+ }
+ if(type.equals ("CDM.GUI.Formats")) {
+ return CollectionDesignManager.format_manager.getControls ();
+ }
+ if (type.equals ("CDM.GUI.Translation")) {
+ return CollectionDesignManager.translation_manager.getControls ();
+ }
+ if (type.equals ("CDM.GUI.Macros")) {
+ return CollectionDesignManager.macros_manager.getControls ();
+ }
+ if(type.equals ("CDM.GUI.SuperCollection")) {
+ return CollectionDesignManager.supercollection_manager.getControls ();
+ }
+ if(type.equals ("CDM.GUI.DepositorMetadata")) {
+ return CollectionDesignManager.depositormetadata_manager.getControls ();
+ }
+ return null;
+ }
+
+ private int page_type = PreviewButton.HOME_PAGE;
+ private String page_params = "";
+ public int getPageType () {
+ System.err.println ("view type = "+view_type);
+ if (view_type.equals ("CDM.GUI.General") || view_type.equals ("CDM.GUI.Translation")) {
+ page_type = PreviewButton.HOME_PAGE;
+ } else {
+
+ if (Gatherer.GS3 == true) {
+ Format4gs3 current_format = ((Format4gs3Manager.FormatControl)view).getCurrentFormat ();
+ String feature_name = current_format.getFeatureName ();
+
+ if (feature_name.equals ("browse")) {
+ page_type = PreviewButton.CLASSIFIER_PAGE;
+ page_params = "CL1";
+ } else
+ if (feature_name.startsWith ("CL")) {
+ page_type = PreviewButton.CLASSIFIER_PAGE;
+ page_params = current_format.getFeatureName ();
+ }
+ else if (feature_name.startsWith ("display")) {
+ page_type = PreviewButton.DOCUMENT_PAGE;
+ page_params = "HASH01e4da6100fdbb11b7ef244b";
+ }
+ else if (feature_name.equals ("search")) {
+ page_type = PreviewButton.SEARCH_PAGE;
+ page_params = "the";
+ } else { // HOME_PAGE
+ }
+ } else {
+ Format current_format = ((FormatManager.FormatControl)view).getCurrentFormat ();
+ String feature_name = current_format.getFeatureName ();
+ System.err.println ("current format = "+feature_name);
+ if (feature_name.equals ("")) {
+ // must be modifying VList or HList
+ // what should we do???
+ page_type = PreviewButton.CLASSIFIER_PAGE;
+ page_params = "CL1";
+ }
+ if (feature_name.startsWith ("CL")) {
+ page_type = PreviewButton.CLASSIFIER_PAGE;
+ page_params = current_format.getFeatureName ();
+ }
+ else if (feature_name.startsWith ("Document")) {
+ page_type = PreviewButton.DOCUMENT_PAGE;
+ page_params = "HASH01e4da6100fdbb11b7ef244b";
+ }
+ else if (feature_name.equals ("Search")) {
+ page_type = PreviewButton.SEARCH_PAGE;
+ page_params = "the";
+ }
+ }
+ }
+ return page_type;
+ }
+
+ public String getPageParams () {
+ return page_params;
+ }
+
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/GComboBox.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/GComboBox.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/GComboBox.java (revision 31635)
@@ -0,0 +1,398 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.plaf.basic.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.util.Utility;
+
+/**
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.3
+ */
+public class GComboBox
+ extends JComboBox {
+
+ private Color background = null;
+ private Color foreground = null;
+ private Color editable_background = null;
+ private Color editable_foreground = null;
+ private Color selection_background = null;
+ private Color selection_foreground = null;
+
+ private boolean sort_objects = true;
+
+ public GComboBox() {
+ super();
+ this.setComponentOrientation(org.greenstone.gatherer.Dictionary.getOrientation());
+ init();
+ setOpaque(!Utility.isMac());
+ }
+
+ public GComboBox(boolean editable) {
+ super();
+ this.setComponentOrientation(org.greenstone.gatherer.Dictionary.getOrientation());
+ setOpaque(!Utility.isMac());
+ setEditable(editable);
+ init();
+
+ }
+
+ public GComboBox(ArrayList data) {
+ super(data.toArray());
+ this.setComponentOrientation(org.greenstone.gatherer.Dictionary.getOrientation());
+ setOpaque(!Utility.isMac());
+ init();
+ }
+
+ public GComboBox(ArrayList data, boolean editable) {
+ super(data.toArray());
+ this.setComponentOrientation(org.greenstone.gatherer.Dictionary.getOrientation());
+ setOpaque(!Utility.isMac());
+ setEditable(editable);
+ init();
+ }
+
+ public GComboBox(ArrayList data, boolean editable, boolean sorted) {
+ super(data.toArray());
+ this.setComponentOrientation(org.greenstone.gatherer.Dictionary.getOrientation());
+ setOpaque(!Utility.isMac());
+ setEditable(editable);
+ setSorted(sorted);
+ init();
+ }
+
+ public GComboBox(ComboBoxModel model) {
+ super(model);
+ this.setComponentOrientation(org.greenstone.gatherer.Dictionary.getOrientation());
+ setOpaque(!Utility.isMac());
+ init();
+ }
+
+ public GComboBox(ComboBoxModel model, boolean editable) {
+ super(model);
+ this.setComponentOrientation(org.greenstone.gatherer.Dictionary.getOrientation());
+ setOpaque(!Utility.isMac());
+ setEditable(editable);
+ init();
+ }
+
+ public GComboBox(Object data[]) {
+ super(data);
+ this.setComponentOrientation(org.greenstone.gatherer.Dictionary.getOrientation());
+ setOpaque(!Utility.isMac());
+ init();
+ }
+
+ public GComboBox(Object data[], boolean editable) {
+ super(data);
+ this.setComponentOrientation(org.greenstone.gatherer.Dictionary.getOrientation());
+ setOpaque(!Utility.isMac());
+ setEditable(editable);
+ init();
+ }
+
+ public GComboBox(Object data[], boolean editable, boolean sorted) {
+ super(data);
+ this.setComponentOrientation(org.greenstone.gatherer.Dictionary.getOrientation());
+ setOpaque(!Utility.isMac());
+ setEditable(editable);
+ setSorted(sorted);
+ init();
+ }
+
+ public GComboBox(Vector data) {
+ super(data);
+ this.setComponentOrientation(org.greenstone.gatherer.Dictionary.getOrientation());
+ setOpaque(!Utility.isMac());
+ init();
+ }
+
+ public GComboBox(Vector data, boolean editable) {
+ super(data);
+ this.setComponentOrientation(org.greenstone.gatherer.Dictionary.getOrientation());
+ setOpaque(!Utility.isMac());
+ setEditable(editable);
+ init();
+ }
+
+ public void setSorted(boolean sort) {
+ sort_objects = sort;
+ }
+ public int add(Object object) {
+ if (dataModel instanceof Model) {
+ return ((Model) dataModel).add(object);
+ }
+ else {
+ return -1;
+ }
+ }
+
+ public Object get(int index) {
+ return dataModel.getElementAt(index);
+ }
+
+ public void clear() {
+ if (dataModel instanceof Model) {
+ ((Model) dataModel).clear();
+ }
+ }
+
+ public void init() {
+ Model model = new Model();
+ ComboBoxModel old_model = (ComboBoxModel) getModel();
+ setModel(model);
+ setOpaque(true);
+
+ // Restore any data given into our model
+ for(int i = 0; i < old_model.getSize(); i++) {
+ model.add(old_model.getElementAt(i));
+ }
+
+ // Change component
+ UI ui = new UI();
+ setUI(ui);
+ ui.setButtonBackground();
+
+ // Initialization
+ this.background = Configuration.getColor("coloring.collection_tree_background", false);
+ this.foreground = Configuration.getColor("coloring.collection_tree_foreground", false);
+ this.editable_background = Configuration.getColor("coloring.editable_background", false);
+ this.editable_foreground = Configuration.getColor("coloring.editable_foreground", false);
+ this.selection_background = Configuration.getColor("coloring.collection_selection_background", false);
+ this.selection_foreground = Configuration.getColor("coloring.collection_selection_foreground", false);
+ if (isEditable()) {
+ this.setBackground(editable_background);
+ this.setForeground(editable_foreground);
+ }
+ else {
+ this.setBackground(background);
+ this.setForeground(foreground);
+ }
+ setBorder(BorderFactory.createLoweredBevelBorder());
+ setRenderer(new Renderer());
+ }
+
+ /** Overridden to do nothing.
+ * @param background
+ */
+ public void setBackground(Color background) {
+ }
+
+ public void setBackgroundEditableColor(Color editable_background) {
+ this.editable_background = editable_background;
+ }
+
+ public void setBackgroundNonSelectionColor(Color background) {
+ this.background = background;
+ }
+
+ public void setBackgroundSelectionColor(Color selection_background) {
+ this.selection_background = selection_background;
+ }
+
+ public void setEditable(boolean editable) {
+ setEditor(new Editor());
+ super.setEditable(editable);
+ if (isEditable()) {
+ this.setBackground(editable_background);
+ this.setForeground(editable_foreground);
+ }
+ else {
+ this.setBackground(background);
+ this.setForeground(foreground);
+ }
+ }
+
+ public void setTextEditableColor(Color editable_foreground) {
+ this.editable_foreground = editable_foreground;
+ }
+
+ public void setTextNonSelectionColor(Color foreground) {
+ this.foreground = foreground;
+ }
+
+ public void setTextSelectionColor(Color selection_foreground) {
+ this.selection_foreground = selection_foreground;
+ }
+
+
+ private class Editor
+ extends JTextField
+ implements ComboBoxEditor {
+ public Editor() {
+ setOpaque(true);
+ if (isEditable()) {
+ setBackground(editable_background);
+ setForeground(editable_foreground);
+ }
+ else {
+ setBackground(background);
+ setForeground(foreground);
+ }
+ setSelectionColor(selection_background);
+ setSelectedTextColor(selection_foreground);
+ }
+
+ public Component getEditorComponent() {
+ return this;
+ }
+
+ public Object getItem() {
+ return getText();
+ }
+
+ public void setItem(Object item) {
+ if(item != null) {
+ setText(item.toString());
+ }
+ else {
+ setText("");
+ }
+ }
+ }
+
+ private class Model
+ extends DefaultComboBoxModel {
+
+
+ public int add(Object extension) {
+ int position = 0;
+ String extension_str = extension.toString().toLowerCase();
+ while(extension != null && position < getSize()) {
+ String sibling = getElementAt(position).toString().toLowerCase();
+ int order = extension_str.compareTo(sibling);
+ // If we are now less than the sibling, insert here.
+ if(sort_objects && order < 0) {
+ insertElementAt(extension, position);
+ extension = null;
+ }
+ // We are equal to the sibling, thus we already exist in list. Done.
+ else if(order == 0) {
+ extension = null;
+ }
+ // Continue searching.
+ else {
+ position++;
+ }
+ }
+ if(extension != null) {
+ position = getSize();
+ addElement(extension);
+ }
+ return position;
+ }
+
+ public void clear() {
+ removeAllElements();
+ }
+ }
+
+ private class Renderer
+ extends JLabel
+ implements ListCellRenderer {
+ public Renderer() {
+ super("");
+ this.setOpaque(true);
+ this.setComponentOrientation(org.greenstone.gatherer.Dictionary.getOrientation());
+ }
+ public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
+ if(value != null) {
+ this.setText(value.toString());
+ }
+ else {
+ this.setText("");
+ }
+ if(isSelected) {
+ this.setBackground(selection_background);
+ this.setForeground(selection_foreground);
+ }
+ else if (isEditable()) {
+ this.setBackground(editable_background);
+ this.setForeground(editable_foreground);
+ }
+ else {
+ this.setBackground(background);
+ this.setForeground(foreground);
+ }
+ return this;
+ }
+ }
+
+ private class UI
+ extends BasicComboBoxUI {
+ public UI() {
+ super();
+ }
+
+ protected ComboPopup createPopup() {
+ BasicComboPopup popup = new BasicComboPopup(comboBox);
+ // ---- I don't know why this code is here... maybe it is needed for the Mac? ----
+// {
+// public void show() {
+// if(comboBox.getMaximumRowCount() > 0) {
+// Dimension popupSize = new Dimension( comboBox.getWidth(), getPopupHeightForRowCount( comboBox.getMaximumRowCount()));
+// Rectangle popupBounds = new Rectangle( 0, 0, popupSize.width, popupSize.height);
+// scroller.setMaximumSize( popupBounds.getSize() );
+// scroller.setPreferredSize( popupBounds.getSize() );
+// scroller.setMinimumSize( popupBounds.getSize() );
+// list.invalidate();
+// int selectedIndex = comboBox.getSelectedIndex();
+// if ( selectedIndex == -1 ) {
+// list.clearSelection();
+// }
+// else {
+// list.setSelectedIndex( selectedIndex );
+// }
+// list.ensureIndexIsVisible( list.getSelectedIndex() );
+// setLightWeightPopupEnabled( comboBox.isLightWeightPopupEnabled() );
+// show(arrowButton, arrowButton.getSize().width - (popupSize.width + 4), arrowButton.getSize().height);
+// }
+// }
+// };
+ popup.getAccessibleContext().setAccessibleParent(comboBox);
+ return popup;
+ }
+
+ public void setButtonBackground() {
+ arrowButton.setBackground(Configuration.getColor("coloring.button_background", false));
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/GLIButton.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/GLIButton.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/GLIButton.java (revision 31635)
@@ -0,0 +1,85 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import org.greenstone.gatherer.util.Utility;
+
+/** A button that knows whether it should paint itself as opaque or otherwise. */
+public class GLIButton
+ extends JButton {
+
+ public GLIButton() {
+ super();
+ setOpaque(!Utility.isMac());
+ }
+
+ public GLIButton(ImageIcon icon) {
+ super(icon);
+ setOpaque(!Utility.isMac());
+ }
+
+ public GLIButton(String title) {
+ super(title);
+ setOpaque(!Utility.isMac());
+ }
+
+ public GLIButton(String title, ImageIcon icon) {
+ super(title, icon);
+ setOpaque(!Utility.isMac());
+ }
+
+ public GLIButton(ImageIcon icon, String tooltip) {
+ super(icon);
+ setOpaque(!Utility.isMac());
+ setToolTipText(tooltip);
+ }
+ public GLIButton(String title, String tooltip) {
+ super(title);
+ setOpaque(!Utility.isMac());
+ setToolTipText(tooltip);
+ }
+
+ public GLIButton(String title, ImageIcon icon, String tooltip) {
+ super(title, icon);
+ setOpaque(!Utility.isMac());
+ setToolTipText(tooltip);
+ }
+
+
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/GProgressBar.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/GProgressBar.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/GProgressBar.java (revision 31635)
@@ -0,0 +1,207 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ * Copyright (C) New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+
+import javax.swing.*;
+import org.greenstone.gatherer.Dictionary;
+
+
+/** A progress bar that doesn't pack a sad when you feed it longs (such as the sizes of files) which get mangled into negative numbers. I know how it feels, as I often have negative progress. Also tries to be efficent when updating the JProgressBar, so only updates when the value actually changes, which become necessary when you move a large number of files with millions of bytes (where upon shifting a file of a thousand bytes would be pretty insignificant, maybe not even a percent, but would fire several progress updates).
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ */
+public class GProgressBar
+ extends JProgressBar
+{
+ private long cur_value = 0L;
+ private long max_value = 0L;
+
+ /** The previous percentage value. */
+ private int previous = -1;
+
+
+ public GProgressBar()
+ {
+ super(0, 100);
+ this.setComponentOrientation(Dictionary.getOrientation());
+ }
+
+
+ public void addMaximum(long amount)
+ {
+ if (amount > 0) {
+ max_value = max_value + amount;
+ update();
+ }
+ }
+
+
+ public void addValue(long amount)
+ {
+ if (amount > 0) {
+ cur_value = cur_value + amount;
+ update();
+ }
+ }
+
+
+ public void clear()
+ {
+ previous = -1;
+ update();
+ }
+
+
+ public void reset()
+ {
+ cur_value = 0L;
+ max_value = 0L;
+ previous = -1;
+ update();
+ }
+
+
+ public void setIndeterminate(boolean indeterminate)
+ {
+ SwingUtilities.invokeLater(new SafeSetIndeterminateTask(indeterminate));
+ }
+
+
+ public void setString(String label)
+ {
+ SwingUtilities.invokeLater(new SafeSetStringTask(label));
+ }
+
+
+ public void setMaximum(int value)
+ {
+ max_value = value;
+ update();
+ }
+
+
+ public void setValue(int value)
+ {
+ cur_value = value;
+ update();
+ }
+
+
+ /** Only bother updating the progress bar if the percentage has changed. */
+ private void update()
+ {
+ int percentage = -1;
+ if (cur_value == 0L || max_value == 0L) {
+ percentage = 0;
+ }
+ else {
+ long per_value = (cur_value * 100) / max_value;
+ percentage = (int) per_value;
+ if (percentage > 100) {
+ percentage = 100;
+ }
+ }
+
+ if (percentage != previous) {
+ previous = percentage;
+ setString(percentage + "%");
+ SwingUtilities.invokeLater(new SafeSetValueTask(percentage));
+ }
+ }
+
+
+ /** Internal class to call JProgressBar.setIndeterminate safely. */
+ private class SafeSetIndeterminateTask
+ implements Runnable
+ {
+ private boolean indeterminate;
+
+ public SafeSetIndeterminateTask(boolean indeterminate)
+ {
+ this.indeterminate = indeterminate;
+ }
+
+ public void run()
+ {
+ unsafeSetIndeterminate(indeterminate);
+ }
+ }
+
+ /** This method is unsafe, -unless- it is called from the Event Queue (eg. from SafeSetIndeterminateTask). */
+ private void unsafeSetIndeterminate(boolean indeterminate)
+ {
+ super.setIndeterminate(indeterminate);
+ }
+
+
+ /** Internal class to call JProgressBar.setString safely. */
+ private class SafeSetStringTask
+ implements Runnable
+ {
+ private String label;
+
+ public SafeSetStringTask(String label)
+ {
+ this.label = label;
+ }
+
+ public void run()
+ {
+ unsafeSetString(label);
+ }
+ }
+
+ /** This method is unsafe, -unless- it is called from the Event Queue (eg. from SafeSetStringTask). */
+ private void unsafeSetString(String label)
+ {
+ super.setString(label);
+ }
+
+
+ /** Internal class to call JProgressBar.setValue safely. */
+ private class SafeSetValueTask
+ implements Runnable
+ {
+ private int value;
+
+ public SafeSetValueTask(int value)
+ {
+ this.value = value;
+ }
+
+ public void run()
+ {
+ unsafeSetValue(value);
+ }
+ }
+
+ /** This method is unsafe, -unless- it is called from the Event Queue (eg. from SafeSetValueTask). */
+ private void unsafeSetValue(int value)
+ {
+ super.setValue(value);
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/GUIManager.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/GUIManager.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/GUIManager.java (revision 31635)
@@ -0,0 +1,1218 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.datatransfer.*;
+import java.awt.event.*;
+import java.io.File;
+import java.lang.*;
+import java.net.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.filechooser.*;
+import javax.swing.plaf.*;
+import javax.swing.text.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.collection.Collection;
+import org.greenstone.gatherer.collection.CollectionManager;
+import org.greenstone.gatherer.file.FileOpenActionListener;
+import org.greenstone.gatherer.file.WorkspaceTree;
+import org.greenstone.gatherer.gui.metaaudit.MetaAuditFrame;
+import org.greenstone.gatherer.gui.tree.DragTree;
+import org.greenstone.gatherer.metadata.FilenameEncoding;
+import org.greenstone.gatherer.metadata.MetadataSet;
+import org.greenstone.gatherer.metadata.MetadataXMLFileManager;
+import org.greenstone.gatherer.remote.RemoteGreenstoneServer;
+import org.greenstone.gatherer.util.JarTools;
+import org.greenstone.gatherer.util.StaticStrings;
+import org.greenstone.gatherer.util.Utility;
+
+/** The GUIManager is in charge of creating the Gatherer window frame then filling it with the goodness of the view panes. GUIManager not only creates these panes, but allows some messaging between them. Furthermore GUIManager includes functionality from menu driven choices, simply as it was easier to put it here once and have it accessible from all pane children. */
+public class GUIManager
+ extends JFrame
+ implements ActionListener, ChangeListener, WindowFocusListener{
+ /** The download pane contains controls for downloading internet sites. */
+ public DownloadPane download_pane = null;
+ /** The gather pane is more like a file manager where you drag files from one tree to another. */
+ public GatherPane gather_pane = null;
+ /** The enrich pane is used to assign, edit and remove metadata from files within the collection. */
+ public EnrichPane enrich_pane = null;
+ /** The design pane allows you to edit the design of the library in terms of the collection configuration file. - the stuff that requires rebuilding */
+ public DesignPane design_pane = null;
+ /** The create pane contains scripting options for importing and building collections into libraries. */
+ public CreatePane create_pane = null;
+ /** The format pane allows you to edit the design of the library in terms of the collection configuration file. - the stuff that doesn't require rebuilding */
+ public FormatPane format_pane = null;
+
+ public FileOpenActionListener foa_listener = new FileOpenActionListener();
+
+
+ /** A reference to the currently instantiated help window, if any. */
+ private HelpFrame help = null;
+ /** The menu bar. */
+ public MenuBar menu_bar = null;
+ public MetaAuditFrame meta_audit;
+ /** Are certain panes currently locked? */
+ private boolean locked = false;
+ /** The size of the Gatherer window. */
+ private Dimension size = null;
+ /** The panel within the window that other components are placed on. */
+ private JPanel content_pane = null;
+ /** The last view pane selected. */
+ private JPanel previous_pane;
+ /** The main tab pane containing the different views, available here to trap view change events. */
+ private JTabbedPane tab_pane = null;
+ /** A threaded tab changer to try and avoid NPE on exit. */
+ private TabUpdater tab_updater = null;
+
+ final static String newline = "\n";
+ final static String space = " ";
+
+
+ /**Constructor. Enable window events and arranges all other components.
+ * @param size The intial Dimension of the screen.
+ */
+ public GUIManager(Dimension size) {
+ super();
+ this.setComponentOrientation(Dictionary.getOrientation());
+ // Initialization
+ this.help = new HelpFrame();
+ this.size = size;
+
+ this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
+
+ // Add a focus listener to ourselves. Thus if we gain focus when a Modal Dialog should instead have it, we can try to bring the modal dialog to the fore.
+ this.addFocusListener(new GLIGUIFocusListener());
+
+ this.addWindowFocusListener(this);
+
+ // Make the Tool tip hang around for a rediculous amount of time.
+ ToolTipManager.sharedInstance().setDismissDelay(10000);
+
+ // Set up some other UI stuff. (fonts handled in Gatherer.main())
+ UIManager.put("FileChooser.lookInLabelText", Dictionary.get("SaveCollectionBox.Look_In"));
+ UIManager.put("FileChooser.filesOfTypeLabelText", Dictionary.get("SaveCollectionBox.Files_Of_Type"));
+ UIManager.put("FileChooser.fileNameLabelText", Dictionary.get("SaveCollectionBox.File_Name"));
+
+ // JOptionPane options
+ // http://www.java2s.com/Tutorial/Java/0240__Swing/SettingJOptionPanebuttonlabelstoFrench.htm
+ UIManager.put("OptionPane.cancelButtonText", Dictionary.get("General.Cancel"));
+ UIManager.put("OptionPane.noButtonText", Dictionary.get("General.No"));
+ UIManager.put("OptionPane.okButtonText", Dictionary.get("General.OK"));
+ UIManager.put("OptionPane.yesButtonText", Dictionary.get("General.Yes"));
+ }
+
+
+ public void windowGainedFocus(WindowEvent e)
+ {
+ }
+
+
+ public void windowLostFocus(WindowEvent e)
+ {
+ // Save the loaded collection
+ if (Gatherer.c_man != null && Gatherer.c_man.ready() && Gatherer.c_man.getCollection().cdm != null) {
+ Gatherer.c_man.saveCollection();
+ }
+ }
+
+
+ private class GLIGUIFocusListener
+ extends FocusAdapter {
+ public void focusGained(FocusEvent e) {
+ if (ModalDialog.current_modal != null) {
+ ModalDialog.current_modal.makeVisible();
+ ModalDialog.current_modal.toFront();
+ }
+ }
+ }
+
+ /** Any implementation of ActionListener must include this method so that we can be informed when an action has occured. In this case we are listening to actions from the menu-bar, and should react appropriately.
+ * @param event An ActionEvent containing information about the action that has occured.
+ */
+ public void actionPerformed(ActionEvent event) {
+ Object esrc = event.getSource();
+ // *************
+ // File Options.
+ // *************
+ if (esrc == menu_bar.file_associations) {
+ Gatherer.assoc_man.edit();
+ }
+ else if (esrc == menu_bar.file_close) {
+ saveThenCloseCurrentCollection();
+ }
+ else if (esrc == menu_bar.file_delete) {
+ new DeleteCollectionTask().start();
+ }
+ else if (esrc == menu_bar.file_cdimage) {
+ WriteCDImagePrompt wcdip = new WriteCDImagePrompt();
+ wcdip.display();
+ wcdip.destroy();
+ wcdip = null;
+ }
+ else if (esrc == menu_bar.file_exportas) {
+ ExportAsPrompt eap = new ExportAsPrompt();
+ eap.display();
+ eap.destroy();
+ eap = null;
+ }
+ else if (esrc == menu_bar.file_exit) {
+ exit();
+ }
+ else if (esrc == menu_bar.file_new) {
+ new NewCollectionTask().start();
+ }
+ else if (esrc == menu_bar.file_open) {
+ new OpenCollectionTask().start();
+ }
+ else if (esrc == menu_bar.file_options) {
+ new Preferences();
+ }
+ else if (esrc == menu_bar.file_save) {
+ // Very important: make sure metadata values are saved too
+ enrich_pane.stopEditingAndRebuild();
+ // Make sure all the metadata has been saved to file
+ MetadataXMLFileManager.saveMetadataXMLFiles();
+
+ Gatherer.c_man.saveCollection();
+ }
+
+ // *************
+ // Edit Options.
+ // *************
+ else if(esrc == menu_bar.edit_copy) {
+ try {
+ KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
+ // Get the component with selected text as a JTextComponent
+ JTextComponent text = (JTextComponent) kfm.getPermanentFocusOwner();//getFocusOwner();
+ text.copy();
+ }
+ catch (Exception cce) {
+ // If the component is not a text component ignore the copy command
+ DebugStream.println(cce.toString());
+ }
+ }
+ else if(esrc == menu_bar.edit_cut) {
+ try {
+ KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
+ // Get the component with selected text as a JTextComponent
+ JTextComponent text = (JTextComponent) kfm.getPermanentFocusOwner();
+ // Cut the text to the clipboard
+ text.cut();
+ }
+ catch (ClassCastException cce) {
+ // If the component is not a text component ignore the cut command
+ DebugStream.println(cce.toString());
+ }
+ }
+ else if(esrc == menu_bar.edit_paste) {
+ try {
+ KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
+ // Get the component with selected text as a JTextComponent
+ JTextComponent text = (JTextComponent) kfm.getPermanentFocusOwner();
+ // Cut the text to the clipboard
+ text.paste();
+ }
+ catch (ClassCastException cce) {
+ // If the component is not a text component ignore the paste command
+ DebugStream.println(cce.toString());
+ }
+ }
+ else if(esrc == menu_bar.edit_config) {
+ if(Gatherer.c_man.getCollection() != null) {
+
+ ConfigFileEditor configEditor = new ConfigFileEditor();
+ configEditor.setVisible(true);
+ configEditor.setSize(900,700);
+ }
+ }
+
+ // *************
+ // Help Options.
+ // *************
+ else if (esrc == menu_bar.help_general) {
+ HelpFrame.setView("introduction");
+ }
+ else if (esrc == menu_bar.help_download) {
+ HelpFrame.setView("themirrorview");
+ }
+ else if (esrc == menu_bar.help_gather) {
+ HelpFrame.setView("collectingfiles");
+ }
+ else if (esrc == menu_bar.help_enrich) {
+ HelpFrame.setView("enrichingacollection");
+ }
+ else if (esrc == menu_bar.help_design) {
+ HelpFrame.setView("designingacollection");
+ }
+ else if (esrc == menu_bar.help_create) {
+ HelpFrame.setView("producingthecollection");
+ }
+ else if (esrc == menu_bar.help_format) {
+ HelpFrame.setView("formattingacollection");
+ }
+ else if (esrc == menu_bar.help_about) {
+ new AboutDialog(this);
+ }
+ }
+
+
+ /** Any actions that should happen after the display of the Gatherer window can be called here. Currently only updates the browser pane if it is active to work around bug in Mozilla renderer implementation.
+ */
+ public void afterDisplay() {
+ if (download_pane != null) {
+ download_pane.afterDisplay();
+ }
+ enrich_pane.afterDisplay();
+ }
+
+
+ public void closeCurrentCollection()
+ {
+ tab_pane.setSelectedComponent(gather_pane);
+ Gatherer.c_man.closeCollection();
+ FilenameEncoding.closeCollection(); // clear filename-to-encodings map
+ }
+
+
+ public void saveThenCloseCurrentCollection()
+ {
+ Gatherer.c_man.saveCollection();
+ closeCurrentCollection();
+ }
+
+
+ /** This is called when we're absolutely finished with the GLI. It is *not* called when the applet is suspended.
+ */
+ public void destroy()
+ {
+ // Destroying create pane ensures the latest log has been closed
+ if (create_pane != null) {
+ create_pane.destroy();
+ }
+
+ // Deal to help
+ if (help != null) {
+ help.destroy();
+ help = null;
+ }
+ }
+
+
+ /** Enabled events on the window to be trapped, creates all the visual components, then builds the tab and other layouts.
+ */
+ public void display() {
+ content_pane = (JPanel) this.getContentPane();
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ // Enable window-type events to be fired.
+ enableEvents(AWTEvent.WINDOW_EVENT_MASK);
+ // Initialise and layout sub-components, plus other window dressing.
+ try {
+ this.setSize(size);
+
+ // Set the title
+ String collection_title = null;
+ String collection_name = null;
+ if (Gatherer.c_man.ready()) {
+ Collection collection = Gatherer.c_man.getCollection();
+ collection_title = collection.getTitle();
+ collection_name = collection.getGroupQualifiedName(true);
+ collection = null;
+ }
+ setTitle(collection_title, collection_name);
+ collection_title = null;
+ collection_name = null;
+
+ // Pretty corner icon
+ String gsmall_image = "gatherer.png";
+ if (Configuration.fedora_info.isActive()) {
+ gsmall_image = "fli-" + gsmall_image;
+ }
+ if(Gatherer.isGsdlRemote) {
+ gsmall_image = "client-" + gsmall_image;
+ }
+ this.setIconImage(JarTools.getImage(gsmall_image).getImage());
+ // BorderLayout for the main screen. I'll try my best to avoid these in subcomponents as they're space greedy.
+ content_pane.setLayout(new BorderLayout());
+ // Create the menu-bar and stick it up the top.
+ menu_bar = new MenuBar(new MenuListenerImpl());
+ menu_bar.setComponentOrientation(Dictionary.getOrientation());
+
+ //feedback changes
+ //content_pane.add(menu_bar, BorderLayout.NORTH);
+ this.setJMenuBar(menu_bar);
+ // end feedback changes
+
+ // Create the tabbed pane and plop it in the center where it will
+ // expand to consume all available space like any good gas would.
+ tab_pane = new JTabbedPane();
+ tab_pane.setComponentOrientation(Dictionary.getOrientation());
+ tab_pane.addChangeListener(this);
+ tab_pane.setFont(Configuration.getFont("general.font", false));
+
+ if (Configuration.get("workflow.download", true) && Gatherer.isDownloadEnabled) {
+ download_pane = new DownloadPane();
+ // "GUI.Download_Tooltip" is used automatically
+ tab_pane.addTab(Dictionary.get("GUI.Download"), JarTools.getImage("download.gif"), download_pane, Dictionary.get("GUI.Download_Tooltip"));
+ tab_pane.setEnabledAt(tab_pane.indexOfComponent(download_pane), Configuration.get("workflow.download", false));
+ }
+
+ gather_pane = new GatherPane();
+ gather_pane.display();
+ if (Configuration.get("workflow.gather", true)) {
+ // "GUI.Gather_Tooltip" is used automatically
+ tab_pane.addTab(Dictionary.get("GUI.Gather"), JarTools.getImage("gather.gif"), gather_pane, Dictionary.get("GUI.Gather_Tooltip"));
+ tab_pane.setEnabledAt(tab_pane.indexOfComponent(gather_pane), Configuration.get("workflow.gather", false));
+ }
+
+ enrich_pane = new EnrichPane();
+ enrich_pane.display();
+ if (Configuration.get("workflow.enrich", true)) {
+ // "GUI.Enrich_Tooltip" is used automatically
+ tab_pane.addTab(Dictionary.get("GUI.Enrich"), JarTools.getImage("enrich.gif"), enrich_pane, Dictionary.get("GUI.Enrich_Tooltip"));
+ tab_pane.setEnabledAt(tab_pane.indexOfComponent(enrich_pane), false);
+ }
+
+ design_pane = new DesignPane();
+ design_pane.display();
+ if (Configuration.get("workflow.design", true)) {
+ // "GUI.Design_Tooltip" is used automatically
+ if (Configuration.fedora_info.isActive()) {
+ tab_pane.addTab("Plugins", JarTools.getImage("design.gif"), design_pane, Dictionary.get("GUI.Design_Tooltip"));
+ }
+ else {
+ tab_pane.addTab(Dictionary.get("GUI.Design"), JarTools.getImage("design.gif"), design_pane, Dictionary.get("GUI.Design_Tooltip"));
+ }
+
+ tab_pane.setEnabledAt(tab_pane.indexOfComponent(design_pane), false);
+ }
+
+ create_pane = new CreatePane();
+ create_pane.setComponentOrientation(Dictionary.getOrientation());
+ create_pane.display();
+ if (Configuration.get("workflow.create", true)) {
+ // "GUI.Create_Tooltip" is used automatically
+ tab_pane.addTab(Dictionary.get("GUI.Create"), JarTools.getImage("create.gif"), create_pane, Dictionary.get("GUI.Create_Tooltip"));
+ tab_pane.setEnabledAt(tab_pane.indexOfComponent(create_pane), false);
+ }
+
+ format_pane = new FormatPane();
+ format_pane.setComponentOrientation(Dictionary.getOrientation());
+ format_pane.display();
+ if (Configuration.get("workflow.format", true)) {
+ tab_pane.addTab(Dictionary.get("GUI.Format"), JarTools.getImage("format.gif"), format_pane, Dictionary.get("GUI.Format_Tooltip"));
+ tab_pane.setEnabledAt(tab_pane.indexOfComponent(format_pane), false);
+ }
+
+ // The MetaAuditFrame must be created after the gather/enrich panes but before they get focus
+ meta_audit = new MetaAuditFrame();
+ meta_audit.setComponentOrientation(Dictionary.getOrientation());
+ // Select the collect pane if it is available
+ if (tab_pane.indexOfComponent(gather_pane) != -1) {
+ tab_pane.setSelectedComponent(gather_pane);
+ }
+ // Otherwise find the first tab that is enabled and select that.
+ else {
+ for (int i = 0; i < tab_pane.getTabCount(); i++) {
+ if (tab_pane.isEnabledAt(i)) {
+ tab_pane.setSelectedIndex(i);
+ break;
+ }
+ }
+ }
+
+ content_pane.add(tab_pane, BorderLayout.CENTER);
+
+ // Add an extra progress bar at the bottom of every screen when using a remote Greenstone server
+ if (Gatherer.isGsdlRemote) {
+ JPanel remote_greenstone_server_progress_panel = new JPanel();
+ //remote_greenstone_server_progress_panel.setComponentOrientation(Dictionary.getOrientation());
+ JLabel remote_greenstone_server_progress_label = new JLabel(Dictionary.get("RemoteGreenstoneServer.Progress"));
+ //remote_greenstone_server_progress_label.setComponentOrientation(Dictionary.getOrientation());
+ remote_greenstone_server_progress_panel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ remote_greenstone_server_progress_panel.setLayout(new BorderLayout());
+ remote_greenstone_server_progress_panel.add(remote_greenstone_server_progress_label, BorderLayout.LINE_START);
+ remote_greenstone_server_progress_panel.add(Gatherer.remoteGreenstoneServer.getProgressBar(), BorderLayout.CENTER);
+ content_pane.add(remote_greenstone_server_progress_panel, BorderLayout.SOUTH);
+ }
+
+ // Call refresh to update all controls to reflect current collection status.
+ refresh(-1, Gatherer.c_man.ready());
+ }
+ catch (Exception e) {
+ DebugStream.printStackTrace(e);
+ // The GUI failing to build is an app killer
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ public void exit()
+ {
+ exit(0);
+ }
+
+ // some cases we have already saved the collection, but don't want to
+ // override the general.open_collection value here
+ public void exitNoCollectionSave(int exit_status) {
+ // Store the current position and size of the GLI for next time
+ Configuration.setBounds("general.bounds", true, getBounds());
+
+ // Save configuration
+ Configuration.save();
+
+ // Hide the main window
+ setVisible(false);
+
+ // If we're running as an applet we don't quit here (we quit when the browser calls GathererApplet.destroy())
+ if (!Gatherer.isApplet) {
+ Gatherer.exit(exit_status);
+ }
+
+ }
+ /** This method ensures that all the things needing saving are saved before Gatherer.exit() is called.
+ */
+ public void exit(int exit_status)
+ {
+ boolean collection_open = false;
+ // If we have a collection open remember it for next time, then save it and close it
+ if (Gatherer.c_man.ready()) {
+ // but we no longer remember open collections for remote GLI, as this causes problems
+ // in applets when other users on a machine want to use an applet and GLI wants to load
+ // a collection left open from a previous session and *requires* the old user to authenticate
+ if(Gatherer.isGsdlRemote) {
+ Configuration.setString("general.open_collection"+Configuration.gliPropertyNameSuffix(),
+ true, "");
+ } else {
+ Configuration.setString("general.open_collection"+Configuration.gliPropertyNameSuffix(),
+ true, CollectionManager.getLoadedCollectionColFilePath());
+ }
+ saveThenCloseCurrentCollection();
+ collection_open = true;
+ }
+ else {
+ // if there was no open collection, then write out the collect dir path for next time
+ // IF this is not the default collect directory and not a remote GLI/applet
+ if(Gatherer.isGsdlRemote || Gatherer.getCollectDirectoryPath().equals(
+ Gatherer.getDefaultGSCollectDirectoryPath(true))) {
+ Configuration.setString("general.open_collection"+Configuration.gliPropertyNameSuffix(),
+ true, "");
+ } else {
+ Configuration.setString("general.open_collection"+Configuration.gliPropertyNameSuffix(),
+ true, Gatherer.getCollectDirectoryPath());
+ }
+ }
+
+
+ // Basically, if GLI coldir != gsdlsite_colhome, we ask them if they want to save GLI coldir as gsdlsite_colhome
+ // If gsdlsite_colhome = "" (meaning default GS coldir) and GLI is also set to defaultGScoldir, no saving necessary.
+ // We ONLY do this for the local included apache web server, since the issue revolves around gsdlsite.cfg containing
+ // the global collecthome when using the apache web server. This is not an issue for remote GS or server.exe (the latter
+ // has separate config files specifying collecthome for when GLI is running whereas when just the server.exe is running).
+ if(!Gatherer.isGsdlRemote && !Gatherer.isPersistentServer()) {
+
+ String defaultColDir = Gatherer.getDefaultGSCollectDirectoryPath(false); // no filesep at end
+ String gliColDir = Gatherer.getCollectDirectoryPath();
+ String serverColDir = Gatherer.gsdlsite_collecthome;
+
+ boolean showSettingsDlg = true;
+ // if collectdir was changed during GLI session (to something different from what's in gsdlsite), need to ask what to store
+ String gsdlsiteColdir = Gatherer.gsdlsite_collecthome.replace("\"", "");
+ if(gliColDir.equals(gsdlsiteColdir+File.separator)) {
+ showSettingsDlg = false;
+ } else if(gsdlsiteColdir.equals("") // both set to default coldir
+ && gliColDir.equals(defaultColDir+File.separator)) {
+ showSettingsDlg = false;
+ }
+ if(showSettingsDlg) {
+
+ // else we will be showing the Collect Directory Settings Dialog
+ if(gliColDir.endsWith(File.separator)) {
+ gliColDir = gliColDir.substring(0, gliColDir.length()-1);
+ }
+
+ if(serverColDir.equals("")) {
+ serverColDir = defaultColDir;
+ }
+ collectDirSettingsDialog(defaultColDir, serverColDir, gliColDir, collection_open);
+ }
+ }
+
+ // Store the current position and size of the GLI for next time
+ Configuration.setBounds("general.bounds", true, getBounds());
+
+ // Save configuration
+ Configuration.save();
+
+ // Hide the main window
+ setVisible(false);
+
+ // If we're running as an applet we don't quit here (we quit when the browser calls GathererApplet.destroy())
+ if (!Gatherer.isApplet) {
+ Gatherer.exit(exit_status);
+ }
+ }
+
+ public void collectDirSettingsDialog(final String defaultColDir,
+ final String from, final String to, final boolean collection_open)
+ {
+ final JDialog colHomeDialog
+ = new JDialog(this, Dictionary.get("GUI.CollectHome.title"), true); // this = Gatherer.g_man
+ colHomeDialog.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
+
+
+ JRadioButton gliYes = new JRadioButton(Dictionary.get("General.Yes"), true);
+ // the default selection for GLI, collecthome=to
+ JRadioButton serverYes = new JRadioButton(Dictionary.get("General.Yes")); // not selected for the server
+ JRadioButton gliNo = null;
+ JRadioButton gliThirdOption = null;
+ JRadioButton serverNo = null;
+ JRadioButton serverThirdOption = null;
+
+ if(from.equals(defaultColDir)) {
+ gliNo = new JRadioButton(Dictionary.get("GUI.CollectHome.resetToDefault"));
+ serverNo = new JRadioButton(Dictionary.get("GUI.CollectHome.leaveAtDefault"), true);
+ // default selection for server, collecthome=from
+ } else {
+ gliNo = new JRadioButton(Dictionary.get("General.No"));
+ serverNo = new JRadioButton(Dictionary.get("General.No"), true); // default selection for server
+ if(!to.equals(defaultColDir)) { // neither from nor to is the default GS collect dir, so give them that as the third option
+ gliThirdOption = new JRadioButton(Dictionary.get("GUI.CollectHome.reset"));
+ serverThirdOption = new JRadioButton(Dictionary.get("GUI.CollectHome.reset"));
+ }
+ }
+
+ JPanel gliPanel = new JPanel(); // flowlayout by default
+ ButtonGroup gliGroup = new ButtonGroup();
+ JPanel serverPanel = new JPanel(); // flowlayout by default
+ ButtonGroup serverGroup = new ButtonGroup();
+
+ gliGroup.add(gliYes);
+ gliPanel.add(gliYes);
+ serverGroup.add(serverYes);
+ serverPanel.add(serverYes);
+
+ gliGroup.add(gliNo);
+ gliPanel.add(gliNo);
+ serverGroup.add(serverNo);
+ serverPanel.add(serverNo);
+
+ if(gliThirdOption != null) {
+ gliThirdOption = new JRadioButton("Reset to default");
+ serverThirdOption = new JRadioButton("Reset to default");
+
+ gliGroup.add(gliThirdOption);
+ gliPanel.add(gliThirdOption);
+ serverGroup.add(serverThirdOption);
+ serverPanel.add(serverThirdOption);
+ }
+
+ // vars need to be final to use them in the actionlistener below
+ final JRadioButton gli_yes = gliYes;
+ final JRadioButton gli_no = gliNo;
+ final JRadioButton gli_optional = gliThirdOption;
+ final JRadioButton server_yes = serverYes;
+ final JRadioButton server_no = serverNo;
+ final JRadioButton server_optional = serverThirdOption;
+
+ JButton okButton = new JButton(Dictionary.get("General.OK"));
+ okButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ // store the option chosen for GLI
+ String gliColDir = to;
+ if(gli_optional != null && gli_optional.isSelected()) {
+ // defaultColDir
+ gliColDir = "";
+ } else if(gli_yes.isSelected()) {
+ gliColDir = to;
+ } else if (gli_no.isSelected()) {
+ gliColDir = from;
+ }
+ if(defaultColDir.equals(gliColDir)) {
+ gliColDir = "";
+ }
+ if(!(collection_open && gli_yes.isSelected())) { // don't overwrite open collections
+ Configuration.setString(
+ "general.open_collection"+Configuration.gliPropertyNameSuffix(),
+ true, gliColDir);
+ }
+
+ // store the option chosen for the server's settings
+ String serverColDir = from;
+ if(server_optional != null && server_optional.isSelected()) {
+ // defaultColDir
+ serverColDir = null;
+ } else if(server_yes.isSelected()) {
+ serverColDir = to;
+ } else if (server_no.isSelected()) {
+ serverColDir = from;
+ }
+ if(serverColDir != null && defaultColDir.equals(serverColDir)) {
+ serverColDir = null;
+ }
+
+ if(serverColDir != null) {
+ serverColDir = serverColDir.replace("\"","");
+ serverColDir = "\""+serverColDir+"\"";
+ }
+
+ Gatherer.gsdlsite_collecthome = Utility.updatePropertyConfigFile(
+ Gatherer.getGsdlSiteConfigFile(), "collecthome", serverColDir);
+
+ colHomeDialog.dispose();
+ }
+ });
+
+ //String[] dirs = {from, to};
+ //JLabel message = new JLabel(Dictionary.get("GUI.CollectHome.message", dirs));
+ JLabel message = new JLabel(Dictionary.get("GUI.CollectHome.message"));
+ JLabel fromDirLine = new JLabel(Dictionary.get("GUI.CollectHome.dir", from));
+ fromDirLine.setBorder(BorderFactory.createEmptyBorder(0,25,0,0)); // padding
+ JLabel toLine = new JLabel(Dictionary.get("GUI.CollectHome.to"));
+ JLabel toDirLine = new JLabel(Dictionary.get("GUI.CollectHome.dir", to));
+ toDirLine.setBorder(BorderFactory.createEmptyBorder(0,25,0,0)); // padding
+ JLabel gliOption = new JLabel(Dictionary.get("GUI.CollectHome.gli"));
+ JLabel serverOption = new JLabel(Dictionary.get("GUI.CollectHome.server"));
+
+ JPanel mainPanel = new JPanel();
+ mainPanel.setLayout(new GridLayout(9, 1));
+ mainPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); // padding
+ mainPanel.add(message);
+ mainPanel.add(fromDirLine);
+ mainPanel.add(toLine);
+ mainPanel.add(toDirLine);
+ mainPanel.add(gliOption);
+ mainPanel.add(gliPanel);
+ mainPanel.add(serverOption);
+ mainPanel.add(serverPanel);
+ mainPanel.add(okButton);
+ Container c = colHomeDialog.getContentPane();
+ c.setLayout(new BorderLayout());
+ c.add(mainPanel, BorderLayout.CENTER);
+ colHomeDialog.getRootPane().setDefaultButton(okButton);
+
+ colHomeDialog.pack();
+ colHomeDialog.setVisible(true);
+ }
+
+
+ /** This method is called when the collection is being built, and is used to disable all controls in all pane which could change the state of the collection.
+ */
+ public void lockCollection(boolean import_stage, boolean lock)
+ {
+ locked = lock;
+
+ if (import_stage) {
+ int gather_pos = tab_pane.indexOfComponent(gather_pane);
+ if (gather_pos != -1) {
+ tab_pane.setEnabledAt(gather_pos, !lock);
+ }
+ int enrich_pos = tab_pane.indexOfComponent(enrich_pane);
+ if (enrich_pos != -1) {
+ tab_pane.setEnabledAt(enrich_pos, !lock);
+ }
+ }
+
+ int design_pos = tab_pane.indexOfComponent(design_pane);
+ if (design_pos != -1) {
+ tab_pane.setEnabledAt(design_pos, !lock);
+ }
+ }
+
+
+ public void modeChanged(int mode) {
+ // Set the title
+ String collection_title = null;
+ String collection_name = null;
+ if (Gatherer.c_man.ready()) {
+ Collection collection = Gatherer.c_man.getCollection();
+ collection_title = collection.getTitle();
+ collection_name = collection.getGroupQualifiedName(true);
+ collection = null;
+ }
+ setTitle(collection_title, collection_name);
+ collection_title = null;
+ collection_name = null;
+ // Now pass on the message to anyone who cares
+ if (download_pane != null) {
+ download_pane.modeChanged(mode);
+ }
+ if (gather_pane != null) {
+ gather_pane.modeChanged(mode);
+ }
+ if (enrich_pane != null) {
+ enrich_pane.modeChanged(mode);
+ }
+ if (design_pane != null) {
+ design_pane.modeChanged(mode);
+ }
+ if (create_pane != null) {
+ create_pane.modeChanged(mode);
+ }
+ if (format_pane != null) {
+ format_pane.modeChanged(mode);
+ }
+ }
+
+
+ public void refresh(int refresh_reason, boolean collection_loaded)
+ {
+ // Set the collection information in the title bar
+ if (collection_loaded) {
+ Collection collection = Gatherer.c_man.getCollection();
+ setTitle(collection.getTitle(), collection.getGroupQualifiedName(true));
+ }
+ else {
+ setTitle(null, null);
+ }
+
+ // Update the menu bar
+ menu_bar.refresh(refresh_reason, collection_loaded);
+
+ // Update the loaded panes
+ if (download_pane != null) {
+ download_pane.refresh(refresh_reason, collection_loaded);
+ }
+ if (gather_pane != null) {
+ gather_pane.refresh(refresh_reason, collection_loaded);
+ }
+ if (enrich_pane != null) {
+ enrich_pane.refresh(refresh_reason, collection_loaded);
+ }
+ if (design_pane != null) {
+ design_pane.refresh(refresh_reason, collection_loaded);
+ }
+ if (create_pane != null) {
+ create_pane.refresh(refresh_reason, collection_loaded);
+ }
+ if (format_pane != null) {
+ format_pane.refresh(refresh_reason, collection_loaded);
+ }
+
+ // Now enable tabs as necessary. Do this on event queue to prevent crazy NPEs
+ if (!locked) {
+ if (tab_updater == null) {
+ tab_updater = new TabUpdater(tab_pane, collection_loaded);
+ }
+ else {
+ tab_updater.setReady(collection_loaded);
+ }
+ SwingUtilities.invokeLater(tab_updater);
+ }
+ }
+
+
+ public void refreshCollectionTree(int refresh_reason)
+ {
+ if (gather_pane != null) {
+ gather_pane.refreshCollectionTree(refresh_reason);
+ }
+ }
+
+
+ public void refreshWorkspaceTree(int refresh_reason)
+ {
+ if (gather_pane != null) {
+ gather_pane.refreshWorkspaceTree(refresh_reason);
+ }
+ }
+
+ public void refreshWorkspaceTreeGreenstoneCollections()
+ {
+ refreshWorkspaceTree(WorkspaceTree.LIBRARY_CONTENTS_CHANGED);
+ }
+
+ /** Specifies whether a certain tab is enabled or not. */
+ private void setTabEnabled(String rawname, boolean state) {
+ // Retrieve the dictionary based name.
+ String name = Dictionary.get("GUI." + rawname);
+ int index = tab_pane.indexOfTab(name);
+ // Of course we may not have this tab available.
+ if(index != -1) {
+ // Some tabs are also dependant on if a collection is ready
+ Component component = tab_pane.getComponentAt(index);
+ if(component == enrich_pane || component == design_pane || component == create_pane || component == format_pane) {
+ tab_pane.setEnabledAt(index, state && Gatherer.c_man != null && Gatherer.c_man.ready());
+ }
+ else {
+ tab_pane.setEnabledAt(index, state);
+ }
+ // If this was the currently selected tab and it is now disabled, change the view to the first enabled tab.
+ if(tab_pane.getSelectedIndex() == index && !state) {
+ boolean found = false;
+ for(int i = 0; !found && i < tab_pane.getTabCount(); i++) {
+ if(tab_pane.isEnabledAt(i)) {
+ tab_pane.setSelectedIndex(i);
+ found = true;
+ }
+ }
+ // If there are no tabs enabled, which should be impossible, then select the first tab
+ if(!found) {
+ tab_pane.setSelectedIndex(0);
+ }
+ }
+ }
+ }
+
+ /** Change the string shown in the title bar of the main gui frame. If either value is null, the 'No Collection' string is shown instead.
+ * @param title
+ * @param name
+ */
+ public void setTitle(String title, String name) {
+
+ // Finally display the collection name in the title bar.
+ StringBuffer title_buffer = new StringBuffer(Configuration.getApplicationTitle());
+ title_buffer.append(StaticStrings.SPACE_CHARACTER);
+ title_buffer.append(Gatherer.PROGRAM_VERSION);
+ title_buffer.append(StaticStrings.SPACE_CHARACTER);
+ title_buffer.append(StaticStrings.SPACE_CHARACTER);
+ // Server version information
+ title_buffer.append(Gatherer.getServerVersionAsString());
+ title_buffer.append(StaticStrings.SPACE_CHARACTER);
+ title_buffer.append(StaticStrings.SPACE_CHARACTER);
+ // Describe the current user mode
+ title_buffer.append(StaticStrings.MODE_STR);
+ title_buffer.append(Configuration.getModeAsString());
+ title_buffer.append(StaticStrings.SPACE_CHARACTER);
+ title_buffer.append(StaticStrings.SPACE_CHARACTER);
+ // Now for the current collection
+ title_buffer.append(StaticStrings.COLLECTION_STR);
+ if (title != null && name != null) {
+ title_buffer.append(title);
+ title_buffer.append(StaticStrings.SPACE_CHARACTER);
+ title_buffer.append(StaticStrings.OPEN_PARENTHESIS_CHARACTER);
+ title_buffer.append(name);
+ title_buffer.append(StaticStrings.CLOSE_PARENTHESIS_CHARACTER);
+ }
+ else {
+ title_buffer.append(Dictionary.get("Collection.No_Collection"));
+ }
+ this.setTitle(title_buffer.toString());
+ title_buffer = null;
+ }
+
+
+ private class OpenCollectionTask
+ extends Thread
+ {
+ public void run()
+ {
+ String collection_file_path = showOpenCollectionDialog();
+
+ // User has selected a collection to open
+ if (collection_file_path != null) {
+ // If there is already a collection open, save and close it
+ if (Gatherer.c_man.ready()) {
+ saveThenCloseCurrentCollection();
+ }
+
+ // Open the selected collection
+ Gatherer.c_man.loadCollection(collection_file_path);
+ }
+ }
+ }
+
+
+ /** When the load collection option is chosen this method is called to produce the modal file load prompt.
+ */
+ private String showOpenCollectionDialog()
+ {
+ OpenCollectionDialog dialog = new OpenCollectionDialog();
+ dialog.setComponentOrientation(Dictionary.getOrientation());
+ if (dialog.display() == OpenCollectionDialog.OK_OPTION) {
+ return dialog.getFileName();
+ }
+
+ // User must have cancelled the action
+ return null;
+ }
+
+
+ /** When called this method causes the MetadataAuditTable to display a nice dialog box which contains all the metadata assigned in the collection.
+ */
+ public void showMetaAuditBox() {
+ wait(true);
+ meta_audit.display();
+ wait(false);
+ }
+
+
+ private class NewCollectionTask
+ extends Thread
+ {
+ public void run()
+ {
+ // Create the collection details prompt from new collection prompt
+ NewCollectionDetailsPrompt ncd_prompt = new NewCollectionDetailsPrompt();
+
+ // Create the new collection (if not cancelled) in a new thread.
+ if (!ncd_prompt.isCancelled()) {
+ // If there is already a collection open, save and close it.
+ if (Gatherer.c_man.ready()) {
+ saveThenCloseCurrentCollection();
+ }
+
+ // Create new collection.
+ Gatherer.c_man.createCollection(ncd_prompt.getDescription(), Configuration.getEmail(), ncd_prompt.getName(), ncd_prompt.getTitle(), ncd_prompt.getBase(), new ArrayList());
+ ncd_prompt.dispose();
+ }
+
+ // Done
+ ncd_prompt = null;
+ }
+ }
+
+
+ private class DeleteCollectionTask
+ extends Thread
+ {
+ public void run()
+ {
+ // The rest is handled by the DeleteCollectionPrompt
+ DeleteCollectionPrompt dc_prompt = new DeleteCollectionPrompt();
+ if (dc_prompt.display()) {
+ //closeCurrentCollection();
+ }
+ dc_prompt.destroy();
+ dc_prompt = null;
+ }
+ }
+
+
+ /** Any implementation of ChangeListener must include this method so we can be informed when the state of one of the registered objects changes. In this case we are listening to view changes within the tabbed pane.
+ * @param event A ChangeEvent containing information about the event that fired this call.
+ */
+ public void stateChanged(ChangeEvent event)
+ {
+ if (previous_pane != null) {
+ if (previous_pane == gather_pane) {
+ gather_pane.loseFocus();
+ }
+ else if (previous_pane == enrich_pane) {
+ enrich_pane.loseFocus();
+ }
+ else if (previous_pane == design_pane) {
+ design_pane.loseFocus();
+ }
+ else if (previous_pane == create_pane) {
+ create_pane.loseFocus();
+ }
+ else if (previous_pane == format_pane) {
+ format_pane.loseFocus();
+ }
+ }
+
+ menu_bar.tabSelected(tab_pane.getSelectedIndex());
+ int selected_index = tab_pane.getSelectedIndex();
+ if (selected_index == tab_pane.indexOfComponent(download_pane)) {
+ download_pane.gainFocus();
+ }
+ else if (selected_index == tab_pane.indexOfComponent(gather_pane)) {
+ gather_pane.gainFocus();
+ }
+ else if (selected_index == tab_pane.indexOfComponent(enrich_pane)) {
+ enrich_pane.gainFocus();
+ }
+ else if (selected_index == tab_pane.indexOfComponent(design_pane)) {
+ design_pane.gainFocus();
+ }
+ else if (selected_index == tab_pane.indexOfComponent(create_pane)) {
+ create_pane.gainFocus();
+ }
+ else if (selected_index == tab_pane.indexOfComponent(format_pane)) {
+ format_pane.gainFocus();
+ }
+
+ previous_pane = (JPanel) tab_pane.getSelectedComponent();
+
+ }
+
+
+ private MouseListener mouse_blocker_listener = new MouseAdapter() {};
+
+ public void updateUI()
+ {
+ JPanel pane = (JPanel) getContentPane();
+ pane.updateUI();
+ // Also update all of the tabs according to workflow.
+ workflowUpdate("Download", Configuration.get("workflow.download", false));
+ workflowUpdate("Gather", Configuration.get("workflow.gather", false));
+ workflowUpdate("Enrich", Configuration.get("workflow.enrich", false));
+ workflowUpdate("Design", Configuration.get("workflow.design", false));
+ workflowUpdate("Create", Configuration.get("workflow.create", false));
+ workflowUpdate("Format", Configuration.get("workflow.format", false));
+ }
+
+ public void wait(boolean waiting) {
+ Component glass_pane = getGlassPane();
+ if(waiting) {
+ // Show wait cursor.
+ glass_pane.addMouseListener(mouse_blocker_listener);
+ glass_pane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+ glass_pane.setVisible(true);
+ }
+ else {
+ // Hide wait cursor.
+ glass_pane.setVisible(false);
+ glass_pane.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+ glass_pane.removeMouseListener(mouse_blocker_listener);
+ }
+ glass_pane = null;
+ }
+
+ public void workflowUpdate(String raw, boolean state) {
+ WorkflowUpdater task = new WorkflowUpdater(raw, state);
+ SwingUtilities.invokeLater(task);
+ task = null;
+ }
+
+
+ /**Overridden from JFrame so we can exit safely when window is closed (or destroyed).
+ * @param event A WindowEvent containing information about the event that fired this call.
+ */
+ protected void processWindowEvent(WindowEvent event) {
+ if(event.getID() == WindowEvent.WINDOW_CLOSING) {
+ exit();
+ }
+ }
+
+
+ /** Listens to actions upon the menu bar, and if it detects a click over the help menu brings the help window to the front if it has become hidden.
+ */
+ private class MenuListenerImpl
+ implements MenuListener {
+ /** Called whenever a popup menu is hidden, but we don't care.
+ * @param e Some MenuEvent that we could care less about.
+ */
+ public void menuCanceled(MenuEvent e) {
+ }
+ /** Called whenever a menu header (ie button) becomes unselected, but we don't care.
+ * @param e Some MenuEvent that we could care less about.
+ */
+ public void menuDeselected(MenuEvent e) {
+ }
+ /** This method, when a menu is first opened, is the only one we respond to by bringing the help window to the front if possible, but only if there is a help window and the help menu is the one opening.
+ * @param e The MenuEvent whose source is checked.
+ */
+ public void menuSelected(MenuEvent e) {
+ if(e.getSource() == menu_bar.help) {
+ if(menu_bar.help.isSelected()) {
+ menu_bar.help.doClick(10);
+ }
+ }
+ else if(e.getSource() == menu_bar.edit) { // someone clicked the toplevel Edit menu
+ // gray out the Edit Config File menu if there's no collection currently open.
+ if(Gatherer.c_man.getCollection() == null) {
+ menu_bar.edit_config.setEnabled(false);
+ }
+ else { // don't forget to reenable the Edit ConfigXML menu item when applicable
+ menu_bar.edit_config.setEnabled(true);
+ }
+ }
+
+ }
+ }
+
+ private class TabUpdater
+ implements Runnable {
+ private boolean ready = false;
+ private int download_pos = -1;
+ private int enrich_pos = -1;
+ private int design_pos = -1;
+ private int create_pos = -1;
+ private int format_pos = -1;
+ private int export_pos = -1;
+ private JTabbedPane tab_pane = null;
+
+ public TabUpdater(JTabbedPane tab_pane, boolean ready) {
+ this.ready = ready;
+ this.tab_pane = tab_pane;
+ download_pos = tab_pane.indexOfComponent(download_pane);
+ enrich_pos = tab_pane.indexOfComponent(enrich_pane);
+ design_pos = tab_pane.indexOfComponent(design_pane);
+ create_pos = tab_pane.indexOfComponent(create_pane);
+ format_pos = tab_pane.indexOfComponent(format_pane);
+ }
+
+ public void run()
+ {
+ if (download_pos != -1) {
+ if (ready) {
+ tab_pane.setEnabledAt(download_pos, Configuration.get("workflow.download", false));
+ }
+ else {
+ tab_pane.setEnabledAt(download_pos, Configuration.get("workflow.download", true));
+ }
+ }
+ if (enrich_pos != -1) {
+ tab_pane.setEnabledAt(enrich_pos, ready && Configuration.get("workflow.enrich", false));
+ }
+ if (design_pos != -1) {
+ tab_pane.setEnabledAt(design_pos, ready && Configuration.get("workflow.design", false) && Configuration.getMode() > Configuration.ASSISTANT_MODE);
+ }
+ if (create_pos != -1) {
+ tab_pane.setEnabledAt(create_pos, ready && Configuration.get("workflow.create", false));
+ }
+ if (format_pos != -1) {
+ tab_pane.setEnabledAt(format_pos, ready && Configuration.get("workflow.format", false) && Configuration.getMode() > Configuration.ASSISTANT_MODE);
+ }
+ }
+
+ public void setReady(boolean ready) {
+ this.ready = ready;
+ }
+ }
+
+ private class WorkflowUpdater
+ implements Runnable {
+ private boolean state;
+ private String raw;
+ public WorkflowUpdater(String raw, boolean state) {
+ this.raw = raw;
+ this.state = state;
+ }
+ public void run() {
+ setTabEnabled(raw, state);
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/GUIUtils.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/GUIUtils.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/GUIUtils.java (revision 31635)
@@ -0,0 +1,69 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import java.util.*;
+
+public class GUIUtils {
+
+ /** The name of the mouse listener that initiates editing on a double click. */
+ static final private String SINGLE_CLICK_LISTENER = "SingleClickListener";
+
+ /** Neat method to disable file renaming in filechooser.
+ * Thanks to: vladi21 from www.experts-exchange.com
+ */
+ static public void disableRename(Component c) {
+ if (c instanceof JList){
+ EventListener[] listeners=c.getListeners(MouseListener.class);
+ for(int i=0; listeners != null && i < listeners.length; i++) {
+ if (listeners[i].toString().indexOf(SINGLE_CLICK_LISTENER) != -1) {
+ c.removeMouseListener((MouseListener)listeners[i]);
+ }
+ }
+ return;
+ }
+ if (c instanceof Container) {
+ Component[] children = null;
+ children = ((Container)c).getComponents();
+ if (children != null) {
+ for(int i = 0; children != null && i < children.length; i++) {
+ disableRename(children[i]);
+ }
+ }
+ }
+ }
+
+
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/GatherPane.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/GatherPane.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/GatherPane.java (revision 31635)
@@ -0,0 +1,442 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.tree.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.collection.CollectionTree;
+import org.greenstone.gatherer.collection.CollectionTreeNode;
+import org.greenstone.gatherer.file.FileNode;
+import org.greenstone.gatherer.file.FileOpenActionListener;
+import org.greenstone.gatherer.file.FileQueue;
+import org.greenstone.gatherer.file.FileSystemModel;
+import org.greenstone.gatherer.file.RecycleBin;
+import org.greenstone.gatherer.file.WorkspaceTree;
+import org.greenstone.gatherer.file.WorkspaceTreeNode;
+import org.greenstone.gatherer.gui.tree.DragTree;
+import org.greenstone.gatherer.util.DragComponent;
+import org.greenstone.gatherer.util.DragGroup;
+import org.greenstone.gatherer.util.JarTools;
+import org.greenstone.gatherer.util.Utility;
+
+
+/** The collection pane is analogous with a file manager. It is there that the user chooses which files to include in their collection and what structure the file hierarchy should take. The later aspect is not important for the Greenstone Suite, but is usefull for grouping files for ease of metadata markup. The view essentially consists of two file trees, one denoting the entire source workspace and the other the files within your collection. The trees themselves have a title bar at the top, a filter control at the bottom, and are coloured to indicate activity (grey for disabled). The remainder of the screen is taken by a status area, to indicate current file job progress during copying etc, and three buttons for controlling features of the view.
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.3
+ */
+public class GatherPane
+ extends JPanel
+ implements ActionListener
+{
+ private CardLayout card_layout = null;
+ private JPanel card_pane = null;
+ /** The name of the panel containing the "collection loaded" (collection tree) card. */
+ private String COLLECTION_LOADED_CARD = "";
+ /** The name of the panel containing the "no collection loaded" placeholder */
+ private String NO_COLLECTION_LOADED_CARD = "No collection loaded";
+ /** The group encompassing all of the components available as drop targets for drag and drop actions. Required so that only one component renders the ghost and higlights itself as a target, which the other members are restored to their original, pristine, condition. */
+ private DragGroup group = null;
+ /** The tree showing the files within the collection. */
+ private CollectionTree collection_tree = null;
+ /** The threaded queue that handles the actually movement of files, so that the gui remains responsive. */
+ private FileQueue file_queue = null;
+ /** The filter currently applied to the collection tree. */
+ private Filter collection_filter = null;
+ /** The filter currently applied to the workspace tree. */
+ private Filter workspace_filter = null;
+ /** The button used to cancel all pending file queue jobs. */
+ private JButton stop_action = null;
+ /** The button used to create a new folder in the collection tree. */
+ private JButton new_folder = null;
+ /** The label shown at the top of the collection tree. */
+ private JLabel collection_label = null;
+ /** The label shown in the status area explaining the file apon which action is taking place. */
+ private JLabel filename_label = null;
+ /** The label shown explaining the current state of the file queue thread. */
+ private JLabel status_label = null;
+ /** The label at the top of the workspace tree. */
+ private JLabel workspace_label = null;
+ /** The panel that contains the collection tree. */
+ private JPanel collection_pane = null;
+ /** The panel that contains the various controls including the status area. */
+ private JPanel control_pane = null;
+ /** The panel that contains the workspace tree. */
+ private JPanel workspace_pane = null;
+ /** The scrollable area into which the collection tree is placed. */
+ private JScrollPane collection_scroll = null;
+ /** The scrollable area into which the workspace tree is placed. */
+ private JScrollPane workspace_scroll = null;
+ /** A split pane seperating the two trees, allowing for the screen real-estate for each to be changed. */
+ private JSplitPane tree_pane = null;
+ /** The minimum size a gui component can become. */
+ static private Dimension MIN_SIZE = new Dimension( 90, 90);
+ /** The default size of the status area. */
+ static private Dimension STATUS_SIZE = new Dimension(450, 120);
+ /** The initial size of the trees. */
+ static private Dimension TREE_SIZE = new Dimension(400, 430);
+
+ /** The tree showing the available source workspace. */
+ public WorkspaceTree workspace_tree = null;
+
+
+ public GatherPane()
+ {
+ this.group = new DragGroup();
+ this.file_queue = Gatherer.f_man.getQueue();
+ this.setComponentOrientation(org.greenstone.gatherer.Dictionary.getOrientation());
+ // Create components.
+ stop_action = new GLIButton(Dictionary.get("Collection.Stop"), Dictionary.get("Collection.Stop_Tooltip"));
+ stop_action.addActionListener(this);
+ stop_action.setEnabled(false);
+ file_queue.registerStopButton(stop_action);
+
+ new_folder = new GLIButton(JarTools.getImage("folder.gif"), Dictionary.get("Collection.New_Folder_Tooltip"));
+ new_folder.addActionListener(this);
+ new_folder.setEnabled(false);
+ new_folder.setMinimumSize(MIN_SIZE);
+ new_folder.setPreferredSize(MIN_SIZE);
+
+ }
+
+ /** Any implementation of ActionListener requires this method so that when an action is performed the appropriate effect can occur. In this case there are three valid possibilities. If the action occured on the recycle bin, then delete the current selection from the collection tree. If the action instead occured on the new folder button, then create a new folder under the current (single) selection in the collection tree. And finally if the cancel button was pressed, cancel the current, and remaining, jobs on the file queue. */
+ public void actionPerformed(ActionEvent event) {
+ // If a user has clicked on the bin button directly remove whatever
+ // files are selected in the active tree.
+ if (event.getSource() == Gatherer.recycle_bin) {
+ if (!Gatherer.recycle_bin.ignore()) {
+ // Find the active tree (you've made selections in).
+ DragTree tree = (DragTree) group.getActive();
+ if (tree != null) { // if something has indeed been selected
+ // Fudge things a bit
+ group.setSource(tree);
+ // Determine the selection.
+ TreePath paths[] = tree.getSelectionPaths();
+ if(paths != null) {
+ FileNode[] source_nodes = new FileNode[paths.length];
+ for(int i = 0; i < paths.length; i++) {
+ source_nodes[i] = (FileNode)(paths[i].getLastPathComponent());
+ }
+ Gatherer.f_man.action(tree, source_nodes, Gatherer.recycle_bin, null);
+ }
+ }
+ }
+ }
+ // If a user has clicked on new_folder create a new folder under
+ // whatever node is selected.
+ else if(event.getSource() == new_folder && collection_tree != null) {
+ int count = collection_tree.getSelectionCount();
+ boolean error = false;
+ if(count == 1) {
+ TreePath path = collection_tree.getSelectionPath();
+ CollectionTreeNode node = (CollectionTreeNode) path.getLastPathComponent();
+ if (node.getAllowsChildren()) {
+ Gatherer.f_man.newFolder(collection_tree, node);
+ }
+ else {
+ // try the parent
+ CollectionTreeNode parent = (CollectionTreeNode) node.getParent();
+ if (parent!=null && parent.getAllowsChildren()) {
+ Gatherer.f_man.newFolder(collection_tree, parent);
+ } else {
+ error = true;
+ }
+ }
+ }
+ else {
+ error = true;
+ }
+ if(error) {
+ // instead of an error, we now create a new folder at the root
+ CollectionTreeNode node = (CollectionTreeNode) collection_tree.getModel().getRoot();
+ Gatherer.f_man.newFolder(collection_tree, node);
+ }
+ }
+ else if(event.getSource() == stop_action) {
+ file_queue.cancelAction();
+ }
+ }
+
+
+ /** Generates the pane on controls used to 'collect' files into the collection. Resposible for creating, connecting and laying out these controls. */
+ public void display()
+ {
+ // Workspace Tree
+ workspace_pane = new JPanel();
+ workspace_pane.setMinimumSize(MIN_SIZE);
+ workspace_pane.setPreferredSize(TREE_SIZE);
+ workspace_pane.setSize(TREE_SIZE);
+ workspace_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ workspace_label = new JLabel(Dictionary.get("Collection.Workspace"));
+ workspace_label.setOpaque(true);
+ workspace_label.setBackground(Configuration.getColor("coloring.workspace_heading_background", false));
+ workspace_label.setForeground(Configuration.getColor("coloring.workspace_heading_foreground", false));
+ workspace_label.setComponentOrientation(Dictionary.getOrientation());
+
+ workspace_tree = new WorkspaceTree();
+ workspace_tree.setComponentOrientation(Dictionary.getOrientation());
+ group.add(workspace_tree);
+ workspace_scroll = new JScrollPane(workspace_tree);
+ workspace_scroll.setComponentOrientation(Dictionary.getOrientation());
+ workspace_filter = workspace_tree.getFilter();
+ workspace_filter.setComponentOrientation(Dictionary.getOrientation());
+
+ card_layout = new CardLayout();
+
+ // Collection Tree
+ collection_pane = new JPanel();
+ collection_pane.setMinimumSize(MIN_SIZE);
+ collection_pane.setPreferredSize(TREE_SIZE);
+ collection_pane.setSize(TREE_SIZE);
+ collection_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ collection_label = new JLabel(Dictionary.get("Collection.Collection"));
+ collection_label.setOpaque(true);
+ collection_label.setComponentOrientation(Dictionary.getOrientation());
+
+ collection_tree = Gatherer.c_man.getCollectionTree();
+ collection_tree.setEnabled(Gatherer.c_man.getCollectionTreeModel() != null);
+ group.add(collection_tree);
+ collection_filter = collection_tree.getFilter();
+
+ tree_pane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
+ tree_pane.setComponentOrientation(Dictionary.getOrientation());
+ // No collection loaded pane
+ JPanel no_collection_pane = new JPanel();
+ no_collection_pane.setBackground(Color.lightGray);
+ no_collection_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JLabel no_collection_label = new JLabel(Dictionary.get("Collection.Collection"));
+ no_collection_label.setBackground(Color.lightGray);
+ no_collection_label.setForeground(Color.black);
+ no_collection_label.setOpaque(true);
+ no_collection_label.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel no_collection_loaded_panel = new JPanel();
+ no_collection_loaded_panel.setBorder(BorderFactory.createLineBorder(Color.black));
+ no_collection_loaded_panel.setBackground(Color.lightGray);
+ no_collection_loaded_panel.setComponentOrientation(Dictionary.getOrientation());
+
+ JLabel no_collection_loaded_label = new JLabel(Dictionary.get("Collection.No_Collection_Loaded"));
+ no_collection_loaded_label.setHorizontalAlignment(JLabel.CENTER);
+ no_collection_loaded_label.setVerticalAlignment(JLabel.CENTER);
+ no_collection_loaded_label.setComponentOrientation(Dictionary.getOrientation());
+
+ // Status pane
+ control_pane = new JPanel();
+ control_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel inner_pane = new JPanel();
+ inner_pane.setSize(STATUS_SIZE);
+ inner_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ card_pane = new JPanel();
+ card_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel file_pane = new JPanel();
+ file_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel progress_pane = new JPanel();
+ progress_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JLabel file_status = file_queue.getFileStatus();
+ file_status.setComponentOrientation(Dictionary.getOrientation());
+
+ JProgressBar progress_bar = file_queue.getProgressBar();
+ progress_bar.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel button_pane = new JPanel();
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+ RecycleBin recycle_bin = Gatherer.recycle_bin;
+ recycle_bin.addActionListener(this);
+ recycle_bin.setMinimumSize(MIN_SIZE);
+ recycle_bin.setPreferredSize(MIN_SIZE);
+ recycle_bin.setToolTipText(Dictionary.get("Collection.Delete_Tooltip"));
+ group.add(recycle_bin);
+
+ // Layout Components.
+ workspace_pane.setLayout(new BorderLayout());
+ workspace_pane.add(workspace_label, BorderLayout.NORTH);
+ workspace_pane.add(workspace_scroll, BorderLayout.CENTER);
+ workspace_pane.add(workspace_filter, BorderLayout.SOUTH);
+
+ collection_pane.setLayout(new BorderLayout());
+ collection_pane.add(collection_label, BorderLayout.NORTH);
+
+ no_collection_loaded_panel.setLayout(new BorderLayout());
+ no_collection_loaded_panel.add(no_collection_loaded_label, BorderLayout.CENTER);
+
+ no_collection_pane.setLayout(new BorderLayout());
+ no_collection_pane.add(no_collection_label, BorderLayout.NORTH);
+ no_collection_pane.add(no_collection_loaded_panel, BorderLayout.CENTER);
+
+ card_pane.setLayout(card_layout);
+ card_pane.add(no_collection_pane, NO_COLLECTION_LOADED_CARD);
+ card_pane.add(collection_pane, COLLECTION_LOADED_CARD);
+
+ if (Dictionary.getOrientation().isLeftToRight()){
+ tree_pane.add(workspace_pane, JSplitPane.LEFT);
+ tree_pane.add(card_pane, JSplitPane.RIGHT);
+ }else{
+ tree_pane.add(workspace_pane, JSplitPane.RIGHT);
+ tree_pane.add(card_pane, JSplitPane.LEFT);
+ }
+
+
+ tree_pane.setDividerLocation(TREE_SIZE.width - 10);
+
+ file_pane.setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
+ file_pane.setLayout(new BorderLayout());
+ file_pane.add(file_status, BorderLayout.CENTER);
+ file_pane.add(stop_action, BorderLayout.LINE_END);
+
+ progress_pane.setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
+ progress_pane.setLayout(new BorderLayout());
+ progress_pane.add(progress_bar, BorderLayout.CENTER);
+
+ inner_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(10,10,10,10), BorderFactory.createLoweredBevelBorder()));
+ inner_pane.setLayout(new GridLayout(2,1));
+ inner_pane.add(file_pane);
+ inner_pane.add(progress_pane);
+
+ button_pane.add(new_folder);
+ button_pane.add(recycle_bin);
+
+ control_pane.setLayout(new BorderLayout());
+ control_pane.add(inner_pane, BorderLayout.CENTER);
+ control_pane.add(button_pane, BorderLayout.LINE_END);
+
+ this.setLayout(new BorderLayout());
+ this.add(tree_pane, BorderLayout.CENTER);
+ this.add(control_pane, BorderLayout.SOUTH);
+ }
+
+
+ /** Called to inform this control panel that it has just gained focus as an effect of the user clicking on its tab.
+ */
+ public void gainFocus()
+ {
+ // Add the collection tree and filter back into this pane
+ collection_scroll = new JScrollPane(collection_tree);
+ collection_pane.add(collection_scroll, BorderLayout.CENTER);
+ collection_pane.add(collection_filter, BorderLayout.SOUTH);
+ }
+
+
+ /** Called to inform this pane that it has just lost focus as an effect of the user clicking on another tab
+ */
+ public void loseFocus()
+ {
+ // Remove the collection tree and filter from this pane so it can be added to the Enrich pane
+ collection_pane.remove(collection_scroll);
+ collection_pane.remove(collection_filter);
+ }
+
+
+ /** Retrieve a list of the currently selected file records in the collection tree. */
+ private CollectionTreeNode[] getCollectionTreeSelection()
+ {
+ TreePath paths[] = collection_tree.getSelectionPaths();
+ CollectionTreeNode records[] = null;
+ if (paths != null) {
+ records = new CollectionTreeNode[paths.length];
+ for (int i = 0; i < records.length; i++) {
+ records[i] = (CollectionTreeNode) paths[i].getLastPathComponent();
+ }
+ }
+ return records;
+ }
+
+
+ /** Called whenever the detail mode changes to ensure the filters are at an appropriate level (ie only editable by those that understand regular expression matching)
+ * @param mode the mode level as an int
+ */
+ public void modeChanged(int mode) {
+ collection_filter.setEditable(mode >= Configuration.LIBRARIAN_MODE);
+ workspace_filter.setEditable(mode >= Configuration.LIBRARIAN_MODE);
+ }
+
+
+ /** Refresh this pane, depending on what has just happened (refresh_reason). */
+ public void refresh(int refresh_reason, boolean collection_loaded)
+ {
+ if (collection_loaded) {
+ // Update collection tree
+ if (refresh_reason == Gatherer.COLLECTION_OPENED) {
+ collection_tree.setModel(Gatherer.c_man.getCollectionTreeModel());
+ }
+
+ card_layout.show(card_pane, COLLECTION_LOADED_CARD);
+ }
+ else {
+ // Update collection tree
+ collection_tree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode("Error")));
+
+ card_layout.show(card_pane, NO_COLLECTION_LOADED_CARD);
+ }
+
+ // File sizes turned on/off
+ if (refresh_reason == Gatherer.PREFERENCES_CHANGED) {
+ refreshWorkspaceTree(DragTree.TREE_DISPLAY_CHANGED);
+ refreshCollectionTree(DragTree.TREE_DISPLAY_CHANGED);
+ }
+
+ // Enable or disable the controls
+ collection_tree.setEnabled(collection_loaded);
+ collection_filter.setEnabled(collection_loaded);
+ new_folder.setEnabled(collection_loaded);
+ }
+
+
+ public void refreshCollectionTree(int refresh_reason) {
+ collection_tree.refresh(null);
+ }
+
+
+ public void refreshWorkspaceTree(int refresh_reason) {
+ workspace_tree.refresh(refresh_reason);
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/HelpFrame.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/HelpFrame.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/HelpFrame.java (revision 31635)
@@ -0,0 +1,395 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Principal Author: John Thompson, NZDL Project, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.tree.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.util.JarTools;
+import org.greenstone.gatherer.util.StaticStrings;
+import org.greenstone.gatherer.util.XMLTools;
+import org.w3c.dom.*;
+
+
+/**
+ * This class provides a nice help facility. It is a separate frame that can be positioned
+ * as the user wishes, and provides a contents page and the help information.
+ * @author Michael Dewsnip, NZDL Project
+ */
+public class HelpFrame
+ extends JFrame
+{
+ /** The size of the frame */
+ static private final Dimension SIZE = new Dimension(800, 560);
+
+ static private HelpFrame self = null;
+
+ /** The help view at the bottom of the frame. */
+ static private JEditorPane help_pane = null;
+ /** The help contents tree at the top of the frame. */
+ static private JTree help_contents_tree = null;
+ /** The help contents tree model. */
+ static private HelpContentsTreeModel help_contents_tree_model = null;
+ /** The split between the contents tree and the page view. */
+ static private JSplitPane split_pane = null;
+
+ public HelpFrame()
+ {
+ setDefaultCloseOperation(HIDE_ON_CLOSE);
+ setSize(SIZE);
+ setTitle(Dictionary.get("Help.Title"));
+ this.setComponentOrientation(Dictionary.getOrientation());
+
+ help_pane = new JEditorPane();
+ help_pane.setComponentOrientation(Dictionary.getOrientation());
+ help_pane.setEditable(false);
+ help_pane.addHyperlinkListener(new HelpPaneHyperlinkListener());
+
+ HelpContentsTreeNode help_tree_root_node = new HelpContentsTreeNode(null, Dictionary.get("Help.Contents"));
+ help_contents_tree_model = new HelpContentsTreeModel(help_tree_root_node);
+ help_contents_tree = new JTree((DefaultTreeModel) help_contents_tree_model);
+ help_contents_tree.setComponentOrientation(Dictionary.getOrientation());
+ help_contents_tree.addTreeSelectionListener(new HelpContentsTreeSelectionListener());
+ help_contents_tree.setExpandsSelectedPaths(true);
+
+ // Creation
+ JPanel content_pane = (JPanel) this.getContentPane();
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+ split_pane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
+ JScrollPane help_contents_tree_scroll = new JScrollPane(help_contents_tree);
+ help_contents_tree_scroll.setComponentOrientation(Dictionary.getOrientation());
+ JScrollPane help_pane_scroll = new JScrollPane(help_pane);
+ help_pane_scroll.setComponentOrientation(Dictionary.getOrientation());
+
+ // Layout
+ if (Dictionary.getOrientation().isLeftToRight()){
+ split_pane.add(help_contents_tree_scroll, JSplitPane.LEFT);
+ split_pane.add(help_pane_scroll, JSplitPane.RIGHT);
+ }else{
+ split_pane.add(help_contents_tree_scroll, JSplitPane.RIGHT);
+ split_pane.add(help_pane_scroll, JSplitPane.LEFT);
+ }
+
+ content_pane.setLayout(new BorderLayout());
+ content_pane.add(split_pane, BorderLayout.CENTER);
+
+ // Center
+ Dimension screen_size = Toolkit.getDefaultToolkit().getScreenSize();
+ setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
+
+
+ // Pretty corner icon
+ String gsmall_image = "gatherer.png";
+ if (Configuration.fedora_info.isActive()) {
+ gsmall_image = "fli-" + gsmall_image;
+ }
+
+ this.setIconImage(JarTools.getImage(gsmall_image).getImage());
+
+ self = this;
+ }
+
+ public void destroy()
+ {
+ help_contents_tree = null;
+ help_pane = null;
+ }
+
+
+ /** Retrieve the relative file path to the language-specific help index xml file. */
+ private String getHelpFolder()
+ {
+ String help_folder = "help/" + Configuration.getLanguage() + "/";
+
+ // Applet case: get help files out of JAR file
+ if (Gatherer.isApplet && JarTools.getResource("/" + help_folder) != null) {
+ return help_folder;
+ }
+
+ // Normal case: get help files out of GLI "help" folder
+ if (new File(help_folder).exists()) {
+ return help_folder;
+ }
+
+ // Resort to English
+ return "help/" + StaticStrings.ENGLISH_LANGUAGE_STR + "/";
+ }
+
+
+ private URL getURLForSection(String section_name)
+ {
+ // Form the path to the help file from the section name
+ if (section_name != null) {
+ String help_file_path = getHelpFolder() + section_name + StaticStrings.HTM_FILE_EXTENSION;
+
+ try {
+ // Applet case: get help file out of JAR file
+ if (Gatherer.isApplet) {
+ return JarTools.getResource("/" + help_file_path);
+ }
+
+ // Normal case: get help file out of GLI "help" folder
+ return (new File(help_file_path)).toURI().toURL();
+ }
+ catch (Exception exception) {
+ DebugStream.printStackTrace(exception);
+ }
+ }
+
+ return null;
+ }
+
+
+ static private void selectHelpContentsTreeNode(String section_name)
+ {
+ // Find the tree node with this section name, then select it
+ selectHelpContentsTreeNode(help_contents_tree_model.getHelpContentsTreeNodeNamed(section_name));
+ }
+
+
+ static private void selectHelpContentsTreeNode(HelpContentsTreeNode help_tree_node)
+ {
+ // Ensure the node in the help contents tree is selected and visible
+ TreePath help_tree_node_path = new TreePath(help_tree_node.getPath());
+ help_contents_tree.setSelectionPath(help_tree_node_path);
+ help_contents_tree.scrollPathToVisible(help_tree_node_path);
+ }
+
+
+ static public void setView(String section_name)
+ {
+ // Select the appropriate node in the help contents tree
+ selectHelpContentsTreeNode(section_name);
+
+ // Show the help page with the specified section name
+ self.showHelpPage(section_name);
+ }
+
+
+ private void showHelpPage(String section_name)
+ {
+ // Find the tree node with this section name, then show the page for the node
+ showHelpPage(getURLForSection(section_name));
+ }
+
+
+ private void showHelpPage(URL help_page_url)
+ {
+ // Display the selected page
+ if (help_page_url != null) {
+ try {
+ help_pane.setPage(help_page_url);
+ }
+ catch (Exception exception) {
+ DebugStream.printStackTrace(exception);
+ }
+ }
+
+ // Make the help frame visible
+ setVisible(true);
+ split_pane.setDividerLocation(0.4);
+ }
+
+
+ private class HelpContentsTreeModel
+ extends DefaultTreeModel
+ {
+ public HelpContentsTreeModel(MutableTreeNode help_tree_root_node)
+ {
+ super(help_tree_root_node);
+
+ // Load the XML help index file and build the contents structure from it
+ try {
+ String help_index_file_path = getHelpFolder() + "help_index.xml";
+ Document document = null;
+ if (Gatherer.isApplet && JarTools.getResource(help_index_file_path) != null) {
+ document = XMLTools.parseXMLFile(help_index_file_path, true);
+ }
+
+ if (new File(help_index_file_path).exists()) {
+ document = XMLTools.parseXMLFile(help_index_file_path, false);
+ }
+
+ if (document == null) {
+ String errormsg = "HelpFrame.HelpContentsTreeModel constructor(): There's no help document after parsing";
+ System.err.println(errormsg);
+ DebugStream.println(errormsg);
+ return;
+ }
+ // Traverse the help hierarchy, building a tree representing its structure
+ Node document_node = document.getFirstChild();
+
+ // Add a help contents tree node for each major section
+ int section_number = 0;
+ NodeList children = document_node.getChildNodes();
+ for (int i = 0; i < children.getLength(); i++) {
+ Node child = children.item(i);
+ if (child.getNodeName().equals(StaticStrings.SECTION_ELEMENT)) {
+ section_number++;
+ buildHelpContentsTree(help_tree_root_node, section_number + StaticStrings.EMPTY_STR, child);
+ }
+ }
+ }
+ catch (Exception exception) {
+ DebugStream.printStackTrace(exception);
+ }
+ }
+
+
+ private void buildHelpContentsTree(MutableTreeNode parent, String pos, Node node)
+ {
+ // Determine the section name
+ String section_name = ((Element) node).getAttribute(StaticStrings.NAME_ATTRIBUTE);
+
+ // Determine the section title
+ String section_title = "";
+ NodeList children = node.getChildNodes();
+ for (int i = 0; i < children.getLength(); i++) {
+ Node child = children.item(i);
+ if (child.getNodeName().equals(StaticStrings.TITLE_ELEMENT)) {
+ section_title = pos + ": ";
+ if (child.getFirstChild() != null) {
+ section_title += child.getFirstChild().getNodeValue();
+ }
+ }
+ }
+
+ // Add the node into the tree
+ HelpContentsTreeNode help_tree_node = new HelpContentsTreeNode(section_name, section_title);
+ insertNodeInto(help_tree_node, parent, parent.getChildCount());
+
+ // Apply recursively to the children of this node
+ int section_number = 0;
+ for (int i = 0; i < children.getLength(); i++) {
+ Node child = children.item(i);
+ if (child.getNodeName().equals(StaticStrings.SECTION_ELEMENT)) {
+ section_number++;
+ buildHelpContentsTree(help_tree_node, pos + StaticStrings.STOP_CHARACTER + section_number, child);
+ }
+ }
+ }
+
+
+ private HelpContentsTreeNode getHelpContentsTreeNodeNamed(String section_name)
+ {
+ // Find the node with name matching section name, somewhere in the tree
+ if (section_name != null) {
+ Enumeration descendants = ((DefaultMutableTreeNode) root).preorderEnumeration();
+ while (descendants.hasMoreElements()) {
+ HelpContentsTreeNode help_tree_node = (HelpContentsTreeNode) descendants.nextElement();
+ if (section_name.equals(help_tree_node.section_name)) {
+ return help_tree_node;
+ }
+ }
+ }
+
+ // Couldn't be found, so just return the root (Contents) node
+ return (HelpContentsTreeNode) root;
+ }
+ }
+
+
+ /** This listener is used to listen for selection changes in the contents tree, and
+ * to show the appropriate page as required.
+ */
+ private class HelpContentsTreeSelectionListener
+ implements TreeSelectionListener
+ {
+ /** Any implementation of TreeSelectionListener must include this method so we can be informed when the tree selection changes.
+ * @param event A TreeSelectionEvent containing al the relevant information about the event itself.
+ */
+ public void valueChanged(TreeSelectionEvent event)
+ {
+ HelpContentsTreeNode help_tree_node = (HelpContentsTreeNode) event.getPath().getLastPathComponent();
+ selectHelpContentsTreeNode(help_tree_node);
+ showHelpPage(help_tree_node.section_name);
+ }
+ }
+
+
+ /** This class provides a wrapper around a DefaultMutableTreeNode which
+ * provides the ability to set an html page to be loaded when this node is selected.
+ */
+ private class HelpContentsTreeNode
+ extends DefaultMutableTreeNode
+ {
+ /** The unique name of the section (matches the HTML filename) */
+ public String section_name = null;
+ /** The title to be displayed for this tree node. */
+ public String section_title = null;
+
+ public HelpContentsTreeNode(String section_name, String section_title)
+ {
+ this.section_name = section_name;
+ this.section_title = section_title;
+ }
+
+ public String toString()
+ {
+ return section_title;
+ }
+ }
+
+
+ private class HelpPaneHyperlinkListener
+ implements HyperlinkListener
+ {
+ public void hyperlinkUpdate(HyperlinkEvent e)
+ {
+ if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
+ // Determine which node in the help contents tree to select, then select it
+ String section_link = e.getURL().getFile();
+ if (section_link.endsWith(".htm")) {
+ section_link = section_link.substring(section_link.lastIndexOf("/") + 1);
+ section_link = section_link.substring(0, section_link.length() - ".htm".length());
+ selectHelpContentsTreeNode(section_link);
+ }
+
+ // Change to the page specified by the link
+ showHelpPage(e.getURL());
+ }
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/LockFileDialog.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/LockFileDialog.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/LockFileDialog.java (revision 31635)
@@ -0,0 +1,246 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import javax.swing.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.util.JarTools;
+import org.greenstone.gatherer.util.XMLTools;
+import org.w3c.dom.*;
+
+/** A lock file dialog is shown whenever GLI detects an existing lock on a collection it is trying to open. The user can look at the details provided with the dialog, and decide whether to 'steal' the lock or to cancel the loading. */
+public class LockFileDialog
+ extends ModalDialog {
+
+ private int choice;
+ private Document document;
+ private JButton cancel_button;
+ private JButton ok_button;
+ private LockFileDialog self;
+
+ static final public int NO_OPTION = 0;
+ static final public int YES_OPTION = 1;
+
+ static final private Dimension LABEL_SIZE = new Dimension(100, 25);
+ static final private Dimension SIZE = new Dimension(500, 250);
+ static final private int ICON_SIZE = 30;
+
+ public LockFileDialog(JFrame parent, String name, File lock_file) {
+ super(parent, Dictionary.get("LockFileDialog.Title"), true);
+ this.setComponentOrientation(Dictionary.getOrientation());
+ setSize(SIZE);
+ setJMenuBar(new SimpleMenuBar("openingacollection"));
+ this.self = this;
+
+ // Parse the lock file
+ document = XMLTools.parseXMLFile(lock_file);
+
+ // Creation
+ JPanel content_pane = (JPanel) getContentPane();
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+ JPanel upper_pane = new JPanel();
+ upper_pane.setComponentOrientation(Dictionary.getOrientation());
+ ImageIcon icon = JarTools.getImage("lcolicn.gif");
+ ImageIcon scaled_icon = new ImageIcon(icon.getImage().getScaledInstance(ICON_SIZE, ICON_SIZE, Image.SCALE_DEFAULT));
+ JLabel icon_label = new JLabel(scaled_icon);
+ icon_label.setComponentOrientation(Dictionary.getOrientation());
+ JPanel title_pane = new JPanel();
+ title_pane.setComponentOrientation(Dictionary.getOrientation());
+ JLabel title_one_label = new JLabel(Dictionary.get("General.Warning"));
+ title_one_label.setComponentOrientation(Dictionary.getOrientation());
+ JTextArea title_two_textarea = new JTextArea(Dictionary.get("LockFileDialog.Lockfile_Message_One"));
+ title_two_textarea.setComponentOrientation(Dictionary.getOrientation());
+ title_two_textarea.setEditable(false);
+ title_two_textarea.setLineWrap(true);
+ title_two_textarea.setOpaque(false);
+ title_two_textarea.setRows(3);
+ title_two_textarea.setWrapStyleWord(true);
+
+ JPanel central_pane = new JPanel();
+ central_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel name_pane = new JPanel();
+ name_pane.setComponentOrientation(Dictionary.getOrientation());
+ JLabel name_label = new JLabel(Dictionary.get("LockFileDialog.Name"));
+ name_label.setComponentOrientation(Dictionary.getOrientation());
+ name_label.setPreferredSize(LABEL_SIZE);
+
+ JTextField name_field = new JTextField(name);
+ name_field.setComponentOrientation(Dictionary.getOrientation());
+ name_field.setEditable(false);
+ name_field.setBackground(Configuration.getColor("coloring.collection_tree_background", false));
+
+
+ JPanel person_pane = new JPanel();
+ person_pane.setComponentOrientation(Dictionary.getOrientation());
+ JLabel person_label = new JLabel(Dictionary.get("LockFileDialog.User"));
+ person_label.setComponentOrientation(Dictionary.getOrientation());
+ person_label.setPreferredSize(LABEL_SIZE);
+
+ JTextField person_field = new JTextField(getValue("User"));
+ person_field.setComponentOrientation(Dictionary.getOrientation());
+ person_field.setEditable(false);
+ person_field.setBackground(Configuration.getColor("coloring.collection_tree_background", false));
+
+ JPanel machine_pane = new JPanel();
+ machine_pane.setComponentOrientation(Dictionary.getOrientation());
+ JLabel machine_label = new JLabel(Dictionary.get("LockFileDialog.Machine"));
+ machine_label.setComponentOrientation(Dictionary.getOrientation());
+ machine_label.setPreferredSize(LABEL_SIZE);
+
+ JTextField machine_field = new JTextField(getValue("Machine"));
+ machine_field.setComponentOrientation(Dictionary.getOrientation());
+ machine_field.setEditable(false);
+ machine_field.setBackground(Configuration.getColor("coloring.collection_tree_background", false));
+
+ JPanel created_pane = new JPanel();
+ created_pane.setComponentOrientation(Dictionary.getOrientation());
+ JLabel created_label = new JLabel(Dictionary.get("LockFileDialog.Date"));
+ created_label.setPreferredSize(LABEL_SIZE);
+ created_label.setComponentOrientation(Dictionary.getOrientation());
+
+ JTextField created_field = new JTextField(getValue("Date"));
+ created_field.setComponentOrientation(Dictionary.getOrientation());
+ created_field.setEditable(false);
+ created_field.setBackground(Configuration.getColor("coloring.collection_tree_background", false));
+
+ JLabel central_label = new JLabel(Dictionary.get("LockFileDialog.Lockfile_Message_Two"));
+ central_label.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel button_pane = new JPanel();
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+ ok_button = new GLIButton(Dictionary.get("General.OK"), Dictionary.get("LockFileDialog.OK_Tooltip"));
+
+ cancel_button = new GLIButton(Dictionary.get("General.Cancel"), Dictionary.get("LockFileDialog.Cancel_Tooltip"));
+
+ // Connection
+ ok_button.addActionListener(new MyActionListener());
+ cancel_button.addActionListener(new MyActionListener());
+
+ // Layout
+ icon_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,10));
+
+ title_pane.setLayout(new BorderLayout());
+ title_pane.add(title_one_label, BorderLayout.NORTH);
+ title_pane.add(title_two_textarea, BorderLayout.CENTER);
+
+ upper_pane.setLayout(new BorderLayout());
+ upper_pane.add(icon_label, BorderLayout.LINE_START);
+ upper_pane.add(title_pane, BorderLayout.CENTER);
+
+ name_pane.setLayout(new BorderLayout());
+ name_pane.add(name_label, BorderLayout.LINE_START);
+ name_pane.add(name_field, BorderLayout.CENTER);
+
+ person_pane.setLayout(new BorderLayout());
+ person_pane.add(person_label, BorderLayout.LINE_START);
+ person_pane.add(person_field, BorderLayout.CENTER);
+
+ machine_pane.setLayout(new BorderLayout());
+ machine_pane.add(machine_label, BorderLayout.LINE_START);
+ machine_pane.add(machine_field, BorderLayout.CENTER);
+
+ created_pane.setLayout(new BorderLayout());
+ created_pane.add(created_label, BorderLayout.LINE_START);
+ created_pane.add(created_field, BorderLayout.CENTER);
+
+ central_pane.setLayout(new GridLayout(5,1,0,5));
+ central_pane.add(name_pane);
+ central_pane.add(person_pane);
+ central_pane.add(machine_pane);
+ central_pane.add(created_pane);
+ central_pane.add(central_label);
+
+ button_pane.setLayout(new GridLayout(1,2,5,0));
+ button_pane.add(ok_button);
+ button_pane.add(cancel_button);
+
+ content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ content_pane.setLayout(new BorderLayout());
+ content_pane.add(upper_pane, BorderLayout.NORTH);
+ content_pane.add(central_pane, BorderLayout.CENTER);
+ content_pane.add(button_pane, BorderLayout.SOUTH);
+
+ // Display
+ if (parent != null) {
+ Rectangle frame_bounds = parent.getBounds();
+ setLocation(frame_bounds.x + (frame_bounds.width - SIZE.width) / 2, frame_bounds.y + (frame_bounds.height - SIZE.height) / 2);
+ }
+ else {
+ Dimension screen_size = Toolkit.getDefaultToolkit().getScreenSize();
+ setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
+ }
+ }
+
+ public int getChoice() {
+ setVisible(true);
+ return choice;
+ }
+
+ private String getValue(String name) {
+ String value = "";
+ if(document != null) {
+ try {
+ Element value_element = (Element) XMLTools.getNodeFromNamed(document.getDocumentElement(), name);
+ value = XMLTools.getValue(value_element);
+ }
+ catch (Exception error) {
+ }
+ }
+ else {
+ value = Dictionary.get("LockFileDialog.Error");
+ }
+ return value;
+ }
+
+ private class MyActionListener
+ implements ActionListener {
+ public void actionPerformed(ActionEvent event) {
+ if(event.getSource() == ok_button) {
+ choice = YES_OPTION;
+ }
+ else {
+ choice = NO_OPTION;
+ }
+ self.dispose();
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/MenuBar.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/MenuBar.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/MenuBar.java (revision 31635)
@@ -0,0 +1,311 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.util.JarTools;
+import org.greenstone.gatherer.util.Utility;
+
+/** The menu bar for the Gatherer main GUI.
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.2
+ */
+public class MenuBar
+ extends JMenuBar
+{
+ static public ImageIcon BLANK_ICON = JarTools.getImage("blank.gif");
+ static public ImageIcon HELP_ICON = JarTools.getImage("help.png");
+
+ /** The icon to be displayed alongside the context choosen help file. */
+ private int current_tab = -1;
+
+ public JMenu file = null;
+ public JMenu edit = null;
+ public JMenu help = null;
+ public JMenuItem file_associations;
+ public JMenuItem file_cdimage = null;
+ public JMenuItem file_close = null;
+ public JMenuItem file_delete = null;
+ public JMenuItem file_exit = null;
+ public JMenuItem file_exportas = null;
+ public JMenuItem file_new = null;
+ public JMenuItem file_open = null;
+ public JMenuItem file_options = null;
+ public JMenuItem file_save = null;
+ public JMenuItem edit_copy;
+ public JMenuItem edit_cut;
+ public JMenuItem edit_paste;
+ public JMenuItem edit_config;
+ public JMenuItem help_general;
+ public JMenuItem help_download;
+ public JMenuItem help_gather;
+ public JMenuItem help_enrich;
+ public JMenuItem help_design;
+ public JMenuItem help_create;
+ public JMenuItem help_format;
+ public JMenuItem help_about;
+
+
+ public MenuBar(MenuListener menu_listener)
+ {
+ this.setComponentOrientation(Dictionary.getOrientation());
+
+ file = new JMenu();
+ file.setEnabled(false);
+ file.setText(Dictionary.get("Menu.File"));
+ file.setComponentOrientation(Dictionary.getOrientation());
+
+ file_associations = new JMenuItem(Dictionary.get("Menu.File_Associations"));
+ file_associations.addActionListener(Gatherer.g_man);
+ file_associations.setComponentOrientation(Dictionary.getOrientation());
+
+ file_cdimage = new JMenuItem(Dictionary.get("Menu.File_CDimage"));
+ file_cdimage.addActionListener(Gatherer.g_man);
+ file_cdimage.setEnabled(!Gatherer.isGsdlRemote);
+ file_cdimage.setComponentOrientation(Dictionary.getOrientation());
+
+ file_close = new JMenuItem(Dictionary.get("Menu.File_Close"));
+ file_close.addActionListener(Gatherer.g_man);
+ file_close.setEnabled(false);
+ file_close.setComponentOrientation(Dictionary.getOrientation());
+
+ file_delete = new JMenuItem(Dictionary.get("Menu.File_Delete"));
+ file_delete.addActionListener(Gatherer.g_man);
+ file_delete.setComponentOrientation(Dictionary.getOrientation());
+
+ file_exit = new JMenuItem(Dictionary.get("Menu.File_Exit"));
+ file_exit.addActionListener(Gatherer.g_man);
+ file_exit.setComponentOrientation(Dictionary.getOrientation());
+
+ file_exportas = new JMenuItem(Dictionary.get("Menu.File_ExportAs"));
+ file_exportas.addActionListener(Gatherer.g_man);
+ file_exportas.setEnabled(!Gatherer.isGsdlRemote);
+ file_exportas.setComponentOrientation(Dictionary.getOrientation());
+
+ file_new = new JMenuItem(Dictionary.get("Menu.File_New"));
+ file_new.addActionListener(Gatherer.g_man);
+ file_new.setComponentOrientation(Dictionary.getOrientation());
+
+ file_open = new JMenuItem(Dictionary.get("Menu.File_Open"));
+ file_open.addActionListener(Gatherer.g_man);
+ file_open.setComponentOrientation(Dictionary.getOrientation());
+
+ file_options = new JMenuItem(Dictionary.get("Menu.File_Options"));
+ file_options.addActionListener(Gatherer.g_man);
+ file_options.setComponentOrientation(Dictionary.getOrientation());
+
+ file_save = new JMenuItem(Dictionary.get("Menu.File_Save"));
+ file_save.addActionListener(Gatherer.g_man);
+ file_save.setEnabled(false);
+ file_save.setComponentOrientation(Dictionary.getOrientation());
+
+ // Layout (file menu)
+ file.add(file_new);
+ file.add(file_open);
+ file.add(file_save);
+ file.add(file_close);
+ file.add(new JSeparator());
+ file.add(file_delete);
+ file.add(file_exportas);
+ // these currently don't work. better to just disable them?
+ if (!Gatherer.GS3) {
+ file.add(file_cdimage);
+ }
+ file.add(new JSeparator());
+ file.add(file_associations);
+ file.add(file_options);
+ file.add(new JSeparator());
+ file.add(file_exit);
+
+ // Edit menu
+ edit = new JMenu();
+ edit.setEnabled(false);
+ edit.setText(Dictionary.get("Menu.Edit"));
+ edit.setComponentOrientation(Dictionary.getOrientation());
+ String modkey = "ctrl";
+ if(Utility.isMac()) { // on Mac, we now use the Apple key instead of Ctrl for GLI/GEMS edit shortcuts
+ // http://stackoverflow.com/questions/5585919/creating-unicode-character-from-its-number
+ char appleKeyCodepoint = 0x2318; // applekey symbol unicode codepoint: U+2318 (\u2318)
+ String appleKeySymbol = String.valueOf(appleKeyCodepoint);
+ modkey = appleKeySymbol;
+ }
+
+ edit_cut = new JMenuItem(Dictionary.get("Menu.Edit_Cut", modkey));
+ edit_cut.addActionListener(Gatherer.g_man);
+ edit_cut.setComponentOrientation(Dictionary.getOrientation());
+
+ edit_copy = new JMenuItem(Dictionary.get("Menu.Edit_Copy", modkey));
+ edit_copy.addActionListener(Gatherer.g_man);
+ edit_copy.setComponentOrientation(Dictionary.getOrientation());
+
+ edit_paste = new JMenuItem(Dictionary.get("Menu.Edit_Paste", modkey));
+ edit_paste.addActionListener(Gatherer.g_man);
+ edit_paste.setComponentOrientation(Dictionary.getOrientation());
+
+ // Layout (edit menu)
+ edit.add(edit_cut);
+ edit.add(edit_copy);
+ edit.add(edit_paste);
+
+ // if GS3, then we have a menu item that allows editing of config files
+ if(Gatherer.GS3) {
+
+ edit_config = new JMenuItem(Dictionary.get("Menu.Edit_Config"));
+ // handle the actual Edit > ColConfig.xml menu item
+ edit_config.addActionListener(Gatherer.g_man);
+ edit_config.setComponentOrientation(Dictionary.getOrientation());
+ edit.add(edit_config);
+ // The Edit menu itself now listens, in order to gray out the
+ // Edit > collectionConfig.xml option when no collection is open
+ // (JMenu doesn't work with ActionListener only with MenuListener, see
+ // http://stackoverflow.com/questions/9862165/jmenu-actionlistener)
+ edit.addMenuListener(menu_listener);
+
+ }
+
+ // Help menu
+ help = new JMenu();
+ help.setIcon(HELP_ICON);
+ help.setText(Dictionary.get("Menu.Help"));
+ help.setComponentOrientation(Dictionary.getOrientation());
+
+ help_general = new JMenuItem(Dictionary.get("Source.General"));
+ help_general.addActionListener(Gatherer.g_man);
+ help_general.setComponentOrientation(Dictionary.getOrientation());
+
+ help_download = new JMenuItem(Dictionary.get("GUI.Download"), BLANK_ICON);
+ help_download.addActionListener(Gatherer.g_man);
+ help_download.setComponentOrientation(Dictionary.getOrientation());
+
+ help_gather = new JMenuItem(Dictionary.get("GUI.Gather"), BLANK_ICON);
+ help_gather.addActionListener(Gatherer.g_man);
+ help_gather.setComponentOrientation(Dictionary.getOrientation());
+
+ help_enrich = new JMenuItem(Dictionary.get("GUI.Enrich"), BLANK_ICON);
+ help_enrich.addActionListener(Gatherer.g_man);
+ help_enrich.setComponentOrientation(Dictionary.getOrientation());
+
+ help_design = new JMenuItem(Dictionary.get("GUI.Design"), BLANK_ICON);
+ help_design.addActionListener(Gatherer.g_man);
+ help_design.setComponentOrientation(Dictionary.getOrientation());
+
+ help_create = new JMenuItem(Dictionary.get("GUI.Create"), BLANK_ICON);
+ help_create.addActionListener(Gatherer.g_man);
+ help_create.setComponentOrientation(Dictionary.getOrientation());
+
+ help_format = new JMenuItem(Dictionary.get("GUI.Format"), BLANK_ICON);
+ help_format.addActionListener(Gatherer.g_man);
+ help_format.setComponentOrientation(Dictionary.getOrientation());
+
+ help_about = new JMenuItem(Dictionary.get("Menu.Help_About"));
+ help_about.addActionListener(Gatherer.g_man);
+ help_about.setComponentOrientation(Dictionary.getOrientation());
+
+ // Layout (help menu)
+ help.add(help_general);
+ help.add(new JSeparator());
+ if (Configuration.get("workflow.download", true) && Gatherer.isDownloadEnabled) {
+ help.add(help_download);
+ }
+ if (Configuration.get("workflow.gather", true)) {
+ help.add(help_gather);
+ }
+ if (Configuration.get("workflow.enrich", true)) {
+ help.add(help_enrich);
+ }
+ if (Configuration.get("workflow.design", true)) {
+ help.add(help_design);
+ }
+ if (Configuration.get("workflow.create", true)) {
+ help.add(help_create);
+ }
+ if (Configuration.get("workflow.format", true)) {
+ help.add(help_format);
+ }
+ help.add(new JSeparator());
+ help.add(help_about);
+
+ // Layout (menu bar)
+ this.add(file);
+ this.add(Box.createHorizontalStrut(15));
+ this.add(edit);
+ this.add(Box.createHorizontalGlue());
+ this.add(help);
+ }
+
+ /** Once a quit has been requested by the user, prevent any further menu selections. */
+ public void exit() {
+ file.setEnabled(false);
+ edit.setEnabled(false);
+ help.setEnabled(false);
+ }
+
+ public void refresh(int refresh_reason, boolean ready)
+ {
+ file_close.setEnabled(ready);
+ file_save.setEnabled(ready);
+ }
+
+
+ /** In order to provide context aware help advice we keep track of which
+ * tab the user has open, and then highlight that help menu item with
+ * separators.
+ * @param tab_index The index of the selected tab (0-7).
+ */
+ public void tabSelected(int tab_index) {
+ JMenuItem selected;
+ if(current_tab != -1) {
+ // Remove the image
+ selected = help.getItem(current_tab);
+ if(selected != null) {
+ selected.setIcon(BLANK_ICON);
+ }
+ }
+ current_tab = tab_index + 2;
+ selected = help.getItem(current_tab);
+ if(selected != null) {
+ selected.setIcon(HELP_ICON);
+ }
+ selected = null;
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/MetadataElementListCellRenderer.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/MetadataElementListCellRenderer.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/MetadataElementListCellRenderer.java (revision 31635)
@@ -0,0 +1,82 @@
+package org.greenstone.gatherer.gui;
+
+
+import java.awt.*;
+import javax.swing.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.metadata.MetadataElement;
+import org.greenstone.gatherer.metadata.MetadataTools;
+
+
+public class MetadataElementListCellRenderer
+ extends JLabel
+ implements ListCellRenderer
+{
+ private byte previous_directionality = Character.DIRECTIONALITY_UNDEFINED;
+
+
+ public byte getDirectionality()
+ {
+ if (previous_directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT || previous_directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC || previous_directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING || previous_directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE) {
+ return Character.DIRECTIONALITY_RIGHT_TO_LEFT;
+ }
+ else {
+ return Character.DIRECTIONALITY_LEFT_TO_RIGHT;
+ }
+ }
+
+
+ public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus)
+ {
+ if (value instanceof MetadataElement) {
+ MetadataElement metadata_element = (MetadataElement) value;
+
+ String interface_language_code = Configuration.getLanguage();
+ String metadata_element_display = "";
+
+ String metadata_element_display_name = metadata_element.getDisplayName();
+ if (metadata_element_display_name != null) {
+ metadata_element_display += metadata_element_display_name + ":";
+ }
+ else {
+ metadata_element_display += metadata_element.getName() + ":";
+ }
+
+ String metadata_element_definition = MetadataTools.getMetadataElementAttribute(metadata_element, "definition", interface_language_code, "en");
+ if (metadata_element_definition != null) {
+ metadata_element_display += " " + metadata_element_definition;
+ }
+
+// String metadata_element_comment = MetadataTools.getMetadataElementAttribute(metadata_element, "comment", interface_language_code, "en");
+// if (metadata_element_comment != null) {
+// metadata_element_display += " " + metadata_element_comment;
+// }
+
+ setText(metadata_element_display);
+
+// // Determine if the text should be left aligned or right aligned
+// String text = metadata_element_display;
+// int text_index = 0;
+// byte directionality = Character.DIRECTIONALITY_UNDEFINED;
+// while (directionality == Character.DIRECTIONALITY_UNDEFINED && text_index < text.length()) {
+// directionality = Character.getDirectionality(text.charAt(text_index));
+// text_index++;
+// }
+// if (directionality != previous_directionality) {
+// previous_directionality = directionality;
+// if (directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT || directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC || directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING || directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE) {
+// setHorizontalAlignment(JLabel.TRAILING);
+// setHorizontalTextPosition(JLabel.TRAILING);
+// setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
+// }
+// else {
+// setHorizontalAlignment(JLabel.LEADING);
+// setHorizontalTextPosition(JLabel.LEADING);
+// setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
+// }
+// }
+ }
+
+ return this;
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/MetadataImportMappingPrompt.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/MetadataImportMappingPrompt.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/MetadataImportMappingPrompt.java (revision 31635)
@@ -0,0 +1,197 @@
+package org.greenstone.gatherer.gui;
+
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import javax.swing.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.metadata.MetadataElement;
+import org.greenstone.gatherer.metadata.MetadataSet;
+import org.greenstone.gatherer.metadata.MetadataSetManager;
+import org.greenstone.gatherer.metadata.MetadataTools;
+
+
+public class MetadataImportMappingPrompt
+ implements ActionListener
+{
+ final static public int ADD_BUTTON_PRESSED = 0;
+ final static public int MERGE_BUTTON_PRESSED = 1;
+ final static public int IGNORE_BUTTON_PRESSED = 2;
+ final static private Dimension DIALOG_SIZE = new Dimension(640, 180);
+
+ private int result;
+ private String metadata_element_name_full = null;
+ private GComboBox metadata_sets_combobox = null;
+ private GComboBox metadata_elements_combobox = null;
+ private JButton add_button = null;
+ private JButton merge_button = null;
+ private JButton ignore_button = null;
+ private JDialog on_screen = null;
+
+
+ public MetadataImportMappingPrompt(String metadata_element_name_full)
+ {
+ this.metadata_element_name_full = metadata_element_name_full;
+
+ // Construction and configuration
+ JDialog dialog = new ModalDialog(Gatherer.g_man);
+ dialog.setComponentOrientation(Dictionary.getOrientation());
+ dialog.setModal(true);
+ dialog.setSize(DIALOG_SIZE);
+ dialog.setJMenuBar(new SimpleMenuBar("importingpreviouslyassignedmetadata"));
+ dialog.setTitle(Dictionary.get("MIMP.Title"));
+
+ // All the loaded metadata sets except the extracted metadata set are applicable
+ ArrayList metadata_sets = MetadataSetManager.getMetadataSets();
+ for (int i = metadata_sets.size() - 1; i >= 0; i--) {
+ if (((MetadataSet) metadata_sets.get(i)).getNamespace().equals(MetadataSetManager.EXTRACTED_METADATA_NAMESPACE)) {
+ metadata_sets.remove(i);
+ }
+ }
+
+ add_button = new GLIButton(Dictionary.get("MIMP.Add"), Dictionary.get("MIMP.Add_Tooltip"));
+ add_button.addActionListener(this);
+
+ merge_button = new GLIButton(Dictionary.get("MIMP.Merge"), Dictionary.get("MIMP.Merge_Tooltip"));
+ merge_button.addActionListener(this);
+ merge_button.setEnabled(true);
+
+ ignore_button = new GLIButton(Dictionary.get("MIMP.Ignore"), Dictionary.get("MIMP.Ignore_Tooltip"));
+ ignore_button.addActionListener(this);
+ ignore_button.setEnabled(true);
+
+ // !! Need to add instructions: "MIMP.Instructions", args: source elem
+ metadata_elements_combobox = new GComboBox();
+
+ metadata_sets_combobox = new GComboBox(metadata_sets);
+ metadata_sets_combobox.addActionListener(new MetadataSetListSelectionListener());
+ metadata_sets_combobox.setSelectedIndex(0);
+
+ // Layout
+ JPanel left_pane = new JPanel();
+ left_pane.setComponentOrientation(Dictionary.getOrientation());
+ left_pane.setLayout(new GridLayout(3,1));
+
+ JLabel tmp_lable;
+
+ tmp_lable = new JLabel(Dictionary.get("MIMP.Source_Element"));
+ tmp_lable.setComponentOrientation(Dictionary.getOrientation());
+ left_pane.add(tmp_lable);
+
+ tmp_lable = new JLabel(Dictionary.get("MIMP.Target_Set"));
+ tmp_lable.setComponentOrientation(Dictionary.getOrientation());
+ left_pane.add(tmp_lable);
+
+ tmp_lable = new JLabel(Dictionary.get("MIMP.Target_Element"));
+ tmp_lable.setComponentOrientation(Dictionary.getOrientation());
+ left_pane.add(tmp_lable);
+
+ JPanel right_pane = new JPanel();
+ right_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ right_pane.setLayout(new GridLayout(3,1));
+ tmp_lable = new JLabel(metadata_element_name_full);
+ tmp_lable.setComponentOrientation(Dictionary.getOrientation());
+ right_pane.add(tmp_lable);
+ right_pane.add(metadata_sets_combobox);
+ right_pane.add(metadata_elements_combobox);
+
+ JPanel button_pane = new JPanel();
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+ button_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5));
+ button_pane.setLayout(new GridLayout(1,3));
+ button_pane.add(add_button);
+ button_pane.add(merge_button);
+ button_pane.add(ignore_button);
+
+ JPanel content_pane = (JPanel) dialog.getContentPane();
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+ content_pane.setLayout(new BorderLayout());
+ content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ content_pane.add(left_pane, BorderLayout.LINE_START);
+ content_pane.add(right_pane, BorderLayout.CENTER);
+ content_pane.add(button_pane, BorderLayout.SOUTH);
+
+ // Display
+ Dimension screen_size = Configuration.screen_size;
+ dialog.setLocation((screen_size.width - DIALOG_SIZE.width) / 2, (screen_size.height - DIALOG_SIZE.height) / 2);
+ on_screen = dialog;
+ on_screen.setVisible(true); // Blocks until hidden.
+ on_screen.dispose();
+ on_screen = null;
+ }
+
+
+ /** Any implementation of ActionListener must include this method so that we can be informed when an action has occured.
+ * @param event An ActionEvent containing information gatherer when this event occured.
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ Object esrc = event.getSource();
+
+ if (esrc == add_button) {
+ result = ADD_BUTTON_PRESSED;
+ }
+ if (esrc == merge_button) {
+ result = MERGE_BUTTON_PRESSED;
+ }
+ if (esrc == ignore_button) {
+ result = IGNORE_BUTTON_PRESSED;
+ }
+
+ on_screen.setVisible(false);
+ }
+
+
+ public MetadataElement getSelectedMetadataElement()
+ {
+ return (MetadataElement) metadata_elements_combobox.getSelectedItem();
+ }
+
+
+ public MetadataSet getSelectedMetadataSet()
+ {
+ return (MetadataSet) metadata_sets_combobox.getSelectedItem();
+ }
+
+
+ public int getResult()
+ {
+ return result;
+ }
+
+
+ private class MetadataSetListSelectionListener
+ implements ActionListener
+ {
+ public void actionPerformed(ActionEvent event)
+ {
+ boolean enable_add_button = true;
+ String metadata_element_name = MetadataTools.getMetadataElementName(metadata_element_name_full);
+
+ MetadataSet metadata_set = (MetadataSet) metadata_sets_combobox.getSelectedItem();
+ ArrayList metadata_elements = metadata_set.getMetadataSetElements();
+
+ metadata_elements_combobox.removeAllItems();
+ for (int i = 0; i < metadata_elements.size(); i++) {
+ MetadataElement metadata_element = (MetadataElement) metadata_elements.get(i);
+ metadata_elements_combobox.addItem(metadata_element);
+ if (metadata_element.getName().equals(metadata_element_name)) {
+ metadata_elements_combobox.setSelectedIndex(i);
+ enable_add_button = false;
+ }
+ }
+
+ add_button.setEnabled(enable_add_button);
+
+ // are there any elements in the set? if not (e.g. with exp set),
+ // disable merge
+ if (metadata_elements.size()==0) {
+ merge_button.setEnabled(false);
+ }
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/MetadataSetDialog.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/MetadataSetDialog.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/MetadataSetDialog.java (revision 31635)
@@ -0,0 +1,428 @@
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import java.util.ArrayList;
+import java.util.Vector;
+import java.io.File;
+import java.util.Observer;
+import java.util.Observable;
+
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.cdm.DynamicListModel;
+import org.greenstone.gatherer.metadata.MetadataSet;
+import org.greenstone.gatherer.metadata.MetadataSetManager;
+import org.greenstone.gatherer.gems.*;
+
+public class MetadataSetDialog
+ extends ModalDialog {
+
+ static private Dimension SIZE = new Dimension(600, 300);
+ static private Dimension ADD_SIZE = new Dimension(600, 500);
+
+ private ArrayList current_metadata_sets;
+ private DynamicListModel current_metadata_model;
+
+ private JButton add_button = null;
+ private JButton edit_button = null;
+ private JButton remove_button = null;
+
+ private JButton close_button = null;
+
+ private JList current_set_list = null;
+ private MetadataSetDialog set_dialog = null;
+ private boolean sets_changed = false;
+
+ private GEMS gems = null;
+
+ public MetadataSetDialog() {
+ super(Gatherer.g_man, true);
+ set_dialog = this;
+ this.setComponentOrientation(Dictionary.getOrientation());
+ setJMenuBar(new SimpleMenuBar("selectingmetadatasets"));
+ setSize(SIZE);
+ setTitle(Dictionary.get("MetadataSetDialog.Title"));
+
+ current_metadata_sets = MetadataSetManager.getMetadataSets();
+ current_metadata_model = new DynamicListModel();
+
+ int current_size = current_metadata_sets.size();
+ for (int i=0; iJTable.
+ * @param value The value to assign to the cell at [row, column] as an Object .
+ * @param isSelected true if cell is selected.
+ * @param hasFocus true if cell has focus.
+ * @param row The row of the cell to render as an int .
+ * @param column The column of the cell to render as an int .
+ * @return The default table cell renderer Component .
+ */
+ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
+ {
+ JComponent component = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
+ component.setComponentOrientation(Dictionary.getOrientation());
+
+ int real_column = table.convertColumnIndexToModel(column);
+ // First column: inherited metadata icon
+ if (real_column == 0 && value != null) {
+ component = new JLabel(JarTools.getImage("upfolder.gif"));
+ component.setToolTipText(Dictionary.get("EnrichPane.InheritedMetadata_Tooltip"));
+ }
+
+ // Make sure the focus always stay in the value cell of the selected row
+ if (real_column == 2 && isSelected) {
+ table.editCellAt(row, column);
+ if (table.isEditing()) {
+ table.getEditorComponent().requestFocus();
+ }
+ }
+
+ // Set up the component
+ component.setOpaque(true);
+
+ // Foreground
+ if (metadata_value_table_model.isCommon(row)) {
+ component.setForeground(Color.black);
+ }
+ else {
+ component.setForeground(Color.gray);
+ }
+
+ // Background
+ if (isSelected) {
+ component.setBackground(Configuration.getColor("coloring.workspace_heading_background", true));
+ }
+ else {
+ if (real_column < 2) {
+ component.setBackground(Configuration.getColor("coloring.collection_heading_background", true));
+ }
+ else {
+ component.setBackground(Configuration.getColor("coloring.collection_tree_background", true));
+ }
+ }
+
+ // The value column of cells never paints focus
+ if (real_column == 2) {
+ component.setBorder(BorderFactory.createEmptyBorder(0,2,0,0));
+ }
+
+ // We set a tooltip over the element column containing the definition of the element
+ if (value instanceof MetadataElement) {
+ String interface_language_code = Configuration.getLanguage();
+ String metadata_element_definition = MetadataTools.getMetadataElementAttribute((MetadataElement) value, "definition", interface_language_code, "en");
+ if (metadata_element_definition != null) {
+ component.setToolTipText(Utility.formatHTMLWidth(metadata_element_definition, 60));
+ }
+ }
+
+ return component;
+ }
+ }
+
+
+ private class MetadataValueTableKeyListener
+ extends KeyAdapter
+ {
+ /** Gives notification of key events on the text field */
+ public void keyPressed(KeyEvent key_event)
+ {
+ // Enter: save the current value then add a blank row for the selected metadata element
+ if (key_event.getKeyCode() == KeyEvent.VK_ENTER) {
+ // ...but not for extracted metadata elements of course
+ MetadataValueTableEntry metadata_value_table_entry = getSelectedMetadataValueTableEntry();
+ if (!metadata_value_table_entry.getMetadataElement().isExtractedMetadataElement()) {
+ addBlankRowForSelectedMetadataElement();
+ }
+
+ // We do not want this event to be processed by the table also
+ key_event.consume();
+ }
+ }
+ }
+
+
+ private class MetadataValueTextFieldMouseListener
+ extends MouseAdapter
+ {
+ public void mouseClicked(MouseEvent mouse_event)
+ {
+ // Double-click: pop up an editor dialog for large metadata values
+ if (mouse_event.getClickCount() == 2) {
+ EditorDialog editor_dialog = new EditorDialog();
+ String new_metadata_value_string = editor_dialog.display(metadata_value_text_field.getText());
+ if (new_metadata_value_string != null) {
+ setMetadataValueTextFieldValue(new_metadata_value_string);
+ }
+ }
+ }
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/MetadataValueTreePane.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/MetadataValueTreePane.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/MetadataValueTreePane.java (revision 31635)
@@ -0,0 +1,330 @@
+/**
+ *############################################################################
+ * A component of the Greenstone Librarian Interface, part of the Greenstone
+ * digital library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
+ *
+ * Copyright (C) 2005 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################################################
+ */
+
+package org.greenstone.gatherer.gui;
+
+
+import java.awt.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.tree.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.metadata.MetadataElement;
+import org.greenstone.gatherer.metadata.MetadataValue;
+import org.greenstone.gatherer.metadata.MetadataValueTreeNode;
+import org.greenstone.gatherer.util.PatternTokenizer;
+
+
+/**
+ * This class is a little bit complex, a little bit subtle, and a little bit odd.
+ * The strange interaction model is due to the fact that it is very tightly
+ * coupled to the EnrichPane.
+ *
+ * The interaction is complex because there are three separate controls that the
+ * user may interact with, each of which can affect the other two. The three
+ * controls are:
+ * - The "assigned metadata" table, at the top right of the meta edit pane.
+ * - The "metadata value" text field, where users can type in new values.
+ * - The "metadata value tree", which shows other values that have been
+ * assigned to the selected metadata element.
+ *
+ * The interactions are:
+ * - The "assigned metadata" table
+ * Users may select one (and only one) row in this table. Selecting a row
+ * chooses one metadata element. The text field will be set to the current
+ * value of the metadata element. This value will also be selected in the
+ * metadata value tree.
+ *
+ * - The "metadata value" text field
+ * If the value the user has typed in this is a prefix of an entry in the
+ * value tree, this value will be selected in the value tree. In this case,
+ * pressing "Tab" will complete the value (ie. make the value in the text
+ * field the same as the value in the tree). This is to allow users to
+ * quickly and easily assign existing metadata values to new documents.
+ *
+ * - The "metadata value tree"
+ * Selecting a value in the tree will set the text field to this value.
+ */
+public class MetadataValueTreePane
+ extends JPanel
+{
+ private boolean ignore_tree_selection_event = false;
+ /** The metadata value that the tree pane is built on */
+ private MetadataValue metadata_value = null;
+
+ /** Used to display either the MetadataValueTree (when metadata is selected) or a placeholder panel */
+ private CardLayout card_layout = null;
+ /** The name of the panel containing the metadata value tree */
+ private String METADATA_VALUE_TREE_CARD = "";
+ /** The name of the panel containing the "extracted metadata element selected" placeholder */
+ private String EXTRACTED_METADATA_ELEMENT_SELECTED_CARD = "Extracted metadata element selected";
+ /** The name of the panel containing the "inherited metadata selected" placeholder */
+ private String INHERITED_METADATA_SELECTED_CARD = "Inherited metadata selected";
+ /** The name of the panel containing the "not only file only metadata selected" placeholder */
+ private String NOT_ONE_FILE_ONLY_METADATA_SELECTED_CARD = "Not one file only metadata selected";
+ /** The name of the panel containing the "no metadata element selected" placeholder */
+ private String NO_METADATA_ELEMENT_SELECTED_CARD = "No metadata element selected";
+
+ private JLabel metadata_value_tree_label = new JLabel();
+ private JTextArea extracted_metadata_element_selected_message;
+ private JTree metadata_value_tree;
+
+
+ public MetadataValueTreePane()
+ {
+ super();
+ JScrollPane scrol_tmp;
+ this.setComponentOrientation(Dictionary.getOrientation());
+ metadata_value_tree_label.setComponentOrientation(Dictionary.getOrientation());
+ // Card containing the metadata value tree
+ metadata_value_tree = new JTree();
+ metadata_value_tree.setComponentOrientation(Dictionary.getOrientation());
+ metadata_value_tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
+ metadata_value_tree.setModel(null);
+ metadata_value_tree.setRootVisible(false);
+ metadata_value_tree.putClientProperty("JTree.lineStyle", "Angled");
+
+ JPanel metadata_value_tree_pane = new JPanel();
+ metadata_value_tree_pane.setComponentOrientation(Dictionary.getOrientation());
+ metadata_value_tree_pane.setLayout(new BorderLayout());
+ metadata_value_tree_pane.add(metadata_value_tree_label, BorderLayout.NORTH);
+ scrol_tmp = new JScrollPane(metadata_value_tree);
+ scrol_tmp.setComponentOrientation(Dictionary.getOrientation());
+ metadata_value_tree_pane.add(scrol_tmp, BorderLayout.CENTER);
+
+ // Card containing the "extracted metadata element selected" message
+ extracted_metadata_element_selected_message = new JTextArea("");
+ extracted_metadata_element_selected_message.setComponentOrientation(Dictionary.getOrientation());
+ extracted_metadata_element_selected_message.setEditable(false);
+ extracted_metadata_element_selected_message.setLineWrap(true);
+ extracted_metadata_element_selected_message.setOpaque(false);
+ extracted_metadata_element_selected_message.setWrapStyleWord(true);
+
+ JPanel extracted_metadata_element_selected_pane = new JPanel();
+ extracted_metadata_element_selected_pane.setComponentOrientation(Dictionary.getOrientation());
+ extracted_metadata_element_selected_pane.setBorder(BorderFactory.createEmptyBorder(25,0,0,0));
+ extracted_metadata_element_selected_pane.setLayout(new BorderLayout());
+ extracted_metadata_element_selected_pane.add(extracted_metadata_element_selected_message, BorderLayout.CENTER);
+
+ // Card containing the "inherited metadata selected" message
+ JTextArea inherited_metadata_selected_message = new JTextArea(Dictionary.get("EnrichPane.InheritedMetadataSelected"));
+ inherited_metadata_selected_message.setEditable(false);
+ inherited_metadata_selected_message.setLineWrap(true);
+ inherited_metadata_selected_message.setOpaque(false);
+ inherited_metadata_selected_message.setWrapStyleWord(true);
+ inherited_metadata_selected_message.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel inherited_metadata_selected_pane = new JPanel();
+ inherited_metadata_selected_pane.setBorder(BorderFactory.createEmptyBorder(25,0,0,0));
+ inherited_metadata_selected_pane.setLayout(new BorderLayout());
+ inherited_metadata_selected_pane.add(inherited_metadata_selected_message, BorderLayout.CENTER);
+ inherited_metadata_selected_pane.setComponentOrientation(Dictionary.getOrientation());
+ // Card containing the "not one file only metadata selected" message
+ JTextArea not_one_file_only_metadata_selected_message = new JTextArea(Dictionary.get("EnrichPane.NotOneFileOnlyMetadataSelected"));
+ not_one_file_only_metadata_selected_message.setEditable(false);
+ not_one_file_only_metadata_selected_message.setLineWrap(true);
+ not_one_file_only_metadata_selected_message.setOpaque(false);
+ not_one_file_only_metadata_selected_message.setWrapStyleWord(true);
+ not_one_file_only_metadata_selected_message.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel not_one_file_only_metadata_selected_pane = new JPanel();
+ not_one_file_only_metadata_selected_pane.setBorder(BorderFactory.createEmptyBorder(25,0,0,0));
+ not_one_file_only_metadata_selected_pane.setLayout(new BorderLayout());
+ not_one_file_only_metadata_selected_pane.add(not_one_file_only_metadata_selected_message, BorderLayout.CENTER);
+ not_one_file_only_metadata_selected_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ // Card containing the "no metadata element selected" message
+ JLabel no_metadata_element_selected_label = new JLabel(Dictionary.get("EnrichPane.No_Metadata_Element"));
+ no_metadata_element_selected_label.setHorizontalAlignment(JLabel.CENTER);
+ no_metadata_element_selected_label.setOpaque(false);
+ no_metadata_element_selected_label.setVerticalAlignment(JLabel.CENTER);
+ no_metadata_element_selected_label.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel no_metadata_element_selected_pane = new JPanel();
+ no_metadata_element_selected_pane.setLayout(new BorderLayout());
+ no_metadata_element_selected_pane.add(no_metadata_element_selected_label, BorderLayout.CENTER);
+ no_metadata_element_selected_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ card_layout = new CardLayout();
+
+ this.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ this.setFont(Configuration.getFont("general.font", false));
+ this.setLayout(card_layout);
+ this.add(no_metadata_element_selected_pane, NO_METADATA_ELEMENT_SELECTED_CARD);
+ this.add(extracted_metadata_element_selected_pane, EXTRACTED_METADATA_ELEMENT_SELECTED_CARD);
+ this.add(inherited_metadata_selected_pane, INHERITED_METADATA_SELECTED_CARD);
+ this.add(not_one_file_only_metadata_selected_pane, NOT_ONE_FILE_ONLY_METADATA_SELECTED_CARD);
+ this.add(metadata_value_tree_pane, METADATA_VALUE_TREE_CARD);
+ }
+
+
+ public void addMetadataValueTreeSelectionListener(TreeSelectionListener tree_selection_listener)
+ {
+ metadata_value_tree.addTreeSelectionListener(tree_selection_listener);
+ }
+
+
+ /** Returns a TreePath for the node most closely matching the metadata value. */
+ private TreePath getClosestPath(String metadata_value_string)
+ {
+ if (metadata_value_tree.getModel() == null) {
+ return null;
+ }
+
+ // Start at the root of the tree
+ MetadataValueTreeNode tree_node = (MetadataValueTreeNode) metadata_value_tree.getModel().getRoot();
+
+ // Separate hierarchical values
+ PatternTokenizer tokenizer = new PatternTokenizer(metadata_value_string, MetadataValueTreeNode.METADATA_VALUE_TREE_NODE_HIERARCHY_TOKEN);
+ while (tokenizer.hasMoreTokens()) {
+ String token = tokenizer.nextToken();
+
+ // All components except the last must match exactly
+ if (tokenizer.hasMoreTokens()) {
+ for (int i = 0; i < tree_node.getChildCount(); i++) {
+ MetadataValueTreeNode child_node = (MetadataValueTreeNode) tree_node.getChildAt(i);
+ if (child_node.getValue().equals(token)) {
+ // The matching node has been found, so move onto the next token
+ tree_node = child_node;
+ break;
+ }
+ }
+ }
+
+ // The last component may match partially
+ else {
+ for (int i = 0; i < tree_node.getChildCount(); i++) {
+ MetadataValueTreeNode child_node = (MetadataValueTreeNode) tree_node.getChildAt(i);
+ if (child_node.getFullValue().startsWith(metadata_value_string)) {
+ // The closest node has been found, so return its path
+ return new TreePath(child_node.getPath());
+ }
+ }
+
+ // Not even a partial match
+ return null;
+ }
+ }
+
+ // If nothing else, return the path of the root node
+ return new TreePath(tree_node.getPath());
+ }
+
+
+ public MetadataValueTreeNode getSelectedMetadataValueTreeNode()
+ {
+ if (metadata_value_tree.getSelectionCount() == 0 || ignore_tree_selection_event) {
+ return null;
+ }
+
+ return (MetadataValueTreeNode) metadata_value_tree.getSelectionPath().getLastPathComponent();
+ }
+
+
+ public void rebuild(MetadataValue new_metadata_value)
+ {
+ // If the metadata value hasn't changed there is nothing to do
+ if (new_metadata_value == metadata_value) {
+ return;
+ }
+
+ MetadataElement metadata_element = ((metadata_value != null) ? metadata_value.getMetadataElement() : null);
+ metadata_value = new_metadata_value;
+
+ // Selection cleared, so display "no metadata element selected" card and we're done
+ if (new_metadata_value == null) {
+ metadata_value_tree.setModel(null);
+ card_layout.show(this, NO_METADATA_ELEMENT_SELECTED_CARD);
+ return;
+ }
+
+ // If a piece of inherited metadata is selected, display "inherited metadata selected" card and we're done
+ if (new_metadata_value.isInheritedMetadata()) {
+ card_layout.show(this, INHERITED_METADATA_SELECTED_CARD);
+ return;
+ }
+
+ // If the metadata applies to multiple files, display "not one file only metadata selected" card and we're done
+ if (new_metadata_value.isOneFileOnlyMetadata() == false) {
+ card_layout.show(this, NOT_ONE_FILE_ONLY_METADATA_SELECTED_CARD);
+ return;
+ }
+
+ // If an extracted metadata element is selected, display "extracted metadata element selected" card
+ MetadataElement new_metadata_element = new_metadata_value.getMetadataElement();
+ if (new_metadata_element.isExtractedMetadataElement()) {
+ String[] args = new String[1];
+ args[0] = new_metadata_element.getDisplayName();
+ extracted_metadata_element_selected_message.setText(Dictionary.get("EnrichPane.AutoMessage", args));
+ card_layout.show(this, EXTRACTED_METADATA_ELEMENT_SELECTED_CARD);
+ return;
+ }
+
+ // Display the value tree for the selected metadata element
+ String[] args = new String[1];
+ args[0] = new_metadata_element.getDisplayName();
+ metadata_value_tree_label.setText(Dictionary.get("EnrichPane.ExistingValues", args));
+
+ metadata_value_tree.setModel(new_metadata_element.getMetadataValueTreeModel());
+ card_layout.show(this, METADATA_VALUE_TREE_CARD);
+ selectBestPathForMetadataValue(new_metadata_value.getFullValue());
+ }
+
+
+ public void selectBestPathForMetadataValue(String metadata_value_string)
+ {
+ TreePath best_path = getClosestPath(metadata_value_string);
+
+ // Select the new path in the tree
+ // The tree selection event this causes must be ignored, since it alters the metadata text field value
+ ignore_tree_selection_event = true;
+ metadata_value_tree.setSelectionPath(best_path);
+ ignore_tree_selection_event = false;
+
+ // If a folder has been specified, make sure it is expanded
+ if (metadata_value_string.endsWith(MetadataValueTreeNode.METADATA_VALUE_TREE_NODE_HIERARCHY_TOKEN) && !metadata_value_tree.isExpanded(best_path)) {
+ metadata_value_tree.expandPath(best_path);
+ }
+
+ // Make sure the tree path is expanded
+ metadata_value_tree.makeVisible(best_path);
+
+ // Make sure the tree path is visible on screen
+ final Rectangle bounds = metadata_value_tree.getPathBounds(best_path);
+ if (bounds != null) {
+ bounds.x = 0; // Want the tree to be always flushed left
+
+ // Scrolling the path to be visible must be done on the GUI thread
+ Runnable task = new Runnable() {
+ public void run() {
+ metadata_value_tree.scrollRectToVisible(bounds);
+ }
+ };
+ SwingUtilities.invokeLater(task);
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/ModalDialog.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/ModalDialog.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/ModalDialog.java (revision 31635)
@@ -0,0 +1,218 @@
+/*#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+
+
+/** An extension of the JDialog that overrides the JVM's typical modal behaviour. This typical behaviour is that when a modal dialog is opened, all other windows cease to respond to user events until the modal dialog is disposed. However this prevents us opening the help documents property whenever a modal dialog is open. Thus we override the modal behaviour so that only the owner frame or dialog is blocked.
+ * Note that because we always call the super constructor with modal set to false, this should be made visible with setVisible(true) rather than show() which will return straight away. */
+// feedback note: veronika had changed all the super constructor calls to
+// use modal instead of false - not sure if this is needed so I have not
+// put that in. --kjdon
+public class ModalDialog
+ extends JDialog {
+
+ /** The current modal dialog being shown on screen, if any. */
+ static public ModalDialog current_modal = null;
+
+ /** true if this dialog should be modal, ie block user actions to its owner window, false otherwise. */
+ protected boolean modal = false;
+ /** true if this dialog is currently waiting some thread. */
+ protected boolean waiting = false;
+
+ /** Constructor.
+ */
+ public ModalDialog() {
+ super((Frame)null, "", false);
+ this.setComponentOrientation(Dictionary.getOrientation());
+ }
+
+ /** Constructor.
+ * @param parent the Dialog which is the owener of this dialog.
+ */
+ public ModalDialog(Dialog parent) {
+ super(parent, "", false);
+ this.setComponentOrientation(Dictionary.getOrientation());
+ }
+
+ /** Constructor.
+ * @param parent the Dialog which is the owener of this dialog.
+ * @param modal true if this dialog should be modal, ie block user actions to its owner window, false otherwise.
+ */
+ public ModalDialog(Dialog parent, boolean modal) {
+ super(parent, "", false);
+ this.setComponentOrientation(Dictionary.getOrientation());
+ this.modal = modal;
+ }
+
+ /** Constructor.
+ * @param parent the Dialog which is the owner of this dialog.
+ * @param title the String to use as the title for this dialog.
+ */
+ public ModalDialog(Dialog parent, String title) {
+ super (parent, title, false);
+ this.setComponentOrientation(Dictionary.getOrientation());
+ this.modal = false;
+ }
+
+ /** Constructor.
+ * @param parent the Dialog which is the owener of this dialog.
+ * @param title the String to use as the title for this dialog.
+ * @param modal true if this dialog should be modal, ie block user actions to its owner window, false otherwise.
+ */
+ public ModalDialog(Dialog parent, String title, boolean modal) {
+ super (parent, title, false);
+ this.setComponentOrientation(Dictionary.getOrientation());
+ this.modal = modal;
+ }
+
+ /** Constructor.
+ * @param parent the Frame which is the owener of this dialog.
+ */
+ public ModalDialog(Frame parent) {
+ super(parent, "", false);
+ this.setComponentOrientation(Dictionary.getOrientation());
+ }
+
+ /** Constructor.
+ * @param parent the Frame which is the owener of this dialog.
+ * @param modal whether this dialog is modal or not
+ */
+ public ModalDialog(Frame parent, boolean modal) {
+ super(parent, "", false);
+ this.setComponentOrientation(Dictionary.getOrientation());
+ this.modal = modal;
+ }
+
+ /** Constructor.
+ * @param parent the Frame which is the owner of this dialog.
+ * @param title the String to use as the title for this dialog.
+ */
+ public ModalDialog(Frame parent, String title) {
+ super (parent, title, false);
+ this.setComponentOrientation(Dictionary.getOrientation());
+ }
+
+ /** Constructor.
+ * @param parent the Frame which is the owener of this dialog.
+ * @param title the String to use as the title for this dialog.
+ * @param modal true if this dialog should be modal, ie block user actions to its owner window, false otherwise.
+ */
+ public ModalDialog(Frame parent, String title, boolean modal) {
+ super (parent, title, false);
+ this.modal = modal;
+ this.setComponentOrientation(Dictionary.getOrientation());
+ }
+
+ public void dispose() {
+ super.dispose();
+ }
+
+ /** Ensures the current dialog is visible. */
+ public void makeVisible() {
+ super.setVisible(true);
+ }
+
+ /** The set visible method is overriden to provide modal functionality. It essentially hijacks control of the event dispatch thread while the dialog is open, only allowing non-user events to be passed to the parent dialog. Furthermore it only has this effect within the current AWT component tree by utilitizing the TreeLock.
+ * @param visible true if this dialog should be painted on-screen, false otherwise.
+ */
+ public void setVisible(boolean visible)
+ {
+ if (visible) {
+ current_modal = this;
+ }
+ else {
+ current_modal = null;
+ }
+
+ // If we are in the AWT Dispatch thread then it is all good.
+ if (SwingUtilities.isEventDispatchThread()) {
+ super.setVisible(visible);
+ if (modal && visible) {
+ EventQueue theQueue = getToolkit().getSystemEventQueue();
+ while (isVisible()) {
+ try {
+ AWTEvent event = theQueue.getNextEvent();
+ Object src = event.getSource();
+
+ // Block all keyboard and mouse events to the parent component
+ if (src.equals(getParent())) {
+ if (event instanceof KeyEvent || event instanceof MouseEvent) {
+ // System.err.println("Event to parent component blocked.");
+ continue;
+ }
+ }
+
+ // Re-dispatch other events
+ if (event instanceof ActiveEvent) {
+ ((ActiveEvent) event).dispatch();
+ }
+ else if (src instanceof Component) {
+ ((Component) src).dispatchEvent(event);
+ }
+ }
+ catch (Exception exception) {
+ DebugStream.printStackTrace(exception);
+ }
+ }
+ }
+ }
+ else {
+ try {
+ SwingUtilities.invokeAndWait(new MakeDialogVisibleTask(this, visible));
+ }
+ catch (Exception exception) {
+ DebugStream.printStackTrace(exception);
+ }
+ }
+ }
+
+ private class MakeDialogVisibleTask
+ implements Runnable {
+ private boolean make_visible;
+ private ModalDialog dialog;
+ public MakeDialogVisibleTask(ModalDialog dialog, boolean make_visible) {
+ this.dialog = dialog;
+ this.make_visible = make_visible;
+ }
+ public void run() {
+ // Blocks until the user dismisses the dialog
+ dialog.setVisible(make_visible);
+ }
+ }
+
+ /** Overridden method so we can control modality and not rely on the Dialog default.
+ * @param modal true if this dialog should be modal, ie block user actions to its owner window, false otherwise.
+ */
+ public void setModal (boolean modal) {
+ this.modal = modal;
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/ModalProgressPopup.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/ModalProgressPopup.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/ModalProgressPopup.java (revision 31635)
@@ -0,0 +1,92 @@
+/**
+ *############################################################################
+ * A component of the Greenstone Librarian Interface, part of the Greenstone
+ * digital library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
+ *
+ * Copyright (C) 2005 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################################################
+ */
+
+package org.greenstone.gatherer.gui;
+
+
+import java.awt.*;
+import javax.swing.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+
+
+public class ModalProgressPopup
+ extends ModalDialog
+{
+ /** The size of the popup */
+ final static private Dimension SIZE = new Dimension(300, 75);
+
+ private String message = null;
+
+
+ public ModalProgressPopup(String title, String message)
+ {
+ super(Gatherer.g_man, title);
+ this.message = message;
+ this.setComponentOrientation(Dictionary.getOrientation());
+ }
+
+
+ /** Method to close the popup. */
+ public void close()
+ {
+ setVisible(false);
+ Gatherer.g_man.wait(false);
+ }
+
+
+ /** Method to display the popup on screen. */
+ public void display()
+ {
+ setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
+ setSize(SIZE);
+
+ // Create
+ JProgressBar progress_bar = new JProgressBar();
+ progress_bar.setComponentOrientation(Dictionary.getOrientation());
+ progress_bar.setIndeterminate(true);
+
+ // Layout
+ JPanel content_pane = (JPanel) getContentPane();
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+ content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ content_pane.setLayout(new BorderLayout());
+
+ JLabel tmp = new JLabel(message);
+ tmp.setComponentOrientation(Dictionary.getOrientation());
+
+ content_pane.add(tmp, BorderLayout.NORTH);
+ content_pane.add(progress_bar, BorderLayout.CENTER);
+
+ Dimension screen_size = Configuration.screen_size;
+ setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
+ setVisible(true);
+
+ // Lock the rest of the GLI interface until close() is called
+ Gatherer.g_man.wait(true);
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/NewCollectionDetailsPrompt.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/NewCollectionDetailsPrompt.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/NewCollectionDetailsPrompt.java (revision 31635)
@@ -0,0 +1,728 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: John Thompson, Greenstone Project, NZDL, University of Waikato
+ *
+ * Copyright (C) 2003 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.File;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.text.*;
+import javax.swing.filechooser.FileFilter;
+import javax.swing.filechooser.FileView;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.collection.BasicCollectionConfiguration;
+import org.greenstone.gatherer.remote.RemoteGreenstoneServer;
+import org.greenstone.gatherer.util.JarTools;
+import org.greenstone.gatherer.util.StaticStrings;
+import org.greenstone.gatherer.util.Utility;
+
+public class NewCollectionDetailsPrompt
+ extends ModalDialog {
+
+
+ static public boolean titleClashes(String collectDir, String title, File current_config_file) {
+ // An empty collection title never clashes with anything. It may look ugly in the final collection but there is nothing wrong with having no title for a particular language.
+ if(title == null || title.length() == 0) {
+ return false;
+ }
+ File collect_directory = new File(collectDir);
+ String file_name = (Gatherer.GS3)? Utility.CONFIG_GS3_FILE : Utility.CONFIG_FILE;
+ File children[] = collect_directory.listFiles();
+ for(int i = 0; children != null && i < children.length; i++) {
+ if(children[i].isDirectory()) {
+ File config_file = new File(children[i], file_name);
+ if(current_config_file == null || !config_file.equals(current_config_file)) {
+ BasicCollectionConfiguration other_collection = new BasicCollectionConfiguration(config_file);
+ if(other_collection.getName().equalsIgnoreCase(title)) {
+ return true;
+ }
+ other_collection = null;
+ }
+ config_file = null;
+ }
+ }
+ return false;
+ }
+
+ private boolean cancelled;
+ private boolean collectDirChanged;
+ private File base_final;
+ private JButton chdir_button;
+ private JButton create_button;
+ private JComboBox base_collection;
+ private JDialog self;
+ private JRadioButton personal_collection_button = null;
+ private JTextArea description;
+ private JTextField title;
+ private String collectBasePath;
+ private String newCollectPath;
+ private String description_final;
+ private String title_final="";
+
+ static private Dimension COMPONENT_SIZE = new Dimension(230, 25);
+ /** The size of this new collection dialog box. */
+ static private Dimension SIZE = new Dimension(600, 280);
+ static private int FILENAME_SIZE = 8;
+
+ /** Constructor.
+ * @see org.greenstone.gatherer.util.Utility
+ */
+ public NewCollectionDetailsPrompt() {
+ super(Gatherer.g_man, true);
+ this.cancelled = true;
+ this.setComponentOrientation(Dictionary.getOrientation());
+ this.self = this;
+ newCollectPath = Gatherer.getCollectDirectoryPath();
+ collectBasePath = null;
+ collectDirChanged = false;
+
+ // Setup
+ setJMenuBar(new SimpleMenuBar("creatingacollection"));
+ setSize(SIZE);
+ setTitle(Dictionary.get("NewCollectionPrompt.Title"));
+
+ // Model building. Build a model of all of the collections in the gsdl collect directory with the appropriate directories.
+ Vector base_collection_model = new Vector();
+ setupBaseCollections(base_collection_model, null); // if no collect directory passed in: use default collect directory
+
+
+ // Creation
+ JPanel content_pane = (JPanel) getContentPane();
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+ content_pane.setOpaque(true);
+ JPanel upper_pane = new JPanel();
+ upper_pane.setComponentOrientation(Dictionary.getOrientation());
+ JLabel instructions_label = new JLabel(Dictionary.get("NewCollectionPrompt.Instructions"));
+ instructions_label.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel title_pane = new JPanel();
+ title_pane.setComponentOrientation(Dictionary.getOrientation());
+ JLabel title_label = new JLabel(Dictionary.get("CDM.General.Collection_Name"));
+ title_label.setComponentOrientation(Dictionary.getOrientation());
+ title = new JTextField();
+ title.setComponentOrientation(Dictionary.getOrientation());
+ title.setPreferredSize(COMPONENT_SIZE);
+ title.setToolTipText(Dictionary.get("CDM.General.Collection_Name_Tooltip"));
+ JLabel name_label = new JLabel(Dictionary.get("NewCollectionPrompt.Collection_Name"));
+ name_label.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel center_pane = new JPanel();
+ center_pane.setComponentOrientation(Dictionary.getOrientation());
+ JPanel description_pane = new JPanel();
+ description_pane.setComponentOrientation(Dictionary.getOrientation());
+ JLabel description_label = new JLabel(Dictionary.get("NewCollectionPrompt.Collection_Description"));
+ description_label.setComponentOrientation(Dictionary.getOrientation());
+ description = new JTextArea();
+ description.setComponentOrientation(Dictionary.getOrientation());
+ description.setBackground(Configuration.getColor("coloring.editable_background", false));
+ description.setForeground(Configuration.getColor("coloring.editable_foreground", false));
+ description.setRows(5);
+ description.setToolTipText(Dictionary.get("CDM.General.Collection_Extra_Tooltip"));
+
+ JPanel bottom_pane = new JPanel();
+ bottom_pane.setComponentOrientation(Dictionary.getOrientation());
+ // Base Collection
+ JPanel base_collection_pane = new JPanel();
+ base_collection_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JLabel base_collection_label = new JLabel(Dictionary.get("NewCollectionPrompt.Base_Collection"));
+ base_collection_label.setComponentOrientation(Dictionary.getOrientation());
+
+ base_collection = new JComboBox(base_collection_model);
+ base_collection.setComponentOrientation(Dictionary.getOrientation());
+ base_collection.setOpaque(false);
+ base_collection.setToolTipText(Dictionary.get("NewCollectionPrompt.Base_Collection_Tooltip"));
+
+ JPanel collection_scope_pane = new JPanel();
+ collection_scope_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ personal_collection_button = new JRadioButton(Dictionary.get("NewCollectionPrompt.Collection_Scope_Personal"));
+ personal_collection_button.setToolTipText(Dictionary.get("NewCollectionPrompt.Collection_Scope_Personal_Tooltip"));
+ personal_collection_button.setBackground(Configuration.getColor("coloring.collection_tree_background", false));
+ personal_collection_button.setOpaque(false);
+ personal_collection_button.setComponentOrientation(Dictionary.getOrientation());
+
+ JRadioButton shared_collection_button = new JRadioButton(Dictionary.get("NewCollectionPrompt.Collection_Scope_Shared"));
+ shared_collection_button.setToolTipText(Dictionary.get("NewCollectionPrompt.Collection_Scope_Shared_Tooltip"));
+ shared_collection_button.setBackground(Configuration.getColor("coloring.collection_tree_background", false));
+ shared_collection_button.setOpaque(false);
+ shared_collection_button.setComponentOrientation(Dictionary.getOrientation());
+
+ ButtonGroup collection_scope_group = new ButtonGroup();
+ collection_scope_group.add(personal_collection_button);
+ collection_scope_group.add(shared_collection_button);
+ personal_collection_button.setSelected(true);
+
+ JPanel button_pane = new JPanel();
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+ create_button = new GLIButton(Dictionary.get("General.OK"), Dictionary.get("General.OK_Tooltip"));
+ chdir_button = new GLIButton(Dictionary.get("General.CD"), Dictionary.get("General.CD_Tooltip"));
+ JButton cancel_button = new GLIButton(Dictionary.get("General.Cancel"), Dictionary.get("General.Cancel_Tooltip"));
+
+ // Connection
+ chdir_button.addActionListener(new ChangeDirListener());
+ if(Gatherer.isGsdlRemote) { // for client GLI disable changing directories
+ chdir_button.setEnabled(false);
+ } else { // can't base collections on any in OTHER collect dirs in client GLI
+ base_collection.addActionListener(new OtherCollectionsListener());
+ }
+ cancel_button.addActionListener(new CancelListener());
+ create_button.addActionListener(new CreateListener());
+ description.addKeyListener(new DescriptionListener());
+
+ // Layout
+ title_pane.setLayout(new BorderLayout(5,0));
+ title_pane.add(title_label, BorderLayout.LINE_START);
+ title_pane.add(title, BorderLayout.CENTER);
+
+ upper_pane.setLayout(new GridLayout(2,1));
+ upper_pane.add(instructions_label);
+ upper_pane.add(title_pane);
+
+ JScrollPane scrol_tmp;
+
+ description_pane.setLayout(new BorderLayout());
+ description_pane.add(description_label, BorderLayout.NORTH);
+ scrol_tmp=new JScrollPane(description);
+ scrol_tmp.setComponentOrientation(Dictionary.getOrientation());
+ description_pane.add(scrol_tmp, BorderLayout.CENTER);
+
+ base_collection_pane.setLayout(new BorderLayout(5,0));
+ base_collection_pane.add(base_collection_label, BorderLayout.LINE_START);
+ base_collection_pane.add(base_collection, BorderLayout.CENTER);
+
+ collection_scope_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
+ collection_scope_pane.setLayout(new GridLayout(1,2));
+ collection_scope_pane.add(personal_collection_button);
+ collection_scope_pane.add(shared_collection_button);
+
+ center_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
+ center_pane.setLayout(new BorderLayout());
+ center_pane.add(description_pane, BorderLayout.CENTER);
+
+ bottom_pane.setLayout(new BorderLayout());
+ bottom_pane.add(base_collection_pane, BorderLayout.NORTH);
+ if (Gatherer.isGsdlRemote) {
+ bottom_pane.add(collection_scope_pane, BorderLayout.CENTER);
+ bottom_pane.add(button_pane, BorderLayout.SOUTH);
+ }
+ else {
+ bottom_pane.add(button_pane, BorderLayout.CENTER);
+ }
+
+ button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
+ button_pane.setLayout(new GridLayout(1,3));
+ button_pane.add(create_button);
+ button_pane.add(chdir_button);
+ button_pane.add(cancel_button);
+
+ content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ content_pane.setLayout(new BorderLayout());
+ content_pane.add(upper_pane, BorderLayout.NORTH);
+ content_pane.add(center_pane, BorderLayout.CENTER);
+ content_pane.add(bottom_pane, BorderLayout.SOUTH);
+ // Final dialog setup & positioning.
+ getRootPane().setDefaultButton(create_button);
+ Dimension screen_size = Configuration.screen_size;
+ setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
+ setVisible(true);
+ }
+
+ public boolean isCancelled() {
+ return cancelled;
+ }
+
+ public File getBase() {
+ return base_final;
+ }
+
+ public String getDescription() {
+ return description_final;
+ }
+
+ /** Generates the collection short filename by taking the first eight characters of the title (spaces removed) and then adjusting by further truncating and then adding an unique suffix as necessary.
+ * @return the filename as a String
+ */
+ public String getName()
+ {
+ // Retrieve the first 8 non-whitespace ASCII characters of title_final.
+ StringBuffer name_buffer = new StringBuffer("");
+ for (int i = 0, u = 0; (i < title_final.length() && u < 8); i++) {
+ // To be safe we make sure the internal collection name (folder name) contains only ASCII characters
+ char c = title_final.charAt(i);
+ if ((int) c < 128 && Character.isLetterOrDigit(c)) {
+ name_buffer.append(Character.toLowerCase(c));
+ u++;
+ }
+ }
+
+ // Use "col" as the base collection name if we have nothing left
+ if (name_buffer.length() == 0) {
+ name_buffer = new StringBuffer("col");
+ }
+
+ // Remote collections that aren't shared have the user's username prefixed to the collection name
+ if (Gatherer.isGsdlRemote && personal_collection_button.isSelected()) {
+ name_buffer = new StringBuffer(Gatherer.remoteGreenstoneServer.getUsername() + "-" + name_buffer.toString());
+ }
+
+ // We need to ensure the filename is unique
+ int counter = 0;
+ StringBuffer new_name_buffer = new StringBuffer(name_buffer.toString());
+ while(filenameClashes(new_name_buffer.toString())) {
+ new_name_buffer = new StringBuffer(name_buffer.toString());
+ counter++;
+ String suffix = String.valueOf(counter);
+ // If we have to truncate the namestring so as to fit the suffix
+ if(suffix.length() + new_name_buffer.length() > 8) {
+ new_name_buffer.replace(new_name_buffer.length() - suffix.length(), new_name_buffer.length(), suffix);
+ }
+ // Or just append it if that isn't necessary
+ else {
+ new_name_buffer.append(suffix);
+ }
+ }
+
+ // All done
+ return new_name_buffer.toString();
+ }
+
+ private boolean filenameClashes(String filename)
+ {
+ File collect_directory = new File(Gatherer.getCollectDirectoryPath());
+ File children[] = collect_directory.listFiles();
+ for(int i = 0; children != null && i < children.length; i++) {
+ if(children[i].getName().equals(filename)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public String getTitle() {
+ return title_final;
+ }
+
+ private void setupBaseCollections(Vector base_collection_model, String collectDirectory) {
+
+ File collect_directory = null;
+ if(collectDirectory != null) {
+ collect_directory = new File(collectDirectory);
+ }
+
+ // need to modify this to base a coll on any collection from any site
+ if (Gatherer.GS3 && !Gatherer.isGsdlRemote) {
+ File sites_dir = new File(Gatherer.getSitesDirectoryPath());
+ File [] sites = sites_dir.listFiles();
+ for (int i=0; i
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import javax.swing.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.collection.CollectionTreeNode;
+import org.greenstone.gatherer.file.FileManager;
+
+public class NewFolderOrFilePrompt
+ extends JDialog
+ implements ActionListener {
+ private CollectionTreeNode node;
+ private JButton cancel_button;
+ private JButton ok_button;
+ private JTextField name_field;
+ private String name;
+
+ private int type;
+ private String extension="";
+ static final private Dimension SIZE = new Dimension(350,115);
+
+ public NewFolderOrFilePrompt(CollectionTreeNode node, int type, String extension) {
+ super(Gatherer.g_man, true);
+ this.setComponentOrientation(Dictionary.getOrientation());
+ if (type == FileManager.FILE_TYPE) {
+ setTitle(Dictionary.get("NewFolderOrFilePrompt.Title_File"));
+ } else {
+ setTitle(Dictionary.get("NewFolderOrFilePrompt.Title_Folder"));
+ }
+ this.type = type;
+ if (extension != null) {
+ this.extension = extension;
+ }
+ this.node = node;
+ }
+
+ public void actionPerformed(ActionEvent event) {
+ if (event.getSource() == ok_button) {
+ name = name_field.getText()+extension;
+ }
+ else if(event.getSource() == cancel_button) {
+ name = null;
+ }
+ dispose();
+ }
+
+ public String display() {
+ setSize(SIZE);
+ JPanel content_pane = (JPanel) getContentPane();
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel labels_pane = new JPanel();
+ labels_pane.setComponentOrientation(Dictionary.getOrientation());
+ JPanel fields_pane = new JPanel();
+ fields_pane.setComponentOrientation(Dictionary.getOrientation());
+ JPanel info_pane = new JPanel();
+ info_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JLabel destination_label = new JLabel(Dictionary.get("NewFolderOrFilePrompt.Destination_Name"));
+ destination_label.setComponentOrientation(Dictionary.getOrientation());
+
+ JTextField destination_textfield = new JTextField(node.getFile().getName());
+ destination_textfield.setComponentOrientation(Dictionary.getOrientation());
+ destination_textfield.setBackground(Configuration.getColor("coloring.collection_tree_background", false));
+ destination_textfield.setEditable(false);
+
+ JLabel name_label = new JLabel();
+ name_label.setComponentOrientation(Dictionary.getOrientation());
+ name_field = new JTextField(getAutomaticName());
+ name_field.setComponentOrientation(Dictionary.getOrientation());
+ name_field.selectAll(); // select all the text in the textfield
+
+ if (type == FileManager.FILE_TYPE) {
+ name_label.setText(Dictionary.get("NewFolderOrFilePrompt.File_Name"));
+ name_field.setToolTipText(Dictionary.get("NewFolderOrFilePrompt.File_Name_Tooltip"));
+ } else {
+ name_label.setText(Dictionary.get("NewFolderOrFilePrompt.Folder_Name"));
+ name_field.setToolTipText(Dictionary.get("NewFolderOrFilePrompt.Folder_Name_Tooltip"));
+ }
+
+ JPanel button_pane = new JPanel();
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+ ok_button = new GLIButton(Dictionary.get("General.OK"), Dictionary.get("General.OK_Tooltip"));
+
+ cancel_button = new GLIButton(Dictionary.get("General.Cancel"), Dictionary.get("General.Pure_Cancel_Tooltip"));
+
+ // Connection
+ cancel_button.addActionListener(this);
+ ok_button.addActionListener(this);
+ getRootPane().setDefaultButton(ok_button);
+
+ // Layout
+ labels_pane.setLayout(new GridLayout(2,1, 5,0));
+ labels_pane.add(destination_label);
+ labels_pane.add(name_label);
+
+ fields_pane.setLayout(new GridLayout(2,1,0,5));
+ fields_pane.add(destination_textfield);
+ fields_pane.add(name_field);
+
+ info_pane.setLayout(new BorderLayout(5,0));
+ info_pane.add(labels_pane, BorderLayout.LINE_START);
+ info_pane.add(fields_pane, BorderLayout.CENTER);
+
+ button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
+ button_pane.setLayout(new GridLayout(1,2,0,5));
+ button_pane.add(ok_button);
+ button_pane.add(cancel_button);
+
+ content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ content_pane.setLayout(new BorderLayout());
+ content_pane.add(info_pane, BorderLayout.CENTER);
+ content_pane.add(button_pane, BorderLayout.SOUTH);
+
+ // Display
+ Rectangle frame_bounds = Gatherer.g_man.getBounds();
+ setLocation(frame_bounds.x + (frame_bounds.width - SIZE.width) / 2, frame_bounds.y + (frame_bounds.height - SIZE.height) / 2);
+ setVisible(true);
+ return name;
+ }
+
+ private String getAutomaticName()
+ {
+ File file = node.getFile();
+ String default_name;
+ if (type == FileManager.FILE_TYPE) {
+ default_name = Dictionary.get("NewFolderOrFilePrompt.Default_File_Name");
+ } else {
+ default_name = Dictionary.get("NewFolderOrFilePrompt.Default_Folder_Name");
+ }
+
+ File temp_file = new File(file, default_name+extension);
+ int count = 1;
+ while (temp_file.exists()) {
+ temp_file = new File(file, default_name + " " + count+extension);
+ count++;
+ }
+ if (extension.equals("")) {
+ return temp_file.getName();
+ }
+ String name = temp_file.getName();
+ // remove the extension
+ return name.substring(0, name.length()-extension.length());
+
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/NonWhitespaceField.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/NonWhitespaceField.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/NonWhitespaceField.java (revision 31635)
@@ -0,0 +1,77 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+/**************************************************************************************
+ * Written: 04/07/03
+ * Revised:
+ **************************************************************************************/
+import javax.swing.*;
+import javax.swing.text.*;
+import org.greenstone.gatherer.Dictionary;
+
+/** A JTextField that doesn't allow whitespace characters.
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.4
+ */
+public class NonWhitespaceField
+ extends JTextField {
+
+ public NonWhitespaceField() {
+ super();
+ this.setComponentOrientation(Dictionary.getOrientation());
+ }
+
+ public NonWhitespaceField(String text) {
+ super(text);
+ this.setComponentOrientation(Dictionary.getOrientation());
+ }
+
+ protected Document createDefaultModel() {
+ return new NonWhitespaceDocument();
+ }
+
+ static class NonWhitespaceDocument
+ extends PlainDocument {
+
+ public void insertString(int offs, String str, AttributeSet a)
+ throws BadLocationException {
+
+ if (str == null) {
+ return;
+ }
+ char[] raw = str.toCharArray();
+ StringBuffer result = new StringBuffer("");
+ for (int i = 0; i < raw.length; i++) {
+ if(!Character.isWhitespace(raw[i])) {
+ result.append(raw[i]);
+ }
+ }
+ super.insertString(offs, result.toString(), a);
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/NumberedJTextArea.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/NumberedJTextArea.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/NumberedJTextArea.java (revision 31635)
@@ -0,0 +1,237 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+
+package org.greenstone.gatherer.gui;
+
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+
+import org.fife.ui.rsyntaxtextarea.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.undo.*;
+
+/**
+ * A textarea with the line number next to each line of the text.
+ * It provides undo and redo buttons that are already hooked up to listeners
+ * You can add these buttons to a JComponent and they will behave just like the
+ * existing RSyntaxArea's Ctrl-Z and Ctrl-Y shortcuts and undo/redo popup menus.
+ */
+public class NumberedJTextArea extends RSyntaxTextArea /* JTextArea */
+ implements UndoableEditListener, ActionListener
+{
+
+ String id = null;
+
+ public final GLIButton undoButton;
+ public final GLIButton redoButton;
+
+ public NumberedJTextArea(String tooltip) {
+ this("", tooltip);
+ }
+
+ public NumberedJTextArea (String id, String tooltip) {
+ super();
+
+ this.id = id;
+
+ // maybe use this textarea's id field to customise the undo/redo button tooltips?
+ undoButton = new GLIButton(Dictionary.get("General.Undo"), Dictionary.get("General.Undo_Tooltip"));
+ undoButton.setEnabled(false);
+
+ redoButton = new GLIButton(Dictionary.get("General.Redo"), Dictionary.get("General.Redo_Tooltip"));
+ redoButton.setEnabled(false);
+
+ undoButton.addActionListener(this);
+ redoButton.addActionListener(this);
+
+ // next, initialise this RSyntaxTextArea:
+
+ // Adding the UndoableEditListener has to come after instantiation of the undo and
+ // redo buttons, since the listener expects these buttons to already exist
+ this.getDocument().addUndoableEditListener(this);
+
+ /* Fields specific to RSyntaxQuery inherited class */
+ setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_XML);
+ setBracketMatchingEnabled(true);
+ setAnimateBracketMatching(true);
+ setAntiAliasingEnabled(true);
+ setAutoIndentEnabled(true);
+ setPaintMarkOccurrencesBorder(false);
+
+ /* Standard fields to JTextArea */
+ setOpaque(false);
+ setBackground(Configuration.getColor("coloring.editable_background", false));
+ setCaretPosition(0);
+ setLineWrap(true);
+ setRows(11);
+ setWrapStyleWord(false);
+ setToolTipText(tooltip);
+ }
+
+ public void paintComponent(Graphics g)
+ {
+ Insets insets = getInsets();
+ Rectangle rectangle = g.getClipBounds();
+ g.setColor(Color.white);
+ g.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
+
+ super.paintComponent(g);
+
+ if (rectangle.x < insets.left)
+ {
+ FontMetrics font_metrics = g.getFontMetrics();
+ int font_height = font_metrics.getHeight();
+ int y = font_metrics.getAscent() + insets.top;
+ int line_number_start_point = ((rectangle.y + insets.top) / font_height) + 1;
+ if (y < rectangle.y)
+ {
+ y = line_number_start_point * font_height - (font_height - font_metrics.getAscent());
+ }
+ int y_axis_end_point = y + rectangle.height + font_height;
+ int x_axis_start_point = insets.left;
+ x_axis_start_point -= getFontMetrics(getFont()).stringWidth(Math.max(getRows(), getLineCount() + 1) + " ");
+ if (!this.getText().trim().equals(""))
+ {
+ g.setColor(Color.DARK_GRAY);
+ }
+ else
+ {
+ g.setColor(Color.white);
+ }
+ int length = ("" + Math.max(getRows(), getLineCount() + 1)).length();
+ while (y < y_axis_end_point)
+ {
+ g.drawString(line_number_start_point + " ", x_axis_start_point, y);
+ y += font_height;
+ line_number_start_point++;
+ }
+ }
+ }
+
+
+ public Insets getInsets()
+ {
+ Insets insets = super.getInsets(new Insets(0, 0, 0, 0));
+ insets.left += getFontMetrics(getFont()).stringWidth(Math.max(getRows(), getLineCount() + 1) + " ");
+ return insets;
+ }
+
+
+ // Overriding, to ensure that even if ctrl-z was pressed to undo something,
+ // the undo and redo buttons are in sync with that.
+ public void undoLastAction() {
+ super.undoLastAction();
+ redoButton.setEnabled(true);
+
+ if (!this.canUndo()) {
+ undoButton.setEnabled(false);
+ } else {
+ undoButton.setEnabled(true);
+ }
+ }
+
+ // Overriding
+ public void redoLastAction() {
+ super.redoLastAction();
+ undoButton.setEnabled(true);
+
+ if (!this.canRedo()) {
+ redoButton.setEnabled(false);
+ } else {
+ redoButton.setEnabled(true);
+ }
+ }
+
+ // Overriding
+ public void discardAllEdits() {
+ // Buttons have to be disabled before discardAllEdits(). If done in reverse order, buttons get re-enabled
+ undoButton.setEnabled(false);
+ redoButton.setEnabled(false);
+ super.discardAllEdits();
+ }
+
+ // The RSyntaxTextarea class already provides undo and redo functionality hooked up to their
+ // usual keyboard shortcuts and to rightclick popup menus. The actionListener below merely
+ // reuses this behaviour and attaches it to the undoButton and redoButton.
+ public void actionPerformed(ActionEvent event)
+ {
+ // actionPerformed() not defined in any of the superclasses RTextArea, RTextAreaBase, JTextArea
+ //super.actionPerformed(event); // compile error
+
+ if(event.getSource() == undoButton) {
+
+ try {
+ // calls canUndo() and undoLastAction() defined by RTextArea
+ // internally calls its undoManager's canUndo() and undo() to handle compoundEdits
+ if (this.canUndo()) {
+ this.undoLastAction();
+ }
+
+ if (!this.canUndo()) {
+ undoButton.setEnabled(false);
+ } else {
+ undoButton.setEnabled(true);
+ }
+
+ } catch (Exception e) {
+ System.err.println("Exception trying to undo: " + e.getMessage());
+ e.printStackTrace();
+ }
+ }
+ else if (event.getSource() == redoButton) {
+ try {
+ // calls canRedo() and redoLastAction() defined by RTextArea
+ // internally calls its undoManager's canRedo() and redo() to handle compoundEdits
+
+ if (this.canRedo()) {
+ this.redoLastAction();
+ }
+
+ if (!this.canRedo()) {
+ redoButton.setEnabled(false);
+ } else {
+ redoButton.setEnabled(true);
+ }
+
+ } catch (Exception e) {
+ System.err.println("Exception trying to redo: " + e.getMessage());
+ e.printStackTrace();
+ }
+ }
+ }
+
+
+ // defined in interface UndoableEditListener
+ public void undoableEditHappened(UndoableEditEvent evt)
+ {
+ undoButton.setEnabled(true);
+ redoButton.setEnabled(false);
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/OpenCollectionDialog.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/OpenCollectionDialog.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/OpenCollectionDialog.java (revision 31635)
@@ -0,0 +1,448 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.collection.BasicCollectionConfiguration;
+import org.greenstone.gatherer.util.StaticStrings;
+import org.greenstone.gatherer.util.Utility;
+import org.greenstone.gatherer.shell.GShell;
+import org.greenstone.gatherer.shell.GShellEvent;
+import org.greenstone.gatherer.shell.GShellListener;
+import org.greenstone.gatherer.shell.GShellProgressMonitor;
+
+
+/** A dialog which provides a straight-forward access to the currently installed collections. It also will contain the ability to continue through to the original OpenCollectionDialog if your source collection is located somewhere other than the gsdl collect folder. */
+public class OpenCollectionDialog
+ extends ModalDialog {
+
+ static final public int OK_OPTION = 0;
+ static final public int CANCEL_OPTION = 1;
+
+ static final private Dimension SIZE = new Dimension(640,480);
+ static final private String BLANK = "b";
+ static final private String DESCRIPTION = "d";
+
+ private CardLayout card_layout;
+ private int result;
+ private JButton cancel_button;
+ private JButton chdir_button;
+ private JButton open_button;
+ private JList collection_list;
+ private JTextArea description_textarea;
+ private JPanel description_pane;
+ private String filename;
+ private String newCollectPath;
+
+ public OpenCollectionDialog() {
+ super(Gatherer.g_man, "", true);
+ setJMenuBar(new SimpleMenuBar("openingacollection"));
+ this.setComponentOrientation(Dictionary.getOrientation());
+ setSize(SIZE);
+ setTitle(Dictionary.get("OpenCollectionDialog.Title"));
+ newCollectPath = Gatherer.getCollectDirectoryPath();
+
+ // Creation
+ JPanel content_pane = (JPanel) getContentPane();
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel center_pane = new JPanel();
+ center_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel collection_list_pane = new JPanel();
+ collection_list_pane.setComponentOrientation(Dictionary.getOrientation());
+ JLabel collection_list_label = new JLabel(Dictionary.get("OpenCollectionDialog.Available_Collections"));
+ collection_list_label.setComponentOrientation(Dictionary.getOrientation());
+ collection_list = new JList(new CollectionListModel());
+ collection_list.setComponentOrientation(Dictionary.getOrientation());
+
+ description_pane = new JPanel();
+ description_pane.setComponentOrientation(Dictionary.getOrientation());
+ card_layout = new CardLayout();
+
+ JPanel blank_pane = new JPanel();
+ blank_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel description_textarea_pane = new JPanel();
+ description_textarea_pane.setComponentOrientation(Dictionary.getOrientation());
+ JLabel description_textarea_label = new JLabel(Dictionary.get("OpenCollectionDialog.Description"));
+ description_textarea_label.setComponentOrientation(Dictionary.getOrientation());
+ description_textarea = new JTextArea();
+ description_textarea.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel button_pane = new JPanel();
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+ open_button = new GLIButton(Dictionary.get("OpenCollectionDialog.Open"), Dictionary.get("OpenCollectionDialog.Open_Tooltip"));
+ open_button.setEnabled(false);
+ chdir_button = new GLIButton(Dictionary.get("General.CD"), Dictionary.get("General.CD_Tooltip"));
+ cancel_button = new GLIButton(Dictionary.get("General.Cancel"), Dictionary.get("General.Pure_Cancel_Tooltip"));
+
+ // Connection
+ chdir_button.addActionListener(new ChangeDirListener());
+ if(Gatherer.isGsdlRemote) { // disable changing directories for client GLI
+ chdir_button.setEnabled(false);
+ }
+ cancel_button.addActionListener(new CancelListener());
+ open_button.addActionListener(new OpenListener());
+ CollectionListSelectionListener clsl = new CollectionListSelectionListener();
+ collection_list.addListSelectionListener(clsl);
+ collection_list.addMouseListener(clsl);
+ clsl = null;
+
+ // Layout
+ JScrollPane scrol_tmp;
+
+ collection_list_pane.setLayout(new BorderLayout());
+ collection_list_pane.add(collection_list_label, BorderLayout.NORTH);
+ scrol_tmp = new JScrollPane(collection_list);
+ scrol_tmp.setComponentOrientation(Dictionary.getOrientation());
+ collection_list_pane.add(scrol_tmp, BorderLayout.CENTER);
+
+ description_textarea_pane.setLayout(new BorderLayout());
+ description_textarea_pane.add(description_textarea_label, BorderLayout.NORTH);
+ scrol_tmp =new JScrollPane(description_textarea);
+ scrol_tmp.setComponentOrientation(Dictionary.getOrientation());
+ description_textarea_pane.add(scrol_tmp, BorderLayout.CENTER);
+
+ description_pane.setLayout(card_layout);
+ description_pane.add(description_textarea_pane, DESCRIPTION);
+ description_pane.add(blank_pane, BLANK);
+
+ center_pane.setLayout(new GridLayout(2,1,0,5));
+ center_pane.add(collection_list_pane);
+ center_pane.add(description_pane);
+
+ button_pane.setLayout(new GridLayout(1,3));
+ button_pane.add(open_button);
+ button_pane.add(chdir_button);
+ button_pane.add(cancel_button);
+
+ content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ content_pane.setLayout(new BorderLayout());
+ content_pane.add(center_pane, BorderLayout.CENTER);
+ content_pane.add(button_pane, BorderLayout.SOUTH);
+ // Final dialog setup & positioning.
+ getRootPane().setDefaultButton(open_button);
+ Dimension screen_size = Configuration.screen_size;
+ setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
+ screen_size = null;
+ }
+
+ public void destroy() {
+ }
+
+ public int display() {
+ setVisible(true);
+ return result;
+ }
+
+ public String getFileName() {
+ return this.filename;
+ }
+
+ private class ChangeDirListener implements ActionListener {
+ public void actionPerformed(ActionEvent event) {
+ JFileChooser chooser = new JFileChooser(newCollectPath);
+ chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+ chooser.setDialogTitle(Dictionary.get("General.ChooseCollectDirectory"));
+ int returnVal = chooser.showOpenDialog(OpenCollectionDialog.this);
+ if(returnVal == JFileChooser.APPROVE_OPTION) {
+ String oldCollectPath = newCollectPath;
+ newCollectPath = chooser.getSelectedFile().getAbsolutePath() + File.separator;
+
+ if(!newCollectPath.equals(oldCollectPath)) {
+ // reload the list of available collections using this new path
+ CollectionListModel listModel = (CollectionListModel)collection_list.getModel();
+ listModel.reload(newCollectPath);
+ collection_list.repaint();
+ }
+ }
+ }
+ }
+
+ private class CancelListener
+ implements ActionListener {
+
+ public void actionPerformed(ActionEvent event) {
+ result = CANCEL_OPTION;
+ OpenCollectionDialog.this.dispose();
+ }
+ }
+
+ private class CollectionListSelectionListener
+ extends MouseAdapter
+ implements ListSelectionListener {
+
+ public void mouseClicked(MouseEvent event) {
+ ///ystem.err.println("Mouse clicked");
+ if(event.getClickCount() >= 2) {
+ Point location = event.getPoint();
+ int index = collection_list.locationToIndex(location);
+ collection_list.setSelectedIndex(index);
+ location = null;
+ open_button.doClick();
+ }
+ }
+
+ public void valueChanged(ListSelectionEvent event) {
+ if(collection_list.isSelectionEmpty()) {
+ card_layout.show(description_pane, BLANK);
+ open_button.setEnabled(false);
+ }
+ else {
+ BasicCollectionConfiguration collection_configuration = (BasicCollectionConfiguration) collection_list.getSelectedValue();
+ description_textarea.setText(collection_configuration.getDescription());
+ description_textarea.setCaretPosition(0);
+ card_layout.show(description_pane, DESCRIPTION);
+ open_button.setEnabled(true);
+ }
+ }
+ }
+
+ // use this if we ever go back to viewing all colls at once
+ /* private class GS3CollectionListModel
+ extends AbstractListModel {
+
+ private TreeSet data;
+
+ public GS3CollectionListModel() {
+ data = new TreeSet();
+ File sites_folder = new File(Utility.getSitesDir(Configuration.gsdl3_path));
+ File sites[] = sites_folder.listFiles();
+ for (int s=0; s if it is, don't add, but recurse to look for child collections in collect-group
+
+ if (collection_configuration.getCollectGroup().equals("true")) {
+ load_collection_configs(data, collection_folder);
+ }
+ else {
+ // add this collection. We want to allow non gli collections, so add anything that has a config file.
+ data.add(collection_configuration);
+ }
+ }
+ config_file = null;
+ }
+ collection_foldername = null;
+ collection_folder = null;
+ }
+ collection_folders = null;
+ collect_directory = null;
+ }
+
+
+
+ public Object getElementAt(int index) {
+ Iterator iterator = data.iterator();
+ for(int i = 0; iterator.hasNext(); i++) {
+ Object object = iterator.next();
+ if(i == index) {
+ iterator = null;
+ return object;
+ }
+ object = null;
+ }
+ iterator = null;
+ return null;
+ }
+
+ public int getSize() {
+ return data.size();
+ }
+
+ public void convertToGS3Collection(File collection_folder) {
+
+ File collect_cfg_file = new File(collection_folder.getAbsolutePath() + File.separator + "etc" + File.separator + "collect.cfg");
+ File build_cfg_file = new File(collection_folder.getAbsolutePath() + File.separator+"index" + File.separator + "build.cfg");
+
+ if (collect_cfg_file.exists() && build_cfg_file.exists()){
+ // Generate the convert_coll_from_gs2.pl command
+ ArrayList command_parts_list = new ArrayList();
+ if (!Gatherer.isGsdlRemote) {
+ command_parts_list.add(Configuration.perl_path);
+ command_parts_list.add("-S");
+ }
+ command_parts_list.add(Configuration.getGS3ScriptPath() + "convert_coll_from_gs2.pl");
+ command_parts_list.add("-collectdir");
+ command_parts_list.add(collection_folder.getParent());
+ command_parts_list.add(collection_folder.getName());
+
+ // Run the convert_coll_from_gs2.pl command
+ String[] command_parts = (String[]) command_parts_list.toArray(new String[0]);
+ GShell process = new GShell(command_parts, GShell.CONVERT, COLLECT, null, null, GShell.GSHELL_CONVERT);
+ //process.addGShellListener(this);
+ process.run(); // Don't bother threading this... yet
+
+ // From now, won't dangerously delete GS2 collect and build config files anymore
+ //collect_cfg_file.delete();
+ //build_cfg_file.delete();
+
+ File collect_bak_file = new File(collection_folder.getAbsolutePath() + File.separator + "etc" + File.separator + "collect.cfg.bak");
+ File build_bak_file = new File(collection_folder.getAbsolutePath() + File.separator+"index" + File.separator + "build.cfg.bak");
+ if(!collect_cfg_file.renameTo(collect_bak_file)) {
+ System.err.println("Unable to move collect.cfg to " + collect_bak_file);
+ }
+ if(!build_cfg_file.renameTo(build_bak_file)) {
+ System.err.println("Unable to move build.cfg to " + build_bak_file);
+ }
+ }
+ }
+ }
+
+ private class OpenListener
+ implements ActionListener {
+
+ public void actionPerformed(ActionEvent event) {
+ result = OK_OPTION;
+ Object selected_object = collection_list.getSelectedValue();
+ if (selected_object instanceof BasicCollectionConfiguration) {
+ BasicCollectionConfiguration collection_configuration = (BasicCollectionConfiguration)selected_object; //(BasicCollectionConfiguration) collection_list.getSelectedValue();
+ //****************
+ File collect_cfg_file = collection_configuration.getFile(); // returns the collect.cfg file
+ result = FormatConversionDialog.checkForGS2FormatStatements(collect_cfg_file);
+ if(result == OK_OPTION) { // either there were no gs2 format stmts or user chose to proceed
+ File etc_folder = collect_cfg_file.getParentFile();
+ File collection_folder = etc_folder.getParentFile();
+ filename = collection_folder.getAbsolutePath() + File.separator + "gli.col";
+ collection_folder = null;
+ etc_folder = null;
+ collect_cfg_file = null;
+ collection_configuration = null;
+ }
+ OpenCollectionDialog.this.dispose();
+ }
+
+ Gatherer.collectDirectoryHasChanged(Gatherer.getCollectDirectoryPath(),
+ newCollectPath, Gatherer.g_man.getContentPane());
+ // will tell the server that the collect directory has changed and that
+ // the workspace needs to be refreshed (Documents in Greenstone Collections)
+
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/OptionsPane.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/OptionsPane.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/OptionsPane.java (revision 31635)
@@ -0,0 +1,646 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.text.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.cdm.Argument;
+import org.greenstone.gatherer.cdm.ArgumentControl;
+import org.greenstone.gatherer.collection.ScriptOptions;
+import org.greenstone.gatherer.collection.Collection;
+import org.greenstone.gatherer.collection.CollectionManager;
+import org.greenstone.gatherer.util.AppendLineOnlyFileDocument;
+import org.greenstone.gatherer.util.AppendLineOnlyFileDocumentOwner;
+import org.greenstone.gatherer.util.StaticStrings;
+
+/** This class serves as the data holder for all subclasses of option panes, such as Import options or All options. It also contains methods for creating each of the option lines as they would appear in the subpane. Futhermore it has a method for considering all the arguments and generating a String[] to allow you to pass them to the GShell .
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.2
+ */
+public class OptionsPane
+ extends JPanel
+ implements AppendLineOnlyFileDocumentOwner, MouseListener {
+
+ static final public char SUCCESSFUL = 's';
+ static final public char UNSUCCESSFUL = 'u';
+ static final public char CANCELLED = 'c';
+ static final public char UNKNOWN = 'x';
+ static final public char SCHEDULED = 'd';
+
+ static private int BUILD = 0;
+ static private int IMPORT = 1;
+ static private int SCHEDULE = 2;
+ static private int MINIMUM_ROWS = 15;
+
+ /** All process messages are written to this log text area. */
+ public JTextArea log_textarea = null;
+
+ private ArrayList current_controls;
+
+ /** The ScriptOptions data object contains all the option settings we wish to persist between Gatherer sessions (and thus is stored in Collection ). */
+ private ScriptOptions build_options = null;
+ private ScriptOptions import_options = null;
+ private ScriptOptions schedule_options = null;
+
+ private FileEntry file_entry = null;
+
+ /** the log pane - we only create it once now, not each time */
+ private JPanel log_pane = null;
+ /** the list of previous log messages */
+ private JList log_list = null;
+ private Vector writing_documents;
+
+
+ /** The default constructor creates the few session length options, but either retrieves the rest from the current collection, or creates a default set of options. */
+ public OptionsPane(ScriptOptions import_options, ScriptOptions build_options, ScriptOptions schedule_options) {
+ this.build_options = build_options;
+ this.import_options = import_options;
+ this.schedule_options = schedule_options;
+ this.current_controls = new ArrayList();
+ this.writing_documents = new Vector();
+ this.setComponentOrientation(Dictionary.getOrientation());
+
+ // Have to do this here, not in display, as the message log view may not have been displayed yet.
+ log_textarea = new JTextArea();
+ log_textarea.setComponentOrientation(Dictionary.getOrientation());
+ log_textarea.setEditable(false);
+ }
+
+ /** This method creates the panel with all the build only options on it.
+ * @param pane a JPanel which already has previous arguments available on it, to allow for lower moes to concatenate all of the arguments together
+ * @return a JPanel which can be used to display all the build only options
+ * @see org.greenstone.gatherer.Configuration#EXPERT_MODE
+ * @see org.greenstone.gatherer.Configuration#getColor
+ * @see org.greenstone.gatherer.Configuration#getMode
+ * @see org.greenstone.gatherer.Gatherer#config
+ * @see org.greenstone.gatherer.cdm.Argument
+ * @see org.greenstone.gatherer.collection.BuildOptions#getBuildArgument
+ * @see org.greenstone.gatherer.collection.BuildOptions#getBuildArgumentCount
+ * @see org.greenstone.gatherer.collection.BuildOptions#getBuildValue
+ * @see org.greenstone.gatherer.collection.BuildOptions#getBuildValueEnabled
+ * @see org.greenstone.gatherer.gui.OptionsPane.MyArgumentControl
+ */
+ public JPanel buildBuild(JPanel pane) {
+ // Reset the arguments
+ if(pane == null) {
+ current_controls.clear();
+ }
+ ArrayList build_arguments = new ArrayList();
+ int current_mode = Configuration.getMode();
+ int total_build_argument_count = build_options.getArgumentCount();
+
+ for(int i = 0; i < total_build_argument_count; i++) {
+ // Retrieve the argument so we know how to format the control.
+ Argument argument = build_options.getArgument(i);
+
+ if(!argument.isHiddenGLI() && argument.getModeLevel() <= current_mode) {
+ // Now attempt to retrieve any existing value for this argument.
+ boolean enabled = build_options.getValueEnabled(argument.getName());
+ String value = build_options.getValue(argument.getName());
+ MyArgumentControl argument_control = new MyArgumentControl(BUILD, argument, enabled, value);
+ build_arguments.add(argument_control);
+ }
+ }
+ current_controls.addAll(build_arguments);
+
+ // Now that we know how many arguments there are we can build the pane to view them on. Modes lower than EXPERT can provide a previous pane on which to add the arguments.
+ if(pane == null || current_mode >= Configuration.EXPERT_MODE) {
+ pane = new JPanel();
+ pane.setComponentOrientation(Dictionary.getOrientation());
+ pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ pane.setBackground(Configuration.getColor("coloring.collection_tree_background", false));
+ int argument_count = build_arguments.size();
+ // If in any of the higher detail modes, and assuming we don't want super phat argument controls, we better ensure there is a minimum number of lines in the grid layout
+ if(current_mode >= Configuration.EXPERT_MODE) {
+ if(argument_count < MINIMUM_ROWS) {
+ argument_count = MINIMUM_ROWS;
+ }
+ pane.setLayout(new GridLayout(argument_count, 1, 5, 5));
+ }
+ // Otherwise we're just going to throw them on one after another and chuck it in a scroll pane anyway
+ else {
+ // use GridLayout with 0 rows == as many rows as needed. Unfortunately, rows will
+ // grow fat if space too large, but we don't know how many rows we'll need yet.
+ pane.setLayout(new GridLayout(0, 1, 5, 5));
+ }
+ }
+
+ for(int j = 0; j < build_arguments.size(); j++) {
+ pane.add((JComponent)build_arguments.get(j));
+ }
+ pane.addMouseListener(this);
+ build_arguments = null;
+ return pane;
+ }
+
+ /**Wendy's attempt at writing a buildSchedule Jpanel. */
+ public JPanel buildSchedule(JPanel pane) {
+ //reset the arguments
+ if(pane == null) {
+ current_controls.clear();
+ }
+
+ ArrayList schedule_arguments = new ArrayList();
+ int current_mode = Configuration.getMode();
+
+ int total_schedule_argument_count = schedule_options.getArgumentCount();
+
+ for(int i = 0; i < total_schedule_argument_count; i++) {
+ // Retrieve the argument so we know how to format the control.
+ Argument argument = schedule_options.getArgument(i);
+
+ if(!argument.isHiddenGLI() && argument.getModeLevel() <= current_mode) {
+ // Now attempt to retrieve any existing value for this argument.
+ boolean enabled = schedule_options.getValueEnabled(argument.getName());
+ String value = schedule_options.getValue(argument.getName());
+ MyArgumentControl argument_control = new MyArgumentControl(SCHEDULE, argument, enabled, value);
+ schedule_arguments.add(argument_control);
+ }
+ }
+ current_controls.addAll(schedule_arguments);
+
+ // Now that we know how many arguments there are we can build the pane to view them on. Modes lower than EXPERT can provide a previous pane on which to add the arguments.
+ if(pane == null || current_mode >= Configuration.EXPERT_MODE) {
+ pane = new JPanel();
+ pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ pane.setBackground(Configuration.getColor("coloring.collection_tree_background", false));
+ int argument_count = schedule_arguments.size();
+ // If in any of the higher detail modes, and assuming we don't want super phat argument controls, we better ensure there is a minimum number of lines in the grid layout
+ if(current_mode >= Configuration.EXPERT_MODE) {
+ if(argument_count < MINIMUM_ROWS) {
+ argument_count = MINIMUM_ROWS;
+ }
+ pane.setLayout(new GridLayout(argument_count, 1, 5, 5));
+ }
+ // Otherwise we're just going to throw them on one after another and chuck it in a scroll pane anyway
+ else {
+ // use GridLayout with 0 rows == as many rows as needed. Unfortunately, rows will
+ // grow fat if space too large, but we don't know how many rows we'll need yet.
+ pane.setLayout(new GridLayout(0, 1, 5, 5));
+ }
+ }
+
+ for(int j = 0; j < schedule_arguments.size(); j++) {
+ pane.add((JComponent)schedule_arguments.get(j));
+ }
+ pane.addMouseListener(this);
+ schedule_arguments = null;
+ return pane;
+
+ }
+
+ /** This method creates the panel with all the import only options on it.
+ * @param pane a JPanel which already has previous arguments available on it, to allow for lower moes to concatenate all of the arguments together
+ * @return a JPanel which can be used to display all the build only options
+ * @see org.greenstone.gatherer.Configuration#EXPERT_MODE
+ * @see org.greenstone.gatherer.Configuration#getColor
+ * @see org.greenstone.gatherer.Configuration#getMode
+ * @see org.greenstone.gatherer.Gatherer#config
+ * @see org.greenstone.gatherer.cdm.Argument
+ * @see org.greenstone.gatherer.collection.BuildOptions#getImportArgument
+ * @see org.greenstone.gatherer.collection.BuildOptions#getImportArgumentCount
+ * @see org.greenstone.gatherer.collection.BuildOptions#getImportValue
+ * @see org.greenstone.gatherer.collection.BuildOptions#getImportValueEnabled
+ * @see org.greenstone.gatherer.gui.OptionsPane.ArgumentControl
+ */
+ public JPanel buildImport(JPanel pane) {
+ // Reset the arguments
+ if(pane == null) {
+ current_controls.clear();
+ }
+ ArrayList import_arguments = new ArrayList();
+ int current_mode = Configuration.getMode();
+ int total_import_argument_count = import_options.getArgumentCount();
+ for(int i = 0; i < total_import_argument_count; i++) {
+ // Retrieve the argument so we know how to format the control.
+ Argument argument = import_options.getArgument(i);
+ if(!argument.isHiddenGLI() && argument.getModeLevel() <= current_mode) {
+ // Now attempt to retrieve any existing value for this argument.
+ boolean enabled = import_options.getValueEnabled(argument.getName());
+ String value = import_options.getValue(argument.getName());
+ MyArgumentControl argument_control = new MyArgumentControl(IMPORT, argument, enabled, value);
+ import_arguments.add(argument_control);
+ }
+ }
+ current_controls.addAll(import_arguments);
+ // Now that we know how many arguments there are we can build the pane to view them on. Modes lower than EXPERT can provide a previous pane on which to add the arguments.
+ if(pane == null || current_mode >= Configuration.EXPERT_MODE) {
+ pane = new JPanel();
+ pane.setComponentOrientation(Dictionary.getOrientation());
+ pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ pane.setBackground(Configuration.getColor("coloring.collection_tree_background", false));
+ int argument_count = import_arguments.size();
+ // If in any of the higher detail modes, and assuming we don't want super phat argument controls, we better ensure there is a minimum number of lines in the grid layout
+ if(current_mode >= Configuration.EXPERT_MODE) {
+ if(argument_count < MINIMUM_ROWS) {
+ argument_count = MINIMUM_ROWS;
+ }
+ pane.setLayout(new GridLayout(argument_count, 1, 5, 5));
+ }
+ // Otherwise we're just going to throw them on one after another and chuck it in a scroll pane anyway
+ else {
+ // use GridLayout with 0 rows == as many rows as needed. Unfortunately, rows will
+ // grow fat if space too large, but we don't know how many rows we'll need yet.
+ pane.setLayout(new GridLayout(0, 1, 5, 5));
+ }
+ }
+
+ for(int j = 0; j < import_arguments.size(); j++) {
+ pane.add((JComponent)import_arguments.get(j));
+ }
+ pane.addMouseListener(this);
+ import_arguments = null;
+ return pane;
+ }
+
+ /** This method is used to build a panel based on the message log, which is nothing like any of the other panels.
+ * @return A JPanel containing a scrollable text area which represents the shell process message log.
+ */
+ public JPanel buildLog() {
+ // we now save the log pane
+ if (log_pane == null) {
+ log_pane = new JPanel(new BorderLayout());
+ log_pane.setComponentOrientation(Dictionary.getOrientation());
+ // Build a list of the log files available, ordering by last modified. Log files are like build_log.date.txt
+ DefaultListModel contents = new DefaultListModel();
+ File log_directory = new File(CollectionManager.getLoadedCollectionLogDirectoryPath());
+ File children[] = log_directory.listFiles();
+ for(int i = 0; children != null && i < children.length; i++) {
+ String filename = children[i].getName();
+ if(filename.startsWith("build_log.") && filename.endsWith(".txt") ) {
+ String datestamp = filename.substring(filename.indexOf(".") + 1, filename.lastIndexOf(".")).toLowerCase();
+ if(datestamp.indexOf("s") == -1 && datestamp.indexOf("u") == -1 && datestamp.indexOf("c") == -1 && datestamp.indexOf("x") == -1) {
+ FileEntry entry = new FileEntry(children[i].getName(), children[i].getAbsolutePath());
+ // We are about to insert it. But where.
+ boolean found = false;
+ for(int j = 0; !found && j < contents.size(); j++) {
+ FileEntry sibling = (FileEntry) contents.getElementAt(j);
+ int order = entry.compareTo(sibling);
+ if(order > 0) {
+ contents.insertElementAt(entry, j);
+ found = true;
+ }
+ }
+ if(!found) {
+ contents.addElement(entry);
+ }
+ }
+ }
+ }
+
+ log_list = new JList(contents);
+ log_list.setComponentOrientation(Dictionary.getOrientation());
+ log_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ log_list.setLayoutOrientation(JList.VERTICAL);
+ log_list.setPreferredSize(new Dimension(600, 100));
+ log_list.setVisibleRowCount(3);
+ log_list.addListSelectionListener(new LogListListener());
+
+ JLabel log_history_label = new JLabel(Dictionary.get("OptionsPane.LogHistory"));
+ log_history_label.setComponentOrientation(Dictionary.getOrientation());
+ JPanel log_history_pane = new JPanel();
+ log_history_pane.setComponentOrientation(Dictionary.getOrientation());
+ log_history_pane.setPreferredSize(new Dimension(600, 100));
+ log_history_pane.setLayout(new BorderLayout());
+ log_history_pane.add(log_history_label, BorderLayout.NORTH);
+ JScrollPane scrol_tmp=new JScrollPane(log_list);
+ scrol_tmp.setComponentOrientation(Dictionary.getOrientation());
+ log_history_pane.add(scrol_tmp, BorderLayout.CENTER);
+
+ scrol_tmp=new JScrollPane(log_textarea);
+ scrol_tmp.setComponentOrientation(Dictionary.getOrientation());
+ log_pane.add(scrol_tmp, BorderLayout.CENTER);
+ log_pane.add(log_history_pane, BorderLayout.SOUTH);
+ }
+ return log_pane;
+ }
+
+ public AppendLineOnlyFileDocument createNewLogDocument() {
+ long time = System.currentTimeMillis();
+ StringBuffer name = new StringBuffer();
+ name.append("build_log.");
+ name.append(time);
+ name.append(".txt");
+ // just in case there is no log directory
+ File file = new File(CollectionManager.getLoadedCollectionLogDirectoryPath() + name.toString());
+ File parent_file = file.getParentFile();
+ parent_file.mkdirs();
+ parent_file = null;
+ // create the file entry and add it to the list at pos 0 - it will always be the newest one created
+ file_entry = new FileEntry(name.toString(), file.getAbsolutePath());
+ ((DefaultListModel)log_list.getModel()).add(0, file_entry);
+ log_list.setSelectedIndex(0);
+ // Finally retrieve and return the document associated with this file entry
+ return file_entry.getDocument();
+ }
+
+ /** ensure the current build_log file has finished transferring from memory to disk, and that the random access
+ * file is properly closed when a collection is closed. Else we can't delete the just-closed collection since the
+ * build_log file resource is still kept open. */
+ public void closeCurrentLogDocument() {
+ if(file_entry == null) {
+ return;
+ }
+ AppendLineOnlyFileDocument buildDoc = file_entry.getDocument();
+ if(buildDoc != null) {
+ buildDoc.close();
+ }
+ }
+
+
+ /** Attempts to discover the latest document count.
+ * @return An int detailing the number of documents in this collection.
+ */
+ public int getDocumentCount() {
+ if(Gatherer.c_man.ready()) {
+ int count = Gatherer.c_man.getCollection().getDocumentCount();
+ if(count != 0) {
+ return count;
+ }
+ }
+ return 1;
+ }
+
+ /** Called by our magic log documents after they have finished writing themselves to file, whereapon it is no longer necessary to hold a reference to them. */
+ public void remove(AppendLineOnlyFileDocument document) {
+ writing_documents.remove(document);
+ }
+
+ public void resetFileEntry() {
+ if(file_entry != null) {
+ file_entry.reset();
+ }
+ }
+
+ /** Given a panel containing ArgumentControls, update the values associated with them. */
+ public void update(JPanel panel) {
+ if(panel == log_pane) {
+ return;
+ }
+
+ for(int i = 0; i < panel.getComponentCount(); i++) {
+ Component component = panel.getComponent(i);
+ if(component instanceof MyArgumentControl) {
+ ((MyArgumentControl)component).update();
+ }
+ }
+ }
+
+ /** Implementation side-effect
+ * @param e a MouseEvent
+ */
+ public void mouseClicked(MouseEvent e) {}
+
+ /** Implementation side-effect
+ * @param e a MouseEvent
+ */
+ public void mouseEntered(MouseEvent e) {}
+
+ /** Implemented to ensure that, by the time the mouse pointer leaves the current build options screen, ang changes to the value the JSpinners have been commited
+ * @param e a MouseEvent
+ */
+ public void mouseExited(MouseEvent e) {
+ // Loop through the controls, and if the current control is a JSpinner, commit its current editing
+ for(int i = 0; i < current_controls.size(); i++) {
+ MyArgumentControl control = (MyArgumentControl) current_controls.get(i);
+ JComponent value_control = control.getValueControl();
+ if(value_control instanceof JSpinner) {
+ try {
+ ((JSpinner)value_control).commitEdit();
+ }
+ catch(Exception exception) {
+ DebugStream.println("Exception in OptionsPane.mouseExited() - unexpected");
+ DebugStream.printStackTrace(exception);
+ }
+ }
+ value_control = null;
+ control = null;
+ }
+ }
+
+ /** Implementation side-effect
+ * @param e a MouseEvent
+ */
+ public void mousePressed(MouseEvent e) {}
+
+ /** Implementation side-effect
+ * @param e a MouseEvent
+ */
+ public void mouseReleased(MouseEvent e) {}
+
+ private class MyArgumentControl
+ extends ArgumentControl {
+ private int type;
+
+ public MyArgumentControl(int type, Argument argument, boolean enable, String value) {
+ super(argument, enable, value);
+ this.type = type;
+ }
+
+ /** Update the values stored in the collection so as to remember the current state of this argument. */
+ public void update() {
+ String name = getArgumentName();
+ boolean enable = isEnabled();
+ String value = getValue();
+ // If this argument was a flag, but is now disabled, remove from the build options altogether
+ if(!enable && value == null) {
+ if(type == BUILD) {
+ build_options.removeValue(name);
+ }
+ else if(type == SCHEDULE) {
+ schedule_options.removeValue(name);
+ }
+ else {
+ import_options.removeValue(name);
+ }
+ }
+ // Otherwise update the argument value
+ else {
+ if(type == BUILD) {
+ build_options.setValue(name, enable, value);
+ }
+ else if(type == SCHEDULE) {
+ schedule_options.setValue(name, enable, value);
+ }
+ else {
+ import_options.setValue(name, enable, value);
+ }
+ }
+ }
+ }
+
+ /** Holds a File which has a particular naming convention build_log.date.txt also keeps a Date corresponding to the date in its name*/
+ private class FileEntry {
+
+ private AppendLineOnlyFileDocument current_document;
+ private Date date;
+ private long last_modified;
+ private String display;
+ private String filename;
+ private String filepath;
+
+ public FileEntry(String filename, String filepath) {
+ this.date = null;
+ this.display = null;
+ this.filename = filename;
+ this.filepath = filepath;
+ this.last_modified = 0L;
+ }
+
+ /** returns 0 if the dates are the same, -ve number if the current FileEntry is earlier than the fe FileEntry ...*/
+ public int compareTo(FileEntry file_entry) {
+ Date our_date = getDate();
+ Date other_date = file_entry.getDate();
+ return our_date.compareTo(other_date);
+ }
+
+ public Date getDate() {
+ if(date == null) {
+ // Need to exclude first '.'
+ int first_index = filename.indexOf(".") + 1;
+ // Need to exclude the last '.'
+ int last_index = filename.lastIndexOf(".");
+ if(first_index > 0 && last_index > 0 && first_index < last_index) {
+ String date_string = filename.substring(first_index, last_index);
+ date = new Date(Long.parseLong(date_string));
+ }
+ else {
+ date = new Date(); // Current date
+ }
+ }
+ return date;
+ }
+
+ public AppendLineOnlyFileDocument getDocument() {
+ if(current_document == null) {
+ current_document = new AppendLineOnlyFileDocument(filepath);
+ }
+ return current_document;
+ }
+
+ public void reset() {
+ display = null;
+ }
+
+ /** we only want the date out of the file name, not the whole path */
+ public String toString() {
+ File file = new File(filename);
+ if(display == null) {
+ last_modified = file.lastModified();
+ StringBuffer d = new StringBuffer();
+ Date date = getDate();
+ d.append(date.toString());
+ char success = UNKNOWN;
+ File the_file = new File(filepath);
+ if(the_file.exists()) {
+ try {
+ FileInputStream in = new FileInputStream(the_file);
+ success = (char) in.read();
+ in.close();
+ in = null;
+ }
+ catch(Exception error) {
+ ///ystem.err.println("Log '" + filepath + "' not found!");
+ ///atherer.printStackTrace(error);
+ }
+ }
+ the_file = null;
+ switch (success) {
+ case SUCCESSFUL:
+ d.append(Dictionary.get("OptionsPane.Successful"));
+ break;
+ case UNSUCCESSFUL:
+ d.append(Dictionary.get("OptionsPane.Unsuccessful"));
+ break;
+ case CANCELLED:
+ d.append(Dictionary.get("OptionsPane.Cancelled"));
+ break;
+ case SCHEDULED:
+ d.append(Dictionary.get("OptionsPane.Scheduled"));
+ break;
+ default:
+ d.append(Dictionary.get("OptionsPane.Unknown"));
+ }
+ display = d.toString();
+ }
+ return display;
+ }
+ }
+
+ /** a ListSelectionListener that triggers the load of a newly selected log */
+ private class LogListListener implements ListSelectionListener {
+
+ public void valueChanged(ListSelectionEvent e) {
+ if (!e.getValueIsAdjusting()) { // we get two events for one change in list selection - use the false one ( the second one)
+ ///ystem.err.println("Log change detected.");
+ JList source = (JList)e.getSource();
+ file_entry = (FileEntry) source.getSelectedValue();
+ // First we determine if the old log has been completely written to file
+ Document document = log_textarea.getDocument();
+ ///ystem.err.println(" * current document: " + document);
+ ///ystem.err.println(" * new document: " + file_entry.getDocument());
+ // If we are dealing with the same document don't do anything.
+ if(document != file_entry.getDocument()) {
+ if(document instanceof AppendLineOnlyFileDocument) {
+ AppendLineOnlyFileDocument append_line_only_file_document = (AppendLineOnlyFileDocument) document;
+ if(append_line_only_file_document.isStillWriting()) {
+ ///ystem.err.println("Current log is still active... finishing.");
+ writing_documents.add(append_line_only_file_document); // We have to maintain a reference until they are all done.
+ append_line_only_file_document.setOwner(OptionsPane.this);
+ append_line_only_file_document.setExit();
+ }
+ else {
+ ///ystem.err.println("Current log is complete. Nothing to do.");
+ }
+ }
+ // Load the new log
+ log_textarea.setDocument(file_entry.getDocument());
+ }
+ }
+ }
+ }
+
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/Preferences.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/Preferences.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/Preferences.java (revision 31635)
@@ -0,0 +1,1152 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.plaf.*;
+import org.greenstone.gatherer.remote.RemoteGreenstoneServer;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.GAuthenticator;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.cdm.ClassifierManager;
+import org.greenstone.gatherer.cdm.LanguageManager;
+import org.greenstone.gatherer.cdm.PluginManager;
+import org.greenstone.gatherer.collection.Collection;
+import org.greenstone.gatherer.gui.tree.DragTree;
+import org.greenstone.gatherer.util.ArrayTools; // just for debug
+import org.greenstone.gatherer.util.CheckList;
+import org.greenstone.gatherer.util.CheckListEntry;
+import org.greenstone.gatherer.util.StaticStrings;
+import org.greenstone.gatherer.util.XMLTools;
+import org.w3c.dom.*;
+
+public class Preferences
+ extends ModalDialog
+{
+ static final public String CONNECTION_PREFS = "connection";
+ static final public String GENERAL_PREFS = "general";
+
+ static final private Dimension LABEL_SIZE = new Dimension(280, 25);
+ static final private Dimension ROW_SIZE = new Dimension(640, 25);
+ static final private Dimension SIZE = new Dimension(640, 345);
+ static final private String TRUE = "true";
+
+ private CheckList warning_preferences_check_list;
+ private EmailField email_field;
+ private JButton apply_button;
+ private JButton cancel_button;
+ private JButton chdir_button;
+ private JButton ok_button;
+ private JCheckBox show_file_size_checkbox;
+ private JCheckBox use_proxy_checkbox;
+ private JCheckBox view_extracted_metadata_checkbox;
+ private JCheckBox workflow_download;
+ private JCheckBox workflow_gather;
+ private JCheckBox workflow_enrich;
+ private JCheckBox workflow_design;
+ private JCheckBox workflow_create;
+ private JCheckBox workflow_format;
+ private JComboBox language_combobox;
+ private JComboBox servlet_combobox; // GS3
+ private JComboBox site_combobox; // GS3
+ private JRadioButton assistant_mode_radio_button;
+ private JRadioButton expert_mode_radio_button;
+ private JRadioButton librarian_mode_radio_button;
+ private JSpinner proxy_port_field;
+ private JTabbedPane tab_pane;
+ private JTextArea mode_description_textarea;
+ private JTextField font_field;
+ private JTextField gliserver_url_field;
+ private JTextField library_path_field;
+ private JTextField program_field;
+ private JTextField proxy_host_field;
+ private JTextField collect_dir_field;
+
+ private Preferences self;
+
+ private String current_site_selection;
+
+ public Preferences() {
+ this(GENERAL_PREFS);
+ }
+
+ public Preferences(String initial_view) {
+ // Initialize
+ super(Gatherer.g_man, true);
+ this.self = this;
+ setSize(SIZE);
+ setTitle(Dictionary.get("Preferences"));
+ setJMenuBar(new SimpleMenuBar("preferences"));
+
+ // Creation
+ JPanel content_pane = (JPanel) getContentPane();
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+ tab_pane = new JTabbedPane();
+ tab_pane.setComponentOrientation(Dictionary.getOrientation());
+ JPanel general_preferences = createGeneralPreferences();
+ general_preferences.setComponentOrientation(Dictionary.getOrientation());
+ tab_pane.addTab(Dictionary.get("Preferences.General"), null, general_preferences, Dictionary.get("Preferences.General_Tooltip"));
+ tab_pane.addTab(Dictionary.get("Preferences.Mode"), null, createModePreferences(), Dictionary.get("Preferences.Mode_Tooltip"));
+ // tab_pane.addTab(Dictionary.get("Preferences.Workflow"), null, createWorkflowPreferences(), Dictionary.get("Preferences.Workflow_Tooltip"));
+ JPanel connection_preferences = createConnectionPreferences();
+ connection_preferences.setComponentOrientation(Dictionary.getOrientation());
+ tab_pane.addTab(Dictionary.get("Preferences.Connection"), null, connection_preferences, Dictionary.get("Preferences.Connection_Tooltip"));
+ tab_pane.addTab(Dictionary.get("Preferences.Warnings"), null, createWarningPreferences(), Dictionary.get("Preferences.Warnings_Tooltip"));
+
+ JPanel button_pane = new JPanel();
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+ ok_button = new GLIButton(Dictionary.get("General.OK"), Dictionary.get("General.OK_Tooltip"));
+
+ apply_button = new GLIButton(Dictionary.get("General.Apply"), Dictionary.get("General.Apply_Tooltip"));
+
+ cancel_button = new GLIButton(Dictionary.get("General.Cancel"), Dictionary.get("General.Cancel_Tooltip"));
+
+ // Connection
+ ok_button.addActionListener(new OKButtonListener(true));
+ apply_button.addActionListener(new OKButtonListener(false));
+ cancel_button.addActionListener(new CancelButtonListener());
+
+ // Layout
+ button_pane.setBorder(BorderFactory.createEmptyBorder(5,2,2,2));
+ button_pane.setLayout(new GridLayout(1,3,0,5));
+ button_pane.add(ok_button);
+ button_pane.add(apply_button);
+ button_pane.add(cancel_button);
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ content_pane.setLayout(new BorderLayout());
+ content_pane.add(tab_pane, BorderLayout.CENTER);
+ content_pane.add(button_pane, BorderLayout.SOUTH);
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ Dimension frame_size = Gatherer.g_man.getSize();
+ Point frame_location = Gatherer.g_man.getLocation();
+ setLocation(((frame_size.width - SIZE.width) / 2), ((frame_size.height - SIZE.height)));
+
+ // Bring the desired pane to the fore
+ if (initial_view.equals(CONNECTION_PREFS)) {
+ tab_pane.setSelectedComponent(connection_preferences);
+ }
+ else {
+ tab_pane.setSelectedComponent(general_preferences);
+ }
+
+ // Clean up
+ general_preferences = null;
+ connection_preferences = null;
+ frame_location = null;
+ frame_size = null;
+ cancel_button = null;
+ ok_button = null;
+ button_pane = null;
+ tab_pane = null;
+ content_pane = null;
+
+ setVisible(true);
+ }
+
+ private JPanel createConnectionPreferences() {
+ JPanel program_pane = new JPanel();
+ program_pane.setComponentOrientation(Dictionary.getOrientation());
+ program_pane.setPreferredSize(ROW_SIZE);
+ JLabel program_label = new JLabel(Dictionary.get("Preferences.Connection.ProgramCommand"));
+ program_label.setPreferredSize(LABEL_SIZE);
+ program_label.setComponentOrientation(Dictionary.getOrientation());
+
+ program_field = new JTextField(Configuration.getPreviewCommand());
+ program_field.setCaretPosition(0);
+ program_field.setToolTipText(Dictionary.get("Preferences.Connection.ProgramCommand_Tooltip"));
+ program_field.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel library_path_pane = new JPanel();
+ library_path_pane.setPreferredSize(ROW_SIZE);
+ library_path_pane.setComponentOrientation(Dictionary.getOrientation());
+ JLabel library_path_label = new JLabel();
+ library_path_label.setPreferredSize(LABEL_SIZE);
+ library_path_label.setComponentOrientation(Dictionary.getOrientation());
+ String library_url_string = "";
+ if (Configuration.library_url != null) {
+ library_url_string = Configuration.library_url.toString();
+ }
+ library_path_field = new JTextField(library_url_string);
+ library_path_field.setCaretPosition(0);
+ library_path_field.setComponentOrientation(Dictionary.getOrientation());
+ if (Gatherer.GS3) {
+ library_path_label.setText(Dictionary.get("Preferences.Connection.Library_Path_GS3"));
+ library_path_field.setToolTipText(Dictionary.get("Preferences.Connection.Library_Path_Tooltip_GS3"));
+ } else {
+ library_path_label.setText(Dictionary.get("Preferences.Connection.Library_Path"));
+ library_path_field.setToolTipText(Dictionary.get("Preferences.Connection.Library_Path_Tooltip"));
+ }
+
+ // Disable this field when using the applet, as it is automatically determined
+ library_path_label.setEnabled(!Gatherer.isApplet);
+ library_path_field.setEnabled(!Gatherer.isApplet);
+
+ JPanel gliserver_url_pane = null;
+ JLabel gliserver_url_label = null;
+ if (Gatherer.isGsdlRemote && !Gatherer.GS3) {
+ gliserver_url_pane = new JPanel();
+ gliserver_url_pane.setPreferredSize(ROW_SIZE);
+ gliserver_url_label = new JLabel(Dictionary.get("Preferences.Connection.GLIServer_URL"));
+ gliserver_url_label.setPreferredSize(LABEL_SIZE);
+ gliserver_url_label.setComponentOrientation(Dictionary.getOrientation());
+ String gliserver_url_string = "";
+ if (Configuration.gliserver_url != null) {
+ gliserver_url_string = Configuration.gliserver_url.toString();
+ }
+ gliserver_url_field = new JTextField(gliserver_url_string);
+ gliserver_url_field.setComponentOrientation(Dictionary.getOrientation());
+ gliserver_url_field.setCaretPosition(0);
+ gliserver_url_field.setToolTipText(Dictionary.get("Preferences.Connection.GLIServer_URL_Tooltip"));
+
+ // Disable this field when using the applet, as it is automatically determined
+ gliserver_url_label.setEnabled(!Gatherer.isApplet);
+ gliserver_url_field.setEnabled(!Gatherer.isApplet);
+ }
+
+ JPanel site_pane = null;
+ JLabel site_label = null;
+ JPanel servlet_pane = null;
+ JLabel servlet_label = null;
+ if (Gatherer.GS3) {
+ site_pane = new JPanel();
+ site_pane.setPreferredSize(ROW_SIZE);
+ site_pane.setComponentOrientation(Dictionary.getOrientation());
+ site_label = new JLabel(Dictionary.get("Preferences.Connection.Site"));
+ site_label.setPreferredSize(LABEL_SIZE);
+ site_label.setComponentOrientation(Dictionary.getOrientation());
+ // what should we do if Gatherer.servlet_config.getSites() is null?
+ site_combobox = new JComboBox(Gatherer.servlet_config.getSites().toArray());
+ site_combobox.setOpaque(false);
+ site_combobox.setToolTipText(Dictionary.get("Preferences.Connection.Site_Tooltip"));
+ site_combobox.setComponentOrientation(Dictionary.getOrientation());
+
+ servlet_pane = new JPanel();
+ servlet_pane.setPreferredSize(ROW_SIZE);
+ servlet_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ servlet_label = new JLabel(Dictionary.get("Preferences.Connection.Servlet"));
+ servlet_label.setPreferredSize(LABEL_SIZE);
+ servlet_label.setComponentOrientation(Dictionary.getOrientation());
+ servlet_combobox = new JComboBox();
+ servlet_combobox.setOpaque(false);
+ servlet_combobox.setComponentOrientation(Dictionary.getOrientation());
+ // try to locate and select the current site
+ String this_site = Configuration.site_name;
+ for(int b = 0; b < site_combobox.getItemCount(); b++) {
+ String entry = (String) site_combobox.getItemAt(b);
+ if(this_site.equals(entry)) {
+ site_combobox.setSelectedIndex(b);
+ break;
+ }
+ }
+
+ // just in case its not the current one?
+ current_site_selection = (String)site_combobox.getSelectedItem();
+
+ ArrayList servlet_options = Gatherer.servlet_config.getServletsForSite(current_site_selection);
+ if (servlet_options == null) {
+ servlet_combobox.setToolTipText(Dictionary.get("Preferences.Connection.Servlet_Tooltip2"));
+ //servlet_combobox.setModel(new DefaultComboBoxModel());
+ servlet_combobox.setEnabled(false);
+ } else {
+ ///ystem.err.println(ArrayTools.objectArrayToString(servlet_options.toArray()));
+
+ servlet_combobox.setModel(new DefaultComboBoxModel(servlet_options.toArray()));
+ servlet_combobox.setToolTipText(Dictionary.get("Preferences.Connection.Servlet_Tooltip"));
+ servlet_combobox.setEnabled(true);
+ // try to locate and select the current servlet
+ String this_servlet = Configuration.getServletPath();
+ for(int b = 0; b < servlet_combobox.getItemCount(); b++) {
+ String entry = (String) servlet_combobox.getItemAt(b);
+ if(this_servlet.equals(entry)) {
+ servlet_combobox.setSelectedIndex(b);
+ break;
+ }
+ }
+
+ }
+ }
+
+ JPanel collect_dir_pane = new JPanel();
+ collect_dir_pane.setComponentOrientation(Dictionary.getOrientation());
+ collect_dir_pane.setPreferredSize(ROW_SIZE);
+ JLabel collect_dir_label = new JLabel(Dictionary.get("Preferences.Connection.CollectDirectory"));
+ collect_dir_label.setPreferredSize(LABEL_SIZE);
+ collect_dir_label.setComponentOrientation(Dictionary.getOrientation());
+ collect_dir_field = new JTextField(Gatherer.getCollectDirectoryPath());
+ //collect_dir_field = new JTextField(Configuration.getString("general.open_collection"+Configuration.gliPropertyNameSuffix(), true));
+ collect_dir_field.setCaretPosition(0);
+ collect_dir_field.setToolTipText(Dictionary.get("Preferences.Connection.CollectDirectory_Tooltip"));
+ collect_dir_field.setEditable(false);
+ JButton chdir_button = new GLIButton(Dictionary.get("General.CD"), Dictionary.get("General.CD_Tooltip"));
+ chdir_button.addActionListener(new ChangeDirListener());
+ if(Gatherer.isGsdlRemote) { // disable changing directories for client GLI
+ chdir_button.setEnabled(false);
+ }
+
+ boolean currently_enabled = Configuration.get("general.use_proxy", true);
+ // Creation
+ JPanel connection_pane = new JPanel();
+ connection_pane.setComponentOrientation(Dictionary.getOrientation());
+ use_proxy_checkbox = new JCheckBox(Dictionary.get("Preferences.Connection.Use_Proxy"));
+ use_proxy_checkbox.setSelected(currently_enabled);
+ use_proxy_checkbox.setComponentOrientation(Dictionary.getOrientation());
+ use_proxy_checkbox.setPreferredSize(ROW_SIZE);
+
+ JPanel proxy_host_pane = new JPanel();
+ proxy_host_pane.setComponentOrientation(Dictionary.getOrientation());
+ proxy_host_pane.setPreferredSize(ROW_SIZE);
+
+ JLabel proxy_host_label = new JLabel(Dictionary.get("Preferences.Connection.Proxy_Host"));
+ proxy_host_label.setComponentOrientation(Dictionary.getOrientation());
+ proxy_host_label.setPreferredSize(LABEL_SIZE);
+
+ proxy_host_field = new JTextField(Configuration.getString("general.proxy_host", true));
+ proxy_host_field.setEnabled(currently_enabled);
+ proxy_host_field.setToolTipText(Dictionary.get("Preferences.Connection.Proxy_Host_Tooltip"));
+ proxy_host_field.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel proxy_port_pane = new JPanel();
+ proxy_port_pane.setComponentOrientation(Dictionary.getOrientation());
+ proxy_port_pane.setPreferredSize(ROW_SIZE);
+
+ JLabel proxy_port_label = new JLabel(Dictionary.get("Preferences.Connection.Proxy_Port"));
+ proxy_port_label.setPreferredSize(LABEL_SIZE);
+ proxy_port_label.setComponentOrientation(Dictionary.getOrientation());
+
+ String port_value = Configuration.getString("general.proxy_port", true);
+ if(port_value.length() > 0) {
+ proxy_port_field = new JSpinner(new SpinnerNumberModel((new Integer(port_value)).intValue(), 0, 65535, 1));
+ }
+ else {
+ proxy_port_field = new JSpinner(new SpinnerNumberModel(0, 0, 65535, 1));
+ }
+ proxy_port_field.setEnabled(currently_enabled);
+ proxy_port_field.setToolTipText(Dictionary.get("Preferences.Connection.Proxy_Port_Tooltip"));
+
+ // Connection
+ use_proxy_checkbox.addActionListener(new UseProxyListener());
+ if (Gatherer.GS3) {
+ site_combobox.addActionListener(new SiteComboboxListener());
+ }
+
+ // Layout
+ program_pane.setLayout(new BorderLayout());
+ program_pane.add(program_label, BorderLayout.LINE_START);
+ program_pane.add(program_field, BorderLayout.CENTER);
+
+ library_path_pane.setLayout(new BorderLayout());
+ library_path_pane.add(library_path_label, BorderLayout.LINE_START);
+ library_path_pane.add(library_path_field, BorderLayout.CENTER);
+
+ if (Gatherer.isGsdlRemote && !Gatherer.GS3) {
+ gliserver_url_pane.setLayout(new BorderLayout());
+ gliserver_url_pane.add(gliserver_url_label, BorderLayout.LINE_START);
+ gliserver_url_pane.add(gliserver_url_field, BorderLayout.CENTER);
+ }
+
+ if (Gatherer.GS3) {
+ site_pane.setLayout(new BorderLayout());
+ site_pane.add(site_label, BorderLayout.LINE_START);
+ site_pane.add(site_combobox, BorderLayout.CENTER);
+
+ servlet_pane.setLayout(new BorderLayout());
+ servlet_pane.add(servlet_label, BorderLayout.LINE_START);
+ servlet_pane.add(servlet_combobox, BorderLayout.CENTER);
+ }
+
+ collect_dir_pane.setLayout(new BorderLayout());
+ collect_dir_pane.add(collect_dir_label, BorderLayout.LINE_START);
+ collect_dir_pane.add(collect_dir_field, BorderLayout.CENTER);
+ collect_dir_pane.add(chdir_button, BorderLayout.LINE_END);
+
+ proxy_host_pane.setLayout(new BorderLayout());
+ proxy_host_pane.add(proxy_host_label, BorderLayout.LINE_START);
+ proxy_host_pane.add(proxy_host_field, BorderLayout.CENTER);
+
+ proxy_port_pane.setLayout(new BorderLayout());
+ proxy_port_pane.add(proxy_port_label, BorderLayout.LINE_START);
+ proxy_port_pane.add(proxy_port_field, BorderLayout.CENTER);
+
+ connection_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ connection_pane.setLayout(new GridLayout(8,1,0,2));
+ connection_pane.add(program_pane);
+ connection_pane.add(library_path_pane);
+ if (Gatherer.isGsdlRemote && !Gatherer.GS3) {
+ connection_pane.add(gliserver_url_pane);
+ }
+ if (Gatherer.GS3) {
+ connection_pane.add(site_pane);
+ connection_pane.add(servlet_pane);
+ }
+ connection_pane.add(collect_dir_pane);
+
+ connection_pane.add(use_proxy_checkbox);
+ connection_pane.add(proxy_host_pane);
+ connection_pane.add(proxy_port_pane);
+
+ return connection_pane;
+ }
+
+ private JPanel createGeneralPreferences() {
+ JPanel general_pane = new JPanel();
+ general_pane.setComponentOrientation(Dictionary.getOrientation());
+ // Build the model of available languages
+ ArrayList dictionary_model = new ArrayList();
+
+ // The new method makes use of the successor to the languages.dat file, classes/xml/languages.xml
+ NodeList language_elements = LanguageManager.LANGUAGES_DOCUMENT.getDocumentElement().getElementsByTagName(StaticStrings.LANGUAGE_ELEMENT);
+ for(int i = 0; i < language_elements.getLength(); i++) {
+ Element language_element = (Element) language_elements.item(i);
+ if((language_element.hasAttribute(StaticStrings.GLI_ATTRIBUTE)
+ && (language_element.getAttribute(StaticStrings.GLI_ATTRIBUTE)).equalsIgnoreCase(StaticStrings.TRUE_STR))
+ || (language_element.hasAttribute(StaticStrings.MDS_ATTRIBUTE)
+ && (language_element.getAttribute(StaticStrings.MDS_ATTRIBUTE)).equalsIgnoreCase(StaticStrings.TRUE_STR)))
+ {
+ Locale locale = new Locale(language_element.getAttribute(StaticStrings.CODE_ATTRIBUTE));
+ String description = language_element.getAttribute(StaticStrings.NAME_ATTRIBUTE);
+ DictionaryEntry entry = new DictionaryEntry(description, locale);
+ if(!dictionary_model.contains(entry)) {
+ dictionary_model.add(entry);
+ }
+ entry = null;
+ description = null;
+ locale = null;
+ }
+ language_element = null;
+ }
+ language_elements = null;
+
+ // Users email
+ JPanel email_pane = new JPanel();
+ email_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JLabel email_label = new JLabel(Dictionary.get("Preferences.General.Email"));
+ email_label.setComponentOrientation(Dictionary.getOrientation());
+ email_label.setPreferredSize(LABEL_SIZE);
+
+ email_field = new EmailField(Configuration.getColor("coloring.error_background", false));
+ email_field.setText(Configuration.getEmail());
+ email_field.setToolTipText(Dictionary.get("Preferences.General.Email_Tooltip"));
+ email_field.setComponentOrientation(Dictionary.getOrientation());
+ // Font selection
+ JPanel font_pane = new JPanel();
+ font_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JLabel font_label = new JLabel(Dictionary.get("Preferences.General.Font"));
+ font_label.setComponentOrientation(Dictionary.getOrientation());
+ font_label.setPreferredSize(LABEL_SIZE);
+
+ font_field = new JTextField(Configuration.getString("general.font", true));
+ font_field.setToolTipText(Dictionary.get("Preferences.General.Font_Tooltip"));
+ font_field.setComponentOrientation(Dictionary.getOrientation());
+
+ // Extracted metadata
+ view_extracted_metadata_checkbox = new JCheckBox(Dictionary.get("Preferences.General.View_Extracted_Metadata"));
+ view_extracted_metadata_checkbox.setSelected(false);
+ view_extracted_metadata_checkbox.setComponentOrientation(Dictionary.getOrientation());
+
+ if (Configuration.get("general.view_extracted_metadata", Configuration.COLLECTION_SPECIFIC)) {
+ view_extracted_metadata_checkbox.setSelected(true);
+ }
+ view_extracted_metadata_checkbox.setToolTipText(Dictionary.get("Preferences.General.View_Extracted_Metadata_Tooltip"));
+ view_extracted_metadata_checkbox.setComponentOrientation(Dictionary.getOrientation());
+
+ // Show file sizes
+ show_file_size_checkbox = new JCheckBox(Dictionary.get("Preferences.General.Show_File_Size"));
+ show_file_size_checkbox.setSelected(false);
+ show_file_size_checkbox.setComponentOrientation(Dictionary.getOrientation());
+
+ if (Configuration.get("general.show_file_size", Configuration.COLLECTION_SPECIFIC)) {
+ show_file_size_checkbox.setSelected(true);
+ }
+ show_file_size_checkbox.setToolTipText(Dictionary.get("Preferences.General.Show_File_Size_Tooltip"));
+
+ // Language
+ JPanel language_pane = new JPanel();
+ language_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JLabel language_label = new JLabel(Dictionary.get("Preferences.General.Interface_Language"));
+ language_label.setComponentOrientation(Dictionary.getOrientation());
+ language_label.setPreferredSize(LABEL_SIZE);
+
+ language_combobox = new JComboBox(dictionary_model.toArray());
+ language_combobox.setOpaque(false);
+ language_combobox.setToolTipText(Dictionary.get("Preferences.General.Interface_Language_Tooltip"));
+ language_combobox.setComponentOrientation(Dictionary.getOrientation());
+
+ // Try to locate and select the current language
+ // Else default to English
+ String language_code = Configuration.getLanguage();
+ int defaultIndex = 0;
+ DictionaryEntry defaultEntry = null;
+
+ int b = 0;
+ for (; b < language_combobox.getItemCount(); b++) {
+ DictionaryEntry entry = (DictionaryEntry) language_combobox.getItemAt(b);
+ if (language_code.equalsIgnoreCase(entry.getLocale().getLanguage())) {
+ language_combobox.setSelectedIndex(b);
+ break;
+ } else if (entry.getLocale().getLanguage().equalsIgnoreCase("en")) { // store English as fallback
+ defaultIndex = b;
+ defaultEntry = entry;
+ }
+ }
+ // if we cycled through and couldn't find the chosen language to load,
+ // then we set the Preferences' and Configuration's language to English
+ if (b == language_combobox.getItemCount()) {
+ language_combobox.setSelectedIndex(defaultIndex);
+ Configuration.setLocale("general.locale", true, defaultEntry.getLocale());
+ System.err.println("*** GLI doesn't yet support the language: " + language_code
+ + " and has therefore defaulted to: " + Configuration.getLanguage());
+ }
+
+ // Layout
+ email_pane.setLayout(new BorderLayout());
+ email_pane.add(email_label, BorderLayout.LINE_START);
+ email_pane.add(email_field, BorderLayout.CENTER);
+
+ language_pane.setLayout(new BorderLayout());
+ language_pane.add(language_label, BorderLayout.LINE_START);
+ language_pane.add(language_combobox, BorderLayout.CENTER);
+
+ font_pane.setLayout(new BorderLayout());
+ font_pane.add(font_label, BorderLayout.LINE_START);
+ font_pane.add(font_field, BorderLayout.CENTER);
+
+ general_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ general_pane.setLayout(new GridLayout(5,1,0,5));
+ general_pane.add(email_pane);
+ general_pane.add(language_pane);
+ general_pane.add(font_pane);
+ general_pane.add(view_extracted_metadata_checkbox);
+ general_pane.add(show_file_size_checkbox);
+
+ return general_pane;
+ }
+
+ /** Generate the controls for altering the detail mode.
+ * @return a JPanel of controls
+ */
+ private JPanel createModePreferences() {
+ // Create Controls
+ JPanel mode_panel = new JPanel();
+ mode_panel.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel button_panel = new JPanel();
+ button_panel.setComponentOrientation(Dictionary.getOrientation());
+
+ ButtonGroup mode_button_group = new ButtonGroup();
+
+ assistant_mode_radio_button = new JRadioButton(Dictionary.get("Preferences.Mode.Assistant"));
+ assistant_mode_radio_button.setOpaque(false);
+ assistant_mode_radio_button.setComponentOrientation(Dictionary.getOrientation());
+ mode_button_group.add(assistant_mode_radio_button);
+
+ expert_mode_radio_button = new JRadioButton(Dictionary.get("Preferences.Mode.Expert"));
+ expert_mode_radio_button.setOpaque(false);
+ expert_mode_radio_button.setComponentOrientation(Dictionary.getOrientation());
+ mode_button_group.add(expert_mode_radio_button);
+
+ librarian_mode_radio_button = new JRadioButton(Dictionary.get("Preferences.Mode.Librarian"));
+ librarian_mode_radio_button.setOpaque(false);
+ librarian_mode_radio_button.setComponentOrientation(Dictionary.getOrientation());
+ mode_button_group.add(librarian_mode_radio_button);
+
+
+ mode_description_textarea = new JTextArea();
+ mode_description_textarea.setComponentOrientation(Dictionary.getOrientation());
+ mode_description_textarea.setEditable(false);
+ mode_description_textarea.setLineWrap(true);
+ mode_description_textarea.setWrapStyleWord(true);
+ // Determine which value is already selected
+ switch(Configuration.getMode()) {
+ case Configuration.ASSISTANT_MODE:
+ assistant_mode_radio_button.setSelected(true);
+ mode_description_textarea.setText(Dictionary.get("Preferences.Mode.Assistant_Description"));
+ break;
+ case Configuration.EXPERT_MODE:
+ expert_mode_radio_button.setSelected(true);
+ mode_description_textarea.setText(Dictionary.get("Preferences.Mode.Expert_Description"));
+ break;
+ default:
+ librarian_mode_radio_button.setSelected(true);
+ mode_description_textarea.setText(Dictionary.get("Preferences.Mode.Librarian_Description"));
+ }
+ // Connection - when a radio button is selected we have to change the description
+ ModeRadioButtonListener listener = new ModeRadioButtonListener();
+ assistant_mode_radio_button.addActionListener(listener);
+ expert_mode_radio_button.addActionListener(listener);
+ librarian_mode_radio_button.addActionListener(listener);
+ listener = null;
+ // Layout
+ button_panel.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
+ button_panel.setLayout(new GridLayout(3,1,2,2));
+ button_panel.add(assistant_mode_radio_button);
+ button_panel.add(librarian_mode_radio_button);
+ button_panel.add(expert_mode_radio_button);
+
+ mode_panel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ mode_panel.setLayout(new BorderLayout());
+ mode_panel.add(button_panel, BorderLayout.NORTH);
+ mode_panel.add(new JScrollPane(mode_description_textarea), BorderLayout.CENTER);
+
+ return mode_panel;
+ }
+
+
+ /** The warning preferences are controlled through a checklist. */
+ private JPanel createWarningPreferences()
+ {
+ warning_preferences_check_list = new CheckList(false);
+ warning_preferences_check_list.setComponentOrientation(Dictionary.getOrientation());
+
+ // Read all the warnings from the general xml/config.xml file, and their values from the user config.xml file
+ Document general_config_xml_file_document = XMLTools.parseXMLFile("xml/config.xml", true);
+ NodeList argument_elements_nodelist = general_config_xml_file_document.getDocumentElement().getElementsByTagName("Argument");
+ for (int i = 0; i < argument_elements_nodelist.getLength(); i++) {
+ Element argument_element = (Element) argument_elements_nodelist.item(i);
+ String argument_element_name = argument_element.getAttribute("name");
+ if (argument_element_name.startsWith("warning.")) {
+ String warning_title = Dictionary.get(argument_element_name.substring("warning.".length()) + ".Title");
+ boolean warning_enabled = Configuration.get(argument_element_name, true);
+ CheckListEntry warning_entry = new CheckListEntry(warning_title, warning_enabled);
+ warning_entry.setProperty(argument_element_name);
+ warning_preferences_check_list.addEntry(warning_entry);
+ }
+ }
+
+ JPanel warning_preferences_pane = new JPanel();
+ warning_preferences_pane.setComponentOrientation(Dictionary.getOrientation());
+ warning_preferences_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ warning_preferences_pane.setLayout(new BorderLayout());
+ JScrollPane scrol_tmp = new JScrollPane(warning_preferences_check_list);
+ scrol_tmp.setComponentOrientation(Dictionary.getOrientation());
+ warning_preferences_pane.add(scrol_tmp, BorderLayout.CENTER);
+ return warning_preferences_pane;
+ }
+
+
+ private JPanel createWorkflowPreferences() {
+ // Read in the predefined configurations file
+ Vector predefined = new Vector();
+ Document predefined_document = XMLTools.parseXMLFile("xml/workflows.xml", true);
+ Element predefined_element = predefined_document.getDocumentElement();
+ NodeList workflow_elements = predefined_element.getElementsByTagName("Workflow");
+ for(int i = 0; i < workflow_elements.getLength(); i++) {
+ predefined.add(new WorkflowElementWrapper((Element)workflow_elements.item(i)));
+ }
+
+ // Creation
+ JPanel workflow_preferences_pane = new JPanel();
+ workflow_preferences_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel checklist_pane = new JPanel();
+ checklist_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JLabel title_label = new JLabel(Dictionary.get("Preferences.Workflow.Title"));
+ title_label.setComponentOrientation(Dictionary.getOrientation());
+ title_label.setPreferredSize(ROW_SIZE);
+
+ workflow_download = new JCheckBox(Dictionary.get("GUI.Download")+" - "+Dictionary.get("GUI.Download_Tooltip"));
+ workflow_download.setComponentOrientation(Dictionary.getOrientation());
+ workflow_download.setSelected(Configuration.get("workflow.download", false) && Gatherer.isDownloadEnabled);
+ workflow_download.setPreferredSize(ROW_SIZE);
+
+ workflow_gather = new JCheckBox(Dictionary.get("GUI.Gather")+" - "+Dictionary.get("GUI.Gather_Tooltip"));
+ workflow_gather.setSelected(Configuration.get("workflow.gather", false));
+ workflow_gather.setPreferredSize(ROW_SIZE);
+ workflow_gather.setComponentOrientation(Dictionary.getOrientation());
+
+ workflow_enrich = new JCheckBox(Dictionary.get("GUI.Enrich")+" - "+Dictionary.get("GUI.Enrich_Tooltip"));
+ workflow_enrich.setSelected(Configuration.get("workflow.enrich", false));
+ workflow_enrich.setPreferredSize(ROW_SIZE);
+ workflow_enrich.setComponentOrientation(Dictionary.getOrientation());
+
+ workflow_design = new JCheckBox(Dictionary.get("GUI.Design")+" - "+Dictionary.get("GUI.Design_Tooltip"));
+ workflow_design.setSelected(Configuration.get("workflow.design", false));
+ workflow_design.setPreferredSize(ROW_SIZE);
+ workflow_design.setComponentOrientation(Dictionary.getOrientation());
+
+ workflow_create = new JCheckBox(Dictionary.get("GUI.Create")+" - "+Dictionary.get("GUI.Create_Tooltip"));
+ workflow_create.setSelected(Configuration.get("workflow.create", false));
+ workflow_create.setPreferredSize(ROW_SIZE);
+ workflow_create.setComponentOrientation(Dictionary.getOrientation());
+
+ workflow_format = new JCheckBox(Dictionary.get("GUI.Format")+" - "+Dictionary.get("GUI.Format_Tooltip"));
+ workflow_format.setSelected(Configuration.get("workflow.format", false));
+ workflow_format.setPreferredSize(ROW_SIZE);
+ workflow_format.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel predefined_pane = new JPanel();
+ predefined_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JLabel predefined_label = new JLabel(Dictionary.get("Preferences.Workflow.Predefined.Label"));
+ predefined_label.setComponentOrientation(Dictionary.getOrientation());
+
+ JComboBox predefined_combobox = new JComboBox(predefined);
+ predefined_combobox.setOpaque(false);
+ // Connection
+ predefined_combobox.addActionListener(new PredefinedActionListener());
+
+ // Layout
+ checklist_pane.setLayout(new BoxLayout(checklist_pane, BoxLayout.Y_AXIS));
+ checklist_pane.add(title_label);
+ if (Configuration.get("workflow.download", true)) {
+ checklist_pane.add(workflow_download);
+ }
+ if (Configuration.get("workflow.gather", true)) {
+ checklist_pane.add(workflow_gather);
+ }
+ if (Configuration.get("workflow.enrich", true)) {
+ checklist_pane.add(workflow_enrich);
+ }
+ if (Configuration.get("workflow.design", true)) {
+ checklist_pane.add(workflow_design);
+ }
+ if (Configuration.get("workflow.create", true)) {
+ checklist_pane.add(workflow_create);
+ }
+ if (Configuration.get("workflow.format", true)) {
+ checklist_pane.add(workflow_format);
+ }
+
+ predefined_pane.setLayout(new BorderLayout(5,0));
+ predefined_pane.add(predefined_label, BorderLayout.LINE_START);
+ predefined_pane.add(predefined_combobox, BorderLayout.CENTER);
+
+ workflow_preferences_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ workflow_preferences_pane.setLayout(new BorderLayout());
+ workflow_preferences_pane.add(checklist_pane, BorderLayout.CENTER);
+ workflow_preferences_pane.add(predefined_pane, BorderLayout.SOUTH);
+
+ return workflow_preferences_pane;
+ }
+
+ private class ChangeDirListener implements ActionListener {
+ public void actionPerformed(ActionEvent event) {
+ JFileChooser chooser = new JFileChooser(collect_dir_field.getText());
+ chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+ int returnVal = chooser.showOpenDialog(Preferences.this);
+ if(returnVal == JFileChooser.APPROVE_OPTION) {
+ collect_dir_field.setText(chooser.getSelectedFile().getAbsolutePath() + File.separator);
+ } // else collect_dir_field text remains as it is
+ }
+ }
+
+ private class OKButtonListener
+ implements ActionListener {
+ private boolean close;
+ public OKButtonListener(boolean close) {
+ this.close = close;
+ }
+ public void actionPerformed(ActionEvent event)
+ {
+ // Several options requiring restarting the GLI to apply:
+ // interface font, interface language, changed gliserver url or library url
+ boolean restart_required = false;
+ boolean keep_collection_open = false;
+ String restart_message = "";
+
+
+ // Connection preferences
+ String program_str = program_field.getText();
+ if (program_str.length() > 0 && program_str.indexOf("%1") == -1) {
+ program_str = program_str + " %1";
+ }
+ Configuration.setPreviewCommand(program_str);
+
+ String old_library_url = (Configuration.library_url == null) ? null : Configuration.library_url.toString();
+ String library_url_string = library_path_field.getText();
+ if(old_library_url != null && !old_library_url.equals(library_url_string)) {
+
+ // If the server is remote and the library path's been changed, then GLI client needs restart
+ // If the server is local, GLI is going to work with the collection of GSDLPath anyway,
+ // even if the library path for previewing has changed
+ if (Gatherer.isGsdlRemote) {
+ restart_required = true;
+ restart_message = Dictionary.get("Preferences.General.Manual_Restart_Required");
+
+ }
+ if(Gatherer.c_man.getCollection() != null) {
+ // if the gliserver url has changed while a collection is open,
+ // close the open collection of the old library URL
+ Gatherer.g_man.saveThenCloseCurrentCollection();
+ Configuration.setString("general.open_collection"+Configuration.gliPropertyNameSuffix(), true, "");
+ }
+ }
+ if (library_url_string.equals("")) {
+ Configuration.library_url = null;
+ }
+ else {
+ try {
+ Configuration.library_url = new URL(library_url_string);
+ }
+ catch (MalformedURLException exception) {
+ DebugStream.printStackTrace(exception);
+ }
+ }
+
+ Configuration.setString("general.library_url"+Configuration.gliPropertyNameSuffix(),
+ true, library_url_string);
+
+ if (Gatherer.isGsdlRemote && !Gatherer.GS3) {
+ String old_gliserver_url = Configuration.gliserver_url.toString();
+ String gliserver_url_string = gliserver_url_field.getText();
+ if(!old_gliserver_url.equals(gliserver_url_string)) {
+ restart_required = true;
+ restart_message = Dictionary.get("Preferences.General.Manual_Restart_Required");
+
+ if(Gatherer.c_man.getCollection() != null) {
+ // if the gliserver url has changed while a collection is open,
+ // close the open collection of the old gliserver URL
+ Gatherer.g_man.saveThenCloseCurrentCollection();
+ Configuration.setString("general.open_collection"+Configuration.gliPropertyNameSuffix(), true, "");
+ }
+ }
+ if (gliserver_url_string.equals("")) {
+ Configuration.gliserver_url = null;
+ }
+ else {
+ try {
+ Configuration.gliserver_url = new URL(gliserver_url_string);
+ }
+ catch (MalformedURLException exception) {
+ DebugStream.printStackTrace(exception);
+ }
+ }
+ Configuration.setString("general.gliserver_url", true, gliserver_url_string);
+ }
+
+
+ boolean site_changed = false;
+ if (Gatherer.GS3) {
+ String current_site = Configuration.site_name;
+ String new_site =(String)site_combobox.getSelectedItem() ;
+ if (!new_site.equals(current_site)) {
+ site_changed = true;
+ }
+ Configuration.setSiteAndServlet(new_site, (String)servlet_combobox.getSelectedItem());
+ }
+
+ Configuration.set("general.use_proxy", true, use_proxy_checkbox.isSelected());
+ Configuration.setString("general.proxy_host", true, proxy_host_field.getText());
+ Configuration.setString("general.proxy_port", true, proxy_port_field.getValue() + "");
+ Gatherer.setProxy();
+
+ // General preferences
+ Configuration.setEmail(email_field.getText());
+ Configuration.set("general.show_file_size", Configuration.COLLECTION_SPECIFIC, show_file_size_checkbox.isSelected());
+ Configuration.set("general.view_extracted_metadata", Configuration.COLLECTION_SPECIFIC, view_extracted_metadata_checkbox.isSelected());
+
+
+ // GLI interface font
+ String current_font = Configuration.getString("general.font", true);
+ if (!current_font.equals(font_field.getText())) {
+ Configuration.setString("general.font", true, font_field.getText());
+ restart_required = true;
+ restart_message = Dictionary.get("Preferences.General.Restart_Required");
+
+ }
+
+ // GLI interface language
+ String current_lang = Configuration.getLanguage();
+ if (!current_lang.equals(((DictionaryEntry) language_combobox.getSelectedItem()).getLocale().getLanguage())) {
+ // need to save the collection before changing the locale otherwise it stuffs the metadata names up
+ if(Gatherer.c_man.getCollection() != null) {
+ Configuration.setString("general.open_collection"+Configuration.gliPropertyNameSuffix(), true, Gatherer.c_man.getLoadedCollectionColFilePath());
+ Gatherer.g_man.saveThenCloseCurrentCollection();
+ keep_collection_open = true;
+ }
+
+ Configuration.setLocale("general.locale", Configuration.GENERAL_SETTING, ((DictionaryEntry) language_combobox.getSelectedItem()).getLocale());
+ restart_required = true;
+ restart_message = Dictionary.get("Preferences.General.Restart_Required");
+ }
+
+ // Inform the user that a restart is required, if necessary
+ if (restart_required) {
+ JOptionPane.showMessageDialog(Gatherer.g_man, restart_message, Dictionary.get("General.Warning"), JOptionPane.WARNING_MESSAGE);
+ }
+
+ // Mode preferences
+ int current_mode = Configuration.getMode();
+ int new_mode;
+ if (assistant_mode_radio_button.isSelected()) {
+ new_mode = Configuration.ASSISTANT_MODE;
+ }
+ else if (expert_mode_radio_button.isSelected()) {
+ new_mode = Configuration.EXPERT_MODE;
+ }
+ else {
+ new_mode = Configuration.LIBRARIAN_MODE;
+ }
+
+ // If there has been a change in modes, update the config, and also inform the persistant gui views that have a need to know
+ if (new_mode != current_mode) {
+ Configuration.setMode(new_mode);
+ Collection collection = Gatherer.c_man.getCollection();
+ if (collection != null) {
+ collection.cdm.modeChanged(new_mode);
+ }
+ Gatherer.g_man.modeChanged(new_mode);
+ }
+
+ // Warning preferences
+ ListModel warning_preferences_check_list_model = warning_preferences_check_list.getModel();
+ for (int i = 0; i < warning_preferences_check_list_model.getSize(); i++) {
+ CheckListEntry entry = (CheckListEntry) warning_preferences_check_list_model.getElementAt(i);
+ Configuration.set(entry.getProperty(), true, entry.isSelected());
+ }
+
+ if (Gatherer.GS3 && site_changed && Gatherer.c_man.getCollection() != null && !Gatherer.isGsdlRemote) {
+ // shut down the collection
+ System.err.println("shutting down the collection");
+ Gatherer.g_man.saveThenCloseCurrentCollection();
+ }
+
+ // Workflow preferences
+// Configuration.set("workflow.download", false, workflow_download.isSelected());
+// Configuration.set("workflow.gather", false, workflow_gather.isSelected());
+// Configuration.set("workflow.enrich", false, workflow_enrich.isSelected());
+// Configuration.set("workflow.design", false, workflow_design.isSelected());
+// Configuration.set("workflow.create", false, workflow_create.isSelected());
+// Configuration.set("workflow.format", false, workflow_format.isSelected());
+
+// Gatherer.g_man.workflowUpdate("Download", workflow_download.isSelected());
+// Gatherer.g_man.workflowUpdate("Gather", workflow_gather.isSelected());
+// Gatherer.g_man.workflowUpdate("Enrich", workflow_enrich.isSelected());
+// Gatherer.g_man.workflowUpdate("Design", (workflow_design.isSelected() && Configuration.getMode() > Configuration.ASSISTANT_MODE));
+// Gatherer.g_man.workflowUpdate("Create", workflow_create.isSelected());
+// Gatherer.g_man.workflowUpdate("Format", workflow_format.isSelected());
+
+ // Always save configuration changes immediately (in case the GLI crashes)
+ Configuration.save();
+ if (Gatherer.isGsdlRemote && Gatherer.GS3 && site_changed ){
+ Gatherer.remoteGreenstoneServer.downloadCollectionConfigurations();
+ }
+ // Refresh the GLI to account for the configuration changes
+ Gatherer.refresh(Gatherer.PREFERENCES_CHANGED);
+
+ // If proxy is on but proxy details are incomplete, then can't continue
+ if (use_proxy_checkbox.isSelected() && proxy_host_field.getText().equals("")) {
+ JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("Preferences.Connection.Proxy_Host_Missing"),
+ Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
+ } else {
+ // Hide dialog
+ if (close) {
+ self.dispose();
+ }
+ }
+
+ // collect directory change
+ String newCollectPath = collect_dir_field.getText();
+ if(!newCollectPath.endsWith(File.separator)) {
+ newCollectPath += File.separator;
+ }
+ // wait cursor will display while changing the collect directory, need to work out what component to
+ // display it on: main GLI frame or Preferences window, depending on if OK or Apply was pressed
+ Container container = close ? Gatherer.g_man.getContentPane() : Preferences.this.getContentPane();
+ Gatherer.collectDirectoryHasChanged(Gatherer.getCollectDirectoryPath(),
+ newCollectPath, container);
+ // will tell the server that the collect directory has changed and that
+ // the workspace needs to be refreshed (Documents in Greenstone Collections)
+
+ if (restart_required) {
+ if (keep_collection_open) {
+ Gatherer.g_man.exitNoCollectionSave(Gatherer.EXIT_THEN_RESTART);
+ } else {
+ Gatherer.g_man.exit(Gatherer.EXIT_THEN_RESTART);
+ }
+ }
+ }
+ }
+
+ private class CancelButtonListener
+ implements ActionListener {
+ public void actionPerformed(ActionEvent event) {
+ self.dispose();
+ }
+ }
+
+ private class DictionaryEntry
+ implements Comparable {
+ private Locale locale;
+ private String description;
+ public DictionaryEntry(Locale locale) {
+ this.description = null;
+ this.locale = locale;
+ }
+ public DictionaryEntry(String description, Locale locale) {
+ this.description = description;
+ this.locale = locale;
+ }
+ public int compareTo(Object object) {
+ return toString().compareTo(object.toString());
+ }
+ public boolean equals(Object object) {
+ return toString().equals(object.toString());
+ }
+ public Locale getLocale() {
+ return locale;
+ }
+ public String toString() {
+ if(description != null) {
+ return description;
+ }
+ else {
+ return locale.getDisplayName();
+ }
+ }
+ }
+
+ /** This listener updates the mode description depending on what mode is selected. */
+ private class ModeRadioButtonListener
+ implements ActionListener {
+ public void actionPerformed(ActionEvent event) {
+ Object source = event.getSource();
+ if(source == assistant_mode_radio_button) {
+ mode_description_textarea.setText(Dictionary.get("Preferences.Mode.Assistant_Description"));
+ }
+ else if(source == expert_mode_radio_button) {
+ mode_description_textarea.setText(Dictionary.get("Preferences.Mode.Expert_Description"));
+ }
+ else {
+ mode_description_textarea.setText(Dictionary.get("Preferences.Mode.Librarian_Description"));
+ }
+ source = null;
+ }
+ }
+
+ private class PredefinedActionListener
+ implements ActionListener {
+ public void actionPerformed(ActionEvent event) {
+ JComboBox cb = (JComboBox) event.getSource();
+ WorkflowElementWrapper element = (WorkflowElementWrapper) cb.getSelectedItem();
+ CheckboxUpdater task = new CheckboxUpdater(element);
+ SwingUtilities.invokeLater(task);
+ }
+
+ private class CheckboxUpdater
+ implements Runnable {
+ private WorkflowElementWrapper element;
+ public CheckboxUpdater(WorkflowElementWrapper element) {
+ this.element = element;
+ }
+ public void run() {
+ workflow_download.setSelected(element.getEnabled("download"));
+ workflow_gather.setSelected(element.getEnabled("gather"));
+ workflow_enrich.setSelected(element.getEnabled("enrich"));
+ workflow_design.setSelected(element.getEnabled("design"));
+ workflow_create.setSelected(element.getEnabled("create"));
+ workflow_format.setSelected(element.getEnabled("format"));
+ }
+ }
+ }
+
+
+ private class SiteComboboxListener
+ implements ActionListener {
+ private boolean ignore_event=false;
+ public void actionPerformed(ActionEvent event) {
+ System.err.println("event occurred "+event.paramString());
+ String site = (String) site_combobox.getSelectedItem();
+ System.err.println("The site changed to = "+site);
+ if (!site.equals(current_site_selection)) {
+ current_site_selection = site;
+ System.err.println("changed the current selection");
+
+ ArrayList servlet_options = Gatherer.servlet_config.getServletsForSite(current_site_selection);
+ if (servlet_options == null) {
+ ///ystem.err.println("no servlets for this site");
+ servlet_combobox.setModel(new DefaultComboBoxModel());
+ servlet_combobox.setToolTipText(Dictionary.get("Preferences.Connection.Servlet_Tooltip2"));
+ servlet_combobox.setEnabled(false);
+
+ } else {
+ ///ystem.err.println(ArrayTools.objectArrayToString(servlet_options.toArray()));
+ servlet_combobox.setModel(new DefaultComboBoxModel(servlet_options.toArray()));
+ servlet_combobox.setToolTipText(Dictionary.get("Preferences.Connection.Servlet_Tooltip"));
+ servlet_combobox.setEnabled(true);
+ }
+ if (Gatherer.isGsdlRemote){
+ // Close the current collection, remove the lock on this file, ask to login to the new site, then download collection configurations of the site.
+ if (Gatherer.c_man.getCollection()!=null){
+ File lock_file = new File(Gatherer.c_man.getLoadedCollectionDirectoryPath() + "gli.lck");
+ Gatherer.remoteGreenstoneServer.deleteCollectionFile(Gatherer.c_man.getLoadedCollectionName(),lock_file);
+ Gatherer.g_man.closeCurrentCollection();
+ }
+ Configuration.site_name=site;
+ Gatherer.remoteGreenstoneServer.set_remote_greenstone_server_authentication_to_null();
+ Gatherer.remoteGreenstoneServer.downloadCollectionConfigurations();
+ }
+ }
+ }
+ }
+
+ private class UseProxyListener
+ implements ActionListener {
+ public void actionPerformed(ActionEvent event) {
+ boolean enabled = use_proxy_checkbox.isSelected();
+ Configuration.set("general.use_proxy", true, enabled);
+ // Fortunately this is already driven by the event thread.
+ proxy_host_field.setEnabled(enabled);
+ proxy_port_field.setEnabled(enabled);
+ }
+ }
+
+ private class WorkflowElementWrapper {
+ private Element element;
+ private String text;
+ public WorkflowElementWrapper(Element element) {
+ this.element = element;
+ }
+ public boolean getEnabled(String name) {
+ boolean result = true;
+ if(element.getAttribute(name).equalsIgnoreCase("false")) {
+ result = false;
+ }
+ return result;
+ }
+ public String toString() {
+ if (text == null) {
+ text = Dictionary.get(element.getFirstChild().getNodeValue());
+ }
+ return text;
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/PreviewButton.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/PreviewButton.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/PreviewButton.java (revision 31635)
@@ -0,0 +1,173 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.collection.CollectionManager;
+import org.greenstone.gatherer.greenstone.LocalLibraryServer;
+import org.greenstone.gatherer.greenstone3.ServletConfiguration;
+import org.greenstone.gatherer.util.StaticStrings;
+
+
+public class PreviewButton
+ extends GLIButton {
+
+
+ static final public int HOME_PAGE = 1;
+ static final public int SEARCH_PAGE = 2;
+ static final public int CLASSIFIER_PAGE = 3;
+ static final public int DOCUMENT_PAGE = 4;
+
+ private String preview_address;
+ private int page_type = HOME_PAGE;
+ private boolean variable_preview = false;
+
+ private PreviewButtonOwner owner = null;
+ public PreviewButton() {
+ super();
+ PreviewButtonListener pbl = new PreviewButtonListener();
+ addActionListener(pbl);
+ }
+ public PreviewButton(String text, String tooltip) {
+ super(text, tooltip);
+ PreviewButtonListener pbl = new PreviewButtonListener();
+ addActionListener(pbl);
+ }
+
+ public void setOwner(PreviewButtonOwner o) {
+ owner = o;
+ }
+ public void setVariablePreview(boolean vp) {
+ variable_preview = vp;
+ }
+ protected void configureHomeURL() {
+ // we could be working with standalone collections or collection groups
+ // so getting a collection-name may have a collection group prefix
+ // This means CollectionManager.getLoadedCollectionName() needs to (and does) return (colgroup/)subcolname
+ String collGroupWithName = CollectionManager.getLoadedCollectionName(true);
+
+ // set up the home page for the current collection
+ if (Gatherer.GS3) { // GLI for GS3 case
+ if(Configuration.library_url == null) {// && !Configuration.fedora_info.isActive()) {
+ Gatherer.missingEXEC();
+ }
+ // if we now finally have a library URL, then continue with previewing
+ if(Configuration.library_url != null) {
+ // don't do anything fancy here
+ preview_address = Configuration.library_url.toString() + Configuration.getServletPath()
+ + "?a=p&sa=about&c=" + collGroupWithName + "&l=" + Configuration.getLanguage();
+ }
+ return;
+ }
+
+ if(Gatherer.isLocalLibrary) {
+ // check that the local library server is still running - doesn't do anything if it's not supposed to be running
+ // !! Don't like this here
+ LocalLibraryServer.checkServerRunning();
+ if(LocalLibraryServer.isURLPending()) {
+ // Remind the user to press Enter Library, and return from this method
+ JOptionPane.showMessageDialog(Gatherer.g_man,
+ Dictionary.get("General.LLS_Not_Started"),
+ Dictionary.get("General.LLS_Not_Started_Title"),
+ JOptionPane.INFORMATION_MESSAGE);
+ preview_address = null;
+ return;
+ }
+ }
+ // FLI or GLI for GS2 case (no library_url)
+ if(Configuration.fedora_info.isActive()) {
+ preview_address = Configuration.library_url.toString() + "?pid=true&title=true&terms=&query=pid~greenstone:"+collGroupWithName+"*&maxResults=20";
+ //+ "&l=" + Configuration.getLanguage();
+ } else {
+ preview_address = Configuration.library_url.toString() + "?c=" + collGroupWithName + "&l=" + Configuration.getLanguage();
+ }
+
+ String main_args = "&a=p&p=about";
+ /* this code is for switching the preview page depending on what
+ is currently active. but it is not complete yet
+ String main_args = null;
+ String params = null;
+ if (!variable_preview || owner == null) {
+ main_args = "&a=p&p=about";
+ } else {
+ page_type = owner.getPageType();
+ params = owner.getPageParams();
+
+ switch (page_type) {
+ case SEARCH_PAGE:
+ main_args = "&a=q&q="+params;
+ break;
+ case CLASSIFIER_PAGE:
+ main_args = "&a=d&cl="+params;
+ break;
+ case DOCUMENT_PAGE:
+ main_args = "&a=d&d="+params;
+ break;
+ case HOME_PAGE:
+ default:
+ main_args = "&a=p&p=about";
+
+
+ }
+
+
+ }
+ */
+ preview_address += main_args+StaticStrings.TIMESTAMP_ARGUMENT + System.currentTimeMillis();
+
+ }
+ private class PreviewButtonListener
+ implements ActionListener {
+
+ public void actionPerformed(ActionEvent event) {
+ Gatherer.c_man.saveCollection();
+
+ configureHomeURL();
+
+ if (Gatherer.GS3) {
+ // we need to rerun the convert in case the format stuff has changed
+ //for gs3, we don't need this anymore: Gatherer.c_man.convertToGS3Collection();
+ Gatherer.configGS3Server(Configuration.site_name, ServletConfiguration.ADD_COMMAND + Gatherer.c_man.getLoadedCollectionName());
+ }
+ if(preview_address != null) {
+ Gatherer.spawnBrowser(preview_address);
+ }
+
+ }
+
+ }
+
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/PreviewButtonOwner.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/PreviewButtonOwner.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/PreviewButtonOwner.java (revision 31635)
@@ -0,0 +1,40 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+public interface PreviewButtonOwner {
+
+ public int getPageType();
+
+ public String getPageParams();
+
+
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/PreviewCommandDialog.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/PreviewCommandDialog.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/PreviewCommandDialog.java (revision 31635)
@@ -0,0 +1,268 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.File;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.filechooser.*;
+import javax.swing.table.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.util.Utility;
+
+/** This dialog allows the user to choose which browser to use for the preview command
+ * @author Katherine Don, Greenstone Digital Library, University of Waikato
+ * @version 2.3
+ */
+public class PreviewCommandDialog
+ extends ModalDialog {
+
+ /** The default size for the dialog. */
+ static final private Dimension SIZE = new Dimension(500, 195);
+
+ private PreviewCommandDialog self;
+ private JButton browse_button;
+ private JButton cancel_button;
+ private JButton ok_button;
+ private JTextField command_field;
+ private String preview_command=null;
+ /** Create a new PreviewCommandDialog
+ */
+ public PreviewCommandDialog() {
+ super(Gatherer.g_man);
+ this.self = this;
+ JScrollPane scrol_tmp;
+ this.setComponentOrientation(Dictionary.getOrientation());
+ // Creation
+ setModal(true);
+ setSize(SIZE);
+ setJMenuBar(new SimpleMenuBar("thepreviewview"));
+ setTitle(Dictionary.get("PreviewCommandDialog.Title"));
+
+ JPanel content_pane = (JPanel) getContentPane();
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+ content_pane.setBackground(Configuration.getColor("coloring.collection_heading_background", false));
+
+ JTextArea instructions_area = new JTextArea(Dictionary.get("PreviewCommandDialog.Instructions"));
+ instructions_area.setComponentOrientation(Dictionary.getOrientation());
+ instructions_area.setEditable(false);
+ instructions_area.setLineWrap(true);
+ instructions_area.setRows(5);
+ instructions_area.setWrapStyleWord(true);
+
+ JPanel button_pane = new JPanel();
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+ JPanel lower_pane = new JPanel();
+ lower_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel command_pane = new JPanel();
+ command_pane.setComponentOrientation(Dictionary.getOrientation());
+ command_field = new JTextField();
+ command_field.setComponentOrientation(Dictionary.getOrientation());
+ browse_button = new GLIButton(Dictionary.get("FileAssociationDialog.Browse"));
+ browse_button.setEnabled(!Utility.isMac());
+ if (Utility.isMac()) {
+ browse_button.setToolTipText(Dictionary.get("FileAssociationDialog.Browse_Tooltip_Mac"));
+ } else {
+ browse_button.setToolTipText(Dictionary.get("FileAssociationDialog.Browse", "FileAssociationDialog.Browse_Tooltip"));
+ }
+ ok_button = new GLIButton(Dictionary.get("General.OK"), Dictionary.get("FileAssociationDialog.Close_Tooltip"));
+ cancel_button = new GLIButton(Dictionary.get("General.Cancel"), Dictionary.get("General.Pure_Cancel_Tooltip"));
+
+ // Connection
+ browse_button.addActionListener(new BrowseButtonListener());
+ ok_button.addActionListener(new OkButtonListener());
+ cancel_button.addActionListener(new CancelButtonListener());
+
+ // Layout
+ command_pane.setBorder(BorderFactory.createEmptyBorder(2,0,2,0));
+ command_pane.setLayout(new BorderLayout());
+ command_pane.add(command_field, BorderLayout.CENTER);
+ command_pane.add(browse_button, BorderLayout.LINE_END);
+
+ lower_pane.setBorder(BorderFactory.createEmptyBorder(2,0,0,0));
+
+ button_pane.setLayout(new GridLayout(1,2,5,0));
+ button_pane.add(ok_button);
+ button_pane.add(cancel_button);
+
+ lower_pane.setBorder(BorderFactory.createEmptyBorder(2,0,0,0));
+ lower_pane.setLayout(new BorderLayout());
+ lower_pane.add(command_pane, BorderLayout.CENTER);
+ lower_pane.add(button_pane, BorderLayout.SOUTH);
+
+
+ content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ content_pane.setLayout(new BorderLayout());
+ scrol_tmp=new JScrollPane(instructions_area);
+ scrol_tmp.setComponentOrientation(Dictionary.getOrientation());
+ content_pane.add(scrol_tmp, BorderLayout.NORTH);
+ content_pane.add(lower_pane, BorderLayout.CENTER);
+
+ Rectangle screen = Gatherer.g_man.getBounds(null);
+ setLocation((int)(screen.getX() + (screen.getWidth() - SIZE.width) / 2), (int)(screen.getY() + (screen.getHeight() - SIZE.height) / 2));
+ screen = null;
+ }
+
+ public void destroy() {
+ // Disconnect
+ // Clean up
+ self = null;
+ }
+
+ /** Redisplay the dialog
+ */
+ public String display() {
+ setVisible(true);
+ return preview_command;
+ }
+
+
+ private class OkButtonListener
+ implements ActionListener {
+
+ public void actionPerformed(ActionEvent event) {
+ preview_command = command_field.getText();
+ self.dispose();
+
+ }
+ }
+
+
+ /** Whenever the user clicks the browse button, we should open up a file browser to allow them to select an executable file from somewhere in the file system. */
+ private class BrowseButtonListener
+ implements ActionListener {
+ /** Open up a simple JFileChooser when the user clicks the button.
+ * @param event An ActionEvent containing information about the event.
+ */
+ public void actionPerformed(ActionEvent event) {
+ JFileChooser chooser = new JFileChooser(new File(Gatherer.getGLIUserDirectoryPath()));
+ chooser.setComponentOrientation(Dictionary.getOrientation());
+ GUIUtils.disableRename(chooser);
+ chooser.setDialogTitle(Dictionary.get("FileAssociationDialog.Browse_Title"));
+ chooser.setFileFilter(new BatchFileFilter());
+ chooser.setFileFilter(new CoreObjectModelFileFilter());
+ chooser.setFileFilter(new ExecutableFileFilter());
+ chooser.setAcceptAllFileFilterUsed(true);
+ if(chooser.showOpenDialog(Gatherer.g_man) == JFileChooser.APPROVE_OPTION) {
+ command_field.setText(chooser.getSelectedFile().getAbsolutePath());
+ }
+ }
+ }
+
+
+ private class CancelButtonListener
+ implements ActionListener {
+ public void actionPerformed(ActionEvent event) {
+ self.dispose();
+ }
+ }
+
+ /** Batch filter shows only files ending in bat. Based on ImageFilter.java which is a 1.4 example used by FileChooserDemo2.java. */
+ private class BatchFileFilter
+ extends FileFilter {
+
+ /** Accept all .exe files
+ * @param file a File
+ * @return true is this file should be shown, false otherwise
+ */
+ public boolean accept(File file) {
+ return file.getName().toLowerCase().endsWith(".bat");
+ }
+
+ /** The description of this filter
+ * @return a String
+ */
+ public String getDescription() {
+ return Dictionary.get("FileAssociationDialog.Batch_File");
+ }
+ }
+
+ /** Command filter shows only files ending in com. Based on ImageFilter.java which is a 1.4 example used by FileChooserDemo2.java. */
+ private class CoreObjectModelFileFilter
+ extends FileFilter {
+
+ /** Accept all .com files
+ * @param file a File
+ * @return true is this file should be shown, false otherwise
+ */
+ public boolean accept(File file) {
+ return file.getName().toLowerCase().endsWith(".com");
+ }
+
+ /** The description of this filter
+ * @return a String
+ */
+ public String getDescription() {
+ return Dictionary.get("FileAssociationDialog.Command_File");
+ }
+ }
+
+
+ /** Executable filter shows only files ending in exe. Based on ImageFilter.java which is a 1.4 example used by FileChooserDemo2.java. */
+ private class ExecutableFileFilter
+ extends FileFilter {
+
+ /** Accept all .exe files
+ * @param file a File
+ * @return true is this file should be shown, false otherwise
+ */
+ public boolean accept(File file) {
+ return file.getName().toLowerCase().endsWith(".exe");
+ }
+
+ /** The description of this filter
+ * @return a String
+ */
+ public String getDescription() {
+ return Dictionary.get("FileAssociationDialog.Executable_File");
+ }
+ }
+
+
+}
+
+
+
+
+
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/RenamePrompt.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/RenamePrompt.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/RenamePrompt.java (revision 31635)
@@ -0,0 +1,132 @@
+/**
+ *############################################################################
+ * A component of the Greenstone Librarian Interface, part of the Greenstone
+ * digital library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
+ *
+ * Copyright (C) 2006 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import javax.swing.*;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.collection.CollectionTreeNode;
+
+
+public class RenamePrompt
+ extends JDialog
+ implements ActionListener, KeyListener
+{
+ private JButton cancel_button;
+ private JButton ok_button;
+ private JTextField name_textfield;
+ private String name;
+
+ static final private Dimension SIZE = new Dimension(350, 100);
+
+
+ public RenamePrompt(CollectionTreeNode collection_tree_node)
+ {
+ super(Gatherer.g_man, true);
+ this.setComponentOrientation(Dictionary.getOrientation());
+ setTitle(Dictionary.get("RenamePrompt.Title"));
+ name = collection_tree_node.getFile().getName();
+ }
+
+
+ public void actionPerformed(ActionEvent event)
+ {
+ if (event.getSource() == ok_button) {
+ name = name_textfield.getText();
+ }
+ else if (event.getSource() == cancel_button) {
+ name = null;
+ }
+ dispose();
+ }
+
+ public void keyPressed(KeyEvent e) {
+ if (e.getKeyCode() == KeyEvent.VK_ENTER) {
+ // Enter: Click the button in focus
+ Object source = e.getSource();
+ if (source instanceof AbstractButton) {
+ ((AbstractButton) source).doClick();
+ } else if (source == name_textfield) {
+ ok_button.doClick();
+ }
+ }
+ }
+
+ public void keyReleased(KeyEvent e) { }
+
+ public void keyTyped(KeyEvent e) { }
+
+
+ public String display()
+ {
+ setSize(SIZE);
+ JPanel content_pane = (JPanel) getContentPane();
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel info_pane = new JPanel();
+ info_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JLabel name_label = new JLabel(Dictionary.get("RenamePrompt.Name"));
+ name_label.setComponentOrientation(Dictionary.getOrientation());
+ name_textfield = new JTextField(name);
+ name_textfield.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel button_pane = new JPanel();
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+ ok_button = new GLIButton(Dictionary.get("General.OK"), Dictionary.get("General.OK_Tooltip"));
+ cancel_button = new GLIButton(Dictionary.get("General.Cancel"), Dictionary.get("General.Pure_Cancel_Tooltip"));
+
+ // Connection
+ cancel_button.addActionListener(this);
+ ok_button.addActionListener(this);
+ ok_button.addKeyListener(this);
+ name_textfield.addKeyListener(this);
+
+ // Layout
+ info_pane.setLayout(new BorderLayout(5,0));
+ info_pane.add(name_label, BorderLayout.LINE_START);
+ info_pane.add(name_textfield, BorderLayout.CENTER);
+
+ button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
+ button_pane.setLayout(new GridLayout(1,2,0,5));
+ button_pane.add(ok_button);
+ button_pane.add(cancel_button);
+
+ content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ content_pane.setLayout(new BorderLayout());
+ content_pane.add(info_pane, BorderLayout.CENTER);
+ content_pane.add(button_pane, BorderLayout.SOUTH);
+
+ // Display
+ Rectangle frame_bounds = Gatherer.g_man.getBounds();
+ setLocation(frame_bounds.x + (frame_bounds.width - SIZE.width) / 2, frame_bounds.y + (frame_bounds.height - SIZE.height) / 2);
+ setVisible(true);
+ return name;
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/ReplaceSrcDocWithHtmlPrompt.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/ReplaceSrcDocWithHtmlPrompt.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/ReplaceSrcDocWithHtmlPrompt.java (revision 31635)
@@ -0,0 +1,425 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Anupama Krishnan, Greenstone Digital Library, University of Waikato
+ * Code based entirely on Katherine Don's ExplodeMetadataDatabasePrompt.java
+ * (part of the Greenstone Digital Library)
+ *
+ * Copyright (C) 2008 Greenstone Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.text.*;
+import java.io.*;
+import java.util.ArrayList;
+
+import org.w3c.dom.Document;
+
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.cdm.Argument;
+import org.greenstone.gatherer.cdm.ArgumentControl;
+import org.greenstone.gatherer.cdm.CollectionDesignManager;
+import org.greenstone.gatherer.cdm.Plugin;
+import org.greenstone.gatherer.collection.CollectionManager;
+import org.greenstone.gatherer.collection.ScriptOptions;
+import org.greenstone.gatherer.collection.Collection;
+import org.greenstone.gatherer.feedback.Base64;
+import org.greenstone.gatherer.greenstone.LocalGreenstone;
+import org.greenstone.gatherer.gui.tree.DragTree;
+import org.greenstone.gatherer.remote.RemoteGreenstoneServer;
+import org.greenstone.gatherer.shell.GShell;
+import org.greenstone.gatherer.shell.GShellEvent;
+import org.greenstone.gatherer.shell.GShellListener;
+import org.greenstone.gatherer.util.Utility;
+import org.greenstone.gatherer.util.XMLTools;
+
+public class ReplaceSrcDocWithHtmlPrompt
+ extends ModalDialog
+ implements GShellListener
+{
+ /** The size of this new collection dialog box. */
+ static private Dimension SIZE = new Dimension(675, 250);
+ private JDialog self;
+
+ /** the source document files we wil be replacing with their GS3-generated html equivalent */
+ private File[] srcdoc_files = null;
+ /** the names of the srcdoc_files that could not be replaced with their GS3-generated html equivalent */
+ private java.util.Vector failedFiles = null;
+
+ /** the list of potential plugins to be used */
+ private Argument plugin_arg = null;
+ /** holds all the available options for the src-replacing script */
+ private ScriptOptions options = null;
+ /** the pane containing the options */
+ private JPanel options_pane = null;
+ /** the error message if any */
+ private StringBuffer error_message = null;
+ /** whether we were successful or not */
+ private boolean successful;
+ /** The command output of a successful execution of the replace script
+ * containing the generated tailname of the replacement file */
+ private String successfulOutput;
+
+ public ReplaceSrcDocWithHtmlPrompt(File[] source_files) {
+ super(Gatherer.g_man, true);
+ this.self = this;
+ this.srcdoc_files = source_files;
+ failedFiles = new java.util.Vector(source_files.length); // number of failures will never be more than num source docs
+
+ // check that we actually have a src doc file which can be replaced with its GS3-generated html
+ // since all the files here have the same extension, the first file can be used to work out
+ // the necessary plugin
+ ArrayList replacer_plugins = CollectionDesignManager.plugin_manager.getSrcReplacerPlugins(source_files[0]);
+ if (replacer_plugins.size() == 0) {
+ JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("ReplaceSrcWithHTMLPrompt.NotSrcReplaceable"), Dictionary.get("ReplaceSrcWithHTMLPrompt.Title"), JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ plugin_arg = createPluginArgument(replacer_plugins);
+ setJMenuBar(new SimpleMenuBar("replacingSourceDoc"));
+ setSize(SIZE);
+ setTitle(Dictionary.get("ReplaceSrcWithHTMLPrompt.Title"));
+
+ // set up the script options
+ // we have empty initial values
+ String dom_string = "\n ";
+ Document doc = XMLTools.parseXML(new StringReader(dom_string));
+ options = new ScriptOptions(doc.getDocumentElement(), "replace_srcdoc_with_html.pl");
+
+ // Creation
+ JPanel content_pane = (JPanel) getContentPane();
+ content_pane.setOpaque(true);
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+ options_pane = new JPanel();
+
+ addScriptOptions(options_pane);
+ JScrollPane middle_pane = new JScrollPane(options_pane);
+ middle_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JTextArea instructions_area = new JTextArea(Dictionary.get("ReplaceSrcWithHTMLPrompt.Instructions"));
+ instructions_area.setComponentOrientation(Dictionary.getOrientation());
+ instructions_area.setEditable(false);
+ instructions_area.setLineWrap(true);
+ instructions_area.setRows(5);
+ instructions_area.setWrapStyleWord(true);
+
+ JPanel button_pane = new JPanel();
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+ JButton srcreplace_button = new GLIButton(Dictionary.get("ReplaceSrcWithHTMLPrompt.ReplaceSrc"), Dictionary.get("ReplaceSrcWithHTMLPrompt.ReplaceSrc_Tooltip"));
+ JButton cancel_button = new GLIButton(Dictionary.get("General.Cancel"), Dictionary.get("General.Cancel_Tooltip"));
+
+ // Connection
+ cancel_button.addActionListener(new CancelListener());
+ srcreplace_button.addActionListener(new SrcReplaceListener());
+
+ // Layout
+
+ button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
+ button_pane.setLayout(new GridLayout(1,2));
+ button_pane.add(srcreplace_button);
+ button_pane.add(cancel_button);
+
+ content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ content_pane.setLayout(new BorderLayout());
+ content_pane.add(instructions_area, BorderLayout.NORTH);
+ content_pane.add(middle_pane, BorderLayout.CENTER);
+ content_pane.add(button_pane, BorderLayout.SOUTH);
+
+ // Final dialog setup & positioning.
+ Dimension screen_size = Configuration.screen_size;
+ setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
+ setVisible(true);
+ }
+
+ public void destroy()
+ {
+
+ }
+
+ /** All implementation of GShellListener must include this method so the listener can be informed of messages from the GShell.
+ * @param event A GShellEvent that contains, amoung other things, the message.
+ */
+ public synchronized void message(GShellEvent event) {
+ String message = event.getMessage();
+ if (message.startsWith("replace_srcdoc_with_html.pl>")) {
+ message = message.substring(28); // length of "replace_srcdoc_with_html.pl>"?
+ error_message.append(message);
+ error_message.append("\n");
+ }
+ }
+
+ /** All implementation of GShellListener must include this method so the listener can be informed when a GShell begins its task. Implementation side-effect, not actually used.
+ * @param event A GShellEvent that contains details of the initial state of the GShell before task comencement.
+ */
+ public synchronized void processBegun(GShellEvent event) {
+ // We don't care.
+ }
+
+ /** All implementation of GShellListener must include this method so the listener can be informed when a GShell completes its task.
+ * @param event A GShellEvent that contains details of the final state of the GShell after task completion.
+ */
+ public synchronized void processComplete(GShellEvent event) {
+ successful = false;
+ if(event.getStatus() == GShell.OK) {
+ successful = true;
+ GShell process = (GShell)event.getSource();
+ successfulOutput = process.getCommandOutput();
+ process.resetCommandOutput();
+ }
+ }
+
+ private Argument createPluginArgument(ArrayList plugin_list) {
+ Argument arg = new Argument();
+ arg.setName("plugin");
+ arg.setDescription(Dictionary.get("ReplaceSrcWithHTMLPrompt.Plugin"));
+ arg.setRequired(true);
+ arg.setType(Argument.ENUM);
+ for (int i=0; i 0 ? failedFiles.get(0).toString() : "";
+ for(int i = 1; i < failedFiles.size(); i++) {
+ failedFilenames = failedFilenames + ", " + failedFiles.get(i);
+ }
+
+ String label = Dictionary.get("ReplaceSrcWithHTMLPrompt.Failed_ReplaceSrc", failedFilenames);//srcdoc_file.getName());
+ SimpleResultDialog result_dialog = new SimpleResultDialog(title, label, message);
+ result_dialog.setVisible(true); // Blocks
+ result_dialog.dispose();
+ result_dialog = null;
+ }
+ }
+
+ private class CancelListener
+ implements ActionListener {
+ public void actionPerformed(ActionEvent event) {
+ self.dispose();
+ }
+ }
+
+ private class SrcReplaceListener
+ implements ActionListener {
+
+ public void actionPerformed(ActionEvent event) {
+ Gatherer.g_man.wait(true);
+ // update the options
+ updateScriptOptions();
+ self.dispose();
+ (new ReplaceSrcDocWithHtmlTask()).start();
+ }
+ }
+
+
+ private class ReplaceSrcDocWithHtmlTask
+ extends Thread
+ {
+ public ReplaceSrcDocWithHtmlTask()
+ {
+ }
+
+ public void run()
+ {
+ int exit_value = 0;
+ String errMsg = "";
+
+ // We're going to try replacing all selected documents and then
+ // display errormessages
+ // first run the replace process for all src files, and accumulate error exit values (0 means it's fine)
+ for(int i = 0; i < srcdoc_files.length; i++) {
+ int exit_value_this_time = replaceSrcDoc(i);
+ exit_value += exit_value_this_time; // if all files successfully replaced, exit_value will stay at 0
+
+ // accumulate all error and success msgs to display after processing all the files
+ // that are to be replaced
+ errMsg += error_message.toString();
+ error_message = null;
+
+ if(exit_value_this_time != 0) {
+ failedFiles.add(srcdoc_files[i].getName()); // add this file to list of failures
+ } else {
+ if (Gatherer.isGsdlRemote) {
+ //System.err.println("*****ReplaceSrcDocWithHtmlPrompt.java - run() - gsdl remote case");
+
+ // Conversion may have renamed the file by URL- or base64-encoding it.
+ // The new replacement file's tailname is the last line of the script output.
+ String[] lines = successfulOutput.split("\n");
+ String suffixlessFilename = lines[lines.length-1];
+ String htmlFile = suffixlessFilename+".html";
+
+ // Delete the local copy of the old source document file on the client side
+ // (it's already been replaced on the server side), and download the updated
+ // (html) file and any directory containing associated files
+ Utility.delete(srcdoc_files[i]); // remove the local copy of src doc
+
+ // download the generated html file from the server side to put it
+ // into the import directory on the client side
+ Gatherer.remoteGreenstoneServer.downloadCollectionFile(
+ CollectionManager.getLoadedCollectionName(),
+ new File(srcdoc_files[i].getParentFile(), htmlFile));
+
+ // download any assoc folder which is srcfilename+"_files"
+ File assoc_folder = new File(srcdoc_files[i].getParentFile(), suffixlessFilename+"_files");
+
+ // If an associated_folder by such a name exists, download it
+ if(Gatherer.remoteGreenstoneServer.exists(CollectionManager.getLoadedCollectionName(), assoc_folder)) {
+ Gatherer.remoteGreenstoneServer.downloadCollectionFile(
+ CollectionManager.getLoadedCollectionName(), assoc_folder);
+ }
+ }
+ // Whether remote or local case, need to refressh the GLI filemanager system to show
+ // what files are in the current collection space
+ Gatherer.g_man.refreshCollectionTree(DragTree.COLLECTION_CONTENTS_CHANGED); // refreshes coll's content view
+ Gatherer.g_man.wait(false);
+ }
+ } // end for loop
+
+ // Display error messages now that we finished processing the multiple files
+ if(exit_value != 0) {
+ resultPrompt(false, errMsg);
+ } else {
+ resultPrompt(true, errMsg);
+ }
+ } // end run method
+
+ } //end Task class
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/SimpleMenuBar.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/SimpleMenuBar.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/SimpleMenuBar.java (revision 31635)
@@ -0,0 +1,36 @@
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.util.JarTools;
+
+public class SimpleMenuBar
+ extends JMenuBar
+ implements ActionListener {
+
+ private String page_name;
+
+ public SimpleMenuBar(String page_name) {
+ this.page_name = page_name;
+ this.setComponentOrientation(Dictionary.getOrientation());
+ JMenu help = new JMenu();
+ help.setComponentOrientation(Dictionary.getOrientation());
+ help.setIcon(JarTools.getImage("help.gif"));
+ help.setText(Dictionary.get("Menu.Help"));
+
+ JMenuItem help_help = new JMenuItem(Dictionary.get("Menu.Help"));
+ help_help.setComponentOrientation(Dictionary.getOrientation());
+ help_help.addActionListener(this);
+
+ help.add(help_help);
+
+ add(Box.createHorizontalGlue());
+ add(help);
+ }
+
+ public void actionPerformed(ActionEvent event) {
+ HelpFrame.setView(page_name);
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/SimpleResultDialog.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/SimpleResultDialog.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/SimpleResultDialog.java (revision 31635)
@@ -0,0 +1,125 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.util.ArrayList;
+import javax.swing.*;
+import javax.swing.event.*;
+
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.Dictionary;
+
+public class SimpleResultDialog
+ extends ModalDialog {
+
+ private Dimension size = new Dimension(600,300);
+ private SimpleResultDialog self;
+
+ public SimpleResultDialog(String title, String label, String results) {
+ super(Gatherer.g_man, title, true);
+ this.self = this;
+ this.setComponentOrientation(Dictionary.getOrientation());
+
+ setUpGUI(label, results);
+ }
+
+ /** If you are calling this from another dialog, and you want that dialog blocked while this is open, need to use this method, passing in the calling dialog as the parent */
+ public SimpleResultDialog(Dialog parent, String title, String label, String results) {
+
+ super(parent, title, true);
+ this.self = this;
+
+ setUpGUI(label, results);
+ }
+
+ private void setUpGUI(String label, String results) {
+ setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
+ setSize(size);
+
+ JPanel content_pane = (JPanel) getContentPane();
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+ JLabel result_label = new JLabel(label);
+ result_label.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel button_pane = new JPanel();
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+ JButton close_button = new GLIButton(Dictionary.get("General.Close"));
+ close_button.addActionListener(new CloseButtonListener());
+
+ JPanel output_pane = new JPanel();
+ output_pane.setComponentOrientation(Dictionary.getOrientation());
+ JLabel output_label = new JLabel(Dictionary.get("General.Review_Output"));
+ output_label.setComponentOrientation(Dictionary.getOrientation());
+
+ JTextArea output_textarea = new JTextArea(results);
+ output_textarea.setComponentOrientation(Dictionary.getOrientation());
+ output_textarea.setCaretPosition(0);
+ output_textarea.setEditable(false);
+ output_textarea.setLineWrap(true);
+ output_textarea.setWrapStyleWord(true);
+
+ button_pane.setLayout(new BorderLayout());
+ button_pane.add(close_button, BorderLayout.LINE_END);
+
+ output_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
+ output_pane.setLayout(new BorderLayout());
+ output_pane.add(output_label, BorderLayout.NORTH);
+ output_pane.add(new JScrollPane(output_textarea), BorderLayout.CENTER);
+
+ content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ content_pane.setLayout(new BorderLayout());
+ content_pane.add(result_label, BorderLayout.NORTH);
+ content_pane.add(output_pane, BorderLayout.CENTER);
+ content_pane.add(button_pane, BorderLayout.SOUTH);
+
+ // Position
+ Rectangle frame_bounds = Gatherer.g_man.getBounds();
+ setLocation(frame_bounds.x + (frame_bounds.width - size.width) / 2, frame_bounds.y + (frame_bounds.height - size.height) / 2);
+ //new SimpleResultDialog(Gatherer.g_man, title, label, results);
+ }
+
+
+ private class CloseButtonListener
+ implements ActionListener {
+ public void actionPerformed(ActionEvent event) {
+ self.setVisible(false);
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/URLField.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/URLField.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/URLField.java (revision 31635)
@@ -0,0 +1,316 @@
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.net.*;
+import javax.swing.*;
+import java.util.*;
+import java.io.*;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.Configuration;
+
+/** Modified.
+ * Class used by WarningDialog and Gatherer which represents a textual input control
+ * (TextField or Combobox) into which a URL can be entered. Provides some static methods
+ * shared by these controls and by a general JTextField control which can all be used
+ * in a WarningDialog. Class URLField also contains an inner Interface class and two
+ * static inner classes that implement this interface: Text and DropDown.
+ * Finally, another static inner class URLCollectionPair associates each Gliserver URL
+ * (or Library URL) that can be displayed in the DropDown with the last opened collection
+ * file for the Greenstone server at that URL.
+*/
+public class URLField
+{
+ static public String getText(JComponent control)
+ {
+ if(control instanceof JTextField) { // not specifically a URL Field, therefore, we're not meant to do checking
+ return ((JTextField)control).getText();
+ }
+ else if(control instanceof URLField.DropDown) {
+ return (String)((JComboBox)control).getSelectedItem();
+ }
+
+ // unknown control, shouldn't happen
+ return "";
+ }
+
+ static public String setText(JComponent control, String text)
+ {
+ if(control instanceof JTextField) { // not specifically a URL Field, therefore, we're not meant to do checking
+ ((JTextField)control).setText(text);
+ }
+ else if(control instanceof URLField.DropDown) {
+ ((JComboBox)control).setSelectedItem(text);
+ }
+
+ // unknown control, shouldn't happen
+ return "";
+ }
+
+ static public boolean validateURL(JComponent control)
+ {
+ if(control instanceof URLField.Text) {
+ return ((URLField.Text)control).validateURL();
+ }
+ else if (control instanceof JTextField) {
+ return true; // not specifically a URL Field, therefore, we're not meant to do checking
+ }
+ else { // control is a DropDown
+ return ((URLField.DropDown)control).validateURL();
+ }
+ }
+
+ static public boolean validateURL(String url_string)
+ {
+ if (!url_string.equals("")) {
+ // Check the URL string is valid by trying to create a URL object from it
+ try {
+ new URL(url_string);
+ }
+ catch (MalformedURLException exception) {
+ // URL string is invalid
+ return false;
+ }
+ }
+
+ // URL string is valid
+ return true;
+ }
+
+ static public void store(JComponent control, String affected_property) {
+ if(control instanceof JTextField) {
+ String value = ((JTextField)control).getText();
+ Configuration.setString(affected_property, true, value);
+ Configuration.save(); // save it in case of a crash
+ }
+ else { // DropDown
+ ((DropDown)control).saveProperties(); // saves it already
+ }
+ }
+
+
+ /** INTERFACE CLASS IMPLEMENTED BY Static inner classes Text and DropDown */
+ public static interface URLFieldControl {
+ public boolean validateURL();
+ }
+
+
+ /** STATIC INNER CLASS URLTextField */
+ public static class Text
+ extends JTextField implements URLFieldControl
+ {
+ public Text(Color foreground, Color background)
+ {
+ super();
+ this.setComponentOrientation(Dictionary.getOrientation());
+ setBackground(background);
+ setForeground(foreground);
+ }
+
+ public boolean validateURL() {
+ String url_string = getText();
+ return URLField.validateURL(url_string);
+ }
+ }
+
+
+ /** STATIC INNER CLASS DropDown, an editable URLComboBox */
+ public static class DropDown
+ extends JComboBox implements URLFieldControl
+ {
+ private String checkString = null; // optional string to check the URLs items against to see whether the URLs contain this
+ private String affectedProperty = null; // the Configuration property associated with this DropDown
+ private String coaffectedProperty = null; // the Configuration property whose suffix should change along with the affectedProperty
+
+ private static final int MAX_URLS = 5;
+
+ public DropDown(Color foreground, Color background, String[] defaultURLs,
+ String affectedProperty, String coaffectedProperty)
+ {
+ super(URLCollectionPair.createDefaultsArray(defaultURLs));
+ this.setEditable(true);
+ this.getEditor().selectAll();
+ this.setComponentOrientation(Dictionary.getOrientation());
+
+ setBackground(background);
+ setForeground(foreground);
+
+ this.affectedProperty = affectedProperty;
+ this.coaffectedProperty = coaffectedProperty;
+ // read any URLS specified in the config file
+ setComboBoxValues();
+
+ }
+
+ public DropDown(Color foreground, Color background, String[] defaultURLs,
+ String affectedProperty, String coaffectedProperty, String checkString)
+ {
+ this(foreground, background, defaultURLs, affectedProperty, coaffectedProperty);
+ this.checkString = checkString;
+ }
+
+ public boolean validateURL() {
+ //String url_string = (String)this.getEditor().getItem();
+ String url_string = this.getSelectedItem().toString(); // returns url of the combobox item
+
+ if(checkString != null && !url_string.endsWith(checkString)) {
+ return false;
+ } else {
+ return URLField.validateURL(url_string);
+ }
+ }
+
+ public void saveProperties() {
+ // 1. Add the first url just edited (if any)
+
+ // The user might have created a new STRING, or selected an existing URLCollectionPair
+ // We don't know the type!
+ Object o = this.getSelectedItem();
+ URLCollectionPair editedItem = (o instanceof URLCollectionPair)
+ ? (URLCollectionPair)o : new URLCollectionPair(o.toString());
+
+ if(editedItem != null) {
+ insertItemAt(editedItem, 0);
+ } else {
+ editedItem = (URLCollectionPair)getItemAt(0); // else reuse the first default:
+ }
+ Configuration.setString(affectedProperty, true, editedItem.getURL());
+
+ // concurrently set the last opened collection for this url
+ String lastOpenedCollection = editedItem.getCollection();
+ if(lastOpenedCollection.equals("")) {
+ Configuration.setString(coaffectedProperty, true, null);
+ } else {
+ Configuration.setString(coaffectedProperty, true, lastOpenedCollection);
+ }
+
+
+ // 2. Add the remaining urls into the combobox
+ int url_count = 1; // remain under MAX_URLS AND under the number of actual URLs in the dropdown
+ for(int i = 0; url_count < MAX_URLS && i < getItemCount(); i++, url_count++) {
+
+ URLCollectionPair item = (URLCollectionPair)getItemAt(i);
+ String url = item.getURL();
+
+ if(item == null || url.equals(editedItem.getURL()) || url.equals("")) {
+ // skip any duplicates of the just-edited URL and skip empty URLs
+ url_count--;
+ }
+ else {
+ Configuration.setString(affectedProperty+url_count, true, url);
+
+ // concurrently save the name of the corresponding open collection
+ if(item.getCollection().equals("")) {
+ Configuration.setString(coaffectedProperty+url_count, true, null);
+ } else {
+ Configuration.setString(coaffectedProperty+url_count, true, item.getCollection());
+ }
+
+ } // else retain old value for this affectedProperty (general.gliserver_url)
+ }
+ Configuration.save();
+ }
+
+
+ // Get values from Configuration file, or (if none) use defaultURLs.
+ // Put them into the combobox
+ private void setComboBoxValues() {
+
+ String url = Configuration.getString(affectedProperty, true);
+ String collection = Configuration.getString(coaffectedProperty, true);
+ boolean finished = url.equals("");
+ if(!finished) {
+ this.removeAllItems(); // remove defaults, since config file now contains init values
+ this.addItem(new URLCollectionPair(url, collection));
+ } // else urls and combobox already contains the defaults
+
+
+ for(int i = 1; !finished; i++) {
+ url = Configuration.getString(affectedProperty+i, true);
+ collection = Configuration.getString(coaffectedProperty+i, true);
+ if(url.equals("")) {
+ finished = true;
+ }
+ else { // url is not empty
+ this.addItem(new URLCollectionPair(url, collection));
+ }
+ }
+
+ // setting the size of the dropdown control
+ Dimension newSize=new Dimension(getPreferredSize().width+20, getPreferredSize().height);
+ setPreferredSize(newSize);
+ }
+ }
+
+
+ /** STATIC INNER CLASS URLCollectionPair.
+ * Since the configuration for a (remote) Greenstone2 and Greenstone3 is now
+ * stored in a single file for both, we can have GLI connecting to different servers
+ * while for each server, a different collection may have been left open last time.
+ * This class URLCollectionPair will associate a GLIServer_URL or Library_URL with
+ * the last opened collection file for it. It is used by URLField.DropDown combobox. */
+ public static class URLCollectionPair {
+ String url; // gliserverURL
+ String collection; // last opened collection for the corresponding gliserver
+
+
+ public static URLCollectionPair[] createDefaultsArray(String[] defaultURLs) {
+ URLCollectionPair[] pairs = new URLCollectionPair[defaultURLs.length];
+ for(int i = 0; i < pairs.length; i++) {
+ pairs[i] = new URLCollectionPair(defaultURLs[i]); // no associated open collection
+ }
+ return pairs;
+ }
+
+ public URLCollectionPair(String url) {
+ this.set(url, "");
+ }
+
+ public URLCollectionPair(String url, String collection) {
+ this.set(url, collection);
+ }
+
+ public void set(String url, String collection) {
+ this.url = url;
+ this.collection = collection;
+ }
+
+ public void setURL(String url) {
+ this.url = url;
+ }
+
+ public void setCollection(String collection) {
+ this.collection = collection;
+ }
+
+ public String getCollection() {
+ return collection;
+ }
+
+ public String getURL() {
+ return url;
+ }
+
+ /** For display in comboboxes */
+ public String toString() {
+ return url;
+ }
+
+ /** @returns the number that is suffixed to the gliserver_url */
+ public String getSuffixNumber() {
+ String numericSuffix = "";
+ boolean stop = false;
+ for(int index = url.length()-1; index >= 0 && !stop; index--) { // work from the end
+ char character = url.charAt(index);
+ if(Character.isDigit(character)) {
+ numericSuffix = Character.toString(character) + numericSuffix;
+ } else {
+ stop = true;
+ }
+ }
+
+ return numericSuffix;
+ }
+ }
+
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/WarningDialog.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/WarningDialog.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/WarningDialog.java (revision 31635)
@@ -0,0 +1,288 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: John Thompson, Greenstone Project, NZDL, University of Waikato
+ *
+ * Copyright (C) 2003 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.util.JarTools;
+
+
+/** A dialog that warns about something, and allows the user to turn off the warning in the future. */
+public class WarningDialog
+ extends ModalDialog
+ implements ActionListener, KeyListener
+{
+ static final private Dimension NORMAL_SIZE = new Dimension(450, 160);
+ static final private Dimension SETTING_SIZE = new Dimension(450, 200);
+
+ private int result = JOptionPane.CANCEL_OPTION;
+ private JButton cancel_button;
+ private JButton ok_button;
+ private JCheckBox show_check;
+ private JComponent value_field;
+ private JPanel value_panel;
+ private String affected_property;
+ private String full_property;
+ /** Whether or not to display the checkbox that will turn off this dialog in future */
+ private final boolean showcheckbox;
+
+
+ public WarningDialog(String warning_name, String warning_title, String warning_message, String affected_property, boolean can_cancel)
+ {
+ this(warning_name, warning_title, warning_message, affected_property, can_cancel, true); // true for show checkbox
+ }
+
+
+ public WarningDialog(String warning_name, String warning_title, String warning_message, String affected_property, boolean can_cancel,
+ boolean showcheckbox)
+ {
+ super(Gatherer.g_man, "Warning", true);
+ this.setComponentOrientation(Dictionary.getOrientation());
+ this.showcheckbox = showcheckbox;
+
+ // Determine the name of this prompt.
+ this.affected_property = affected_property;
+ this.full_property = warning_name;
+
+ // Now build dialog.
+ if (affected_property != null) {
+ setSize(SETTING_SIZE);
+ }
+ else {
+ setSize(NORMAL_SIZE);
+ }
+ setTitle(warning_title);
+
+ // Creation
+ JPanel content_pane = (JPanel) getContentPane();
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+ JPanel text_pane = new JPanel();
+ text_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ String gmedium_image = "gatherer_medium.png";
+ if (Configuration.fedora_info.isActive()) {
+ gmedium_image = "fli-" + gmedium_image;
+ }
+
+ JLabel icon_label = new JLabel(JarTools.getImage(gmedium_image));
+ icon_label.setComponentOrientation(Dictionary.getOrientation());
+
+ JTextArea text_area = new JTextArea();
+ text_area.setComponentOrientation(Dictionary.getOrientation());
+ text_area.setEditable(false);
+ text_area.setLineWrap(true);
+ text_area.setText(warning_message);
+ text_area.setCaretPosition(0);
+ text_area.setWrapStyleWord(true);
+
+ value_panel = new JPanel();
+ value_panel.setComponentOrientation(Dictionary.getOrientation());
+ JLabel value_label = new JLabel(Dictionary.get("WarningDialog.Value"));
+ value_label.setComponentOrientation(Dictionary.getOrientation());
+
+ value_field = new JTextField();
+ value_field.setComponentOrientation(Dictionary.getOrientation());
+ JPanel bottom_pane = new JPanel();
+ bottom_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel control_pane = new JPanel();
+ control_pane.setComponentOrientation(Dictionary.getOrientation());
+ ok_button = new GLIButton(Dictionary.get("General.OK"), Dictionary.get("General.OK_Tooltip"));
+ cancel_button = new GLIButton(Dictionary.get("General.Cancel"), Dictionary.get("General.Pure_Cancel_Tooltip"));
+
+ // Connection
+ ok_button.addActionListener(this);
+ cancel_button.addActionListener(this);
+ ok_button.addKeyListener(this);
+ cancel_button.addKeyListener(this);
+ getRootPane().setDefaultButton(ok_button);
+
+ // Layout
+ icon_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5));
+
+ value_label.setBorder(BorderFactory.createEmptyBorder(0, icon_label.getPreferredSize().width, 0, 0));
+
+ value_panel.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
+ value_panel.setLayout(new BorderLayout(5,0));
+ value_panel.add(value_label, BorderLayout.LINE_START);
+ value_panel.add(value_field, BorderLayout.CENTER);
+
+ text_pane.setLayout(new BorderLayout());
+ text_pane.add(icon_label, BorderLayout.LINE_START);
+ text_pane.add(new JScrollPane(text_area), BorderLayout.CENTER);
+ if(affected_property != null) {
+ text_pane.add(value_panel, BorderLayout.SOUTH);
+ }
+
+ if(can_cancel) {
+ control_pane.setLayout(new GridLayout(1,2,5,0));
+ control_pane.add(ok_button);
+ control_pane.add(cancel_button);
+ }
+ else {
+ control_pane.setLayout(new BorderLayout());
+ control_pane.add(ok_button, BorderLayout.LINE_END);
+ }
+
+ bottom_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
+ bottom_pane.setLayout(new BorderLayout());
+ bottom_pane.add(control_pane, BorderLayout.LINE_END);
+ if(showcheckbox) {
+ show_check = new JCheckBox(Dictionary.get("WarningDialog.Dont_Show_Again"));
+ show_check.setComponentOrientation(Dictionary.getOrientation());
+ bottom_pane.add(show_check, BorderLayout.CENTER);
+ }
+
+ content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ content_pane.setLayout(new BorderLayout());
+ content_pane.add(text_pane, BorderLayout.CENTER);
+ content_pane.add(bottom_pane, BorderLayout.SOUTH);
+
+ // Position
+ Dimension size = getSize();
+ if (Gatherer.g_man != null) {
+ Rectangle frame_bounds = Gatherer.g_man.getBounds();
+ setLocation(frame_bounds.x + (frame_bounds.width - size.width) / 2, frame_bounds.y + (frame_bounds.height - size.height) / 2);
+ }
+ else {
+ Dimension screen_size = Toolkit.getDefaultToolkit().getScreenSize();
+ setLocation((screen_size.width - size.width) / 2, (screen_size.height - size.height) / 2);
+ }
+ }
+
+ public void actionPerformed(ActionEvent event) {
+ boolean valid_value = true;
+ if(event.getSource() == ok_button) {
+ if(affected_property != null && Configuration.self != null) {
+ valid_value = URLField.validateURL(value_field);
+ if(valid_value) {
+ // Store the value of the property
+ URLField.store(value_field, affected_property); //Configuration.setString(affected_property, true, value);
+ }
+ }
+ if(valid_value) {
+ result = JOptionPane.OK_OPTION;
+ }
+ }
+
+ if(valid_value) {
+ if (Configuration.self != null && showcheckbox) {
+ // Store the state of the show message checkbox.
+ Configuration.set(full_property, true, !show_check.isSelected());
+ }
+ // Done.
+ setVisible(false);
+ }
+ else {
+ JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("WarningDialog.Invalid_Value"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
+ }
+ }
+
+ /** Gives notification of key events on the buttons */
+ public void keyPressed(KeyEvent e) {
+ if (e.getKeyCode() == KeyEvent.VK_ENTER) {
+ // Enter: Click the button in focus
+ Object source = e.getSource();
+ if (source instanceof AbstractButton) {
+ ((AbstractButton) source).doClick();
+ }
+ }
+ }
+
+ public void keyReleased(KeyEvent e) {}
+
+ public void keyTyped(KeyEvent e) {}
+
+
+ public int display() {
+ if (Configuration.self == null || Configuration.get(full_property, false)) {
+ setVisible(true);
+ }
+ // We are no longer showing this dialog, so result must always be true.
+ else {
+ result = JOptionPane.OK_OPTION;
+ }
+ // Ask the parent to repaint, just to be sure
+ if(Gatherer.g_man != null) {
+ Gatherer.g_man.repaint();
+ }
+ return result;
+ }
+
+ /** Allows you to specify whether this dialog is a warning dialog, or only a message dialog.
+ * @param message_only true if this dialog shows a message, false for a warning
+ */
+ public void setMessageOnly(boolean message_only) {
+ if(!showcheckbox) {
+ return;
+ }
+
+ // If this is a message then change the checkbox
+ if(message_only) {
+ show_check.setText(Dictionary.get("WarningDialog.Dont_Show_Again_Message"));
+ }
+ // And if its a warning, change them back
+ else {
+ show_check.setText(Dictionary.get("WarningDialog.Dont_Show_Again"));
+ }
+ }
+
+ /** Allows you to replace the generic text field control with a JTextField subclass with further functionality. For instance you might provide a URLField to allow only valid URLs to be accepted.
+ * @param control the JTextField subclass you want to use for the control
+ */
+ public void setValueField(JComponent control) {
+ // Remove the current control
+ value_panel.remove(value_field);
+ // Replace with the new one
+ value_field = control;
+ // Re-add
+ value_panel.add(value_field, BorderLayout.CENTER);
+ }
+
+ protected void processWindowEvent(WindowEvent event) {
+ if(event.getID() == WindowEvent.WINDOW_ACTIVATED) {
+ if(affected_property != null) {
+ value_field.requestFocus();
+ }
+ else {
+ ok_button.requestFocus();
+ }
+ }
+ else {
+ super.processWindowEvent(event);
+ }
+ }
+
+ public void setValueField(String field_value){
+ URLField.setText(this.value_field, field_value);
+ }
+
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/WriteCDImagePrompt.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/WriteCDImagePrompt.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/WriteCDImagePrompt.java (revision 31635)
@@ -0,0 +1,661 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.util.ArrayList;
+import javax.swing.*;
+import javax.swing.event.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.collection.BasicCollectionConfiguration;
+import org.greenstone.gatherer.collection.CollectionManager;
+import org.greenstone.gatherer.greenstone.LocalGreenstone;
+import org.greenstone.gatherer.shell.GShell;
+import org.greenstone.gatherer.shell.GShellEvent;
+import org.greenstone.gatherer.shell.GShellListener;
+import org.greenstone.gatherer.util.ArrayTools;
+import org.greenstone.gatherer.util.CheckList;
+import org.greenstone.gatherer.util.CheckListEntry;
+import org.greenstone.gatherer.util.StaticStrings;
+import org.greenstone.gatherer.util.Utility;
+
+/** This class provides the functionality to write a CD-ROM/DVD image of current collections from the GSDLHOME/collect/ directory to CDROM. The user chooses the collection from a list, where each entry also displays details about itself, confirms the delete of a collection by checking a checkbox then presses the ok button to actually delete the collection.
+ Copied from DeleteCollectionPrompt
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.3
+ */
+public class WriteCDImagePrompt
+ extends ModalDialog
+ implements GShellListener {
+
+ private OKButtonListener ok_button_listener;
+ private ArrayList all_collections = null;
+ private ArrayList selected_collections = null;
+ /** The list of collections to include in exported cd-rom/dvd image */
+ private CheckList list = null;
+ /** The currently selected collection for deletion. */
+ private BasicCollectionConfiguration collection = null;
+ /** A reference to ourself so any inner-classes can dispose of us. */
+ private WriteCDImagePrompt prompt = null;
+ /** The close button, which exits the prompt without deleting anything. */
+ private JButton cancel_button = null;
+ /** The ok button which causes the selected collection to be deleted. */
+ private JButton ok_button = null;
+ /** The label above details. */
+ private JLabel details_label = null;
+ /** The label above the list. */
+ private JLabel list_label = null;
+ /** The text area used to display details about the collection selected. */
+ private JTextArea details_textarea = null;
+ /** The text area used to display instructions for the cd-rom/dvd export */
+ private JTextArea instructions_textarea;
+
+ /** The radio button for switching on the noinstall option */
+ private JRadioButton noinstall_button = null;
+ private JRadioButton install_button = null;
+ private JTextField title_field = null;
+ private JTextField estimated_size_field = null;
+ private JLabel title_label = null;
+ private JLabel estimated_size_label = null;
+ /** A string array used to pass arguments to the exportcol.pl script */
+ private String args[] = null;
+ private String cd_title = null;
+ /** whether the exporting was successful or not */
+ private boolean successful = false;
+ /** whether we are trying to export or not */
+ private boolean exporting = false;
+ /** the error message if any */
+ private StringBuffer error_message = null;
+ /** This is the size of the exported stuff on its own without collections. */
+ private long total_exported_size = 14000000;
+ /** The size of the export prompt screen. */
+ public static final Dimension SIZE = new Dimension(500, 500);
+
+ /** Constructor.
+ * @see org.greenstone.gatherer.collection.WriteCDImagePrompt.CancelButtonListener
+ * @see org.greenstone.gatherer.collection.WriteCDImagePrompt.CollectionListListener
+ * @see org.greenstone.gatherer.collection.WriteCDImagePrompt.OKButtonListener
+ */
+ public WriteCDImagePrompt() {
+ super(Gatherer.g_man, true);
+ this.setComponentOrientation(Dictionary.getOrientation());
+ cancel_button = new GLIButton(Dictionary.get("General.Close"), Dictionary.get("General.Close_Tooltip"));
+
+ details_textarea = new JTextArea(Dictionary.get("DeleteCollectionPrompt.No_Collection"));
+ details_textarea.setComponentOrientation(Dictionary.getOrientation());
+ details_textarea.setEditable(false);
+
+ details_label = new JLabel(Dictionary.get("DeleteCollectionPrompt.Collection_Details"));
+ details_label.setComponentOrientation(Dictionary.getOrientation());
+
+ instructions_textarea = new JTextArea(Dictionary.get("WriteCDImagePrompt.Instructions"));
+ instructions_textarea.setComponentOrientation(Dictionary.getOrientation());
+ instructions_textarea.setCaretPosition(0);
+ instructions_textarea.setEditable(false);
+ instructions_textarea.setLineWrap(true);
+ instructions_textarea.setRows(4);
+ instructions_textarea.setWrapStyleWord(true);
+
+ all_collections = new ArrayList();
+ list = new CheckList(true);
+ list_label = new JLabel(Dictionary.get("DeleteCollectionPrompt.Collection_List"));
+ list_label.setComponentOrientation(Dictionary.getOrientation());
+
+ ok_button = new GLIButton(Dictionary.get("WriteCDImagePrompt.Export"), Dictionary.get("WriteCDImagePrompt.Export_Tooltip"));
+
+ title_field = new JTextField();
+ title_field.setComponentOrientation(Dictionary.getOrientation());
+ // Dictionary.setTooltip(title_field, "WriteCDImagePrompt.CD_Name_Tooltip");
+ title_label = new JLabel(Dictionary.get("WriteCDImagePrompt.CD_Name"));
+ title_label.setComponentOrientation(Dictionary.getOrientation());
+
+ estimated_size_field = new JTextField();
+ estimated_size_field.setComponentOrientation(Dictionary.getOrientation());
+ estimated_size_field.setEditable(false);
+
+ // work out the size of the images directory - could be large if they
+ // have the classic interface pack installed
+ total_exported_size += getFileSize(new File(LocalGreenstone.getDirectoryPath()+File.separator+"images"));
+ estimated_size_field.setText(Utility.formatFileLength(total_exported_size));
+ estimated_size_field.setBackground(Configuration.getColor("coloring.collection_tree_background", false));
+ estimated_size_label = new JLabel(Dictionary.get("WriteCDImagePrompt.Size_Label"));
+ estimated_size_label.setComponentOrientation(Dictionary.getOrientation());
+
+ scanForCollections();
+ list.setListData(all_collections);
+
+ prompt = this;
+ setSize(SIZE);
+ setTitle(Dictionary.get("WriteCDImagePrompt.Export"));
+
+ setJMenuBar(new SimpleMenuBar("exportingcollections"));
+ cancel_button.addActionListener(new CancelButtonListener());
+ list.addListSelectionListener(new CollectionListListener());
+ list.clearSelection();
+ list.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ ok_button_listener = new OKButtonListener();
+ ok_button.addActionListener(ok_button_listener);
+ ok_button.setEnabled(false);
+ //title.getDocument().addDocumentListener(new DocumentListener());
+
+ noinstall_button = new JRadioButton(Dictionary.get("WriteCDImagePrompt.NoInstall"));
+ noinstall_button.setToolTipText(Dictionary.get("WriteCDImagePrompt.NoInstall_Tooltip"));
+ noinstall_button.setComponentOrientation(Dictionary.getOrientation());
+
+ install_button = new JRadioButton(Dictionary.get("WriteCDImagePrompt.Install"));
+ install_button.setToolTipText(Dictionary.get("WriteCDImagePrompt.Install_Tooltip"));
+ install_button.setComponentOrientation(Dictionary.getOrientation());
+
+ }
+
+ /** Destructor. */
+ public void destroy() {
+ all_collections.clear();
+ all_collections = null;
+ cancel_button = null;
+ details_textarea = null;
+ details_label = null;
+ list = null;
+ ok_button = null;
+ prompt = null;
+ if (selected_collections!=null) {
+ selected_collections.clear();
+ selected_collections = null;
+ }
+ title_field = null;
+ title_label = null;
+ }
+
+ /** This method causes the modal prompt to be displayed.
+ * returns true if it has exported the collections that are currently selected */
+ public boolean display() {
+
+ JScrollPane scrol_tmp;
+ // Radio buttons
+ ButtonGroup radio_group = new ButtonGroup();
+ radio_group.add(install_button);
+ install_button.setSelected(true);
+ radio_group.add(noinstall_button);
+
+ // Top pane
+ JPanel instructions_pane = new JPanel(new BorderLayout());
+ instructions_pane.setComponentOrientation(Dictionary.getOrientation());
+ instructions_pane.setBorder(BorderFactory.createEmptyBorder(5,5,0,5));
+ scrol_tmp = new JScrollPane(instructions_textarea);
+ scrol_tmp.setComponentOrientation(Dictionary.getOrientation());
+ instructions_pane.add(scrol_tmp, BorderLayout.CENTER);
+
+ title_label.setBorder(BorderFactory.createEmptyBorder(0,5,0,15));
+
+ JPanel title_pane = new JPanel(new BorderLayout());
+ title_pane.setComponentOrientation(Dictionary.getOrientation());
+ title_pane.add(title_label, BorderLayout.LINE_START);
+ title_pane.add(title_field, BorderLayout.CENTER);
+ title_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
+
+ JPanel radio_pane = new JPanel(new GridLayout(2,1));
+ radio_pane.setComponentOrientation(Dictionary.getOrientation());
+ install_button.setBackground(Configuration.getColor("coloring.collection_heading_background", true));
+ noinstall_button.setBackground(Configuration.getColor("coloring.collection_heading_background", true));
+ radio_pane.add(install_button);
+ radio_pane.add(noinstall_button);
+
+ JPanel options_pane = new JPanel(new BorderLayout());
+ options_pane.setComponentOrientation(Dictionary.getOrientation());
+ options_pane.add(title_pane, BorderLayout.NORTH);
+ options_pane.add(radio_pane, BorderLayout.CENTER);
+ instructions_pane.add(options_pane, BorderLayout.SOUTH);
+
+ // Central pane
+ JPanel list_pane = new JPanel(new BorderLayout());
+ list_pane.setComponentOrientation(Dictionary.getOrientation());
+ list_pane.add(list_label, BorderLayout.NORTH);
+ scrol_tmp =new JScrollPane(list);
+ scrol_tmp.setComponentOrientation(Dictionary.getOrientation());
+ list_pane.add(scrol_tmp, BorderLayout.CENTER);
+ list_pane.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0));
+
+ JPanel details_pane = new JPanel(new BorderLayout());
+ details_pane.add(details_label, BorderLayout.NORTH);
+ scrol_tmp =new JScrollPane(details_textarea);
+ scrol_tmp.setComponentOrientation(Dictionary.getOrientation());
+ details_pane.add(scrol_tmp, BorderLayout.CENTER);
+ details_pane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
+
+ JPanel central_pane = new JPanel(new GridLayout(2, 1));
+ central_pane.setComponentOrientation(Dictionary.getOrientation());
+ central_pane.add(list_pane);
+ central_pane.add(details_pane);
+ central_pane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+
+ // Lower pane
+ JPanel estimated_size_pane = new JPanel(new BorderLayout());
+ estimated_size_pane.setComponentOrientation(Dictionary.getOrientation());
+ estimated_size_pane.add(estimated_size_label, BorderLayout.LINE_START);
+ estimated_size_pane.add(estimated_size_field, BorderLayout.CENTER);
+ estimated_size_pane.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0));
+
+ JPanel button_pane = new JPanel(new GridLayout(1, 2));
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+ button_pane.add(ok_button);
+ button_pane.add(cancel_button);
+ button_pane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
+
+ JPanel lower_pane = new JPanel(new BorderLayout());
+ lower_pane.setComponentOrientation(Dictionary.getOrientation());
+ lower_pane.add(estimated_size_pane, BorderLayout.NORTH);
+ lower_pane.add(button_pane, BorderLayout.SOUTH);
+ lower_pane.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5));
+
+ // Final.
+ JPanel content_pane = (JPanel)this.getContentPane();
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+ content_pane.setLayout(new BorderLayout());
+ content_pane.add(instructions_pane, BorderLayout.NORTH);
+ content_pane.add(central_pane, BorderLayout.CENTER);
+ content_pane.add(lower_pane, BorderLayout.SOUTH);
+
+ // Center and display.
+ Dimension screen_size = Configuration.screen_size;
+ this.setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
+ this.setVisible(true); // blocks until the dialog is killed
+ return true;
+
+ }
+
+
+ private long getFileSize(File file)
+ {
+ long file_size = 0;
+
+ // Directory case
+ if (file.isDirectory()) {
+ File files[] = file.listFiles();
+ for (int i = 0; i < files.length; i++) {
+ file_size += getFileSize(files[i]);
+ }
+ }
+ // File case
+ else {
+ file_size = file.length();
+ }
+
+ return file_size;
+ }
+
+ /** This method calls the builcol.pl scripts via a GShell so as to not lock up the processor.
+ * @see org.greenstone.gatherer.Configuration
+ * @see org.greenstone.gatherer.Gatherer
+ * @see org.greenstone.gatherer.collection.Collection
+ * @see org.greenstone.gatherer.gui.BuildOptions
+ * @see org.greenstone.gatherer.shell.GShell
+ * @see org.greenstone.gatherer.shell.GShellListener
+ * @see org.greenstone.gatherer.shell.GShellProgressMonitor
+ * @see org.greenstone.gatherer.util.Utility
+ */
+ public void writeCDImageCollections()
+ {
+ DebugStream.println("WriteCDImagePrompt.writeCDImageCollections()");
+
+ int num_collections = selected_collections.size();
+ if (num_collections == 0) return;
+ cd_title = title_field.getText();
+ cd_title = cd_title.trim();
+ cd_title = cd_title.replaceAll("\"","");
+
+ // Generate the exportcol.pl command
+ ArrayList command_parts_list = new ArrayList();
+ if (!Gatherer.isGsdlRemote) {
+ command_parts_list.add(Configuration.perl_path);
+ command_parts_list.add("-S");
+ }
+ command_parts_list.add(LocalGreenstone.getBinScriptDirectoryPath() + "exportcol.pl");
+
+ command_parts_list.add("-gli");
+ command_parts_list.add("-language");
+ command_parts_list.add(Configuration.getLanguage());
+
+ if (cd_title.equals("")) {
+ command_parts_list.add("-cdname");
+ command_parts_list.add("Greenstone Collections");
+
+ command_parts_list.add("-cddir");
+ command_parts_list.add("exported_collections");
+ }
+ else {
+ command_parts_list.add("-cdname");
+ command_parts_list.add(cd_title);
+
+ String cd_dir = "exported_"+cd_title.replaceAll("\\s","");
+ command_parts_list.add("-cddir");
+ command_parts_list.add(cd_dir);
+ }
+
+ if (noinstall_button.isSelected()) {
+ command_parts_list.add("-noinstall");
+ }
+
+ // we want to be able to export collections in other collect directories too
+ String collectDir = Gatherer.getCollectDirectoryPath();
+ if(collectDir != Gatherer.getDefaultGSCollectDirectoryPath(true)) {
+ command_parts_list.add("-collectdir");
+ command_parts_list.add(collectDir);
+ }
+
+ for (int i = 0; i < num_collections; i++) {
+ command_parts_list.add(((BasicCollectionConfiguration) selected_collections.get(i)).getShortName());
+ }
+
+ DebugStream.print("export command = ");
+ for (int i = 0; i < command_parts_list.size(); i++) {
+ DebugStream.print(command_parts_list.get(i) + " ");
+ }
+ DebugStream.println("");
+ // Run the exportcol.pl command
+ String[] command_parts = (String[]) command_parts_list.toArray(new String[0]);
+ GShell process = new GShell(command_parts, GShell.CDIMAGE, 3, this, null, GShell.GSHELL_CDIMAGE);
+ process.start();
+ //process.run();
+ DebugStream.println("WriteCDImagePrompt.writeCDImageCollections().return");
+
+ }
+
+ /** Shows an export complete prompt.
+ * @param success A boolean indicating if the collection was successfully deleted.
+ * @see org.greenstone.gatherer.collection.Collection
+ */
+ public void resultPrompt(boolean success, String extra) {
+ args = new String[2];
+ StringBuffer coll_names = new StringBuffer();
+ for (int i=0; i0) {
+ coll_names.append(", ");
+ }
+ BasicCollectionConfiguration complete_collection = (BasicCollectionConfiguration)selected_collections.get(i);
+ coll_names.append(complete_collection.getName() + StaticStrings.SPACE_CHARACTER + StaticStrings.OPEN_PARENTHESIS_CHARACTER + complete_collection.getShortName() + StaticStrings.CLOSE_PARENTHESIS_CHARACTER);
+ complete_collection = null;
+ }
+
+ args[0] = coll_names.toString();
+ args[1] = LocalGreenstone.getTmpDirectoryPath();
+ if(cd_title.equals("")) {
+ args[1] += "exported_collections";
+ } else {
+ args[1] += "exported_"+cd_title.replaceAll("\\s","");
+ }
+ String title;
+ String label;
+ if (success) {
+ title = Dictionary.get("WriteCDImagePrompt.Successful_Title");
+ label = Dictionary.get("WriteCDImagePrompt.Successful_Export", args);
+ } else {
+ title = Dictionary.get("WriteCDImagePrompt.Failed_Title");
+ label = Dictionary.get("WriteCDImagePrompt.Failed_Export", args);
+ }
+ SimpleResultDialog result_dialog = new SimpleResultDialog(title, label, extra);
+ result_dialog.setVisible(true); // Blocks
+ result_dialog.dispose();
+ result_dialog = null;
+ }
+
+ /** Method to scan the collect directory retrieving and reloading each collection it finds, while building the list of known collections.
+ * @see org.greenstone.gatherer.Configuration
+ * @see org.greenstone.gatherer.Gatherer
+ * @see org.greenstone.gatherer.util.ArrayTools
+ * @see org.greenstone.gatherer.util.Utility
+ */
+ private void scanForCollections() {
+ // Start at the collect dir.
+ File collect_directory = new File(Gatherer.getCollectDirectoryPath());
+ if (collect_directory.exists()) {
+ scanForCollectionsInternal(collect_directory);
+ }
+ }
+
+ private void scanForCollectionsInternal(File collect_directory) {
+
+ String file_name = (Gatherer.GS3)? Utility.CONFIG_GS3_FILE : Utility.CONFIG_FILE;
+ // For each child directory see if it contains a .cfg file and
+ // if so try to load it..
+ File collections[] = collect_directory.listFiles();
+ ArrayTools.sort(collections);
+ for(int i = 0; collections != null && i < collections.length; i++) {
+ if(collections[i].isDirectory() && !collections[i].getName().equals(StaticStrings.MODEL_COLLECTION_NAME)) {
+ File config_file = new File(collections[i], file_name);
+ if (config_file.exists()) {
+ BasicCollectionConfiguration config = new BasicCollectionConfiguration(config_file);
+ if (config.getCollectGroup().equals("true")) {
+ scanForCollectionsInternal(collections[i]);
+ } else {
+ all_collections.add(config);
+ config = null;
+ }
+ }
+ }
+ }
+ }
+
+
+ /** All implementation of GShellListener must include this method so the listener can be informed of messages from the GShell.
+ * @param event A GShellEvent that contains, amoung other things, the message.
+ */
+ public synchronized void message(GShellEvent event) {
+ // Ignore the messages from RecPlug with 'show_progress' set (used for progress bars)
+ String message = event.getMessage();
+ if (message.startsWith("exportcol.pl>")) {
+ message = message.substring(13);
+ //DebugStream.println("message = "+event.getMessage());
+ error_message.append(message);
+ error_message.append("\n");
+ }
+ }
+
+ /** All implementation of GShellListener must include this method so the listener can be informed when a GShell begins its task. Implementation side-effect, not actually used.
+ * @param event A GShellEvent that contains details of the initial state of the GShell before task comencement.
+ */
+ public synchronized void processBegun(GShellEvent event) {
+ // We don't care.
+ }
+ /** All implementation of GShellListener must include this method so the listener can be informed when a GShell completes its task.
+ * @param event A GShellEvent that contains details of the final state of the GShell after task completion.
+ */
+ public synchronized void processComplete(GShellEvent event) {
+ successful = false;
+ if(event.getStatus() == GShell.OK) {
+ if(event.getType() == GShell.CDIMAGE) {
+ successful = true;
+ }
+ }
+ ok_button_listener.processComplete();
+ }
+
+ /** A button listener implementation, which listens for actions on the close button and disposes of the dialog when detected. */
+ private class CancelButtonListener
+ implements ActionListener {
+ /** Any implementation of ActionListener must include this method so we can be informed when the button is actioned.
+ * @param event An ActionEvent containing all the relevant information garnered from the event itself.
+ */
+ public void actionPerformed(ActionEvent event) {
+ prompt.dispose();
+ }
+ }
+
+ /** This private class listens for selection events in from the list and then displays the appropriate details for that collection.
+ */
+ private class CollectionListListener
+ implements ListSelectionListener
+ {
+ /** Any implementation of ListSelectionListener must include this method so we can be informed when the list selection changes.
+ * @param event a ListSelectionEvent containing all the relevant information garnered from the event itself
+ */
+ public void valueChanged(ListSelectionEvent event)
+ {
+ // Wait for things to settle down a bit
+ if (event.getValueIsAdjusting()) {
+ return;
+ }
+
+ // Can only export when something is ticked
+ ok_button.setEnabled(!list.isNothingTicked());
+
+ if (list.isSelectionEmpty()) {
+ // This only happens when the dialog is first entered
+ details_textarea.setText(Dictionary.get("DeleteCollectionPrompt.No_Collection"));
+ return;
+ }
+
+ collection = (BasicCollectionConfiguration) ((CheckListEntry) list.getSelectedValue()).getObject();
+ args = new String[3];
+ args[0] = collection.getCreator();
+ args[1] = collection.getMaintainer();
+ args[2] = collection.getDescription();
+ details_textarea.setText(Dictionary.get("DeleteCollectionPrompt.Details", args));
+ details_textarea.setCaretPosition(0);
+
+ // Find the size of the "etc", "images" and "index" directories of the collection
+ String collection_directory_path = CollectionManager.getCollectionDirectoryPath(collection.getShortName()); // (colgroup/)subcol
+ File etc_directory = new File(collection_directory_path + "etc");
+ File images_directory = new File(collection_directory_path + "images");
+ File index_directory = new File(collection_directory_path + "index");
+ long collection_size_built = getFileSize(etc_directory) + getFileSize(images_directory) + getFileSize(index_directory);
+
+ // Add/subtract it from the total, depending on whether the collection has just been ticked/unticked
+ if (((CheckListEntry) list.getSelectedValue()).isSelected()) {
+ total_exported_size += collection_size_built;
+ }
+ else {
+ total_exported_size -= collection_size_built;
+ }
+
+ // Update the size field
+ estimated_size_field.setText(Utility.formatFileLength(total_exported_size));
+ }
+ }
+
+
+ /** The OK button listener implementation. */
+ private class OKButtonListener
+ implements ActionListener {
+ private Component glass_pane;
+ private MouseListener mouse_blocker_listener;
+ private ProgressDialog progress_dialog;
+
+ /** Any implementation of ActionListener must include this method so we can be informed when the button is actioned.
+ * @param event An ActionEvent containing all the relevant information garnered from the event itself.
+ * @see org.greenstone.gatherer.Configuration
+ * @see org.greenstone.gatherer.Gatherer
+ * @see org.greenstone.gatherer.util.Utility
+ */
+ public void actionPerformed(ActionEvent event) {
+ ///ystem.err.println("OK Clicked");
+ // Make sure there are some colls specified
+ selected_collections = list.getTicked();
+ error_message = new StringBuffer();
+
+ // Set the cursor to hourglass
+ glass_pane = getGlassPane();
+ mouse_blocker_listener = new MouseAdapter() {};
+ glass_pane.addMouseListener(mouse_blocker_listener);
+ glass_pane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+ glass_pane.setVisible(true);
+
+ // Export the selected collection.
+ ///ystem.err.println("Write CD-ROM/DVD image for named collections");
+ writeCDImageCollections();
+
+ // Show progress dialog
+ ///ystem.err.println("Showing progress dialog");
+ progress_dialog = new ProgressDialog();
+ progress_dialog.setVisible(true);
+ }
+
+ public void processComplete() {
+ ///ystem.err.println("Process complete");
+ // Dispose of progress dialog
+ progress_dialog.setVisible(false);
+ progress_dialog.dispose();
+ progress_dialog = null;
+
+ // unset the cursor
+ glass_pane.setVisible(false);
+ glass_pane.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+ glass_pane.removeMouseListener(mouse_blocker_listener);
+ glass_pane = null;
+ mouse_blocker_listener= null;
+
+ if (successful) {
+ resultPrompt(true, error_message.toString());
+ } else {
+ resultPrompt(false, error_message.toString());
+ }
+ error_message = null;
+ }
+
+ private class ProgressDialog
+ extends ModalDialog {
+
+ private Dimension size = new Dimension(400,65);
+
+ public ProgressDialog() {
+ super(Gatherer.g_man, Dictionary.get("WriteCDImagePrompt.Title"), true);
+ this.setComponentOrientation(Dictionary.getOrientation());
+ setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
+ setSize(size);
+ JPanel content_pane = (JPanel) getContentPane();
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+ JLabel progress_label = new JLabel(Dictionary.get("WriteCDImagePrompt.Progress_Label"));
+ progress_label.setComponentOrientation(Dictionary.getOrientation());
+
+ JProgressBar progress_bar = new JProgressBar();
+ progress_bar.setComponentOrientation(Dictionary.getOrientation());
+ progress_bar.setIndeterminate(true);
+ content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ content_pane.setLayout(new BorderLayout());
+ content_pane.add(progress_label, BorderLayout.NORTH);
+ content_pane.add(progress_bar, BorderLayout.CENTER);
+ // Position
+ Rectangle frame_bounds = Gatherer.g_man.getBounds();
+ setLocation(frame_bounds.x + (frame_bounds.width - size.width) / 2, frame_bounds.y + (frame_bounds.height - size.height) / 2);
+ }
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/metaaudit/Autofilter.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/metaaudit/Autofilter.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/metaaudit/Autofilter.java (revision 31635)
@@ -0,0 +1,379 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui.metaaudit;
+
+
+import java.util.ArrayList;
+import org.greenstone.gatherer.util.StaticStrings;
+
+/** An Autofilter object stores the filters set on a single column, and provides a method for determining if a certain value passes the filter.
+ * @author John Thompson
+ * @version 2.3
+ */
+public class Autofilter {
+
+ static public void main(String[] args) {
+ if(args.length != 2) {
+ System.err.println("Usage: java Autofilter ");
+ }
+ else {
+ switch(compareToPattern(args[0], args[1])) {
+ case LESS_THAN:
+ System.err.print(args[0] + " is less than");
+ break;
+ case GREATER_THAN:
+ System.err.print(args[0] + " is greater than");
+ break;
+ case EQUAL:
+ System.err.print(args[0] + " is equal to");
+ break;
+ default:
+ System.err.print(args[0] + " is something to");
+ }
+ System.err.println(" " + args[1]);
+ }
+ }
+
+ /** A String comparison which also recognises the WildCard character. The basic idea is that '*' matches zero or more of any characters, and if we limited patterns to be like "a*" then as soon as we hit the star we could return a match. The problem is that we want patterns like "b*y" which would match "boy" but not "bee" nor "bus" (but should return less than or greater than for each of these respectively). Thus our testing has to become non-deterministic so the matches are conducted like so:
+ * Given p = [ b * y ]
+ * Test s = [ b e e ] Result: p0=s0,{(p2>s1=>ERROR),(p1=s1,{(p1>s2=>ERROR),(p2>s2=>LESS_THAN)})} => LESS_THAN
+ * Test s = [ b o y ] Result: p0=s0,{(p2>s1=>ERROR),(p1=s1,{(p1>s2=>ERROR),(p2=s2=>EQUAL)})} => EQUAL
+ * Test s = [ b u s ] Result: p0=s0,{(p2>s1=>ERROR),(p1=s1,{(p1>s2=>ERROR),(p2GREATER_THAN)})} => GREATER_THAN
+ * where the two () phrases within a {} represent two different 'threads' of processing. Errors are generated when one string runs out of characters before the other. Only non-error results are propogated. I've decided to implement this non-deterministic matching as a recursive function call.
+ * If you analyse it you can see there are three possible actions when a '*' is detected.
+ * 1. * is an empty string, ignore it in pattern and try to compare the next character in p with the current character in s. This case only occurs if there are more characters in p (otherwise apple would be greater than a*)
+ * 2. * matches exactly the current character in s, try to match the next character in p with the next character in s
+ * 3. * matches some string of characters including the current character in s, try to match '*' against the next character in s
+ * @param s the String being matched
+ * @param p the Pattern to match it against
+ * @return -1 if s is less than p, 0 if they are equal, 1 if s is greater than p
+ */
+ static private int compareToPattern(String s, String p) {
+ if(p.indexOf(StaticStrings.STAR_CHAR) == -1) {
+ return compareToPattern(s, p, 0, 0, false);
+ }
+ else {
+ return compareToPattern(s, p, 0, 0, true);
+ }
+ }
+
+ static final private int LESS_THAN = -1;
+ static final private int EQUAL = 0;
+ static final private int GREATER_THAN = 1;
+ static final private int ERROR = 42;
+
+ static private int compareToPattern(String s, String p, int s_index, int p_index, boolean wildcard_matching) {
+ // Lets do the simple cases first.
+ // Both out of characters at the same time
+ if(s_index >= s.length() && p_index >= p.length()) {
+ ///ystem.err.println("Both out of characters. Equal.");
+ return EQUAL;
+ }
+ // s out of characters first
+ if(s_index >= s.length()) {
+ // Not allowed to run out if matching wildcard
+ if(wildcard_matching) {
+ return ERROR;
+ }
+ // Remember that wildcard matches the empty string too, so as long as there are only '*' characters left in the pattern we can say they are equal as well.
+ else {
+ while(p_index < p.length()) {
+ if(p.charAt(p_index) == StaticStrings.STAR_CHAR) {
+ p_index++;
+ }
+ else {
+ ///ystem.err.println("S out of characters. Less Than.");
+ return LESS_THAN;
+ }
+ }
+ // We've run out of pattern and it only contained '*' so we're still equal
+ ///ystem.err.println("Both out of characters. Equal.");
+ return EQUAL;
+ }
+ }
+ // p out of characters first
+ if(p_index >= p.length()) {
+ // Not allowed to run out if matching wildcard
+ if(wildcard_matching) {
+ return ERROR;
+ }
+ // so it is greater than
+ else {
+ ///ystem.err.println("P out of characters. Greater Than.");
+ return GREATER_THAN;
+ }
+ }
+ char s_char = s.charAt(s_index);
+ char p_char = p.charAt(p_index);
+ ///ystem.err.println("Comparing " + s_char + " against pattern character " + p_char);
+ // Equivelent characters
+ // Now the tricky-dicky bit - WildCard matching.
+ if(p_char == StaticStrings.STAR_CHAR) {
+ int result;
+ // Case 1
+ ///ystem.err.println("WildCard Case 1");
+ if(p_index + 1 < p.length()) {
+ if((result = compareToPattern(s, p, s_index, p_index + 1, wildcard_matching)) != ERROR) {
+ return result;
+ }
+ }
+ // Case 2
+ ///ystem.err.println("WildCard Case 2");
+ if((result = compareToPattern(s, p, s_index + 1, p_index + 1, wildcard_matching)) != ERROR) {
+ return result;
+ }
+ // Case 3
+ ///ystem.err.println("WildCard Case 3");
+ if((result = compareToPattern(s, p, s_index + 1, p_index, wildcard_matching)) != ERROR) {
+ return result;
+ }
+ }
+ if(s_char == p_char) {
+ return compareToPattern(s, p, s_index + 1, p_index + 1, wildcard_matching);
+ }
+ // A preceeding character
+ if(s_char < p_char) {
+ ///ystem.err.println("s char is less than p char");
+ if(wildcard_matching) {
+ ///ystem.err.println("Because we are wildcard matching we still have to match the rest of s incase of errors.");
+ if(compareToPattern(s, p, s_index + 1, p_index + 1, wildcard_matching) != ERROR) {
+ ///ystem.err.println("No error. Less than.");
+ return LESS_THAN;
+ }
+ else {
+ ///ystem.err.println("Error detected.");
+ return ERROR;
+ }
+ }
+ else {
+ ///ystem.err.println("Less than.");
+ return LESS_THAN;
+ }
+ }
+ // A succeeding character
+ if(s_char > p_char) {
+ ///ystem.err.println("s char is greater than p char");
+ if(wildcard_matching) {
+ ///ystem.err.println("Because we are wildcard matching we still have to match the rest of s incase of errors.");
+ if(compareToPattern(s, p, s_index + 1, p_index + 1, wildcard_matching) != ERROR) {
+ ///ystem.err.println("No error. Greater than.");
+ return GREATER_THAN;
+ }
+ else {
+ ///ystem.err.println("Error detected.");
+ return ERROR;
+ }
+ }
+ else {
+ ///ystem.err.println("Greater than.");
+ return GREATER_THAN;
+ }
+ }
+ // Oh-No. Well, we'll just assume that string is less than pattern
+ return LESS_THAN;
+ }
+
+ /** true if the filter should be applied, false to indicate the filter is turned off. */
+ public boolean active;
+ /** true if the matching for the first expression should be case sensitive, false otherwise. */
+ public boolean casesense_one;
+ /** true if the matching for the second expression should be case sensitive, false otherwise. */
+ public boolean casesense_two;
+ /** Used to determine the operation intended when applying two filters, and set using the values of the OPERATION_TYPE enumeration. */
+ public boolean operation;
+ /** Used to determine how the column this filter is applied to should be sorted, and set using the values of the SORT_TYPE enumeration. */
+ public boolean sort;
+ /** The method to be used for the first filter expression, set from the values of the METHOD_LIST enumeration. */
+ public int method_one;
+ /** The method to be used for the second filter expression, set from the values of the METHOD_LIST enumeration. */
+ public int method_two;
+ /** The value to be matched against for the first expression. */
+ public String value_one;
+ /** The value to be matched against for the second expression. */
+ public String value_two;
+ /** An element of the SORT_TYPE enumeration, indicates lowest to highest value column ordering. */
+ public static final boolean ASCENDING = true;
+ /** An element of the OPERATION_TYPE enumeration, indicates that both filter expressions must be met (conjunction). */
+ public static final boolean AND = true;
+ /** An element of the SORT_TYPE enumeration, indicates highest to lowest value column ordering. */
+ public static final boolean DESCENDING = false;
+ /** An element of the OPERATION_TYPE enumeration, indicates that either (or both) filter expressions must be met (disjunction). */
+ public static final boolean OR = false;
+ /** An enumeration of symbolic names of various matching methods. */
+ public static final String METHOD_LIST[] = { "Autofilter.eqeq", "Autofilter.!eq", "Autofilter.<", "Autofilter.", "Autofilter.>eq", "Autofilter.^", "Autofilter.!^", "Autofilter.$", "Autofilter.!$", "Autofilter.?", "Autofilter.!?" };
+
+ /** Default Constructor. */
+ public Autofilter() {
+ operation = OR;
+ sort = ASCENDING;
+ }
+ /** Determine if this filter is currently active.
+ * @return true if it is active, false otherwise.
+ */
+ public boolean active() {
+ return active;
+ }
+ /** Determine if this list of values (for a certain cell) passes the filter.
+ * @param values An ArrayList of values sourced from a single cell in the associated column.
+ * @return true if the values match and should be displayed, false otherwise.
+ */
+ public boolean filter(ArrayList values) {
+ boolean result = false;
+ if(value_one != null) {
+ result = filter(values, method_one, value_one, casesense_one);
+ if(result) {
+ if(operation == AND && value_two != null) {
+ result = filter(values, method_two, value_two, casesense_two);
+ }
+ }
+ else if(operation == OR && value_two != null) {
+ result = filter(values, method_two, value_two, casesense_two);
+ }
+ }
+ return result;
+ }
+ /** Set the current activity state of this filter.
+ * @param active The new state of this filter, true to activate, false otherwise.
+ */
+ public void setActive(boolean active) {
+ this.active = active;
+ }
+ /** Set one of the filter expressions using the given information.
+ * @param number The number of the filter you wish to set as an int . Either 1 or 2.
+ * @param method An int indicating the method to be used when matching.
+ * @param value The String to be matched against.
+ * @param casesense true if this expression should be case sensitive, false otherwise.
+ */
+ public void setFilter(int number, int method, String value, boolean casesense) {
+ if(!casesense && value != null) {
+ value = value.toLowerCase();
+ }
+ if(number == 1) {
+ casesense_one = casesense;
+ method_one = method;
+ value_one = value;
+ }
+ else {
+ casesense_two = casesense;
+ method_two = method;
+ value_two = value;
+ }
+ }
+ /** Set the operation to be used to join the two filters (if a second filter is set).
+ * @param operation true for conjunct filters, false for disjunct.
+ */
+ public void setOperation(boolean operation) {
+ this.operation = operation;
+ }
+ /** Set the sort order of this column.
+ * @param sort true for ascending sort, false for descending.
+ */
+ public void setSort(boolean sort) {
+ this.sort = sort;
+ }
+ /** Decide whether a row should be displayed or filtered. The result depends on the selector set with setFilterType.
+ * @param values An ArrayList of values to be checked against the filter.
+ * @param method The method of matching to be used, as an int .
+ * @param target The String to match against.
+ * @param casesense true if the match is to be case sensitive, false otherwise.
+ * @return true to display the row, false to hide it.
+ */
+ public boolean filter(ArrayList values, int method, String target, boolean casesense) {
+ boolean result = false;
+ // There are several special cases when the filter always returns turn, such as when the target is the wildcard character.
+ if(target == null || target.length() == 0 || target.equals("*")) {
+ result = true;
+ }
+ else {
+ // For each value in the list...
+ for(int i = 0; i < values.size(); i++) {
+ boolean pass;
+ String source;
+ // Account for case sensitivity.
+ if(casesense) {
+ source = values.get(i).toString();
+ }
+ else {
+ source = values.get(i).toString().toLowerCase();
+ }
+ ///ystem.err.println("Testing " + source + " against pattern " + target);
+ // Perform the match, based on the selected method.
+ switch(method) {
+ case 1: // !EQ
+ pass = (compareToPattern(source, target) != 0);
+ break;
+ case 2: // <
+ pass = (compareToPattern(source, target) < 0);
+ break;
+ case 3: //
+ pass = (compareToPattern(source, target) > 0);
+ break;
+ case 5: // >eq
+ pass = (compareToPattern(source, target) >= 0);
+ break;
+ case 6: // ^
+ pass = source.startsWith(target);
+ break;
+ case 7: // !^
+ pass = !(source.startsWith(target));
+ break;
+ case 8: // $
+ pass = source.endsWith(target);
+ break;
+ case 9: // !$
+ pass = !(source.endsWith(target));
+ break;
+ case 10: // ?
+ pass = (source.indexOf(target) != -1);
+ break;
+ case 11: // !?
+ pass = (source.indexOf(target) == -1);
+ break;
+ default: // EQEQ
+ pass = (compareToPattern(source, target) == 0);
+ break;
+ }
+ result = result || pass;
+ }
+ }
+ return result;
+ }
+ /** Produce a textual representation of this autofilter.
+ * @return A String displaying details of this autofilter.
+ */
+ public String toString() {
+ String result = "One: " + method_one + " - " + value_one + " - " + casesense_one;
+ if(value_two != null) {
+ result = result + "\n" + "Operation: " + (operation?"AND":"OR") + "\n";
+ result = result + "Two: " + method_two + " - " + value_two + " - " + casesense_two;
+ }
+ return result;
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/metaaudit/AutofilterDialog.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/metaaudit/AutofilterDialog.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/metaaudit/AutofilterDialog.java (revision 31635)
@@ -0,0 +1,440 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui.metaaudit;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import javax.swing.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.gui.GLIButton;
+import org.greenstone.gatherer.gui.SimpleMenuBar;
+import org.greenstone.gatherer.gui.ModalDialog;
+
+/** The autofilter concept comes from Microsoft Excel spreadsheets that use autofilters to filter to the sheet. When you click on the heading of a column, a new prompt allows you to specify what filter should be acting apon the selected column. Any new filter is conjoined with any previous filters to provide a sheet containing only rows that match all current filters. Each column must also provide an indicator for determining if a filter is set (in this case a special icon) and and a method for removing a filter (use the clear filter button within the autofilter dialog). Having recently discovered that most JVMs aren't very good at recoving memory used by dialog and frame windows, special care must be made to deallocate all references properly, as a user may open several dozen autofilter prompts over the lifetime of a session.
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.3
+ */
+public final class AutofilterDialog
+ extends ModalDialog {
+ /** The filter being edited. */
+ private Autofilter filter;
+ /** A reference to ourselves so our inner classes can reference us. */
+ private AutofilterDialog self;
+
+ private boolean cancelled;
+ /** The value returned from the filter dialog prompt. Used to determine if a prompt was set or unset, and what subsequent action to take. */
+ private byte return_value = 0;
+ /** The button used to cancel the prompt. */
+ private JButton cancel_button = null;
+ private JButton remove_button = null;
+ private JButton set_button = null;
+ /** Used to specify that the given filters should be applied conjunctly. */
+ private JRadioButton and_radiobutton = null;
+ /** The check box used to specify whether the first filter is case sensitive. */
+ private JCheckBox first_case = null;
+ /** Used to disable the second filter. */
+ private JRadioButton none_radiobutton = null;
+ /** Used to specify that the given filters should be applied disjunctly. */
+ private JRadioButton or_radiobutton = null;
+ /** The check box used to specify whether the second filter is case sensitive. */
+ private JCheckBox second_case = null;
+ /** Used to specify the order of the resulting set: Ascending or Descending. */
+ private JRadioButton ascending_radiobutton = null;
+ private JRadioButton descending_radiobutton = null;
+ /** The method used to match the first filter: Contains, Doesn't contain etc. */
+ private JComboBox first_method = null;
+ /** The value to be matched for the first filter. */
+ private JComboBox first_value = null;
+ /** The method used to match the first filter. Options as for the first method. */
+ private JComboBox second_method = null;
+ /** The value to be matched for the second filter. */
+ private JComboBox second_value = null;
+ /** Used for the most basic filter where an Equals, Case sensitive method is automatically used. */
+ private JComboBox value = null;
+ /** The label which displays the name of the currently selected column (the column that will be associated with the autofilter this dialog produces). */
+ private JLabel name;
+ /** The autofilter prompt contains two different panes, one for basic functionality and one for advanced. This containiner component is used to allow access to each via a 'tabbed' interface. */
+ private JTabbedPane control = null;
+ /** A reference back to the MetaAudit dialog that spawned this prompt. Used to make sure that any open dialog window is always in front of the audit pane. */
+ private MetaAuditFrame dialog;
+ /** The default size for the autofilter control. */
+ static final private Dimension SIZE = new Dimension(640, 245);
+
+ /** Constructor.
+ * @param dialog A reference to the MetaAuditFrame that spawned this dialog.
+ * @see org.greenstone.gatherer.gui.metaaudit.Autofilter
+ * @see org.greenstone.gatherer.gui.metaaudit.AutofilterDialog.ButtonListener
+ * @see org.greenstone.gatherer.gui.metaaudit.AutofilterDialog.CheckListener
+ */
+ public AutofilterDialog(MetaAuditFrame dialog) {
+ super(Gatherer.g_man);
+ this.dialog = dialog;
+ this.self = this;
+ this.setComponentOrientation(Dictionary.getOrientation());
+ setModal(true);
+ setJMenuBar(new SimpleMenuBar("reviewingmetadata"));
+ setSize(SIZE);
+ setTitle(Dictionary.get("Autofilter.Title"));
+
+ // Creation
+ JPanel content_pane = (JPanel) getContentPane();
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+ JPanel name_pane = new JPanel();
+ name_pane.setComponentOrientation(Dictionary.getOrientation());
+ JLabel name_label = new JLabel(Dictionary.get("Autofilter.Name"));
+ name_label.setComponentOrientation(Dictionary.getOrientation());
+ JTextField name_template = new JTextField();
+ name_template.setComponentOrientation(Dictionary.getOrientation());
+ name = new JLabel();
+ name.setComponentOrientation(Dictionary.getOrientation());
+ name.setBorder(name_template.getBorder());
+ control = new JTabbedPane();
+ control.setComponentOrientation(Dictionary.getOrientation());
+ JPanel value_pane = new JPanel();
+ value_pane.setComponentOrientation(Dictionary.getOrientation());
+ JPanel inner_value_pane = new JPanel();
+ inner_value_pane.setComponentOrientation(Dictionary.getOrientation());
+ JLabel value_label = new JLabel(Dictionary.get("Autofilter.eqeq"));
+ value_label.setComponentOrientation(Dictionary.getOrientation());
+ value = new JComboBox();
+ value.setComponentOrientation(Dictionary.getOrientation());
+ value.setOpaque(false);
+ value.setEditable(false);
+ JPanel custom_pane = new JPanel();
+ custom_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JPanel first_pane = new JPanel();
+ first_pane.setComponentOrientation(Dictionary.getOrientation());
+ first_method = new JComboBox();
+ first_method.setComponentOrientation(Dictionary.getOrientation());
+ first_method.setOpaque(false);
+ first_value = new JComboBox();
+ first_value.setComponentOrientation(Dictionary.getOrientation());
+ first_value.setOpaque(false);
+ first_value.setEditable(true);
+ first_value.addItem("");
+ first_value.setSelectedItem("");
+ first_case = new JCheckBox(Dictionary.get("Autofilter.Case_Sensitive"));
+ first_case.setComponentOrientation(Dictionary.getOrientation());
+ JPanel operator_pane = new JPanel();
+ operator_pane.setComponentOrientation(Dictionary.getOrientation());
+ JLabel operator_label = new JLabel(Dictionary.get("Autofilter.Operator"));
+ operator_label.setComponentOrientation(Dictionary.getOrientation());
+ ButtonGroup operator_group = new ButtonGroup();
+ and_radiobutton = new JRadioButton(Dictionary.get("Autofilter.AND"));
+ and_radiobutton.setComponentOrientation(Dictionary.getOrientation());
+ and_radiobutton.setOpaque(false);
+ none_radiobutton = new JRadioButton(Dictionary.get("Autofilter.None"));
+ none_radiobutton.setComponentOrientation(Dictionary.getOrientation());
+ none_radiobutton.setOpaque(false);
+ none_radiobutton.setSelected(true);
+ or_radiobutton = new JRadioButton(Dictionary.get("Autofilter.OR"));
+ or_radiobutton.setComponentOrientation(Dictionary.getOrientation());
+ or_radiobutton.setOpaque(false);
+ operator_group.add(none_radiobutton);
+ operator_group.add(and_radiobutton);
+ operator_group.add(or_radiobutton);
+
+ JPanel second_pane = new JPanel();
+ second_pane.setComponentOrientation(Dictionary.getOrientation());
+ second_method = new JComboBox();
+ second_method.setComponentOrientation(Dictionary.getOrientation());
+ second_method.setOpaque(false);
+ second_method.setEnabled(false);
+ second_value = new JComboBox();
+ second_value.setComponentOrientation(Dictionary.getOrientation());
+ second_value.setOpaque(false);
+ second_value.setEditable(true);
+ second_value.setEnabled(false);
+ second_value.addItem("");
+ second_value.setSelectedItem("");
+ second_case = new JCheckBox(Dictionary.get("Autofilter.Case_Sensitive"));
+ second_case.setComponentOrientation(Dictionary.getOrientation());
+ JPanel lower_pane = new JPanel();
+ lower_pane.setComponentOrientation(Dictionary.getOrientation());
+ JPanel order_pane = new JPanel();
+ order_pane.setComponentOrientation(Dictionary.getOrientation());
+ order_pane.setToolTipText(Dictionary.get("Autofilter.Order_Tooltip"));
+ JLabel order_label = new JLabel(Dictionary.get("Autofilter.Order"));
+ order_label.setComponentOrientation(Dictionary.getOrientation());
+ ButtonGroup order_group = new ButtonGroup();
+ ascending_radiobutton = new JRadioButton(Dictionary.get("Autofilter.Ascending"));
+ ascending_radiobutton.setComponentOrientation(Dictionary.getOrientation());
+ ascending_radiobutton.setOpaque(false);
+ ascending_radiobutton.setSelected(true);
+ descending_radiobutton = new JRadioButton(Dictionary.get("Autofilter.Descending"));
+ descending_radiobutton.setComponentOrientation(Dictionary.getOrientation());
+ descending_radiobutton.setOpaque(false);
+ order_group.add(ascending_radiobutton);
+ order_group.add(descending_radiobutton);
+
+ // Assign values to method comboboxes.
+ for(int i = 0; i < Autofilter.METHOD_LIST.length; i++) {
+ first_method.addItem(Dictionary.get(Autofilter.METHOD_LIST[i]));
+ second_method.addItem(Dictionary.get(Autofilter.METHOD_LIST[i]));
+ }
+ JPanel button_pane = new JPanel();
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+ cancel_button = new GLIButton(Dictionary.get("General.Cancel"), Dictionary.get("General.Pure_Cancel_Tooltip"));
+ remove_button = new GLIButton(Dictionary.get("Autofilter.Remove"), Dictionary.get("Autofilter.Remove_Tooltip"));
+ set_button = new GLIButton(Dictionary.get("Autofilter.Set"), Dictionary.get("Autofilter.Set_Tooltip"));
+
+ // Connection
+ and_radiobutton.addActionListener(new CheckListener(true));
+ none_radiobutton.addActionListener(new CheckListener(false));
+ or_radiobutton.addActionListener(new CheckListener(true));
+ cancel_button.addActionListener(new ButtonListener()); // It returns any currently set filter
+ remove_button.addActionListener(new ButtonListener(false));
+ set_button.addActionListener(new ButtonListener(true));
+
+ // Layout
+ name_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5));
+
+ name_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
+ name_pane.setLayout(new BorderLayout());
+ name_pane.add(name_label, BorderLayout.LINE_START);
+ name_pane.add(name, BorderLayout.CENTER);
+
+ value_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5));
+
+ inner_value_pane.setLayout(new BorderLayout());
+ inner_value_pane.add(value_label, BorderLayout.LINE_START);
+ inner_value_pane.add(value, BorderLayout.CENTER);
+
+ value_pane.setBorder(BorderFactory.createEmptyBorder(5,10,5,10));
+ value_pane.setLayout(new BorderLayout());
+ value_pane.add(inner_value_pane, BorderLayout.NORTH);
+
+ first_pane.setLayout(new BorderLayout());
+ first_pane.add(first_method, BorderLayout.LINE_START);
+ first_pane.add(first_value, BorderLayout.CENTER);
+ first_pane.add(first_case, BorderLayout.LINE_END);
+
+ operator_pane.setLayout(new GridLayout(1,4));
+ operator_pane.add(operator_label);
+ operator_pane.add(none_radiobutton);
+ operator_pane.add(and_radiobutton);
+ operator_pane.add(or_radiobutton);
+
+ second_pane.setLayout(new BorderLayout());
+ second_pane.add(second_method, BorderLayout.LINE_START);
+ second_pane.add(second_value, BorderLayout.CENTER);
+ second_pane.add(second_case, BorderLayout.LINE_END);
+
+ order_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
+ order_pane.setLayout(new GridLayout(1,2));
+ order_pane.add(order_label);
+ order_pane.add(ascending_radiobutton);
+ order_pane.add(descending_radiobutton);
+
+ custom_pane.setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
+ custom_pane.setLayout(new GridLayout(3,1));
+ custom_pane.add(first_pane);
+ custom_pane.add(operator_pane);
+ custom_pane.add(second_pane);
+
+ control.add(Dictionary.get("Autofilter.Filter_By_Value"), value_pane);
+ control.add(Dictionary.get("Autofilter.Custom_Filter"), custom_pane);
+
+ button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
+ button_pane.setLayout(new GridLayout(1, 3));
+ button_pane.add(set_button);
+ button_pane.add(remove_button);
+ button_pane.add(cancel_button);
+
+ lower_pane.setLayout(new BorderLayout());
+ lower_pane.add(order_pane, BorderLayout.CENTER);
+ lower_pane.add(button_pane, BorderLayout.SOUTH);
+
+ content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ content_pane.setLayout(new BorderLayout());
+ content_pane.add(name_pane, BorderLayout.NORTH);
+ content_pane.add(control, BorderLayout.CENTER);
+ content_pane.add(lower_pane, BorderLayout.SOUTH);
+
+ Dimension screen_size = Configuration.screen_size;
+ setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
+ screen_size = null;
+ }
+
+ public boolean cancelled() {
+ return cancelled;
+ }
+
+ /** Destructor. */
+ public void destroy() {
+ dispose();
+ first_case = null;
+ second_case = null;
+ first_method = null;
+ first_value = null;
+ second_method = null;
+ second_value = null;
+ value = null;
+ control = null;
+ dialog = null;
+ self = null;
+ name = null;
+ }
+ /** Display the modal dialog box, allowing the user to define the filter. When the user presses one of the buttons, dispose and return to the caller providing an indication of which button was pressed.
+ * @return An int which indicates which button was pressed to dismiss the dialog.
+ */
+ public Autofilter display(Autofilter filter, ArrayList raw_values, String column_name) {
+ this.cancelled = false;
+ this.filter = filter;
+ name.setText(column_name);
+ // Prune values so it only contains unique entries, then order.
+ TreeSet values = new TreeSet(raw_values);
+ value.setModel(new DefaultComboBoxModel(values.toArray()));
+ String star = "*";
+ value.insertItemAt(star, 0);
+ value.setSelectedItem(star);
+ first_value.setModel(new DefaultComboBoxModel(values.toArray()));
+ second_value.setModel(new DefaultComboBoxModel(values.toArray()));
+ // Restore previous values.
+ if(filter != null && filter.value_one != null) {
+ value.setSelectedItem(filter.value_one);
+ first_method.setSelectedIndex(filter.method_one);
+ first_value.setSelectedItem(filter.value_one);
+ first_case.setSelected(filter.casesense_one);
+ if (filter.value_two == null) {
+ none_radiobutton.setSelected(true);
+ }
+ else {
+ if (filter.operation) {
+ and_radiobutton.setSelected(true);
+ }
+ else {
+ or_radiobutton.setSelected(true);
+ }
+ second_method.setSelectedIndex(filter.method_two);
+ second_value.setSelectedItem(filter.value_two);
+ second_case.setSelected(filter.casesense_two);
+ }
+ if (filter.sort) {
+ ascending_radiobutton.setSelected(true);
+ // order.setSelectedIndex(0);
+ }
+ else {
+ descending_radiobutton.setSelected(true);
+ // order.setSelectedIndex(1);
+ }
+ }
+ // Display
+ setVisible(true);
+ dialog.toFront();
+ return this.filter;
+ }
+
+ /** Listens for actions on the button it is attached to, and when notified sets the return_value and disposes of the dialog. */
+ private final class ButtonListener
+ implements ActionListener {
+ private boolean cancel_button;
+ private boolean return_filter;
+
+ /** Default constructor. Returns any filter that was already set */
+ public ButtonListener() {
+ this.cancel_button = true;
+ }
+
+ /** Does an action on this button cause a filter to be returned. */
+ /** Constructor takes an associated return value as an argument.
+ * @param return_filter true if we update then return the filter, false to clear existing filter.
+ */
+ public ButtonListener(boolean return_filter) {
+ this.cancel_button = false;
+ this.return_filter = return_filter;
+ }
+ /** When any registered component is actioned apon, set the value and hide the dialog. We hide rather than dispose, because hide assures the data values will be retained.
+ * @param event An ActionEvent containing information about the action that caused this method call.
+ * @see org.greenstone.gatherer.gui.metaaudit.Autofilter
+ */
+ public void actionPerformed(ActionEvent event) {
+ if(cancel_button) {
+ cancelled = true;
+ }
+ else if(return_filter) {
+ if(control.getSelectedIndex() == 0) {
+ filter.setFilter(1, 0, (String)value.getSelectedItem(), true);
+ filter.setFilter(2, 0, null, true);
+ }
+ else {
+ filter.setFilter(1, first_method.getSelectedIndex(), (String)first_value.getSelectedItem(), first_case.isSelected());
+ if(!none_radiobutton.isSelected()) {
+ if (and_radiobutton.isSelected()) {
+ filter.setOperation(Autofilter.AND);
+ }
+ else {
+ filter.setOperation(Autofilter.OR);
+ }
+ filter.setFilter(2, second_method.getSelectedIndex(), (String)second_value.getSelectedItem(), second_case.isSelected());
+ }
+ }
+ if (ascending_radiobutton.isSelected()) {
+ filter.setSort(Autofilter.ASCENDING);
+ }
+ else {
+ filter.setSort(Autofilter.DESCENDING);
+ }
+ }
+ else {
+ filter = null;
+ }
+ setVisible(false);
+ }
+ }
+ /** Listens for actions on the check box it is attached to, and when notified sets the state of the second method and value to the specified state. */
+ private final class CheckListener
+ implements ActionListener {
+ /** The default desire state any check button this listens to. */
+ private boolean desired_state = false;
+ /** The constructor takes an associated desired state.
+ * @param desired_state The state that should be set when this is actioned, as a boolean .
+ */
+ public CheckListener(boolean desired_state) {
+ this.desired_state = desired_state;
+ }
+ /** Whenever our registered components are actioned apon, set the state of the second method and value to the desired state. */
+ public void actionPerformed(ActionEvent event) {
+ second_method.setEnabled(desired_state);
+ second_value.setEnabled(desired_state);
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/metaaudit/Filter.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/metaaudit/Filter.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/metaaudit/Filter.java (revision 31635)
@@ -0,0 +1,151 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui.metaaudit;
+
+/**************************************************************************************
+ * Title: Gatherer
+ * Description: The Gatherer: a tool for gathering and enriching a digital collection.
+ * Company: The University of Waikato
+ * Written: 03/09/02
+ * Revised: 04/10/02 - Commented
+ **************************************************************************************/
+import de.qfs.lib.gui.TableModelFilter;
+import de.qfs.lib.gui.TableModelFilterEvent;
+import de.qfs.lib.gui.TableModelFilterListener;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Vector;
+import javax.swing.table.TableModel;
+
+/** This class essentially manages the autofilters applied to the metaaudit table.
+ * @author John Thompson
+ * @version 2.3
+ */
+public class Filter
+ implements TableModelFilter {
+ /** An array of autofilters. */
+ private Autofilter filters[];
+ /** The registered TableModelFilterListeners. */
+ private Vector listeners = new Vector ();
+ /** Constructor.
+ * @param columns The number of columns this Filter will be expected to filter, as an int .
+ */
+ public Filter(int columns) {
+ filters = new Autofilter[columns];
+ }
+ /** Add a TableModelFilterListener to the TableModelFilter.
+ * @param listener The TableModelFilterListener to add, if it isn't already registered.
+ */
+ public synchronized void addTableModelFilterListener(TableModelFilterListener listener) {
+ if (!listeners.contains(listener)) {
+ listeners.addElement(listener);
+ }
+ }
+ /** Remove a filter from a column.
+ * @param column The column number to remove the filter from, as an int .
+ */
+ public void clearFilter(int column) {
+ if(filters[column] != null) {
+ filters[column].setActive(false);
+ }
+ }
+
+
+ /** Determine if a certain row should be shown in the table by checking it against all the current filters.
+ * @param model The TableModel the row is from.
+ * @param row The row number as an int
+ * @return true if the rows data matches all autofilters set and should be displayed, false otherwise.
+ */
+ public boolean filter(TableModel model, int row)
+ {
+ for (int i = 0; i < filters.length; i++) {
+ if (filters[i] != null && filters[i].active()) {
+ ArrayList values = (ArrayList) model.getValueAt(row, i);
+ if (filters[i].filter(values) == false) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+
+ /** Retrieve the autofilter associated with a certain column.
+ * @param column The column number as an int .
+ * @return The Autofilter assigned to that column.
+ */
+ public Autofilter getFilter(int column) {
+ Autofilter filter = filters[column];
+ if(filter == null) {
+ filter = new Autofilter();
+ filters[column] = filter;
+ }
+ return filter;
+ }
+ /** Determine if a certain column is filtered.
+ * @param column The column number as an int .
+ * @return true if there is an active autofilter assigned to this column, false otherwise.
+ */
+ public boolean isFiltered(int column) {
+ boolean result = false;
+ if(filters[column] != null) {
+ result = filters[column].active();
+ }
+ return result;
+ }
+ /** Remove a TableModelFilterListener from the TableModelFilter.
+ * @param listener The TableModelFilterListener to remove.
+ */
+ public synchronized void removeTableModelFilterListener(TableModelFilterListener listener) {
+ listeners.removeElement(listener);
+ }
+ /** Called whenever the filters assigned changes significantly, thus prompting a reload of the table model data. */
+ public void fireFilterChanged() {
+ ///ystem.err.println("Filter Changed!");
+ Vector tmp;
+ synchronized(this) {
+ tmp = (Vector) listeners.clone();
+ }
+
+ TableModelFilterEvent event = new TableModelFilterEvent (this);
+ Enumeration enumeration = tmp.elements();
+ while (enumeration.hasMoreElements()) {
+ ((TableModelFilterListener) enumeration.nextElement()).filterChanged(event);
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/metaaudit/HeaderListener.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/metaaudit/HeaderListener.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/metaaudit/HeaderListener.java (revision 31635)
@@ -0,0 +1,79 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui.metaaudit;
+
+
+import de.qfs.lib.gui.TableModelSorter;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import org.greenstone.gatherer.metadata.MetadataAuditTableModel;
+
+public class HeaderListener
+ extends MouseAdapter {
+ private MetaAuditFrame parent_frame;
+ private MetaAuditTable table;
+ public HeaderListener(MetaAuditFrame parent_frame, MetaAuditTable table) {
+ this.parent_frame = parent_frame;
+ this.table = table;
+ }
+
+ public void mouseClicked(MouseEvent event) {
+ // Determine the column this was clicked on.
+ int column = table.getColumnModel().getColumnIndexAtX(event.getX());
+ int clicked_column = table.convertColumnIndexToModel(column);
+ // Display the currently assigned filter.
+ Filter filter_model = table.getFilter();
+ Autofilter filter = filter_model.getFilter(clicked_column);
+ MetadataAuditTableModel model = table.getOriginalModel();
+ ArrayList default_values = model.getColumnValues(clicked_column);
+ String column_name = model.getColumnName(clicked_column);
+ filter = parent_frame.autofilter_dialog.display(filter, default_values, column_name);
+ if(!parent_frame.autofilter_dialog.cancelled()) {
+ if(filter == null) {
+ filter_model.clearFilter(clicked_column);
+ }
+ else {
+ filter.setActive(true);
+ TableModelSorter sorter = table.getSorter();
+ sorter.setSortColumn(clicked_column);
+ sorter.setSortAscending(filter.sort);
+ }
+ filter_model.fireFilterChanged();
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/metaaudit/HeaderRenderer.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/metaaudit/HeaderRenderer.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/metaaudit/HeaderRenderer.java (revision 31635)
@@ -0,0 +1,79 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui.metaaudit;
+
+import java.awt.*;
+import javax.swing.*;
+import javax.swing.table.TableCellRenderer;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.util.JarTools;
+
+/** This custom button renderer replaces the boring old column headers with clickable versions which are based on JToggleButtons.
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.3
+ */
+public class HeaderRenderer
+ extends JToggleButton
+ implements TableCellRenderer {
+
+ /** The constructor sets up a margin around the button, and adds the filter icons.
+ */
+ public HeaderRenderer() {
+ setMargin(new Insets(0,0,0,0));
+ setIcon(JarTools.getImage("filter.gif"));
+ setSelectedIcon(JarTools.getImage("filter-on.gif"));
+ }
+
+ /** Called to create the component to be used as the 'rubber-stamp' for the table cell given the follwing arguments.
+ * @param table The JTable that is asking the renderer to draw; can be null .
+ * @param value The value of the cell to be rendered as an Object . It is up to the specific renderer to interpret and draw the value. For example, if value is the string "true", it could be rendered as a string or it could be rendered as a check box that is checked. null is a valid value.
+ * @param isSelected true if the cell is to be rendered with the selection highlighted; otherwise false .
+ * @param hasFocus If true , render cell appropriately. For example, put a special border on the cell, if the cell can be edited, render in the color used to indicate editing.
+ * @param row The row index of the cell being drawn as an int . When drawing the header, the value of row is -1.
+ * @param column The column index of the cell being drawn as an int .
+ * @see org.greenstone.gatherer.gui.metaaudit.MetaAuditTable
+ */
+ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
+ setText(value.toString());
+ setToolTipText(Dictionary.get("Autofilter.Table_Header_Tooltip", value.toString()));
+ MetaAuditTable temp = (MetaAuditTable) table;
+ if (temp != null) { // And it may if this is called during table init.
+ setSelected(temp.isFiltered(column));
+ }
+ return this;
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/metaaudit/MetaAuditFrame.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/metaaudit/MetaAuditFrame.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/metaaudit/MetaAuditFrame.java (revision 31635)
@@ -0,0 +1,216 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui.metaaudit;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.tree.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.collection.CollectionTreeNode;
+import org.greenstone.gatherer.gui.GLIButton;
+import org.greenstone.gatherer.gui.SimpleMenuBar;
+import org.greenstone.gatherer.gui.ModalDialog;
+
+
+/** The MetaAuditFrame provides a table view of all of the metadata assigned to a selection of CollectionTreeNodes. All values for a certain file and a certain metadata element appear in the same cell. This table can be sorted by any column, and also has a MS Excel-like AutoFilter allowing you to restrict the rows visible to only those that match a certain set of criteria (applied to each column, and then 'ANDED', or cojoined, to determine the filter). Finally this dialog does not block the Gatherer tool, so the file selection can be changed and the dialog will just generate a new table dynamically.
+ * Much effort has gone into optimizing this table, as it quickly becomes slow and unresponsive to build/filter/sort when the number of records selected is high. However its performance is still nowhere as good as the Excel spreadsheet, and selections of 1000+ records can cause some serious waiting problems. Performance progression, shown in terms of time taken to perform action, are shown below. Note that building includes both the creation of the data model, and/or the time taken to lay out row and column sizes (due to this using multiple entry cells). Also all tables, by default, must be sorted by one column, which is initially the first column in ascending order:
+ *
+ * Time Taken with 1000 records (ms)
+ * Action Original Revision 1 Revision 2 Revision 3
+ * Build 113013 1500 1280 665
+ * Sort 34228 353 341 338
+ * Filter 57110 485 485 485
+ *
+ *
+ *
+ * Time Taken with 10,000 records (ms) [hdlsub]
+ * Action Original Revision 1 Revision 2 Revision 3
+ * Build DNF 8667 6784 3081
+ * Sort DNF 236 241 228
+ * Filter DNF 59272 59767 59614
+ *
+ *
+ *
+ * Time Taken with 30,000 records (ms) [hdl]
+ * Action Original Revision 1 Revision 2 Revision 3
+ * Build DNF 19037 16478 8111
+ * Sort DNF 314 301 385
+ * Filter DNF [300000] [300000] [300000]
+ *
+ * It is easy to see that while building and sorting have become quite fast and reasonable, sorting is still unacceptable in terms of performance. The difficulty is this, that while sorting is easy (and fast) to implement via the Decorator pattern (making the overall table model a MDVC one) it requires the entire contents of a column to be available. Thus no matter the speed of your sort algorithm you will end up making N calls to getValueAt() in the table data model, for N records. This is not a problem for the initial model, hence the small sort times. However when you attempt to place another deocrator model (or modify the existing one) in order to filter only selected rows, even if the filtering itself takes little time, this little time quickly grows to unacceptable levels. In fact testing has shown it to grow much faster than linear time (though I'm not sure where). Doubling the number of records to filter increases the processing time by 5-7 times.
+ * In attempting to solve this problem, I have found many resources that mention a similar problem when trying to build visual representations of database tables, especially over networks or other temporally-non-deterministic connections. The common solution is to 'page' only those records necessary into memory on demand, thus making initial response significantly faster, while paying a small price for subsequent page seeks. There are even studies and algorithms for determining pages loads etc, I would imagine garnered from the memory management community. However none of these really help me usless I can get around the problem of having to have the entire columns population immeditaly accessable for sorting purposes.
+ * I believe with more time is would be possible to make a hybrid table model with properties such as you would require for the aforemenetioned database application, but which also provides fast methods for determining the highest and lowest records perhaps by building traversable heaps in the original model. Such a solution would incur a build penalty, and could consume significant memory, but would provide ordered and sequential access to the data within.
+ * The discussion aside, I have run out of time to do anything further on this, so will include as is, and provide a warning dialog whenever the number of records might suffer slow processing (1000+ records).
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.3
+ */
+public class MetaAuditFrame
+ extends ModalDialog
+ implements TreeSelectionListener {
+ public AutofilterDialog autofilter_dialog;
+ /** Whether the selection has changed since the last time we built the model (because it has been hidden and theres no point refreshing a model you can't see!). */
+ private boolean invalid = true;
+ /** An array holding the most recent list of paths selected (plus some nulls for those paths removed). */
+ private CollectionTreeNode[] records = null;
+ /** A reference to ourselves so that inner classes can interact with us. */
+ private MetaAuditFrame self;
+ /** The table in which the metadata is shown. */
+ private MetaAuditTable table;
+ /** The default size for the metaaudit dialog. */
+ static final private Dimension SIZE = new Dimension(640,505);
+
+
+ public MetaAuditFrame()
+ {
+ super(Gatherer.g_man);
+
+ // Arguments
+ this.autofilter_dialog = new AutofilterDialog(this);
+ this.self = this;
+ this.table = new MetaAuditTable(this);
+ this.setComponentOrientation(Dictionary.getOrientation());
+ // Creation
+ setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);
+ setSize(SIZE);
+ setTitle(Dictionary.get("MetaAudit.Title"));
+ setJMenuBar(new SimpleMenuBar("reviewingmetadata"));
+
+ JPanel content_pane = (JPanel) getContentPane();
+ content_pane.setComponentOrientation(Dictionary.getOrientation());
+ JPanel button_pane = new JPanel();
+ button_pane.setComponentOrientation(Dictionary.getOrientation());
+
+ JButton close_button = new GLIButton(Dictionary.get("MetaAudit.Close"), Dictionary.get("MetaAudit.Close_Tooltip"));
+
+ // Connection
+ close_button.addActionListener(new CloseListener());
+ Gatherer.c_man.getCollectionTree().addTreeSelectionListener(this);
+
+ // Layout
+ button_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ button_pane.setLayout(new BorderLayout());
+ button_pane.add(close_button, BorderLayout.CENTER);
+
+ content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ content_pane.setLayout(new BorderLayout());
+ content_pane.add(new JScrollPane(table), BorderLayout.CENTER);
+ content_pane.add(button_pane, BorderLayout.SOUTH);
+
+ Dimension screen_size = Configuration.screen_size;
+ setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
+ screen_size = null;
+ close_button = null;
+ button_pane = null;
+ content_pane = null;
+ }
+
+ /** Destructor. */
+ public void destroy() {
+ records = null;
+ self = null;
+ table = null;
+ }
+
+ /** Display the dialog on screen. */
+ public void display() {
+ if(invalid) {
+ rebuildModel();
+ }
+ setVisible(true);
+ }
+
+ /** This method is called whenever the selection within the collection tree changes. This causes the table to be rebuilt with a new model.
+ * @param event A TreeSelectionEvent containing information about the event.
+ */
+ public void valueChanged(TreeSelectionEvent event) {
+ Object source = event.getSource();
+ if(source instanceof JTree) {
+ TreePath paths[] = ((JTree)source).getSelectionPaths();
+ if(paths != null) {
+ records = new CollectionTreeNode[paths.length];
+ for(int i = 0; i < paths.length; i++) {
+ records[i] = (CollectionTreeNode) paths[i].getLastPathComponent();
+ }
+ if(isVisible()) {
+ rebuildModel();
+ }
+ else {
+ invalid = true;
+ }
+ }
+ }
+ }
+
+ public void wait(boolean waiting) {
+ Component glass_pane = getGlassPane();
+ if(waiting) {
+ // Show wait cursor.
+ glass_pane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+ glass_pane.setVisible(true);
+ }
+ else {
+ // Hide wait cursor.
+ glass_pane.setVisible(false);
+ glass_pane.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+ }
+ glass_pane = null;
+ }
+
+ /** Rebuild the metaaudit table model using the current collection tree selection.
+ */
+ private void rebuildModel() {
+ // Build and set model
+ table.newModel(records);
+ // Done.
+ invalid = false;
+ }
+
+ /** Listens for actions upon the close button, and if detected closes the dialog */
+ private class CloseListener
+ implements ActionListener {
+ /** Any implementation of ActionListener must include this method so that we can be informed when an action has been performed on our target control(s).
+ * @param event An ActionEvent containing information about the event.
+ */
+ public void actionPerformed(ActionEvent event) {
+ self.setVisible(false);
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/metaaudit/MetaAuditRenderer.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/metaaudit/MetaAuditRenderer.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/metaaudit/MetaAuditRenderer.java (revision 31635)
@@ -0,0 +1,63 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui.metaaudit;
+
+import java.awt.*;
+import java.util.ArrayList;
+import javax.swing.*;
+import javax.swing.table.DefaultTableCellRenderer;
+
+public class MetaAuditRenderer
+ extends DefaultTableCellRenderer {
+
+ public Component getTableCellRendererComponent(JTable table, Object value, boolean is_selected, boolean has_focus, int row, int column) {
+ ArrayList array = (ArrayList) value;
+ JLabel label = (JLabel) super.getTableCellRendererComponent(table, "", is_selected, has_focus, row, column);
+ JPanel panel = new JPanel();
+ panel.setBackground(label.getBackground());
+ panel.setBorder(label.getBorder());
+ if(array != null) {
+ panel.setLayout(new GridLayout(array.size(), 1));
+ for(int i = 0; i < array.size(); i++) {
+ JLabel entry = new JLabel(array.get(i).toString());
+ entry.setFont(label.getFont());
+ panel.add(entry);
+ }
+ }
+ return panel;
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/metaaudit/MetaAuditTable.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/metaaudit/MetaAuditTable.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/metaaudit/MetaAuditTable.java (revision 31635)
@@ -0,0 +1,200 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui.metaaudit;
+
+import de.qfs.lib.gui.*;
+import java.awt.*;
+import java.util.ArrayList;
+import javax.swing.JTable;
+import javax.swing.event.TableModelEvent;
+import javax.swing.table.*;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.collection.CollectionTreeNode;
+import org.greenstone.gatherer.metadata.MetadataAuditTableModel;
+
+
+/**
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.3
+ */
+public class MetaAuditTable
+ extends JTable
+ implements TableModelSorterListener {
+
+ private boolean initial_state;
+
+ private Filter filter;
+
+ private HeaderRenderer header_renderer;
+
+ private MetaAuditFrame parent_frame;
+
+ private MetaAuditRenderer cell_renderer;
+
+ private MetadataAuditTableModel metadata_audit_table_model;
+
+ private SortedTableHelper helper;
+
+ static final private int MARGIN = 10;
+
+ public MetaAuditTable(MetaAuditFrame parent_frame) {
+ super();
+ this.setComponentOrientation(Dictionary.getOrientation());
+ this.cell_renderer = new MetaAuditRenderer();
+ this.header_renderer = new HeaderRenderer();
+ this.parent_frame = parent_frame;
+ getTableHeader().addMouseListener(new HeaderListener(parent_frame, this));
+ setAutoResizeMode(AUTO_RESIZE_OFF);
+ }
+
+ public Filter getFilter() {
+ return filter;
+ }
+
+
+ public MetadataAuditTableModel getOriginalModel()
+ {
+ return metadata_audit_table_model;
+ }
+
+
+ public TableModelSorter getSorter() {
+ return helper.getTableModelSorter();
+ }
+
+ public boolean isFiltered(int column) {
+ if(filter == null) {
+ return false;
+ }
+ return filter.isFiltered(column);
+ }
+
+ public void newModel(CollectionTreeNode records[]) {
+ if(records == null || records.length == 0) {
+ setModel(new DefaultTableModel());
+ return;
+ }
+ wait(true);
+ initial_state = true;
+ // System.err.println("Build new Model - should only be called once per click!");
+ metadata_audit_table_model = new MetadataAuditTableModel();
+ metadata_audit_table_model.rebuild(records);
+ setModel(metadata_audit_table_model);
+ filter = new Filter(metadata_audit_table_model.getColumnCount());
+ helper = new SortedTableHelper (this, filter, new DefaultTableModelSorter(), header_renderer, null);
+ helper.prepareTable();
+ // Add renderer to columns.
+ TableColumnModel column_model = getColumnModel();
+ for(int i = 0; i < column_model.getColumnCount(); i++) {
+ column_model.getColumn(i).setCellRenderer(cell_renderer);
+ column_model.getColumn(i).setHeaderRenderer(header_renderer);
+ }
+ wait(false);
+ initial_state = false;
+ }
+
+ public void tableChanged(TableModelEvent e) {
+ wait(true);
+ super.tableChanged(e);
+
+ // Create storage area
+ ArrayList heights[] = new ArrayList[dataModel.getRowCount()];
+ int widths_value[] = new int[dataModel.getColumnCount()];
+ ArrayList widths[] = new ArrayList[dataModel.getColumnCount()];
+
+ // Iterate through cell values finding tallest and widest for each row and column respectively.
+ for (int j = 0; j < dataModel.getRowCount(); j++) {
+ for (int k = 0; k < dataModel.getColumnCount(); k++) {
+ Object object = dataModel.getValueAt(j, k);
+ if (object instanceof ArrayList) {
+ ArrayList data = (ArrayList) object;
+ if (heights[j] == null || data.size() > heights[j].size()) {
+ heights[j] = data;
+ }
+ String longest = "";
+ for (int i = 0; i < data.size(); i++) {
+ String temp = data.get(i).toString();
+ if (temp.length() > longest.length()) {
+ longest = temp;
+ }
+ }
+ if (widths[k] == null || longest.length() > widths_value[k]) {
+ widths_value[k] = longest.length();
+ widths[k] = data;
+ }
+ }
+ }
+ }
+
+ // Now assign the dimensions we have determined to be the largest.
+ for (int l = 0; l < heights.length; l++) {
+ if (heights[l] != null) {
+ Component component = cell_renderer.getTableCellRendererComponent(this, heights[l], false, false, 0, 0);
+ Dimension size = component.getPreferredSize();
+ setRowHeight(l, size.height);
+ }
+ }
+ for (int m = 0; m < widths.length; m++) {
+ if (widths[m] != null) {
+ Component component = cell_renderer.getTableCellRendererComponent(this, widths[m], false, false, 0, 0);
+ Dimension size = component.getPreferredSize();
+ int width = size.width + MARGIN;
+ if (width > (2 * parent_frame.getSize().width) / 3) {
+ width = (2 * parent_frame.getSize().width) / 3;
+ }
+
+ Component header = header_renderer.getTableCellRendererComponent(this, dataModel.getColumnName(m), false, false, 0, 0);
+ if (header.getPreferredSize().width + MARGIN > width) {
+ width = header.getPreferredSize().width + MARGIN;
+ }
+ columnModel.getColumn(m).setPreferredWidth(width);
+ }
+ }
+
+ wait(initial_state);
+ }
+
+
+ public void sortOrderChanged(TableModelSorterEvent event) {
+ }
+
+ private void wait(boolean waiting) {
+ if(parent_frame != null) {
+ parent_frame.wait(waiting);
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/tree/DragTree.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/tree/DragTree.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/tree/DragTree.java (revision 31635)
@@ -0,0 +1,820 @@
+package org.greenstone.gatherer.gui.tree;
+
+import java.awt.*;
+import java.awt.datatransfer.*;
+import java.awt.dnd.*;
+import java.awt.event.*;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.plaf.basic.BasicTreeUI;
+import javax.swing.tree.*;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.file.FileNode;
+import org.greenstone.gatherer.file.FileSystemModel;
+import org.greenstone.gatherer.gui.Filter;
+import org.greenstone.gatherer.util.DragComponent;
+import org.greenstone.gatherer.util.DragGroup;
+import org.greenstone.gatherer.util.DragTreeSelectionModel;
+import org.greenstone.gatherer.util.Utility;
+
+public abstract class DragTree
+ extends JTree
+ implements Autoscroll, DragGestureListener, DragSourceListener, DropTargetListener, DragComponent, TreeSelectionListener {
+ /** The normal background color. */
+ private Color background_color;
+ /** The normal foreground color. */
+ private Color foreground_color;
+ /** The Group this component belongs to. */
+ private DragGroup group;
+ /** The filter for this tree. */
+ protected Filter filter = null;
+ /** The image to use for the disabled background. */
+ private ImageIcon disabled_background;
+ /** The image to use for a normal background. */
+ private ImageIcon normal_background;
+ /** The icon to use for multiple node drag'n'drops. We decided against using the windows paradigm or a block of x horizontal lines for x files. */
+ private ImageIcon multiple_icon = new ImageIcon("resource"+File.separator+"multiple.gif");
+ /** The default drag action, although its not that important as we provide custom icons during drags. */
+ private int drag_action = DnDConstants.ACTION_MOVE;
+ /** The location of the last ghost drawn, so that we can repair the 'spoilt' area. */
+ private Point pt_last = null;
+ /** The region borderer by the lower cue line. */
+ private Rectangle lower_cue_line;
+ /** The region covered by the drag ghost icon. */
+ private Rectangle ra_ghost = new Rectangle();
+ /** The region borderer by the upper cue line. */
+ private Rectangle upper_cue_line;
+ /** The last tree path the drag was hovered over. */
+ private TreePath previous_path = null;
+
+ static private Cursor NO_DRAG_CURSOR = null;
+
+ static private final Color TRANSPARENT_COLOR = new Color(0,0,0,0);
+ /** The distance from the edge of the current view within the scroll bar which if entered causes the view to scroll. */
+ static private final int AUTOSCROLL_MARGIN = 12;
+
+ static public final int TREE_DISPLAY_CHANGED = 0;
+ static public final int LOADED_COLLECTION_CHANGED = 1;
+ static public final int COLLECTION_CONTENTS_CHANGED = 2;
+
+
+ public DragTree(TreeModel model, boolean mixed_selection)
+ {
+ super();
+
+ // For some reason these aren't set with Java 1.4.2 and the GTK look and feel?
+ if (UIManager.get("Tree.leftChildIndent") == null) {
+ UIManager.put("Tree.leftChildIndent", new Integer(7));
+ }
+ if (UIManager.get("Tree.rightChildIndent") == null) {
+ UIManager.put("Tree.rightChildIndent", new Integer(13));
+ }
+
+ init(mixed_selection);
+ if (model != null) {
+ setModel(model);
+ }
+ }
+
+ public void init(boolean mixed_selection) {
+ if (NO_DRAG_CURSOR == null) {
+ NO_DRAG_CURSOR = DragSource.DefaultMoveNoDrop;
+ }
+
+ // Init
+ this.filter = new Filter(this, null);
+
+ // Creation
+ this.putClientProperty("JTree.lineStyle", "Angled");
+ this.setAutoscrolls(true);
+ this.setEditable(false);
+ this.setLargeModel(true);
+ this.setOpaque(true);
+ this.setRootVisible(false);
+ this.setSelectionModel(new DragTreeSelectionModel(this, mixed_selection));
+ this.setShowsRootHandles(true);
+ this.setUI(new BasicTreeUI());
+
+ // Connection
+ addKeyListener(new DragTreeKeyListener());
+ addMouseListener(Gatherer.g_man.foa_listener);
+ addTreeExpansionListener(Gatherer.g_man.foa_listener);
+ addTreeSelectionListener(this);
+
+ DragTreeCellRenderer renderer = new DragTreeCellRenderer();
+ //make the renderer paint nodes as transparent when not selected
+ //renderer.setBackgroundNonSelectionColor(new Color(0,0,0,0));
+ setCellRenderer(renderer);
+ // It turns out VariableHeightLayoutCache is buggy under MacOS, so I'll force it to use FixedHeightLayoutCache. To do that I have to set a cell height, ala below, and set the large model property to true, ala above. And buggy VariableHeightLayoutCache goes away. Plus this actually provides a minor performance boost as the layout manager doesn't have to calculate new bounds each time (well... I suppose any gain is actually pretty much eclipsed by the time taken for file access while we determine what a certain nodes children are).
+ // And once we have the cell renderer, use it to determine a fixed height for the rows
+ Component prototype_row = renderer.getTreeCellRendererComponent(this, "Prototype", true, true, false, 0, true);
+ this.setRowHeight(prototype_row.getSize().height);
+ prototype_row = null;
+
+
+ // Drag'n'drop Setup
+ // Drag source setup.
+ DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(this, drag_action, this);
+ // Drop destination setup.
+ new DropTarget(this, drag_action, this, true);
+ }
+
+ // Autoscroll Interface - Scroll because the mouse cursor is in our scroll zone.
+ // The following code was borrowed from the book:
+ // Java Swing
+ // By Robert Eckstein, Marc Loy & Dave Wood
+ // Paperback - 1221 pages 1 Ed edition (September 1998)
+ // O'Reilly & Associates; ISBN: 156592455X
+ // The relevant chapter of which can be found at:
+ // http://www.oreilly.com/catalog/jswing/chapter/dnd.beta.pdf
+ // But I've probably tortured it beyond all recognition anyway.
+ public void autoscroll(Point pt) {
+ // Figure out which row we're on.
+ int row = getRowForLocation(pt.x, pt.y);
+ // If we are not on a row then ignore this autoscroll request
+ if (row < 0) return;
+ Rectangle bounds = getBounds();// Yes, scroll up one row
+ // Now decide if the row is at the top of the screen or at the bottom. We do this to make the previous row (or the next row) visible as appropriate. If we're at the absolute top or bottom, just return the first or last row respectively.
+ // Is row at top of screen?
+ if(pt.y + bounds.y <= AUTOSCROLL_MARGIN) {
+ // Yes, scroll up one row
+ if(row <= 0) {
+ row = 0;
+ }
+ else {
+ row = row - 1;
+ }
+ }
+ else {
+ // No, scroll down one row
+ if(row < getRowCount() - 1) {
+ row = row + 1;
+ }
+ }
+ this.scrollRowToVisible(row);
+ }
+
+ /** In order for the appearance to be consistant, given we may be in the situation where the pointer has left our focus but the ghost remains, this method allows other members of the GGroup to tell this component to clear its ghost.
+ */
+ public void clearGhost() {
+ // Erase the last ghost image and cue line
+ paintImmediately(ra_ghost.getBounds());
+ }
+
+ /** Any implementation of DragSourceListener must include this method so we can be notified when the drag event ends (somewhere else), which will in turn remove actions.
+ * @param event A DragSourceDropEvent containing all the information about the end of the drag event.
+ */
+ public void dragDropEnd(DragSourceDropEvent event) {
+ }
+
+ /** Any implementation of DragSourceListener must include this method so we can be notified when the drag focus enters this component.
+ * @param event A DragSourceDragEvent containing all the information
+ * about the drag event.
+ */
+ public void dragEnter(DragSourceDragEvent event) {
+ // Handled elsewhere.
+ }
+ /** Any implementation of DropTargetListener must include this method so we can be notified when the drag focus enters this component, which in this case is to grab focus from within our group.
+ * @param event A DropTargetDragEvent containing all the information about the drag event.
+ */
+ public void dragEnter(DropTargetDragEvent event) {
+ group.grabFocus(this);
+ }
+ /** Any implementation of DragSourceListener must include this method so we can be notified when the drag focus leaves this component.
+ * @param event A DragSourceEvent containing all the information about the drag event.
+ */
+ public void dragExit(DragSourceEvent event) {
+ clearGhost();
+ }
+
+ /** Any implementation of DropTargetListener must include this method
+ * so we can be notified when the drag focus leaves this component.
+ * @param event A DropTargetEvent containing all the information
+ * about the drag event.
+ */
+ public void dragExit(DropTargetEvent event) {
+ clearGhost();
+ }
+
+ /** Any implementation of DragGestureListener must include this method
+ * so we can be notified when a drag action has been noticed, thus a
+ * drag action has begun.
+ * @param event A DragGestureEvent containing all the information about
+ * the drag event.
+ */
+ public void dragGestureRecognized(DragGestureEvent event)
+ {
+ // Check that the tree is draggable
+ if (!isDraggable()) {
+ return;
+ }
+
+ // Disable editing, unless you want to have the edit box pop-up part way through dragging.
+ this.setEditable(false);
+ // We need this to find one of the selected nodes.
+ Point origin = event.getDragOrigin();
+ TreePath path = this.getPathForLocation(origin.x, origin.y);
+ // Taking into account our delayed model of selection, it is possible the user has performed a select and drag in one click. Here we utilize the Windows paradigm like so: If the node at the origin of the drag and drop is already in our selection then we perform multiple drag and drop. Otherwise we recognise that this is a distinct drag-drop and move only the origin node.
+ if(!isPathSelected(path)) {
+ ((DragTreeSelectionModel)selectionModel).setImmediate(true);
+ setSelectionPath(path);
+ ((DragTreeSelectionModel)selectionModel).setImmediate(false);
+ }
+ if(path == null) {
+ return;
+ }
+ if (!isValidDrag()) {
+ try {
+ event.startDrag(NO_DRAG_CURSOR, new StringSelection("dummy"), this);
+ //dragging = true;
+ }
+ catch(Exception error) {
+ error.printStackTrace();
+ }
+ return;
+ }
+ // Now update the selection stored as far as the group is concerned.
+ group.setSelection(getSelectionPaths());
+ group.setSource(this);
+ // First grab ghost.
+ group.grabFocus(this);
+ // Ghost Image stuff.
+ Rectangle rect = this.getPathBounds(path);
+ group.mouse_offset = new Point(origin.x - rect.x, origin.y - rect.y);
+ // Create the ghost image.
+ // Retrieve the selected files.
+ int selection_count = getSelectionCount();
+ if(selection_count > 0) {
+ JLabel label;
+ if(selection_count == 1) {
+ TreePath node_path = getSelectionPath();
+ FileNode node = (FileNode) path.getLastPathComponent();
+ label = new JLabel(node.toString(), ((DefaultTreeCellRenderer)getCellRenderer()).getLeafIcon(), JLabel.CENTER);
+ }
+ else {
+ String title = getSelectionCount() + " " + Dictionary.get("Tree.Files");
+ label = new JLabel(title, ((DefaultTreeCellRenderer)getCellRenderer()).getClosedIcon(), JLabel.CENTER);
+ title = null;
+ }
+ // The layout manager normally does this.
+ Dimension label_size = label.getPreferredSize();
+ label.setSize(label_size);
+ label.setBackground(TRANSPARENT_COLOR);
+ label.setOpaque(true);
+ // Get a buffered image of the selection for dragging a ghost image.
+ group.image_ghost = new BufferedImage(label_size.width, label_size.height, BufferedImage.TYPE_INT_ARGB_PRE);
+ label_size = null;
+ // Get a graphics context for this image.
+ Graphics2D g2 = group.image_ghost.createGraphics();
+ // Make the image ghostlike
+ g2.setComposite(AlphaComposite.getInstance (AlphaComposite.SRC, 0.5f));
+ // Ask the cell renderer to paint itself into the BufferedImage
+ label.paint(g2);
+ g2 = null;
+ label = null;
+ try {
+ event.startDrag(new Cursor(Cursor.DEFAULT_CURSOR), group.image_ghost, group.mouse_offset, new StringSelection("dummy"), this);
+ //dragging = true;
+ }
+ catch(Exception error) {
+ error.printStackTrace();
+ }
+ }
+
+ }
+
+ /** Implementation side-effect.
+ * @param event A DragSourceDragEvent containing all the information about the drag event.
+ */
+ public void dragOver(DragSourceDragEvent event) {
+ }
+
+ /** Any implementation of DropTargetListener must include this method
+ * so we can be notified when the drag moves in this component.
+ * @param event A DropTargetDragEvent containing all the information
+ * about the drag event.
+ */
+ public void dragOver(DropTargetDragEvent event) {
+ // Draw the mouse ghost
+ Graphics2D g2 = (Graphics2D) getGraphics();
+ Point pt = event.getLocation();
+ if(pt_last != null && pt.equals(pt_last)) {
+ return;
+ }
+ pt_last = pt;
+ if(!DragSource.isDragImageSupported() && group.image_ghost != null) {
+ // Erase the last ghost image and cue line
+ paintImmediately(ra_ghost.getBounds());
+ // Remember where you are about to draw the new ghost image
+ ra_ghost.setRect(pt.x - group.mouse_offset.x, pt.y - group.mouse_offset.y, group.image_ghost.getWidth(), group.image_ghost.getHeight());
+ // Draw the ghost image
+ g2.drawImage(group.image_ghost, AffineTransform.getTranslateInstance(ra_ghost.getX(), ra_ghost.getY()), null);
+ }
+ // Now we highlight the target node if it is a valid drop target. Of course we don't bother if we are still over a node which has already been identified as to whether its a drop target
+ TreePath target_path = this.getPathForLocation(pt.x, pt.y);
+
+ if (target_path == null) {
+ // the user has moved the mouse into background area - need to remove any existing cue lines
+ if(upper_cue_line != null && lower_cue_line != null) {
+ paintImmediately(upper_cue_line.getBounds());
+ paintImmediately(lower_cue_line.getBounds());
+ previous_path = null;
+ }
+ // don't need to display anything
+ return;
+ }
+
+ if(previous_path == null || target_path != null && !target_path.equals(previous_path)) {
+ // Immediately clear the old cue lines.
+ if(upper_cue_line != null && lower_cue_line != null) {
+ paintImmediately(upper_cue_line.getBounds());
+ paintImmediately(lower_cue_line.getBounds());
+ }
+ if(isValidDrop(target_path)) {
+ // Get the drop target's bounding rectangle
+ Rectangle raPath = getPathBounds(target_path);
+ // Cue line bounds (2 pixels beneath the drop target)
+ upper_cue_line = new Rectangle(0, raPath.y + (int)raPath.getHeight(), getWidth(), 2);
+ lower_cue_line = new Rectangle(0, raPath.y, getWidth(), 2);
+ g2.setColor(((DefaultTreeCellRenderer)cellRenderer).getBackgroundSelectionColor()); // The cue line color
+ g2.fill(upper_cue_line); // Draw the cue line
+ g2.fill(lower_cue_line);
+ }
+ else {
+ upper_cue_line = null;
+ lower_cue_line = null;
+ }
+ }
+ }
+
+ /** Any implementation of DropTargetListener must include this method
+ * so we can be notified when the drag ends, ie the transferable is
+ * dropped.
+ * @param event A DropTargetDropEvent containing all the information
+ * about the end of the drag event.
+ */
+ public void drop(DropTargetDropEvent event) {
+ ///start = System.currentTimeMillis();
+ ///ystem.err.println("Drop target drop: " + this);
+ if (!isDroppable()) {
+ return;
+ }
+
+ event.acceptDrop(drag_action);
+
+ // Determine what node we dropped over.
+ Point pt = event.getLocation();
+ TreePath target_path = this.getPathForLocation(pt.x, pt.y);
+ FileNode target = null;
+ if(target_path != null) {
+ if(isValidDrop(target_path)) {
+ target = (FileNode) target_path.getLastPathComponent();
+ } else if (isValidDropOntoFile(target_path)) {
+ target = (FileNode) target_path.getParentPath().getLastPathComponent();
+ }
+ }
+ else {
+ TreeModel m = getModel();
+ // check that we have a model and is a FileSystemModel (ie check that a collection is loaded
+ if (m != null && m instanceof FileSystemModel) {
+ target = (FileNode) m.getRoot();
+ }
+ }
+ target_path = null;
+ pt = null;
+ if(target != null) {
+ ///ystem.err.println("Valid drop.");
+ TreePath[] selection = group.getSelection();
+ if(target != null && selection != null) {
+ FileNode[] source_nodes = new FileNode[selection.length];
+ for(int i = 0; i < source_nodes.length; i++) {
+ source_nodes[i] = (FileNode) selection[i].getLastPathComponent();
+ }
+ Gatherer.f_man.action(group.getSource(), source_nodes, this, target);
+ source_nodes = null;
+ }
+ group.setSelection(null);
+ group.setSource(null);
+ selection = null;
+ target = null;
+ }
+
+ // Clear up the group.image_ghost
+ paintImmediately(ra_ghost.getBounds());
+ event.getDropTargetContext().dropComplete(true);
+ group.image_ghost = null;
+ }
+
+
+ /** Any implementation of DragSourceListener must include this method
+ * so we can be notified when the action to be taken upon drop changes.
+ * @param event A DragSourceDragEvent containing all the information
+ * about the drag event.
+ */
+ public void dropActionChanged(DragSourceDragEvent event) {
+ }
+
+ /** Any implementation of DropTargetListener must include this method
+ * so we can be notified when the action to be taken upon drop changes.
+ * @param event A DropTargetDragEvent containing all the information
+ * about the drag event.
+ */
+ public void dropActionChanged(DropTargetDragEvent event) {
+ }
+
+ /** Used to notify this component that it has gained focus. It should
+ * make some effort to inform the user of this.
+ */
+ public void gainFocus() {
+ ///ystem.err.println("Gained focus: " + this);
+ ((DragTreeCellRenderer)cellRenderer).gainFocus();
+ repaint();
+ }
+
+ /** Autoscroll Interface...
+ * The following code was borrowed from the book:
+ * Java Swing
+ * By Robert Eckstein, Marc Loy & Dave Wood
+ * Paperback - 1221 pages 1 Ed edition (September 1998)
+ * O'Reilly & Associates; ISBN: 156592455X
+ *
+ * The relevant chapter of which can be found at:
+ * http://www.oreilly.com/catalog/jswing/chapter/dnd.beta.pdf
+ * Calculate the insets for the *JTREE*, not the viewport
+ * the tree is in. This makes it a bit messy.
+ */
+ public Insets getAutoscrollInsets()
+ {
+ Rectangle raOuter = this.getBounds();
+ Rectangle raInner = this.getParent().getBounds();
+ return new Insets(raInner.y - raOuter.y + AUTOSCROLL_MARGIN,
+ raInner.x - raOuter.x + AUTOSCROLL_MARGIN,
+ raOuter.height - raInner.height - raInner.y + raOuter.y + AUTOSCROLL_MARGIN,
+ raOuter.width - raInner.width - raInner.x + raOuter.x + AUTOSCROLL_MARGIN);
+ }
+
+ public Filter getFilter() {
+ return filter;
+ }
+
+ public String getSelectionDetails() {
+ return ((DragTreeSelectionModel)selectionModel).getDetails();
+ }
+
+ public FileSystemModel getTreeModel() {
+ return (FileSystemModel) getModel();
+ }
+
+
+ protected abstract boolean isDraggable();
+
+
+ protected abstract boolean isDroppable();
+
+
+ /** This method is used to inform this component when it loses focus,
+ * and should indicate this somehow.
+ */
+ public void loseFocus() {
+ ///ystem.err.println("Lost focus: " + this);
+ ((DragTreeCellRenderer)cellRenderer).loseFocus();
+ repaint();
+ }
+
+
+ public void paint(Graphics g) {
+ if(disabled_background != null) {
+ int height = getSize().height;
+ int offset = 0;
+ ImageIcon background;
+ if(isEnabled()) {
+ background = normal_background;
+ }
+ else {
+ background = disabled_background;
+ }
+ while((height - offset) > 0) {
+ g.drawImage(background.getImage(), 0, offset, null);
+ offset = offset + background.getIconHeight();
+ }
+ background = null;
+ }
+ super.paint(g);
+ }
+
+ public void refresh(TreePath path) {
+ if (treeModel instanceof FileSystemModel) {
+ ((FileSystemModel)treeModel).refresh(path);
+ }
+ else {
+ // System.err.println("DragTree::refresh - Tree model is " + treeModel);
+ }
+ }
+
+ public void setBackgroundNonSelectionColor(Color color) {
+ background_color = color;
+ if(isEnabled()) {
+ setBackground(color);
+ ((DefaultTreeCellRenderer)cellRenderer).setBackgroundNonSelectionColor(color);
+ }
+ else {
+ setBackground(Color.lightGray);
+ ((DefaultTreeCellRenderer)cellRenderer).setBackgroundNonSelectionColor(Color.lightGray);
+ }
+ repaint();
+ }
+
+ public void setBackgroundSelectionColor(Color color) {
+ ((DefaultTreeCellRenderer)cellRenderer).setBackgroundSelectionColor(color);
+ repaint();
+ }
+
+ /** Override the normal setEnabled so the Tree exhibits a little more
+ * change, which in this instance is the background colour changing.
+ * @param state Whether this GTree should be in an enabled state.
+ */
+ public void setEnabled(boolean state) {
+ super.setEnabled(state);
+
+ // Change some colors
+ if(state) {
+ setBackground(background_color);
+ ((DefaultTreeCellRenderer)cellRenderer).setBackgroundNonSelectionColor(background_color);
+ ((DefaultTreeCellRenderer)cellRenderer).setTextNonSelectionColor(foreground_color);
+ }
+ else {
+ setBackground(Color.lightGray);
+ ((DefaultTreeCellRenderer)cellRenderer).setBackgroundNonSelectionColor(Color.lightGray);
+ ((DefaultTreeCellRenderer)cellRenderer).setTextNonSelectionColor(Color.black);
+ }
+ repaint();
+ }
+
+ public void setGroup(DragGroup group) {
+ this.group = group;
+ }
+
+ /** Determines whether the following selection attempts should go through the normal delayed selection model, or should happen immediately.*/
+ public void setImmediate(boolean state) {
+ ((DragTreeSelectionModel)selectionModel).setImmediate(state);
+ }
+
+ public void setModel(TreeModel model) {
+ super.setModel(model);
+ if(model instanceof FileSystemModel) {
+ FileSystemModel file_system_model = (FileSystemModel) model;
+ file_system_model.setTree(this);
+ removeTreeExpansionListener(file_system_model);
+ addTreeExpansionListener(file_system_model);
+ removeTreeWillExpandListener(file_system_model);
+ addTreeWillExpandListener(file_system_model);
+ file_system_model = null;
+ }
+ }
+
+ /** Ensure that that file node denoted by the given file is selected. */
+ public void setSelection(File file) {
+ // We know the file exists, and thus that it must exists somewhere in our tree hierarchy.
+ // 1. Retrieve the root node of our tree.
+ FileNode current = (FileNode) getModel().getRoot();
+ // 2. Find that node in the file parents, keeping track of each intermediate file.
+ ArrayList files = new ArrayList();
+ while(file != null && !current.toString().equals(file.getName())) {
+ files.add(0, file);
+ file = file.getParentFile();
+ }
+ if(file == null) {
+ return;
+ }
+ // 3. While there are still remaining intermediate files.
+ while(files.size() > 0) {
+ file = (File) files.remove(0);
+ // 3a. Find the next file in the current nodes children.
+ boolean found = false;
+ current.map();
+ for(int i = 0; !found && i < current.getChildCount(); i++) {
+ FileNode child = (FileNode) current.getChildAt(i);
+ if(child.toString().equals(file.getName())) {
+ // 3b. Make the current node this node (if found) and continue.
+ found = true;
+ current = child;
+ }
+ }
+ // 3c. If not found then return as this node can't exists somehow.
+ if(!found) {
+ return;
+ }
+ }
+ // 4. We should now have the desired node. Remember to make the selection immediate.
+ TreePath path = new TreePath(current.getPath());
+ setImmediate(true);
+ setSelectionPath(path);
+ setImmediate(false);
+ }
+
+ public void setTextNonSelectionColor(Color color) {
+ foreground_color = color;
+ if(isEnabled()) {
+ ((DefaultTreeCellRenderer)cellRenderer).setTextNonSelectionColor(color);
+ }
+ else {
+ ((DefaultTreeCellRenderer)cellRenderer).setTextNonSelectionColor(Color.black);
+ }
+ repaint();
+ }
+
+ public void setTextSelectionColor(Color color) {
+ ((DefaultTreeCellRenderer)cellRenderer).setTextSelectionColor(color);
+ repaint();
+ }
+
+ public void valueChanged(TreeSelectionEvent event) {
+ if(group == null) {
+ ///ystem.err.println("Oh my god, this tree has no group: " + this);
+ }
+ else {
+ group.grabFocus(this);
+ }
+ }
+
+ /** returns false for dummy nodes (ones without files), and system root
+ * nodes */
+ private boolean isValidDrag() {
+ //because you cant select nodes that are children of another selection, and we use a contiguous selection model, we just test the first selection path
+ TreePath node_path = getSelectionPath();
+ FileNode node = (FileNode) node_path.getLastPathComponent();
+
+ if (node.getFile() == null) {
+ return false;
+ }
+ if (node.isFileSystemRoot()) {
+ return false;
+ }
+ // We also don't allow the user to select files that reside within the currently loaded collection
+ TreePath[] paths = getSelectionPaths();
+ for(int i = 0; i < paths.length; i++) {
+ FileNode child = (FileNode) paths[i].getLastPathComponent();
+ if (child.isInLoadedCollection()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean isValidDropOntoFile(TreePath target_path) {
+ boolean valid = false;
+ if(target_path != null) {
+ FileNode target_node = (FileNode) target_path.getLastPathComponent();
+ if (target_node.isLeaf()) {
+ // check the parent node for being a valid drop
+ return isValidDrop(target_path.getParentPath());
+ }
+ }
+ return false;
+ }
+
+
+ private boolean isValidDrop(TreePath target_path) {
+ boolean valid = false;
+ if(target_path == null) {
+ previous_path = null;
+ }
+ else {
+ FileNode target_node = (FileNode) target_path.getLastPathComponent();
+ // We can only continue testing if the node is a folder.
+ if(!target_node.isLeaf()) {
+ // Now we check if the node is readonly.
+ if(!target_node.isReadOnly()) {
+ // Finally we check the target path against the paths in the selection to ensure we are not adding to our own ancestors!
+ TreePath[] selection = group.getSelection();
+ boolean failed = false;
+ for(int i = 0; !failed && selection != null && i < selection.length; i++) {
+ failed = selection[i].isDescendant(target_path);
+ }
+ // Having finally completed all the tests, we can highlight the drop target.
+ if(!failed) {
+ valid = true;
+ }
+ else {
+ ///ystem.err.println("Invalid. Target is descendant of itself.");
+ }
+ }
+ else {
+ ///ystem.err.println("Read only.");
+ }
+ }
+ else {
+ ///ystem.err.println("Leaf node. Children not allowed.");
+ }
+ previous_path = target_path;
+ }
+ return valid;
+ }
+
+
+ /** This class listens for certain key presses, such as [Enter] or [Delete], and responds appropriately. */
+ private class DragTreeKeyListener
+ extends KeyAdapter
+ {
+ private boolean vk_left_pressed = false;
+ /** Called whenever a key that was pressed is released, it is this action that will cause the desired effects (this allows for the key event itself to be processed prior to this listener dealing with it). */
+ public void keyReleased(KeyEvent event) {
+ ///ystem.err.println("Key Release detected. " + event.getKeyCode());
+ if(event.getKeyCode() == KeyEvent.VK_DELETE) {
+ // Get the selected files from the tree and removal them using the default dnd removal method.
+ // Find the active tree (you've made selections in).
+ DragTree tree = (DragTree) group.getActive();
+ // Fudge things a bit
+ group.setSource(tree);
+ // Determine the selection.
+ TreePath paths[] = tree.getSelectionPaths();
+ if(paths != null) {
+ FileNode[] source_nodes = new FileNode[paths.length];
+ for(int i = 0; i < source_nodes.length; i++) {
+ source_nodes[i] = (FileNode) paths[i].getLastPathComponent();
+ }
+ Gatherer.f_man.action(tree, source_nodes, Gatherer.recycle_bin, null);
+ source_nodes = null;
+ }
+ }
+ else if(event.getKeyCode() == KeyEvent.VK_ENTER) {
+ // Get the first selected file.
+ DragTree tree = (DragTree)event.getSource();
+ TreePath path = tree.getSelectionPath();
+ if(path != null) {
+ File file = ((FileNode)path.getLastPathComponent()).getFile();
+ if (file != null && file.isFile()) {
+ Gatherer.f_man.openFileInExternalApplication(file);
+ }
+ else {
+ if(!tree.isExpanded(path)) {
+ tree.expandPath(path);
+ }
+ else {
+ tree.collapsePath(path);
+ }
+ }
+ }
+ } else if (event.getKeyCode() == KeyEvent.VK_UP || event.getKeyCode() == KeyEvent.VK_DOWN) {
+ DragTree tree = (DragTree)event.getSource();
+ // we need to manually do the up and down selections
+ boolean up = (event.getKeyCode() == KeyEvent.VK_UP);
+ int current_row = tree.getLeadSelectionRow();
+ tree.setImmediate(true);
+ if (up) {
+ if (current_row > 0) {
+ tree.clearSelection();
+ tree.setSelectionRow(current_row-1);
+ }
+ } else {
+ if (current_row < tree.getRowCount()-1){
+ tree.clearSelection();
+ tree.setSelectionRow(current_row+1);
+ }
+ }
+ tree.setImmediate(false);
+ } else if (event.getKeyCode() == KeyEvent.VK_LEFT) {
+ // left key on a file shifts the selection to the parent folder
+ DragTree tree = (DragTree)event.getSource();
+ TreePath path = tree.getLeadSelectionPath();
+ if(path != null) {
+ File file = ((FileNode)path.getLastPathComponent()).getFile();
+ if(file != null) {
+ if (file.isFile() || vk_left_pressed) {
+ vk_left_pressed = false;
+ TreePath parent_path = path.getParentPath();
+ if (parent_path != null && parent_path.getParentPath() != null) {
+ // if this file is in the top level folder, don't move the focus
+ tree.setImmediate(true);
+ tree.clearSelection();
+ tree.setSelectionPath(parent_path);
+ tree.setImmediate(false);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // we need to watch for left clicks on an unopened folder - should shift the focus to teh parent folder. But because there is some other mysterious key listener that does opening and closing folders on right and left clicks, we must detect the situation before the other handler has done its job, and process it after.
+ public void keyPressed(KeyEvent event) {
+ if (event.getKeyCode() == KeyEvent.VK_LEFT) {
+ // left key on closed directory shifts the selection to the parent folder
+ DragTree tree = (DragTree)event.getSource();
+ TreePath path = tree.getLeadSelectionPath();
+ if(path == null) return;
+ File file = ((FileNode)path.getLastPathComponent()).getFile();
+ if(file == null) return;
+
+ if (file.isDirectory() && tree.isCollapsed(path)) {
+ vk_left_pressed = true;
+ }
+ }
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/tree/DragTreeCellRenderer.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/tree/DragTreeCellRenderer.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/gui/tree/DragTreeCellRenderer.java (revision 31635)
@@ -0,0 +1,89 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.gui.tree;
+
+import java.awt.*;
+import javax.swing.*;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.file.FileNode;
+import org.greenstone.gatherer.util.StaticStrings;
+import org.greenstone.gatherer.util.Utility;
+
+public class DragTreeCellRenderer
+ extends DefaultTreeCellRenderer
+{
+ private Color selection_background;
+ private Color selection_foreground;
+
+ public DragTreeCellRenderer() {
+ super();
+ // Have to wait until this image is loaded
+ selection_background = getBackgroundSelectionColor();
+ selection_foreground = getTextSelectionColor();
+ }
+
+ public void gainFocus() {
+ setBackgroundSelectionColor(selection_background);
+ setTextSelectionColor(selection_foreground);
+ }
+
+ /** Configures the renderer based on the passed in components. */
+ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus)
+ {
+ // Rendering FileNodes
+ if (value instanceof FileNode) {
+ FileNode node = (FileNode) value;
+ String node_text = node.toString();
+
+ // Add file sizes if necessary
+ if (Configuration.get("general.show_file_size", Configuration.COLLECTION_SPECIFIC) && node.getFile() != null && !node.getAllowsChildren()) {
+ node_text = node_text + StaticStrings.SPACE_CHARACTER + StaticStrings.LBRACKET_CHARACTER + Utility.formatFileLength(node.getFile().length()) + StaticStrings.RBRACKET_CHARACTER;
+ }
+
+ return (JLabel) super.getTreeCellRendererComponent(tree, node_text, sel, expanded, leaf, row, hasFocus);
+ }
+
+ // Use default renderer for anything else
+ return (JLabel) super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
+ }
+
+ public void loseFocus() {
+ setBackgroundSelectionColor(Color.lightGray);
+ setTextSelectionColor(Color.black);
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/DocGAFile.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/DocGAFile.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/DocGAFile.java (revision 31635)
@@ -0,0 +1,45 @@
+/**
+ *############################################################################
+ * A component of the Greenstone Librarian Interface, part of the Greenstone
+ * digital library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
+ *
+ * Copyright (C) 2004 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################################################
+ */
+
+package org.greenstone.gatherer.metadata;
+
+
+import java.io.*;
+import java.util.*;
+import java.net.URLDecoder;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.util.Utility;
+
+
+/** This class represents one doc.xml file */
+
+public class DocGAFile extends DocXMLFile
+{
+ public DocGAFile(String doc_xml_file_path)
+ {
+ super(doc_xml_file_path, "Description", "Metadata"); // metadataWrap and metadataItem
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/DocMetsXMLFile.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/DocMetsXMLFile.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/DocMetsXMLFile.java (revision 31635)
@@ -0,0 +1,44 @@
+/**
+ *############################################################################
+ * A component of the Greenstone Librarian Interface, part of the Greenstone
+ * digital library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
+ *
+ * Copyright (C) 2004 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################################################
+ */
+
+package org.greenstone.gatherer.metadata;
+
+
+import java.io.*;
+import java.util.*;
+import java.net.URLDecoder;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.util.Utility;
+
+
+/** This class represents one docmets.xml file */
+public class DocMetsXMLFile extends DocXMLFile
+{
+ public DocMetsXMLFile(String doc_xml_file_path)
+ {
+ super(doc_xml_file_path, "mets:xmlData", "ex:metadata"); // metadataWrap, metadataItem
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/DocXMLFile.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/DocXMLFile.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/DocXMLFile.java (revision 31635)
@@ -0,0 +1,541 @@
+/**
+ *############################################################################
+ * A component of the Greenstone Librarian Interface, part of the Greenstone
+ * digital library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
+ *
+ * Copyright (C) 2004 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################################################
+ */
+
+package org.greenstone.gatherer.metadata;
+
+
+import java.io.*;
+import java.util.*;
+import java.net.URLDecoder;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.util.Utility;
+
+
+/** This class represents one doc.xml file */
+
+public abstract class DocXMLFile extends File
+{
+ protected HashMap source_file_name_to_description_elements_mapping = new HashMap();
+
+ protected final String MetadataWrap;
+ protected final String MetadataItem;
+
+ public DocXMLFile(String doc_xml_file_path, String metaWrap, String metaItem)
+ {
+ super(doc_xml_file_path);
+ this.MetadataWrap = metaWrap;
+ this.MetadataItem = metaItem;
+ }
+
+
+ public ArrayList getMetadataExtractedFromFile(File file)
+ {
+ // Build up a list of metadata extracted from this file
+ ArrayList metadata_values = new ArrayList();
+
+ String file_relative_path = file.getAbsolutePath();
+ int import_index = file_relative_path.indexOf("import");
+ if (import_index != -1) {
+ file_relative_path = file_relative_path.substring(import_index + "import".length() + 1);
+ }
+
+ // Check whether this file (i.e. doc.xml or docmets.xml on inheritance) file contains extracted metadata for the specified file
+ ArrayList description_elements_list = (ArrayList) source_file_name_to_description_elements_mapping.get(file_relative_path);
+ if (description_elements_list == null) {
+ // ...it doesn't
+ return metadata_values;
+ }
+
+ MetadataSet extracted_metadata_set = MetadataSetManager.getMetadataSet(MetadataSetManager.EXTRACTED_METADATA_NAMESPACE);
+
+ // Parse the file
+ DebugStream.println("Applicable file: " + this);
+ try {
+ BufferedReader buffered_reader = new BufferedReader(new InputStreamReader(new FileInputStream(this), "UTF-8"));
+
+ int description_element_num = 0;
+ int next_description_element_start = ((Integer) description_elements_list.get(description_element_num)).intValue();
+ boolean in_relevant_description_element = false;
+
+ String line = null;
+ for (int line_num = 0; (line = buffered_reader.readLine()) != null; line_num++) {
+ // Check if this line contains the start of a relevant "Description" element
+ // (mets:xmlData in METS parlance, Description in GreenstoneArchive format)
+ if (line_num == next_description_element_start) {
+ in_relevant_description_element = true;
+ continue;
+ }
+
+ // If we're not in a relevant Description element we don't care about anything
+ if (in_relevant_description_element == false) {
+ continue;
+ }
+
+ // Check if this line contains the end of the relevant Description element
+ if (line.indexOf(""+MetadataWrap+">") != -1) {
+ description_element_num++;
+ if (description_element_num == description_elements_list.size()) {
+ break;
+ }
+
+ next_description_element_start = ((Integer) description_elements_list.get(description_element_num)).intValue();
+ in_relevant_description_element = false;
+ continue;
+ }
+
+ // If this line doesn't contain a complete Metadata element, we're not interested
+ if (line.indexOf("<"+MetadataItem+" ") == -1 || line.indexOf(""+MetadataItem+">") == -1) {
+ continue;
+ }
+
+ // Extract the metadata element name
+ int name_index = line.indexOf(" name=\"") + " name=\"".length();
+ String metadata_element_name_full = line.substring(name_index, line.indexOf("\"", name_index));
+
+ // If the metadata has a namespace it isn't extracted metadata, so we're not interested
+ // Actually, if it is ex. then we are interested
+ String metadata_set_namespace = MetadataTools.getMetadataSetNamespace(metadata_element_name_full);
+
+ if (!metadata_set_namespace.equals("") && !metadata_set_namespace.equals("ex")) {
+ continue;
+ }
+
+ // Extracted metadata!
+ // do it like this just in case we have ex.
+ String metadata_element_name = MetadataTools.getMetadataElementName(metadata_element_name_full);
+
+ // We completely ignore bibliographic data
+ if (metadata_element_name.equals("SourceSegment")) {
+ buffered_reader.close();
+ return new ArrayList();
+ }
+
+ // Ignore metadata starting with gsdl (used to be lowercase metadata and /metadata)
+ if (metadata_element_name.startsWith("gsdl")) {
+ continue;
+ }
+
+ MetadataElement metadata_element = extracted_metadata_set.getMetadataElementWithName(metadata_element_name);
+
+ // Value trees are not stored for extracted metadata, so create a new value tree node now
+ int value_index = line.indexOf(">", name_index) + ">".length();
+ String metadata_element_value = line.substring(value_index, line.lastIndexOf(""+MetadataItem+">"));
+
+ metadata_element.addMetadataValue(metadata_element_value);
+ MetadataValueTreeNode metadata_value_tree_node = metadata_element.getMetadataValueTreeNode(metadata_element_value);
+
+ // Add the new metadata value to the list
+ MetadataValue metadata_value = new MetadataValue(metadata_element, metadata_value_tree_node);
+ metadata_values.add(metadata_value);
+ }
+
+ buffered_reader.close();
+ }
+ catch (FileNotFoundException exception) {
+ DebugStream.printStackTrace(exception);
+ }
+ catch (IOException exception) {
+ DebugStream.printStackTrace(exception);
+ }
+
+ return metadata_values;
+ }
+
+
+
+
+ /**
+ * Every file must be skimmed when a collection is opened, for two reasons:
+ * - To build a mapping from source file to its corresponding doc.xml file
+ * - To get a complete list of all extracted metadata elements
+ */
+ public void skimFile()
+ {
+ MetadataSet extracted_metadata_set = MetadataSetManager.getMetadataSet(MetadataSetManager.EXTRACTED_METADATA_NAMESPACE);
+
+ // Skim the file as quickly as possible (don't parse as XML), looking at the Metadata elements
+ DebugStream.println("Skimming " + this + "...");
+ try {
+ BufferedReader buffered_reader = new BufferedReader(new InputStreamReader(new FileInputStream(this), "UTF-8"));
+ int description_element_start = -1;
+
+ String line = null;
+ for (int line_num = 0; (line = buffered_reader.readLine()) != null; line_num++) {
+ // This line contains the start of a "MetadataWrap" element
+ // (mets:xmlData in METS parlance, Description in GreenstoneArchive format)
+ if (line.indexOf("<"+MetadataWrap+">") != -1) {
+ if (description_element_start != -1) {
+ System.err.println("Parse error: previous " + MetadataWrap + " element unfinished!");
+ }
+ description_element_start = line_num;
+ continue;
+ }
+
+ // This line contains the end of a "MetadataWrap" element
+ if (line.indexOf(""+MetadataWrap+">") != -1) {
+ if (description_element_start == -1) {
+ System.err.println("Parse error: "+MetadataWrap+" element unstarted!");
+ }
+ description_element_start = -1;
+ continue;
+ }
+
+ // If we're not in a"MetadataWrap" element there shouldn't be any Metadata elements
+ if (description_element_start == -1) {
+ continue;
+ }
+
+ // This line doesn't contain a Metadata element, so we're not interested
+ if (line.indexOf("<"+MetadataItem+" ") == -1) {
+ DebugStream.println("Warning: "+MetadataWrap+" element line doesn't contain Metadata element.");
+ continue;
+ }
+
+ // Extract the metadata element name
+ int name_index = line.indexOf(" name=\"") + " name=\"".length();
+ String metadata_element_name_full = line.substring(name_index, line.indexOf("\"", name_index));
+
+ // If the metadata has a namespace it isn't extracted metadata, so we're not interested
+ String metadata_set_namespace = MetadataTools.getMetadataSetNamespace(metadata_element_name_full);
+ if (!metadata_set_namespace.equals("") && !metadata_set_namespace.equals("ex")) {
+ continue;
+ }
+
+ // Extracted metadata! May have ex. so make sure we remove that
+ String metadata_element_name = MetadataTools.getMetadataElementName(metadata_element_name_full);
+ // Note which file this is for
+ if (metadata_element_name.equals("gsdlsourcefilename")) {
+ // Extract the gsdlsourcefilename element value
+ int value_index = line.indexOf(">", name_index) + ">".length();
+ String gsdlsourcefilename_value = line.substring(value_index, line.indexOf("<", value_index));
+
+ // We're only interested in the path relative to the import folder
+ int import_index = gsdlsourcefilename_value.indexOf("import");
+ if (import_index != -1) {
+ gsdlsourcefilename_value = gsdlsourcefilename_value.substring(import_index + "import".length());
+
+ boolean is_unix_path = gsdlsourcefilename_value.startsWith("/");
+ gsdlsourcefilename_value = gsdlsourcefilename_value.substring(1);
+
+ // URL decode gsdlsourcefilename. Need to set the decoder to use the default system encoding
+ // This is stored in the System's file.encoding property.
+ gsdlsourcefilename_value = URLDecoder.decode(gsdlsourcefilename_value, System.getProperty("file.encoding"));
+
+ // Make sure the path matches the OS that is running
+ if (is_unix_path && Utility.isWindows()) {
+ // Convert path from Unix to Windows
+ gsdlsourcefilename_value = gsdlsourcefilename_value.replaceAll("\\/", "\\\\");
+ }
+ else if (!is_unix_path && !Utility.isWindows()) {
+ // Convert path from Windows to Unix
+ gsdlsourcefilename_value = gsdlsourcefilename_value.replaceAll("\\\\", "/");
+ }
+
+ // Remember this for quick access later
+ if (source_file_name_to_description_elements_mapping.get(gsdlsourcefilename_value) == null) {
+ source_file_name_to_description_elements_mapping.put(gsdlsourcefilename_value, new ArrayList());
+ }
+
+ ((ArrayList) source_file_name_to_description_elements_mapping.get(gsdlsourcefilename_value)).add(new Integer(description_element_start));
+ }
+
+ // Warn about an odd gsdlsourcefilename, except if it is the Greenstone "tmp" directory or
+ // (as in the case of using FLI) if it is the etc/collect.cfg or etc/collectionConfig.xml file
+ // which are the gsdlsourcefilenames for the fedora digital object representing a collection.
+ // This (tmp dir) is true when the source files come from a zip file processed by ZIPPlug, for example
+ else if (gsdlsourcefilename_value.indexOf("tmp") == -1
+ && !gsdlsourcefilename_value.endsWith("collect.cfg")
+ && !gsdlsourcefilename_value.endsWith("collectionConfig.xml")) {
+ // We don't really know what is going on...
+ System.err.println("Warning: Could not understand gsdlsourcefilename " + gsdlsourcefilename_value);
+ }
+ }
+
+ // Ignore metadata starting with gsdl (used to be lowercase metadata and /metadata)
+ if (metadata_element_name.startsWith("gsdl")) {
+ continue;
+ }
+
+ MetadataElement metadata_element = extracted_metadata_set.getMetadataElementWithName(metadata_element_name);
+ if (metadata_element == null) {
+ // This element isn't defined in ex.mds, so create it for this session
+ DebugStream.println("Extracted metadata element not defined: " + metadata_element_name);
+ extracted_metadata_set.addMetadataElementForThisSession(metadata_element_name);
+ }
+ }
+
+ buffered_reader.close();
+ }
+ catch (FileNotFoundException exception) {
+ DebugStream.printStackTrace(exception);
+ }
+ catch (IOException exception) {
+ DebugStream.printStackTrace(exception);
+ }
+ }
+
+
+ /*
+ public ArrayList getMetadataExtractedFromFile(File file)
+ {
+ // Build up a list of metadata extracted from this file
+ ArrayList metadata_values = new ArrayList();
+
+ String file_relative_path = file.getAbsolutePath();
+ int import_index = file_relative_path.indexOf("import");
+ if (import_index != -1) {
+ file_relative_path = file_relative_path.substring(import_index + "import".length() + 1);
+ }
+
+ // Check whether this doc.xml file contains extracted metadata for the specified file
+ ArrayList description_elements_list = (ArrayList) source_file_name_to_description_elements_mapping.get(file_relative_path);
+ if (description_elements_list == null) {
+ // ...it doesn't
+ return metadata_values;
+ }
+
+ MetadataSet extracted_metadata_set = MetadataSetManager.getMetadataSet(MetadataSetManager.EXTRACTED_METADATA_NAMESPACE);
+
+ // Parse the doc.xml file
+ DebugStream.println("Applicable doc.xml file: " + this);
+ try {
+ BufferedReader buffered_reader = new BufferedReader(new FileReader(this));
+
+ int description_element_num = 0;
+ int next_description_element_start = ((Integer) description_elements_list.get(description_element_num)).intValue();
+ boolean in_relevant_description_element = false;
+
+ String line = null;
+ for (int line_num = 0; (line = buffered_reader.readLine()) != null; line_num++) {
+ // Check if this line contains the start of a relevant Description element
+ if (line_num == next_description_element_start) {
+ in_relevant_description_element = true;
+ continue;
+ }
+
+ // If we're not in a relevant Description element we don't care about anything
+ if (in_relevant_description_element == false) {
+ continue;
+ }
+
+ // Check if this line contains the end of the relevant Description element
+ if (line.indexOf("") != -1) {
+ description_element_num++;
+ if (description_element_num == description_elements_list.size()) {
+ break;
+ }
+
+ next_description_element_start = ((Integer) description_elements_list.get(description_element_num)).intValue();
+ in_relevant_description_element = false;
+ continue;
+ }
+
+ // If this line doesn't contain a complete Metadata element, we're not interested
+ if (line.indexOf("") == -1) {
+ continue;
+ }
+
+ // Extract the metadata element name
+ int name_index = line.indexOf(" name=\"") + " name=\"".length();
+ String metadata_element_name_full = line.substring(name_index, line.indexOf("\"", name_index));
+
+ // If the metadata has a namespace it isn't extracted metadata, so we're not interested
+ String metadata_set_namespace = MetadataTools.getMetadataSetNamespace(metadata_element_name_full);
+ if (!metadata_set_namespace.equals("")) {
+ continue;
+ }
+
+ // Extracted metadata!
+ String metadata_element_name = metadata_element_name_full;
+
+ // We completely ignore bibliographic data
+ if (metadata_element_name.equals("SourceSegment")) {
+ buffered_reader.close();
+ return new ArrayList();
+ }
+
+ // Ignore metadata starting with gsdl (used to be lowercase metadata and /metadata)
+ if (metadata_element_name.startsWith("gsdl")) {
+ continue;
+ }
+
+ MetadataElement metadata_element = extracted_metadata_set.getMetadataElementWithName(metadata_element_name);
+
+ // Value trees are not stored for extracted metadata, so create a new value tree node now
+ int value_index = line.indexOf(">", name_index) + ">".length();
+ String metadata_element_value = line.substring(value_index, line.lastIndexOf(" "));
+
+ metadata_element.addMetadataValue(metadata_element_value);
+ MetadataValueTreeNode metadata_value_tree_node = metadata_element.getMetadataValueTreeNode(metadata_element_value);
+
+ // Add the new metadata value to the list
+ MetadataValue metadata_value = new MetadataValue(metadata_element, metadata_value_tree_node);
+ metadata_values.add(metadata_value);
+ }
+
+ buffered_reader.close();
+ }
+ catch (FileNotFoundException exception) {
+ DebugStream.printStackTrace(exception);
+ }
+ catch (IOException exception) {
+ DebugStream.printStackTrace(exception);
+ }
+
+ return metadata_values;
+ }
+
+ */
+
+ /**
+ * Every doc.xml file must be skimmed when a collection is opened, for two reasons:
+ * - To build a mapping from source file to its corresponding doc.xml file
+ * - To get a complete list of all extracted metadata elements
+ */
+ /*
+ public void skimFile()
+ {
+ MetadataSet extracted_metadata_set = MetadataSetManager.getMetadataSet(MetadataSetManager.EXTRACTED_METADATA_NAMESPACE);
+
+ // Skim the doc.xml file as quickly as possible (don't parse as XML), looking at the Metadata elements
+ DebugStream.println("Skimming " + this + "...");
+ try {
+ BufferedReader buffered_reader = new BufferedReader(new FileReader(this));
+ int description_element_start = -1;
+
+ String line = null;
+ for (int line_num = 0; (line = buffered_reader.readLine()) != null; line_num++) {
+ // This line contains the start of a Description element
+ if (line.indexOf("") != -1) {
+ if (description_element_start != -1) {
+ System.err.println("Parse error: previous Description element unfinished!");
+ }
+ description_element_start = line_num;
+ continue;
+ }
+
+ // This line contains the end of a Description element
+ if (line.indexOf(" ") != -1) {
+ if (description_element_start == -1) {
+ System.err.println("Parse error: Description element unstarted!");
+ }
+ description_element_start = -1;
+ continue;
+ }
+
+ // If we're not in a Description element there shouldn't be any Metadata elements
+ if (description_element_start == -1) {
+ continue;
+ }
+
+ // This line doesn't contain a Metadata element, so we're not interested
+ if (line.indexOf("", name_index) + ">".length();
+ String gsdlsourcefilename_value = line.substring(value_index, line.indexOf("<", value_index));
+
+ // We're only interested in the path relative to the import folder
+ int import_index = gsdlsourcefilename_value.indexOf("import");
+ if (import_index != -1) {
+ gsdlsourcefilename_value = gsdlsourcefilename_value.substring(import_index + "import".length());
+
+ boolean is_unix_path = gsdlsourcefilename_value.startsWith("/");
+ gsdlsourcefilename_value = gsdlsourcefilename_value.substring(1);
+
+ // URL decode gsdlsourcefilename. Need to set the decoder to use the default system encoding
+ // This is stored in the System's file.encoding property.
+ gsdlsourcefilename_value = URLDecoder.decode(gsdlsourcefilename_value, System.getProperty("file.encoding"));
+
+ // Make sure the path matches the OS that is running
+ if (is_unix_path && Utility.isWindows()) {
+ // Convert path from Unix to Windows
+ gsdlsourcefilename_value = gsdlsourcefilename_value.replaceAll("\\/", "\\\\");
+ }
+ else if (!is_unix_path && !Utility.isWindows()) {
+ // Convert path from Windows to Unix
+ gsdlsourcefilename_value = gsdlsourcefilename_value.replaceAll("\\\\", "/");
+ }
+
+ // Remember this for quick access later
+ if (source_file_name_to_description_elements_mapping.get(gsdlsourcefilename_value) == null) {
+ source_file_name_to_description_elements_mapping.put(gsdlsourcefilename_value, new ArrayList());
+ }
+
+ ((ArrayList) source_file_name_to_description_elements_mapping.get(gsdlsourcefilename_value)).add(new Integer(description_element_start));
+ }
+
+ // Warn about an odd gsdlsourcefilename, except if it is the Greenstone "tmp" directory
+ // This is true when the source files come from a zip file processed by ZIPPlug, for example
+ else if (gsdlsourcefilename_value.indexOf("tmp") == -1) {
+ // We don't really know what is going on...
+ System.err.println("Warning: Could not understand gsdlsourcefilename " + gsdlsourcefilename_value);
+ }
+ }
+
+ // Ignore metadata starting with gsdl (used to be lowercase metadata and /metadata)
+ if (metadata_element_name.startsWith("gsdl")) {
+ continue;
+ }
+
+ MetadataElement metadata_element = extracted_metadata_set.getMetadataElementWithName(metadata_element_name);
+ if (metadata_element == null) {
+ // This element isn't defined in ex.mds, so create it for this session
+ DebugStream.println("Extracted metadata element not defined: " + metadata_element_name);
+ extracted_metadata_set.addMetadataElementForThisSession(metadata_element_name);
+ }
+ }
+
+ buffered_reader.close();
+ }
+ catch (FileNotFoundException exception) {
+ DebugStream.printStackTrace(exception);
+ }
+ catch (IOException exception) {
+ DebugStream.printStackTrace(exception);
+ }
+ }
+ */
+
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/DocXMLFileManager.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/DocXMLFileManager.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/DocXMLFileManager.java (revision 31635)
@@ -0,0 +1,105 @@
+/**
+ *############################################################################
+ * A component of the Greenstone Librarian Interface, part of the Greenstone
+ * digital library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
+ *
+ * Copyright (C) 2004 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################################################
+ */
+
+package org.greenstone.gatherer.metadata;
+
+
+import java.io.*;
+import java.util.*;
+import org.greenstone.gatherer.DebugStream;
+
+
+/** This class is a static class that manages the doc.xml files */
+public class DocXMLFileManager
+{
+ static private ArrayList doc_xml_files = new ArrayList();
+
+
+ static public void clearDocXMLFiles()
+ {
+ doc_xml_files.clear();
+ }
+
+
+ static public ArrayList getMetadataExtractedFromFile(File file)
+ {
+ // Build up a list of metadata values extracted from this file
+ ArrayList metadata_values = new ArrayList();
+
+ // Look at each loaded doc.xml file to see if any have extracted metadata for this file
+ for (int i = 0; i < doc_xml_files.size(); i++) {
+ DocXMLFile doc_xml_file = (DocXMLFile) doc_xml_files.get(i);
+ metadata_values.addAll(doc_xml_file.getMetadataExtractedFromFile(file));
+ }
+
+ return metadata_values;
+ }
+
+
+ static public void loadDocXMLFiles(File directory, String filename_match)
+ {
+ // Make sure the directory (archives) exists
+ if (directory.exists() == false) {
+ return;
+ }
+
+ // Look recursively at each subfile of the directory for doc.xml files
+ File[] directory_files = directory.listFiles();
+ for (int i = 0; i < directory_files.length; i++) {
+ File child_file = directory_files[i];
+ if (child_file.isDirectory()) {
+ loadDocXMLFiles(child_file,filename_match);
+ }
+ else if (child_file.getName().equals(filename_match)) {
+ // e.g. doc.xml (for regular Greenstone, docmets.xml for Fedora)
+
+ loadDocXMLFile(child_file,filename_match);
+ }
+ }
+ }
+
+
+ static private void loadDocXMLFile(File doc_xml_file_file,String filename_match)
+ {
+ String file = doc_xml_file_file.getAbsolutePath();
+
+ // Need to do typecasts in the following to keep Java 1.4 happy
+ DocXMLFile doc_xml_file
+ = (filename_match.equals("docmets.xml"))
+ ? (DocXMLFile) new DocMetsXMLFile(file)
+ : (DocXMLFile) new DocGAFile(file);
+
+ try {
+ doc_xml_file.skimFile();
+ doc_xml_files.add(doc_xml_file);
+ }
+ catch (Exception exception) {
+ // We catch any exceptions here so errors in doc.xml files don't stop the collection from loading
+ System.err.println("Error: Could not skim doc.xml file " + doc_xml_file.getAbsolutePath());
+ DebugStream.printStackTrace(exception);
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/FilenameEncoding.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/FilenameEncoding.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/FilenameEncoding.java (revision 31635)
@@ -0,0 +1,439 @@
+/**
+ *############################################################################
+ * A component of the Greenstone Librarian Interface, part of the Greenstone
+ * digital library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
+ *
+ * Copyright (C) 2010 Greenstone Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################################################
+ */
+
+package org.greenstone.gatherer.metadata;
+
+import java.io.File;
+import java.net.*;
+import java.nio.charset.*;
+import java.util.*;
+import org.greenstone.gatherer.collection.CollectionManager;
+import org.greenstone.gatherer.DebugStream;
+
+/** Static access class that contains many of the methods used to work with filename encodings.
+* Works closely with classes FileNode, CollectionTreeNode, MetadataXMLFile, MetadataXMLFileManager
+* to maintain a map of URLEncodedFilenames to their filename encodings.
+* The process of filename encoding further affects the CollectionManager which refreshes its CollectionTree,
+* FileManager (move, delete, rename actions), MetadataValueTableModel, EnrichPane. */
+
+public class FilenameEncoding {
+ /** Display of filenames in the trees are in URL encoding, if debugging */
+ public static boolean DEBUGGING = false;
+
+ /** Set to false by Gatherer if the locale is UTF-8, as Java's handling is
+ * such that non-UTF8 filename encodings on a UTF-8 locale are destructively
+ * converted so that the bytecodes in the filename are not preserved. */
+ public static boolean MULTIPLE_FILENAME_ENCODINGS_SUPPORTED = false;
+
+ /** Also set by Gatherer.
+ * If the OS supports multiple filename encodings, we will be working with URL strings
+ * and the applicable separators are always the forward slash ("/") not File.separator.
+ * If multiple filename encodings are not supported, we're dealing with File.separator. */
+ public static String URL_FILE_SEPARATOR = File.separator;
+
+
+ /** gs.filenameEncoding is a special sort of metadata that is not merely to be stored along
+ * with a file, but is to be applied in real-time on the file's name in the CollectionTree
+ * display. Since FileNodes are constantly destroyed and reconstructed by that Tree when
+ * its nodes are expanded and contracted, storing the filename encodings of each file along
+ * with the file in a FileNode doesn't help because it doesn't last. Instead of rediscovering
+ * the encoding at every stage by querying the metadataXML file, we store the encodings for
+ * fast access: in a map of (URLEncodedFilePath, filename-encoding) pairs.
+ * The current design of the map is to only store any active filename metadata assigned
+ * directly at that file/folder's level, and if there is none discovered at that level, then
+ * storing the empty string for it. Therefore, if the hashmap contains no entry for
+ * a file, it means this still needs to be retrieved. */
+ public static Map map = new HashMap();
+
+//*********************** BUSY REFRESHING / REQUIRING REFRESH *********************
+
+ /** Set to true if filename encoding metadata was changed. Called by the enter keyPress
+ * event in gui.EnrichPane and when the gs.FilenameEncoding field loses focus. */
+ private static boolean refreshRequired = false;
+
+ synchronized public static boolean isRefreshRequired() {
+ return refreshRequired;
+ }
+
+ synchronized public static void setRefreshRequired(boolean state) {
+ if(MULTIPLE_FILENAME_ENCODINGS_SUPPORTED) {
+ refreshRequired = state;
+ } else {
+ refreshRequired = false;
+ }
+ }
+
+//************************** MAP RETRIEVAL METHODS ******************************
+
+ /** Returns the cumulative gs.filenameEncoding metadata
+ * assigned to a file inside the collection. */
+ public static String findFilenameEncoding(
+ File file, String urlEncodedFilePath, boolean bruteForceLookup)
+ {
+ //if(bruteForceLookup) {
+ // return findFilenameEncodingBruteForce(file, urlEncodedFilePath, bruteForceLookup);
+ //}
+
+ String encoding = "";
+
+ // Check any assigned encoding at this level, starting with the map first
+ // and else retrieving the filename encoding from the metadata file
+ if(!map.containsKey(urlEncodedFilePath)) {
+
+ // Check for filename encoding metadata *directly* associated with the file
+ // Now don't need to get any inherited encoding metadata here, because of
+ // the way we're storing and retrieving encoding information from the map.
+ ArrayList list = MetadataXMLFileManager.getMetadataAssignedDirectlyToFile(file, true); // true: gets gs.filenameEncoding only
+ if(!list.isEmpty()) {
+ MetadataValue metavalue = (MetadataValue)list.get(0); // get(list.size()-1);
+ encoding = metavalue.getValue();
+ } // else no filename encoding set yet at this level
+
+ // Now we've done a lookup at this level cache the result in the map,
+ // including empty strings, to indicate that we've done a full lookup
+ map.put(urlEncodedFilePath, encoding);
+ }
+ else { // an entry exists in the map, get it from there
+ encoding = (String)map.get(urlEncodedFilePath);
+ }
+
+ // if no meta was specified at at the file level, look for any inherited metadata
+ if(encoding.equals("")) {
+ encoding = getInheritedFilenameEncoding(urlEncodedFilePath, file);
+ }
+
+ //System.err.println("\n@@@@Looked for: " + urlEncodedFilePath + " | found: " + encoding);
+ return encoding; // found something in map, may still be "", but it's what was stored
+ }
+
+ /** Checks the file-to-encoding map for all the superfolders of the given
+ * filename in sequence for an applicable encoding. Note that the file/folder
+ * at the level of urlFoldername (and dir) has already been inspected. */
+ static public String getInheritedFilenameEncoding(String urlFoldername, File dir)
+ {
+ String encoding = "";
+ boolean done = false;
+
+ // don't want to search past import folder which is as
+ // far as we need to go to determine inherited encodings
+ File importDir = new File(CollectionManager.getLoadedCollectionImportDirectoryPath());
+ if(dir.equals(importDir)) { // if the top-level dir was already checked, we're done
+ done = true;
+ }
+
+ // For directories, first remove trailing file separator in order to start checking from higher level folders
+ int lastIndex = urlFoldername.length()-1;
+ char urlFileSeparatorChar = URL_FILE_SEPARATOR.charAt(0);
+ if(urlFoldername.charAt(lastIndex) == urlFileSeparatorChar) {
+ urlFoldername = urlFoldername.substring(0, lastIndex);
+ }
+
+ while(!done) {
+ // get the folder that's one level up
+ dir = dir.getParentFile();
+
+ int index = urlFoldername.lastIndexOf(URL_FILE_SEPARATOR);
+ if(index == -1) { // no more slashes
+ done = true;
+ } else {
+ urlFoldername = urlFoldername.substring(0, index);
+ }
+
+ // now look in the map to see whether there's an encoding for this folder
+ String folder = urlFoldername + URL_FILE_SEPARATOR;
+ if(map.containsKey(folder)) {
+ encoding = (String)map.get(folder); // may be ""
+ } else { // no entry in map, so look in the metadata.xml at this folder level
+ ArrayList list = MetadataXMLFileManager.getMetadataAssignedDirectlyToFile(
+ dir, true); // true: gets gs.filenameEncoding only
+ if(!list.isEmpty()) {
+ MetadataValue metavalue = (MetadataValue)list.get(0); // get(list.size()-1);
+ encoding = metavalue.getValue();
+ }
+ map.put(folder, encoding); // may be ""
+ }
+
+ if(!encoding.equals("")){
+ done = true;
+ } // else if "", loop to check next folder up
+ else if(dir.equals(importDir)) { // don't iterate past the import folder, which we've now checked
+ done = true;
+ }
+ }
+
+ return encoding;
+ }
+
+ /** Called by GUIManager when a collection is closed. This then empties the
+ * file-to-encoding map which is applicable only on a per-collection basis */
+ static public void closeCollection() {
+ //printFilenameMap("Closing collection. Clearing file-to-encoding map of entries:");
+ map.clear();
+ }
+
+ // Useful for debugging: prints contents of file-to-encoding map
+ static public void printFilenameMap(String heading) {
+ System.err.println("\n********************************************");
+ System.err.println(heading.toUpperCase());
+ Iterator entries = map.entrySet().iterator();
+ while(entries.hasNext()) {
+ Map.Entry entry = (Map.Entry)entries.next();
+ System.err.println("+ " + (String)entry.getKey() + ": " + (String)entry.getValue());
+ }
+ System.err.println("********************************************\n");
+ }
+
+ // UNUSED at present. Brute force version of the findFilenameEncoding() method
+ // Doesn't use the map, but gets *all* the metadata assigned to a file/folder to
+ // work out the encoding applicable to a file/folder.
+ public static String findFilenameEncodingBruteForce(File file, String urlEncodedFilename,
+ boolean bruteForceLookup)
+ {
+ System.err.println("\n***** BRUTE FORCE getFilenameEncoding() called\n");
+
+
+ String encoding = "";
+
+ // Check for filename encoding metadata *directly* associated with the file
+ // Now don't need to get any inherited encoding metadata here, because of
+ // the way we're storing and retrieving encoding information from the map.
+
+ ArrayList list = MetadataXMLFileManager.getMetadataAssignedToFile(file, true); // true: gets gs.filenameEncoding only
+ if(!list.isEmpty()) {
+ // try to get the filename encoding meta that was assigned last to this
+ // file, even though it makes no sense to have multiple values for it
+ MetadataValue metavalue = (MetadataValue)list.get(list.size()-1);
+ encoding = metavalue.getValue();
+
+ if(encoding == null) { // unlikely ???
+ System.err.println("**** ERROR: encoding for "
+ + urlEncodedFilename + " is NULL!");
+ encoding = "";
+ }
+ } // else no filename encoding set yet, perhaps
+ //System.err.println("**** Found encoding for " + urlEncodedFilename + " " + encoding);
+ return encoding;
+ }
+
+//****************************** APPLYING ENCODINGS TO FILENAMES *****************************
+
+ /** URL encoded version of the byte codes of the given file's name */
+ public static String calcURLEncodedFilePath(File file) {
+ if(!MULTIPLE_FILENAME_ENCODINGS_SUPPORTED) {
+ return file.getAbsolutePath();
+ }
+ else {
+ String filename = fileToURLEncoding(file);
+ return filename;
+ }
+ }
+
+ /** URL encoded version of the byte codes of this file's name */
+ public static String calcURLEncodedFileName(String urlfilepath) {
+ String filename = urlfilepath;
+ if(filename.endsWith(URL_FILE_SEPARATOR)) { // directory, remove trailing slash
+ filename = filename.substring(0, filename.length() - 1);
+ }
+
+ // remove the directory prefix (if any) to get the filename
+ int index = filename.lastIndexOf(URL_FILE_SEPARATOR);
+ if(index != -1) {
+ filename = filename.substring(index+1); // skip separator
+ }
+
+ return filename;
+ }
+
+ /** Given a string representing an alias to an official encoding (and unofficial ones
+ * starting with "Latin-"), attempts to work out what the canonical encoding for that is.
+ * If the given encoding is unrecognised, it is returned as is. */
+ public static String canonicalEncodingName(String encoding) {
+ String canonicalEncoding = encoding;
+ try {
+ // Latin-1 -> ISO-8859-1
+ String alias = canonicalEncoding.toLowerCase();
+ if(alias.startsWith("latin")){
+ canonicalEncoding = "ISO-8859" + alias.substring("latin".length());
+ }
+
+ // canonical encoding for official aliases
+ canonicalEncoding = Charset.forName(canonicalEncoding).name();
+ return canonicalEncoding;
+ } catch (Exception e) {
+ System.err.println("(Could not recognise encoding (alias): "
+ + encoding + ".)");
+ return encoding; // no alias could be found, return the original parameter
+ }
+ }
+
+//************************* GETTING THE URL ENCODING OF FILENAMES *********************************
+ // Dr Bainbridge's methods
+ /* On Linux machines that are set to using an ISO-8859 (Latin) type encoding,
+ * we can work with URL-encoded filenames in Java. Java works with whatever
+ * encoding the filesystem uses. Unlike systems working with UTF-8, where Java
+ * interprets filenames as UTF-8 (a destructive process since characters invalid
+ * for UTF-8 are replaced with the invalid character, which means the original
+ * character's byte codes can not be regained), working with an ISO-8859-1
+ * system means the original byte codes of the characters are preserved,
+ * regardless of whether the characters represent ISO-8859-1 or not. Such byte
+ * codes are converted by the following method to the correct URL versions of
+ * the strings that the filenames represent (that is, the correct URL representations
+ * of the filenames in their original encodings). This is useful for interactions with
+ * Perl as Java and Perl can use URL-encoded filenames to talk about the same files
+ * on the file system, instead of having to work out what encoding they are in. */
+
+ public static String fileToURLEncoding(File file) {
+ if(!MULTIPLE_FILENAME_ENCODINGS_SUPPORTED) {
+ return file.getAbsolutePath();
+ }
+
+ String filename_url_encoded = "";
+
+ // The following test for whether the file exists or not is a problem
+ // when a File object--whose actual file is in the process of being moved
+ // and therefore temporarily does not 'exist' on the actual system--can't
+ // be URL encoded: the following would return "" when a file doesn't exist.
+ // So commenting out the test.
+ /*
+ if(!file.getName().equals("recycle")) {
+ if(!file.isFile() && !file.isDirectory()) {
+ System.err.println("*** ERROR. Java can't see file: " + file.getAbsolutePath());
+ return "";
+ }
+
+ if(!file.exists()) {
+ System.err.println("*** NOTE: File doesn't exist: " + file.getAbsolutePath());
+ return ""; //file.getName();
+ }
+ }
+ */
+
+ URI filename_uri = file.toURI();
+ try {
+ // The trick:
+ // 1. toASCIIString() will %xx encode values > 127
+ // 2. Decode the result to "ISO-8859-1"
+ // 3. URL encode the bytes to string
+
+ // Step 2 forces the string to be 8-bit values. It
+ // doesn't matter if the starting raw filename was *not*
+ // in the ISO-8859-1 encoding, the effect is to ensure
+ // we have an 8-bit byte string that (numerically)
+ // captures the right value. These numerical values are
+ // then used to determine how to URL encode it
+
+ String filename_ascii = filename_uri.toASCIIString();
+ String filename_raw_bytes = URLDecoder.decode(filename_ascii,"ISO-8859-1");
+ filename_url_encoded = iso_8859_1_filename_to_url_encoded(filename_raw_bytes);
+
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ // Give up trying to convert
+ filename_url_encoded = file.getAbsolutePath();
+ }
+ return filename_url_encoded;
+ }
+
+
+ // For unicode codepoints see:
+ // http://unicode.org/Public/MAPPINGS/ISO8859/8859-1.TXT for ISO8859-1 (Latin-1)
+ // where 0xE2 maps to codepoint 0x00E2 and is defined as "Latin small letter a with circumflex"
+ // http://unicode.org/Public/MAPPINGS/ISO8859/8859-7.TXT for ISO8859-7 (Greek)
+ // where 0xE2 maps to codepoint 0x03B2 and is defined as "Greek small letter beta"
+ public static String iso_8859_1_filename_to_url_encoded(String raw_bytes_filename)
+ throws Exception
+ {
+ String urlEncoded = "";
+
+ try {
+ // By this point we have a UTF-8 encoded string that captures
+ // what the ISO-8859-1 (Latin-1) character is that corresponded to the
+ // 8-bit numeric value for that character in the filename
+ // on the file system
+
+ // For example:
+ // File system char: = %E2
+ // Equivalent Latin 1 char: = %E2
+ // Mapped to UTF-8: =
+
+ // Our task is to take the string the contains and ensure that
+ // we "see" it as
+
+ byte [] raw_bytes = raw_bytes_filename.getBytes("ISO-8859-1");
+ String unicode_filename = new String(raw_bytes,"UTF-8");
+
+ for(int i = 0; i < unicode_filename.length(); i++) {
+ char charVal = unicode_filename.charAt(i);
+ if ((int)charVal > 255) {
+ urlEncoded += String.format("%02X;", (int)charVal);
+ }
+ else if((int)charVal > 127) {
+ urlEncoded += String.format("%%%02X", (int)charVal);
+ } else {
+ urlEncoded += String.format("%c", (char)charVal);
+ }
+ }
+ }
+ catch (Exception e) {
+ //e.printStackTrace();
+ throw(e);
+ }
+
+ return urlEncoded;
+ }
+
+ // unused for now
+ public static String raw_filename_to_url_encoded(String fileName)
+ throws Exception
+ {
+ String urlEncoded = "";
+ try {
+ byte[] bytes = fileName.getBytes();
+
+ for(int i = 0; i < bytes.length; i++) {
+ // mask each byte (by applying & 0xFF) to make the signed
+ // byte (in the range -128 to 127) unsigned (in the range
+ // 0 to 255).
+
+ int byteVal = (int)(bytes[i] & 0xFF);
+
+ if(byteVal > 127) {
+ urlEncoded += String.format("%%%02X", (int)byteVal);
+ } else {
+ urlEncoded += String.format("%c",(char)byteVal);
+ }
+ }
+ }
+ catch (Exception e) {
+ //e.printStackTrace();
+ throw(e);
+ }
+
+ return urlEncoded;
+ }
+
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/MetadataAuditTableModel.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/MetadataAuditTableModel.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/MetadataAuditTableModel.java (revision 31635)
@@ -0,0 +1,240 @@
+/**
+ *############################################################################
+ * A component of the Greenstone Librarian Interface, part of the Greenstone
+ * digital library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
+ *
+ * Copyright (C) 2004 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################################################
+ */
+
+package org.greenstone.gatherer.metadata;
+
+
+import java.awt.*;
+import java.io.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.table.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.collection.CollectionTreeNode;
+
+
+public class MetadataAuditTableModel
+ extends AbstractTableModel
+{
+ /** It is not expected that this class will ever be serialized */
+ private static final long serialVersionUID = 0L;
+
+ /** The CollectionTreeNodes this model is built for */
+ private CollectionTreeNode[] file_nodes = null;
+ /** The list of MetadataElements in the table (columns) */
+ private ArrayList metadata_elements = new ArrayList();
+ /** The list of MetadataAuditTableEntries in the table (rows) */
+ private ArrayList metadata_audit_table_entries = new ArrayList();
+
+
+ /** Returns the number of columns in this table. */
+ public int getColumnCount()
+ {
+ return 1 + metadata_elements.size();
+ }
+
+
+ /** Returns the name of the specified column. */
+ public String getColumnName(int col)
+ {
+ // First column: filename
+ if (col == 0) {
+ return Dictionary.get("AuditTable.File");
+ }
+
+ int metadata_element_index = col - 1;
+ return metadata_elements.get(metadata_element_index).toString();
+ }
+
+
+ /** Returns the values in a particular column. */
+ public ArrayList getColumnValues(int col)
+ {
+ // First column: filename
+ if (col == 0) {
+ ArrayList column_values = new ArrayList();
+ for (int i = 0; i < metadata_audit_table_entries.size(); i++) {
+ MetadataAuditTableEntry metadata_audit_table_entry = (MetadataAuditTableEntry) metadata_audit_table_entries.get(i);
+ column_values.add(metadata_audit_table_entry.getFilename());
+ }
+
+ return column_values;
+ }
+
+ // Check values are reasonable
+ int metadata_element_index = col - 1;
+ if (metadata_element_index < 0 || metadata_element_index >= metadata_elements.size()) {
+ return null;
+ }
+
+ ArrayList column_values = new ArrayList();
+ for (int i = 0; i < metadata_audit_table_entries.size(); i++) {
+ MetadataAuditTableEntry metadata_audit_table_entry = (MetadataAuditTableEntry) metadata_audit_table_entries.get(i);
+ MetadataValueTreeNode[] metadata_value_tree_nodes = metadata_audit_table_entry.getMetadataValueTreeNodes(metadata_element_index);
+ if (metadata_value_tree_nodes != null) {
+ for (int j = 0; j < metadata_value_tree_nodes.length; j++) {
+ column_values.add(metadata_value_tree_nodes[j].toString());
+ }
+ }
+ }
+
+ return column_values;
+ }
+
+
+ /** Returns the number of rows in this table. */
+ public int getRowCount()
+ {
+ return metadata_audit_table_entries.size();
+ }
+
+
+ /** Returns the cell value at a given row and column as an Object. */
+ public Object getValueAt(int row, int col)
+ {
+ // Check values are reasonable
+ if (row < 0 || row >= metadata_audit_table_entries.size()) {
+ return null;
+ }
+
+ // First column: filename
+ if (col == 0) {
+ ArrayList cell_values = new ArrayList();
+ cell_values.add(((MetadataAuditTableEntry) metadata_audit_table_entries.get(row)).getFilename());
+ return cell_values;
+ }
+
+ // Check values are reasonable
+ int metadata_element_index = col - 1;
+ if (metadata_element_index < 0 || metadata_element_index >= metadata_elements.size()) {
+ return null;
+ }
+
+ ArrayList cell_values = new ArrayList();
+ MetadataAuditTableEntry metadata_audit_table_entry = (MetadataAuditTableEntry) metadata_audit_table_entries.get(row);
+ MetadataValueTreeNode[] metadata_value_tree_nodes = metadata_audit_table_entry.getMetadataValueTreeNodes(metadata_element_index);
+ if (metadata_value_tree_nodes != null) {
+ for (int j = 0; j < metadata_value_tree_nodes.length; j++) {
+ cell_values.add(metadata_value_tree_nodes[j].toString());
+ }
+ }
+
+ return cell_values;
+ }
+
+
+ public void rebuild(CollectionTreeNode[] file_nodes)
+ {
+ metadata_elements = MetadataSetManager.getEveryMetadataSetElement();
+ metadata_audit_table_entries.clear();
+
+ this.file_nodes = file_nodes;
+ if (file_nodes != null && file_nodes.length > 0) {
+ // Create model builder
+ MetadataAuditTableModelBuilder builder = new MetadataAuditTableModelBuilder();
+ builder.run();
+ }
+ }
+
+
+ private class MetadataAuditTableModelBuilder
+ {
+ // Build a list of MetadataAuditTableEntries that represent the metadata asssigned to the selected files
+ public void run()
+ {
+ // Process each of the selected files in turn
+ for (int i = 0; i < file_nodes.length; i++) {
+ File current_file = file_nodes[i].getFile();
+
+ // Get the metadata assigned to this file
+ ArrayList current_file_metadata = MetadataXMLFileManager.getMetadataAssignedToFile(current_file);
+
+ // Get the extracted metadata for this file, if desired
+ if (Configuration.get("general.view_extracted_metadata", Configuration.COLLECTION_SPECIFIC) == true) {
+ current_file_metadata.addAll(DocXMLFileManager.getMetadataExtractedFromFile(current_file));
+ }
+
+ MetadataAuditTableEntry metadata_audit_table_entry = new MetadataAuditTableEntry(current_file, current_file_metadata);
+ metadata_audit_table_entries.add(metadata_audit_table_entry);
+ }
+ }
+ }
+
+
+ /** This class represents one (file, metadata) pair */
+ private class MetadataAuditTableEntry
+ {
+ private File file = null;
+ private ArrayList[] metadata_values = null;
+
+
+ public MetadataAuditTableEntry(File file, ArrayList metadata_values_list)
+ {
+ this.file = file;
+
+ metadata_values = new ArrayList[metadata_elements.size()];
+ for (int i = 0; i < metadata_values.length; i++) {
+ metadata_values[i] = new ArrayList();
+ }
+
+ for (int i = 0; i < metadata_values_list.size(); i++) {
+ MetadataValue metadata_value = (MetadataValue) metadata_values_list.get(i);
+ MetadataElement metadata_element = metadata_value.getMetadataElement();
+
+ // Find the right column to put the value in
+ for (int j = 0; j < metadata_elements.size(); j++) {
+ if (metadata_element.equals(metadata_elements.get(j))) {
+ metadata_values[j].add(metadata_value);
+ break;
+ }
+ }
+ }
+ }
+
+
+ public String getFilename()
+ {
+ return file.getName();
+ }
+
+
+ public MetadataValueTreeNode[] getMetadataValueTreeNodes(int metadata_element_index)
+ {
+ ArrayList metadata_values_list = metadata_values[metadata_element_index];
+ if (metadata_values_list == null) {
+ return null;
+ }
+
+ MetadataValueTreeNode[] metadata_value_tree_nodes = new MetadataValueTreeNode[metadata_values_list.size()];
+ for (int i = 0; i < metadata_values_list.size(); i++) {
+ metadata_value_tree_nodes[i] = ((MetadataValue) metadata_values_list.get(i)).getMetadataValueTreeNode();
+ }
+
+ return metadata_value_tree_nodes;
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/MetadataChangedListener.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/MetadataChangedListener.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/MetadataChangedListener.java (revision 31635)
@@ -0,0 +1,37 @@
+/**
+ *############################################################################
+ * A component of the Greenstone Librarian Interface, part of the Greenstone
+ * digital library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
+ *
+ * Copyright (C) 2005 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################################################
+ */
+
+package org.greenstone.gatherer.metadata;
+
+
+import org.greenstone.gatherer.collection.CollectionTreeNode;
+
+
+public interface MetadataChangedListener
+{
+ /** Notification that the metadata assigned to the collection has changed. */
+ public void metadataChanged(CollectionTreeNode[] file_nodes);
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/MetadataElement.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/MetadataElement.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/MetadataElement.java (revision 31635)
@@ -0,0 +1,231 @@
+/**
+ *############################################################################
+ * A component of the Greenstone Librarian Interface, part of the Greenstone
+ * digital library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
+ *
+ * Copyright (C) 2004 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################################################
+ */
+
+package org.greenstone.gatherer.metadata;
+
+
+import java.io.File;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.util.XMLTools;
+import org.greenstone.gatherer.util.StaticStrings;
+import org.w3c.dom.*;
+
+
+/** This class represents one Metadata element */
+public class MetadataElement
+{
+ // Language-independent metadata element name, with namespace (eg. dc.Subject)
+ private String metadata_element_name_full = null;
+ private Element metadata_element_element = null;
+ private MetadataValueTreeModel metadata_value_tree_model = null;
+ /* this is true if the metadata element comes with a pre defined set of values */
+ private boolean predefined_values = false;
+ /* This restricts the user to only the predefined values */
+ private boolean restricted_values = false;
+ /* can we have more than one value for this metadata element per document? */
+ /* default, true = yes */
+ private boolean accumulating = true;
+ public MetadataElement(String metadata_element_name_full, Element metadata_element_element)
+ {
+ this.metadata_element_name_full = metadata_element_name_full;
+ this.metadata_element_element = metadata_element_element;
+ this.metadata_value_tree_model = new MetadataValueTreeModel(this);
+ if (metadata_element_element != null) {
+ if (metadata_element_element.getAttribute(StaticStrings.PREDEFINED_METADATA_ATTRIBUTE).equals("true")) {
+ predefined_values = true;
+ loadOptionList();
+ if (metadata_element_element.getAttribute(StaticStrings.RESTRICTED_METADATA_ATTRIBUTE).equals("true")) {
+ // cant have restricted values if no predefined values
+ restricted_values = true;
+ }
+ }
+ if (metadata_element_element.getAttribute("accumulating").equals("false")) {
+ accumulating = false;
+ }
+ }
+ }
+
+ // do this better
+ protected boolean loadOptionList() {
+ NodeList values = metadata_element_element.getElementsByTagName("Value");
+ for (int i=0; i 0) {
+ return i - 1;
+ }
+ }
+
+ // Have to select the last entry
+ return metadata_value_table_entries.size() - 1;
+ }
+
+
+ public Class getColumnClass(int col)
+ {
+ return getValueAt(0, col).getClass();
+ }
+
+
+ /** Returns the number of columns in this table. */
+ public int getColumnCount()
+ {
+ return COLUMN_NAMES.length;
+ }
+
+
+ /** Retrieves the name of the specified column. */
+ public String getColumnName(int col)
+ {
+ return COLUMN_NAMES[col];
+ }
+
+
+ /* Called to retrieve the MetadataValueTableEntry at a certain row. Usually caused by the user selecting a row in the table. It is synchronized so that the model doesn't up and change while we're trying to retrieve the indicated element. */
+ public synchronized MetadataValueTableEntry getMetadataValueTableEntry(int row)
+ {
+ if (row >= 0 && row < metadata_value_table_entries.size()) {
+ return (MetadataValueTableEntry) metadata_value_table_entries.get(row);
+ }
+
+ return null;
+ }
+
+
+ /** Returns the number of rows in this table. */
+ public int getRowCount()
+ {
+ return metadata_value_table_entries.size();
+ }
+
+
+ /** Returns the cell value at a given row and column as an Object. */
+ public Object getValueAt(int row, int col)
+ {
+ // Check values are reasonable
+ if (row < 0 || row >= metadata_value_table_entries.size() || col < 0 || col >= COLUMN_NAMES.length) {
+ return null;
+ }
+
+ MetadataValueTableEntry metadata_value_table_entry = (MetadataValueTableEntry) metadata_value_table_entries.get(row);
+
+ if(metadata_value_table_entry == null) {
+ System.err.println("\n@@@@@ MetadataValueTableModel.getValueAt(): metadata_value_table_entry is unexpectedly null!\n");
+ return null;
+ }
+
+ if (col == 0 && metadata_value_table_entry.isInheritedMetadata()) {
+ return metadata_value_table_entry.getFolderMetadataInheritedFrom();
+ }
+
+ if (col == 1) {
+ return metadata_value_table_entry.getMetadataElement();
+ }
+
+ if (col == 2)
+ {
+ return decodeHTMLEntities(metadata_value_table_entry.getFullValue());
+ }
+
+ return null;
+ }
+
+ public String decodeHTMLEntities(String text)
+ {
+ text = text.replace("(", "(");
+ text = text.replace(")", ")");
+ text = text.replace(",", ",");
+ text = text.replace("<", "<");
+ text = text.replace(">", ">");
+ text = text.replace("[", "[");
+ text = text.replace("]", "]");
+ text = text.replace("{", "{");
+ text = text.replace("}", "}");
+ return text;
+ }
+
+ public boolean isCellEditable(int row, int col)
+ {
+ // The inherited and element columns are never editable
+ if (col < 2) {
+ return false;
+ }
+
+ // Extracted and inherited metadata is not editable
+ MetadataValueTableEntry metadata_value_table_entry = (MetadataValueTableEntry) metadata_value_table_entries.get(row);
+ if (metadata_value_table_entry.getMetadataElement().isExtractedMetadataElement() || metadata_value_table_entry.isInheritedMetadata()) {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ /** Determine if the given metadata is common to all selected file nodes. */
+ public boolean isCommon(MetadataValueTableEntry metadata_value_table_entry)
+ {
+ return (file_nodes != null && metadata_value_table_entry.getOccurrences() == file_nodes.length);
+ }
+
+
+ /** Determine if the given metadata is common to all selected file nodes. */
+ public boolean isCommon(int row)
+ {
+ if (row >= 0 && row < metadata_value_table_entries.size()) {
+ return isCommon((MetadataValueTableEntry) metadata_value_table_entries.get(row));
+ }
+
+ return false;
+ }
+
+
+ public void rebuild(CollectionTreeNode[] file_nodes)
+ {
+ this.file_nodes = file_nodes;
+ metadata_value_table_entries.clear();
+
+ // Collection is in a state of flux
+ if (!Gatherer.c_man.ready()) {
+ return;
+ }
+
+ if (file_nodes == null || file_nodes.length == 0) {
+ return;
+ }
+
+ // Create model builder
+ MetadataValueTableModelBuilder builder = new MetadataValueTableModelBuilder();
+ builder.run();
+ }
+
+
+ public void setValueAt(Object new_metadata_value, int row, int col)
+ {
+ MetadataValueTableEntry metadata_value_table_entry = getMetadataValueTableEntry(row);
+ if(metadata_value_table_entry == null) {
+ System.err.println("\n@@@@@ MetadataValueTableModel.setValueAt(): metadata_value_table_entry is unexpectedly null!\n");
+ return;
+ }
+
+ // If nothing has changed no action is necessary
+ String old_metadata_value = metadata_value_table_entry.getFullValue();
+ if (new_metadata_value.equals(old_metadata_value)) {
+ return;
+ }
+
+ // Lock the interface so nothing can be changed while the metadata edit is being processed
+ Gatherer.g_man.wait(true);
+
+
+ // Check for a restricted metadata element
+ MetadataElement metadata_element = metadata_value_table_entry.getMetadataElement();
+ if (!new_metadata_value.equals("") && metadata_element.isRestricted()) {
+ if (metadata_element.getMetadataValueTreeNode((String)new_metadata_value)==null) {
+ WarningDialog dialog = new WarningDialog("warning.InvalidMetadata", Dictionary.get("InvalidMetadata.Title"), Dictionary.get("InvalidMetadata.Message"), null, false);
+ int dialog_result = dialog.display();
+ dialog.dispose();
+ Gatherer.g_man.wait(false);
+ return;
+
+ }
+ }
+
+ // Metadata value added
+ if (old_metadata_value.equals("") && !new_metadata_value.equals("")) {
+ // If we're adding metadata to folders display the warning
+ if (!file_nodes[0].isLeaf()) {
+ WarningDialog dialog = new WarningDialog("warning.DirectoryLevelMetadata", Dictionary.get("DirectoryLevelMetadata.Title"), Dictionary.get("DirectoryLevelMetadata.Message"), null, true);
+ int dialog_result = dialog.display();
+ dialog.dispose();
+ if (dialog_result != JOptionPane.OK_OPTION) {
+ Gatherer.g_man.wait(false);
+ return;
+ }
+ }
+
+ MetadataValueTreeNode metadata_value_tree_node = metadata_element.addMetadataValue((String) new_metadata_value);
+ MetadataValue metadata_value = new MetadataValue(metadata_element, metadata_value_tree_node);
+ metadata_value.setIsAccumulatingMetadata(true);
+ (new AppendMetadataTask(metadata_value)).run();
+ }
+
+ // Metadata value removed
+ else if (!old_metadata_value.equals("") && new_metadata_value.equals("")) {
+ (new RemoveMetadataTask(metadata_value_table_entry)).run();
+ }
+
+ // Metadata value replaced
+ else {
+ MetadataValueTreeNode metadata_value_tree_node = metadata_element.addMetadataValue((String) new_metadata_value);
+ MetadataValue metadata_value = new MetadataValue(metadata_element, metadata_value_tree_node);
+ metadata_value.setIsAccumulatingMetadata(!metadata_value_table_entry.isInheritedMetadata());
+ (new ReplaceMetadataTask(metadata_value_table_entry, metadata_value)).run();
+ }
+ }
+
+
+ private class AppendMetadataTask
+ // extends Thread
+ {
+ private MetadataValue metadata_value = null;
+
+ private AppendMetadataTask(MetadataValue metadata_value)
+ {
+ this.metadata_value = metadata_value;
+ }
+
+ public void run()
+ {
+ try {
+ // Edit metadata.xml files to add the metadata
+ MetadataXMLFileManager.addMetadata(file_nodes, metadata_value);
+ }
+ catch (Exception exception) {
+ // We need to catch any exceptions here so the interface is unlocked below
+ DebugStream.printStackTrace(exception);
+ }
+
+ // Operation finished, so turn busy cursor off and unlock interface
+ Gatherer.g_man.wait(false);
+ }
+ }
+
+
+ private class ReplaceMetadataTask
+ // extends Thread
+ {
+ private MetadataValueTableEntry selected_metadata_value_table_entry = null;
+ private MetadataValue metadata_value = null;
+
+ private ReplaceMetadataTask(MetadataValueTableEntry selected_metadata_value_table_entry, MetadataValue metadata_value)
+ {
+ this.selected_metadata_value_table_entry = selected_metadata_value_table_entry;
+ this.metadata_value = metadata_value;
+ }
+
+ public void run()
+ {
+ try {
+ // Edit metadata.xml files to replace the metadata
+ MetadataXMLFileManager.replaceMetadata(file_nodes, selected_metadata_value_table_entry, metadata_value);
+ }
+ catch (Exception exception) {
+ // We need to catch any exceptions here so the interface is unlocked below
+ DebugStream.printStackTrace(exception);
+ }
+
+ // Operation finished, so turn busy cursor off and unlock interface
+ Gatherer.g_man.wait(false);
+ }
+ }
+
+
+ private class RemoveMetadataTask
+ // extends Thread
+ {
+ private MetadataValueTableEntry selected_metadata_value_table_entry = null;
+
+ private RemoveMetadataTask(MetadataValueTableEntry selected_metadata_value_table_entry)
+ {
+ this.selected_metadata_value_table_entry = selected_metadata_value_table_entry;
+ }
+
+ public void run()
+ {
+ try {
+ // Edit metadata.xml files to remove the metadata
+ MetadataXMLFileManager.removeMetadata(file_nodes, selected_metadata_value_table_entry);
+ }
+ catch (Exception exception) {
+ // We need to catch any exceptions here so the interface is unlocked below
+ DebugStream.printStackTrace(exception);
+ }
+
+ // Operation finished, so turn busy cursor off and unlock interface
+ Gatherer.g_man.wait(false);
+ }
+ }
+
+
+ private class MetadataValueTableModelBuilder
+ {
+ public void run()
+ {
+ // System.err.println("Building MetadataValueTableModel...");
+
+ // Build a list of MetadataValueTableEntries that represent the metadata asssigned to the selected files
+ boolean hid_extracted_metadata = false;
+ ArrayList metadata_elements_seen = new ArrayList();
+
+ // Process each of the selected files in turn
+ for (int i = 0; i < file_nodes.length; i++) {
+ File current_file = file_nodes[i].getFile();
+
+ // Get the metadata assigned to this file
+ ArrayList assigned_metadata = MetadataXMLFileManager.getMetadataAssignedToFile(current_file);
+ for (int j = 0; j < assigned_metadata.size(); j++) {
+ MetadataValue metadata_value = (MetadataValue) assigned_metadata.get(j);
+ MetadataElement metadata_element = metadata_value.getMetadataElement();
+
+ // Insert this metadata value into the table, unless it already exists (in which case increment its count)
+ insertMetadataValue(metadata_value);
+
+ // Remember we have seen this metadata element
+ if (metadata_elements_seen.contains(metadata_element) == false) {
+ metadata_elements_seen.add(metadata_element);
+ }
+ }
+
+ // Get the extracted metadata for this file, if desired
+ if (Configuration.get("general.view_extracted_metadata", Configuration.COLLECTION_SPECIFIC) == true) {
+ ArrayList extracted_metadata = DocXMLFileManager.getMetadataExtractedFromFile(current_file);
+ for (int k = 0; k < extracted_metadata.size(); k++) {
+ MetadataValue metadata_value = (MetadataValue) extracted_metadata.get(k);
+
+ // Insert this metadata value into the table, unless it already exists (in which case increment its count)
+ insertMetadataValue(metadata_value);
+ }
+ }
+ }
+
+ // Make sure each non-extracted metadata element appears in the table (even if blank)
+ ArrayList every_metadata_set_element = MetadataSetManager.getEveryMetadataSetElement();
+ for (int i = 0; i < every_metadata_set_element.size(); i++) {
+ MetadataElement metadata_element = (MetadataElement) every_metadata_set_element.get(i);
+
+ // If we haven't seen this metadata element and it isn't extracted, add it now
+ if (!metadata_elements_seen.contains(metadata_element) && !metadata_element.isExtractedMetadataElement()) {
+ MetadataValueTableEntry metadata_value_table_entry = new MetadataValueTableEntry(metadata_element, new MetadataValueTreeNode(""));
+
+ // Blank metadata is common to all selected files (otherwise it wouldn't be blank!)
+ metadata_value_table_entry.setOccurrences(file_nodes.length);
+
+ // Add it to the table
+ insertMetadataValueTableEntry(metadata_value_table_entry);
+ }
+ }
+
+ // If extracted metadata was hidden, display the warning
+ if (hid_extracted_metadata) {
+ showExtractedMetadataWarning();
+ }
+ }
+
+
+ /** Inserts the new metadata value into the table */
+ private int insertMetadataValue(MetadataValue metadata_value)
+ {
+ return insertMetadataValueTableEntry(new MetadataValueTableEntry(metadata_value));
+ }
+
+
+ /** Inserts the new metadata value table entry into the table */
+ private int insertMetadataValueTableEntry(MetadataValueTableEntry metadata_value_table_entry)
+ {
+ MetadataElement metadata_element = metadata_value_table_entry.getMetadataElement();
+
+ // Find the correct place to insert the table entry
+ for (int i = 0; i < metadata_value_table_entries.size(); i++) {
+ MetadataValueTableEntry current_metadata_value_table_entry = (MetadataValueTableEntry) metadata_value_table_entries.get(i);
+ int element_comparison = MetadataSetManager.compareMetadataElements(current_metadata_value_table_entry.getMetadataElement(), metadata_element);
+
+ // We've found the right element, so check if the value already exists
+ if (element_comparison == 0) {
+ int value_comparison = current_metadata_value_table_entry.compareTo(metadata_value_table_entry);
+ if (value_comparison == 0) {
+ // Entry already exists, so increment count (except for blank entries)
+ if (!metadata_value_table_entry.getFullValue().equals("")) {
+ current_metadata_value_table_entry.anotherOccurrence();
+ }
+ return i;
+ }
+ }
+
+ // Found insertion point
+ if (element_comparison > 0) {
+ metadata_value_table_entries.add(i, metadata_value_table_entry);
+ fireTableRowsInserted(i, i);
+ return i;
+ }
+ }
+
+ // Must go at the end of the table
+ metadata_value_table_entries.add(metadata_value_table_entry);
+ fireTableRowsInserted(metadata_value_table_entries.size() - 1, metadata_value_table_entries.size() - 1);
+ return metadata_value_table_entries.size() - 1;
+ }
+
+
+ private void showExtractedMetadataWarning()
+ {
+ Runnable task = new Runnable() {
+ public void run() {
+ WarningDialog dialog = new WarningDialog("warning.ExtractedMetadata", Dictionary.get("ExtractedMetadata.Title"), Dictionary.get("ExtractedMetadata.Message"), null, false);
+ dialog.display();
+ dialog.dispose();
+ dialog = null;
+ }
+ };
+ SwingUtilities.invokeLater(task);
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/MetadataValueTreeModel.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/MetadataValueTreeModel.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/MetadataValueTreeModel.java (revision 31635)
@@ -0,0 +1,137 @@
+/**
+ *############################################################################
+ * A component of the Greenstone Librarian Interface, part of the Greenstone
+ * digital library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
+ *
+ * Copyright (C) 2004 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################################################
+ */
+
+package org.greenstone.gatherer.metadata;
+
+
+import java.io.*;
+import java.util.*;
+import javax.swing.tree.*;
+
+
+/** This class represents all the metadata values for one metadata element */
+public class MetadataValueTreeModel
+ extends DefaultTreeModel
+{
+ public MetadataValueTreeModel(MetadataElement metadata_element)
+ {
+ // The root node of the value tree is the MetadataElement it represents
+ super(new MetadataValueTreeNode(metadata_element));
+ }
+
+
+ public MetadataValueTreeNode addMetadataValue(String metadata_value)
+ {
+ return addMetadataValueInternal((MetadataValueTreeNode) root, metadata_value);
+ }
+
+
+ private MetadataValueTreeNode addMetadataValueInternal(MetadataValueTreeNode parent_metadata_value_tree_node, String metadata_value_remaining)
+ {
+ // Split the metadata value into a hierarchy
+ int split_point = metadata_value_remaining.indexOf(MetadataValueTreeNode.METADATA_VALUE_TREE_NODE_HIERARCHY_TOKEN);
+ String metadata_value = ((split_point == -1) ? metadata_value_remaining : metadata_value_remaining.substring(0, split_point));
+ metadata_value_remaining = ((split_point == -1) ? "" : metadata_value_remaining.substring(split_point + 1));
+
+ // Add the value into the tree in the correct place (sorted)
+ MetadataValueTreeNode metadata_value_tree_node = null;
+ for (int i = 0; i < parent_metadata_value_tree_node.getChildCount(); i++) {
+ MetadataValueTreeNode child_metadata_value_tree_node = (MetadataValueTreeNode) parent_metadata_value_tree_node.getChildAt(i);
+ int c = metadata_value.compareTo(child_metadata_value_tree_node.getValue());
+
+ // Insert node before existing value
+ if (c < 0) {
+ metadata_value_tree_node = new MetadataValueTreeNode(metadata_value);
+ insertNodeInto(metadata_value_tree_node, parent_metadata_value_tree_node, i);
+ break;
+ }
+
+ // Node already exists (don't add again)
+ if (c == 0) {
+ metadata_value_tree_node = child_metadata_value_tree_node;
+ break;
+ }
+ }
+
+ // If no node has been added yet, it must go at the end of the list
+ if (metadata_value_tree_node == null) {
+ metadata_value_tree_node = new MetadataValueTreeNode(metadata_value);
+ insertNodeInto(metadata_value_tree_node, parent_metadata_value_tree_node, parent_metadata_value_tree_node.getChildCount());
+ }
+
+ // If there is some of the metadata value remaining, add that hierarchically
+ if (!metadata_value_remaining.equals("")) {
+ metadata_value_tree_node = addMetadataValueInternal(metadata_value_tree_node, metadata_value_remaining);
+ }
+
+ return metadata_value_tree_node;
+ }
+
+
+ public MetadataValueTreeNode getMetadataValueTreeNode(String metadata_value)
+ {
+ return getMetadataValueTreeNodeInternal((MetadataValueTreeNode) root, metadata_value);
+ }
+
+
+ private MetadataValueTreeNode getMetadataValueTreeNodeInternal(MetadataValueTreeNode parent_metadata_value_tree_node, String metadata_value_remaining)
+ {
+ // Split the metadata value into a hierarchy
+ int split_point = metadata_value_remaining.indexOf(MetadataValueTreeNode.METADATA_VALUE_TREE_NODE_HIERARCHY_TOKEN);
+ String metadata_value = ((split_point == -1) ? metadata_value_remaining : metadata_value_remaining.substring(0, split_point));
+ metadata_value_remaining = ((split_point == -1) ? "" : metadata_value_remaining.substring(split_point + 1));
+
+ // Find the value in the tree in the correct place (sorted)
+ MetadataValueTreeNode metadata_value_tree_node = null;
+ for (int i = 0; i < parent_metadata_value_tree_node.getChildCount(); i++) {
+ MetadataValueTreeNode child_metadata_value_tree_node = (MetadataValueTreeNode) parent_metadata_value_tree_node.getChildAt(i);
+ int c = metadata_value.compareTo(child_metadata_value_tree_node.getValue());
+
+ // Node doesn't exist in the tree
+ if (c < 0) {
+ return null;
+ }
+
+ // Node exists
+ if (c == 0) {
+ metadata_value_tree_node = child_metadata_value_tree_node;
+ break;
+ }
+ }
+
+ // If no node has been found, it doesn't exist
+ if (metadata_value_tree_node == null) {
+ return null;
+ }
+
+ // If there is some of the metadata value remaining, find that recursively
+ if (!metadata_value_remaining.equals("")) {
+ metadata_value_tree_node = getMetadataValueTreeNodeInternal(metadata_value_tree_node, metadata_value_remaining);
+ }
+
+ return metadata_value_tree_node;
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/MetadataValueTreeNode.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/MetadataValueTreeNode.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/MetadataValueTreeNode.java (revision 31635)
@@ -0,0 +1,79 @@
+/**
+ *############################################################################
+ * A component of the Greenstone Librarian Interface, part of the Greenstone
+ * digital library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
+ *
+ * Copyright (C) 2004 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################################################
+ */
+
+package org.greenstone.gatherer.metadata;
+
+
+import javax.swing.tree.*;
+
+
+/** This class represents one Metadata value */
+public class MetadataValueTreeNode
+ extends DefaultMutableTreeNode
+{
+ static public final String METADATA_VALUE_TREE_NODE_HIERARCHY_TOKEN = "|";
+
+ private String value = null;
+
+
+ public MetadataValueTreeNode(Object object)
+ {
+ super(object);
+ }
+
+
+ public MetadataValueTreeNode(String value)
+ {
+ this.value = value;
+ }
+
+
+ public String getFullValue()
+ {
+ if (parent == null) {
+ return "";
+ }
+
+ if (((MetadataValueTreeNode) parent).isRoot()) {
+ return value;
+ }
+
+ return ((MetadataValueTreeNode) parent).getFullValue() + METADATA_VALUE_TREE_NODE_HIERARCHY_TOKEN + value;
+ }
+
+
+ public String getValue()
+ {
+ return value;
+ }
+
+
+ /** This is used to display the node in the value tree */
+ public String toString()
+ {
+ return value;
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/MetadataXMLFile.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/MetadataXMLFile.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/MetadataXMLFile.java (revision 31635)
@@ -0,0 +1,769 @@
+/**
+ *############################################################################
+ * A component of the Greenstone Librarian Interface, part of the Greenstone
+ * digital library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
+ *
+ * Copyright (C) 2004 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################################################
+ */
+
+package org.greenstone.gatherer.metadata;
+
+
+import java.io.*;
+import java.util.*;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.collection.CollectionTreeNode;
+import org.greenstone.gatherer.util.XMLTools;
+import org.w3c.dom.*;
+
+
+/** This class represents one metadata.xml file */
+public class MetadataXMLFile
+ extends File
+{
+ static final private String DESCRIPTION_ELEMENT = "Description";
+ static final private String DIRECTORY_FILENAME = ".*";
+ static final private String FILENAME_ELEMENT = "FileName";
+ static final private String FILESET_ELEMENT = "FileSet";
+ static final private String METADATA_ELEMENT = "Metadata";
+ static final private String[] nonEscapingElements = new String[]{FILENAME_ELEMENT};
+
+ /** Special metadata field: the filename encoding is a unique sort of metadata in
+ * that it is not just information stored with a collection file, but also needs to
+ * be applied in real-time to the collection file (to its filename) for display. */
+ static final public String FILENAME_ENCODING_METADATA = "gs.filenameEncoding";
+
+ // To speed things up a bit we keep the last accessed metadata.xml file in memory
+ static private File loaded_file = null;
+ static private Document loaded_file_document = null;
+ static private boolean loaded_file_changed = false;
+
+
+ public MetadataXMLFile(String metadata_xml_file_path)
+ {
+ super(metadata_xml_file_path);
+ }
+
+
+ public void addMetadata(CollectionTreeNode file_node, ArrayList metadata_values)
+ {
+ // If this metadata.xml file isn't the one currently loaded, load it now
+ if (loaded_file != this) {
+ // First we must save out the currently loaded file
+ saveLoadedFile();
+
+ // Parse the metadata.xml file
+ Document document = XMLTools.parseXMLFile(this);
+ if (document == null) {
+ System.err.println("Error: Could not parse metadata.xml file " + getAbsolutePath());
+ return;
+ }
+
+ loaded_file = this;
+ loaded_file_document = document;
+ }
+
+ // Determine the file's path relative to the location of the metadata.xml file
+ String metadata_xml_file_directory_path = FilenameEncoding.fileToURLEncoding(getParentFile());
+ String file_relative_path = file_node.getURLEncodedFilePath().substring(metadata_xml_file_directory_path.length());
+ if (file_relative_path.startsWith(FilenameEncoding.URL_FILE_SEPARATOR)) {
+ file_relative_path = file_relative_path.substring(FilenameEncoding.URL_FILE_SEPARATOR.length());
+ }
+
+ // Form a regular expression that specifies the scope of the metadata
+ String file_path_regexp;
+ if (file_relative_path.equals("")) {
+ // Special case for matching all files in the directory
+ file_path_regexp = DIRECTORY_FILENAME;
+ }
+ else {
+ // Convert the file path into a regular expression that will match it
+ file_path_regexp = MetadataTools.getRegularExpressionThatMatchesFilePath(file_relative_path);
+ }
+
+ // Find the appropriate FileSet element for this file
+ Element appropriate_fileset_element = null;
+
+ // Read all the FileSet elements in the file
+ NodeList fileset_elements_nodelist = loaded_file_document.getElementsByTagName(FILESET_ELEMENT);
+ for (int i = 0; i < fileset_elements_nodelist.getLength(); i++) {
+ Element current_fileset_element = (Element) fileset_elements_nodelist.item(i);
+
+ // Check the FileName elements of the FileSet to see if we have a match
+ NodeList filename_elements_nodelist = current_fileset_element.getElementsByTagName(FILENAME_ELEMENT);
+ for (int j = 0; j < filename_elements_nodelist.getLength(); j++) {
+ Element current_filename_element = (Element) filename_elements_nodelist.item(j);
+ String current_filename_element_value = XMLTools.getElementTextValue(current_filename_element);
+
+ // Only exact matches can be extended with new metadata
+ if (current_filename_element_value.equals(file_path_regexp)) {
+ appropriate_fileset_element = current_fileset_element;
+ break;
+ }
+ }
+ }
+
+ // If no appropriate FileSet element exists create a new one for this file
+ if (appropriate_fileset_element == null) {
+ DebugStream.println("Creating new FileSet element for file since none exists..."+file_path_regexp);
+ appropriate_fileset_element = loaded_file_document.createElement(FILESET_ELEMENT);
+
+ Element new_filename_element = loaded_file_document.createElement(FILENAME_ELEMENT);
+ new_filename_element.appendChild(loaded_file_document.createTextNode(file_path_regexp));
+ appropriate_fileset_element.appendChild(new_filename_element);
+
+ Element new_description_element = loaded_file_document.createElement(DESCRIPTION_ELEMENT);
+ appropriate_fileset_element.appendChild(new_description_element);
+
+ // add the fileset element for .* at the top: especially important for
+ // non-accumulating (and override mode) meta. Other type fileset elements can be appended
+ if(file_path_regexp.equals(DIRECTORY_FILENAME)) {
+ loaded_file_document.getDocumentElement().insertBefore(appropriate_fileset_element,
+ loaded_file_document.getDocumentElement().getFirstChild());
+ } else {
+ loaded_file_document.getDocumentElement().appendChild(appropriate_fileset_element);
+ }
+ }
+
+ // Add each of the metadata values to the FileSet's Description element
+ Element description_element = (Element) appropriate_fileset_element.getElementsByTagName(DESCRIPTION_ELEMENT).item(0);
+ for (int i = 0; i < metadata_values.size(); i++) {
+ MetadataValue metadata_value = (MetadataValue) metadata_values.get(i);
+ String metadata_element_name_full = metadata_value.getMetadataElement().getFullName();
+
+ // Remove any characters that are invalid in XML
+ String metadata_value_string = XMLTools.removeInvalidCharacters(metadata_value.getFullValue());
+
+ // Square brackets need to be escaped because they are a special character in Greenstone
+ metadata_value_string = metadata_value_string.replaceAll("\\[", "[");
+ metadata_value_string = metadata_value_string.replaceAll("\\]", "]");
+
+ // the gs.filenameEncoding metadata is unique in that, when added, removed or
+ // changed, it must be applied on the file(name) whose metadata has been adjusted
+ if(metadata_element_name_full.equals(FILENAME_ENCODING_METADATA)) {
+ metadata_value_string = processFilenameEncoding(file_path_regexp,
+ file_node, metadata_value_string, false);
+ // true only if removing meta
+ }
+
+ // Check if this piece of metadata has already been assigned to this FileSet element
+ boolean metadata_already_assigned = false;
+ NodeList metadata_elements_nodelist = description_element.getElementsByTagName(METADATA_ELEMENT);
+ for (int k = 0; k < metadata_elements_nodelist.getLength(); k++) {
+ Element current_metadata_element = (Element) metadata_elements_nodelist.item(k);
+
+ // Check if the metadata element name matches
+ String current_metadata_element_name_full = current_metadata_element.getAttribute("name");
+ if (current_metadata_element_name_full.equals(metadata_element_name_full)) {
+ // if the metadata must not accumulate, then edit the current value
+ if (!metadata_value.isAccumulatingMetadata()) {
+ XMLTools.setNodeText(current_metadata_element, metadata_value_string);
+ metadata_already_assigned = true;
+ break;
+ }
+ // Check if the metadata element value matches
+ String current_metadata_value_string = XMLTools.getElementTextValue(current_metadata_element);
+ if (current_metadata_value_string.equals(metadata_value_string)) {
+ // Metadata already assigned
+ metadata_already_assigned = true;
+ break;
+ }
+ }
+ }
+
+ // If the piece of metadata hasn't already been assigned, add it now
+ if (!metadata_already_assigned) {
+ // Create a new Metadata element to record this metadata
+ Element new_metadata_element = loaded_file_document.createElement(METADATA_ELEMENT);
+ new_metadata_element.setAttribute("name", metadata_value.getMetadataElement().getFullName());
+ new_metadata_element.setAttribute("mode", (metadata_value.isAccumulatingMetadata() ? "accumulate" : "override"));
+ new_metadata_element.appendChild(loaded_file_document.createTextNode(metadata_value_string));
+
+ // Accumulating metadata: add at the end
+ if (metadata_value.isAccumulatingMetadata()) {
+ description_element.appendChild(new_metadata_element);
+ }
+ // Override metadata: add at the start (so it overrides inherited metadata without affecting other assigned metadata)
+ else {
+ description_element.insertBefore(new_metadata_element, description_element.getFirstChild());
+ }
+ }
+ }
+
+ // Remember that we've changed the file so it gets saved when a new one is loaded
+ loaded_file_changed = true;
+ }
+
+
+ public ArrayList getMetadataAssignedToFile(File file, boolean fileEncodingOnly)
+ {
+ // If this metadata.xml file isn't the one currently loaded, load it now
+ if (loaded_file != this) {
+ // First we must save out the currently loaded file
+ saveLoadedFile();
+
+ // Parse the metadata.xml file
+ Document document = XMLTools.parseXMLFile(this);
+ if (document == null) {
+ System.err.println("Error: Could not parse metadata.xml file " + getAbsolutePath());
+ return new ArrayList();
+ }
+
+ loaded_file = this;
+ loaded_file_document = document;
+ }
+
+ // Determine the file's path relative to the location of the metadata.xml file
+ String file_relative_path = FilenameEncoding.fileToURLEncoding(file);
+ File metadata_xml_file_directory = getParentFile();
+ String metadata_xml_file_directory_path = FilenameEncoding.fileToURLEncoding(metadata_xml_file_directory);
+ file_relative_path = file_relative_path.substring(metadata_xml_file_directory_path.length());
+
+ if (file_relative_path.startsWith(FilenameEncoding.URL_FILE_SEPARATOR)) {
+ file_relative_path = file_relative_path.substring(FilenameEncoding.URL_FILE_SEPARATOR.length());
+ }
+
+ // Build up a list of metadata assigned to this file
+ ArrayList metadata_values = new ArrayList();
+
+ // Read all the FileSet elements in the file
+ NodeList fileset_elements_nodelist = loaded_file_document.getElementsByTagName(FILESET_ELEMENT);
+ for (int i = 0; i < fileset_elements_nodelist.getLength(); i++) {
+ Element current_fileset_element = (Element) fileset_elements_nodelist.item(i);
+ boolean current_fileset_matches = false;
+ boolean is_one_file_only_metadata = true;
+ File folder_metadata_inherited_from = null;
+
+ // Check the FileName elements of the FileSet to see if we have a match
+ NodeList filename_elements_nodelist = current_fileset_element.getElementsByTagName(FILENAME_ELEMENT);
+ for (int j = 0; j < filename_elements_nodelist.getLength(); j++) {
+ Element current_filename_element = (Element) filename_elements_nodelist.item(j);
+ String current_filename_element_value = XMLTools.getElementTextValue(current_filename_element);
+
+ // Does this fileset specify metadata for one file only?
+ is_one_file_only_metadata = true;
+ if (current_filename_element_value.indexOf("*") != -1 && !current_filename_element_value.equals(DIRECTORY_FILENAME)) {
+ // No, it specifies metadata for multiple files (but not all the files in the directory)
+ is_one_file_only_metadata = false;
+ }
+
+ // This fileset specifies metadata for the file
+ if (file_relative_path.matches(current_filename_element_value)) {
+ current_fileset_matches = true;
+ if (!file_relative_path.equals("") && current_filename_element_value.equals(DIRECTORY_FILENAME)) {
+ folder_metadata_inherited_from = metadata_xml_file_directory;
+ }
+ break;
+ }
+
+ // This fileset specifies metadata for the folder the file is in
+ if (file_relative_path.startsWith(current_filename_element_value + FilenameEncoding.URL_FILE_SEPARATOR)) {
+ current_fileset_matches = true;
+ folder_metadata_inherited_from = new File(metadata_xml_file_directory, current_filename_element_value);
+ break;
+ }
+ }
+
+ // The FileSet doesn't apply, so move onto the next one
+ if (current_fileset_matches == false) {
+ continue;
+ }
+
+ // Read all the Metadata elements in the fileset
+ NodeList metadata_elements_nodelist = current_fileset_element.getElementsByTagName(METADATA_ELEMENT);
+ for (int k = 0; k < metadata_elements_nodelist.getLength(); k++) {
+ Element current_metadata_element = (Element) metadata_elements_nodelist.item(k);
+ String metadata_element_name_full = current_metadata_element.getAttribute("name");
+ // if we're only looking for fileEncoding metadata and this isn't it, skip to the next
+ if(fileEncodingOnly && !metadata_element_name_full.equals(FILENAME_ENCODING_METADATA)) {
+ continue;
+ }
+ String metadata_set_namespace = MetadataTools.getMetadataSetNamespace(metadata_element_name_full);
+
+ // Ignore legacy crap
+ if (metadata_set_namespace.equals("hidden")) {
+ continue;
+ }
+
+ MetadataSet metadata_set = MetadataSetManager.getMetadataSet(metadata_set_namespace);
+ if (metadata_set == null) {
+ // The metadata set isn't loaded, so give the option of mapping the element into a loaded set
+ String target_metadata_element_name_full = MetadataSetManager.mapUnloadedMetadataElement(metadata_element_name_full);
+ if (target_metadata_element_name_full == null || target_metadata_element_name_full.equals("")) {
+ // Skip this element if we still don't have a loaded element for it
+ continue;
+ }
+
+ metadata_element_name_full = target_metadata_element_name_full;
+ metadata_set_namespace = MetadataTools.getMetadataSetNamespace(metadata_element_name_full);
+ metadata_set = MetadataSetManager.getMetadataSet(metadata_set_namespace);
+ }
+
+ MetadataElement metadata_element = MetadataTools.getMetadataElementWithName(metadata_element_name_full);
+
+ String metadata_element_name = MetadataTools.getMetadataElementName(metadata_element_name_full);
+ // If the element doesn't exist in the metadata set, we're not interested
+ //Shaoqun modified. It needs to be added to metadata_set because the user might disable skim file
+ if (metadata_element == null) {
+ metadata_element = metadata_set.addMetadataElementForThisSession(metadata_element_name);
+ // continue;
+ }
+
+ // Square brackets need to be escaped because they are a special character in Greenstone
+ String metadata_value_string = XMLTools.getElementTextValue(current_metadata_element);
+ metadata_value_string = metadata_value_string.replaceAll("[", "[");
+ metadata_value_string = metadata_value_string.replaceAll("]", "]");
+
+ MetadataValueTreeNode metadata_value_tree_node = metadata_element.getMetadataValueTreeNode(metadata_value_string);
+
+ // If there is no metadata value tree node for this value, create it
+ if (metadata_value_tree_node == null) {
+ DebugStream.println("Note: No value tree node for metadata value \"" + metadata_value_string + "\"");
+ metadata_element.addMetadataValue(metadata_value_string);
+ metadata_value_tree_node = metadata_element.getMetadataValueTreeNode(metadata_value_string);
+ }
+
+ MetadataValue metadata_value = new MetadataValue(metadata_element, metadata_value_tree_node);
+ metadata_value.inheritsMetadataFromFolder(folder_metadata_inherited_from);
+ metadata_value.setIsOneFileOnlyMetadata(is_one_file_only_metadata);
+
+ // Is this accumulating metadata?
+ if (current_metadata_element.getAttribute("mode").equals("accumulate")) {
+ metadata_value.setIsAccumulatingMetadata(true);
+ }
+
+ // Add the new metadata value to the list
+ metadata_values.add(metadata_value);
+ }
+ }
+
+ return metadata_values;
+ }
+
+
+ public void removeMetadata(CollectionTreeNode file_node, ArrayList metadata_values)
+ {
+ // If this metadata.xml file isn't the one currently loaded, load it now
+ if (loaded_file != this) {
+ // First we must save out the currently loaded file
+ saveLoadedFile();
+
+ // Parse the metadata.xml file
+ Document document = XMLTools.parseXMLFile(this);
+ if (document == null) {
+ System.err.println("Error: Could not parse metadata.xml file " + getAbsolutePath());
+ return;
+ }
+
+ loaded_file = this;
+ loaded_file_document = document;
+ }
+
+ // Determine the file's path relative to the location of the metadata.xml file
+ String metadata_xml_file_directory_path = FilenameEncoding.fileToURLEncoding(getParentFile());
+ String file_relative_path = file_node.getURLEncodedFilePath().substring(metadata_xml_file_directory_path.length());
+ if (file_relative_path.startsWith(FilenameEncoding.URL_FILE_SEPARATOR)) {
+ file_relative_path = file_relative_path.substring(FilenameEncoding.URL_FILE_SEPARATOR.length());
+ }
+
+ // Form a regular expression that specifies the scope of the metadata
+ String file_path_regexp;
+ if (file_relative_path.equals("")) {
+ // Special case for matching all files in the directory
+ file_path_regexp = DIRECTORY_FILENAME;
+ }
+ else {
+ // Convert the file path into a regular expression that will match it
+ file_path_regexp = MetadataTools.getRegularExpressionThatMatchesFilePath(file_relative_path);
+ }
+
+ // Find the appropriate FileSet element for this file
+ Element appropriate_fileset_element = null;
+
+ // Read all the FileSet elements in the file
+ NodeList fileset_elements_nodelist = loaded_file_document.getElementsByTagName(FILESET_ELEMENT);
+ for (int i = 0; i < fileset_elements_nodelist.getLength(); i++) {
+ Element current_fileset_element = (Element) fileset_elements_nodelist.item(i);
+
+ // Check the FileName elements of the FileSet to see if we have a match
+ NodeList filename_elements_nodelist = current_fileset_element.getElementsByTagName(FILENAME_ELEMENT);
+ for (int j = 0; j < filename_elements_nodelist.getLength(); j++) {
+ Element current_filename_element = (Element) filename_elements_nodelist.item(j);
+ String current_filename_element_value = XMLTools.getElementTextValue(current_filename_element);
+
+ // Only exact matches can be extended with new metadata
+ if (current_filename_element_value.equals(file_path_regexp)) {
+ appropriate_fileset_element = current_fileset_element;
+ break;
+ }
+ }
+ }
+
+ // If no appropriate FileSet element exists the metadata isn't assigned in this metadata.xml file
+ if (appropriate_fileset_element == null) {
+ DebugStream.println("Note: No appropriate FileSet element found when removing metadata from " + this);
+ return;
+ }
+
+ // Remove each of the metadata values from the FileSet's Description element
+ for (int i = 0; i < metadata_values.size(); i++) {
+ MetadataValue metadata_value = (MetadataValue) metadata_values.get(i);
+
+ // Remove any characters that are invalid in XML
+ String metadata_value_string = XMLTools.removeInvalidCharacters(metadata_value.getFullValue());
+
+ // Square brackets need to be escaped because they are a special character in Greenstone
+ metadata_value_string = metadata_value_string.replaceAll("\\[", "[");
+ metadata_value_string = metadata_value_string.replaceAll("\\]", "]");
+
+ // Find the Metadata element to delete from the fileset
+ String metadata_element_name_full = metadata_value.getMetadataElement().getFullName();
+ NodeList metadata_elements_nodelist = appropriate_fileset_element.getElementsByTagName(METADATA_ELEMENT);
+ for (int k = 0; k < metadata_elements_nodelist.getLength(); k++) {
+ Element current_metadata_element = (Element) metadata_elements_nodelist.item(k);
+
+ // Check the metadata element name matches
+ String current_metadata_element_name_full = current_metadata_element.getAttribute("name");
+ if (current_metadata_element_name_full.equals(metadata_element_name_full)) {
+ // Check the metadata element value matches
+ String current_metadata_value_string = XMLTools.getElementTextValue(current_metadata_element);
+ if (current_metadata_value_string.equals(metadata_value_string)) {
+
+ // Remove this Metadata element
+ current_metadata_element.getParentNode().removeChild(current_metadata_element);
+
+ // the gs.filenameEncoding metadata is unique in that, when added, removed or
+ // changed, it must be applied on the file(name) whose metadata has been adjusted
+ if(current_metadata_element_name_full.equals(FILENAME_ENCODING_METADATA)) {
+
+ // metadata_value_string will hereafter be the inherited gs.FilenameEncoding
+ // metadata (if any), now that the value at this level has been removed
+ metadata_value_string = processFilenameEncoding(file_path_regexp,
+ file_node, "", true); // true only if *removing* this meta
+ }
+
+ // If there are no Metadata elements left now, remove the (empty) FileSet element
+ if (metadata_elements_nodelist.getLength() == 0) {
+ appropriate_fileset_element.getParentNode().removeChild(appropriate_fileset_element);
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
+ // Remember that we've changed the file so it gets saved when a new one is loaded
+ loaded_file_changed = true;
+ }
+
+
+ public void replaceMetadata(CollectionTreeNode file_node, MetadataValue old_metadata_value, MetadataValue new_metadata_value)
+ {
+ // If this metadata.xml file isn't the one currently loaded, load it now
+ if (loaded_file != this) {
+ // First we must save out the currently loaded file
+ saveLoadedFile();
+
+ // Parse the metadata.xml file
+ Document document = XMLTools.parseXMLFile(this);
+ if (document == null) {
+ System.err.println("Error: Could not parse metadata.xml file " + getAbsolutePath());
+ return;
+ }
+
+ loaded_file = this;
+ loaded_file_document = document;
+ }
+
+ // Determine the file's path relative to the location of the metadata.xml file
+ String metadata_xml_file_directory_path = FilenameEncoding.fileToURLEncoding(getParentFile());
+ String file_relative_path = file_node.getURLEncodedFilePath().substring(metadata_xml_file_directory_path.length());
+ if (file_relative_path.startsWith(FilenameEncoding.URL_FILE_SEPARATOR)) {
+ file_relative_path = file_relative_path.substring(FilenameEncoding.URL_FILE_SEPARATOR.length());
+ }
+
+ // Form a regular expression that specifies the scope of the metadata
+ String file_path_regexp;
+ if (file_relative_path.equals("")) {
+ // Special case for matching all files in the directory
+ file_path_regexp = DIRECTORY_FILENAME;
+ }
+ else {
+ // Convert the file path into a regular expression that will match it
+ file_path_regexp = MetadataTools.getRegularExpressionThatMatchesFilePath(file_relative_path);
+ }
+
+ // Remove any characters that are invalid in XML
+ String old_metadata_value_string = XMLTools.removeInvalidCharacters(old_metadata_value.getFullValue());
+ String new_metadata_value_string = XMLTools.removeInvalidCharacters(new_metadata_value.getFullValue());
+
+ // Square brackets need to be escaped because they are a special character in Greenstone
+ old_metadata_value_string = old_metadata_value_string.replaceAll("\\[", "[");
+ old_metadata_value_string = old_metadata_value_string.replaceAll("\\]", "]");
+ new_metadata_value_string = new_metadata_value_string.replaceAll("\\[", "[");
+ new_metadata_value_string = new_metadata_value_string.replaceAll("\\]", "]");
+
+ // Read all the FileSet elements in the file
+ NodeList fileset_elements_nodelist = loaded_file_document.getElementsByTagName(FILESET_ELEMENT);
+ for (int i = 0; i < fileset_elements_nodelist.getLength(); i++) {
+ Element current_fileset_element = (Element) fileset_elements_nodelist.item(i);
+ boolean current_fileset_matches = false;
+
+ // Check the FileName elements of the FileSet to see if we have a match
+ NodeList filename_elements_nodelist = current_fileset_element.getElementsByTagName(FILENAME_ELEMENT);
+ for (int j = 0; j < filename_elements_nodelist.getLength(); j++) {
+ Element current_filename_element = (Element) filename_elements_nodelist.item(j);
+ String current_filename_element_value = XMLTools.getElementTextValue(current_filename_element);
+
+ // Only exact matches can be edited
+ if (current_filename_element_value.equals(file_path_regexp)) {
+ current_fileset_matches = true;
+ break;
+ }
+ }
+
+ // The FileSet doesn't apply, so move onto the next one
+ if (current_fileset_matches == false) {
+ continue;
+ }
+
+ // Each metadata value is only allowed to be assigned once
+ boolean new_metadata_value_already_exists = false;
+ Element metadata_element_to_edit = null;
+
+ // Find the Metadata element to replace in the fileset
+ String metadata_element_name_full = old_metadata_value.getMetadataElement().getFullName();
+ NodeList metadata_elements_nodelist = current_fileset_element.getElementsByTagName(METADATA_ELEMENT);
+ for (int k = 0; k < metadata_elements_nodelist.getLength(); k++) {
+ Element current_metadata_element = (Element) metadata_elements_nodelist.item(k);
+
+ // Check the metadata element name matches
+ String current_metadata_element_name_full = current_metadata_element.getAttribute("name");
+ if (!current_metadata_element_name_full.equals(metadata_element_name_full)) {
+ continue;
+ }
+
+ // Check the new metadata value doesn't already exist
+ String current_metadata_value_string = XMLTools.getElementTextValue(current_metadata_element);
+ if (current_metadata_value_string.equals(new_metadata_value_string)) {
+ new_metadata_value_already_exists = true;
+ }
+
+ // Check the metadata element value matches
+ if (current_metadata_value_string.equals(old_metadata_value_string)) {
+ metadata_element_to_edit = current_metadata_element;
+ }
+ }
+
+ // If the new metadata value already existed, remove the original value
+ if (new_metadata_value_already_exists) {
+ if(metadata_element_to_edit != null) { //?????????
+ metadata_element_to_edit.getParentNode().removeChild(metadata_element_to_edit);
+ } else {
+ System.err.println("ERROR MetadataXMLFile: metadata_element_to_edit is null");
+ }
+ }
+ // Otherwise replace the old value with the new value
+ // Ensure metadata_element_to_edit isn't null (may occur when multiple files are selected)
+ else if (metadata_element_to_edit != null) {
+
+ // the gs.filenameEncoding metadata is unique in that, when added, removed or
+ // changed, it must be applied on the file(name) whose metadata has been adjusted
+ if(metadata_element_name_full.equals(FILENAME_ENCODING_METADATA)) {
+ new_metadata_value_string = processFilenameEncoding(file_path_regexp, file_node, new_metadata_value_string, false);
+ // true only if removing meta
+ }
+ XMLTools.setElementTextValue(metadata_element_to_edit, new_metadata_value_string);
+ }
+ }
+
+ // Remember that we've changed the file so it gets saved when a new one is loaded
+ loaded_file_changed = true;
+ }
+
+
+ static public void saveLoadedFile()
+ {
+ // If we have a file loaded into memory and it has been modified, save it now
+ if (loaded_file != null && loaded_file_changed == true) {
+ XMLTools.writeXMLFile(loaded_file, loaded_file_document, nonEscapingElements);
+
+
+ loaded_file_changed = false;
+ }
+ }
+
+
+ /**
+ * Every metadata.xml file must be skimmed when a collection is opened, for three very important reasons:
+ * - To handle any non-namespaced metadata in the metadata.xml files (this is mapped and the files rewritten)
+ * - To get a complete list of the metadata elements in the collection (used in Design and Format panes)
+ * - To build complete and accurate metadata value trees (used in the Enrich pane)
+ */
+ public void skimFile()
+ {
+ boolean file_changed = false;
+
+ // Parse the metadata.xml file
+ DebugStream.println("Skimming metadata.xml file " + this + "...");
+
+ Document document = XMLTools.parseXMLFile(this);
+ if (document == null) {
+ System.err.println("Error: Could not parse metadata.xml file " + getAbsolutePath());
+ return;
+ }
+
+ // Read all the Metadata elements in the file
+ HashMap target_metadata_element_name_attrs_cache = new HashMap();
+ NodeList metadata_elements_nodelist = document.getElementsByTagName(METADATA_ELEMENT);
+ for (int i = 0; i < metadata_elements_nodelist.getLength(); i++) {
+ Element current_metadata_element = (Element) metadata_elements_nodelist.item(i);
+ String metadata_element_name_full = current_metadata_element.getAttribute("name");
+ String metadata_set_namespace = MetadataTools.getMetadataSetNamespace(metadata_element_name_full);
+
+ // Ignore legacy crap
+ if (metadata_set_namespace.equals("hidden")) {
+ continue;
+ }
+
+ MetadataSet metadata_set = MetadataSetManager.getMetadataSet(metadata_set_namespace);
+ if (metadata_set == null) {
+ // The metadata set isn't loaded, so give the option of mapping the element into a loaded set
+ String target_metadata_element_name_full = MetadataSetManager.mapUnloadedMetadataElement(metadata_element_name_full);
+ if (target_metadata_element_name_full == null || target_metadata_element_name_full.equals("")) {
+ // Skip this element if we still don't have a loaded element for it
+ continue;
+ }
+
+ // Update the metadata.xml file to have the new (namespaced) element name
+ // Instead of using current_metadata_element.setAttribute("name", target_metadata_element_name_full)
+ // we create an Attr object for each target metadata element name, and cache them
+ // This makes a *huge* difference (namespacing a metadata.xml file with 45000 metadata entries now
+ // takes 45 seconds instead of 30 minutes!) -- why is setting the value of a Node so slow?
+ Attr target_metadata_element_name_attr = (Attr) target_metadata_element_name_attrs_cache.get(target_metadata_element_name_full);
+ if (target_metadata_element_name_attr == null) {
+ target_metadata_element_name_attr = document.createAttribute("name");
+ target_metadata_element_name_attr.setValue(target_metadata_element_name_full);
+ target_metadata_element_name_attrs_cache.put(target_metadata_element_name_full, target_metadata_element_name_attr);
+ }
+
+ // Remove the old name attribute and add the new (namespaced) one
+ current_metadata_element.removeAttribute("name");
+ current_metadata_element.setAttributeNode((Attr) target_metadata_element_name_attr.cloneNode(false));
+ file_changed = true;
+
+ metadata_element_name_full = target_metadata_element_name_full;
+ metadata_set_namespace = MetadataTools.getMetadataSetNamespace(metadata_element_name_full);
+ metadata_set = MetadataSetManager.getMetadataSet(metadata_set_namespace);
+ }
+
+ String metadata_element_name = MetadataTools.getMetadataElementName(metadata_element_name_full);
+ MetadataElement metadata_element = metadata_set.getMetadataElementWithName(metadata_element_name);
+
+ // If the element doesn't exist in the metadata set, add it
+ if (metadata_element == null) {
+ metadata_element = metadata_set.addMetadataElementForThisSession(metadata_element_name);
+ }
+
+ // Square brackets need to be escaped because they are a special character in Greenstone
+ String metadata_value_string = XMLTools.getElementTextValue(current_metadata_element);
+ metadata_value_string = metadata_value_string.replaceAll("[", "[");
+ metadata_value_string = metadata_value_string.replaceAll("]", "]");
+
+ metadata_element.addMetadataValue(metadata_value_string);
+ }
+
+ // Rewrite the metadata.xml file if it has changed
+ if (file_changed) {
+ XMLTools.writeXMLFile(this, document);
+ }
+ }
+
+ /**
+ * The gs.filenameEncoding metadata is unique in that, when added, removed or
+ * replaced, it must be applied on the file(name) whose metadata has been
+ * adjusted.
+ * This method handles all that, given the regular expression or filepath name
+ * to match on (.* matches subdirectories), the affected fileNode, the new
+ * encoding value and whether a new encoding value has been added/an existing
+ * one has been replaced or whether the encoding metadata has been removed.
+ * The new adjusted value for the encoding metadata is returned.
+ *
+ * MetadataXMLFileManager maintains a hashmap of (URL-encoded filepaths, encoding)
+ * to allow fast access to previously assigned gs.filenameEncoding metadata (if
+ * any) for each file. This hashmap also needs to be updated, but this update
+ * is complicated by the fact that it concerns regular expressions that could
+ * affect multiple filenames.
+ */
+ public String processFilenameEncoding(String file_path_regexp, CollectionTreeNode file_node,
+ String encoding_metadata_value, boolean removingMetadata)
+ {
+ if(!FilenameEncoding.MULTIPLE_FILENAME_ENCODINGS_SUPPORTED) {
+ return encoding_metadata_value;
+ }
+
+ // Work out this filenode's new encoding and apply it:
+
+ if(removingMetadata) { // encoding_metadata_value = ""
+ // gs.filenameEncoding metadata being removed, work out
+ // any inherited metadata to replace it with in the meta-table
+ encoding_metadata_value = FilenameEncoding.getInheritedFilenameEncoding(
+ file_node.getURLEncodedFilePath(), file_node.getFile());
+ // should be canonical encoding already
+ }
+ else if(!encoding_metadata_value.equals("")) {
+ // if adding or replacing filename encoding,
+ // get the canonical encoding name for this alias
+ encoding_metadata_value = FilenameEncoding.canonicalEncodingName(encoding_metadata_value);
+ }
+ // Reencode the display of this filenode only as any affected
+ // childnodes will be reencoded on FileNode.refreshDescendantEncodings()
+ file_node.reencodeDisplayName(encoding_metadata_value);
+
+
+ // Whether removing or adding/replacing the file's gs.filename encoding meta,
+ // store this in the file-to-encoding map for fast access, since the map stores
+ // empty string values when no meta has been assigned at this file level.
+ // In the case of removingMetadata, the value stored will be the fallback value
+
+ String urlpath = file_node.getURLEncodedFilePath();
+ if(removingMetadata) {
+ // remove it from the map instead of inserting "", so that when folders in the collectiontree
+ // are being deleted or shifted, the removemetada (and addmetadata) calls that get fired
+ // for each affected filenodes does not cause the undesirable effect of multiple "" to be
+ // entered into the filename-to-encoding map for filepaths that no longer exist .
+ FilenameEncoding.map.remove(urlpath);
+ } else { // for adding and replacing, put the encoding into the map (also replaces any existing encoding for it)
+ FilenameEncoding.map.put(urlpath, encoding_metadata_value);
+ }
+
+ // If new folder-level metadata (or metadata for a set of files fitting a pattern) has been
+ // assigned, the file_to_encodings map will be cleared for all descendant folders and files,
+ // so that these can be re-calculated upon refreshing the visible parts of the CollectionTree.
+ // Mark the state as requiring a refresh of the CollectionTree.
+ // This next step also serves to prevent the MetadataValueTableModel from trying to update
+ // itself while a refresh (involving re-encoding of filenames of visible nodes) is in progress.
+ FilenameEncoding.setRefreshRequired(true);
+
+ return encoding_metadata_value;
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/MetadataXMLFileManager.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/MetadataXMLFileManager.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/MetadataXMLFileManager.java (revision 31635)
@@ -0,0 +1,489 @@
+/**
+ *############################################################################
+ * A component of the Greenstone Librarian Interface, part of the Greenstone
+ * digital library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
+ *
+ * Copyright (C) 2005 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################################################
+ */
+
+package org.greenstone.gatherer.metadata;
+
+
+import java.io.*;
+import java.util.*;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.collection.CollectionManager;
+import org.greenstone.gatherer.collection.CollectionTreeNode;
+import org.greenstone.gatherer.remote.RemoteGreenstoneServer;
+import org.greenstone.gatherer.util.XMLTools;
+
+
+/** This class is a static class that manages the metadata.xml files */
+public class MetadataXMLFileManager
+{
+ static private ArrayList metadata_xml_files = new ArrayList();
+ /** The objects listening for MetadataChanged events. */
+ static private ArrayList metadata_changed_listeners = new ArrayList();
+ /** Keep track of which metadata.xml files have been modified so we can upload them to the server */
+ static private ArrayList modified_metadata_xml_files = new ArrayList();
+
+ /** For non-accumulating metadata (gs.FilenameEncoding), need the metadata.xml files
+ * sorted in top-down folder level order, which is achieved through this Comparator.
+ * By declaring a static class member here, we avoid recreating this object for every
+ * comparison, which would otherwise have resulted in a constructor call each time. */
+ static private MetadataXMLFileComparator metadataXMLFileComparator = new MetadataXMLFileComparator();
+
+
+ static public void addMetadata(CollectionTreeNode file_node, ArrayList metadata_values)
+ {
+ addMetadata(new CollectionTreeNode[] { file_node }, metadata_values);
+ }
+
+
+ static public void addMetadata(CollectionTreeNode[] file_nodes, MetadataValue metadata_value)
+ {
+ ArrayList metadata_values = new ArrayList();
+ metadata_values.add(metadata_value);
+ addMetadata(file_nodes, metadata_values);
+ }
+
+
+ static public void addMetadata(CollectionTreeNode[] file_nodes, ArrayList metadata_values)
+ {
+ // Check the list of metadata values is non-empty
+ if (metadata_values.isEmpty()) {
+ return;
+ }
+
+ // Add the metadata to each file node in turn
+ for (int i = 0; i < file_nodes.length; i++) {
+ File current_file = file_nodes[i].getFile();
+ DebugStream.println("Adding metadata to " + current_file.getAbsolutePath());
+
+ // Find which metadata.xml file needs editing
+ boolean applicable_metadata_xml_file_found = false;
+ File current_file_directory = (current_file.isDirectory() ? current_file : current_file.getParentFile());
+ String current_file_directory_path = current_file_directory.getAbsolutePath();
+ for (int j = 0; j < metadata_xml_files.size(); j++) {
+ MetadataXMLFile metadata_xml_file = (MetadataXMLFile) metadata_xml_files.get(j);
+
+ // This metadata.xml file is only applicable if it is at the same level as the file
+ if (current_file_directory_path.equals(metadata_xml_file.getParentFile().getAbsolutePath())) {
+ applicable_metadata_xml_file_found = true;
+ metadata_xml_file.addMetadata(file_nodes[i], metadata_values);
+ if (!modified_metadata_xml_files.contains(metadata_xml_file)) {
+ modified_metadata_xml_files.add(metadata_xml_file);
+ }
+ }
+ }
+
+ // If no applicable metadata.xml file exists, we have to create a new one
+ if (!applicable_metadata_xml_file_found) {
+ // Create a new (empty) metadata.xml file in the file's directory...
+ File new_metadata_xml_file_file = new File(current_file_directory, "metadata.xml");
+ XMLTools.writeXMLFile(new_metadata_xml_file_file, XMLTools.parseXMLFile("xml/metadata.xml", true));
+
+ // ...load it...
+ MetadataXMLFile new_metadata_xml_file = loadMetadataXMLFile(new_metadata_xml_file_file,true);
+
+ // ...and add the metadata
+ new_metadata_xml_file.addMetadata(file_nodes[i], metadata_values);
+ if (!modified_metadata_xml_files.contains(new_metadata_xml_file)) {
+ modified_metadata_xml_files.add(new_metadata_xml_file);
+ }
+ }
+ }
+
+ // Let any listeners know that the metadata has changed
+ fireMetadataChangedEvent(file_nodes);
+ }
+
+
+ static public void addMetadataChangedListener(MetadataChangedListener metadata_changed_listener)
+ {
+ metadata_changed_listeners.add(metadata_changed_listener);
+ }
+
+
+ static public void clearMetadataXMLFiles()
+ {
+ metadata_xml_files.clear();
+ }
+
+
+ static private void fireMetadataChangedEvent(CollectionTreeNode[] file_nodes)
+ {
+ // Send the event off to all the MetadataChangedListeners
+ for (int i = 0; i < metadata_changed_listeners.size(); i++) {
+ ((MetadataChangedListener) metadata_changed_listeners.get(i)).metadataChanged(file_nodes);
+ }
+ }
+
+
+ /** Returns the metadata assigned to a file outside the collection, excluding folder-level/inherited metadata. */
+ static public ArrayList getMetadataAssignedDirectlyToExternalFile(File file)
+ {
+ DebugStream.println("Getting metadata assigned directly to external file " + file + "...");
+
+ // Build up a list of applicable metadata.xml files
+ ArrayList applicable_metadata_xml_files = new ArrayList();
+
+ File directory = (file.isDirectory() ? file : file.getParentFile());
+ while (directory != null) {
+ File metadata_xml_file = new File(directory, "metadata.xml");
+ if (metadata_xml_file.exists() && !metadata_xml_file.isDirectory()) {
+ // It is very important that shallower files come before deeper ones
+ applicable_metadata_xml_files.add(0, new MetadataXMLFile(metadata_xml_file.getAbsolutePath()));
+ }
+
+ directory = directory.getParentFile();
+ }
+
+ // Get the metadata assigned to the specified file from the applicable metadata.xml files
+ ArrayList assigned_metadata = getMetadataAssignedToFile(file, applicable_metadata_xml_files, false);
+
+ // Remove any folder-level metadata
+ for (int i = assigned_metadata.size() - 1; i >= 0; i--) {
+ if (((MetadataValue) assigned_metadata.get(i)).isInheritedMetadata()) {
+ assigned_metadata.remove(i);
+ }
+ }
+
+ return assigned_metadata;
+ }
+
+
+ /** Returns the metadata assigned to a file inside the collection, excluding folder-level/inherited metadata. */
+ static public ArrayList getMetadataAssignedDirectlyToFile(File file)
+ {
+ return getMetadataAssignedDirectlyToFile(file, false);
+ }
+
+ /** Returns the metadata assigned to a file inside the collection, excluding folder-level/inherited metadata. */
+ static public ArrayList getMetadataAssignedDirectlyToFile(File file, boolean filenameEncodingMetaOnly)
+ {
+
+ // Get all the metadata assigned to the specified file...
+ ArrayList assigned_metadata = getMetadataAssignedToFile(file, filenameEncodingMetaOnly);
+
+ // ...then remove any folder-level metadata
+ for (int i = assigned_metadata.size() - 1; i >= 0; i--) {
+ if (((MetadataValue) assigned_metadata.get(i)).isInheritedMetadata()) {
+ assigned_metadata.remove(i);
+ }
+ }
+
+ return assigned_metadata;
+ /*
+ // Get all the metadata assigned to the specified file...
+ // Build up a list of applicable metadata.xml files - which in this case
+ // is exclusively the metadata file at this file/folder's own level.
+ ArrayList applicable_metadata_xml_files = new ArrayList();
+
+ // Find the metadata.xml file (if any) that is at the same level as the file
+ String file_directory_path = (file.isDirectory() ? file : file.getParentFile()).getAbsolutePath() + File.separator;
+ for (int i = 0; i < metadata_xml_files.size(); i++) {
+ MetadataXMLFile metadata_xml_file = (MetadataXMLFile) metadata_xml_files.get(i);
+
+ if (file_directory_path.equals(metadata_xml_file.getParentFile().getAbsolutePath() + File.separator)) {
+ //System.err.println("Found metadata_xml_file: " + metadata_xml_file);
+ applicable_metadata_xml_files.add(metadata_xml_file);
+ }
+ }
+
+ if(applicable_metadata_xml_files.size() == 0) {
+ return new ArrayList(0);
+ }
+
+ // Return the metadata assigned to the specified file from the applicable metadata.xml files
+ return getMetadataAssignedToFile(file, applicable_metadata_xml_files, filenameEncodingMetaOnly);
+ */
+ }
+
+
+ /** Returns all the metadata assigned to a file inside the collection. */
+ static public ArrayList getMetadataAssignedToFile(File file)
+ {
+ return getMetadataAssignedToFile(file, false);
+ }
+
+ /** Returns all the metadata assigned to a file inside the collection (or
+ * just gs.filenameEncoding if parameter filenameEncodingMetaOnly is true),
+ * including folder-level/inherited metadata.
+ */
+ static public ArrayList getMetadataAssignedToFile(File file, boolean filenameEncodingMetaOnly)
+ {
+ // Build up a list of applicable metadata.xml files
+ ArrayList applicable_metadata_xml_files = new ArrayList();
+
+ // Look at each loaded metadata.xml file to see if it is potentially applicable
+ String file_directory_path = (file.isDirectory() ? file : file.getParentFile()).getAbsolutePath() + File.separator;
+ for (int i = 0; i < metadata_xml_files.size(); i++) {
+ MetadataXMLFile metadata_xml_file = (MetadataXMLFile) metadata_xml_files.get(i);
+
+ // This metadata.xml file is only potentially applicable if it is above or at the same level as the file
+ if (file_directory_path.startsWith(metadata_xml_file.getParentFile().getAbsolutePath() + File.separator)) {
+ applicable_metadata_xml_files.add(metadata_xml_file);
+ }
+ }
+
+ // Sort the metadataxml files in order starting from those in the
+ // topmost folders down to the one in the lowest level folder.
+ Collections.sort(applicable_metadata_xml_files, metadataXMLFileComparator);
+ // Return the metadata assigned to the specified file from the applicable metadata.xml files
+ return getMetadataAssignedToFile(file, applicable_metadata_xml_files, filenameEncodingMetaOnly);
+ }
+
+ // package access method
+ static ArrayList getMetadataAssignedToFile(File file, ArrayList applicable_metadata_xml_files,
+ boolean filenameEncodingMetaOnly)
+ {
+ // Build up a list of metadata values assigned to this file
+ ArrayList metadata_values_all = new ArrayList();
+
+ // Look at each applicable metadata.xml file to see if it assigns metadata to this file
+ for (int i = 0; i < applicable_metadata_xml_files.size(); i++) {
+ MetadataXMLFile metadata_xml_file = (MetadataXMLFile) applicable_metadata_xml_files.get(i);
+ DebugStream.println("Applicable metadata.xml file: " + metadata_xml_file);
+
+ ArrayList metadata_values = metadata_xml_file.getMetadataAssignedToFile(file, filenameEncodingMetaOnly);
+ for (int j = 0; j < metadata_values.size(); j++) {
+ MetadataValue metadata_value = (MetadataValue) metadata_values.get(j);
+
+ // Overriding metadata: remove any values with this metadata element
+ if (metadata_value.isAccumulatingMetadata() == false) {
+ for (int k = metadata_values_all.size() - 1; k >= 0; k--) {
+ if (((MetadataValue) metadata_values_all.get(k)).getMetadataElement().equals(metadata_value.getMetadataElement())) {
+ metadata_values_all.remove(k);
+ }
+ }
+ }
+
+ metadata_values_all.add(metadata_value);
+ }
+ }
+
+ return metadata_values_all;
+ }
+
+
+ static public void loadMetadataXMLFiles(File directory, boolean skimfile)
+ {
+ // Make sure the directory (import) exists
+ if (directory.exists() == false) {
+ return;
+ }
+
+ // Look recursively at each subfile of the directory for metadata.xml files
+ File[] directory_files = directory.listFiles();
+ for (int i = 0; i < directory_files.length; i++) {
+ File child_file = directory_files[i];
+ if (child_file.isDirectory()) {
+ loadMetadataXMLFiles(child_file,skimfile);
+ }
+ else if (child_file.getName().equals("metadata.xml")) {
+ loadMetadataXMLFile(child_file,skimfile);
+ }
+ }
+ }
+
+
+ static private MetadataXMLFile loadMetadataXMLFile(File metadata_xml_file_file, boolean skimfile)
+ {
+ MetadataXMLFile metadata_xml_file = new MetadataXMLFile(metadata_xml_file_file.getAbsolutePath());
+ if (metadata_xml_files.contains(metadata_xml_file)) {
+ // This metadata.xml file has already been loaded, so return the loaded object
+ return (MetadataXMLFile) metadata_xml_files.get(metadata_xml_files.indexOf(metadata_xml_file));
+ }
+ if(skimfile){
+ metadata_xml_file.skimFile();
+ }
+ metadata_xml_files.add(metadata_xml_file);
+ return metadata_xml_file;
+ }
+
+
+ static public void removeMetadata(CollectionTreeNode file_node, ArrayList metadata_values)
+ {
+ removeMetadata(new CollectionTreeNode[] { file_node }, metadata_values);
+ }
+
+
+ static public void removeMetadata(CollectionTreeNode[] file_nodes, MetadataValue metadata_value)
+ {
+ ArrayList metadata_values = new ArrayList();
+ metadata_values.add(metadata_value);
+ removeMetadata(file_nodes, metadata_values);
+ }
+
+
+ static public void removeMetadata(CollectionTreeNode[] file_nodes, ArrayList metadata_values)
+ {
+ // Check the list of metadata values is non-empty
+ if (metadata_values.isEmpty()) {
+ return;
+ }
+
+ // Remove the metadata from each file node in turn
+ for (int i = 0; i < file_nodes.length; i++) {
+ File current_file = file_nodes[i].getFile();
+ DebugStream.println("Removing metadata from " + current_file.getAbsolutePath());
+
+ // Find which metadata.xml file needs editing
+ File current_file_directory = (current_file.isDirectory() ? current_file : current_file.getParentFile());
+ String current_file_directory_path = current_file_directory.getAbsolutePath();
+ for (int j = 0; j < metadata_xml_files.size(); j++) {
+ MetadataXMLFile metadata_xml_file = (MetadataXMLFile) metadata_xml_files.get(j);
+
+ // This metadata.xml file is only potentially applicable if it is above or at the same level as the file
+ if (current_file_directory_path.startsWith(metadata_xml_file.getParentFile().getAbsolutePath())) {
+ metadata_xml_file.removeMetadata(file_nodes[i], metadata_values);
+ if (!modified_metadata_xml_files.contains(metadata_xml_file)) {
+ modified_metadata_xml_files.add(metadata_xml_file);
+ }
+ }
+ }
+ }
+
+ // Let any listeners know that the metadata has changed
+ fireMetadataChangedEvent(file_nodes);
+ }
+
+
+ static public void removeMetadataChangedListener(MetadataChangedListener metadata_changed_listener)
+ {
+ metadata_changed_listeners.remove(metadata_changed_listener);
+ }
+
+
+ static public void replaceMetadata(CollectionTreeNode[] file_nodes, MetadataValue old_metadata_value, MetadataValue new_metadata_value)
+ {
+ // Replace the metadata in each file node in turn
+ for (int i = 0; i < file_nodes.length; i++) {
+ File current_file = file_nodes[i].getFile();
+ DebugStream.println("Replacing metadata in " + current_file.getAbsolutePath());
+
+ // Find which metadata.xml file needs editing
+ File current_file_directory = (current_file.isDirectory() ? current_file : current_file.getParentFile());
+ String current_file_directory_path = current_file_directory.getAbsolutePath();
+ for (int j = 0; j < metadata_xml_files.size(); j++) {
+ MetadataXMLFile metadata_xml_file = (MetadataXMLFile) metadata_xml_files.get(j);
+
+ // This metadata.xml file is only applicable if it is at the same level as the file
+ if (current_file_directory_path.equals(metadata_xml_file.getParentFile().getAbsolutePath())) {
+ metadata_xml_file.replaceMetadata(file_nodes[i], old_metadata_value, new_metadata_value);
+ if (!modified_metadata_xml_files.contains(metadata_xml_file)) {
+ modified_metadata_xml_files.add(metadata_xml_file);
+ }
+ }
+ }
+ }
+
+ // Let any listeners know that the metadata has changed
+ fireMetadataChangedEvent(file_nodes);
+ }
+
+
+ /** Ensures that all the metadata is written to metadata.xml files. */
+ static public void saveMetadataXMLFiles()
+ {
+ // Save the file currently loaded into memory out to disk
+ MetadataXMLFile.saveLoadedFile();
+
+ // If the collection is stored on a remote server, upload all the modified files now
+ if (Gatherer.isGsdlRemote) {
+ if (modified_metadata_xml_files.isEmpty()) {
+ DebugStream.println("No modified metadata.xml files to upload.");
+ return;
+ }
+
+ // Upload the files modified since last time, then reset the list
+ Gatherer.remoteGreenstoneServer.uploadCollectionFiles(
+ CollectionManager.getLoadedCollectionName(), (File[]) modified_metadata_xml_files.toArray(new File[0]));
+ modified_metadata_xml_files.clear();
+ }
+ }
+
+
+ static public void unloadMetadataXMLFile(File metadata_xml_file_file)
+ {
+ DebugStream.println("Unloading metadata.xml file " + metadata_xml_file_file);
+
+ // Find the metadata.xml file in the list of loaded files, and remove it
+ for (int i = 0; i < metadata_xml_files.size(); i++) {
+ MetadataXMLFile metadata_xml_file = (MetadataXMLFile) metadata_xml_files.get(i);
+ if (metadata_xml_file_file.getAbsolutePath().equals(metadata_xml_file.getAbsolutePath())) {
+ metadata_xml_files.remove(i);
+ break;
+ }
+ }
+ }
+
+
+ /**
+ * Comparator to order MetadataXMLFiles in ascending order from
+ * those in a higher level folder to those in a lower level folder
+ * It is based on the assumption that all MetadataXMLFiles sent to
+ * it to compare will be linear descendants of one toplevel folder
+ * E.g. /A/metadata.xml, /A/B/metadata.xml, /A/B/C/D/metadata.xml.
+ * In other words, that each is a substring of one of the others until we
+ * the toplevel folder is reached.
+ */
+ private static class MetadataXMLFileComparator implements Comparator {
+
+ public int compare(Object o1, Object o2) {
+ if(!(o1 instanceof MetadataXMLFile)) {
+ return -1;
+ } else if (!(o2 instanceof MetadataXMLFile)) {
+ return 1;
+ }
+
+ // Both are MetadataXMLFiles objects. Remove the terminating
+ // "metadata.xml" from their filenames to get their containing folder
+ String filename1 = ((MetadataXMLFile)o1).getParentFile().getAbsolutePath();
+ String filename2 = ((MetadataXMLFile)o2).getParentFile().getAbsolutePath();
+
+ // if 1 is a prefix of 2, then 1 < 2 in the ordering (1 comes before 2)
+ if(filename2.startsWith(filename1)) {
+ return -1;
+ } else if(filename1.startsWith(filename2)) {
+ return 1;
+ } else {
+ // unlikely that the metadata.xml files will be the same
+ // or that neither is a prefix of the other
+ return filename1.compareTo(filename2); // sorts in ascending order
+ }
+
+ }
+
+ public boolean equals(Object obj) {
+ if(!(obj instanceof MetadataXMLFileComparator)) {
+ return false;
+ }
+
+ // else it is the same sort of comparator
+ return true;
+ }
+
+ }
+
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/ProfileXMLFile.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/ProfileXMLFile.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/ProfileXMLFile.java (revision 31635)
@@ -0,0 +1,118 @@
+/**
+ *############################################################################
+ * A component of the Greenstone Librarian Interface, part of the Greenstone
+ * digital library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
+ *
+ * Copyright (C) 2004 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################################################
+ */
+
+package org.greenstone.gatherer.metadata;
+
+
+import java.io.*;
+import java.util.*;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.collection.CollectionManager;
+import org.greenstone.gatherer.remote.RemoteGreenstoneServer;
+import org.greenstone.gatherer.util.XMLTools;
+import org.w3c.dom.*;
+
+
+/** This class represents one profile.xml file */
+public class ProfileXMLFile
+ extends File
+{
+ private HashMap metadata_mapping = null;
+
+
+ public ProfileXMLFile(String profile_xml_file_path)
+ {
+ super(profile_xml_file_path);
+ }
+
+
+ public String getMetadataElementFor(String metadata_element_name_full)
+ {
+ if (getMetadataMapping() == null) {
+ return null;
+ }
+
+ return (String) metadata_mapping.get(metadata_element_name_full);
+ }
+
+
+ public HashMap getMetadataMapping()
+ {
+ // If we have already loaded the metadata mapping, use it
+ if (metadata_mapping != null) {
+ return metadata_mapping;
+ }
+
+ // Build the metadata mapping from the profile.xml file
+ metadata_mapping = new HashMap();
+
+ // Parse the profile.xml file
+ Document document = XMLTools.parseXMLFile(this);
+ if (document == null) {
+ System.err.println("Error: Could not parse profile.xml file " + getAbsolutePath());
+ return null;
+ }
+
+ // Read all the Action elements in the file
+ NodeList action_elements_nodelist = document.getElementsByTagName("Action");
+ for (int i = 0; i < action_elements_nodelist.getLength(); i++) {
+ Element current_action_element = (Element) action_elements_nodelist.item(i);
+ String source_metadata_element_name_full = current_action_element.getAttribute("source");
+ String target_metadata_element_name_full = current_action_element.getAttribute("target");
+ metadata_mapping.put(source_metadata_element_name_full, target_metadata_element_name_full);
+ }
+
+ return metadata_mapping;
+ }
+
+
+ public void mapElement(String source_metadata_element_name_full, String target_metadata_element_name_full)
+ {
+ // Parse the profile.xml file
+ Document document = XMLTools.parseXMLFile(this);
+ if (document == null) {
+ System.err.println("Error: Could not parse profile.xml file " + getAbsolutePath());
+ return;
+ }
+
+ // Create a new Action element to record this mapping
+ Element new_action_element = document.createElement("Action");
+ new_action_element.setAttribute("source", source_metadata_element_name_full);
+ new_action_element.setAttribute("target", target_metadata_element_name_full);
+ document.getDocumentElement().appendChild(new_action_element);
+
+ // Rewrite the profile.xml file
+ XMLTools.writeXMLFile(this, document);
+
+ // This is inefficient but for simplicity we'll just upload the file every time it is changed
+ if (Gatherer.isGsdlRemote) {
+ Gatherer.remoteGreenstoneServer.uploadCollectionFile(CollectionManager.getLoadedCollectionName(), this);
+ }
+
+ // Invalidate the metadata mapping
+ metadata_mapping = null;
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/ProfileXMLFileManager.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/ProfileXMLFileManager.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/metadata/ProfileXMLFileManager.java (revision 31635)
@@ -0,0 +1,90 @@
+/**
+ *############################################################################
+ * A component of the Greenstone Librarian Interface, part of the Greenstone
+ * digital library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
+ *
+ * Copyright (C) 2004 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################################################
+ */
+
+package org.greenstone.gatherer.metadata;
+
+
+import java.io.*;
+import java.util.*;
+import org.greenstone.gatherer.util.XMLTools;
+
+
+/** This class is a static class that manages the profile.xml file */
+public class ProfileXMLFileManager
+{
+ static private ProfileXMLFile profile_xml_file = null;
+
+
+ static public void clearProfileXMLFile()
+ {
+ profile_xml_file = null;
+ }
+
+
+ static public void loadProfileXMLFile(File directory)
+ {
+ // Make sure the directory (metadata) exists
+ if (directory.exists() == false) {
+ directory.mkdir();
+ }
+
+ File profile_xml_file_file = new File(directory, "profile.xml");
+ if (!profile_xml_file_file.exists()) {
+ // Create a new (empty) profile.xml file in this directory...
+ XMLTools.writeXMLFile(profile_xml_file_file, XMLTools.parseXMLFile("xml/profile.xml", true));
+ }
+
+ profile_xml_file = new ProfileXMLFile(profile_xml_file_file.getAbsolutePath());
+ }
+
+
+ static public String getMetadataElementFor(String metadata_element_name_full)
+ {
+ if (profile_xml_file == null) {
+ return null;
+ }
+
+ return profile_xml_file.getMetadataElementFor(metadata_element_name_full);
+ }
+
+
+ static public HashMap getMetadataMapping()
+ {
+ if (profile_xml_file == null) {
+ return null;
+ }
+
+ return profile_xml_file.getMetadataMapping();
+ }
+
+
+ static public void mapElement(String metadata_element_name_full, String target_metadata_element_name_full)
+ {
+ if (profile_xml_file != null) {
+ profile_xml_file.mapElement(metadata_element_name_full, target_metadata_element_name_full);
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/ActionQueue.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/ActionQueue.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/ActionQueue.java (revision 31635)
@@ -0,0 +1,194 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Greenstone Librarian Interface application, part of
+ * the Greenstone digital library suite from the New Zealand Digital
+ * Library Project at the University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, NZDL Project, University of Waikato
+ *
+ * Copyright (C) 2005 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+
+package org.greenstone.gatherer.remote;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.zip.*;
+import javax.swing.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.FedoraInfo;
+import org.greenstone.gatherer.GAuthenticator;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.collection.CollectionManager;
+import org.greenstone.gatherer.shell.GShell;
+import org.greenstone.gatherer.util.UnzipTools;
+import org.greenstone.gatherer.util.Utility;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.methods.multipart.FilePart;
+import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
+import org.apache.commons.httpclient.methods.multipart.Part;
+import org.apache.commons.httpclient.methods.multipart.*;
+import org.apache.commons.httpclient.params.*;
+import org.apache.commons.httpclient.HttpStatus;
+
+// ----------------------------------------------------------------------------------------------------
+// Part of the RemoteGreenstoneServer's QUEUE LAYER
+// ----------------------------------------------------------------------------------------------------
+
+// Moved here. Previously called RemoteGreenstoneServerActionQueue
+/**
+ * A Thread that maintains a queue of RemoteGreenstoneServer Actions
+ * that are to be executed in FIFO fashion.
+*/
+class ActionQueue extends Thread
+{
+ /** The queue of waiting jobs. */
+ private ArrayList queue = null;
+ private boolean exited;
+
+ public ActionQueue()
+ {
+ super("RemoteGreenstoneServerActionQueue");
+ exited = false;
+ if (Gatherer.isGsdlRemote) {
+ queue = new ArrayList();
+ start();
+ }
+ }
+
+
+ synchronized public void addAction(RemoteGreenstoneServerAction remote_greenstone_server_action)
+ {
+ queue.add(remote_greenstone_server_action);
+ notifyAll();
+ }
+
+
+ synchronized public int size()
+ {
+ return queue.size();
+ }
+
+ synchronized public RemoteGreenstoneServerAction getAction(int i)
+ {
+ return (RemoteGreenstoneServerAction)queue.get(i);
+ }
+
+ synchronized public boolean hasExited() {
+ return exited;
+ }
+
+ synchronized public void clear() {
+ queue.clear();
+ }
+
+ public void run()
+ {
+ boolean exit = false;
+
+ while (!exit) {
+ RemoteGreenstoneServerAction remote_greenstone_server_action = null;
+
+ // Wait until we are notify()ed by addAction that there is a new job on the queue
+ try {
+ if(Gatherer.remoteGreenstoneServer != null) {
+ Gatherer.remoteGreenstoneServer.getProgressBar().setAction(null);
+ }
+ synchronized (this) {
+ while(queue.size() <= 0) {
+ wait(); // wait for queue size to become > 0, which is done by external thread (performAction())
+ }
+ // Now there is at least one job on the queue, get the next in line and process it
+ remote_greenstone_server_action = (RemoteGreenstoneServerAction) queue.get(0); //getAction(0) is already synchronized
+ }
+ } catch (InterruptedException exception) {
+ // It may be that GLI is exiting if we get here
+ exit = true;
+ }
+
+ if(exit) {
+ break;
+ }
+
+ try {
+ remote_greenstone_server_action.perform();
+
+ // No exceptions were thrown, so the action was successful
+ remote_greenstone_server_action.processed_successfully = true;
+ }
+ catch (RemoteGreenstoneServerAction.ActionCancelledException exception) {
+ remote_greenstone_server_action.processed_successfully = false;
+ }
+ catch(java.net.ConnectException exception) {
+ if(exception.getMessage().trim().startsWith("Connection refused")) {
+ exit = true;
+ } else {
+ DebugStream.printStackTrace(exception);
+ }
+ JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("RemoteGreenstoneServer.Error", exception.getMessage()), Dictionary.get("RemoteGreenstoneServer.Error_Title"), JOptionPane.ERROR_MESSAGE);
+ remote_greenstone_server_action.processed_successfully = false;
+ }
+ catch (FileNotFoundException exception) {
+ // FileNotFoundException happens when there's no GS server at the user-provided
+ // url (the address of gliserver.pl is wrong).
+ exit = true;
+ DebugStream.printStackTrace(exception);
+ JOptionPane.showMessageDialog(Gatherer.g_man,
+ Dictionary.get("RemoteGreenstoneServer.Error",
+ "No gliserver.pl found. " + exception.getMessage()),
+ Dictionary.get("RemoteGreenstoneServer.Error_Title"),
+ JOptionPane.ERROR_MESSAGE);
+ remote_greenstone_server_action.processed_successfully = false;
+ }
+ catch (Exception exception) {
+ DebugStream.printStackTrace(exception);
+ JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("RemoteGreenstoneServer.Error", exception.getMessage()), Dictionary.get("RemoteGreenstoneServer.Error_Title"), JOptionPane.ERROR_MESSAGE);
+ remote_greenstone_server_action.processed_successfully = false;
+ }
+
+ // We're done with this action, for better or worse
+ try {
+ remote_greenstone_server_action.processed = true;
+ synchronized(remote_greenstone_server_action) {
+ remote_greenstone_server_action.notifyAll(); // notifies RemoteGreenstoneServer.performAction()
+ }
+ } catch (Exception exception) {
+ System.err.println("RemoteGreenstoneServerActionQueue.run() - exception: " + exception);
+ }
+ synchronized (this) { // remove the action just processed from the queue, since it's done.
+ queue.remove(0);
+ }
+ }
+
+ // Out of while, means exit = true
+ // Stop the gazillion annoying error messages when the connection was simply
+ // refused or when the user pressed Cancel in the opening dialog, by clearing
+ // the action queue of subsequent actions and setting exited to true.
+ synchronized(this) {
+ queue.clear();
+ exited = true;
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/RemoteGreenstoneServer.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/RemoteGreenstoneServer.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/RemoteGreenstoneServer.java (revision 31635)
@@ -0,0 +1,833 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, NZDL Project, University of Waikato
+ *
+ * Copyright (C) 2005 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+
+package org.greenstone.gatherer.remote;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.zip.*;
+import javax.swing.*;
+import java.io.ByteArrayOutputStream;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.FedoraInfo;
+import org.greenstone.gatherer.GAuthenticator;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.collection.CollectionManager;
+import org.greenstone.gatherer.shell.GShell;
+import org.greenstone.gatherer.util.UnzipTools;
+import org.greenstone.gatherer.util.Utility;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.methods.multipart.FilePart;
+import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
+import org.apache.commons.httpclient.methods.multipart.Part;
+import org.apache.commons.httpclient.methods.multipart.*;
+import org.apache.commons.httpclient.params.*;
+import org.apache.commons.httpclient.HttpStatus;
+
+
+public class RemoteGreenstoneServer
+{
+ // A PasswordAuthentication object is created whenever it is required
+ static private PasswordAuthentication remote_greenstone_server_authentication = null;
+ // static private PasswordAuthentication remote_greenstone_server_authentication = new PasswordAuthentication(System.getProperty("user.name"), new char[] { });
+
+ // the language and region environment variables (in "lang_REGION" form)
+ // this is necessary in order for the client and server sides to zip and unzip
+ // using the same settings
+ public final String lang_region;
+
+ private ActionQueue remote_greenstone_server_action_queue;
+ private RemoteGreenstoneServer.ProgressBar progress_bar;
+
+ public RemoteGreenstoneServer() {
+ // Create the progress_bar first since ActionQueue uses it in its thread
+ // (the thread will start immediately).
+ progress_bar = new RemoteGreenstoneServer.ProgressBar();
+ remote_greenstone_server_action_queue = new ActionQueue();
+
+ String langReg = System.getenv("LANG");
+ lang_region = (langReg == null) ? "" : langReg;
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+ // PUBLIC LAYER
+ // ----------------------------------------------------------------------------------------------------
+ public String deleteCollection(String collection_name)
+ {
+ return performAction(new RemoteGreenstoneServerAction.DeleteCollectionAction(collection_name));
+ }
+
+
+ public String deleteCollectionFile(String collection_name, File collection_file)
+ {
+ return performAction(new RemoteGreenstoneServerAction.DeleteCollectionFileAction(collection_name, collection_file));
+ }
+
+
+ public String downloadCollection(String collection_name)
+ {
+ return performAction(new RemoteGreenstoneServerAction.DownloadCollectionAction(collection_name));
+ }
+
+
+ public String downloadCollectionArchives(String collection_name)
+ {
+ return performAction(new RemoteGreenstoneServerAction.DownloadCollectionArchivesAction(collection_name));
+ }
+
+
+ public String downloadCollectionConfigurations()
+ {
+ return performAction(new RemoteGreenstoneServerAction.DownloadCollectionConfigurationsAction());
+ }
+
+
+ public String downloadCollectionFile(String collection_name, File collection_file)
+ {
+ return performAction(new RemoteGreenstoneServerAction.DownloadCollectionFileAction(collection_name, collection_file));
+ }
+
+ // get web.xml from the server -- for a remote gli of GS3
+ public String downloadWebXMLFile()
+ {
+ return performAction(new RemoteGreenstoneServerAction.DownloadWebXMLFileAction());
+ }
+
+ public String getScriptOptions(String script_name, String script_arguments)
+ {
+ return performAction(new RemoteGreenstoneServerAction.GetScriptOptionsAction(script_name, script_arguments));
+ }
+
+ //get all available site names from the server -- for a remote gli of GS3
+ public String getSiteNames()
+ {
+ return performAction(new RemoteGreenstoneServerAction.GetSiteNamesAction());
+ }
+
+ public String moveCollectionFile(String collection_name, File source_collection_file, File target_collection_file)
+ {
+ return performAction(new RemoteGreenstoneServerAction.MoveCollectionFileAction(
+ collection_name, source_collection_file, target_collection_file));
+ }
+
+
+ public String newCollectionDirectory(String collection_name, File new_collection_directory)
+ {
+ return performAction(new RemoteGreenstoneServerAction.NewCollectionDirectoryAction(
+ collection_name, new_collection_directory));
+ }
+
+
+ public String runScript(String collection_name, String script_name, String script_arguments, GShell shell)
+ {
+ return performAction(new RemoteGreenstoneServerAction.RunScriptAction(
+ collection_name, script_name, script_arguments, shell));
+ }
+
+
+ public String uploadCollectionFile(String collection_name, File collection_file)
+ {
+ return performAction(new RemoteGreenstoneServerAction.UploadCollectionFilesAction(
+ collection_name, new File[] { collection_file }));
+ }
+
+
+ public String uploadCollectionFiles(String collection_name, File[] collection_files)
+ {
+ return performAction(new RemoteGreenstoneServerAction.UploadCollectionFilesAction(collection_name, collection_files));
+ }
+
+
+ public String uploadFilesIntoCollection(String collection_name, File[] source_files, File target_collection_directory)
+ {
+ return performAction(new RemoteGreenstoneServerAction.UploadFilesIntoCollectionAction(
+ collection_name, source_files, target_collection_directory));
+ }
+
+ public boolean exists(String collection_name, File collection_file)
+ {
+ String result = performAction(new RemoteGreenstoneServerAction.ExistsAction(collection_name, collection_file));
+ if(result.indexOf("exists") != -1) {
+ return true;
+ }
+ else if(result.indexOf("does not exist") != -1) {
+ return false;
+ }
+ return false;
+ }
+
+ public int getGreenstoneVersion()
+ {
+ // returns message "Greenstone version is: "
+ String result = performAction(new RemoteGreenstoneServerAction.VersionAction());
+ int index = result.indexOf(":");
+ if(index != -1) {
+ result = result.substring(index+1).trim(); // skip the space after colon, must remove surrounding spaces
+ }
+ // if space is returned, then the request failed (the server may not have been running)
+ int greenstoneVersion = result.equals("") ? -1 : Integer.parseInt(result);
+ return greenstoneVersion;
+ }
+
+ /** For constructing the preview command (the library URL) with */
+ public String getLibraryURL(String serverHomeURL)
+ {
+ // returns message "Greenstone library URL suffix is: "
+ String libSuffix = performAction(new RemoteGreenstoneServerAction.LibraryURLSuffixAction());
+ int index = libSuffix.indexOf(":");
+ if(index != -1) {
+ libSuffix = libSuffix.substring(index+1).trim(); // skip the space after colon and remove surrounding spaces
+ }
+
+ // serverHomeURL is of the form, http://domain/other/stuff. We want the prefix upto & including domain
+ // and prepend that to the libraryURLSuffix
+ index = -1;
+ for(int i = 0; i < 3; i++) {
+ index = serverHomeURL.indexOf("/", index+1);
+ if(index == -1) { // shouldn't happen, but if it does, we'll be in an infinite loop
+ break;
+ }
+ }
+ serverHomeURL = serverHomeURL.substring(0, index);
+ return serverHomeURL + libSuffix;
+ }
+
+ // ----------------------------------------------------------------------------------------------------
+
+
+ public void exit()
+ {
+ System.err.println("Exiting, number of jobs on queue: " + remote_greenstone_server_action_queue.size());
+
+ // If there are still jobs on the queue we must wait for the jobs to finish
+ while (remote_greenstone_server_action_queue.size() > 0) {
+ synchronized (remote_greenstone_server_action_queue) {
+ try {
+ DebugStream.println("Waiting for queue to become empty...");
+ remote_greenstone_server_action_queue.wait(500);
+ }
+ catch (InterruptedException exception) {}
+ }
+ }
+ }
+
+
+ // ----------------------------------------------------------------------------------------------------
+ // QUEUE LAYER
+ // ----------------------------------------------------------------------------------------------------
+
+
+ /** Returns null if we cannot wait for the action to finish, "" if the action failed, or the action output. */
+ private String performAction(RemoteGreenstoneServerAction remote_greenstone_server_action)
+ {
+ // Check for whether the queue thread stopped running because
+ // the user cancelled out. If so, exit GLI.
+ if(remote_greenstone_server_action_queue.hasExited()) {
+ remote_greenstone_server_action_queue.clear();
+ //remote_greenstone_server_action_queue = null;
+ Gatherer.exit();
+ return "";
+ }
+
+ // Add the action to the queue
+ remote_greenstone_server_action_queue.addAction(remote_greenstone_server_action);
+ String threadName = Thread.currentThread().getName();
+
+ // If we're running in the GUI thread we must return immediately
+ // We cannot wait for the action to complete because this will block any GUI updates
+ if (SwingUtilities.isEventDispatchThread()) {
+ System.err.println(threadName
+ + "\tACTION QUEUED: In event dispatch thread, returning immediately...\n\t"
+ + remote_greenstone_server_action);
+ return null;
+ }
+
+ // Otherwise wait until the action is processed
+ try {
+ synchronized (remote_greenstone_server_action) {
+ while(!remote_greenstone_server_action.processed) {
+ //System.err.println("Waiting for action to complete...: " + remote_greenstone_server_action);
+ DebugStream.println("Waiting for action to complete...");
+ remote_greenstone_server_action.wait(); // wait to be notified when the action has been processed
+ }
+ }
+ } catch (Exception e) {
+ System.err.println("RemoteGreenstoneServer.performAction() - exception: " + e);
+ e.printStackTrace();
+ }
+
+ // Return "" if the action failed
+ if (!remote_greenstone_server_action.processed_successfully) {
+ return "";
+ }
+ // Otherwise return the action output
+ return remote_greenstone_server_action.action_output;
+ }
+
+
+ // ----------------------------------------------------------------------------------------------------
+ // PROGRESS BAR
+ // ----------------------------------------------------------------------------------------------------
+
+ static class ProgressBar
+ extends JProgressBar
+ {
+ public ProgressBar()
+ {
+ setBackground(Configuration.getColor("coloring.collection_tree_background", false));
+ setForeground(Configuration.getColor("coloring.collection_tree_foreground", false));
+ setString(Dictionary.get("FileActions.No_Activity"));
+ setStringPainted(true);
+ }
+
+ /** synchronized to avoid conflicts since several threads access this */
+ synchronized public void setAction(String action)
+ {
+ if (action != null) {
+ DebugStream.println(action);
+ }
+
+ // We cannot call this from the GUI thread otherwise the progress bar won't start
+ if (SwingUtilities.isEventDispatchThread()) {
+ System.err.println("ERROR: RemoteGreenstoneServerProgressBar.setAction() called from event dispatch thread!");
+ return;
+ }
+
+ // Set the string on the progress bar, and start or stop it
+ if (action == null) {
+ setString(Dictionary.get("FileActions.No_Activity"));
+ setIndeterminate(false);
+ }
+ else {
+ setString(action);
+ setIndeterminate(true);
+ }
+ }
+ }
+
+ /** synchronized to avoid conflicts since several threads access this */
+ synchronized public RemoteGreenstoneServer.ProgressBar getProgressBar()
+ {
+ return progress_bar;
+ }
+
+
+ // ----------------------------------------------------------------------------------------------------
+ // AUTHENTICATION LAYER
+ // ----------------------------------------------------------------------------------------------------
+
+ static private class RemoteGreenstoneServerAuthenticateTask
+ extends Thread
+ {
+ public void run()
+ {
+ remote_greenstone_server_authentication = new RemoteGreenstoneServerAuthenticator().getAuthentication();
+ }
+
+
+ static private class RemoteGreenstoneServerAuthenticator
+ extends GAuthenticator
+ {
+ public PasswordAuthentication getAuthentication(String username, String password)
+ {
+ return getPasswordAuthentication(username,password);
+ }
+
+ public PasswordAuthentication getAuthentication()
+ {
+ return getPasswordAuthentication();
+ }
+
+ protected String getMessageString()
+ {
+ if (Gatherer.GS3){
+ return (Dictionary.get("RemoteGreenstoneServer.Authentication_Message_gs3") + " " + Configuration.site_name);
+ }
+ return Dictionary.get("RemoteGreenstoneServer.Authentication_Message");
+ }
+ }
+ }
+
+ public void set_remote_greenstone_server_authentication_to_null() {
+ remote_greenstone_server_authentication = null;
+ }
+
+ private void authenticateUser()
+ throws RemoteGreenstoneServerAction.ActionCancelledException
+ {
+ // If we don't have any authentication information then ask for it now
+ if (remote_greenstone_server_authentication == null) {
+ try {
+ // We have to do this on the GUI thread
+ SwingUtilities.invokeAndWait(new RemoteGreenstoneServerAuthenticateTask());
+ }
+ catch (Exception exception) {
+ System.err.println("Exception occurred when authenticating the user: " + exception);
+ DebugStream.printStackTrace(exception);
+ }
+
+ // If it is still null then the user has cancelled the authentication, so the action is cancelled
+ if (remote_greenstone_server_authentication == null) {
+ throw new RemoteGreenstoneServerAction.ActionCancelledException();
+ }
+ }
+ }
+
+
+ public String getUsername()
+ {
+ if (remote_greenstone_server_authentication != null) {
+ return remote_greenstone_server_authentication.getUserName();
+ }
+
+ return null;
+ }
+
+
+ // ----------------------------------------------------------------------------------------------------
+ // REQUEST LAYER
+ // ----------------------------------------------------------------------------------------------------
+
+
+ /** Returns the command output if the action completed, throws some kind of exception otherwise. */
+ String downloadFile(String gliserver_args, String file_path)
+ throws Exception
+ {
+ while (true) {
+ // Check that Configuration.gliserver_url is set
+ if (Configuration.gliserver_url == null) {
+ throw new Exception("Empty gliserver URL: please set this in Preferences before continuing.");
+ }
+
+ // Ask for authentication information (if necessary), then perform the action
+ authenticateUser();
+ String gliserver_url_string = Configuration.gliserver_url.toString();
+ String command_output = downloadFileInternal(gliserver_url_string, gliserver_args, file_path);
+
+ // Debugging - print any ok messages to stderr
+ //System.err.println("**** RECEIVED (sendCommandToServer()): " + command_output);
+
+ // Check the first line to see if authentication has failed; if so, go around the loop again
+ if (command_output.startsWith("ERROR: Authentication failed:")) {
+ JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("RemoteGreenstoneServer.Error", command_output.substring("ERROR: ".length())), Dictionary.get("RemoteGreenstoneServer.Error_Title"), JOptionPane.ERROR_MESSAGE);
+ remote_greenstone_server_authentication = null;
+ continue;
+ }
+ // Check if the collection is locked by someone else; if so, see if the user wants to steal the lock
+ else if (command_output.startsWith("ERROR: Collection is locked by: ")) {
+ if (JOptionPane.showConfirmDialog(Gatherer.g_man, Dictionary.get("RemoteGreenstoneServer.Steal_Lock_Message", command_output.substring("ERROR: Collection is locked by: ".length())), Dictionary.get("RemoteGreenstoneServer.Error_Title"), JOptionPane.YES_NO_OPTION) == JOptionPane.NO_OPTION) {
+ // The user has decided to cancel the action
+ throw new RemoteGreenstoneServerAction.ActionCancelledException();
+ }
+
+ // The user has decided to steal the lock... rerun the command with "&steal_lock="
+ gliserver_args += "&steal_lock=";
+ continue;
+ }
+ // Handle other types of errors by throwing an exception
+ else if (command_output.startsWith("ERROR: ")) {
+ throw new Exception(command_output.substring("ERROR: ".length()));
+ }
+
+ // There were no exceptions thrown so the action must have succeeded
+ return command_output;
+ }
+ }
+
+ /** Returns true or false depending on whether authentication is required for the cmd
+ * string embedded in the given gliserver_args. No authentication is required for either
+ * of the commands greenstone-server-version and get-library-url-suffix. */
+ private boolean isAuthenticationRequired(String gliserver_args) {
+ return ((gliserver_args.indexOf("greenstone-server-version") == -1)
+ && (gliserver_args.indexOf("get-library-url-suffix") == -1));
+ }
+
+
+ /** Returns the command output if the action completed, throws some kind of exception otherwise.
+ * Package access, so that RemoteGreenstoneServerAction.java can call this.
+ */
+ String sendCommandToServer(String gliserver_args, GShell shell)
+ throws Exception
+ {
+ while (true) {
+
+ // Check that Configuration.gliserver_url is set
+ if (Configuration.gliserver_url == null) {
+ throw new Exception("Empty gliserver URL: please set this in Preferences before continuing.");
+ }
+
+ // Ask for authentication information (if necessary), then perform the action
+ if(isAuthenticationRequired(gliserver_args)) {
+ try {
+ authenticateUser();
+ } catch (RemoteGreenstoneServerAction.ActionCancelledException e) {
+ // Authentication popup only appears at the start. If the user cancelled
+ // out of it, then another remote action always remains on the queue,
+ // preventing a clean exit. Need to clear queue before exit.
+ synchronized (remote_greenstone_server_action_queue) {
+ remote_greenstone_server_action_queue.clear();
+ }
+ Gatherer.exit();
+ }
+ }
+ String gliserver_url_string = Configuration.gliserver_url.toString();
+ String command_output = sendCommandToServerInternal(gliserver_url_string, gliserver_args, shell);
+
+ // Debugging - print any ok messages to stderr
+ //if(!(command_output.trim().startsWith("<"))) {
+ //System.err.println("**** RECEIVED (sendCommandToServer()): " + command_output);
+ //}
+
+ // Check the first line to see if authentication has failed; if so, go around the loop again
+ if (command_output.startsWith("ERROR: Authentication failed:")) {
+ JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("RemoteGreenstoneServer.Error", command_output.substring("ERROR: ".length())), Dictionary.get("RemoteGreenstoneServer.Error_Title"), JOptionPane.ERROR_MESSAGE);
+ remote_greenstone_server_authentication = null;
+ continue;
+ }
+ // Check if the collection is locked by someone else; if so, see if the user wants to steal the lock
+ else if (command_output.startsWith("ERROR: Collection is locked by: ")) {
+ if (JOptionPane.showConfirmDialog(Gatherer.g_man, Dictionary.get("RemoteGreenstoneServer.Steal_Lock_Message", command_output.substring("ERROR: Collection is locked by: ".length())), Dictionary.get("RemoteGreenstoneServer.Error_Title"), JOptionPane.YES_NO_OPTION) == JOptionPane.NO_OPTION) {
+ // The user has decided to cancel the action
+ throw new RemoteGreenstoneServerAction.ActionCancelledException();
+ }
+
+ // The user has decided to steal the lock... rerun the command with "&steal_lock="
+ gliserver_args += "&steal_lock=";
+ continue;
+ }
+ // Handle other types of errors by throwing an exception
+ else if (command_output.startsWith("ERROR: ")) {
+ throw new Exception(command_output.substring("ERROR: ".length()));
+ } else if (command_output.indexOf("ERROR: ") != -1) { // check if ERROR occurs anywhere else in the output
+ throw new Exception(command_output);
+ }
+
+
+ // There were no exceptions thrown so the action must have succeeded
+ return command_output;
+ }
+ }
+
+
+ /** Returns the command output if the action completed, throws some kind of exception otherwise. */
+ String uploadFile(String gliserver_args, String file_path)
+ throws Exception
+ {
+ while (true) {
+ // Check that Configuration.gliserver_url is set
+ if (Configuration.gliserver_url == null) {
+ throw new Exception("Empty gliserver URL: please set this in Preferences before continuing.");
+ }
+
+ // Ask for authentication information (if necessary), then perform the action
+ authenticateUser();
+ String gliserver_url_string = Configuration.gliserver_url.toString();
+ String command_output = uploadFileInternal(gliserver_url_string, gliserver_args, file_path);
+ // System.err.println("Command output: " + command_output);
+
+ // Check the first line to see if authentication has failed; if so, go around the loop again
+ if (command_output.startsWith("ERROR: Authentication failed:")) {
+ JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("RemoteGreenstoneServer.Error", command_output.substring("ERROR: ".length())), Dictionary.get("RemoteGreenstoneServer.Error_Title"), JOptionPane.ERROR_MESSAGE);
+ remote_greenstone_server_authentication = null;
+ continue;
+ }
+ // Check if the collection is locked by someone else; if so, see if the user wants to steal the lock
+ else if (command_output.startsWith("ERROR: Collection is locked by: ")) {
+ if (JOptionPane.showConfirmDialog(Gatherer.g_man, Dictionary.get("RemoteGreenstoneServer.Steal_Lock_Message", command_output.substring("ERROR: Collection is locked by: ".length())), Dictionary.get("RemoteGreenstoneServer.Error_Title"), JOptionPane.YES_NO_OPTION) == JOptionPane.NO_OPTION) {
+ // The user has decided to cancel the action
+ throw new RemoteGreenstoneServerAction.ActionCancelledException();
+ }
+
+ // The user has decided to steal the lock... rerun the command with "&steal_lock="
+ gliserver_args += "&steal_lock=";
+ continue;
+ }
+ // Handle other types of errors by throwing an exception
+ else if (command_output.startsWith("ERROR: ")) {
+ throw new Exception(command_output.substring("ERROR: ".length()));
+ }
+
+ // There were no exceptions thrown so the action must have succeeded
+ return command_output;
+ }
+ }
+
+
+ // ----------------------------------------------------------------------------------------------------
+ // NETWORK LAYER
+ // ----------------------------------------------------------------------------------------------------
+
+
+ /** Returns the command output if the action completed, throws some kind of exception otherwise. */
+ private String downloadFileInternal(String download_cgi, String cgi_args, String file_path)
+ throws Exception
+ {
+ DebugStream.println("gliserver URL: " + download_cgi);
+ System.err.println("gliserver args: " + cgi_args);
+
+ // Add username and password, and a timestamp
+ cgi_args += "&un=" + remote_greenstone_server_authentication.getUserName();
+ cgi_args += "&pw=" + new String(remote_greenstone_server_authentication.getPassword());
+ cgi_args += "&ts=" + System.currentTimeMillis();
+ if (Gatherer.GS3){
+ cgi_args += "&site=" + Configuration.site_name;
+ }
+
+ URL download_url = new URL(download_cgi);
+ URLConnection dl_connection = download_url.openConnection();
+ dl_connection.setDoOutput(true);
+ OutputStream dl_os = dl_connection.getOutputStream();
+
+ PrintWriter dl_out = new PrintWriter(dl_os);
+ dl_out.println(cgi_args);
+ dl_out.close();
+
+ // Download result from running cgi script
+ InputStream dl_is = dl_connection.getInputStream();
+ BufferedInputStream dl_bis = new BufferedInputStream(dl_is);
+ DataInputStream dl_dbis = new DataInputStream(dl_bis);
+
+ String first_line = "";
+ byte[] buf = new byte[1024];
+ int len = dl_dbis.read(buf);
+ if (len >= 0) {
+ String first_chunk = new String(buf, 0, len);
+ // first_line = first_chunk.substring(0, ((first_chunk.indexOf("\n") != -1) ? first_chunk.indexOf("\n") : len));
+ first_line = first_chunk.substring(0, ((first_chunk.indexOf("\n") != -1) ? first_chunk.indexOf("\n") : ((first_chunk.length()= 0) {
+ zip_bfos.write(buf, 0, len);
+ len = dl_dbis.read(buf);
+ }
+
+ zip_bfos.close();
+ zip_fos.close();
+ }
+
+ dl_dbis.close();
+ dl_bis.close();
+ dl_is.close();
+ return first_line;
+ }
+
+
+ /** Returns the command output if the action completed, throws some kind of exception otherwise. */
+ private String sendCommandToServerInternal(String gliserver_url_string, String cgi_args, GShell shell)
+ throws Exception
+ {
+ DebugStream.println("gliserver URL: " + gliserver_url_string);
+ System.err.println("gliserver args: " + cgi_args);
+
+ // Add username and password, and a timestamp
+ if(isAuthenticationRequired(cgi_args)) {
+ cgi_args += "&un=" + remote_greenstone_server_authentication.getUserName();
+ cgi_args += "&pw=" + new String(remote_greenstone_server_authentication.getPassword());
+ }
+ cgi_args += "&ts=" + System.currentTimeMillis();
+ if (Gatherer.GS3){
+ cgi_args += "&site=" + Configuration.site_name;
+ }
+
+ URL gliserver_url = new URL(gliserver_url_string + "?" + cgi_args);
+ URLConnection gliserver_connection = gliserver_url.openConnection();
+
+ // Read the output of the command from the server, and return it
+ StringBuffer command_output_buffer = new StringBuffer(2048);
+ InputStream gliserver_is = gliserver_connection.getInputStream();
+ BufferedReader gliserver_in = new BufferedReader(new InputStreamReader(gliserver_is, "UTF-8"));
+ String gliserver_output_line = gliserver_in.readLine();
+ while (gliserver_output_line != null) {
+ if (shell != null) {
+ shell.fireMessage(gliserver_output_line);
+ if (shell.hasSignalledStop()) {
+ throw new RemoteGreenstoneServerAction.ActionCancelledException();
+ }
+ }
+ command_output_buffer.append(gliserver_output_line + "\n");
+ gliserver_output_line = gliserver_in.readLine();
+ }
+ gliserver_in.close();
+
+ return command_output_buffer.toString();
+ }
+
+
+ /** Returns the command output if the action completed, throws some kind of exception otherwise. */
+ private String uploadFileInternal(String upload_cgi, String cgi_args, String file_path)
+ throws Exception
+ {
+ System.err.println("gliserver URL: " + upload_cgi);
+ System.err.println("gliserver args: " + cgi_args);
+
+ //For a remote GS3
+ //GS3 is running on Tomcat, and Tomcat requires a connection timeout to be set up at the client
+ //side while uploading files. As HttpURLConnection couldn't set the connection timeout, HttpClient.jar
+ //from Jakarta is applied to solve this problem only for uploading files.
+ if (Gatherer.GS3) {
+
+ // Setup the POST method
+ PostMethod httppost = new PostMethod(upload_cgi+"?"+cgi_args); // cgi_args: QUERY_STRING on perl server side
+
+ // construct the multipartrequest form
+ String[] cgi_array=cgi_args.split("&");// get parameter-value pairs from cgi_args string
+ Part[] parts=new Part[cgi_array.length+5];
+
+ // The FilePart: consisting of the (cgi-arg) name and (optional) filename in the Content-Disposition
+ // of the POST request Header (see CGI.pm), and the actual zip file itself. It uses the defaults:
+ // Content-Type: application/octet-stream; charset=ISO-8859-1,Content-Transfer-Encoding: binary
+ parts[0]= new FilePart("uploaded_file", "zipFile", new File(file_path));
+
+ parts[1]= new StringPart("un", remote_greenstone_server_authentication.getUserName());
+ parts[2]= new StringPart("pw", new String(remote_greenstone_server_authentication.getPassword()));
+ parts[3]= new StringPart("ts", String.valueOf(System.currentTimeMillis()));
+ parts[4]= new StringPart("site", Configuration.site_name);
+ // find all parameters of cgi-args and add them into Part[]
+ for (int i=0; i 0) {
+ dos.write(buffer, 0, bytesRead);
+ bytesAvailable = fileInputStream.available();
+ bufferSize = Math.min(bytesAvailable, maxBufferSize);
+ bytesRead = fileInputStream.read(buffer, 0, bufferSize);
+ }
+
+ // close streams
+ fileInputStream.close();
+ dos.flush();
+ dos.close();
+
+ // Read the output of the command from the server, and return it
+ String command_output = "";
+ InputStream gliserver_is = gliserver_connection.getInputStream();
+ BufferedReader gliserver_in = new BufferedReader(new InputStreamReader(gliserver_is, "UTF-8"));
+ String gliserver_output_line = gliserver_in.readLine();
+ while (gliserver_output_line != null) {
+ command_output += gliserver_output_line + "\n";
+ gliserver_output_line = gliserver_in.readLine();
+ }
+ gliserver_in.close();
+
+ return command_output;
+ }
+
+
+ // ----------------------------------------------------------------------------------------------------
+ // UTILITIES
+ // ----------------------------------------------------------------------------------------------------
+
+
+ public String getPathRelativeToDirectory(File file, String directory_path)
+ {
+ String file_path = file.getAbsolutePath();
+ if (!file_path.startsWith(directory_path)) {
+ System.err.println("ERROR: File path " + file_path + " is not a child of " + directory_path);
+ return file_path;
+ }
+
+ String relative_file_path = file_path.substring(directory_path.length());
+ if (relative_file_path.startsWith(File.separator)) {
+ relative_file_path = relative_file_path.substring(File.separator.length());
+ }
+ return relative_file_path;
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/RemoteGreenstoneServerAction.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/RemoteGreenstoneServerAction.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/RemoteGreenstoneServerAction.java (revision 31635)
@@ -0,0 +1,664 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Greenstone Librarian Interface application, part of
+ * the Greenstone digital library suite from the New Zealand Digital
+ * Library Project at the University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, NZDL Project, University of Waikato
+ *
+ * Copyright (C) 2005 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+*/
+
+package org.greenstone.gatherer.remote;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.zip.*;
+import javax.swing.*;
+import java.io.ByteArrayOutputStream;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.FedoraInfo;
+import org.greenstone.gatherer.GAuthenticator;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.collection.CollectionManager;
+import org.greenstone.gatherer.feedback.Base64;
+import org.greenstone.gatherer.shell.GShell;
+import org.greenstone.gatherer.util.UnzipTools;
+import org.greenstone.gatherer.util.Utility;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.methods.multipart.FilePart;
+import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
+import org.apache.commons.httpclient.methods.multipart.Part;
+import org.apache.commons.httpclient.methods.multipart.*;
+import org.apache.commons.httpclient.params.*;
+import org.apache.commons.httpclient.HttpStatus;
+
+
+// Code moved here from RemoteGreenstoneServer.java
+// ----------------------------------------------------------------------------------------------------
+// ACTIONS
+// ----------------------------------------------------------------------------------------------------
+/** RemoteGreenstoneServer Actions that can go into the remote GS server's ActionQueue.
+ * Contains many package access inner classes that are Actions.
+*/
+public abstract class RemoteGreenstoneServerAction
+{
+ public String action_output = null;
+ public boolean processed = false;
+ public boolean processed_successfully;
+
+ protected RemoteGreenstoneServer remote = null;
+ protected RemoteGreenstoneServer.ProgressBar progress_bar = null;
+
+ public RemoteGreenstoneServerAction() {
+ remote = Gatherer.remoteGreenstoneServer;
+ progress_bar = remote.getProgressBar();
+ }
+
+ abstract public void perform()
+ throws Exception;
+
+ /*
+ protected String sendCommandToServer(String gliserver_args, GShell shell)
+ throws Exception
+ {
+ return Gatherer.remoteGreenstoneServer.sendCommandToServer(gliserver_args, shell);
+ }
+
+ protected void setAction(String action) {
+ Gatherer.remoteGreenstoneServer.progress_bar.setAction(action);
+ }*/
+
+ static class ActionCancelledException
+ extends Exception
+ {
+ }
+
+ /**
+ * --------------------------------------------------------------------------------------------
+ * DELETE COLLECTION
+ * --------------------------------------------------------------------------------------------
+ */
+ static class DeleteCollectionAction
+ extends RemoteGreenstoneServerAction
+ {
+ private String collection_name;
+
+ public DeleteCollectionAction(String collection_name)
+ {
+ this.collection_name = collection_name;
+ }
+
+ public void perform()
+ throws Exception
+ {
+ progress_bar.setAction("Deleting collection " + collection_name + "...");
+
+ String delete_collection_command = "cmd=delete-collection";
+ delete_collection_command += "&c=" + URLEncoder.encode(collection_name.replace(File.separatorChar, '|'), "UTF-8");
+ action_output = remote.sendCommandToServer(delete_collection_command, null);
+ }
+ }
+
+
+ /**
+ * --------------------------------------------------------------------------------------------
+ * DELETE COLLECTION FILE
+ * --------------------------------------------------------------------------------------------
+ */
+ static class DeleteCollectionFileAction
+ extends RemoteGreenstoneServerAction
+ {
+ private String collection_name;
+ private File collection_file;
+
+ public DeleteCollectionFileAction(String collection_name, File collection_file)
+ {
+ this.collection_name = collection_name;
+ this.collection_file = collection_file;
+ }
+
+ public void perform()
+ throws Exception
+ {
+ String collection_directory_path = CollectionManager.getCollectionDirectoryPath(collection_name);
+ String collection_file_relative_path = remote.getPathRelativeToDirectory(collection_file, collection_directory_path);
+ collection_file_relative_path = collection_file_relative_path.replaceAll((Utility.isWindows() ? "\\\\" : "\\/"), "|");
+ progress_bar.setAction("Deleting collection file " + collection_file_relative_path + "...");
+
+ String delete_collection_file_command = "cmd=delete-collection-file";
+ delete_collection_file_command += "&c=" + URLEncoder.encode(collection_name.replace(File.separatorChar, '|'), "UTF-8");
+ delete_collection_file_command += "&file=" + Base64.encodeBytesInSingleLine(collection_file_relative_path.getBytes());
+ action_output = remote.sendCommandToServer(delete_collection_file_command, null);
+ }
+ }
+
+
+ /**
+ * --------------------------------------------------------------------------------------------
+ * DOWNLOAD COLLECTION
+ * --------------------------------------------------------------------------------------------
+ */
+ static class DownloadCollectionAction
+ extends RemoteGreenstoneServerAction
+ {
+ private String collection_name;
+
+ public DownloadCollectionAction(String collection_name)
+ {
+ this.collection_name = collection_name;
+ }
+
+ public void perform()
+ throws Exception
+ {
+ progress_bar.setAction("Downloading remote collection " + collection_name + "...");
+
+ String download_collection_command = "cmd=download-collection";
+ download_collection_command += "&c=" + URLEncoder.encode(collection_name.replace(File.separatorChar, '|'), "UTF-8");
+ download_collection_command += "&lr=" + remote.lang_region;
+ String zip_file_path = Gatherer.getCollectDirectoryPath() + collection_name + ".zip";
+ action_output = remote.downloadFile(download_collection_command, zip_file_path);
+
+ // Delete the existing (local) collection directory
+ Utility.delete(new File(CollectionManager.getCollectionDirectoryPath(collection_name)));
+
+ // Unzip the collection just downloaded
+ UnzipTools.unzipFile(zip_file_path, Gatherer.getCollectDirectoryPath());
+ }
+ }
+
+
+ /**
+ * --------------------------------------------------------------------------------------------
+ * DOWNLOAD COLLECTION ARCHIVES
+ * --------------------------------------------------------------------------------------------
+ */
+ static class DownloadCollectionArchivesAction
+ extends RemoteGreenstoneServerAction
+ {
+ private String collection_name;
+
+ public DownloadCollectionArchivesAction(String collection_name)
+ {
+ this.collection_name = collection_name;
+ }
+
+ public void perform()
+ throws Exception
+ {
+ progress_bar.setAction("Downloading collection archives for " + collection_name + "...");
+
+ String download_collection_archives_command = "cmd=download-collection-archives";
+ download_collection_archives_command += "&c=" + URLEncoder.encode(collection_name.replace(File.separatorChar, '|'), "UTF-8");
+ download_collection_archives_command += "&lr=" + remote.lang_region;
+ String zip_file_path = Gatherer.getCollectDirectoryPath() + collection_name + "-archives.zip";
+ action_output = remote.downloadFile(download_collection_archives_command, zip_file_path);
+
+ // Delete the existing (local) collection archives
+ Utility.delete(new File(CollectionManager.getLoadedCollectionArchivesDirectoryPath()));
+
+ // Unzip the collection archives just downloaded
+ UnzipTools.unzipFile(zip_file_path, Gatherer.getCollectDirectoryPath());
+ }
+ }
+
+
+ /**
+ * --------------------------------------------------------------------------------------------
+ * DOWNLOAD COLLECTION CONFIGURATIONS
+ * --------------------------------------------------------------------------------------------
+ */
+ static class DownloadCollectionConfigurationsAction
+ extends RemoteGreenstoneServerAction
+ {
+ public DownloadCollectionConfigurationsAction()
+ {
+ }
+
+ public void perform()
+ throws Exception
+ {
+ progress_bar.setAction("Downloading collection configurations...");
+
+ // Delete the existing (local) collect directory
+ Utility.delete(new File(Gatherer.getCollectDirectoryPath()));
+ new File(Gatherer.getCollectDirectoryPath()).mkdirs();
+
+ String download_collection_configurations_command = "cmd=download-collection-configurations";
+ download_collection_configurations_command += "&lr=" + remote.lang_region;
+ String zip_file_path = Gatherer.getCollectDirectoryPath() + "collections.zip";
+ action_output = remote.downloadFile(download_collection_configurations_command, zip_file_path);
+
+ // Unzip the collection configurations just downloaded
+ UnzipTools.unzipFile(zip_file_path, Gatherer.getCollectDirectoryPath());
+ }
+ }
+
+ /**
+ * --------------------------------------------------------------------------------------------
+ * DISCOVERING WHAT VERSION THE REMOTE GREENSTONE SERVER IS (2 or 3)
+ * --------------------------------------------------------------------------------------------
+ */
+
+ static class VersionAction
+ extends RemoteGreenstoneServerAction
+ {
+ public void perform()
+ throws Exception
+ {
+ action_output = remote.sendCommandToServer("cmd=greenstone-server-version", null);
+ }
+ }
+
+ static class LibraryURLSuffixAction
+ extends RemoteGreenstoneServerAction
+ {
+ public void perform()
+ throws Exception
+ {
+ action_output = remote.sendCommandToServer("cmd=get-library-url-suffix", null);
+ }
+ }
+
+ /**
+ * --------------------------------------------------------------------------------------------
+ * CHECKING IF A FILE/FOLDER EXISTS ON SERVER SIDE
+ * --------------------------------------------------------------------------------------------
+ */
+ static class ExistsAction
+ extends RemoteGreenstoneServerAction
+ {
+ private String collection_name;
+ private File collection_file;
+
+ public ExistsAction(String collection_name, File collection_file)
+ {
+ this.collection_name = collection_name;
+ this.collection_file = collection_file;
+ }
+
+ public void perform()
+ throws Exception
+ {
+ String collection_directory_path = CollectionManager.getCollectionDirectoryPath(collection_name);
+ String collection_file_relative_path = remote.getPathRelativeToDirectory(collection_file, collection_directory_path);
+ collection_file_relative_path = collection_file_relative_path.replaceAll((Utility.isWindows() ? "\\\\" : "\\/"), "|");
+ File file = new File(collection_directory_path, collection_file_relative_path);
+
+ String file_exists_command = "cmd=file-exists";
+ file_exists_command += "&c=" + URLEncoder.encode(collection_name.replace(File.separatorChar, '|'), "UTF-8");
+ // base64 encode the filename to preserve special characters
+ file_exists_command += "&file=" + Base64.encodeBytesInSingleLine(collection_file_relative_path.getBytes());
+
+ // returns either "File exists" or "File does not exist"
+ // for the file/folder collection_file
+ action_output = remote.sendCommandToServer(file_exists_command, null);
+ }
+ }
+
+
+ /**
+ * --------------------------------------------------------------------------------------------
+ * DOWNLOAD COLLECTION FILE
+ * --------------------------------------------------------------------------------------------
+ */
+ static class DownloadCollectionFileAction
+ extends RemoteGreenstoneServerAction
+ {
+ private String collection_name;
+ private File collection_file;
+
+ public DownloadCollectionFileAction(String collection_name, File collection_file)
+ {
+ this.collection_name = collection_name;
+ this.collection_file = collection_file;
+ }
+
+ public void perform()
+ throws Exception
+ {
+ String collection_directory_path = CollectionManager.getCollectionDirectoryPath(collection_name);
+ String collection_file_relative_path = remote.getPathRelativeToDirectory(collection_file, collection_directory_path);
+ collection_file_relative_path = collection_file_relative_path.replaceAll((Utility.isWindows() ? "\\\\" : "\\/"), "|");
+ progress_bar.setAction("Downloading collection file " + collection_file_relative_path + "...");
+
+ String download_collection_file_command = "cmd=download-collection-file";
+ download_collection_file_command += "&c=" + URLEncoder.encode(collection_name.replace(File.separatorChar, '|'), "UTF-8");
+ download_collection_file_command += "&file=" + URLEncoder.encode(collection_file_relative_path, "UTF-8");
+ download_collection_file_command += "&lr=" + remote.lang_region;
+
+ // String zip_file_name = collection_name + "-" + collection_file.getName() + ".zip";
+ // String zip_file_path = collection_directory_path + zip_file_name;
+ String zip_file_path = Gatherer.getCollectDirectoryPath() + collection_name + "-" + collection_file.getName() + ".zip"; // collect/(colgroup/)coltail/colfile.zip
+ action_output = remote.downloadFile(download_collection_file_command, zip_file_path);
+
+ // Unzip the collection file just downloaded
+ UnzipTools.unzipFile(zip_file_path, collection_directory_path);
+ }
+ }
+
+ /**
+ * --------------------------------------------------------------------------------------------
+ * DOWNLOAD web.xml FILE --for a remote GS3
+ * --------------------------------------------------------------------------------------------
+ */
+ static class DownloadWebXMLFileAction
+ extends RemoteGreenstoneServerAction
+ {
+ public DownloadWebXMLFileAction()
+ {}
+
+ public void perform()
+ throws Exception
+ {
+ String web_xml_directory_path=(Configuration.gli_user_directory_path);
+ String download_web_xml_file_command = "cmd=download-web-xml-file";
+ download_web_xml_file_command += "&file=" + URLEncoder.encode("web.xml", "UTF-8");
+ download_web_xml_file_command += "&lr=" + remote.lang_region;
+ String zip_file_name = "web-xml.zip";
+ String zip_file_path = web_xml_directory_path + zip_file_name;
+ action_output = remote.downloadFile(download_web_xml_file_command, zip_file_path);
+
+ // Unzip the web.xml file just downloaded
+ UnzipTools.unzipFile(zip_file_path,web_xml_directory_path);
+ }
+ }
+
+ /**
+ * --------------------------------------------------------------------------------------------
+ * GET SCRIPT OPTIONS
+ * --------------------------------------------------------------------------------------------
+ */
+ static class GetScriptOptionsAction
+ extends RemoteGreenstoneServerAction
+ {
+ private String script_name;
+ private String script_arguments;
+
+ public GetScriptOptionsAction(String script_name, String script_arguments)
+ {
+ this.script_name = script_name;
+ this.script_arguments = script_arguments;
+ // classinfo.pl script has a "collection" argument. Convert any colgroup/coltail to colgroup|coltail:
+ // (Note that this may be the only method here that does not url encode the collection name before sendit it to the server)
+ this.script_arguments = script_arguments.replace(File.separatorChar, '|');
+ }
+
+ public void perform()
+ throws Exception
+ {
+ progress_bar.setAction("Getting options for " + script_name + "...");
+
+ String get_script_options_command = "cmd=get-script-options";
+ get_script_options_command += "&script=" + script_name;
+ get_script_options_command += "&xml=";
+ get_script_options_command += "&language=" + Configuration.getLanguage();
+ get_script_options_command += script_arguments;
+ action_output = remote.sendCommandToServer(get_script_options_command, null);
+ }
+ }
+
+ /**
+ * --------------------------------------------------------------------------------------------
+ * GET ALL NAMES OF SITES // for a remote GS3
+ * --------------------------------------------------------------------------------------------
+ */
+ static class GetSiteNamesAction
+ extends RemoteGreenstoneServerAction
+ {
+ public GetSiteNamesAction()
+ {}
+
+ public void perform()
+ throws Exception
+ {
+ progress_bar.setAction("Getting names of sites ...");
+
+ String get_script_options_command = "cmd=get-site-names";
+ action_output = remote.sendCommandToServer(get_script_options_command, null);
+ }
+ }
+
+ /**
+ * --------------------------------------------------------------------------------------------
+ * MOVE COLLECTION FILE
+ * --------------------------------------------------------------------------------------------
+ */
+ static class MoveCollectionFileAction
+ extends RemoteGreenstoneServerAction
+ {
+ private String collection_name;
+ private File source_collection_file;
+ private File target_collection_file;
+
+ public MoveCollectionFileAction(String collection_name, File source_collection_file, File target_collection_file)
+ {
+ this.collection_name = collection_name;
+ this.source_collection_file = source_collection_file;
+ this.target_collection_file = target_collection_file;
+ }
+
+ public void perform()
+ throws Exception
+ {
+ String collection_directory_path = CollectionManager.getCollectionDirectoryPath(collection_name);
+ String source_collection_file_relative_path = remote.getPathRelativeToDirectory(
+ source_collection_file, collection_directory_path);
+ source_collection_file_relative_path = source_collection_file_relative_path.replaceAll((Utility.isWindows() ? "\\\\" : "\\/"), "|");
+ String target_collection_file_relative_path = remote.getPathRelativeToDirectory(
+ target_collection_file, collection_directory_path);
+ target_collection_file_relative_path = target_collection_file_relative_path.replaceAll((Utility.isWindows() ? "\\\\" : "\\/"), "|");
+ progress_bar.setAction("Moving file " + source_collection_file_relative_path + " -> " + target_collection_file_relative_path + "...");
+
+ String move_collection_file_command = "cmd=move-collection-file";
+ move_collection_file_command += "&c=" + URLEncoder.encode(collection_name.replace(File.separatorChar, '|'), "UTF-8");
+ move_collection_file_command += "&source=" + Base64.encodeBytesInSingleLine(source_collection_file_relative_path.getBytes());
+ move_collection_file_command += "&target=" + Base64.encodeBytesInSingleLine(target_collection_file_relative_path.getBytes());
+ //move_collection_file_command += "&source=" + URLEncoder.encode(source_collection_file_relative_path, "UTF-8");
+ //move_collection_file_command += "&target=" + URLEncoder.encode(target_collection_file_relative_path, "UTF-8");
+ action_output = remote.sendCommandToServer(move_collection_file_command, null);
+ }
+ }
+
+
+ /**
+ * --------------------------------------------------------------------------------------------
+ * NEW COLLECTION DIRECTORY
+ * --------------------------------------------------------------------------------------------
+ */
+ static class NewCollectionDirectoryAction
+ extends RemoteGreenstoneServerAction
+ {
+ private String collection_name;
+ private File new_collection_directory;
+
+ public NewCollectionDirectoryAction(String collection_name, File new_collection_directory)
+ {
+ this.collection_name = collection_name;
+ this.new_collection_directory = new_collection_directory;
+ }
+
+ public void perform()
+ throws Exception
+ {
+ String collection_directory_path = CollectionManager.getCollectionDirectoryPath(collection_name);
+ String new_collection_directory_relative_path = remote.getPathRelativeToDirectory(
+ new_collection_directory, collection_directory_path);
+ new_collection_directory_relative_path = new_collection_directory_relative_path.replaceAll((Utility.isWindows() ? "\\\\" : "\\/"), "|");
+ progress_bar.setAction("Creating new directory " + new_collection_directory_relative_path + "...");
+
+ String new_collection_directory_command = "cmd=new-collection-directory";
+ new_collection_directory_command += "&c=" + URLEncoder.encode(collection_name.replace(File.separatorChar, '|'), "UTF-8");
+ new_collection_directory_command += "&directory=" + URLEncoder.encode(new_collection_directory_relative_path, "UTF-8");
+ action_output = remote.sendCommandToServer(new_collection_directory_command, null);
+ }
+ }
+
+
+ /**
+ * --------------------------------------------------------------------------------------------
+ * RUN SCRIPT
+ * --------------------------------------------------------------------------------------------
+ */
+ static class RunScriptAction
+ extends RemoteGreenstoneServerAction
+ {
+ private String collection_name;
+ private String script_name;
+ private String script_arguments;
+ private GShell shell;
+
+ public RunScriptAction(String collection_name, String script_name, String script_arguments, GShell shell)
+ {
+ this.collection_name = collection_name;
+ this.script_name = script_name;
+ this.script_arguments = script_arguments;
+ this.shell = shell;
+ }
+
+ public void perform()
+ throws Exception
+ {
+ progress_bar.setAction("Running " + script_name + "...");
+
+ String run_script_command = "cmd=run-script";
+ run_script_command += "&c=" + URLEncoder.encode(collection_name.replace(File.separatorChar, '|'), "UTF-8");
+ run_script_command += "&script=" + script_name;
+ run_script_command += "&language=" + Configuration.getLanguage();
+ run_script_command += script_arguments;
+ action_output = remote.sendCommandToServer(run_script_command, shell);
+ }
+ }
+
+
+ /**
+ * --------------------------------------------------------------------------------------------
+ * UPLOAD COLLECTION FILE
+ * --------------------------------------------------------------------------------------------
+ */
+ static class UploadCollectionFilesAction
+ extends RemoteGreenstoneServerAction
+ {
+ private String collection_name;
+ private File[] collection_files;
+
+
+ public UploadCollectionFilesAction(String collection_name, File[] collection_files)
+ {
+ this.collection_name = collection_name;
+ this.collection_files = collection_files;
+ }
+
+
+ public void perform()
+ throws Exception
+ {
+ progress_bar.setAction("Uploading collection files...");
+
+ // Determine the file paths relative to the collection directory
+ String collection_directory_path = CollectionManager.getCollectionDirectoryPath(collection_name);
+ String[] collection_file_relative_paths = new String[collection_files.length];
+ for (int i = 0; i < collection_files.length; i++) {
+ collection_file_relative_paths[i] = remote.getPathRelativeToDirectory(collection_files[i], collection_directory_path);
+ }
+
+ // Zip up the files to send to the server
+ //String zip_file_name = collection_name + "-" + System.currentTimeMillis() + ".zip";
+ //String zip_file_path = collection_directory_path + zip_file_name;
+ String zip_file_path = collection_directory_path; // collect/(colgroup/)coltail/
+ String zip_file_name = new File(zip_file_path).getName() + "-" + System.currentTimeMillis() + ".zip"; // -.zip
+ zip_file_path += zip_file_name; // collect/(colgroup/)coltail/coltail-time.zip
+ ZipTools.zipFiles(zip_file_path, collection_directory_path, collection_file_relative_paths);
+
+ // Upload the zip file
+ String upload_collection_file_command = "cmd=upload-collection-file";
+ upload_collection_file_command += "&c=" + URLEncoder.encode(collection_name.replace(File.separatorChar, '|'), "UTF-8");
+ upload_collection_file_command += "&file=" + URLEncoder.encode(zip_file_name, "UTF-8");
+ upload_collection_file_command += "&directory=";
+ upload_collection_file_command += "&zip=true";
+ upload_collection_file_command += "&lr=" + remote.lang_region;
+ action_output = remote.uploadFile(upload_collection_file_command, zip_file_path);
+ }
+ }
+
+
+ /**
+ * --------------------------------------------------------------------------------------------
+ * UPLOAD FILES INTO COLLECTION
+ * --------------------------------------------------------------------------------------------
+ */
+ static class UploadFilesIntoCollectionAction
+ extends RemoteGreenstoneServerAction
+ {
+ private String collection_name;
+ private File[] source_files;
+ private File target_collection_directory;
+
+
+ public UploadFilesIntoCollectionAction(String collection_name, File[] source_files, File target_collection_directory)
+ {
+ this.collection_name = collection_name;
+ this.source_files = source_files;
+ this.target_collection_directory = target_collection_directory;
+ }
+
+
+ public void perform()
+ throws Exception
+ {
+ String collection_directory_path = CollectionManager.getCollectionDirectoryPath(collection_name);
+ String target_collection_directory_relative_path = remote.getPathRelativeToDirectory(
+ target_collection_directory, collection_directory_path);
+ target_collection_directory_relative_path = target_collection_directory_relative_path.replaceAll((Utility.isWindows() ? "\\\\" : "\\/"), "|");
+ progress_bar.setAction("Uploading files into collection...");
+
+ //String zip_file_name = collection_name + "-" + System.currentTimeMillis() + ".zip";
+ //String zip_file_path = Gatherer.getCollectDirectoryPath() + zip_file_name;
+ String zip_file_path = Gatherer.getCollectDirectoryPath()
+ + collection_name + "-" + System.currentTimeMillis() + ".zip"; // "collect/(colgroup/)collection_tail_name-.zip"
+ String zip_file_name = new File(zip_file_path).getName(); // "collection_tail_name-.zip"
+ DebugStream.println("Zip file path: " + zip_file_path);
+
+ String base_directory_path = source_files[0].getParentFile().getAbsolutePath();
+ DebugStream.println("Base directory path: " + base_directory_path);
+ String[] source_file_relative_paths = new String[source_files.length];
+ for (int i = 0; i < source_files.length; i++) {
+ DebugStream.println("Source file path: " + source_files[i]);
+ source_file_relative_paths[i] = remote.getPathRelativeToDirectory(source_files[i], base_directory_path);
+ }
+
+ ZipTools.zipFiles(zip_file_path, base_directory_path, source_file_relative_paths);
+
+ String upload_collection_file_command = "cmd=upload-collection-file";
+ upload_collection_file_command += "&c=" + URLEncoder.encode(collection_name.replace(File.separatorChar, '|'), "UTF-8");
+ upload_collection_file_command += "&file=" + URLEncoder.encode(zip_file_name, "UTF-8");
+ upload_collection_file_command += "&directory=" + URLEncoder.encode(target_collection_directory_relative_path, "UTF-8");
+ upload_collection_file_command += "&zip=true";
+ upload_collection_file_command += "&lr=" + remote.lang_region;
+ action_output = remote.uploadFile(upload_collection_file_command, zip_file_path);
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/Unzip.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/Unzip.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/Unzip.java (revision 31635)
@@ -0,0 +1,119 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: David Bainbridge, NZDL Project, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 2005 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+
+package org.greenstone.gatherer.remote;
+
+import java.io.*;
+import java.util.*;
+import java.util.zip.*;
+
+
+public class Unzip
+{
+
+ public static final void copyInputStream(InputStream in, OutputStream out)
+ throws IOException
+ {
+ byte[] buffer = new byte[1024];
+ int len;
+
+ while((len = in.read(buffer)) >= 0)
+ out.write(buffer, 0, len);
+
+ in.close();
+ out.close();
+ }
+
+
+ static public void main(String[] args)
+ {
+ if (args.length != 2) {
+ System.err.println("Usage: Unzip ");
+ return;
+ }
+
+ String zip_file_path = args[0];
+ // System.err.println("Zip file path: " + zip_file_path);
+ String destination_directory_path = args[1];
+ if (!destination_directory_path.endsWith(File.separator)) {
+ destination_directory_path += File.separator;
+ }
+ // System.err.println("Destination directory path: " + destination_directory_path);
+
+ try {
+ ZipFile zipFile = new ZipFile(zip_file_path);
+
+ Enumeration entries = zipFile.entries();
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = (ZipEntry) entries.nextElement();
+
+ if (entry.isDirectory()) {
+ // Assume directories are stored parents first then children.
+ // System.err.println("Extracting directory: " + entry.getName());
+ // This is not robust, just for demonstration purposes.
+ String dir_name = entry.getName();
+ String full_dir_name = destination_directory_path + dir_name;
+ // System.err.println("Full directory name: " + full_dir_name);
+ File dirFile = new File(full_dir_name);
+ dirFile.mkdir();
+ }
+ else {
+ // System.err.println("Extracting file: " + entry.getName());
+ String file_name = entry.getName();
+ String full_file_name = destination_directory_path + file_name;
+ File directory = (new File(full_file_name)).getParentFile();
+ // System.err.println("Full file name: " + full_file_name);
+ if (!directory.exists() && !directory.mkdirs()) {
+ System.err.println("Error: Could not create directory " + directory + "!");
+ }
+
+ FileOutputStream fos = new FileOutputStream(full_file_name);
+ BufferedOutputStream bos = new BufferedOutputStream(fos);
+
+ copyInputStream(zipFile.getInputStream(entry),bos);
+ }
+ }
+
+ zipFile.close();
+ }
+ catch (Exception exception) {
+ System.out.println("ERROR: " + exception);
+ exception.printStackTrace();
+ System.exit(1);
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/ZipCollectionArchives.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/ZipCollectionArchives.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/ZipCollectionArchives.java (revision 31635)
@@ -0,0 +1,117 @@
+package org.greenstone.gatherer.remote;
+
+import java.io.*;
+import java.util.zip.*;
+
+
+/**
+ */
+public class ZipCollectionArchives
+{
+ static public void main(String[] args)
+ {
+ if (args.length != 3) {
+ System.err.println("Usage: ZipCollectionArchives ");
+ return;
+ }
+
+ String zip_file_path = args[0];
+ String collect_directory_path = args[1];
+ String collection_name = args[2];
+
+ if (!collect_directory_path.endsWith(File.separator)) {
+ collect_directory_path += File.separator;
+ }
+
+ try {
+ ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zip_file_path));
+
+ // Add archives directory, with doc.xml files only
+ String archives_relative_path = collection_name + File.separator + "archives";
+ ZipTools.ZipFilter collection_archives_zip_filter = new CollectionArchivesZipFilter();
+ ZipTools.addFileToZip(zos, collect_directory_path, archives_relative_path, collection_archives_zip_filter);
+
+ zos.close();
+ }
+ catch (Exception exception) {
+ exception.printStackTrace();
+ }
+ }
+
+
+ static public class CollectionArchivesZipFilter
+ extends ZipTools.NullZipFilter
+ {
+ private byte[] bytes_remaining = new byte[0];
+ private int section_depth = 0;
+ private boolean in_section_content = false;
+
+
+ public boolean shouldIncludeFile(String relative_file_path)
+ {
+ // Only doc.xml files are strictly required, but we include archives.inf as well to prevent
+ // errors when zipping up the archives of a collection where no files were imported
+ // (in this case the zip file would contain nothing at all)
+ return (relative_file_path.equals("archives.inf") || relative_file_path.endsWith(File.separator + "archives.inf") || relative_file_path.equals("doc.xml") || relative_file_path.endsWith(File.separator + "doc.xml"));
+ }
+
+
+ public boolean shouldIncludeFileContent(String relative_file_path)
+ {
+ // Only content for doc.xml files is included
+ return (relative_file_path.equals("doc.xml") || relative_file_path.endsWith(File.separator + "doc.xml"));
+ }
+
+
+ public void filterFileContent(String relative_file_path, BufferedInputStream bis, ZipOutputStream zos)
+ {
+ // Reset the status in case there were errors in previous doc.xml files
+ section_depth = 0;
+ in_section_content = false;
+
+ // Filter out the ... of the doc.xml files
+ try {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(bis, "UTF-8"));
+ String line = null;
+ while ((line = reader.readLine()) != null) {
+ // If this line isn't filtered, write it out to zos
+ if (!filterFileLine(line)) {
+ line += "\n";
+ byte[] bytes = line.getBytes("UTF-8");
+ zos.write(bytes, 0, bytes.length);
+ }
+ }
+ }
+ catch (Exception exception) {
+ exception.printStackTrace();
+ }
+ }
+
+
+ private boolean filterFileLine(String line)
+ {
+ boolean filter_line = false;
+
+ if (line.indexOf("") != -1) {
+ section_depth++;
+ }
+ if (line.indexOf("") != -1) {
+ in_section_content = true;
+ }
+
+ // If we're in a subsection or in a content element, filter this line
+ if (section_depth > 1 || in_section_content) {
+ filter_line = true;
+ }
+
+ if (line.indexOf(" ") != -1) {
+ in_section_content = false;
+ }
+ if (line.indexOf(" ") != -1) {
+ section_depth--;
+ }
+
+ return filter_line;
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/ZipCollectionConfigurations.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/ZipCollectionConfigurations.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/ZipCollectionConfigurations.java (revision 31635)
@@ -0,0 +1,155 @@
+package org.greenstone.gatherer.remote;
+
+import java.io.*;
+import java.util.zip.*;
+
+
+/**
+ */
+public class ZipCollectionConfigurations
+{
+ static public void main(String[] args)
+ {
+ if (args.length != 4) {
+ System.err.println("Usage: ZipCollectionConfigurations ");
+ return;
+ }
+
+ String zip_file_path = args[0];
+ String collect_directory_path = args[1];
+ String user_name = args[2];
+ String user_groups = args[3];
+
+ if (!collect_directory_path.endsWith(File.separator)) {
+ collect_directory_path += File.separator;
+ }
+ File collect_directory = new File(collect_directory_path);
+
+ try {
+ ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zip_file_path));
+ ZipTools.ZipFilter collection_etc_zip_filter = new CollectionEtcZipFilter();
+ ZipTools.ZipFilter collection_metadata_zip_filter = new CollectionMetadataZipFilter();
+
+ addUserAccessibleCollections(collect_directory_path, "", collect_directory, zos,
+ collection_etc_zip_filter, collection_metadata_zip_filter,
+ user_name, user_groups);
+
+
+
+ zos.putNextEntry(new ZipEntry(".gli"));
+ zos.close();
+ }
+ catch (Exception exception) {
+ exception.printStackTrace();
+ }
+ }
+
+ static private void addUserAccessibleCollections(String toplevel_path, // remains unchanged
+ String relative_path, // grows with each level of recursion
+ File collect_directory, // current collection (group) directory being considered
+ ZipOutputStream zos,
+ ZipTools.ZipFilter collection_etc_zip_filter,
+ ZipTools.ZipFilter collection_metadata_zip_filter,
+ String user_name,
+ String user_groups)
+ {
+
+ if(!relative_path.equals("")) {
+ relative_path += File.separator;
+ }
+
+ // Add the etc and metadata directories from each of the collections the user has access to
+ File[] collection_directories = collect_directory.listFiles();
+ for (int i = 0; i < collection_directories.length; i++) {
+ if (collection_directories[i].isDirectory()) {
+ String collection_name = collection_directories[i].getName();
+ if (checkAccess(collection_name, user_name, user_groups)) {
+
+ String etc_relative_path = relative_path + collection_directories[i].getName() + File.separator + "etc";
+ ZipTools.addFileToZip(zos, toplevel_path, etc_relative_path, collection_etc_zip_filter);
+
+ // The "metadata" directory may not exist (non-GLI collections)
+ String metadata_relative_path = relative_path + collection_directories[i].getName() + File.separator + "metadata";
+ File metadata_directory = new File(toplevel_path + File.separator + metadata_relative_path);
+ if (metadata_directory.exists()) {
+ ZipTools.addFileToZip(zos, toplevel_path, metadata_relative_path, collection_metadata_zip_filter);
+ }
+ else { // if a collection contains no metadata directory, then check if it is a collectgroup
+
+ // read from collection config file in etc folder
+ File config_file = new File(collection_directories[i], "etc"+File.separator+"collect.cfg");
+ if(config_file.exists()) {
+ try {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(config_file)));
+ String line = null;
+ while((line = reader.readLine()) != null) { // read lines and look for: collectgroup true
+ line = line.trim(); // need to ignore surrounding whitespace
+ if(line.startsWith("collectgroup") && line.endsWith("true")) {
+ // if collectgroup, then recurse on current folder
+ addUserAccessibleCollections(toplevel_path, relative_path+collection_directories[i].getName(), collection_directories[i],
+ zos, collection_etc_zip_filter, collection_metadata_zip_filter,
+ user_name, user_groups);
+ }
+ }
+
+ reader.close();
+ } catch(Exception e) {
+ System.err.println("Exception reading from file " + config_file + ": " + e);
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ static private boolean checkAccess(String collection_name, String user_name, String user_groups)
+ {
+ String[] user_groups_array = user_groups.split(",");
+ for (int i = 0; i < user_groups_array.length; i++) {
+ if (user_groups_array[i].equals("all-collections-editor")) {
+ return true;
+ }
+ if (user_groups_array[i].equals("personal-collections-editor") && collection_name.startsWith(user_name + "-")) {
+ return true;
+ }
+ if (user_groups_array[i].equals(collection_name + "-collection-editor")) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ static private class CollectionEtcZipFilter
+ extends ZipTools.NullZipFilter
+ {
+ public boolean shouldIncludeFile(String relative_file_path)
+ {
+ // Only collect.cfg files are included
+ if (relative_file_path.endsWith("collect.cfg")){
+ return (relative_file_path.equals("collect.cfg") || relative_file_path.endsWith(File.separator + "collect.cfg"));
+ }
+
+ // Only collectConfig.xml files are included
+ if (relative_file_path.endsWith("collectionConfig.xml")){
+ return (relative_file_path.equals("collectionConfig.xml") || relative_file_path.endsWith(File.separator + "collectionConfig.xml"));
+ }
+ return false;
+ }
+ }
+
+
+ static private class CollectionMetadataZipFilter
+ extends ZipTools.NullZipFilter
+ {
+ public boolean shouldIncludeFileContent(String relative_file_path)
+ {
+ // We don't include content for any of the metadata set files
+ return false;
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/ZipCollectionShell.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/ZipCollectionShell.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/ZipCollectionShell.java (revision 31635)
@@ -0,0 +1,86 @@
+package org.greenstone.gatherer.remote;
+
+import java.io.*;
+import java.util.zip.*;
+
+
+/**
+ */
+public class ZipCollectionShell
+{
+ static public void main(String[] args)
+ {
+ if ((args.length < 3) || (args.length > 4)) {
+ System.err.println("Usage: ZipCollectionShell [gsdl3]");
+ return;
+ }
+
+ String zip_file_path = args[0];
+ String collect_directory_path = args[1];
+ String collection_name = args[2];
+ String run_gsdl3 ="";
+ if (args.length == 4){
+ run_gsdl3 = args[3];
+ }
+ if (!collect_directory_path.endsWith(File.separator)) {
+ collect_directory_path += File.separator;
+ }
+
+ try {
+ ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zip_file_path));
+ ZipTools.ZipFilter null_zip_filter = new ZipTools.NullZipFilter();
+
+ // Add archives directory, with doc.xml files only
+ String archives_relative_path = collection_name + File.separator + "archives";
+ ZipTools.ZipFilter collection_archives_zip_filter = new ZipCollectionArchives.CollectionArchivesZipFilter();
+ ZipTools.addFileToZip(zos, collect_directory_path, archives_relative_path, collection_archives_zip_filter);
+
+ // Add /etc/collect.cfg file for 2 GS2
+ if (run_gsdl3.length()==0){
+ String collect_cfg_relative_path = collection_name + File.separator + "etc" + File.separator + "collect.cfg";
+ ZipTools.addFileToZip(zos, collect_directory_path, collect_cfg_relative_path, null_zip_filter);
+ }
+ // Add /etc/collectConfig.xml file for a GS3
+ if (run_gsdl3.equals("gsdl3")){
+ String collectionConfig_xml_relative_path = collection_name + File.separator + "etc" + File.separator + "collectionConfig.xml";
+ ZipTools.addFileToZip(zos, collect_directory_path, collectionConfig_xml_relative_path, null_zip_filter);
+ }
+ // Add images directory
+ String images_relative_path = collection_name + File.separator + "images";
+ ZipTools.addFileToZip(zos, collect_directory_path, images_relative_path, null_zip_filter);
+
+ // Add import directory, with zero-length files except for metadata.xml
+ String import_relative_path = collection_name + File.separator + "import";
+ ZipTools.ZipFilter collection_import_zip_filter = new CollectionImportZipFilter();
+ ZipTools.addFileToZip(zos, collect_directory_path, import_relative_path, collection_import_zip_filter);
+
+ // Add index/build.cfg file
+ String build_cfg_relative_path = collection_name + File.separator + "index" + File.separator + "build.cfg";
+ ZipTools.addFileToZip(zos, collect_directory_path, build_cfg_relative_path, null_zip_filter);
+
+ // Add macros/extra.dm file
+ String extra_dm_relative_path = collection_name + File.separator + "macros" + File.separator + "extra.dm";
+ ZipTools.addFileToZip(zos, collect_directory_path, extra_dm_relative_path, null_zip_filter);
+
+ // Add metadata directory
+ String metadata_relative_path = collection_name + File.separator + "metadata";
+ ZipTools.addFileToZip(zos, collect_directory_path, metadata_relative_path, null_zip_filter);
+
+ zos.close();
+ }
+ catch (Exception exception) {
+ exception.printStackTrace();
+ }
+ }
+
+
+ static private class CollectionImportZipFilter
+ extends ZipTools.NullZipFilter
+ {
+ public boolean shouldIncludeFileContent(String relative_file_path)
+ {
+ // We only include file content for metadata.xml files
+ return (relative_file_path.equals("metadata.xml") || relative_file_path.endsWith(File.separator + "metadata.xml"));
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/ZipFiles.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/ZipFiles.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/ZipFiles.java (revision 31635)
@@ -0,0 +1,28 @@
+package org.greenstone.gatherer.remote;
+
+import java.io.File;
+import java.util.ArrayList;
+
+
+/**
+ */
+public class ZipFiles
+{
+ static public void main(String[] args)
+ {
+ if (args.length < 3) {
+ System.err.println("Usage: ZipFiles ( ...)");
+ return;
+ }
+
+ String zip_file_path = args[0];
+ String base_directory_path = args[1];
+ ArrayList relative_file_paths_list = new ArrayList();
+ for (int i = 2; i < args.length; i++) {
+ relative_file_paths_list.add(args[i]);
+ }
+
+ String[] relative_file_paths = (String[]) relative_file_paths_list.toArray(new String[0]);
+ ZipTools.zipFiles(zip_file_path, base_directory_path, relative_file_paths);
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/ZipTools.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/ZipTools.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/remote/ZipTools.java (revision 31635)
@@ -0,0 +1,151 @@
+/**
+ *############################################################################
+ * A component of the Greenstone Librarian Interface, part of the Greenstone
+ * digital library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: David Bainbridge, NZDL Project, University of Waikato, NZ
+ *
+ * Copyright (C) 2005 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################################################
+ */
+
+package org.greenstone.gatherer.remote;
+
+
+import java.io.*;
+import java.util.zip.*;
+
+
+public class ZipTools
+{
+ static public void zipFiles(String zip_file_path, String base_directory_path, String[] relative_file_paths)
+ {
+ try {
+ ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zip_file_path));
+ ZipFilter null_zip_filter = new NullZipFilter();
+
+ // Add each file/directory in turn to the zip file
+ for (int i = 0; i < relative_file_paths.length; i++) {
+ String relative_file_path = relative_file_paths[i];
+ addFileToZip(zos, base_directory_path, relative_file_path, null_zip_filter);
+ }
+
+ zos.close();
+ }
+ catch (Exception exception) {
+ exception.printStackTrace();
+ }
+ }
+
+
+ static public void addFileToZip(ZipOutputStream zos, String base_directory_path, String relative_file_path, ZipFilter zip_filter)
+ {
+ File file = new File(base_directory_path + File.separator + relative_file_path);
+
+ // Check that the file/directory exists
+ if (!file.exists()) {
+ System.err.println("File " + file + " does not exist!");
+ return;
+ }
+
+ try {
+ // Directory case
+ if (file.isDirectory()) {
+ // Add a zip entry for this directory
+ // Actually, don't -- this messes things up completely on Windows and doesn't seem to matter on Linux
+ // zos.putNextEntry(new ZipEntry(relative_file_path + File.separator));
+
+ // Apply recursively to each of the children of the directory
+ File[] child_files = file.listFiles();
+ for (int i = 0; i < child_files.length; i++) {
+ addFileToZip(zos, base_directory_path, relative_file_path + File.separator + child_files[i].getName(), zip_filter);
+ }
+
+ // empty directory. Java zips don't allow empty dirs, so need to put some dummy file in there
+ if(child_files.length == 0) {
+ String rel_path = relative_file_path.replace('\\', '/') + File.separator + "empty";
+ System.err.println("Zipping empty dir requires dummy file: " + rel_path);
+ zos.putNextEntry(new ZipEntry(rel_path));
+ }
+ }
+
+ // File case
+ else {
+ // Add a zip entry for this file
+ if (zip_filter.shouldIncludeFile(relative_file_path)) {
+ // Always use Unix style paths in zip files, even on Windows
+ zos.putNextEntry(new ZipEntry(relative_file_path.replace('\\', '/')));
+
+ if (zip_filter.shouldIncludeFileContent(relative_file_path)) {
+ BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
+ zip_filter.filterFileContent(relative_file_path, bis, zos);
+ bis.close();
+ }
+ }
+ }
+ }
+ catch (Exception exception) {
+ exception.printStackTrace();
+ }
+ }
+
+
+ public interface ZipFilter
+ {
+ public boolean shouldIncludeFile(String relative_file_path);
+
+ public boolean shouldIncludeFileContent(String relative_file_path);
+
+ public void filterFileContent(String relative_file_path, BufferedInputStream bis, ZipOutputStream zos);
+ }
+
+
+ static public class NullZipFilter
+ implements ZipFilter
+ {
+ public boolean shouldIncludeFile(String relative_file_path)
+ {
+ // All files are included
+ return true;
+ }
+
+
+ public boolean shouldIncludeFileContent(String relative_file_path)
+ {
+ // Content for all files is included
+ return true;
+ }
+
+
+ public void filterFileContent(String relative_file_path, BufferedInputStream bis, ZipOutputStream zos)
+ {
+ // No filtering: just read the file and write it directly out to zip
+ try {
+ byte[] data = new byte[1024];
+ int bytes_read;
+ while ((bytes_read = bis.read(data, 0, 1024)) > -1) {
+ zos.write(data, 0, bytes_read);
+ }
+ }
+ catch (Exception exception) {
+ // We can't use DebugStream here
+ exception.printStackTrace();
+ }
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GBuildProgressMonitor.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GBuildProgressMonitor.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GBuildProgressMonitor.java (revision 31635)
@@ -0,0 +1,598 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.shell;
+
+import java.awt.Component;
+import java.util.ArrayList;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.cdm.CollectionDesignManager;
+import org.greenstone.gatherer.gui.GProgressBar;
+import org.greenstone.gatherer.util.StaticStrings;
+/** This implementation of GShellProgressMonitor is designed to parse and translate the progress of a buildcol.pl call.
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.3
+ */
+public class GBuildProgressMonitor
+ implements GShellProgressMonitor {
+ /** The minimum value = 0 */
+ static final private int MIN = 0;
+ /** In order to get a smoothish progress bar, set the maximum to a large number = 1000000 */
+ static final private int MAX = 1000000;
+ /** The various states the build progress state machine can be in, starting at the base state. */
+ static final int BASE = -1;
+ static final int BUILD = 0;
+ static final int COMPRESSTEXT = 10;
+ static final int COMPRESSTEXT_COLLECTTEXTSTATS = 11;
+ static final int COMPRESSTEXT_CREATINGCOMPRESS = 12;
+ static final int COMPRESSTEXT_COMPRESSINGTEXT = 13;
+ static final int INDEX = 20;
+ static final int INDEX_CREATINGINDEXDIC = 21;
+ static final int INDEX_INVERTINGTEXT = 22;
+ static final int INDEX_CREATETHEWEIGHTS = 23;
+ static final int INDEX_CREATESTEMMEDDIC = 24;
+ static final int INDEX_CREATINGSTEMINDX = 25;
+ static final int CREATEINFODATA = 30;
+ static final int PHIND = 40;
+ static final int PHIND_EXTRACTINGVOCAB = 41;
+ static final int PHIND_EXTRACTINGPHRASE = 42;
+ static final int PHIND_SORTANDRENUMBER = 43;
+ static final int PHIND_PHRASEDATABASES = 44;
+ static final int PHIND_WORDLEVELINDEXES = 45;
+ static final int PHIND_DOCINFODATABASES = 46;
+ static final int CREATINGAUXILARY = 50;
+ /** Now the language independant sentinel strings. */
+ static final String BADARGUMENT_ELEMENT = "BadArgument";
+ static final String BADARGUMENTVALUE_ELEMENT = "BadArgumentValue";
+ static final String BADCLASSIFIER_ELEMENT = "BadClassifier";
+ static final String BADPLUGIN_ELEMENT = "BadPlugin";
+ static final String BUILD_ELEMENT = "Build";
+ static final String COLLECTTEXTSTATS_VALUE = "CollectTextStats";
+ static final String COMPRESSINGTEXT_VALUE = "CompressingText";
+ static final String COMPRESSTEXT_VALUE = "CompressText";
+ static final String CREATINGAUXILARY_VALUE = "CreatingAuxilary";
+ static final String CREATINGCOMPRESS_VALUE = "CreatingCompress";
+ static final String CREATINGINDEXDIC_VALUE = "CreatingIndexDic";
+ static final String CREATINGSTEMINDX_VALUE = "CreatingStemIndx";
+ static final String CREATEINFODATA_VALUE = "CreateInfoData";
+ static final String CREATESTEMMEDDIC_VALUE = "CreateStemmedDic";
+ static final String CREATETHEWEIGHTS_VALUE = "CreateTheWeights";
+ static final String DOCINFODATABASES_VALUE = "DocInfoDatabases";
+ static final String EXTRACTINGPHRASE_VALUE = "ExtractingPhrase";
+ static final String EXTRACTINGVOCAB_VALUE = "ExtractingVocab";
+ static final String FATALERROR_ELEMENT = "FatalError";
+ static final String INDEX_VALUE = "Index";
+ static final String INVERTINGTEXT_VALUE = "InvertingText";
+ static final String NAME_ATTRIBUTE = "name";
+ static final String PHASE_ELEMENT = "Phase";
+ static final String PHIND_VALUE = "Phind";
+ static final String PHRASEDATABASES_VALUE = "PhraseDatabases";
+ static final String SKIPCREATINGCOMP_VALUE = "SkipCreatingComp";
+ static final String SORTANDRENUMBER_VALUE = "SortAndRenumber";
+ static final String SOURCE_ATTRIBUTE = "source";
+ static final String STAGE_ELEMENT = "Stage";
+ static final String WARNING_ELEMENT = "Warning";
+ static final String WORDLEVELINDEXES_VALUE = "WordLevelIndexes";
+
+ static final private String ARGUMENT_ATTRIBUTE = "a";
+ static final private String CLASSIFIER_ATTRIBUTE = "c";
+ static final private String PLUGIN_ATTRIBUTE = "p";
+
+ /** Indicates if the GUI has asked the process this object monitors to stop. */
+ private boolean stop = false;
+ /** The current number of stages we have completed. */
+ private int current_stages = 0;
+ /** The number of stages we are expecting to complete. */
+ private int expected_stages = 0;
+
+ private int threshold = Configuration.LIBRARIAN_MODE;
+ /** The current state we are in. The state will change depending on the next flag recieved. */
+ private int state = BASE;
+ /** The progress bar this monitor updates. */
+ private GProgressBar progress_bar;
+ /** A progress bar that is shared between this this listener and the Import monitor. */
+ private GProgressBar shared_progress_bar;
+
+ /** Construct a new GBuildProgressMonitor. */
+ public GBuildProgressMonitor(GProgressBar shared_progress_bar) {
+ this.shared_progress_bar = shared_progress_bar;
+ progress_bar = new GProgressBar();
+ progress_bar.setMaximum(MAX);
+ progress_bar.setMinimum(MIN);
+ progress_bar.setString(null);
+ progress_bar.setStringPainted(true);
+ setValue(MIN);
+ }
+
+ /** Method to register a new progress bar with this monitor.
+ * @param progress_bar The new GProgressBar .
+ */
+ public void addProgressBar(GProgressBar progress_bar) {
+ this.progress_bar = progress_bar;
+ progress_bar.setMaximum(MAX);
+ progress_bar.setMinimum(MIN);
+ progress_bar.setStringPainted(false);
+ progress_bar.setString(null);
+ setValue(MIN);
+ }
+
+ /** Determine the script exit value according to the progress monitor. This gets around a problem where several script failures actually result with a successful exit value.
+ * @return A int with a value of zero if and only if the script was successful.
+ */
+ public int exitValue() {
+ if(state == BASE) {
+ return 0;
+ }
+ return 1;
+ }
+ /** Method to retrieve whatever control is being used as the progress indicator. Usually a GProgressBar but there may be others implemented later.
+ * @return A Component on which the progress of the process is being displayed.
+ */
+ public Component getProgress() {
+ return progress_bar;
+ }
+
+ public GProgressBar getSharedProgress() {
+ return shared_progress_bar;
+ }
+
+ public void messageOnProgressBar(String message)
+ {
+ if (message!=null && !message.equals("")) {
+ progress_bar.setString(message);
+ shared_progress_bar.setString(message);
+ }
+ else {
+ progress_bar.setString(null);
+ shared_progress_bar.setString(null);
+ }
+ }
+
+ /** Method to determine the state of the stop flag, which may be set by the visual component that created this monitor.
+ * @return A boolean indicating if the process has been asked to stop.
+ */
+ public synchronized boolean hasSignalledStop() {
+ return stop;
+ }
+ /** Inform the progress bar that it should programatically increment progress by one step. */
+ public void increment() {
+ // There is no reason this should be called for a build.
+ }
+
+ /** This method is used to 'feed in' a line of text captured from the process.
+ * @param queue a queue which at the moment should contain a single GShellEvent
+ */
+ public void process(ArrayList queue) {
+ GShellEvent event = (GShellEvent) queue.get(0);
+ // Remove 'build.pl> ' bit
+ String line = event.getMessage();
+ line = line.substring(line.indexOf(StaticStrings.GREATER_THAN_CHARACTER) + 1);
+ line = line.trim();
+ // We are only really interested in processing pseudo-XML tags
+ if(line.startsWith(StaticStrings.LESS_THAN_CHARACTER) && line.endsWith(StaticStrings.GREATER_THAN_CHARACTER)) {
+ // All events are vetoed
+ event.veto();
+
+ // Create a new element from it
+ GShellElement element = new GShellElement(line);
+ // Now change states within the state machine as appropriate. A state change may subsequently incur a progress update.
+ String name = null;
+ String value = null;
+ switch(state) {
+ case BASE:
+ // The only thing we would expect is a build message
+ name = element.getElementName();
+ if(name.equals(BUILD_ELEMENT)) {
+ progress_bar.setIndeterminate(false);
+ state = BUILD;
+ // Produce a lower detail message if required
+ if(Configuration.getMode() <= threshold && !stop) {
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Build.BuildBegun1"), event.getStatus()));
+ }
+ }
+ else {
+ // Unknown command, go indeterminate
+ DebugStream.println("Unknown name: " + name);
+ progress_bar.setIndeterminate(true);
+ }
+ name = null;
+ break;
+ case BUILD:
+ // The only thing we would expect is a stage element or a
+ // bad classifier arg name
+ name = element.getElementName();
+
+ if(name.equals(STAGE_ELEMENT)) {
+ progress_bar.setIndeterminate(false);
+ // Now we determine what stage we are moving to
+ value = element.getAttribute(NAME_ATTRIBUTE);
+ if(value.equals(COMPRESSTEXT_VALUE)) {
+ state = COMPRESSTEXT;
+ if(Configuration.getMode() <= threshold) {
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Build.CompressText"), event.getStatus()));
+ }
+ }
+ else if(value.equals(INDEX_VALUE)) {
+ state = INDEX;
+ if(Configuration.getMode() <= threshold) {
+ String args[] = new String[1];
+ args[0] = element.getAttribute(SOURCE_ATTRIBUTE);
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Build.Index", args), event.getStatus()));
+ args = null;
+ }
+ }
+ else if(value.equals(CREATEINFODATA_VALUE)) {
+ state = CREATEINFODATA;
+ if(Configuration.getMode() <= threshold) {
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Build.InfoDatabase"), event.getStatus()));
+ }
+ }
+ else if(value.equals(PHIND_VALUE)) {
+ state = PHIND;
+ if(Configuration.getMode() <= threshold) {
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Build.Phind"), event.getStatus()));
+ }
+ }
+ else if(value.equals(CREATINGAUXILARY_VALUE)) {
+ state = CREATINGAUXILARY;
+ if(Configuration.getMode() <= threshold) {
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Build.Auxilary"), event.getStatus()));
+ }
+ }
+ else {
+ // Unknown command, go indeterminate
+ DebugStream.println("Unknown value: " + value);
+ progress_bar.setIndeterminate(true);
+ }
+ value = null;
+ }
+ // The build end message indicates a successfulish build.
+ else if(name.equals(BUILD_ELEMENT) && element.isClosed()) {
+ progress_bar.setIndeterminate(false);
+ setValue(MAX);
+ state = BASE;
+ if(Configuration.getMode() <= threshold) {
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Build.BuildComplete1"), event.getStatus()));
+ }
+ }
+ // We had a bad argument to a plugin/classifier
+ else if (name.equals(BADARGUMENT_ELEMENT)) {
+ if(Configuration.getMode() <= threshold) {
+ String args[] = new String[1];
+ args[0] = element.getAttribute(ARGUMENT_ATTRIBUTE);
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.BadArgument", args), event.getStatus()));
+ args = null;
+ }
+ }
+ // We had a bad argument value to a plugin/clasifier
+ else if (name.equals(BADARGUMENTVALUE_ELEMENT)) {
+ if(Configuration.getMode() <= threshold) {
+ String args[] = new String[1];
+ args[0] = element.getAttribute(ARGUMENT_ATTRIBUTE);
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.BadArgumentValue", args), event.getStatus()));
+ args = null;
+ }
+ }
+ // And this one tellsa us the plugin
+ else if (name.equals(BADPLUGIN_ELEMENT)) {
+ if(Configuration.getMode() <= threshold) {
+ String args[] = new String[1];
+ args[0] = element.getAttribute(PLUGIN_ATTRIBUTE);
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.BadPluginOptions", args), event.getStatus()));
+ args = null;
+ }
+ }
+ // or the classifier
+ else if (name.equals(BADCLASSIFIER_ELEMENT)) {
+ if(Configuration.getMode() <= threshold) {
+ String args[] = new String[1];
+ args[0] = element.getAttribute(CLASSIFIER_ATTRIBUTE);
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.BadClassifierOptions", args), event.getStatus()));
+ args = null;
+ }
+ }
+
+ else {
+ // Unknown command, go indeterminate
+ DebugStream.println("Unknown name: " + name);
+ progress_bar.setIndeterminate(true);
+ }
+ name = null;
+ break;
+ case COMPRESSTEXT:
+ // We are either dealing with a phase, aa fatal error or an end stage
+ name = element.getElementName();
+ if(name.equals(PHASE_ELEMENT)) {
+ // There are three phases within compressing text (or of four possible ones)
+ value = element.getAttribute(NAME_ATTRIBUTE);
+ if(value.equals(COLLECTTEXTSTATS_VALUE)) {
+ // Nothing to do
+ }
+ else if(value.equals(CREATINGCOMPRESS_VALUE) || value.equals(SKIPCREATINGCOMP_VALUE)) {
+ // One third complete
+ progress_bar.setIndeterminate(false);
+ setValue((int)(((double)current_stages + 0.3d) * MAX) / expected_stages);
+ }
+ else if(value.equals(COMPRESSINGTEXT_VALUE)) {
+ // Two thirds complete
+ progress_bar.setIndeterminate(false);
+ setValue((int)(((double)current_stages + 0.6d) * MAX) / expected_stages);
+ }
+ else {
+ // Unknown command, go indeterminate
+ DebugStream.println("Unknown value: " + value);
+ progress_bar.setIndeterminate(true);
+ }
+ value = null;
+ }
+ // Note that a fatal error is always followed by an end-stage, but not an end build. Thus the final state will be build (not base) and calls to exitValue() with indicate failure.
+ else if(name.equals(FATALERROR_ELEMENT)) {
+ // On simplier modes produce a meaningful message
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Build.FatalError"), event.getStatus()));
+ }
+ else if(name.equals(STAGE_ELEMENT) && element.isClosed()) {
+ // Increase progress so that stage is complete.
+ current_stages++;
+ progress_bar.setIndeterminate(false);
+ setValue((current_stages * MAX) / expected_stages);
+ state = BUILD;
+ }
+ else {
+ // Unknown command, go indeterminate
+ DebugStream.println("Unknown name: " + name);
+ progress_bar.setIndeterminate(true);
+ }
+ name = null;
+ break;
+ case INDEX:
+ /** @todo - If on lower modes output a meaningful message. */
+ name = element.getElementName();
+ if(name.equals(PHASE_ELEMENT)) {
+ value = element.getAttribute(NAME_ATTRIBUTE);
+ if(value.equals(CREATINGINDEXDIC_VALUE)) {
+ // Nothing to do
+ }
+ else if(value.equals(INVERTINGTEXT_VALUE)) {
+ // One fifth complete
+ progress_bar.setIndeterminate(false);
+ setValue((int)(((double)current_stages + 0.2d) * MAX) / expected_stages);
+ }
+ else if(value.equals(CREATETHEWEIGHTS_VALUE)) {
+ // Two fifths complete
+ progress_bar.setIndeterminate(false);
+ setValue((int)(((double)current_stages + 0.4d) * MAX) / expected_stages);
+ }
+ else if(value.equals(CREATESTEMMEDDIC_VALUE)) {
+ // Three fifths complete
+ progress_bar.setIndeterminate(false);
+ setValue((int)(((double)current_stages + 0.6d) * MAX) / expected_stages);
+ }
+ else if(value.equals(CREATINGSTEMINDX_VALUE)) {
+ // Four fifths complete
+ progress_bar.setIndeterminate(false);
+ setValue((int)(((double)current_stages + 0.8d) * MAX) / expected_stages);
+ }
+ else {
+ // Unknown command, go indeterminate
+ DebugStream.println("Unknown value: " + value);
+ progress_bar.setIndeterminate(true);
+ }
+ value = null;
+ }
+ else if(name.equals(WARNING_ELEMENT)) {
+ /** @todo - If on lower modes output a meaningful message. */
+ }
+ // Note that a fatal error is always followed by an end-stage, but not an end build. Thus the final state will be build (not base) and calls to exitValue() with indicate failure.
+ else if(name.equals(FATALERROR_ELEMENT)) {
+ // On lower modes output a meaningful message.
+
+ }
+ else if(name.equals(STAGE_ELEMENT) && element.isClosed()) {
+ // Increase progress so that stage is complete.
+ current_stages++;
+ progress_bar.setIndeterminate(false);
+ setValue((current_stages * MAX) / expected_stages);
+ state = BUILD;
+ }
+ else {
+ // Unknown command, go indeterminate
+ DebugStream.println("Unknown name: " + name);
+ progress_bar.setIndeterminate(true);
+ }
+ name = null;
+ break;
+ case PHIND:
+ name = element.getElementName();
+ if(name.equals(PHASE_ELEMENT)) {
+ value = element.getAttribute(NAME_ATTRIBUTE);
+ if(value.equals(EXTRACTINGVOCAB_VALUE)) {
+ // Nothing to do
+ }
+ else if(value.equals(EXTRACTINGPHRASE_VALUE)) {
+ // One sixth complete
+ progress_bar.setIndeterminate(false);
+ setValue((int)(((double)current_stages + 0.16d) * MAX) / expected_stages);
+ }
+ else if(value.equals(SORTANDRENUMBER_VALUE)) {
+ // Two sixths complete
+ progress_bar.setIndeterminate(false);
+ setValue((int)(((double)current_stages + 0.33d) * MAX) / expected_stages);
+ }
+ else if(value.equals(PHRASEDATABASES_VALUE)) {
+ // Three sixths complete
+ progress_bar.setIndeterminate(false);
+ setValue((int)(((double)current_stages + 0.5d) * MAX) / expected_stages);
+ }
+ else if(value.equals(WORDLEVELINDEXES_VALUE)) {
+ // Four sixths complete
+ progress_bar.setIndeterminate(false);
+ setValue((int)(((double)current_stages + 0.66d) * MAX) / expected_stages);
+ }
+ else if(value.equals(DOCINFODATABASES_VALUE)) {
+ // Five sixths complete
+ progress_bar.setIndeterminate(false);
+ setValue((int)(((double)current_stages + 0.83d) * MAX) / expected_stages);
+ }
+ else {
+ // Unknown command, go indeterminate
+ DebugStream.println("Unknown value: " + value);
+ progress_bar.setIndeterminate(true);
+ }
+ value = null;
+ }
+ else if(name.equals(STAGE_ELEMENT) && element.isClosed()) {
+ // Increase progress so that stage is complete.
+ current_stages++;
+ progress_bar.setIndeterminate(false);
+ setValue((current_stages * MAX) / expected_stages);
+ state = BUILD;
+ }
+ else {
+ // Unknown command, go indeterminate
+ DebugStream.println("Unknown name: " + name);
+ progress_bar.setIndeterminate(true);
+ }
+ name = null;
+ break;
+ case CREATEINFODATA:
+ case CREATINGAUXILARY:
+ name = element.getElementName();
+ if(name.equals(STAGE_ELEMENT) && element.isClosed()) {
+ // Another stage complete
+ current_stages++;
+ progress_bar.setIndeterminate(false);
+ setValue((current_stages * MAX) / expected_stages);
+ state = BUILD;
+ }
+ else {
+ // Unknown command, go indeterminate
+ DebugStream.println("Unknown name: " + name);
+ progress_bar.setIndeterminate(true);
+ }
+ name = null;
+ break;
+ default:
+ // Unknown command, go indeterminate
+ DebugStream.println("Unknown name: " + name);
+ progress_bar.setIndeterminate(true);
+ }
+ }
+ // If we are dealing with lower detail modes then veto any other messages. We do this here as the progress monitors are garuenteed to recieve this message before anything else
+ else if(Configuration.getMode() <= threshold) {
+ event.veto();
+ }
+ }
+
+ public void reset() {
+ current_stages = 0;
+ expected_stages = 0;
+ progress_bar.setIndeterminate(false);
+ progress_bar.setString(null);
+ setValue(MIN);
+ state = BASE;
+ progress_bar.updateUI();
+ }
+
+ public void saving() {
+ }
+
+ public void setSharedProgressBar(GProgressBar shared_progress_bar) {
+ this.shared_progress_bar = shared_progress_bar;
+ }
+
+ /** Since the creator of this process monitor is actually in the GUI, this class provides the simpliest way to send a cancel process message between the two.
+ * @param state The desired state of the stop flag as a boolean .
+ */
+ public synchronized void setStop(boolean state) {
+ this.stop = state;
+ progress_bar.setIndeterminate(false);
+ }
+ /** This method resets this monitor to the start, reseting the process parsing and progress bar.
+ * TODO Everthing.
+ */
+ public void start() {
+ stop = false;
+ setValue(MIN);
+ expected_stages = 3; // Always compress text, create info database and auxiliary creation
+ // Retrieve cdm.
+ CollectionDesignManager cdm = Gatherer.c_man.getCollection().cdm;
+ // If we are using MGPP the number of indexes is always 1
+ if(cdm.index_manager.isMGPP()) {
+ expected_stages = expected_stages + 1;
+ } else if (cdm.index_manager.isLucene()) {
+ // lucene no of indexes == number of levels
+ int num_levels = cdm.index_manager.getNumLevels();
+ if (num_levels == 0) num_levels = 1;
+ expected_stages = expected_stages + num_levels;
+ }
+ else {
+ int num_indexes = cdm.index_manager.getNumIndexes();
+ int num_partitions = cdm.subcollectionindex_manager.getSize();
+ if(num_partitions > 0) {
+ num_indexes = num_indexes * num_partitions;
+ }
+ int num_languages = cdm.language_manager.getSize();
+ if(num_languages > 0) {
+ num_indexes = num_indexes * num_languages;
+ }
+ expected_stages = expected_stages + num_indexes;
+ }
+ // And if we have a phind classifier, we have one further index
+ if(cdm.classifier_manager.isPhindClassifierAssigned()) {
+ expected_stages = expected_stages + 1;
+ }
+ cdm = null;
+ }
+ /** This method indicates the process is complete.
+ * TODO Everthing.
+ */
+ public void stop() {
+ progress_bar.setIndeterminate(false);
+ setValue(MAX);
+ }
+
+ private void setValue(int value) {
+ progress_bar.setValue(value);
+ // Incrementing the shared progress bar is a little more problematic as we may very well be trying to set it to MIN but instead set it to halfway if we're not careful.
+ if(value > MIN) {
+ shared_progress_bar.setValue(MAX + value);
+ }
+ }
+}
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GDefaultProgressMonitor.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GDefaultProgressMonitor.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GDefaultProgressMonitor.java (revision 31635)
@@ -0,0 +1,90 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.shell;
+
+import org.greenstone.gatherer.gui.GProgressBar;
+import java.util.ArrayList;
+import java.awt.Component;
+
+public class GDefaultProgressMonitor implements GShellProgressMonitor{
+ private GProgressBar progress_bar = null;
+ private boolean signalledStop = false;
+
+ public void addProgressBar(GProgressBar progressBar){
+ progress_bar = progressBar;
+ }
+
+ public int exitValue(){
+ if (signalledStop){
+ return 0;
+ }
+ return 1;
+ }
+
+ public GProgressBar getSharedProgress(){
+ return progress_bar;
+ }
+
+ public Component getProgress(){
+ return null;
+ }
+
+ public void messageOnProgressBar(String message){
+
+ }
+
+ public boolean hasSignalledStop(){
+ return signalledStop;
+ }
+
+ public void increment(){}
+
+ public void process(ArrayList queue){}
+
+ public void reset(){}
+
+ public void saving(){}
+
+ public void setStop(boolean state){
+ signalledStop = state;
+ }
+
+ public void start(){}
+
+ public void stop(){}
+
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GImportProgressMonitor.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GImportProgressMonitor.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GImportProgressMonitor.java (revision 31635)
@@ -0,0 +1,481 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.shell;
+
+import java.awt.Component;
+import java.util.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.gui.GProgressBar;
+import org.greenstone.gatherer.util.StaticStrings;
+
+/** This implementation of GShellProgressMonitor is designed to parse and translate the progress of a import.pl call.
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.1
+ */
+public class GImportProgressMonitor
+ implements GShellProgressMonitor {
+ /** Indicates if the progress bar is currently showing a string. */
+ private boolean showing_string = false;
+ /** Indicates if the GUI has asked the process this object monitors to stop. */
+ private boolean stop = false;
+ /** A count of the extracted files processed so far. */
+ private int extracted_file_count;
+ /** The number of documents processed (or rejected) so far. */
+ private int file_count;
+ /** The next value to be set for the progress bar - I use this rather than a compounding progress measure to try to limit rounding inaccuracies (nothing looks worse than the progress bar having to jump the last 10-15%) */
+ private int next_progress_value;
+ /** The number of files expected to be scanned by this import process. */
+ private int num_expected_docs;
+ /** This holds the number of documents actually processed by the import command, as garnered from the final block of text output. */
+ private int num_docs;
+
+ private int threshold = Configuration.LIBRARIAN_MODE;
+ /** The progress bar this monitor updates. */
+ private GProgressBar progress_bar;
+
+ /** The progress bar that is shared with build monitor. */
+ private GProgressBar shared_progress_bar;
+
+ /** */
+ static final private String BLOCKED_ATTRIBUTE = "blocked";
+ static final private String CONSIDERED_ATTRIBUTE = "considered";
+ static final private String FAILED_ATTRIBUTE = "failed";
+ static final private String IGNORED_ATTRIBUTE = "ignored";
+ static final private String IMPORT_ELEMENT = "Import";
+ static final private String PROCESSED_ATTRIBUTE = "processed";
+
+ static final private String ARGUMENT_ATTRIBUTE = "a";
+ /** */
+ static final private String NAME_ATTRIBUTE = "n";
+ /** */
+ static final private String PLUGIN_ATTRIBUTE = "p";
+ static final private String REASON_ATTRIBUTE = "r";
+ /** The fixed portion of the progress bar used for the calculating of file size and other pre-import functions. */
+ static final private int CALCULATION = 50000;
+ /** The fixed portion of the progress bar used for extracted metadata. */
+ static final private int EXTRACTED = 200000;
+ /** The fixed portion of the progress bar used for processed documents. */
+ static final private int PROCESSED = 750000;
+ /** The maximum value for this progress bar. */
+ static final private int MAXIMUM = CALCULATION + EXTRACTED + PROCESSED;
+ /** The minimum value for this progress bar. */
+ static final private int MINIMUM = 0;
+ /** The element name of a file detected message. */
+ static final private String FILE_ELEMENT = "File";
+ /** The element name of a file processing message. */
+ static final private String PROCESSING_ELEMENT = "Processing";
+ /** The element name of a processing error message. */
+ static final private String PROCESSINGERROR_ELEMENT = "ProcessingError";
+ /** The element name of an import complete message. */
+ static final private String IMPORTCOMPLETE_ELEMENT = "ImportComplete";
+ /** The element name of a file not processed. */
+ static final private String NONPROCESSEDFILE_ELEMENT = "NonProcessedFile";
+ /** The element name of a file not recognised. */
+ static final private String NONRECOGNISEDFILE_ELEMENT = "NonRecognisedFile";
+ /** The element name of a Warning message */
+ static final private String WARNING_ELEMENT = "Warning";
+ /** The element name of a BadArgument message */
+ static final private String BADARGUMENT_ELEMENT = "BadArgument";
+ static final private String BADARGUMENTVALUE_ELEMENT = "BadArgumentValue";
+ static final private String BADPLUGIN_ELEMENT = "BadPlugin";
+
+ public GImportProgressMonitor() {
+ progress_bar = new GProgressBar();
+ progress_bar.setIndeterminate(false);
+ progress_bar.setMaximum(MAXIMUM);
+ progress_bar.setMinimum(MINIMUM);
+ progress_bar.setString(null);
+ progress_bar.setStringPainted(true);
+ next_progress_value = CALCULATION;
+ shared_progress_bar = new GProgressBar();
+ shared_progress_bar.setIndeterminate(false);
+ shared_progress_bar.setMaximum(MAXIMUM * 2);
+ shared_progress_bar.setMinimum(MINIMUM);
+ shared_progress_bar.setString(null); // Default string
+ shared_progress_bar.setStringPainted(true);
+ setValue(MINIMUM);
+ }
+
+ /** Method to register a new progress bar with this monitor.
+ * @param progress_bar The new GProgressBar .
+ */
+ public void addProgressBar(GProgressBar progress_bar) {
+ this.progress_bar = progress_bar;
+ progress_bar.setMaximum(MAXIMUM);
+ progress_bar.setMinimum(MINIMUM);
+ setValue(MINIMUM);
+ next_progress_value = CALCULATION;
+ }
+
+ /** Determine the script exit value according to the progress monitor. This gets around a problem where several script failures actually result with a successful exit value.
+ * @return A int with a value of zero if and only if the script was successful.
+ */
+ public int exitValue() {
+ if(num_docs > 0) {
+ return 0;
+ }
+ return 1;
+ }
+
+ /** Retrieve the number of documents recorded by the import monitor during the execution of the import scripts.
+ * @return An int giving the document number.
+ */
+ public int getNumberOfDocuments() {
+ return num_docs;
+ }
+
+ /** Method to retrieve whatever control is being used as the progress indicator. Usually a GProgressBar but there may be others implemented later.
+ * @return A Component on which the progress of the process is being displayed.
+ */
+ public Component getProgress() {
+ return progress_bar;
+ }
+
+ public GProgressBar getSharedProgress() {
+ return shared_progress_bar;
+ }
+
+ public void messageOnProgressBar(String message)
+ {
+ if (message!=null && !message.equals("")) {
+ progress_bar.setString(message);
+ shared_progress_bar.setString(message);
+ showing_string = true;
+ }
+ else {
+ progress_bar.setString(null);
+ shared_progress_bar.setString(null);
+ showing_string = false;
+ }
+ }
+
+ /** Method to determine the state of the stop flag, which may be set by the visual component that created this monitor.
+ * @return A boolean indicating if the process has been asked to stop.
+ */
+ public synchronized boolean hasSignalledStop() {
+ return stop;
+ }
+
+ /** Inform the progress bar that it should programatically increment progress by one step. This is only called during the metadata archive extraction so each step should be (1000000 / 5) / num_docs. */
+ public void increment() {
+ extracted_file_count++;
+ // The current progress is calculated to be:
+ // The fixed calculation value plus the fixed processed value plus some portion of the fixed extracted value. This portion is the extracted_file_count over the total number of documents available. Note that this breaks badly for bibliographical files (for now).
+ setValue(CALCULATION + PROCESSED + ((EXTRACTED * extracted_file_count) / num_docs));
+ }
+
+ /** This method is used to 'feed in' a line of text captured from the process.
+ * @param queue a queue which at the moment should contain a single GShellEvent
+ */
+ public void process(ArrayList queue) {
+ // Retrieve the first event.
+ GShellEvent event = (GShellEvent) queue.get(0);
+ // Remove 'import.pl> ' bit
+ String line = event.getMessage();
+ line = line.substring(line.indexOf(StaticStrings.GREATER_THAN_CHARACTER) + 1);
+ line = line.trim();
+ // System.err.println("**** line = " + line);
+
+ ///ystem.err.println("Process: " + line);
+ if(line.startsWith(StaticStrings.LESS_THAN_CHARACTER) && line.endsWith(StaticStrings.GREATER_THAN_CHARACTER)) {
+ // No original event is passed on, even in the lower modes
+ event.veto();
+ // Create a new element from it
+ GShellElement element = new GShellElement(line);
+ String name = element.getElementName();
+ ///ystem.err.println("Gatherer tag: " + name);
+ // We may be reading a file. Remember we have to offset process as we recieve this message 'before' a document is processed. Hence the use of 'next_progress_value'
+ if(name.equals(IMPORT_ELEMENT)) {
+ ///ystem.err.println("#Import");
+ // We're into parsing output, so we don't need the 'calculating file size' etc string.
+ if(showing_string) {
+ progress_bar.setString(null);
+ showing_string = false;
+ }
+ if(Configuration.getMode() <= threshold) {
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Import.ImportBegun1"), event.getStatus()));
+ }
+ }
+ else if(name.equals(FILE_ELEMENT)) {
+ ///ystem.err.println("#File");
+ file_count++;
+ // Set the next progress
+ if(next_progress_value > 0) {
+ setValue(next_progress_value);
+ }
+ // Now we calculate the next progress value
+ next_progress_value = CALCULATION + ((PROCESSED * file_count) / num_expected_docs);
+ event.veto(); // Unconditional veto
+ }
+ // Or we're being told what plugin is actually processing the file
+ else if(name.equals(PROCESSING_ELEMENT)) {
+ ///ystem.err.println("#FileProcessing");
+ // If we are at lower mode settings fire a new 'dumbed down' event
+ if(Configuration.getMode() <= threshold) {
+ String args[] = new String[2];
+ args[0] = element.getAttribute(NAME_ATTRIBUTE);
+ args[1] = element.getAttribute(PLUGIN_ATTRIBUTE);
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Import.FileProcessing", args), event.getStatus()));
+ args = null;
+ }
+ }
+ // processing error
+ else if (name.equals(PROCESSINGERROR_ELEMENT)) {
+ if(Configuration.getMode() <= threshold) {
+ String args[] = new String[1];
+ args[0] = element.getAttribute(NAME_ATTRIBUTE);
+ String reason = element.getAttribute(REASON_ATTRIBUTE);
+ if (reason == null || reason.equals("")) {
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Import.FileProcessingError", args), event.getStatus()));
+ }
+ else {
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Import.FileProcessingError", args) + " (" + reason + ")", event.getStatus()));
+ }
+ args = null;
+ }
+ }
+ // unrecognised file
+ else if (name.equals(NONRECOGNISEDFILE_ELEMENT)) {
+ if(Configuration.getMode() <= threshold) {
+ String args[] = new String[1];
+ args[0] =element.getAttribute(NAME_ATTRIBUTE);
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Import.FileNotRecognised", args), event.getStatus()));
+ args = null;
+ }
+ }
+ // unprocessed file
+ else if (name.equals(NONPROCESSEDFILE_ELEMENT)) {
+ if(Configuration.getMode() <= threshold) {
+ String args[] = new String[1];
+ args[0] =element.getAttribute(NAME_ATTRIBUTE);
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Import.FileNotProcessed", args), event.getStatus()));
+ args = null;
+ }
+ }
+ // We had a bad argument to a plugin
+ else if (name.equals(BADARGUMENT_ELEMENT)) {
+ if(Configuration.getMode() <= threshold) {
+ String args[] = new String[1];
+ args[0] = element.getAttribute(ARGUMENT_ATTRIBUTE);
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.BadArgument", args), event.getStatus()));
+ args = null;
+ }
+ }
+ // We had a bad argument value to a plugin
+ else if (name.equals(BADARGUMENTVALUE_ELEMENT)) {
+ if(Configuration.getMode() <= threshold) {
+ String args[] = new String[1];
+ args[0] = element.getAttribute(ARGUMENT_ATTRIBUTE);
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.BadArgumentValue", args), event.getStatus()));
+ args = null;
+ }
+ }
+ // And this one tellsa us the plugin
+ else if (name.equals(BADPLUGIN_ELEMENT)) {
+ if(Configuration.getMode() <= threshold) {
+ String args[] = new String[1];
+ args[0] = element.getAttribute(PLUGIN_ATTRIBUTE);
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.BadPluginOptions", args), event.getStatus()));
+ args = null;
+ }
+ }
+ else if (name.equals(WARNING_ELEMENT)) {
+ if(Configuration.getMode() <= threshold) {
+ String args[] = new String[1];
+ args[0] = element.getAttribute(PLUGIN_ATTRIBUTE);
+ String reason = element.getAttribute(REASON_ATTRIBUTE);
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Import.Warning", args) + " " + reason, event.getStatus()));
+ args = null;
+ }
+ }
+
+
+ // Or the import complete element
+ else if(name.equals(IMPORTCOMPLETE_ELEMENT)) {
+ // Set the next progress
+ setValue(next_progress_value);
+ // If we are at lower mode settings fire a new 'dumbed down' event
+ if(Configuration.getMode() <= threshold) {
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Import.ImportComplete1"), event.getStatus()));
+ String considered_str = element.getAttribute(CONSIDERED_ATTRIBUTE);
+
+ if (considered_str.equals(StaticStrings.ONE_CHARACTER)) {
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Import.ImportComplete2.1"), event.getStatus()));
+ } else {
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Import.ImportComplete2", considered_str), event.getStatus()));
+ }
+ considered_str = null;
+ // The number of documents processed
+ String processed_str = element.getAttribute(PROCESSED_ATTRIBUTE);
+ if(!processed_str.equals(StaticStrings.ZERO_CHARACTER)) {
+ if(processed_str.equals(StaticStrings.ONE_CHARACTER)) {
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), " "+Dictionary.get("GShell.Import.ImportComplete.Processed.1"), event.getStatus()));
+ }
+ else {
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), " "+Dictionary.get("GShell.Import.ImportComplete.Processed", processed_str), event.getStatus()));
+ }
+ }
+ // Try to set num docs
+ try {
+ num_docs = Integer.parseInt(processed_str);
+ }
+ catch(Exception exception) {
+ num_docs = 0;
+ }
+ processed_str = null;
+
+ // The number of documents blocked
+ String blocked_str = element.getAttribute(BLOCKED_ATTRIBUTE);
+ if(!blocked_str.equals(StaticStrings.ZERO_CHARACTER)) {
+ if (blocked_str.equals(StaticStrings.ONE_CHARACTER)) {
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), " "+Dictionary.get("GShell.Import.ImportComplete.Blocked.1"), event.getStatus()));
+ } else {
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), " "+Dictionary.get("GShell.Import.ImportComplete.Blocked", blocked_str), event.getStatus()));
+ }
+ }
+ blocked_str = null;
+
+ // The number of documents ignored
+ String ignored_str = element.getAttribute(IGNORED_ATTRIBUTE);
+ if(!ignored_str.equals(StaticStrings.ZERO_CHARACTER)) {
+ if(ignored_str.equals(StaticStrings.ONE_CHARACTER)) {
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), " "+Dictionary.get("GShell.Import.ImportComplete.Ignored.1"), event.getStatus()));
+ } else {
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), " "+Dictionary.get("GShell.Import.ImportComplete.Ignored", ignored_str), event.getStatus()));
+ }
+ }
+ ignored_str = null;
+
+ // The number of documents failed
+ String failed_str = element.getAttribute(FAILED_ATTRIBUTE);
+ if(!failed_str.equals(StaticStrings.ZERO_CHARACTER)) {
+ if(failed_str.equals(StaticStrings.ONE_CHARACTER)) {
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), " "+Dictionary.get("GShell.Import.ImportComplete.Failed.1"), event.getStatus()));
+ } else {
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), " "+Dictionary.get("GShell.Import.ImportComplete.Failed", failed_str), event.getStatus()));
+ }
+ }
+ failed_str = null;
+ // Message is finally ready
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Import.ImportComplete3"), event.getStatus()));
+ } // if mode below threshhold
+ else {
+ // Try to set num docs
+ String processed_str = element.getAttribute(PROCESSED_ATTRIBUTE);
+ try {
+ num_docs = Integer.parseInt(processed_str);
+ }
+ catch(Exception exception) {
+ num_docs = 0;
+ }
+ }
+ }
+ else {
+ // Veto it
+ event.veto();
+ }
+ } // GLI output
+ else if(Configuration.getMode() <= threshold) {
+ event.veto();
+ }
+ }
+
+ public void reset() {
+ setValue(MINIMUM);
+ progress_bar.setString(null);
+ progress_bar.updateUI();
+ }
+
+ public void saving() {
+ progress_bar.setString(Dictionary.get("SaveProgressDialog.Title"));
+ showing_string = true;
+ }
+
+ /** Since the creator of this process monitor is actually in the GUI, this class provides the simpliest way to send a cancel process message between the two.
+ * @param state The desired state of the stop flag as a boolean .
+ */
+ public synchronized void setStop(boolean state) {
+ this.stop = state;
+ }
+
+ /** This method resets this monitor to the start, reseting the process parsing and progress bar.
+ */
+ public void start() {
+ stop = false;
+ setValue(MINIMUM);
+ progress_bar.setString(Dictionary.get("FileActions.Calculating_Size"));
+ showing_string = true;
+ extracted_file_count = 0;
+ file_count = 0;
+ next_progress_value = -1;
+ num_docs = -1;
+ // !! HACK: This is multiplied by 2 because the metadata_read now outputs a tag also
+ // Ideally the metadata read pass would be completely separate and the GLI could work out the number
+ // of documents accurately and provide a much better progress bar
+ num_expected_docs = Gatherer.c_man.getCollection().getCount() * 2;
+ }
+
+ /** This method indicates the process is complete.
+ */
+ public void stop() {
+ setValue(MAXIMUM);
+ }
+
+ int previous_value = -1;
+
+ private void setValue(int value)
+ {
+ // Don't let the value go higher than the maximum
+ if (value > MAXIMUM) {
+ DebugStream.println("Error: Tried to set progress bar higher than maximum!\n");
+ return;
+ }
+ // Don't let the value go backwards
+ if (value != 0 && value < previous_value) {
+ DebugStream.println("Error: Tried to set progress bar smaller than previous!\n");
+ return;
+ }
+
+ progress_bar.setValue(value);
+ shared_progress_bar.setValue(value);
+ previous_value = value;
+ }
+}
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GScheduleProgressMonitor.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GScheduleProgressMonitor.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GScheduleProgressMonitor.java (revision 31635)
@@ -0,0 +1,262 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.shell;
+
+import java.awt.Component;
+import java.util.ArrayList;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.cdm.CollectionDesignManager;
+import org.greenstone.gatherer.gui.GProgressBar;
+import org.greenstone.gatherer.util.StaticStrings;
+/** This implementation of GShellProgressMonitor is designed to parse and translate the progress of a buildcol.pl call.
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.3
+ */
+/* Wendy's feeble attempt at writing a schedule progress monitor. While it's not really needed, I'm writing it so that the schedule.pl prompts will go away. ;) This also may need other "ELEMENTS" added to it.
+ */
+
+public class GScheduleProgressMonitor
+ implements GShellProgressMonitor {
+ /** The minimum value = 0 */
+ static final private int MIN = 0;
+ /** In order to get a smoothish progress bar, set the maximum to a large number = 1000000 */
+ static final private int MAX = 1000000;
+ /** The various states the Schedule progress state machine can be in, starting at the base state. There are only two stages - perl script generation and cron building - so i'm not sure we need too many steps. */
+ static final int BASE = -1;
+ static final int SCHEDULE = 0;
+ static final int DONE = 1;
+ /** Now the language independant sentinel strings. */
+ static final String SCHEDULE_ELEMENT = "Schedule";
+ static final String SCHEDULE_DELETE = "Delete";
+ static final String SCHEDULE_DONE="Done";
+
+ static final private String ARGUMENT_ATTRIBUTE = "a";
+ static final private String CLASSIFIER_ATTRIBUTE = "c";
+ static final private String PLUGIN_ATTRIBUTE = "p";
+
+ /** Indicates if the GUI has asked the process this object monitors to stop. */
+ private boolean stop = false;
+ /** The current number of stages we have completed. */
+ private int current_stages = 0;
+ /** The number of stages we are expecting to complete. */
+ private int expected_stages = 0;
+
+ private int threshold = Configuration.LIBRARIAN_MODE;
+ /** The current state we are in. The state will change depending on the next flag recieved. */
+ private int state = BASE;
+ /** The progress bar this monitor updates. */
+ //private GProgressBar progress_bar;
+ /** A progress bar that is shared between this this listener and the Import and build monitor. not sure we need this. */
+ //private GProgressBar shared_progress_bar;
+
+ /** Construct a new GScheduleProgressMonitor. */
+ public GScheduleProgressMonitor() {
+ //this.shared_progress_bar = shared_progress_bar;
+ //progress_bar = new GProgressBar();
+ //progress_bar.setMaximum(MAX);
+ //progress_bar.setMinimum(MIN);
+ //progress_bar.setString(null);
+ //progress_bar.setStringPainted(true);
+ //setValue(MIN);
+ }
+
+ /** Method to register a new progress bar with this monitor.
+ * @param progress_bar The new GProgressBar .
+ */
+ /*note - we don't really need this. Wendy */
+ public void addProgressBar(GProgressBar progress_bar) {
+ //this.progress_bar = progress_bar;
+ //progress_bar.setMaximum(MAX);
+ //progress_bar.setMinimum(MIN);
+ //progress_bar.setStringPainted(false);
+ //progress_bar.setString(null);
+ //setValue(MIN);
+ }
+
+ /** Determine the script exit value according to the progress monitor. This gets around a problem where several script failures actually result with a successful exit value.
+ * @return A int with a value of zero if and only if the script was successful.
+ */
+ public int exitValue() {
+ if(state == BASE) {
+ return 0;
+ }
+ return 1;
+ }
+ /** Method to retrieve whatever control is being used as the progress indicator. Usually a GProgressBar but there may be others implemented later.
+ * @return A Component on which the progress of the process is being displayed.
+ */
+ public Component getProgress() {
+ //return progress_bar;
+ return null;
+ }
+
+ public GProgressBar getSharedProgress() {
+ //return shared_progress_bar;
+ //try returning null;
+ return null;
+ }
+
+ public void messageOnProgressBar(String message)
+ {
+ //if (message!=null && !message.equals("")) {
+ //progress_bar.setString(message);
+ //shared_progress_bar.setString(message);
+ //}
+ //else {
+ //progress_bar.setString(null);
+ //shared_progress_bar.setString(null);
+ //}
+ }
+
+ /** Method to determine the state of the stop flag, which may be set by the visual component that created this monitor.
+ * @return A boolean indicating if the process has been asked to stop.
+ */
+ public synchronized boolean hasSignalledStop() {
+ return stop;
+ }
+ /** Inform the progress bar that it should programatically increment progress by one step. */
+ public void increment() {
+ // I'm not sure we really need this for scheduling, so i'm going to ignore it for now.
+ }
+
+ /** This method is used to 'feed in' a line of text captured from the process.
+ * @param queue a queue which at the moment should contain a single GShellEvent
+ */
+ public void process(ArrayList queue) {
+ GShellEvent event = (GShellEvent) queue.get(0);
+ // Remove 'schedule.pl> ' bit
+ String line = event.getMessage();
+ line = line.substring(line.indexOf(StaticStrings.GREATER_THAN_CHARACTER) + 1);
+ line = line.trim();
+ // We are only really interested in processing pseudo-XML tags
+ if(line.startsWith(StaticStrings.LESS_THAN_CHARACTER) && line.endsWith(StaticStrings.GREATER_THAN_CHARACTER)) {
+ // All events are vetoed
+ event.veto();
+
+ // Create a new element from it
+ GShellElement element = new GShellElement(line);
+ // Now change states within the state machine as appropriate. A state change may subsequently incur a progress update.
+ String name = null;
+ String value = null;
+
+ name = element.getElementName();
+ if(name.equals(SCHEDULE_ELEMENT)) {
+ //progress_bar.setIndeterminate(false);
+ // Produce a lower detail message if required
+ if(Configuration.getMode() <= threshold && !stop) {
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Schedule.ScheduleBegun1"), event.getStatus()));
+ }
+ }
+ else if(name.equals(SCHEDULE_DELETE)) {
+ if(Configuration.getMode() <= threshold) {
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Schedule.ScheduleDelete"), event.getStatus()));
+ }
+ }
+ else if(name.equals(SCHEDULE_DONE)) {
+ if(Configuration.getMode() <= threshold) {
+ queue.add(new GShellEvent(event.getSource(), 0, event.getType(), Dictionary.get("GShell.Schedule.ScheduleComplete1"), event.getStatus()));
+ }
+ }
+ else {
+ // Unknown command, go indeterminate
+ DebugStream.println("Unknown name: " + name);
+ //progress_bar.setIndeterminate(true);
+ }
+
+
+
+ }
+ // If we are dealing with lower detail modes then veto any other messages. We do this here as the progress monitors are garuenteed to recieve this message before anything else
+ else if(Configuration.getMode() <= threshold) {
+ event.veto();
+ }
+ }
+
+ public void reset() {
+ current_stages = 0;
+ expected_stages = 0;
+ //progress_bar.setIndeterminate(false);
+ //progress_bar.setString(null);
+ setValue(MIN);
+ state = BASE;
+ //progress_bar.updateUI();
+ }
+
+ public void saving() {
+ }
+
+ public void setSharedProgressBar(GProgressBar shared_progress_bar) {
+ //this.shared_progress_bar = shared_progress_bar;
+ }
+
+ /** Since the creator of this process monitor is actually in the GUI, this class provides the simpliest way to send a cancel process message between the two.
+ * @param state The desired state of the stop flag as a boolean .
+ */
+ public synchronized void setStop(boolean state) {
+ this.stop = state;
+ //progress_bar.setIndeterminate(false);
+ }
+ /** This method resets this monitor to the start, reseting the process parsing and progress bar.
+ * TODO Everthing.
+ */
+ public void start() {
+ stop = false;
+ setValue(MIN);
+ expected_stages = 1; // Always compress text, create info database and auxiliary creation
+
+
+ }
+ /** This method indicates the process is complete.
+ * TODO Everthing.
+ */
+ public void stop() {
+ //progress_bar.setIndeterminate(false);
+ setValue(MAX);
+ }
+
+ private void setValue(int value) {
+ //progress_bar.setValue(value);
+ // Incrementing the shared progress bar is a little more problematic as we may very well be trying to set it to MIN but instead set it to halfway if we're not careful.
+ //if(value > MIN) {
+ // shared_progress_bar.setValue(MAX + value);
+ //}
+ }
+}
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GShell.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GShell.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GShell.java (revision 31635)
@@ -0,0 +1,641 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.shell;
+
+import java.io.*;
+import java.net.*;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.regex.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.tree.*;
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.cdm.CollectionConfiguration;
+import org.greenstone.gatherer.cdm.CollectionDesignManager;
+import org.greenstone.gatherer.cdm.CollectionMetaManager;
+import org.greenstone.gatherer.cdm.CollectionMeta;
+import org.greenstone.gatherer.collection.CollectionManager;
+import org.greenstone.gatherer.metadata.DocXMLFileManager;
+import org.greenstone.gatherer.remote.RemoteGreenstoneServer;
+import org.greenstone.gatherer.util.StaticStrings;
+import org.greenstone.gatherer.util.Utility;
+
+
+/** The GShell is reponsible for running a separately threaded process in the command shell. This is necessary for executing the Perl Scripts and also for other system related funcitonality.
+ */
+public class GShell
+ extends Thread {
+ /** A flag used to determine if this process has been asked to cancel. */
+ private boolean cancel = false;
+ private BufferedOutputStream buffered_output_stream = null;
+ /** The list of listeners associated with this class. */
+ private EventListenerList listeners = null;
+ /** The current status of this shell process. */
+ private int status = -1;
+ /** The type of message being sent. */
+ private int msg_type = -1;
+ /** The type of shell process. */
+ private int type = -1;
+ /** The caller of this process, and thus the class most interested in messages. */
+ private GShellListener caller = null;
+ /** The progress monitor associated with this process. */
+ private GShellProgressMonitor progress = null;
+ /** Arguments to be given to the process (including the executable you are calling. */
+ private String args[] = null;
+ /** The command_output returned from executing a process */
+ private String commandOutput = null;
+
+ /** Elements in process type enumeration. */
+ static final public int BUILD = 0;
+ static final public int IMPORT = 1;
+ static final public int NEW = 2;
+ static final public int EXPORTAS = 3;
+ static final public int CDIMAGE = 4;
+ static final public int CONVERT = 5;
+ static final public int EXPLODE = 6;
+ static final public int SRCREPLACE = 7; // for replacing source docs with their html
+ static final public int SCHEDULE = 8;
+ static final public int DELETE = 9;
+
+ /** Elements in status type enumeration. */
+ static final public int ERROR = 0;
+ static final public int OK = 1;
+ static final public int CANCELLED = 2;
+
+ /** Elements in process type name enumeration. */
+ static public String GSHELL_BUILD = "gshell_build";
+ static public String GSHELL_IMPORT = "gshell_import";
+ static public String GSHELL_NEW = "gshell_new";
+ static public String GSHELL_EXPORTAS = "gshell_exportas";
+ static public String GSHELL_CDIMAGE = "gshell_cdimage";
+ static public String GSHELL_CONVERT = "gshell_convert";
+ static public String GSHELL_EXPLODE = "gshell_explode";
+ static public String GSHELL_SRCREPLACE = "gshell_srcreplace"; // for replacing source docs with their html versions
+ static public String GSHELL_SCHEDULE = "gshell_schedule";
+ static public String GSHELL_FEDORA_COLDELETE = "gshell_fedora_col_delete";
+
+ /** Determine if the given process is still executing. It does this by attempting to throw an exception - not the most efficient way, but the only one as far as I know
+ * @param process the Process to test
+ * @return true if it is still executing, false otherwise
+ */
+ static public boolean processRunning(Process process) {
+ boolean process_running = false;
+
+ try {
+ process.exitValue(); // This will throw an exception if the process hasn't ended yet.
+ }
+ catch(IllegalThreadStateException itse) {
+ process_running = true;
+ }
+ catch(Exception exception) {
+ DebugStream.printStackTrace(exception);
+ }
+ return process_running;
+ }
+
+ /** Constructor gatherer all the data required to create a new process, and emit meaningfull messages.
+ * @param args A String[] containing the arguments to the process thread, including the name of the executable.
+ * @param type An int that indicates what group of processes this process belongs to, as some are treated slightly differently (i.e an IMPORT type process is always followed by a BUILD one).
+ * @param msg_type As process threads may be background (like a makecol.pl call) or important processes in their own right (such as an IMPORT-BUILD) we must be able to set what level messages posted by this class will have by usings this int .
+ * @param caller The default GShellListener that is interested in the progress of this process.
+ * @param progress The GShellProgressMonitor associated with this process.
+ * @param name A String identifier given to the process, for convience and debug reasons.
+ */
+ public GShell(String args[], int type, int msg_type, GShellListener caller, GShellProgressMonitor progress, String name) {
+ super(name);
+ this.args = args;
+ this.msg_type = msg_type;
+ this.type = type;
+ this.caller = caller;
+ this.progress = progress;
+ this.status = 0;
+ // Lower this jobs priority
+ this.setPriority(Thread.MIN_PRIORITY);
+ listeners = new EventListenerList();
+ listeners.add(GShellListener.class, caller);
+ }
+ /** This method adds another shell listener to this process.
+ * @param listener The new GShellListener .
+ */
+ public void addGShellListener(GShellListener listener) {
+ listeners.add(GShellListener.class, listener);
+ }
+ /** This method removes a certain shell listener from this process.
+ * @param listener The GShellListener to be removed.
+ */
+ /* private void removeGShellListener(GShellListener listener) {
+ listeners.remove(GShellListener.class, listener);
+ } */
+
+ protected StringBuffer get_stream_char(InputStreamReader isr, StringBuffer line_buffer,
+ BufferedOutputStream bos) throws IOException
+ {
+ int c = isr.read();
+ ///atherer.println("isr: '" + (char) c + "'");
+ if(c == '\n' || c == '\r') {
+ if(line_buffer.length() > 0) {
+ String line = line_buffer.toString();
+ // DebugStream.println("* " + line + " *");
+ fireMessage(type, typeAsString(type) + "> " + line, status, bos);
+ line_buffer = new StringBuffer();
+ }
+ }
+ else {
+ line_buffer.append((char)c);
+ }
+
+ return line_buffer;
+ }
+
+ public String getCommandOutput() { return commandOutput; }
+ public void resetCommandOutput() { commandOutput = null; }
+
+ private void runRemote(String[] args, BufferedOutputStream bos)
+ {
+ // Make sure the process hasn't been cancelled
+ if (hasSignalledStop()) {
+ return;
+ }
+
+ try {
+ int directory_name_end = args[0].lastIndexOf(File.separator);
+ String script_name = ((directory_name_end != -1) ? args[0].substring(directory_name_end + 1) : args[0]);
+ System.err.println("Script name: " + script_name);
+ String collection_name = args[args.length - 1];
+ System.err.println("Collection name: " + collection_name);
+
+
+ String script_args = "";
+ for (int i = 1; i < (args.length - 1); i++) {
+ // Skip arguments that won't make sense on the server
+ // and -site is added in RemoteGreenstonServer.java
+ if (args[i].equals("-collectdir") || args[i].equals("-importdir") || args[i].equals("-builddir") || args[i].equals("-site")) {
+ i++;
+ continue;
+ }
+
+ // Script arguments get changed to CGI arguments
+ if (args[i].startsWith("-")) {
+ script_args += "&" + args[i].substring(1) + "=";
+ if ((i + 1) < (args.length - 1) && !args[i + 1].startsWith("-")) {
+ script_args += URLEncoder.encode(args[i + 1], "UTF-8");
+ i++;
+ }
+ }
+ }
+
+ System.err.println("Script args: " + script_args);
+ buffered_output_stream = bos;
+ String command_output = Gatherer.remoteGreenstoneServer.runScript(collection_name, script_name, script_args, this);
+ this.commandOutput = command_output;
+ status = (command_output.equals("") ? CANCELLED : OK);
+ }
+ catch (Exception exception) {
+ DebugStream.printStackTrace(exception);
+ status = ERROR;
+ }
+ }
+
+
+ private void runLocal(String[] args, BufferedOutputStream bos)
+ {
+ try {
+ String command = "";
+ for(int i = 0; i < args.length; i++) {
+ command = command + args[i] + " ";
+ }
+
+ ///ystem.err.println("Command: " + command);
+ fireMessage(type, Dictionary.get("GShell.Command") + ": " + command, status, null);
+
+ Runtime rt = Runtime.getRuntime();
+ Process prcs = null;
+
+ prcs = rt.exec(args);
+ // If we used single argument exec, spaces in filenames or paths cause problems
+ // I.e. better to avoid: prcs = rt.exec(command);
+
+ InputStreamReader eisr = new InputStreamReader( prcs.getErrorStream(), "UTF-8" );
+ InputStreamReader stdisr = new InputStreamReader( prcs.getInputStream(), "UTF-8" );
+
+ StringBuffer eline_buffer = new StringBuffer();
+ StringBuffer stdline_buffer = new StringBuffer();
+
+ while(/*type != GShell.NEW &&*/ processRunning(prcs) && !hasSignalledStop()) {
+ // Hopefully this doesn't block if the process is trying to write to STDOUT.
+ if((eisr!=null) && eisr.ready()) {
+ eline_buffer = get_stream_char(eisr,eline_buffer,bos);
+ }
+ // Hopefully this won't block if the process is trying to write to STDERR
+ else if(stdisr.ready()) {
+ stdline_buffer = get_stream_char(stdisr,stdline_buffer,bos);
+ }
+ else {
+ try {
+ sleep(100);
+ }
+ catch(Exception exception) {
+ }
+ }
+ }
+
+ if(!hasSignalledStop()) {
+ // Of course, just because the process is finished doesn't
+ // mean the incoming streams are empty. Unfortunately I've
+ // got no chance of preserving order, so I'll process the
+ // error stream first, then the out stream
+ while(eisr.ready()) {
+ eline_buffer = get_stream_char(eisr,eline_buffer,bos);
+ }
+
+ while(stdisr.ready()) {
+ stdline_buffer = get_stream_char(stdisr,stdline_buffer,bos);
+ }
+
+ // Ensure that any messages still remaining in the string buffers are fired off.
+ if(eline_buffer.length() > 0) {
+ String eline = eline_buffer.toString();
+ //DebugStream.println("Last bit of eline: " + eline);
+ fireMessage(type, typeAsString(type) + "> " + eline, status, bos);
+ eline = null;
+ }
+
+ if(stdline_buffer.length() > 0) {
+ String stdline = stdline_buffer.toString();
+ //DebugStream.println("Last bit of stdline: " + stdline);
+ fireMessage(type, typeAsString(type) + "> " + stdline, status, null);
+ stdline = null;
+ }
+ }
+ else {
+ DebugStream.println("We've been asked to stop.");
+ }
+
+
+ if(!hasSignalledStop()) {
+ // Now display final message based on exit value
+
+ prcs.waitFor();
+
+ if(prcs.exitValue() == 0) {
+ status = OK;
+ fireMessage(type, typeAsString(type) + "> " + Dictionary.get("GShell.Success"), status, null);
+ }
+ else {
+ status = ERROR;
+ fireMessage(type, typeAsString(type) + "> " + Dictionary.get("GShell.Failure"), status, null);
+ }
+
+ eisr.close();
+ stdisr.close();
+ }
+ else {
+ // I need to somehow kill the child process. Unfortunately
+ // Thread.stop() and Process.destroy() both fail to do
+ // this. But now, thankx to the magic of Michaels 'close the
+ // stream suggestion', it works fine (no it doesn't!)
+ prcs.getInputStream().close();
+ prcs.getErrorStream().close();
+ prcs.getOutputStream().close();
+ prcs.destroy();
+ status = CANCELLED;
+ }
+ }
+ // Exception
+ catch (Exception exception) {
+ DebugStream.println("Exception in GShell.runLocal() - unexpected");
+ DebugStream.printStackTrace(exception);
+ status = ERROR;
+ }
+ }
+
+
+
+ /** Any threaded class must include this method to allow the thread body to be run. */
+ public void run() {
+ String col_name = args[args.length-1];
+
+ // Determine if the user has asked for an outfile.
+ String out_name = null;
+ BufferedOutputStream bos = null;
+ if(type == IMPORT || type == BUILD || type == SCHEDULE) {
+ if(type == IMPORT) {
+ out_name = (String) Gatherer.c_man.getCollection().import_options.getValue("out");
+ }
+ else if(type == BUILD) {
+ out_name = (String) Gatherer.c_man.getCollection().build_options.getValue("out");
+ }
+ else { // SCHEDULE
+ out_name = (String) Gatherer.c_man.getCollection().schedule_options.getValue("out");
+ }
+ if(out_name != null && out_name.length() > 0) {
+ try {
+ bos = new BufferedOutputStream(new FileOutputStream(new File(out_name), true));
+ }
+ catch (Exception error) {
+ DebugStream.printStackTrace(error);
+ }
+ }
+ }
+
+ // Issue a processBegun event
+ //ystem.err.println("\nFiring process begun for " + type + "...");
+ fireProcessBegun(type, status);
+ //ystem.err.println("Done process begun.");
+ if (Gatherer.isGsdlRemote) {
+ runRemote(args,bos);
+ }
+ else {
+ runLocal(args,bos);
+ }
+ //ystem.err.println("Done runLocal().");
+
+ if(status == OK) {
+ if (type == NEW) {
+ if (Gatherer.isGsdlRemote) {
+ Gatherer.remoteGreenstoneServer.downloadCollection(col_name);
+ }
+ }
+ else if(type == IMPORT) {
+
+ // download the archives directory (if gsdl server is remote)
+ if (Gatherer.isGsdlRemote) {
+
+ if (progress!=null) {
+ progress.messageOnProgressBar("Downloading archive data from server");
+ }
+
+ Gatherer.remoteGreenstoneServer.downloadCollectionArchives(col_name);
+
+ if (progress!=null) {
+ progress.messageOnProgressBar("");
+ }
+ }
+
+ // Refresh the DocXMLFileManager
+ fireMessage(type, typeAsString(type) + "> " + Dictionary.get("GShell.Parsing_Metadata_Start"), status, null);
+ DocXMLFileManager.clearDocXMLFiles();
+ if (Configuration.fedora_info.isActive()) { // FLI case
+ File collection_export_directory = new File(CollectionManager.getLoadedCollectionExportDirectoryPath());
+ DocXMLFileManager.loadDocXMLFiles(collection_export_directory,"docmets.xml");
+ }
+ else {
+ File collection_archives_directory = new File(CollectionManager.getLoadedCollectionArchivesDirectoryPath());
+ DocXMLFileManager.loadDocXMLFiles(collection_archives_directory,"doc.xml");
+ }
+
+
+ fireMessage(type, typeAsString(type) + "> " + Dictionary.get("GShell.Parsing_Metadata_Complete"), status, null);
+ }
+
+ else if(type == BUILD) {
+ // download the building directory (if gsdl server is remote)
+ if ((Gatherer.isGsdlRemote) && (!Configuration.fedora_info.isActive())) {
+ if (progress!=null) {
+ progress.messageOnProgressBar("Downloading index data from server");
+ }
+
+ if (!Gatherer.GS3){
+ // Only need to download build.cfg file
+ File build_cfg_file = new File(CollectionManager.getLoadedCollectionBuildingDirectoryPath(), "build.cfg");
+ Gatherer.remoteGreenstoneServer.downloadCollectionFile(col_name, build_cfg_file);
+ }else{
+ // Only need to download buildConfig.xml file
+ File buildConfig_xml_file = new File(CollectionManager.getLoadedCollectionBuildingDirectoryPath(), "buildConfig.xml");
+ Gatherer.remoteGreenstoneServer.downloadCollectionFile(col_name, buildConfig_xml_file);
+ }
+
+ if (progress!=null) {
+ progress.messageOnProgressBar("");
+ }
+ }
+ }
+ else if(type == CDIMAGE) {
+ // download exported files from tmp folder (if gsdl server is remote)
+ if (Gatherer.isGsdlRemote) {
+ if (progress!=null) {
+ progress.messageOnProgressBar("Downloading CD-ROM data from server");
+ }
+
+ // !! TO DO
+
+ if (progress!=null) {
+ progress.messageOnProgressBar("");
+ }
+ }
+ }
+ }
+
+ // We're done.
+ //ystem.err.println("Firing process complete for " + type + "...");
+ fireProcessComplete(type, status);
+ // Close bos
+ if(bos != null) {
+ try {
+ bos.close();
+ bos = null;
+ }
+ catch(Exception error) {
+ DebugStream.printStackTrace(error);
+ }
+ }
+ }
+
+
+ public void fireMessage(String message)
+ {
+ fireMessage(type, typeAsString(type) + "> " + message, status, buffered_output_stream);
+ }
+
+
+ /** Method for firing a message to all interested listeners.
+ * @param type An int indicating the process type.
+ * @param message The message as a String .
+ * @param status An int specifying the current status of the process.
+ */
+ public void fireMessage(int type, String message, int status, BufferedOutputStream bos) {
+ GShellEvent event = new GShellEvent(this, 0, type, message, status);
+ // If there is a progress monitor attached, pass the event to it first. Note that we pass a queue of messages as the processing may cause one message to be split into several.
+ ArrayList message_queue = new ArrayList();
+ message_queue.add(event);
+ if(progress != null) {
+ progress.process(message_queue);
+ }
+ for(int j = 0; j < message_queue.size(); j++) {
+ GShellEvent current_event = (GShellEvent) message_queue.get(j);
+ // If the event hasn't been vetoed, pass it on to other listeners
+ if(!current_event.isVetoed()) {
+ Object[] concerned = listeners.getListenerList();
+ for(int i = 0; i < concerned.length ; i++) {
+ if(concerned[i] == GShellListener.class) {
+ ((GShellListener)concerned[i+1]).message(current_event);
+ }
+ }
+ concerned = null;
+ }
+ }
+ // And if we have a buffered output stream from error messages, send the message there
+ if(bos != null) {
+ try {
+ bos.write(message.getBytes(), 0, message.length());
+ }
+ catch(Exception exception) {
+ DebugStream.println("Exception in GShell.fireMessage() - unexpected");
+ DebugStream.printStackTrace(exception);
+ }
+ }
+ message_queue = null;
+ event = null;
+ }
+
+ /** Method for firing a process begun event which is called, strangely enough, when the process begins.
+ * @param type An int indicating the process type.
+ * @param status An int specifying the current status of the process.
+ */
+ protected void fireProcessBegun(int type, int status) {
+ // Start the progres monitor if available
+ if(progress != null) {
+ //ystem.err.println("About to call progress.start().");
+ progress.start();
+ //ystem.err.println("Called progress.start().");
+ }
+ // Fire an event
+ GShellEvent event = new GShellEvent(this, 0, type, "", status);
+ Object[] concerned = listeners.getListenerList();
+ for(int i = 0; i < concerned.length ; i++) {
+ if(concerned[i] == GShellListener.class) {
+ ((GShellListener)concerned[i+1]).processBegun(event);
+ }
+ }
+ }
+
+
+ /** Method for firing a process complete event which is called, no surprise here, when the process ends.
+ * @param type An int indicating the process type.
+ * @param status An int specifying the current status of the process.
+ */
+ protected void fireProcessComplete(int type, int status) {
+ // Tidy up by stopping the progress bar. If it was cancelled then the cancel command has arrived via the progress bars and they don't need to be told again (it actually causes problems).
+ if(progress != null && status != CANCELLED) {
+ progress.stop();
+ }
+
+ // If we were cancelled, and we are lower details modes, fire off one last message.
+ if(status == CANCELLED && Configuration.getMode() <= Configuration.LIBRARIAN_MODE) {
+ GShellEvent current_event = new GShellEvent(this, 0, type, Dictionary.get("GShell.Build.BuildCancelled"), status);
+ Object[] concerned = listeners.getListenerList();
+ for(int i = 0; i < concerned.length ; i++) {
+ if(concerned[i] == GShellListener.class) {
+ ((GShellListener)concerned[i+1]).message(current_event);
+ }
+ }
+ concerned = null;
+ }
+
+ String msg = "";
+ // If we are creating collection and have trouble with permissions, we need more messages
+ if(status == ERROR && type == GShell.NEW){
+ msg = args[args.length-1];
+ }
+ // And firing off an event
+ GShellEvent event = new GShellEvent(this, 0, type, msg, status);
+ Object[] concerned = listeners.getListenerList();
+ for(int i = 0; i < concerned.length ; i++) {
+ if(concerned[i] == GShellListener.class) {
+ ((GShellListener)concerned[i+1]).processComplete(event);
+ }
+ }
+ }
+
+ /** Method to determine if the user, via the progress monitor, has signalled stop.
+ * @return A boolean indicating if the user wanted to stop.
+ */
+ public boolean hasSignalledStop() {
+ boolean has_signalled_stop = false;
+ if(progress != null) {
+ has_signalled_stop = progress.hasSignalledStop();
+ }
+ if(has_signalled_stop) {
+ status = CANCELLED;
+ }
+ return has_signalled_stop;
+ }
+
+ /** Converts a type into a text representation.
+ * @param type An int which maps to a shell process type.
+ * @return A String which is the thread process's text name.
+ */
+ public String typeAsString(int type) {
+ String name = null;
+ switch(type) {
+ case BUILD:
+ name = "buildcol.pl";
+ break;
+ case IMPORT:
+ name = "import.pl";
+ break;
+ case NEW:
+ name = "mkcol.pl";
+ break;
+ case EXPORTAS:
+ name = "export.pl";
+ break;
+ case CDIMAGE:
+ name = "exportcol.pl";
+ break;
+ case CONVERT:
+ name = "convert_coll_from_gs2.pl";
+ break;
+ case EXPLODE:
+ name = "explode_metadata_database.pl";
+ break;
+ case SRCREPLACE: // To work with replace_srcdoc_with_html.pl
+ name = "replace_srcdoc_with_html.pl";
+ break;
+ case SCHEDULE:
+ name = "schedule.pl";
+ break;
+ default:
+ name = "";
+ }
+ return name;
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GShellElement.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GShellElement.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GShellElement.java (revision 31635)
@@ -0,0 +1,106 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: John Thompson, New Zealand Digital Library, University of Waikato
+ *
+ * Copyright (C) 2004 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.shell;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+import org.greenstone.gatherer.util.StaticStrings;
+
+/** This class wraps around a pseudo-XML message we have recieved from an external script, and allows access to the element name, attributes etc via method calls. */
+public class GShellElement {
+ private boolean is_closed = false;
+ private HashMap attributes;
+ private String element_name;
+ /** Create a new element around the message string. The string is expected to start with < and end with > */
+ /** this is a hack - why aren't we using proper XML parsing?? */
+ public GShellElement(String message) {
+ // Remove the angle brackets
+ message = message.substring(1, message.length() - 1);
+ // Tokenize the message
+ StringTokenizer tokenizer = new StringTokenizer(message);
+ // The first token is the element name - be aware it may start with the close tag indicator
+ element_name = tokenizer.nextToken();
+ if(element_name.startsWith(StaticStrings.URL_SEPARATOR_CHARACTER)) {
+ element_name = element_name.substring(1);
+ is_closed = true;
+ }
+ // Now while we have remaining tokens
+ while(tokenizer.hasMoreTokens()) {
+ int index = -1;
+ // The next token might be a attribute=value pair or it could be the close tag indicator
+ String token = tokenizer.nextToken();
+ if(token.equals(StaticStrings.URL_SEPARATOR_CHARACTER)) {
+ is_closed = true;
+ }
+ else if(token.length() > 3 && (index = token.indexOf(StaticStrings.EQUALS_CHARACTER)) != -1) {
+ String attribute_name = token.substring(0, index);
+ String attribute_value = token.substring(index + 1);
+ // Value may end in a /
+ if(attribute_value.endsWith(StaticStrings.URL_SEPARATOR_CHARACTER)) {
+ is_closed = true;
+ attribute_value = attribute_value.substring(0, attribute_value.length() - 1);
+ }
+ // We may have to strip ' marks off value - if it starts with ' and doesn't end with ' we read in more tokens until we get an ending '
+ if(attribute_value.startsWith(StaticStrings.SINGLE_QUOTE_CHARACTER)) {
+ while (!attribute_value.endsWith(StaticStrings.SINGLE_QUOTE_CHARACTER) && tokenizer.hasMoreTokens()) {
+ attribute_value += " "+tokenizer.nextToken();
+ }
+ if (attribute_value.endsWith(StaticStrings.SINGLE_QUOTE_CHARACTER)) {
+ attribute_value = attribute_value.substring(1, attribute_value.length() - 1);
+ }
+ }
+ if(attributes == null) {
+ attributes = new HashMap();
+ }
+ attributes.put(attribute_name, attribute_value);
+ attribute_value = null;
+ attribute_name = null;
+ }
+ token = null;
+ }
+ tokenizer = null;
+ }
+
+ public String getAttribute(String attribute_name) {
+ String result = null;
+ if(attributes != null) {
+ result = (String) attributes.get(attribute_name);
+ }
+ if(result == null) {
+ result = "";
+ }
+ return result;
+ }
+
+ public String getElementName() {
+ return element_name;
+ }
+
+ public boolean isClosed() {
+ return is_closed;
+ }
+}
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GShellEvent.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GShellEvent.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GShellEvent.java (revision 31635)
@@ -0,0 +1,120 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.shell;
+
+/** Title: The Gatherer
+ * Description: The Gatherer: a tool for gathering and enriching digital collections.
+ * Copyright: Copyright (c) 2001
+ * Company: The University of Waikato
+ * Written: / /01
+ * Revised: 12/05/02 - Commented
+ * 29/05/02 - Moved into correct package
+ * @author John Thompson, Greenstone Digital Libraries
+ * @version 2.1 */
+import java.awt.AWTEvent;
+
+/** This class encapsulates all the information created by an event within a GShell process. */
+public class GShellEvent
+ extends AWTEvent {
+ /** Allows some part of the program to prevent this message being processed by other parts. */
+ private boolean vetoed = false;
+ /** The status of the process at the completion of event. */
+ private int status = -1;
+ /** The process type (such as COPY, BUILD or IMPORT). */
+ private int type = -1;
+ /** Any message associated with this event. */
+ private String message = null;
+
+ /* Constructor.
+ * @param source The GShell that fired this message.
+ * @param id The event identifier as an int .
+ * @param type The process type as an int .
+ * @param message A String representing any message attatched with this event.
+ * @param status The status of the process post event, as an int .
+ */
+ public GShellEvent(Object source, int id, int type, String message, int status) {
+ super(source, id);
+ this.message = message;
+ this.status = status;
+ this.type = type;
+ }
+
+ /** Gets the message associated with this event.
+ * @return The message as a String or null .
+ */
+ public String getMessage() {
+ return message;
+ }
+
+ /** Gets the status associated with this event. This status can then be matched back to the constants in GShell .
+ * @return An int signifying the process status.
+ */
+ public int getStatus() {
+ return status;
+ }
+
+ /** Gets the type associated with this event. This type can then be matched back to the constants in GShell .
+ * @return An int signifying the process type.
+ */
+ public int getType() {
+ return type;
+ }
+
+ /** Determine if this event has been vetoed by some other part of the GLI
+ * @return true if the event has, and thus shouldn't be further processed, false otherwise.
+ */
+ public boolean isVetoed() {
+ return vetoed;
+ }
+
+ /** Change the message to be displayed by this event - usually from something technical so something easier to understand.
+ * @param message the new message as a String
+ */
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public String toString() {
+ return "org.greenstone.gatherer.shell.GShellEvent[" + message + "," + status + "," + type + "]";
+ }
+
+ /** Prevent this event being processed by other parts of the GLI.
+ */
+ public void veto() {
+ vetoed = true;
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GShellListener.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GShellListener.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GShellListener.java (revision 31635)
@@ -0,0 +1,70 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.shell;
+
+/**
+ * Title: The Gatherer
+ * Description: The Gatherer: a tool for gathering and enriching digital collections.
+ * Copyright: Copyright (c) 2001
+ * Company: The University of Waikato
+ * Written: / /01
+ * Revised: 12/05/02 - Commented
+ * 29/05/02 - Moved into correct package
+ * @author John Thompson, Greenstone Digital Libraries
+ * @version 2.1
+ */
+import java.util.EventListener;
+
+/** This interface details the methods required of a class that wishes to listen to a GShell .
+ */
+public interface GShellListener
+ extends EventListener {
+ /** All implementation of GShellListener must include this method so the listener can be informed of messages from the GShell .
+ * @param event A GShellEvent that contains, amoung other things, the message.
+ */
+ public void message(GShellEvent event);
+
+ /** All implementation of GShellListener must include this method so the listener can be informed when a GShell begins its task.
+ * @param event A GShellEvent that contains details of the initial state of the GShell before task comencement.
+ */
+ public void processBegun(GShellEvent event);
+
+ /** All implementation of GShellListener must include this method so the listener can be informed when a GShell completes its task.
+ * @param event A GShellEvent that contains details of the final state of the GShell after task completion.
+ */
+ public void processComplete(GShellEvent event);
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GShellProgressMonitor.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GShellProgressMonitor.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/shell/GShellProgressMonitor.java (revision 31635)
@@ -0,0 +1,105 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.shell;
+
+/**
+ * Title: The Gatherer
+ * Description: The Gatherer: a tool for gathering and enriching digital collections.
+ * Copyright: Copyright (c) 2001
+ * Company: The University of Waikato
+ * Written: / /01
+ * Revised: 12/05/02 - Commented
+ * 29/05/02 - Moved into correct package
+ * @author John Thompson, Greenstone Digital Libraries
+ * @version 2.1
+ */
+import java.awt.Component;
+import java.util.ArrayList;
+import org.greenstone.gatherer.gui.GProgressBar;
+
+/** When implemented, this interface allows another class to monitor the progress of a GShell process. Specifically implementing classes should be designed to take the textual output from the process and then translate that message into some quanitive measure of progress which can then be shown on the progress bar.
+ */
+public interface GShellProgressMonitor {
+ /** Method to register a new progress bar with this monitor.
+ * @param progress_bar The new GProgressBar .
+ */
+ public void addProgressBar(GProgressBar progress_bar);
+
+ /** Determine the script exit value according to the progress monitor. This gets around a problem where several script failures actually result with a successful exit value.
+ * @return A int with a value of zero if and only if the script was successful.
+ */
+ public int exitValue();
+
+ public GProgressBar getSharedProgress();
+
+ /** Method to retrieve whatever control is being used as the progress indicator. Usually a GProgressBar but there may be others implemented later.
+ * @return A Component on which the progress of the process is being displayed.
+ */
+ public Component getProgress();
+
+ public void messageOnProgressBar(String message);
+
+ /** Method to determine the state of the stop flag, which may be set by the visual component that created this monitor.
+ * @return A boolean indicating if the process has been asked to stop.
+ */
+ public boolean hasSignalledStop();
+
+ /** Inform the progress bar that it should programatically increment progress by one step. */
+ public void increment();
+
+ /** This method is used to 'feed in' a line of text captured from the process.
+ * @param queue what will be used as a queue of events, but what, at the moment, should only have one event in it
+ */
+ public void process(ArrayList queue);
+
+ public void reset();
+
+ public void saving();
+
+ /** Since the creator of this process monitor is actually in the GUI, this class provides the simpliest way to send a cancel process message between the two.
+ * @param state The desired state of the stop flag as a boolean .
+ */
+ public void setStop(boolean state);
+
+ /** This method resets this monitor to the start, reseting the process parsing and progress bar.
+ */
+ public void start();
+
+ /** This method indicates the process is complete.
+ */
+ public void stop();
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/AppendLineOnlyFileDocument.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/AppendLineOnlyFileDocument.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/AppendLineOnlyFileDocument.java (revision 31635)
@@ -0,0 +1,1003 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Greenstone Librarian Interface (GLI) application,
+ * part of the Greenstone digital library software suite from the New
+ * Zealand Digital Library Project at the University of Waikato,
+ * New Zealand.
+ *
+ * Author: John Thompson, Greenstone Project, New Zealand Digital Library,
+ * University of Waikato
+ * http://www.nzdl.org
+ *
+ * Copyright (C) 2004 New Zealand Digital Library, University of Waikato
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+
+package org.greenstone.gatherer.util;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.text.*;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.gui.GLIButton;
+
+/** A Document whose underlying data is stored in a RandomAccessFile, and whose Element implementations lack the memory hogging problems associated with anything that extends the AbstractDocument class. This Document, for reasons of time constraints and sanity, only provides an editting ability of appending new lines to the end of the current document, perfect for our logging needs, completely useless for text editing purposes. Furthermore, since the append actions tend to somewhat swamp the IO, I'll temporarily store strings in the structure model, and write them out using a separate thread.
+ * @author John Thompson, Greenstone Project, New Zealand Digital Library, University of Waikato
+ * @version 2.41 final
+ */
+public class AppendLineOnlyFileDocument
+ implements Document {
+ /** A default string to append at the start of each log file. Of special note is the beginning '.' character that will, upon completion of the import/build process, be replaced with a letter indicating the final status of this build attempt. */
+ static final private String GLI_HEADER_STR = ".";
+ /** The root element of the document model. */
+ private AppendLineOnlyFileDocumentElement root_element;
+ /** The current owner of this document, useful for callbacks when certain tasks are complete. */
+ private AppendLineOnlyFileDocumentOwner owner;
+ /** Is this document a build log, and hence has some extra functionality, of just a straight log. */
+ private boolean build_log = false;
+ /** A list of listeners attached to this document. */
+ private EventListenerList listeners_list;
+ /** This properties hash map allows you to store any metadata about this document for later review. */
+ private HashMap properties;
+ /** Indicates the current length of the underlying file (which may not equal the current number of characters in the document). */
+ private long length;
+ /** The random access file which will back this document model. */
+ private RandomAccessFile file;
+ /** The filename given to the underlying file. */
+ private String filename;
+ /** An independant thread responsible for writing the 'in memory' contents of the document model to the random access file as appropriate. This is done so IO lag does not effect the gui as much as it used to. */
+ private WriterThread writer;
+
+
+ public AppendLineOnlyFileDocument(String filename) {
+ this(filename, true);
+ }
+
+ /** The constructor is responsible for creating several necessary data structures as well as opening the random access file. When it creates the new file, as it is wont to do, it will immediately add the special header line defined above.
+ * @param filename the absolute path name of the file as a String
+ * @see org.greenstone.gatherer.util.AppendLineOnlyFileDocument.AppendLineOnlyFileDocumentElement
+ * @see org.greenstone.gatherer.util.AppendLineOnlyFileDocument.WriterThread
+ * @see org.greenstone.gatherer.util.StaticStrings#NEW_LINE_CHAR
+ */
+ public AppendLineOnlyFileDocument(String filename, boolean build_log) {
+ DebugStream.println("Creating log: " + filename);
+ // Initialization
+ this.build_log = build_log;
+ this.filename = filename;
+ this.listeners_list = new EventListenerList();
+ this.properties = new HashMap();
+ this.writer = new WriterThread();
+ writer.start();
+ // Open underlying file
+ try {
+ file = new RandomAccessFile(filename, "rw");
+ // Create the root element.
+ length = file.length();
+ root_element = new AppendLineOnlyFileDocumentElement();
+ // Now quickly read through the underlying file, building an Element for each line.
+ long start_offset = 0L;
+ file.seek(start_offset);
+ int character = -1;
+ while((character = file.read()) != -1) {
+ if(character == StaticStrings.NEW_LINE_CHAR) {
+ long end_offset = file.getFilePointer();
+ Element child_element = new AppendLineOnlyFileDocumentElement(start_offset, end_offset);
+ root_element.add(child_element);
+ child_element = null;
+ start_offset = end_offset;
+ }
+ }
+ // If there we no lines found, and we are a build log, then append the file header.
+ if(build_log && root_element.getElementCount() == 0) {
+ appendLine(GLI_HEADER_STR);
+ }
+ }
+ catch (Exception error) {
+ DebugStream.printStackTrace(error);
+ }
+ }
+
+ /** Adds a document listener for notification of any changes.
+ * @param listener the new DocumentListener to keep track of
+ */
+ public void addDocumentListener(DocumentListener listener) {
+ ///ystem.err.println("addDocumentListener(" + listener + ")");
+ listeners_list.add(DocumentListener.class, listener);
+ }
+
+ /** Append some content after the document.
+ * @param str the String to add as a new line in the document
+ * @see org.greenstone.gatherer.util.AppendLineOnlyFileDocument.AppendLineOnlyFileDocumentElement
+ * @see org.greenstone.gatherer.util.AppendLineOnlyFileDocument.AppendLineOnlyFileDocumentEvent
+ * @see org.greenstone.gatherer.util.AppendLineOnlyFileDocument.WriterThread#queue
+ */
+ public void appendLine(String str) {
+ // Ensure the string ends in a newline
+ try {
+ if (!str.endsWith("\n")) {
+ str = str + "\n";
+ }
+ int str_length = str.getBytes("UTF-8").length;
+ long start_offset = length;
+ long end_offset = start_offset + (long) str_length;
+ length = length + str_length;
+ // Create a new element to represent this line
+ AppendLineOnlyFileDocumentElement new_line_element = new AppendLineOnlyFileDocumentElement(start_offset, end_offset, str);
+ root_element.add(new_line_element);
+ // Queue the content to be written.
+ writer.queue(new_line_element);
+ // Now fire an event so everyone knows the content has changed.
+ DocumentEvent event = new AppendLineOnlyFileDocumentEvent(new_line_element, (int)start_offset, str_length, DocumentEvent.EventType.INSERT);
+ Object[] listeners = listeners_list.getListenerList();
+ for (int i = listeners.length - 2; i >= 0; i = i - 2) {
+ if (listeners[i] == DocumentListener.class) {
+ ((DocumentListener)listeners[i+1]).insertUpdate(event);
+ }
+ }
+ listeners = null;
+ event = null;
+ new_line_element = null;
+ }
+ catch(Exception error) {
+ DebugStream.printStackTrace(error);
+ }
+ }
+
+ /** Returns a position that will track change as the document is altered.
+ * @param offs the position offset as an int
+ * @return the Position to be monitored
+ * @see org.greenstone.gatherer.util.AppendLineOnlyFileDocument.AppendLineOnlyFileDocumentPosition
+ */
+ public Position createPosition(int offs) {
+ return new AppendLineOnlyFileDocumentPosition(offs);
+ }
+
+ /** ensure the current build_log file has finished transferring from memory to disk, and that the random access
+ * file is properly closed when a collection is closed. Else we can't delete the just-closed collection since the
+ * build_log file resource is still kept open. */
+ public void close() {
+ if(writer == null) { // closed already
+ return;
+ }
+ // get rid of the writer and then close the file
+ setExit();
+ writer = null;
+ try {
+ file.close();
+ } catch(Exception exception) {
+ DebugStream.println("Exception in AppendLineOnlyFileDocument.close() - Unable to close internal file");
+ DebugStream.printStackTrace(exception);
+ }
+ }
+
+ /** The destructor is implemented to ensure the current log file has finished transferring from memory to disk, and that the random access file is properly closed, before GLI exits.
+ * @see org.greenstone.gatherer.util.AppendLineOnlyFileDocument.WriterThread#finish
+ */
+ public void destroy() {
+ try {
+ if(writer != null) {
+ writer.finish();
+ writer = null;
+ file.close();
+ }
+ }
+ catch(Exception exception) {
+ DebugStream.println("Exception in AppendLineOnlyFileDocument.destroy() - unexpected");
+ DebugStream.printStackTrace(exception);
+ }
+ }
+
+ /** Gets the default root element for the document model.
+ * @return the root Element of this document
+ */
+ public Element getDefaultRootElement() {
+ return root_element;
+ }
+
+ /** Returns the length of the data.
+ * @return the length as an int (not a long)
+ */
+ public int getLength() {
+ return (int) length;
+ }
+
+ /** A version of get length which returns the offset to the start of the last line in the document.
+ * @return the offset length as an int
+ * @see org.greenstone.gatherer.util.AppendLineOnlyFileDocument.AppendLineOnlyFileDocumentElement
+ */
+ public int getLengthToNearestLine() {
+ AppendLineOnlyFileDocumentElement last_element = (AppendLineOnlyFileDocumentElement)root_element.getElement(root_element.getElementCount() - 1);
+ if(last_element != null ) {
+ return last_element.getStartOffset();
+ }
+ else {
+ return (int) length; // The best we can do.
+ }
+ }
+
+ /** Retrieve a property that has previously been set for this document.
+ * @param key the key String under which the vaue is stored
+ * @return the value which is also a String, or null if no such value
+ */
+ public Object getProperty(Object key) {
+ return properties.get(key);
+ }
+
+ /** Determine if the writer thread is still running, ie a log file which is currently open for writing
+ * @return true if the writer is still active, false otherwise
+ * @see org.greenstone.gatherer.util.AppendLineOnlyFileDocument.WriterThread#isStillWriting
+ */
+ public boolean isStillWriting() {
+ return writer.isStillWriting();
+ }
+
+ /** Gets a sequence of text from the document.
+ * @param offset the starting offset for the fragment of text required, as an int
+ * @param l the length of the text, also as an int
+ * @see org.greenstone.gatherer.util.AppendLineOnlyFileDocument.AppendLineOnlyFileDocumentElement
+ * @see org.greenstone.gatherer.util.StaticStrings#EMPTY_STR
+ */
+ public String getText(int offset, int l)
+ throws BadLocationException {
+
+ if (l == 0) {
+ return "";
+ }
+
+ try {
+ return read((long) offset, l);
+ }
+ catch (Exception ex) {
+ }
+
+ return "";
+
+// // System.err.println("AppendLineOnlyFileDocument::getText() request...");
+// String request = "getText(" + offset + ", " + l + ")";
+// ///ystem.err.println(request);
+// String text = null;
+// if(text == null || text.length() < l) {
+// try {
+// int file_length = (int) file.length();
+// ///ystem.err.println("file_length = " + file_length + ", length = " + length);
+// if(l == 0) {
+// text = StaticStrings.EMPTY_STR;
+// }
+// else if(0 <= offset && offset < length && (offset + l) <= length) {
+// if(offset < file_length) {
+// text = read((long)offset, l);
+// if(text.length() != l) {
+// ///ystem.err.println("Asked for " + l + " characters of text. But only read " + text.length());
+// throw new BadLocationException("AppendLineOnlyDocument.getText(" + offset + ", " + l + ")", offset);
+// }
+// }
+// else {
+// int index = root_element.getElementIndex(offset);
+// if(index < root_element.getElementCount()) {
+// AppendLineOnlyFileDocumentElement element = (AppendLineOnlyFileDocumentElement) root_element.getElement(index);
+// text = element.getContent();
+// }
+// else {
+// ///ystem.err.println("Index is " + index + " but there are only " + root_element.getElementCount() + " children.");
+// throw new BadLocationException("AppendLineOnlyDocument.getText(" + offset + ", " + l + ")", offset);
+// }
+// }
+
+// }
+// }
+// catch (IOException error) {
+// DebugStream.printStackTrace(error);
+// }
+// if(text == null) {
+// ///ystem.err.println("Text is null.");
+// throw new BadLocationException("AppendLineOnlyDocument.getText(" + offset + ", " + l + ")", offset);
+// }
+// }
+// request = null;
+// return text;
+ }
+
+ /** Fetches the text contained within the given portion of the document.
+ * @param offset the starting offset of the desired text fragment, as an int
+ * @param length the length of the text also as an int
+ * @param txt the Segment into which the text fragment should be placed
+ */
+ public void getText(int offset, int length, Segment txt)
+ throws javax.swing.text.BadLocationException {
+ String str = getText(offset, length);
+ txt.array = str.toCharArray();
+ txt.count = str.length();
+ txt.offset = 0;
+ str = null;
+ }
+
+ /** Set a property for this document.
+ * @param key the key to store the value under, as a String
+ * @param value the property value itself also as a String
+ */
+ public void putProperty(Object key, Object value) {
+ properties.put(key, value);
+ }
+
+ /** Determine if this document is ready by testing if it has an open descriptor to the random access file.
+ * @return true if the file exists thus the document is ready, false otherwise
+ */
+ public boolean ready() {
+ return (file != null);
+ }
+
+ /** Removes a document listener.
+ * @param listener the Document we wish to remove
+ */
+ public void removeDocumentListener(DocumentListener listener) {
+ listeners_list.remove(DocumentListener.class, listener);
+ }
+
+ /** Whenever the user requests a change of the loaded log, this method is called to ensure the previous log has been completely written to file. Note that this only garuentees that text fragments currently queued for writing will be correctly written - while the behaviour is undefined for text fragments added after this call (some may write depending on several race conditions).
+ * @see org.greenstone.gatherer.util.AppendLineOnlyFileDocument.WriterThread#exit
+ * @see org.greenstone.gatherer.util.AppendLineOnlyFileDocument.WriterThread#finish
+ */
+ public void setExit() {
+ writer.exit();
+ writer.finish();
+ }
+
+ /** Establish the current owner of this document.
+ * @param owner the current AppendLineOnlyFileDocumentOwner
+ */
+ public void setOwner(AppendLineOnlyFileDocumentOwner owner) {
+ this.owner = owner;
+ }
+
+ /** To record the final state of the logging process we reserve a single character at the start of the file, and then replace it when the build process is complete.
+ * @param character the final status char to replace the X at the start of the log
+ */
+ public synchronized void setSpecialCharacter(char character) {
+ if(build_log) {
+ try {
+ file.seek(0L);
+ file.write((int)character);
+ }
+ catch (Exception error) {
+ DebugStream.printStackTrace(error);
+ }
+ }
+ }
+
+ /** Request a text representation of this document which currently returns the filename.
+ * @return the text as a String
+ */
+ public String toString() {
+ return filename;
+ }
+
+ /** This method reads a fragment of text from the underlying random access file. It is synchronized so that the file isn't being written to at the same time.
+ * @param start_offset the offset within the file to set the read pointer at, as a long
+ * @param l the number of characters to read as an int
+ */
+ private synchronized String read(long start_offset, int l)
+ throws IOException {
+ //print("read(" + start_offset + ", " + l + ")... ");
+ byte[] buffer = new byte[l];
+ file.seek(start_offset);
+ int result = file.read(buffer, 0, l);
+ return new String(buffer, "UTF-8");
+ //print("read() complete");
+ }
+
+ /** This methods writes a fragment of text to the underlying random access file. It is synchronized so that the file isn't being read from at the same time.
+ * @param start_offset the offset within the file to set the write pointer at, as a long
+ * @param end_offset the final offset of the new text fragment within the file, as a long. This is used to extend the file to be the correct length
+ * @param str the String to be written
+ * @param l the number of characters from str to be written, as an int
+ */
+ private synchronized void write(long start_offset, long end_offset, String str, int l)
+ throws IOException {
+ //print("write(" + start_offset + ", " + end_offset + ", " + str + ", " + l + ")");
+ file.setLength(end_offset);
+ file.seek(start_offset);
+ file.write(str.getBytes("UTF-8"), 0, l);
+ //print("write() complete");
+ }
+
+ /** This class provides the building-block for the document model. Each element is a line in the document, or has no content but contains several child elements. Due to the basic nature of what we are modelling only the root node has children. */
+ private class AppendLineOnlyFileDocumentElement
+ extends ArrayList
+ implements Element {
+ /** Our parent Element. */
+ private Element parent;
+ /** The offset to the end of our fragment(s) of text, in respect to the entire document. */
+ private long end;
+ /** The offset to the start of our fragment(s) of text, in respect to the entire document. */
+ private long start;
+ /** If we haven't been written to file yet, this contains the text fragment itself. */
+ private String content;
+
+ /** Construct a new root element, which can have no content, but calculates its start and end from its children. */
+ public AppendLineOnlyFileDocumentElement() {
+ super();
+ this.end = 0L;
+ this.parent = null;
+ this.start = 0L;
+ }
+
+ /** Construct a new element, whose content is found in bytes start to end - 1 within the random access file backing this document.
+ * @param start the starting offset as a long
+ * @param end the ending offset as a long
+ */
+ public AppendLineOnlyFileDocumentElement(long start, long end) {
+ super();
+ this.end = end;
+ this.parent = null;
+ this.start = start;
+ }
+
+ /** Construct a new element, whose content is provided, but should at some later time be written to bytes start to end - 1 in the random access file backing this document.
+ * @param start the starting offset as a long
+ * @param end the ending offset as a long
+ * @param content the text fragment String which we represent in the model
+ */
+ public AppendLineOnlyFileDocumentElement(long start, long end, String content) {
+ super();
+ this.content = content;
+ this.end = end;
+ this.parent = null;
+ this.start = start;
+ }
+
+ /** Add the given node as one of our children.
+ * @param child the new child Element
+ */
+ public void add(Element child) {
+ super.add(child);
+ ((AppendLineOnlyFileDocumentElement)child).setParent(this);
+ }
+
+ /** Having written the content to file this method removes it from the element. */
+ public void clearContent() {
+ content = null;
+ }
+
+ /** This document does not allow content markup.
+ * @return always returns null
+ */
+ public AttributeSet getAttributes() {
+ return null;
+ }
+
+ /** Retrieve the current content of this element, which may be the text fragment this element represents.
+ * @return either the text fragment as a String, or null if the fragment has already been written to disk
+ */
+ public String getContent() {
+ return content;
+ }
+
+ /** Fetches the document associated with this element.
+ * @return the AppendLineOnlyDocument containing this element
+ * @see org.greenstone.gatherer.util.AppendLineOnlyFileDocument
+ */
+ public Document getDocument() {
+ return AppendLineOnlyFileDocument.this;
+ }
+
+ /** Fetches the child element at the given index.
+ * @param index the int index of the element to retrieve
+ * @return the requested Element
+ */
+ public Element getElement(int index) {
+ Element element;
+ if(0 <= index && index < size()) {
+ element = (Element) get(index);
+ }
+ else {
+ throw new IndexOutOfBoundsException("AppendLineOnlyDocument.AppendLineOnlyFileDocumentElement.getElement(" + index + ")");
+ }
+ return element;
+ }
+
+ /** Gets the number of child elements contained by this element.
+ * @return an int
+ */
+ public int getElementCount() {
+ return size();
+ }
+
+ /** Gets the child element index closest to the given offset.
+ * @param offset the offset within the text of the document, which due to the way the model is built must fll within the bounds of one of our child nodes
+ * @return the closest index as an int
+ */
+ public int getElementIndex(int offset) {
+ int index = -1;
+ if(parent != null) {
+ index = -1;
+ }
+ else if(offset < 0) {
+ index = 0;
+ }
+ else if(offset >= length) {
+ index = size() - 1;
+ }
+ else {
+ int size = size();
+ for(int i = 0; index == -1 && i < size; i++) {
+ Element child = (Element) get(i);
+ if(child.getStartOffset() <= offset && offset < child.getEndOffset()) {
+ index = i;
+ }
+ child = null;
+ }
+ }
+ return index;
+ }
+
+ /** Fetches the offset from the beginning of the document that this element ends at.
+ * @return the offset as an int
+ */
+ public int getEndOffset() {
+ if(parent != null) {
+ return (int) end;
+ }
+ // Return the Documents length.
+ else {
+ return (int) length;
+ }
+ }
+
+ /** This method retrieves the name of the element, however names are not important in this document so the name is always an empty string.
+ * @return an empty String
+ * @see org.greenstone.gatherer.util.StaticStrings#EMPTY_STR
+ */
+ public String getName() {
+ return StaticStrings.EMPTY_STR;
+ }
+
+ /** Fetches the parent element.
+ * @return the parent Element
+ */
+ public Element getParentElement() {
+ return parent;
+ }
+
+ /** Fetches the offset from the beginning of the document that this element begins at.
+ * @return the offset as an int
+ */
+ public int getStartOffset() {
+ if(parent != null) {
+ return (int) start;
+ }
+ else {
+ return 0;
+ }
+ }
+
+ /** Since this is a very simple model, only the root node can have children. All the children are leaves.
+ * @return true if this is the root node, false otherwise
+ */
+ public boolean isLeaf() {
+ return (parent != null);
+ }
+
+ /** Establish the parent of this element
+ * @param parent the parent Element
+ */
+ public void setParent(Element parent) {
+ this.parent = parent;
+ }
+ }
+ // AppendLineOnlyFileDocumentElement
+
+ /** This event is created to encapsulate the details of some change to the document. */
+ private class AppendLineOnlyFileDocumentEvent
+ implements DocumentEvent {
+ /** The type of change event. */
+ private DocumentEvent.EventType type;
+ /** The element this change occured to, or in. */
+ private AppendLineOnlyFileDocumentElement element;
+ /** Another encapsulating class which contains further detail about the change itself. */
+ private AppendLineOnlyFileDocumentElementChange element_change;
+ /** The length of the text fragment affected by the change. */
+ private int len;
+ /** The offset within the document of the start of the change. */
+ private int offset;
+
+ /** Construct a new AppendLineOnlyFileDocumentEvent given the pertinant details.
+ * @param element the AppendLineOnlyFileDocumentElement affected by this change
+ * @param offset the offset within the document of the start of the change
+ * @param len the length of the text fragment affected by the change
+ * @param type the type of change event
+ */
+ public AppendLineOnlyFileDocumentEvent(AppendLineOnlyFileDocumentElement element, int offset, int len, DocumentEvent.EventType type) {
+ this.element = element;
+ this.element_change = null;
+ this.len = len;
+ this.offset = offset;
+ this.type = type;
+ }
+
+ /** Retrieve the docment which generated this event.
+ * @return the Document
+ * @see org.greenstone.gatherer.util.AppendLineOnlyFileDocument
+ */
+ public Document getDocument() {
+ return AppendLineOnlyFileDocument.this;
+ }
+
+ /** Retrieve the length of the change.
+ * @return the length as an int
+ */
+ public int getLength() {
+ return len;
+ }
+
+ /** Retrieve the start offset of the change.
+ * @return the offset as an int
+ */
+ public int getOffset() {
+ return offset;
+ }
+
+ /** Retrieve the type of change.
+ * @return the type as a DocumentEvent.EventType
+ */
+ public DocumentEvent.EventType getType() {
+ return type;
+ }
+
+ /** Retrieve the element change object which further describes the changes. Of course given that we only allow the appending of new lines there really is only one type of event.
+ * @param elem implementation side-effect
+ * @return a DocumentEvent.ElementChange
+ * @see org.greenstone.gatherer.util.AppendLineOnlyFileDocument.AppendLineOnlyFileDocumentEvent.AppendLineOnlyFileDocumentElementChange
+ */
+ public DocumentEvent.ElementChange getChange(Element elem) {
+ if(element_change == null) {
+ element_change = new AppendLineOnlyFileDocumentElementChange();
+ }
+ return element_change;
+ }
+
+ /** A pretty unexciting implementation of the document element change class. This usually describes the myriad of possible change events that can occur on a document. However we only allow line appends so there is really only one type of change. */
+ private class AppendLineOnlyFileDocumentElementChange
+ implements DocumentEvent.ElementChange {
+ /** This array always contains the one Element that was appended. */
+ private Element[] children_added;
+ /** This Element array is always empty. */
+ private Element[] children_removed;
+ /** The index of the affected element within its parents children. */
+ private int index;
+ /** Construct a new AppendLineOnlyFileDocumentElementChange with the default 'append line' details. */
+ public AppendLineOnlyFileDocumentElementChange() {
+ children_added = new Element[1];
+ children_added[0] = element;
+ children_removed = new Element[0];
+ index = root_element.indexOf(element);
+ }
+
+ /** Gets the child element that was appended to the parent element.
+ * @return an Element[] containing the added element
+ */
+ public Element[] getChildrenAdded() {
+ return children_added;
+ }
+
+ /** This model does not allow elements to be removed.
+ * @return an Element[] containing nothing
+ */
+ public Element[] getChildrenRemoved() {
+ return children_removed;
+ }
+
+ /** Returns the root element, as our document structure is only two layers deep.
+ * @return the root Element
+ */
+ public Element getElement() {
+ return root_element;
+ }
+
+ /** Fetches the index within the element represented.
+ * @return an int specifying the index of change within the root element
+ */
+ public int getIndex() {
+ return index;
+ }
+ }
+ // AppendLineOnlyFileDocumentElementChange
+ }
+ // AppendLineOnlyFileDocumentEvent
+
+ /** This class denoted a position within our document by an offset. Quite possibly the last interesting class I've ever had to deal with, but you have to implement it so here goes... */
+ private class AppendLineOnlyFileDocumentPosition
+ implements Position {
+ /** The offset within our document. */
+ private int offset;
+ /** Construct a new position given an offset.
+ * @param offset the offset as an int
+ */
+ public AppendLineOnlyFileDocumentPosition(int offset) {
+ this.offset = offset;
+ }
+ /** Retrieve the offset of this position.
+ * @return the offset as an int
+ */
+ public int getOffset() {
+ return offset;
+ }
+ }
+ // AppendLineOnlyFileDocumentPosition
+
+ /** This thread is responsible to writing the in memory text fragment contents of the document elements out to the random access file. The writing proceedure is implemented this way so that the gui doesn't take the performance hit of the IO. */
+ private class WriterThread
+ extends Thread {
+ /** When true was are being told to assume the contents of the queue are now static, and once completed the queue will be empty (and not expecting any new jobs). Used to finish the queue before the GLI exits. */
+ private boolean empty_queue;
+ /** Setting this flag to true tells the writer to stop regardless of whether there are items left on the queue. */
+ private boolean exit;
+ /** When the writer thread is busy writing content to the file this flag is set to true. */
+ private boolean running;
+ /** The fragment queue as a Vector (for thread reasons). */
+ private Vector queue;
+ /** Construct a new writer thread with an initially empty queue. */
+ public WriterThread() {
+ super("WriterThread");
+ empty_queue = false;
+ exit = false;
+ queue = new Vector();
+ }
+
+ /** This method is called to ask the writer thread to die after it finishes any current write proceceedure. */
+ public synchronized void exit() {
+ //print("WriterThread.exit() start");
+ exit = true;
+ notify();
+ //print("WriterThread.exit() complete");
+ }
+
+ /** When this returns there are no jobs waiting in the queue. */
+ public void finish() {
+ if(!queue.isEmpty() && running) {
+ ///ystem.err.println("Somethings gone wrong, as there are still " + queue.size() + " items to be written out and yet the thread seems to be waiting.");
+ empty_queue = true;
+ run();
+ }
+ }
+
+ /** Determine if the writer is currently running.
+ * @return true if it is, false if it has been exited or if it was never running in the first place (an old log)
+ */
+ public boolean isStillWriting() {
+ return running;
+ }
+
+ /** The run method of this thread checks if there are any document elements queued to be written to file, and then writes them as necessary.
+ * @see org.greenstone.gatherer.util.AppendLineOnlyFileDocument
+ * @see org.greenstone.gatherer.util.AppendLineOnlyFileDocument.AppendLineOnlyFileDocumentElement
+ */
+ public void run() {
+ exit = false;
+ running = true;
+ while(!exit) {
+ if(!queue.isEmpty()) {
+ AppendLineOnlyFileDocumentElement element = (AppendLineOnlyFileDocumentElement) queue.remove(0);
+ // Write the content to file.
+ String content = element.getContent();
+ if(content != null) {
+ try {
+ write(element.getStartOffset(), element.getEndOffset(), content, content.getBytes("UTF-8").length);
+ }
+ catch(Exception error) {
+ DebugStream.printStackTrace(error);
+ }
+ element.clearContent();
+ }
+ }
+ else if(empty_queue) {
+ exit = true;
+ }
+ else {
+ synchronized(this) {
+ try {
+ print("WriterThread.wait() start");
+ wait();
+ }
+ catch(Exception exception) {
+ }
+ print("WriterThread.wait() complete");
+ }
+ }
+ }
+ print("WriterThread completely finished. Exiting.");
+ running = false;
+ if(owner != null) {
+ owner.remove(AppendLineOnlyFileDocument.this);
+ }
+ }
+
+ /** Enqueue a document element to have its text content written to file.
+ * @param element the Element needing its content written out
+ * @see org.greenstone.gatherer.util.AppendLineOnlyFileDocument.AppendLineOnlyFileDocumentElement#getContent
+ */
+ public synchronized void queue(Element element) {
+ print("WriterThread.queue(): " + ((AppendLineOnlyFileDocumentElement)element).getContent());
+ queue.add(element);
+ notify();
+ }
+ }
+ // WriterThread
+
+ // ***** METHODS WE ARE NOW IGNORING BECAUSE WE ARE VIRTUALLY READ-ONLY *****
+
+ /** Not implemented. Add a new undo listener to this document.
+ * @param listener UndoableEditListener
+ */
+ public void addUndoableEditListener(UndoableEditListener listener) {}
+
+ /** Not implemented. Get the last position in the document.
+ * @return null
+ */
+ public Position getEndPosition() { return null; }
+
+ /** Not implemented. Gets all root elements defined.
+ * @return null
+ */
+ public Element[] getRootElements() { return null; }
+
+ /** Not implemented. Retrieve the first position in the document.
+ * @return null
+ */
+ public Position getStartPosition() { return null; }
+
+ /** Not implemented. Insert a text fragment into our document.
+ * @param offset int
+ * @param str String
+ * @param a AttributeSet
+ */
+ public void insertString(int offset, String str, AttributeSet a) {}
+
+ /** Not implemented. Removes some content from the document.
+ * @param offs int
+ * @param len int
+ */
+ public void remove(int offs, int len) {}
+
+ /** Not implemented. Removes an undo listener.
+ * @param listener UndoableEditListener
+ */
+ public void removeUndoableEditListener(UndoableEditListener listener) {}
+
+ /** Not implemented. Renders a runnable apparently - whatever that means.
+ * @param r Runnable
+ */
+ public void render(Runnable r) {}
+
+ // DEBUG ONLY
+
+ /** Print a message to the GLI debug file stream.
+ * @param message the message to be written as a String
+ */
+ static synchronized public void print(String message) {
+ if (message.endsWith("\n")) {
+ DebugStream.print(message);
+ }
+ else {
+ DebugStream.println(message);
+ }
+ }
+
+ /** Create a test document with the ability to add new lines of text as necessary.
+ * @param args the initial starting arguments as a String array
+ * @see org.greenstone.gatherer.gui.GLIButton
+ * @see org.greenstone.gatherer.util.AppendLineOnlyFileDocument
+ * @see org.greenstone.gatherer.util.AppendLineOnlyFileDocument.ReadButtonListener
+ */
+ static public void main(String[] args) {
+ JFrame frame = new JFrame("AppendLineOnlyFileDocument Test");
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ frame.setSize(640,480);
+ JPanel content = (JPanel) frame.getContentPane();
+
+ //PlainDocument document = new PlainDocument();
+ //document.setAsynchronousLoadPriority(-1);
+ final AppendLineOnlyFileDocument document = new AppendLineOnlyFileDocument("temp.txt");
+
+ final JTextArea text_area = new JTextArea(document);
+
+ JButton read_button = new GLIButton("Read Huge File");
+ read_button.addActionListener(new ReadButtonListener(document));
+ content.setLayout(new BorderLayout());
+ content.add(new JScrollPane(text_area), BorderLayout.CENTER);
+ content.add(read_button, BorderLayout.SOUTH);
+
+ frame.setVisible(true);
+ }
+
+ /** Listens for actions on the read button, and if detected creates a new ReadTask to test the document.
+ * @author John Thompson, Greenstone Project, New Zealand Digital Library, University of Waikato
+ * @version 2.41 final
+ */
+ static private class ReadButtonListener
+ implements ActionListener {
+ /** The AppendLineOnlyFileDocument object we are testing. */
+ private AppendLineOnlyFileDocument document;
+
+ /** All the constructor does is take a copy of the document to test.
+ * @param document the Document
+ */
+ public ReadButtonListener(AppendLineOnlyFileDocument document) {
+ this.document = document;
+ }
+
+ /** When the button is clicked this method is called to create the new task and run it.
+ * @param event an ActionEvent containing further information about the button click
+ * @see org.greenstone.gatherer.util.AppendLineOnlyFileDocument.ReadTask
+ */
+ public void actionPerformed(ActionEvent event) {
+ Thread task = new ReadTask(document);
+ task.start();
+ }
+ }
+
+ /** This threaded task opens a large document, aptly named 'big.txt', and then bombards the document object we are testing with lines from the file. This file should be several megs (such as Alice or TREC) to fully test functionality, thread conditions etc.
+ * @author John Thompson, Greenstone Project, New Zealand Digital Library, University of Waikato
+ * @version 2.41 final
+ */
+ static private class ReadTask
+ extends Thread {
+ /** The AppendLineOnlyFileDocument object we are testing. */
+ private AppendLineOnlyFileDocument document;
+
+ /** Construct a new task which will perform testing on the given document.
+ * @param document the AppendLineOnlyFileDocument to append lines to
+ */
+ public ReadTask(AppendLineOnlyFileDocument document) {
+ super("LoadHugeFileThread");
+ this.document = document;
+ }
+
+ /** The runable part of this class opens the file, then reads it in a line at a time, appending these lines to the ever growing document. */
+ public void run() {
+ // Load the specified document
+ try {
+ BufferedReader in = new BufferedReader(new FileReader(new File("big.txt")));
+ String line;
+
+ while ((line = in.readLine()) != null) {
+ document.appendLine(line);
+ try {
+ // Wait a random ammount of time.
+ synchronized(this) {
+ //print("LoadHugeFileThread.wait() start");
+ wait(100);
+ //print("LoadHugeFileThread.wait() complete");
+ }
+ }
+ catch(Exception error) {
+ error.printStackTrace();
+ }
+ }
+ in.close();
+ }
+ catch (Exception error) {
+ error.printStackTrace();
+ }
+ }
+ }
+ // ReadTask
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/AppendLineOnlyFileDocumentOwner.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/AppendLineOnlyFileDocumentOwner.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/AppendLineOnlyFileDocumentOwner.java (revision 31635)
@@ -0,0 +1,46 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Greenstone Librarian Interface (GLI) application,
+ * part of the Greenstone digital library software suite from the New
+ * Zealand Digital Library Project at the University of Waikato,
+ * New Zealand.
+ *
+ * Author: John Thompson
+ * Greenstone Project, New Zealand Digital Library
+ * University of Waikato
+ * http://www.nzdl.org
+ *
+ * Copyright (C) 2004 New Zealand Digital Library, University of Waikato
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.util;
+
+
+/** Any class that wants to act as the owner of an AppendLineOnlyFileDocument has to implement this.
+ * @author John Thompson, Greenstone Project, New Zealand Digital Library, University of Waikato
+ * @version 2.41 final
+ */
+public interface AppendLineOnlyFileDocumentOwner {
+ /** Remove the given document from the owner. The is called by the document itself when it no longer needs a hanging reference in the owner class.
+ * @param document the AppendLineOnlyFileDocument to remove
+ */
+ public void remove(AppendLineOnlyFileDocument document);
+}
+
+
+
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/ArrayTools.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/ArrayTools.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/ArrayTools.java (revision 31635)
@@ -0,0 +1,341 @@
+/* GPL_HEADER */
+package org.greenstone.gatherer.util;
+
+/**************************************************************************************
+ * Title: Gatherer
+ * Description: The Gatherer: a tool for gathering and enriching a digital collection.
+ * Company: The University of Waikato
+ * Written: / /01
+ * Revised: 16/08/02 Improved
+ **************************************************************************************/
+import java.awt.*;
+import java.io.*;
+import java.util.*;
+import javax.swing.filechooser.*;
+import org.w3c.dom.*;
+
+/** This utility class contains a series of static methods for common array type manipulations including appending, casting and changing between containers.
+ * @author John Thompson
+ * @version 2.3
+ */
+public class ArrayTools {
+
+ /** Append a Component onto an array of Components. */
+ static public Component[] add(Component[] a, Component b) {
+ Component[] c = null;
+ if(a != null && b != null) {
+ c = new Component[a.length + 1];
+ System.arraycopy(a, 0, c, 0, a.length);
+ c[c.length - 1] = b;
+ }
+ else if(a == null && b != null) {
+ c = new Component[1];
+ c[0] = b;
+ }
+ else if(a != null && b == null) {
+ c = a;
+ }
+ return c;
+ }
+ /** This method efficiently appends a new element onto the end of a element array.
+ * @param a The initial Element[] .
+ * @param b The new Element .
+ * @return An Element[] containing a followed by b.
+ */
+ static public Element[] add(Element a[], Element b) {
+ Element[] c = null;
+ if(a != null && b != null) {
+ c = new Element[a.length + 1];
+ System.arraycopy(a, 0, c, 0, a.length);
+ c[c.length - 1] = b;
+ }
+ else if(a == null && b != null) {
+ c = new Element[1];
+ c[0] = b;
+ }
+ else if(a != null && b == null) {
+ c = a;
+ }
+ return c;
+ }
+ /** This method efficently appends one element array onto the end of another.
+ * @param a The initial Element[] .
+ * @param b The Element[] to append.
+ * @return An Element[] containing a followed by b.
+ */
+ static public Element[] add(Element a[], Element b[]) {
+ Element[] c = null;
+ if(a != null && b != null) {
+ c = new Element[a.length + b.length];
+ System.arraycopy(a, 0, c, 0, a.length);
+ System.arraycopy(b, 0, c, a.length, b.length);
+ }
+ else if(a == null && b != null) {
+ c = b;
+ }
+ else if(a != null && b == null) {
+ c = a;
+ }
+ return c;
+ }
+ /** This method efficiently appends a new file onto the end of a file array.
+ * @param a The initial File[] .
+ * @param b The new File .
+ * @return A File[] containing a followed by b.
+ */
+ static public File[] add(File a[], File b) {
+ File[] c = null;
+ if(a != null && b != null) {
+ c = new File[a.length + 1];
+ System.arraycopy(a, 0, c, 0, a.length);
+ c[c.length - 1] = b;
+ }
+ else if(a == null && b != null) {
+ c = new File[1];
+ c[0] = b;
+ }
+ else if(a != null && b == null) {
+ c = a;
+ }
+ return c;
+ }
+ /** This method efficently appends one file array onto the end of another.
+ * @param a The initial File[] .
+ * @param b The File[] to append.
+ * @return A File[] containing a followed by b.
+ */
+ static public File[] add(File a[], File b[]) {
+ File[] c = null;
+ if(a != null && b != null) {
+ c = new File[a.length + b.length];
+ System.arraycopy(a, 0, c, 0, a.length);
+ System.arraycopy(b, 0, c, a.length, b.length);
+ }
+ else if(a == null && b != null) {
+ c = b;
+ }
+ else if(a != null && b == null) {
+ c = a;
+ }
+ return c;
+ }
+
+
+ /** This method efficiently appends a new node onto the end of a node array.
+ * @param a The initial Node[] .
+ * @param b The new Node .
+ * @return A Node[] containing a followed by b.
+ */
+ static public Node[] add(Node a[], Node b) {
+ Node[] c = null;
+ if(a != null && b != null) {
+ c = new Node[a.length + 1];
+ System.arraycopy(a, 0, c, 0, a.length);
+ c[c.length - 1] = b;
+ }
+ else if(a == null && b != null) {
+ c = new Node[1];
+ c[0] = b;
+ }
+ else if(a != null && b == null) {
+ c = a;
+ }
+ return c;
+ }
+ /** This method efficently appends one node array onto the end of another.
+ * @param a The initial Node[] .
+ * @param b The Node[] to append.
+ * @return A Node[] containing a followed by b.
+ */
+ static public Node[] add(Node a[], Node b[]) {
+ Node[] c = null;
+ if(a != null && b != null) {
+ c = new Node[a.length + b.length];
+ System.arraycopy(a, 0, c, 0, a.length);
+ System.arraycopy(b, 0, c, a.length, b.length);
+ }
+ else if(a == null && b != null) {
+ c = b;
+ }
+ else if(a != null && b == null) {
+ c = a;
+ }
+ return c;
+ }
+
+ /** This method efficiently appends a new string onto the end of a string array.
+ * @param a The initial String[] .
+ * @param b The new String .
+ * @return A String[] containing a followed by b.
+ */
+ static public String[] add(String a[], String b) {
+ String[] c = null;
+ if(a != null && b != null) {
+ c = new String[a.length + 1];
+ System.arraycopy(a, 0, c, 0, a.length);
+ c[c.length - 1] = b;
+ }
+ else if(a == null && b != null) {
+ c = new String[1];
+ c[0] = b;
+ }
+ else if(a != null && b == null) {
+ c = a;
+ }
+ return c;
+ }
+ /** This method efficently appends one string array onto the end of another.
+ * @param a The initial String[] .
+ * @param b The String[] to append.
+ * @return A String[] containing a followed by b.
+ */
+ static public String[] add(String a[], String b[]) {
+ String[] c = null;
+ if(a != null && b != null) {
+ c = new String[a.length + b.length];
+ System.arraycopy(a, 0, c, 0, a.length);
+ System.arraycopy(b, 0, c, a.length, b.length);
+ }
+ else if(a == null && b != null) {
+ c = b;
+ }
+ else if(a != null && b == null) {
+ c = a;
+ }
+ return c;
+ }
+ /** This method takes an array list and creates a string array.
+ * @param a An ArrayList containing hopefully String or else this will fail.
+ * @return A String[] or null if the array could not be created.
+ */
+ static public String[] arrayListToStringArray(ArrayList a) {
+ String array[] = new String[a.size()];
+ for(int i = 0; i < array.length; i++) {
+ array[i] = (String)a.get(i);
+ }
+ return array;
+ }
+
+ static public File[] filter(File[] files, String pattern, boolean exclude) {
+ int write_ptr = 0;
+ ///ystem.err.println("Filtering by '" + pattern + "', Exclude? " + exclude + " :");
+ for(int read_ptr = 0; read_ptr < files.length; read_ptr++) {
+ File current = files[read_ptr];
+ files[write_ptr] = current;
+ ///ystem.err.print("Testing " + current.getName() + " -> ");
+ // Determine whether we move the write pointer or not.
+ if(current.getName().toLowerCase().matches(pattern)) {
+ if(!exclude) {
+ ///ystem.err.println("Match, Exclude.");
+ write_ptr++;
+ }
+ else {
+ ///ystem.err.println("Match, Include.");
+ }
+ }
+ else {
+ // You can't exclude folders with an inclusion filter!
+ if(exclude || current.isDirectory()) {
+ ///ystem.err.println("Nonmatch, Exclude.");
+ write_ptr++;
+ }
+ else {
+ ///ystem.err.println("Nonmatch, Include.");
+ }
+ }
+ }
+ File[] result = new File[write_ptr];
+ System.arraycopy(files, 0, result, 0, result.length);
+ pattern = null;
+ files = null;
+ return result;
+ }
+
+
+ /** Transforms an Object array into a single string of the form '[o1,o2,...,on]'.
+ * @param objects An Object[] . Note that the objects in this array must support toString() reasonably.
+ * @return A String representing the given array.
+ */
+ static public String objectArrayToString(Object objects[]) {
+ StringBuffer result = new StringBuffer("[");
+ for(int i = 0; i < objects.length; i++) {
+ result.append(objects[i].toString());
+ if(i < objects.length - 1) {
+ result.append(",");
+ }
+ }
+ result.append("]");
+ return result.toString();
+ }
+
+
+ /** Sorts an array of files, putting non-files first. Case insensitive.
+ * @param files_to_sort The File[] to be sorted.
+ */
+ static public void sort(File[] files_to_sort)
+ {
+ // Return if there is nothing to sort
+ if (files_to_sort == null || files_to_sort.length <= 1) {
+ return;
+ }
+
+ FileSystemView fileSystemView = FileSystemView.getFileSystemView();
+ FileComparator comparator = new FileComparator();
+
+ // Separate the file system roots and directories from the files, and sort separately
+ ArrayList files_list = new ArrayList((files_to_sort.length * 3) / 4);
+ ArrayList non_files_list = new ArrayList((files_to_sort.length) / 4);
+ for (int i = 0; i < files_to_sort.length; i++) {
+ File file = files_to_sort[i];
+
+ // File is a system root
+ if (fileSystemView.isFileSystemRoot(file)) {
+ non_files_list.add(file);
+ }
+ // File is a directory
+ else if (file.isDirectory()) {
+ non_files_list.add(file);
+ }
+ // File is an actual file
+ else {
+ files_list.add(file);
+ }
+ }
+
+ // Sort files
+ Object[] files = files_list.toArray();
+ Arrays.sort(files, comparator);
+
+ // Sort non-files
+ Object[] non_files = non_files_list.toArray();
+ Arrays.sort(non_files, comparator);
+
+ // Merge the results, putting non-files before files
+ int j = 0;
+ for (int i = 0; i < non_files.length; i++) {
+ files_to_sort[j++] = (File) non_files[i];
+ }
+ for (int i = 0; i < files.length; i++) {
+ files_to_sort[j++] = (File) files[i];
+ }
+ }
+
+
+ /** Comparator used to order files. */
+ static private class FileComparator
+ implements Comparator {
+
+ /** Compare two files in terms of ordering of their paths.
+ * @param o1 The Object that represents the first file.
+ * @param o2 The other Object , also a file.
+ * @return An int which is <1, 0 or >1 if o1 is o2 respectively.
+ */
+ public int compare(Object o1, Object o2)
+ {
+ File f1 = (File) o1;
+ File f2 = (File) o2;
+
+ return f1.getName().compareToIgnoreCase(f2.getName());
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/CheckList.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/CheckList.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/CheckList.java (revision 31635)
@@ -0,0 +1,246 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.util;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.border.*;
+
+
+/** This class provides a visual component that has the form of a list, as provided by JList but uses JCheckBox for data selection. Thus several elements can be 'ticked' in the list, and this selection returned using the method getSelected(). Parts of this code modified from Trevor Harmon's posting on www.dejanews.com.
+ * @author John Thompson
+ * @version 2.3
+ */
+public class CheckList
+ extends JList
+{
+ /** The border used when a list row is not in focus. */
+ static private Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
+
+ private boolean show_selected_row = true;
+
+
+ /** Constructor. */
+ public CheckList(boolean show_selected_row)
+ {
+ super();
+ this.show_selected_row = show_selected_row;
+
+ setCellRenderer(new CheckListCellRenderer());
+ setModel(new DefaultListModel());
+ setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ addMouseListener(new CheckListMouseListener());
+ }
+
+
+ public void addEntry(CheckListEntry entry)
+ {
+ DefaultListModel model = (DefaultListModel) getModel();
+
+ // Add the entry in alpabetical order
+ String name = entry.toString();
+ for (int i = 0; i < model.size(); i++) {
+ Object sibling = model.getElementAt(i);
+ if (name.compareTo(sibling.toString()) <= 0) {
+ model.add(i, entry);
+ return;
+ }
+ }
+
+ model.addElement(entry);
+ }
+
+
+ public void clearTicked()
+ {
+ DefaultListModel model = (DefaultListModel) getModel();
+ for (int i = 0; i < model.size(); i++) {
+ ((CheckListEntry) model.get(i)).setSelected(false);
+ }
+ updateUI();
+ }
+
+ /** Retrieve all the entries from the list
+ */
+ public ArrayList getAll()
+ {
+ ArrayList result = new ArrayList();
+ DefaultListModel model = (DefaultListModel) getModel();
+ for (int i = 0; i < model.size(); i++) {
+ CheckListEntry entry = (CheckListEntry) model.get(i);
+ result.add(entry.getObject());
+ }
+ return result;
+ }
+
+
+ /** Retrieve the currently ticked entries from this list.
+ * @return An ArrayList containing only those entries from the initial list that are checked.
+ * @see org.greenstone.gatherer.checklist.Entry
+ */
+ public ArrayList getTicked()
+ {
+ ArrayList result = new ArrayList();
+ DefaultListModel model = (DefaultListModel) getModel();
+ for (int i = 0; i < model.size(); i++) {
+ CheckListEntry entry = (CheckListEntry) model.get(i);
+ if (entry.isSelected()) {
+ result.add(entry.getObject());
+ }
+ }
+ return result;
+ }
+
+
+ /** This is different from isSelectionEmpty! */
+ public boolean isNothingTicked()
+ {
+ DefaultListModel model = (DefaultListModel) getModel();
+ for (int i = 0; i < model.size(); i++) {
+ if (((CheckListEntry) model.get(i)).isSelected()) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+ public void setListData(ArrayList list_data)
+ {
+ // Create a new model.
+ setModel(new DefaultListModel());
+
+ // Add the items from the list to the model
+ for (int i = 0; i < list_data.size(); i++) {
+ Object list_object = list_data.get(i);
+ if (list_object instanceof CheckListEntry) {
+ addEntry((CheckListEntry) list_object);
+ }
+ else {
+ addEntry(new CheckListEntry(list_object));
+ }
+ }
+ }
+
+
+ public void setTickedObjects(Object[] objects)
+ {
+ if (objects == null) {
+ return;
+ }
+
+ DefaultListModel model = (DefaultListModel) getModel();
+ for (int i = 0; i < model.size(); i++) {
+ CheckListEntry entry = (CheckListEntry) model.get(i);
+ for (int j = 0; j < objects.length; j++) {
+ if (entry.getObject().equals(objects[j])) {
+ entry.setSelected(true);
+ }
+ }
+ }
+ updateUI();
+ }
+
+ public void setAllTicked() {
+ DefaultListModel model = (DefaultListModel) getModel();
+ for (int i = 0; i < model.size(); i++) {
+ CheckListEntry entry = (CheckListEntry) model.get(i);
+ entry.setSelected(true);
+ }
+ updateUI();
+ }
+
+ /** A custom list cell renderer for producing rows which contain clickable check boxes. */
+ protected class CheckListCellRenderer
+ implements ListCellRenderer
+ {
+ /** Return a component that has been configured to display the specified value. That component's paint method is then called to "render" the cell. If it is necessary to compute the dimensions of a list because the list cells do not have a fixed size, this method is called to generate a component on which getPreferredSize can be invoked.
+ * @param list The JList we're painting.
+ * @param value The value returned by list.getModel().getElementAt(index), as an Object .
+ * @param index The cells index as an int .
+ * @param is_selected true if the specified cell was selected, false otherwise.
+ * @param cell_has_focus true if and only if the specified cell has the focus.
+ * @return A Component whose paint() method will render the specified value.
+ */
+ public Component getListCellRendererComponent(JList list, Object value, int index, boolean is_selected, boolean cell_has_focus) {
+ JCheckBox checkbox = (JCheckBox) value;
+ if (show_selected_row) {
+ checkbox.setBackground(is_selected ? list.getSelectionBackground() : list.getBackground());
+ checkbox.setForeground(is_selected ? list.getSelectionForeground() : list.getForeground());
+ checkbox.setBorderPainted(true);
+ }
+ else {
+ checkbox.setBackground(list.getBackground());
+ checkbox.setForeground(list.getForeground());
+ checkbox.setBorderPainted(false);
+ }
+ checkbox.setEnabled(list.isEnabled());
+ checkbox.setFont(list.getFont());
+ checkbox.setFocusPainted(false);
+ checkbox.setBorder((is_selected) ? UIManager.getBorder("List.focusCellHighlightBorder") : noFocusBorder);
+ return checkbox;
+ }
+ }
+
+
+ /** Listens for clicks apon the checks within the list, and updates as necessary. */
+ private class CheckListMouseListener
+ extends MouseAdapter
+ {
+ /** Called whenever the mouse is clicked over our list. We find the nearest checkbox and change its state.
+ * @param e A MouseEvent containing everything you ever wanted to know about the mouse event but were afraid to ask.
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ if (!isEnabled()) return;
+ JList list = (JList) e.getSource();
+ int index = list.locationToIndex(e.getPoint());
+ CheckListEntry checkbox = (CheckListEntry) list.getModel().getElementAt(index);
+ if (!checkbox.isFixed()) {
+ checkbox.setSelected(!checkbox.isSelected());
+ }
+ checkbox.grabFocus();
+
+ // We need to cause a ListSelectionEvent -- this is ugly but I can't find a better way quickly
+ list.removeSelectionInterval(0, 0);
+ list.setSelectionInterval(index, index);
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/CheckListEntry.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/CheckListEntry.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/CheckListEntry.java (revision 31635)
@@ -0,0 +1,81 @@
+package org.greenstone.gatherer.util;
+
+import javax.swing.JCheckBox;
+import org.greenstone.gatherer.Dictionary;
+
+/** A CheckListEntry encapsulates a single row of the list, both its check box and the object the row represents. */
+public class CheckListEntry
+ extends JCheckBox
+ implements Comparable {
+
+ /** Is this checkboxes state fixed. */
+ private boolean fixed = false;
+ /** The original object this row is based on, and whose toString() is used to generate the check box label. */
+ private Object object = null;
+ /** A sundry string for storing original values (rather than strings retrieved from dictionary). */
+ private String property;
+ /** Constructor. */
+ public CheckListEntry(Object object) {
+ super(object.toString());
+ this.object = object;
+ this.setComponentOrientation(Dictionary.getOrientation());
+ }
+
+ /** Constructor. */
+ public CheckListEntry(String text, boolean is_selected) {
+ super(text);
+ this.setComponentOrientation(Dictionary.getOrientation());
+ setSelected(is_selected);
+ }
+
+ /** Test this CheckListEntry against an Object for ordering.
+ * @param obj the other Object.
+ * @return <0, 0 or >0 if this CheckListEntry is before, equal to or after the given object.
+ */
+ public int compareTo(Object obj) {
+ return toString().toLowerCase().compareTo(obj.toString().toLowerCase());
+ }
+ /** Test this CheckListEntry against an Object for equality.
+ * @param obj the other Object.
+ * @return true if the two are equal, false otherwise.
+ */
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ return toString().equalsIgnoreCase(obj.toString());
+ }
+
+ /** Retrieve the object associated with this entry.
+ * @return An Object .
+ */
+ public Object getObject() {
+ if(object == null) {
+ return getText();
+ }
+ return object;
+ }
+
+ public String getProperty() {
+ return property;
+ }
+
+ public boolean isFixed() {
+ return fixed;
+ }
+
+ public void setFixed(boolean fixed) {
+ this.fixed = fixed;
+ }
+
+ public void setProperty(String property) {
+ this.property = property;
+ }
+
+ public String toString() {
+ if(object == null) {
+ return getText();
+ }
+ return object.toString();
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/Codec.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/Codec.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/Codec.java (revision 31635)
@@ -0,0 +1,391 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.util;
+
+import java.util.*;
+
+/** Provides a standard, extensible way to convert from one format of string to another (given that each format has differing requirements regarding legal characters and escaped characters)
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.3d
+ */
+public class Codec {
+
+ static final public String DECODE_PATH = "DECODE_PATH";
+ static final public String DECODE_SQUARE_BRACKETS = "DECODE_SQUARE_BRACKETS";
+ static final public String DOM_TO_GREENSTONE = "DOM_TO_GREENSTONE";
+ static final public String DOM_TO_TEXT = "DOM_TO_TEXT";
+ static final public String ENCODE_PATH = "ENCODE_PATH";
+ static final public String ENCODE_SQUARE_BRACKETS = "ENCODE_SQUARE_BRACKETS";
+ static final public String ESCAPEDHTML_TO_UNESCAPED = "ESCAPEDHTML_TO_UNESCAPED";
+ static final public String GREENSTONE_TO_DOM = "GREENSTONE_TO_DOM";
+ static final public String GREENSTONE_TO_TEXT = "GREENSTONE_TO_TEXT";
+ static final public String TEXT_TO_DOM = "TEXT_TO_DOM";
+ static final public String TEXT_TO_GREENSTONE = "TEXT_TO_GREENSTONE";
+ static final public String TEXT_TO_REGEXP = "TEXT_TO_REGEXP";
+ static final public String TEXT_TO_SHELL_UNIX = "TEXT_TO_SHELL_UNIX";
+ static final public String TEXT_TO_SHELL_WINDOWS = "TEXT_TO_SHELL_WINDOWS";
+
+ static final private int MAX_CACHE_SIZE = 100;
+
+ static private HashMap TRANSFORMS;
+ static private HashMap3D CACHE;
+
+ /** Static function called to construct TRANSFORMS mappings */
+ static {
+ TRANSFORMS = new HashMap();
+
+ String[] decode_path = {
+ "\\|", "\\\\",
+ "|", "\\|"
+ };
+ TRANSFORMS.put(DECODE_PATH, decode_path);
+ decode_path = null;
+
+ // Transform text into text, but without [ and ]
+ String[] decode_square_brackets = {
+ "[", "\\[",
+ "]", "\\]"
+ };
+ TRANSFORMS.put(DECODE_SQUARE_BRACKETS, decode_square_brackets);
+ decode_square_brackets = null;
+
+ // Translate DOM encoded text into Greenstone encoding
+ String[] dom_to_greenstone = {
+ "'", "\\\\\'",
+ ">", ">",
+ "<", "<",
+ """, "\\\\\"",
+ "&", "&"
+ };
+ // removed "\n", "\\\\n", - config files are allowed new lines
+ // added "\\|", "\\\\"
+
+ TRANSFORMS.put(DOM_TO_GREENSTONE, dom_to_greenstone);
+ dom_to_greenstone = null;
+
+ // Transform DOM encoded text into plain text
+ String[] dom_to_text = {
+ "[", "\\[",
+ "]", "\\]",
+ "'", "\'",
+ ">", ">",
+ "<", "<",
+ """, "\"",
+ "&", "&"
+ };
+ TRANSFORMS.put(DOM_TO_TEXT, dom_to_text);
+ dom_to_text = null;
+
+ // Transform text into a regular expression that will match it
+ String[] text_to_regexp = {
+ "\\\\", "\\\\\\\\",
+ "\\(", "\\\\(",
+ "\\)", "\\\\)",
+ "\\[", "\\\\[",
+ "\\]", "\\\\]",
+ "\\{", "\\\\{",
+ "\\}", "\\\\}",
+ "\\.", "\\\\."
+ };
+ TRANSFORMS.put(TEXT_TO_REGEXP, text_to_regexp);
+ text_to_regexp = null;
+
+ String[] encode_path = {
+ "\\|", "|",
+ "\\\\", "\\|"
+ };
+ TRANSFORMS.put(ENCODE_PATH, encode_path);
+ encode_path = null;
+
+ // Transform text into text, but without [ and ]
+ String[] encode_square_brackets = {
+ "\\[", "[",
+ "\\]", "]"
+ };
+ TRANSFORMS.put(ENCODE_SQUARE_BRACKETS, encode_square_brackets);
+ encode_square_brackets = null;
+
+ // Transform Greenstone encoded text to DOM encoding
+ String[] greenstone_to_dom = {
+ "&", "&",
+ "<", "<",
+ ">", ">",
+ "\\\\\"", """,
+ "\\\\\'", "'",
+ "\"", """,
+ "\'", "'"
+ };
+ // removed"\\\\n", "\n", added "\\\\", "\\|"
+
+ TRANSFORMS.put(GREENSTONE_TO_DOM, greenstone_to_dom);
+ greenstone_to_dom = null;
+
+ // Transform Greenstone encoded text to plain text
+ String[] greenstone_to_text = {
+ "\\\\\"", "\"",
+ "\\\\\'", "\'",
+ """, "\"",
+ "'", "\'",
+ "[", "\\[",
+ "]", "\\]"
+ };
+ // removed "\\\\n", "\n", "\\|", "\\\\"
+
+ TRANSFORMS.put(GREENSTONE_TO_TEXT, greenstone_to_text);
+ greenstone_to_text = null;
+
+ // Transform plain html text into something that can be placed in a DOM
+ String[] text_to_dom = {
+ "&", "&",
+ "<", "<",
+ ">", ">",
+ "\"", """,
+ "\'", "'"
+ };
+ TRANSFORMS.put(TEXT_TO_DOM, text_to_dom);
+ text_to_dom = null;
+
+ // Unescape html (or xml) text
+ String[] escapedhtml_to_unescaped = {
+ "&", "&",
+ "<", "<",
+ ">", ">",
+ """, "\""//,
+ //"'", "\'"
+ };
+ TRANSFORMS.put(ESCAPEDHTML_TO_UNESCAPED, escapedhtml_to_unescaped);
+ escapedhtml_to_unescaped = null;
+
+ // Transform plain html text into greenstone encoding
+ String[] text_to_greenstone = {
+
+ "\\[", "[",
+ "\\]", "]",
+ "\"", """,
+ "\n", "\\\\n"
+ };
+ // "\'", "'",
+ // removed "\\\\", "\\|",
+ TRANSFORMS.put(TEXT_TO_GREENSTONE, text_to_greenstone);
+ text_to_greenstone = null;
+
+ // Transform plain html text into something that can be placed in a shell command
+ String[] text_to_shell_unix = {
+ "\"", "\\\\\"",
+ "\'", "\\\\\'",
+ "\n", "\\\\n"
+ };
+ TRANSFORMS.put(TEXT_TO_SHELL_UNIX, text_to_shell_unix);
+ text_to_shell_unix = null;
+
+ // Transform plain html text into something that can be placed in a shell command. Windows requires twice as many escaped for speech marks to be passed to underlying processes
+ String[] text_to_shell_windows = {
+ "\"", "\\\\\\\\\\\\\"",
+ "\'", "\\\\\'",
+ "\n", "\\\\n"
+ };
+ TRANSFORMS.put(TEXT_TO_SHELL_WINDOWS, text_to_shell_windows);
+ text_to_shell_windows = null;
+
+ CACHE = new HashMap3D();
+ }
+
+ static public String transform(String raw, String transform) {
+ if(raw == null) {
+ return raw;
+ }
+ // System.err.println("Transforming by "+transform+":\n" + raw);
+ String processed = (String) CACHE.get(transform, raw);
+ if(processed == null) {
+ processed = raw;
+ String[] transforms = (String[]) TRANSFORMS.get(transform);
+ if(transforms != null) {
+ for(int i = 0; i < transforms.length; i = i + 2) {
+ String target = transforms[i];
+ String result = transforms[i+1];
+ processed = processed.replaceAll(target, result);
+ }
+ }
+ //DebugStream.println("\n*** Transform: " + transform + " ***");
+ //DebugStream.println("*** Raw : '" + raw + "'");
+ //DebugStream.println("*** Processed: '" + processed + "'");
+ // If cache is at maximum size, empty it and start again
+ if(CACHE.size() == MAX_CACHE_SIZE) {
+ CACHE.clear();
+ }
+ CACHE.put(transform, raw, processed);
+ }
+ return processed;
+ }
+
+ /** Transform either of the accepted unicode escape sequences styles from in the string into single characters */
+ static final private char AND_CHAR = '&';
+ static final private char ESCAPE_CHAR = '\\';
+ static final private char HASH_CHAR = '#';
+ static final private char LOWER_U_CHAR = 'u';
+ static final private char UPPER_U_CHAR = 'U';
+ static final private char SEMICOLON_CHAR = ';';
+
+ static public String transformUnicode(String raw) {
+ StringBuffer processed = new StringBuffer();
+ int index = 0;
+ int raw_length = raw.length();
+ while(index < raw_length) {
+ char c0 = raw.charAt(index);
+ switch(c0) {
+ case AND_CHAR:
+ if(index + 1 < raw_length) {
+ // First the HTML ç type
+ char c1 = raw.charAt(index + 1);
+ if(c1 == HASH_CHAR) {
+ StringBuffer number_str = new StringBuffer();
+ char c2;
+ int offset = 2;
+ while(index + offset < raw_length && (c2 = raw.charAt(index + offset)) != SEMICOLON_CHAR) {
+ number_str.append(c2);
+ offset++;
+ }
+ // We've either run out of characters or have parsed a number
+ if(index + offset < raw_length && raw.charAt(index + offset) == SEMICOLON_CHAR) {
+ int number = Integer.parseInt(number_str.toString());
+ processed.append((char)number);
+ index = index + offset;
+ number_str = null;
+ break;
+ }
+ number_str = null;
+ }
+ }
+ processed.append(c0);
+ break;
+ case ESCAPE_CHAR:
+ // Now the \u00e7 type
+ if(index + 1 < raw_length) {
+ char c3 = raw.charAt(index + 1);
+ if((c3 == UPPER_U_CHAR || c3 == LOWER_U_CHAR) && index + 5 < raw_length) {
+ // We read four digits
+ String hex_str = raw.substring(index + 2, index + 6);
+ int number = Integer.parseInt(hex_str, 16);
+ hex_str = null;
+ processed.append((char)number);
+ index = index + 5;
+ break;
+ }
+ }
+ processed.append(c0);
+ break;
+ default:
+ processed.append(c0);
+ }
+ index++;
+ }
+ return processed.toString();
+ }
+
+ static public void main(String[] args) {
+ if(args.length < 2) {
+ String processed;
+ String raw;
+ String transform;
+
+ System.err.println("Running Test Suite");
+
+ transform = "DOM_TO_GREENSTONE";
+ System.err.println("Test " + transform);
+ raw = "A <\nand a <a href="here.html"><font size='2'>URL</font></a>";
+ System.err.println("Raw: '" + raw + "'");
+ processed = transform(raw, transform);
+ System.err.println("Processed: '" + processed + "'");
+
+ transform = "DOM_TO_TEXT";
+ System.err.println("Test " + transform);
+ raw = "A <\nand a <a href="here.html"><font size='2'>URL</font></a>";
+ System.err.println("Raw: '" + raw + "'");
+ processed = transform(raw, transform);
+ System.err.println("Processed: '" + processed + "'");
+
+ transform = "GREENSTONE_TO_DOM";
+ System.err.println("Test " + transform);
+ raw = "A <\\nand a ";
+ System.err.println("Raw: '" + raw + "'");
+ processed = transform(raw, transform);
+ System.err.println("Processed: '" + processed + "'");
+
+ transform = "GREENSTONE_TO_TEXT";
+ System.err.println("Test " + transform);
+ raw = "These \\[ \\] should be escaped, and so should \\\\ that. These " ' \\n are encoded.";
+ System.err.println("Raw: '" + raw + "'");
+ processed = transform(raw, transform);
+ System.err.println("Processed: '" + processed + "'");
+
+ transform = "TEXT_TO_DOM";
+ System.err.println("Test " + transform);
+ raw = "A <\nand a URL ";
+ System.err.println("Raw: '" + raw + "'");
+ processed = transform(raw, transform);
+ System.err.println("Processed: '" + processed + "'");
+
+ transform = "TEXT_TO_GREENSTONE";
+ System.err.println("Test " + transform);
+ raw = "These [ ] should be escaped, and so should \\ that. These \" \' \n are encoded.";
+ System.err.println("Raw: '" + raw + "'");
+ processed = transform(raw, transform);
+ System.err.println("Processed: '" + processed + "'");
+
+ transform = "TEXT_TO_SHELL";
+ System.err.println("Test " + transform);
+ if(Utility.isWindows()) {
+ System.err.println("[Windows Version]");
+ transform = "TEXT_TO_SHELL_WINDOWS";
+ }
+ else {
+ System.err.println("[Unix Version]");
+ transform = "TEXT_TO_SHELL_UNIX";
+ }
+ raw = "A <\nand a URL ";
+ System.err.println("Raw: '" + raw + "'");
+ processed = transform(raw, transform);
+ System.err.println("Processed: '" + processed + "'");
+
+ System.err.println("***** UNICODE TEST *****");
+ System.err.println("\\u0030 => " + transformUnicode("\\u0030"));
+ System.err.println("\\u0041 => " + transformUnicode("\\u0041"));
+ System.err.println("\\u007a => " + transformUnicode("\\u007a"));
+ System.err.println("\\u00e7 => " + transformUnicode("\\u00e7"));
+ System.err.println("0 => " + transformUnicode("0"));
+ System.err.println("A => " + transformUnicode("A"));
+ System.err.println("z => " + transformUnicode("z"));
+ System.err.println("ç => " + transformUnicode("ç"));
+ }
+ else {
+ System.err.println("Raw: '" + args[0] + "'");
+ System.err.println("Transform: " + args[1]);
+ String processed = transform(args[0], args[1]);
+ System.err.println("Processed: '" + processed + "'");
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/DOMTree.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/DOMTree.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/DOMTree.java (revision 31635)
@@ -0,0 +1,149 @@
+package org.greenstone.gatherer.util;
+
+import java.util.*;
+import javax.swing.*;
+import javax.swing.tree.*;
+import org.w3c.dom.*;
+
+public class DOMTree
+ extends JTree {
+
+ public DOMTree(Document document) {
+ super();
+ setDocument(document);
+ }
+
+ public void setDocument(Document document) {
+ setModel(new DefaultTreeModel(new DOMTreeNode(document.getDocumentElement(), null)));
+ }
+
+ private class DOMTreeNode
+ implements TreeNode {
+
+ private Node node = null;
+ private NodeList children = null;
+ private String text = null;
+ private TreeNode parent = null;
+ private TreeNode self = null;
+
+ DOMTreeNode(Node node, TreeNode parent) {
+ this.node = node;
+ this.parent = parent;
+ this.self = this;
+ }
+
+ /** Returns the children of the receiver as an Enumeration. */
+ public Enumeration children() {
+ if(children == null) {
+ children = node.getChildNodes();
+ }
+ return new NodeListEnumeration();
+ }
+
+ /** Returns true if the receiver allows children. */
+ public boolean getAllowsChildren() {
+ return true;
+ }
+
+ /** Returns the child TreeNode at index childIndex. */
+ public TreeNode getChildAt(int childIndex) {
+ if(children == null) {
+ children = node.getChildNodes();
+ }
+ return new DOMTreeNode(children.item(childIndex), this);
+ }
+
+ /** Returns the number of children TreeNodes the receiver contains. */
+ public int getChildCount() {
+ if(children == null) {
+ children = node.getChildNodes();
+ }
+ return children.getLength();
+ }
+
+ /** Returns the index of node in the receivers children. */
+ public int getIndex(TreeNode find_node) {
+ if(children == null) {
+ children = node.getChildNodes();
+ }
+ Node target_node = ((DOMTreeNode)find_node).getNode();
+ int children_count = children.getLength();
+ for(int i = 0; i < children_count; i++) {
+ Node test_node = children.item(i);
+ if(target_node == test_node) {
+ test_node = null;
+ target_node = null;
+ return i;
+ }
+ test_node = null;
+ }
+ target_node = null;
+ return -1;
+ }
+
+ public Node getNode() {
+ return node;
+ }
+
+ /** Returns the parent TreeNode of the receiver. */
+ public TreeNode getParent() {
+ return parent;
+ }
+
+ /** Returns true if the receiver is a leaf. */
+ public boolean isLeaf() {
+ return !node.hasChildNodes();
+ }
+
+ public String toString() {
+ if(text == null) {
+ StringBuffer temp = new StringBuffer(node.getNodeName());
+ String value = node.getNodeValue();
+ if(value != null && value.length() != 0) {
+ temp.append("=\"");
+ temp.append(value);
+ temp.append("\"");
+ }
+ value = null;
+ if(node instanceof Element) {
+ Element element = (Element) node;
+ temp.append(": ");
+ NamedNodeMap attributes = element.getAttributes();
+ int attribute_count = attributes.getLength();
+ for(int i = 0; i < attribute_count; i++) {
+ Node attribute = attributes.item(i);
+ temp.append(attribute.getNodeName());
+ temp.append("=\"");
+ temp.append(attribute.getNodeValue());
+ if(i < attribute_count - 1) {
+ temp.append("\" ");
+ }
+ else {
+ temp.append("\"");
+ }
+ }
+ attributes = null;
+ element = null;
+ }
+ text = temp.toString();
+ temp = null;
+ }
+ return text;
+ }
+
+ private class NodeListEnumeration
+ implements Enumeration {
+ private int index = 0;
+ /** Tests if this enumeration contains more elements. */
+ public boolean hasMoreElements() {
+ return index < children.getLength();
+ }
+ /** Returns the next element of this enumeration if this enumeration object has at least one more element to provide. */
+ public Object nextElement() {
+ int old_index = index;
+ index = index + 1;
+ return new DOMTreeNode(children.item(old_index), self);
+ }
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/DragComponent.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/DragComponent.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/DragComponent.java (revision 31635)
@@ -0,0 +1,65 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.util;
+/**
+ * Title: Gatherer
+ * Description: The Gatherer: a tool for gathering and enriching a digital collection.
+ * Copyright: Copyright (c) 2001
+ * Company: The University of Waikato
+ * Written: / /01
+ * Revised: 21/06/02 - Moved into correct package and commented.
+ * @author John Thompson, 9826509
+ * @version 2.1
+ */
+import java.awt.event.FocusListener;
+import javax.swing.tree.*;
+import org.greenstone.gatherer.file.FileSystemModel;
+
+/** Any component that wants to act a GTree like drag'n'drop component must implement this. */
+public interface DragComponent {
+ public void addFocusListener(FocusListener listener);
+ /** In order for the appearance to be consistant, given we may be in the situation where the pointer has left our focus but the ghost remains, this method allows other members of the Group to tell this component to repair the 'spoilt' region left by its ghost. */
+ public void clearGhost();
+ /** Used to notify this component that it has gained focus by some method other that mouse focus. */
+ public void gainFocus();
+ /** Retrieve the model associated with this component. */
+ public FileSystemModel getTreeModel();
+ /** This method is used to inform this component when it loses focus by means other than a drag mouse event, and should indicate this somehow. */
+ public void loseFocus();
+ /** Set the components group. */
+ public void setGroup(DragGroup group);
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/DragGroup.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/DragGroup.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/DragGroup.java (revision 31635)
@@ -0,0 +1,136 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.util;
+
+import java.awt.Point;
+import java.awt.event.*;
+import java.awt.image.BufferedImage;
+import java.util.Vector;
+import javax.swing.JComponent;
+import javax.swing.tree.TreePath;
+import org.greenstone.gatherer.gui.tree.DragTree;
+
+
+/** This class acts as a linker between all the various drag and drop enabled DragComponent. It provides methods for ensuring only one component is the drop target, showing extra user feedback, and maintains a single point of focus. Moreover it provides a storage space for necessary shared variables such as mouse_offset of initial drag etc. */
+public class DragGroup
+ implements FocusListener {
+ /** The image used for the ghost icon when dragging. */
+ public BufferedImage image_ghost = null;
+ /** The initial offset of the mouse from the selection within a DragComponent. Then as you drag the mouse, the ghost appears in a visually consistant place, and remains at the same offset throughout the dragging process. */
+ public Point mouse_offset = null;
+ /** The component currently in charge of drawing the ghost icon. Note that this isn't necessarily the only component that needs to repaint 'spoilt' screen real-estate as a side effect of the ghost. */
+ private DragComponent ghost_owner = null;
+ /** The component where the drag began, so that extra information necessary for the drag can be garnered by the drop target, or so that focus can return to the source component if the drop is rejected. */
+ private DragTree drag_source = null;
+ /** The selected nodes, as determined when the drag begun. */
+ private TreePath[] selection = null;
+ /** A list of DragComponents registered as being part of this group. */
+ private Vector registered = new Vector();
+
+ /** Register a DragComponent as begin part of this group.
+ * @param component The DragComponent to add.
+ */
+ public void add(DragComponent component) {
+ if(!registered.contains(component)) {
+ registered.add(component);
+ component.addFocusListener(this);
+ component.setGroup(this);
+ }
+ }
+
+ /** Invoked when a component gains the keyboard focus. */
+ public void focusGained(FocusEvent event) {
+ ((DragComponent)event.getComponent()).gainFocus();
+ }
+
+ /** Invoked when a component loses the keyboard focus. */
+ public void focusLost(FocusEvent event) {
+ ((DragComponent)event.getComponent()).loseFocus();
+ }
+ /** Determines the current 'active' component, ie that component with focus.
+ * @return The DragComponent which is currently responsible for drawing the ghost, and thus is active.
+ */
+ public DragComponent getActive() {
+ return ghost_owner;
+ }
+ /** Retrieve the nodes selected at the beginning of this drag operation.
+ * @return A TreePath[].
+ */
+ public TreePath[] getSelection() {
+ return selection;
+ }
+ /** Retrieve the component which served as the source of this drag action.
+ * @return The DragComponent in question.
+ */
+ public DragTree getSource() {
+ return drag_source;
+ }
+ /** When called this method asserts that one of the registered GComponents just became the proud owner of focus, and is henceforth responsible for drawing the ghost, and acting like a component thats in focus.
+ * @param new_owner The DragComponent that has gained focus during a drag action.
+ */
+ public void grabFocus(DragComponent new_owner) {
+ // Tell the previous owner of ghost to clear itself.
+ if(ghost_owner != null) {
+ ghost_owner.clearGhost();
+ }
+ // Assign new owner
+ ghost_owner = new_owner;
+ // Then tell all other GComponents to indicate they aren't in focus in whatever way they do that (ie selected items of a GTree go gray). Of course the new owners told it is in focus.
+ for(int i = 0; i < registered.size(); i++) {
+ DragComponent comp = (DragComponent)registered.get(i);
+ if(comp == new_owner) {
+ comp.gainFocus();
+ }
+ else {
+ comp.loseFocus();
+ }
+ }
+ }
+ /** Sets the value of selected_nodes.
+ * @param selection The new value for selected_nodes as a TreePath[].
+ */
+ public void setSelection(TreePath[] selection) {
+ this.selection = selection;
+ }
+ /** Sets the value of drag_source.
+ * @param drag_source The new value for drag_source as a DragTree.
+ */
+ public void setSource(DragTree drag_source) {
+ this.drag_source = drag_source;
+ ///ystem.err.println("The drag source is now " + drag_source);
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/DragTreeSelectionModel.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/DragTreeSelectionModel.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/DragTreeSelectionModel.java (revision 31635)
@@ -0,0 +1,278 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.util;
+
+import java.awt.Point;
+import java.awt.event.*;
+import javax.swing.tree.*;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Dictionary;
+import org.greenstone.gatherer.gui.tree.DragTree;
+
+public class DragTreeSelectionModel
+ extends DefaultTreeSelectionModel
+ implements MouseListener {
+
+ private boolean immediate = false;
+ private boolean mixed_selection = false;
+
+ private int type = -1;
+
+ private TreePath temp_path = null;
+ private TreePath temp_paths[] = null;
+
+ static private final int NONE = -1;
+ static private final int ADD = 0;
+ static private final int SET = 1;
+
+ /** Constructor.
+ * @param tree the DragTree which will use this selection model.
+ * @param mixed_selection true if this selection model allows selections to contain files and folders, false otherwise.
+ */
+ public DragTreeSelectionModel(DragTree tree, boolean mixed_selection) {
+ super();
+ this.mixed_selection = mixed_selection;
+ setSelectionMode(DISCONTIGUOUS_TREE_SELECTION);
+ tree.addMouseListener(this);
+ }
+
+ public void addSelectionPath(TreePath path) {
+ if(!immediate) {
+ this.temp_path = path;
+ this.type = this.ADD;
+ }
+ else if(isAppropriate(path)) {
+ temp_path = null;
+ super.addSelectionPath(path);
+ }
+ }
+
+ public void addSelectionPaths(TreePath paths[]) {
+ if(!immediate) {
+ this.temp_paths = paths;
+ this.type = this.ADD;
+ }
+ else if(isAppropriate(paths, true)) {
+ temp_paths = null;
+ super.setSelectionPaths(paths);
+ }
+ }
+
+ public String getDetails() {
+ String suffix = null;
+ int file_count = 0;
+ int folder_count = 0;
+ for(int i = 0; selection != null && i < selection.length; i++) {
+ TreeNode node = (TreeNode) selection[i].getLastPathComponent();
+ if(node.isLeaf()) {
+ file_count++;
+ }
+ else {
+ folder_count++;
+ }
+ }
+
+ // Update the metaaudit_suffix
+ String args[] = null;
+ if(file_count > 0 && folder_count > 0) {
+ if(file_count > 1 && folder_count > 1) {
+ args = new String[2];
+ args[0] = String.valueOf(file_count);
+ args[1] = String.valueOf(folder_count);
+ suffix = Dictionary.get("FileActions.Selected", args);
+ }
+ else if(file_count > 1) {
+ args = new String[1];
+ args[0] = String.valueOf(file_count);
+ suffix = Dictionary.get("FileActions.Files_And_Directory_Selected", args);
+ }
+ else if(folder_count > 1) {
+ args = new String[1];
+ args[0] = String.valueOf(folder_count);
+ suffix = Dictionary.get("FileActions.File_And_Directories_Selected", args);
+ }
+ else {
+ suffix = Dictionary.get("FileActions.File_And_Directory_Selected");
+ }
+ }
+ else if(file_count > 0) {
+ if(file_count > 1) {
+ args = new String[1];
+ args[0] = String.valueOf(file_count);
+ suffix = Dictionary.get("FileActions.Files_Selected", args);
+ }
+ else if(file_count == 1) {
+ suffix = Dictionary.get("FileActions.File_Selected");
+ }
+ }
+ else if(folder_count > 0) {
+ if(folder_count > 1) {
+ args = new String[1];
+ args[0] = String.valueOf(folder_count);
+ suffix = Dictionary.get("FileActions.Directories_Selected", args);
+ }
+ else {
+ suffix = Dictionary.get("FileActions.Directory_Selected");
+ }
+ }
+ args = null;
+
+ return suffix;
+ }
+
+ /** Any implementation of MouseListener must include this method so
+ * we can be informed when the mouse is clicked.
+ * @param event A MouseEvent containing all the information about
+ * this mouse click.
+ */
+ public void mouseClicked(MouseEvent event) {
+ }
+
+ /** Any implementation of MouseListener must include this method so
+ * we can be informed when the mouse enters a component.
+ * @param event A MouseEvent containing all the information about
+ * this mouse action.
+ */
+ public void mouseEntered(MouseEvent event) {
+ }
+
+ /** Any implementation of MouseListener must include this method so
+ * we can be informed when the mouse exits a component.
+ * @param event A MouseEvent containing all the information about
+ * this mouse action.
+ */
+ public void mouseExited(MouseEvent event) {
+ }
+
+ /** Any implementation of MouseListener must include this method so
+ * we can be informed when the mouse is pressed (start of click).
+ * @param event A MouseEvent containing all the information about
+ * this mouse action.
+ */
+ public void mousePressed(MouseEvent event) {
+ }
+
+ /** Any implementation of MouseListener must include this method so
+ * we can be informed when the mouse is released (end of click).
+ * @param event A MouseEvent containing all the information about
+ * this mouse action.
+ */
+ public void mouseReleased(MouseEvent event) {
+ try {
+ switch(this.type) {
+ case 0: // this.ADD
+ if(this.temp_path != null && isAppropriate(temp_path)) {
+ super.addSelectionPath(this.temp_path);
+ this.temp_path = null;
+ }
+ if(this.temp_paths != null && isAppropriate(temp_paths, true)) {
+ super.addSelectionPaths(this.temp_paths);
+ this.temp_paths = null;
+ }
+ this.type = this.NONE;
+ break;
+ case 1: // this.SET
+ if(this.temp_path != null) {
+ super.setSelectionPath(this.temp_path);
+ this.temp_path = null;
+ }
+ if(this.temp_paths != null && isAppropriate(temp_paths, false)) {
+ super.setSelectionPaths(this.temp_paths);
+ this.temp_paths = null;
+ }
+ this.type = this.NONE;
+ break;
+ }
+ }
+ catch (Exception error) {
+ DebugStream.printStackTrace(error);
+ }
+ }
+
+ public void setImmediate(boolean value) {
+ immediate = value;
+ }
+
+ public void setSelectionPath(TreePath path) {
+ // Since this is only a single path we don't need to check whether its an appropriate selection.
+ if(!immediate) {
+ this.temp_path = path;
+ this.type = this.SET;
+ }
+ else {
+ temp_path = null;
+ super.setSelectionPath(path);
+ }
+ }
+
+ public void setSelectionPaths(TreePath paths[]) {
+ if(!immediate) {
+ this.temp_paths = paths;
+ this.type = this.SET;
+ }
+ else if(isAppropriate(paths, false)) {
+ temp_paths = null;
+ super.setSelectionPaths(paths);
+ }
+ }
+
+ /** Ensure that the given path is appropriate to add to the current selection, preventing mixed selections of files and folder if required. We also must check that no path is a ancestor/descendant of another.
+ */
+ private boolean isAppropriate(TreePath path) {
+ boolean appropriate = true;
+ TreeNode new_node = (TreeNode) path.getLastPathComponent();
+ // If there is a current selection
+ if(selection != null && selection.length > 0) {
+ for(int i = 0; appropriate && i < selection.length; i++) {
+ TreeNode current_node = (TreeNode) selection[i].getLastPathComponent();
+ appropriate = appropriate && (mixed_selection || new_node.isLeaf() == current_node.isLeaf());
+ appropriate = appropriate && !path.isDescendant(selection[i]) && !selection[i].isDescendant(path);
+ }
+ }
+ return appropriate;
+ }
+ /** Ensure that the given paths are appropriate to add to the current selection, preventing mixed selections of files and folder if required. We also must check that no path is a ancestor/descendant of another. One last detail to keep in mind is that adding selections depends upon the current selection, whereas set the selection paths doesn't (replaces them instead) and thus no check of the current paths is needed. */
+ private boolean isAppropriate(TreePath[] paths, boolean check_current) {
+ boolean appropriate = true;
+ if(check_current && paths != null && paths.length > 0) {
+ for(int i = 0; appropriate && i < paths.length; i++) {
+ appropriate = appropriate && isAppropriate(paths[i]);
+ }
+ }
+ return appropriate;
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/ExternalProgram.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/ExternalProgram.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/ExternalProgram.java (revision 31635)
@@ -0,0 +1,107 @@
+package org.greenstone.gatherer.util;
+
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import org.greenstone.gatherer.DebugStream;
+
+
+/**
+ * This class provides a convenient method of running external programs and viewing/analysing
+ * their output. The external programs may be run with or without arguments.
+ */
+public class ExternalProgram
+{
+ private Process programProcess = null;
+
+ private BufferedReader programOut = null;
+ private BufferedReader programErr = null;
+
+
+ /**
+ * Prepares to run a specified external program with the specified arguments.
+ *
+ * @param programName The name (and path, if necessary) of the program to run.
+ * @param programArgs The arguments to provide to the program when run.
+ */
+ public ExternalProgram(String programName, String programArgs)
+ throws IOException
+ {
+ // Run the program
+ programProcess = Runtime.getRuntime().exec(programName + " " + programArgs);
+
+ // Grab the program output
+ programOut = new BufferedReader(new InputStreamReader(programProcess.getInputStream()));
+ programErr = new BufferedReader(new InputStreamReader(programProcess.getErrorStream()));
+
+ }
+
+
+
+ /**
+ */
+ public int exitProgram()
+ throws IOException
+ {
+ // Finished, thank you
+ //programIn.close();
+
+ // Wait for the process to finish
+ try {
+ programProcess.waitFor();
+ }
+ catch (InterruptedException ex) {
+ throw new Error("Internal Error: Process interrupted.\n" + ex);
+ }
+
+ // Return the program exit value
+ return programProcess.exitValue();
+ }
+
+
+ /**
+ * Returns a single line of output sent by the program to standard output.
+ *
+ * @return a single line of output sent by the program to standard output.
+ */
+ public String getLineOfProgramOutput()
+ {
+ // Read in a line from the program output
+ try {
+ if (programOut.ready())
+ return programOut.readLine();
+ else
+ return null;
+ }
+ catch (IOException ex) {
+ DebugStream.println("Error: Exception occurred while reading program output.");
+ DebugStream.println("Exception: " + ex);
+ return null;
+ }
+ }
+
+
+ /**
+ * Returns a single line of output sent by the program to standard error.
+ *
+ * @return a single line of output sent by the program to standard error.
+ */
+ public String getLineOfProgramError()
+ {
+ // Read in a line from the program error
+ try {
+ if (programErr.ready())
+ return programErr.readLine();
+ else
+ return null;
+ }
+ catch (IOException ex) {
+ DebugStream.println("Error: Exception occurred while reading program error.");
+ DebugStream.println("Exception: " + ex);
+ return null;
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/GLIEntityResolver.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/GLIEntityResolver.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/GLIEntityResolver.java (revision 31635)
@@ -0,0 +1,191 @@
+/*
+ * GLIEntityResolver.java
+ * Copyright (C) 2008 New Zealand Digital Library, http://www.nzdl.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package org.greenstone.gatherer.util;
+
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.Gatherer;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.EntityResolver;
+import java.io.File;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+
+/**
+ * Uses a list of default search paths, or the file's own path if provided, to resolve entities referenced
+ * in the file. The search paths, including the file's own, have to be within GS.
+ * By default, the GS3HOME/WEB-INF and toplevel GLI user directory are added to the list of search paths,
+ * since this EntityResolver is used for web.xml to resolve its inclusion of servlets.xml, which can be
+ * located in GS3HOME/WEB-INF for a local GLI or is extracted into the gli user directory for client-GLI.
+ * If everything fails, it will try to resolve entities using the classloader, not otherwise used by GLI,
+ * but this part has been copied over from GS3's GSEntityResolver.java.
+ * If ever needed, maybe to make things faster, can maintain a static list of default search paths.
+ */
+public class GLIEntityResolver implements EntityResolver {
+
+ protected ArrayList list_of_local_search_paths = new ArrayList();
+ ClassLoader class_loader = null;
+
+
+ public GLIEntityResolver() {
+ // Add the basic search paths:
+
+ // add the GS3 web/WEB-INF folder as search location for servlets.xml, which web.xml
+ // includes and which is the reason for adding a GLIEntityResolver class into GLI
+
+ if(Gatherer.GS3) {
+ //list_of_local_search_paths.add(new File(Configuration.gsdl3_path));
+ list_of_local_search_paths.add(new File(Configuration.gsdl3_path, "WEB-INF"));
+ }
+
+ // Add gli user dir for remote GS, since that is where the
+ // web.xml and server.xml downloaded from the remote server will be unpacked to
+ if(Gatherer.isGsdlRemote) {
+ list_of_local_search_paths.add(new File(Configuration.gli_user_directory_path));
+ }
+ }
+
+ public GLIEntityResolver(File file) {
+ this(); // adds default search paths
+
+ // add file's own path as first in search list
+ addSafeSearchPath(file, true);
+ }
+
+ /* Methods with the ClassLoader parameter are unused at present */
+ public GLIEntityResolver(ClassLoader loader) {
+ this.class_loader = loader;
+ }
+
+ public GLIEntityResolver(File f, ClassLoader loader) {
+ this(f);
+ this.class_loader = loader;
+ }
+
+ public void setClassLoader(ClassLoader loader) {
+ this.class_loader = loader;
+ }
+
+ public void addSafeSearchPath(File file, boolean prepend) {
+
+ // add the file's directory to list of search paths,
+ // if it is within the greenstone installation and not already in the list
+ if((!Gatherer.GS3 && file.getAbsolutePath().startsWith(Configuration.gsdl_path))
+ || (Gatherer.GS3 && file.getAbsolutePath().startsWith(Configuration.gsdl3_src_path))) {
+
+ File path = file.getParentFile();
+ if(!list_of_local_search_paths.contains(path)) {
+ if(prepend) {
+ list_of_local_search_paths.add(0, path);
+ } else {
+ list_of_local_search_paths.add(path);
+ }
+ }
+ } else {
+ DebugStream.println("### Location of file " + file + " not within GS. Not adding to list of search paths.");
+ }
+ }
+
+ /**
+ * resolveEntity() is not called for every file:
+ * http://www.postseek.com/meta/37735b65e6a459a6aa631f048cc5a0b6
+ *
+ * "I think the parser will call [resolveEntity() on an EntityResolver that has been set], if
+ * [the parser] is unable to find the DTD that your XML file refers to. So if you try to parse
+ * an XML file that doesn't refer to a DTD, it won't be called. And if you try to parse an XML
+ * file where the parser can find the DTD, it won't be called either."
+ *
+ */
+ public InputSource resolveEntity (String public_id, String system_id) {
+
+ DebugStream.println("### resolveEntity() called for " + system_id);
+
+ String temp_id = system_id;
+ if (temp_id.startsWith("file://")) {
+ File f = new File(system_id);
+ if (f.exists()) {
+
+ // check if inside GS2 or GS3 installation
+ if(f.getAbsolutePath().startsWith(Configuration.gsdl_path)
+ || f.getAbsolutePath().startsWith(Configuration.gsdl3_src_path)) {
+
+ DebugStream.println("### file denoted by systemID is inside GS: " + f.getAbsolutePath());
+
+ return new InputSource(system_id); // problem solved
+ } else {
+ DebugStream.println("\t### file denoted by systemID exists, but not located inside GS: "
+ + f.getAbsolutePath());
+ }
+ } else {
+
+ temp_id = f.getName();
+
+ //check in list of search paths
+ Iterator i = list_of_local_search_paths.iterator();
+ while(i.hasNext()) {
+ File searchPath = i.next();
+ DebugStream.println("### searching for entity '" + temp_id + "' in: "
+ + searchPath.getAbsolutePath());
+
+ File searchFile = new File(searchPath, temp_id);
+
+ if(searchFile.exists()) {
+ DebugStream.println("\t Found " + searchFile.getAbsolutePath());
+
+ String newpath = searchFile.getAbsolutePath();
+ if(Utility.isWindows()) {
+ newpath = "file:///" + newpath.replace("\\", "/");
+ } else { // linux version, file protocol starts with file:// and slashes are already URL-style
+ newpath = "file://" + newpath;
+ }
+ return new InputSource(newpath);
+
+ }
+ }
+
+ // else, external entity/file denoted by systemid is not in list of search paths, try classloader
+
+ }
+ } else {
+
+ DebugStream.println("### Entity is not a file: " + system_id);
+
+ if (temp_id.indexOf("/")!= -1) {
+ temp_id = temp_id.substring(temp_id.lastIndexOf("/")+1);
+ }
+ }
+
+ DebugStream.println("### Using classloader to attempt to resolve entity: " + temp_id);
+
+ // try using a class loader. If none provided, use current class loader
+ if (this.class_loader==null) {
+ this.class_loader = this.getClass().getClassLoader();
+ }
+ URL url = class_loader.getResource(temp_id);
+ if (url == null) {
+ return null;
+ }
+
+ return new InputSource("file://"+url.getFile());
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/GS3ServerThread.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/GS3ServerThread.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/GS3ServerThread.java (revision 31635)
@@ -0,0 +1,147 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: Sam McIntosh, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 2011 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+
+package org.greenstone.gatherer.util;
+
+import org.greenstone.gatherer.Configuration;
+import org.greenstone.gatherer.DebugStream;
+
+import java.io.File;
+
+/** Class for GS3, for issuing ant commands from GLI */
+public class GS3ServerThread extends Thread
+{
+ String _gsdl3_src_path = "";
+ String _ant_command = "";
+
+ public GS3ServerThread(String gsdl3_src_path, String ant_command)
+ {
+ _gsdl3_src_path = gsdl3_src_path;
+ _ant_command = ant_command; // "restart"
+ }
+
+
+ public void run()
+ {
+
+ ///System.err.println("**** GS3 server : " + _ant_command);
+
+ SafeProcess p = null;
+ if (Utility.isWindows()) {
+ if(_ant_command.indexOf("start") != -1) { // running an "ant (re)start" command on windows, run start
+ _ant_command = "start";
+ }
+
+ // The path in quotes, and the entire sequence of commands in quotes as well
+ // E.g. the following works in a Runtime.exec() call:
+ // cmd /C "cd "C:\path\to\greenstone3" && ant stop"
+ // and it preserves any spaces in the path to GSDL3SRCHOME (_gsdl3_src_path).
+ p = new SafeProcess("cmd /C \"cd \"" + _gsdl3_src_path + File.separator + "\" && ant " + _ant_command + "\"");
+ }
+ else {
+ if(_ant_command.indexOf("start") != -1) { // if running an "ant (re)start" command on non-Windows, run restart
+ _ant_command = "restart";
+ }
+ p = new SafeProcess(new String[]{"/bin/bash", "-c", "ant " + _ant_command + " -f \"" + _gsdl3_src_path + File.separator + "build.xml\""});
+ }
+
+
+ // in order for the process.waitFor() method to work with Java 6 (JRE 6 is included in GS binaries)
+ // need to make sure the IOstreams of the process are not blocked. For Java 7, this is not necessary
+ // and a waitFor() is sufficient. But with Java 6, the waitFor() causes the server to finally start
+ // after the user has quit GLI.
+ // Process takes no input, but we will still catch the process' instream too
+ // And we'll catch the error and output streams to prevent them from blocking during waitFor()
+ // (For normal input and output stream handling using the Gobblers, see FormatConversionDialog.java)
+
+
+ // prepare our SafeProcess object
+ p.setSplitStdErrorNewLines(true);
+
+ // run it
+ int result = p.runProcess(); // uses default process streamgobbler behaviours and
+ // does the important part: waitFor() the process (ant stop or start or re-start) to terminate.
+ // The int result returned is the exitvalue upon Process.waitFor() returning
+
+ if(result != 0) {
+ System.err.println("**** Failed to successfully " + _ant_command + " the GS3 server.");
+ }
+ ///else {
+ ///System.err.println("**** " + _ant_command + " of the GS3 server successful.");
+ ///}
+
+ }
+
+
+ // can't call ant stop from its own thread - what if GLI has exited by then?
+ // issue call to ant stop from the main GLI thread
+ //GS3ServerThread thread = new GS3ServerThread(Configuration.gsdl_path, "stop");
+ //thread.start();
+ // So, static function to issue the command to stop the server from GLI's own thread.
+ // This will block the main GLI thread until the server has stopped.
+ public static void stopServer() {
+
+ SafeProcess p = null;
+ if (Utility.isWindows()) {
+ // cmd /C "cd "C:\path\to\greenstone3" && ant stop"
+ p = new SafeProcess("cmd /C \"cd \"" + Configuration.gsdl3_src_path + File.separator + "\" && ant stop\"");
+ } else {
+ p = new SafeProcess(new String[]{"/bin/bash", "-c", "ant stop -f \"" + Configuration.gsdl3_src_path + File.separator + "build.xml\""});
+ }
+
+ System.err.println("Issuing stop command to GS3 Server. Waiting for GS3 server to stop...");
+ int result = p.runProcess();
+ if(result == 0) {
+ System.err.println("Successfully stopped GS3 server.");
+ //DebugStream.println("********** SUCCESSFULLY stopped THE GS3 SERVER ON EXIT");
+ }
+ else {
+ System.err.println("********** FAILED TO SUCCESSFULLY stop THE GS3 SERVER ON EXIT");
+ }
+
+ // doing a p.waitFor() without processing the Process' IOstreams causes blocking with Java 6
+ // (i.e. when JRE 6 included with GS binaries). However, p.waitFor() with Java 7 is fine.
+ /*if(p != null && p.waitFor() == 0) {
+ DebugStream.println("********** SUCCESSFULLY stopped THE GS3 SERVER ON EXIT");
+ }
+ else {
+ System.err.println("********** FAILED TO SUCCESSFULLY stop THE GS3 SERVER ON EXIT");
+ //throw new Exception ("Failed to successfully stop the GS3 server on exit.");
+ }*/
+
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/HTMLStringTokenizer.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/HTMLStringTokenizer.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/HTMLStringTokenizer.java (revision 31635)
@@ -0,0 +1,142 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.util;
+
+
+/** This class functions much like a StringTokenizer in that it tokenizes a long string into tokens, however this tokenizer cleverly notices HTML formatting tags. */
+public class HTMLStringTokenizer {
+ /** The current position in the source string. */
+ private int pos = 0;
+ /** The current token, usually created by the last nextToken call. */
+ private String current = null;
+ /** The previous token. */
+ private String previous = null;
+ /** The string to be tokenized, including any HTML markup. */
+ private String source = null;
+ /** Constructor.
+ * @param source The source String to be tokenized.
+ */
+ public HTMLStringTokenizer(String source) {
+ this.source = source;
+ // Parse the first token.
+ parseToken();
+ }
+
+ /** Determines if there are still tokens remaining unparsed in the source.
+ * @return A boolean which is true if there are more tokens.
+ */
+ public boolean hasMoreTokens() {
+ if(current != null && current.length() > 0) {
+ return true;
+ }
+ return false;
+ }
+
+ /** Determines if the tag currently being returned by sameToken is a tag.
+ * @return A boolean indicating if the token is a tag.
+ */
+ public boolean isTag() {
+ if(previous.startsWith("<") && previous.endsWith(">")) {
+ return true;
+ }
+ return false;
+ }
+
+ /** Retrieves the next token.
+ * @return A String representing the token.
+ */
+ public String nextToken() {
+ previous = current;
+ // Get the next token.
+ parseToken();
+ // Return previous.
+ return previous;
+ }
+
+ /** Parses the next token and stores it in current.
+ */
+ private void parseToken() {
+ boolean found = false;
+ boolean tag = false;
+ boolean text = false;
+ // Reset current
+ current = "";
+ // Parse away
+ dumpWhiteSpace();
+ while(pos < source.length() && !found) {
+ char c = (char)source.charAt(pos);
+ if(!tag && !text) {
+ if(c == '<') {
+ tag = true;
+ }
+ else {
+ text = true;
+ }
+ current = current + c;
+ }
+ // Reading a tag. Watch only for '>'.
+ else if(tag) {
+ if(c == '>') {
+ found = true;
+ }
+ current = current + c;
+ }
+ // Reading text. Watch for ' ' and '<'. Rollback '<'.
+ else if(text) {
+ if(c == ' ') {
+ found = true;
+ }
+ else if(c == '<') {
+ found = true;
+ pos--;
+ }
+ else {
+ current = current + c;
+ }
+ }
+ pos++;
+ }
+ }
+
+ /** Method to ignore whitespace in the source.
+ */
+ private void dumpWhiteSpace() {
+ while(pos < source.length() && source.charAt(pos) == ' ') {
+ pos++;
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/HashMap3D.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/HashMap3D.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/HashMap3D.java (revision 31635)
@@ -0,0 +1,138 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.util;
+
+/**************************************************************************************
+ * Written: 20/08/02
+ * Revised:
+ **************************************************************************************/
+import java.util.HashMap;
+import java.util.Iterator;
+
+/** Provides a HashMap implementation that indexes by two keys. Perfect for the storage of metadata references based on their metadata element and assigned value.
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.3
+ */
+public class HashMap3D
+ extends HashMap {
+
+ private int capacity = 4;
+
+ private int size = 0;
+
+ private Object last_key_one = null;
+ private Object last_key_two = null;
+ private Object last_value = null;
+
+ /** Default constructor.
+ */
+ public HashMap3D() {
+ super();
+ }
+ /** Constructor with a specific initial capacity.
+ * @param capacity The initial capacity as an int .
+ */
+ public HashMap3D(int capacity) {
+ super(capacity);
+ this.capacity = capacity;
+ }
+ /** Completely remove the contents of this HashMap and its child HashMaps. */
+ public void clear() {
+ size = 0;
+ Iterator iterator = values().iterator();
+ while(iterator.hasNext()) {
+ HashMap inner_mapping = (HashMap) iterator.next();
+ inner_mapping.clear();
+ inner_mapping = null;
+ }
+ iterator = null;
+ super.clear();
+ }
+ /** Determine if this hash map contains an entry for the given keys. Also cache this entry because theres a good chance the next get call will ask for this entry.
+ * @param key_one The first key as an Object .
+ * @param key_two The second key as an Object .
+ * @return true if such an entry exists, false otherwise.
+ */
+ public boolean contains(Object key_one, Object key_two) {
+ boolean result = false;
+ // Retrieve the hash mapping at key_one.
+ HashMap map = (HashMap) get(key_one);
+ // If there is such a map then retrieve the value at key_two.
+ if(map != null) {
+ last_value = map.get(key_two);
+ if(last_value != null) {
+ last_key_one = key_one;
+ last_key_two = key_two;
+ result = true;
+ }
+ }
+ return result;
+ }
+
+ /** Retrieve an entry from this three dimensional hash mapping.
+ * @param key_one The first key as an Object .
+ * @param key_two The second key as an Object .
+ * @return The value Object located at (key_one, key_two), or null if no such value.
+ */
+ public Object get(Object key_one, Object key_two) {
+ Object result = null;
+ if(key_one.equals(last_key_one) && key_two.equals(last_key_two)) {
+ result = last_value;
+ }
+ else {
+ // Retrieve the hash mapping at key_one.
+ HashMap map = (HashMap) get(key_one);
+ // If there is such a map then retrieve the value at key_two.
+ if(map != null) {
+ result = map.get(key_two);
+ if(result != null) {
+ last_key_one = key_one;
+ last_key_two = key_two;
+ last_value = result;
+ }
+ }
+ }
+ return result;
+ }
+
+ /** Put an entry into this three dimensional hash mapping.
+ * @param key_one The first key, to store this value under, as an Object .
+ * @param key_two The second key, to store this value under, as an Object .
+ * @param value The value Object itself.
+ */
+ public void put(Object key_one, Object key_two, Object value) {
+ // Retrieve the hash mapping at key_one, or if none exists create one.
+ HashMap map = (HashMap) get(key_one);
+ if(map == null) {
+ map = new HashMap(capacity);
+ put(key_one, map);
+ }
+ // Now add the value to this mapping.
+ map.put(key_two, value);
+ size++;
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/JarTools.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/JarTools.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/JarTools.java (revision 31635)
@@ -0,0 +1,141 @@
+/**
+ *############################################################################
+ * A component of the Greenstone Librarian Interface, part of the Greenstone
+ * digital library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
+ *
+ * Copyright (C) 2005 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################################################
+ */
+
+package org.greenstone.gatherer.util;
+
+
+import java.awt.*;
+import java.io.*;
+import java.net.*;
+import javax.swing.*;
+import org.greenstone.gatherer.DebugStream;
+
+
+public class JarTools
+{
+ static private Class root_class = null;
+ static private ImageIcon ERROR_ICON = null;
+
+
+ static public void initialise(Object root_object)
+ {
+ root_class = root_object.getClass();
+ ERROR_ICON = getImage("error.gif");
+ }
+
+
+ static public void extractFromJar(String filename, String dst_dir, boolean must_be_present)
+ {
+ try {
+ // setup input stream for slurping out file
+ InputStream fis = root_class.getResourceAsStream("/"+filename);
+ BufferedInputStream fbis = new BufferedInputStream(fis);
+ DataInputStream fdbis = new DataInputStream(fbis);
+
+ // set up output stream for writing to disk
+ String ofname = dst_dir + filename;
+ FileOutputStream fos = new FileOutputStream(ofname);
+ BufferedOutputStream bfos = new BufferedOutputStream(fos);
+
+ byte[] buf = new byte[1024];
+ int len;
+ int total_bytes = 0;
+ while ((len = fdbis.read(buf)) >= 0) {
+ bfos.write(buf,0,len);
+ total_bytes += len;
+ }
+
+ fdbis.close();
+ bfos.close();
+ }
+ catch (Exception exception) {
+ if (must_be_present) {
+ exception.printStackTrace();
+ }
+ }
+ }
+
+
+ /** Method to retrieve an image icon with the given filename found in classpath or the resouces directory.
+ * @return The specified ImageIcon , or an error image replacement if no such images exists.
+ */
+ static public ImageIcon getImage(String filename) {
+ return getImage(filename, false);
+ }
+
+ static public ImageIcon getImage(String filename, boolean wait_until_complete)
+ {
+ ImageIcon image = null;
+ try {
+ image = new ImageIcon(root_class.getResource("/images/" + filename));
+ }
+ catch (NullPointerException exception) {
+ System.err.println("Error: Could not load image " + filename);
+ DebugStream.println("Error: Could not load image " + filename);
+ }
+
+ if (image == null) {
+ image = ERROR_ICON;
+ }
+
+ if (wait_until_complete) {
+ int load_status;
+ do {
+ load_status = image.getImageLoadStatus();
+ }
+ while (load_status != MediaTracker.ABORTED && load_status != MediaTracker.ERRORED && load_status != MediaTracker.COMPLETE);
+ }
+
+ return image;
+ }
+
+
+ static public URL getResource(String resource_name)
+ {
+ return root_class.getResource(resource_name);
+ }
+
+
+ static public InputStream getResourceAsStream(String resource_name)
+ {
+ return root_class.getResourceAsStream(resource_name);
+ }
+
+
+ static public boolean isInJar(String filename)
+ {
+ try {
+ InputStream fis = root_class.getResourceAsStream("/" + filename);
+ fis.close();
+ }
+ catch (Exception exception) {
+ exception.printStackTrace();
+ return false;
+ }
+
+ return true;
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/PatternTokenizer.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/PatternTokenizer.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/PatternTokenizer.java (revision 31635)
@@ -0,0 +1,72 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.util;
+
+public class PatternTokenizer {
+ private int index;
+ private String content;
+ private String pattern;
+ public PatternTokenizer(String content, String pattern) {
+ this.content = content;
+ this.index = 0;
+ this.pattern = pattern;
+ }
+ public boolean hasMoreTokens() {
+ return (index != -1 && content.length() - index > 0);
+ }
+ public String nextToken() {
+ // Special case when index is zero
+ if(index == 0) {
+ index = content.indexOf(pattern);
+ // No occurance of pattern, return whole string
+ if(index == -1) {
+ return content;
+ }
+ // Return the fragment from 0 to index
+ else {
+ return content.substring(0, index);
+ }
+ }
+ // Otherwise
+ else {
+ // We must first skip the pattern, as it should be at the head of our current view into content
+ index = index + pattern.length();
+ int end_index = content.indexOf(pattern, index);
+ String fragment;
+ // No occurance of pattern, return fragment from index to length
+ if(end_index == -1) {
+ fragment = content.substring(index);
+ }
+ // Return the fragment from index to end index, then set index to end index
+ else {
+ fragment = content.substring(index, end_index);
+ }
+ index = end_index;
+ return fragment;
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/PortFinder.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/PortFinder.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/PortFinder.java (revision 31635)
@@ -0,0 +1,136 @@
+package org.greenstone.gatherer.util;
+
+/** Verbatim copy of what's in GS3's org.greenstone.server */
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.BindException;
+
+// When needing to use sockets, this class can be used to find an available port
+public class PortFinder {
+
+ // Port numbers range from 0 to 65,535. RESERVED PORTS are below 1025
+ // see http://www.gnu.org/software/hello/manual/libc/Ports.html
+ public static final int MAX_PORT = 65535;
+ public static final int PORTS_RESERVED = 1024;
+
+ // Considering a limited range of ports that will be reused (circular buffer)
+ public final int PORT_BASE;
+ public final int PORT_BLOCK_SIZE;
+
+ // Keep track of what port numbers we have checked for availability
+ private int nextFreePort;
+
+ // The socket port number that we will use
+ private int port = -1;
+
+
+ public PortFinder() {
+ this( PORTS_RESERVED+1, (MAX_PORT - PORTS_RESERVED) );
+ }
+
+ public PortFinder(int base, int blocksize) {
+ PORT_BASE = base;
+ PORT_BLOCK_SIZE = blocksize;
+
+ nextFreePort = PORT_BASE;
+ }
+
+ /** Circular buffer. Modifies the value of nextFreePort (the buffer index). */
+ private void incrementNextFreePort() {
+ int offset = nextFreePort - PORT_BASE;
+ offset = (offset + 1) % PORT_BLOCK_SIZE;
+ nextFreePort = PORT_BASE + offset;
+ }
+
+ /** Finds the first available port in the range specified during Constructor call.
+ * @return the number of an available port.
+ */
+ public int findPortInRange(boolean verbose) throws Exception {
+ try {
+ boolean foundFreePort = false;
+ for(int i = 0; i < PORT_BLOCK_SIZE; i++) {
+
+ if(isPortAvailable(nextFreePort, verbose)) {
+ foundFreePort = true;
+ break;
+
+ } else {
+ incrementNextFreePort();
+ }
+ }
+
+ if(foundFreePort) {
+ // Free port number currently found becomes the port number of the socket
+ // that will be used
+ this.port = nextFreePort;
+ incrementNextFreePort();
+
+ } else {
+ throw new Exception("Cannot find an available port in the range "
+ + PORT_BASE + "-" + (PORT_BASE+PORT_BLOCK_SIZE));
+ }
+
+ } catch(IOException e) {
+ System.err.println("Error when trying to find an available port. In PortFinder.findPort() " + e);
+ }
+ return port;
+ }
+
+ /** @return true if the portnumber is in the valid range. */
+ public static boolean isValidPortNumber(int portNum) {
+ return (portNum >= 0 && portNum <= MAX_PORT);
+ }
+
+ /** @return true if the portnumber is in the useable range. */
+ public static boolean isAssignablePortNumber(int portNum) {
+ return (portNum > PORTS_RESERVED && portNum <= MAX_PORT);
+ }
+
+ /** Finds an available port.
+ * @return the number of an available port.
+ */
+ public static int findAnyFreePort() throws Exception {
+ ServerSocket tmpSocket = null;
+ int portNumber = -1;
+ try {
+ // Creates a server socket, bound to the specified port.
+ // A port of 0 creates a socket on any free port.
+ tmpSocket = new ServerSocket(0);
+ portNumber = tmpSocket.getLocalPort();
+ tmpSocket.close();
+ } catch(Exception e) {
+ System.err.println("Unable to find a free port or close it. Got Exception: " + e);
+ tmpSocket = null;
+ }
+ return portNumber;
+ }
+
+ /** @return true if the portnum is available for use */
+ public static boolean isPortAvailable(int portnum, boolean verbose) {
+ ServerSocket tmpSocket = null;
+ try {
+ tmpSocket = new ServerSocket(portnum);
+ tmpSocket.close();
+ //if(verbose) {
+ //System.err.println("Port " + portnum + " not yet in use.");
+ //}
+ return true;
+
+ } catch(BindException ex){
+ // "Signals that an error occurred while attempting to bind a
+ // socket to a local address and port. Typically, the port is
+ // in use, or the requested local address could not be assigned."
+ if(verbose) {
+ System.err.println("Port " + portnum + " already in use or can't be assigned.");
+ }
+ tmpSocket = null;
+ return false;
+ } catch(Exception ex) {
+ // Some other problem creating or closing the server socket
+ System.err.println("Problem creating or closing server socket at port " + portnum);
+ tmpSocket = null;
+ return false;
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/RemoveContentBeforeRootElementXMLReader.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/RemoveContentBeforeRootElementXMLReader.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/RemoveContentBeforeRootElementXMLReader.java (revision 31635)
@@ -0,0 +1,186 @@
+package org.greenstone.gatherer.util;
+
+import java.io.Reader;
+import java.io.IOException;
+
+import java.io.File;
+import java.io.FileReader;
+
+import java.util.regex.Pattern;
+
+public class RemoveContentBeforeRootElementXMLReader extends Reader {
+
+ static final Pattern[] xmlIndicators;
+ static Pattern commentStart;
+ static Pattern commentStop;
+
+ static {
+ // generate the xml starting sequences we will try to match against
+ xmlIndicators = new Pattern[3];
+ try{
+ xmlIndicators[0] = Pattern.compile("<\\?xml"); //usual xml declaration
+ xmlIndicators[1] = Pattern.compile("/]"); // the beginning of a root node
+
+ commentStart = Pattern.compile("");
+
+ } catch ( java.util.regex.PatternSyntaxException pse ) {
+ System.err.println( "Pattern no good. " + pse );
+ xmlIndicators[0] = xmlIndicators[1] = xmlIndicators[2] = null;
+ commentStart = commentStop = null;
+ }
+ }
+
+ Reader ur;
+
+ String finalBuffer = null;
+ int finalBufferIndex = 0;
+
+ public RemoveContentBeforeRootElementXMLReader( Reader ur ) {
+
+ this.ur = ur;
+
+ int found = -1;
+ boolean inComment = false;
+ StringBuffer buffer = null;
+
+ for ( int c = 0; c != -1 && found == -1; ) {
+
+ //read a character
+ try {
+ c = ur.read();
+ } catch( Exception e ) {
+ System.err.println( "Exception while reading underlying Reader in RemoveContentBeforeRootElementXMLReader" );
+ }
+
+ //break out if we have reached the end of the input
+ if ( c == -1 ) {
+ break;
+ }
+
+ //we start buffering when we come across the first <
+ //regardless of whether it turns out to be a relevant < or not
+ if ( buffer == null && (char)c == '<' ) {
+ buffer = new StringBuffer();
+ }
+
+ //if not buffering, just display the character and move onto next character
+ if ( buffer == null ) {
+ System.err.print( (char)c );
+ continue;
+ }
+
+ buffer.append( (char)c );
+
+ //check for comment open or close
+ if ( !inComment ) {
+ if ( commentStart.matcher(buffer.toString()).find() ) {
+ inComment = true;
+ System.err.print( buffer.toString() );
+ buffer = new StringBuffer();
+ }
+ } else {
+ if ( commentStop.matcher(buffer.toString()).find() ) {
+ inComment = false;
+ System.err.print( buffer.toString() );
+ buffer = new StringBuffer();
+
+ //skip to reading next character
+ continue;
+ }
+ }
+
+ if ( !inComment ) {
+
+ //check each indicator to see if found
+ for ( int i = 0; i < xmlIndicators.length && found == -1; i++ ) {
+ if ( xmlIndicators[i].matcher(buffer.toString()).find() ) {
+ found = i;
+ String line = buffer.toString();
+ int lastIndex = line.lastIndexOf('<');
+ //flush the previous characters in the buffer to the console
+ System.err.print(line.substring(0, lastIndex));
+ buffer.delete(0, lastIndex);
+ finalBuffer = buffer.toString();
+ }
+ }
+
+ }
+ }
+
+ if ( found == -1 ) {
+ System.err.println( "RemoveContentBeforeRootElementXMLReader:\n" +
+ "The XML being loaded was not valid: couldn't find start of XML input" );
+ }
+
+ }
+
+ public int read( char[] cbuf, int off, int len ) throws IOException {
+
+ for ( int i=off; i finalBufferIndex ) {
+ char c = finalBuffer.charAt(finalBufferIndex++);
+ if ( finalBufferIndex == finalBuffer.length() ) {
+ finalBuffer = null;
+ }
+ return c;
+ }
+
+ return ur.read();
+ }
+
+ public void close() throws IOException {
+ ur.close();
+ }
+
+ public static void main ( String[] args ) {
+
+ //init
+ System.out.println( "------------\nWill now initialise the test reader\n------------" );
+ RemoveContentBeforeRootElementXMLReader parser = null;
+ try {
+ parser = new RemoveContentBeforeRootElementXMLReader(
+ new FileReader( new File("text.xml") ) );
+ } catch ( java.io.FileNotFoundException fnfe ) {
+ System.err.println( "Please create text.xml to test this class" );
+ System.exit(-1);
+ }
+
+ //read the rest of the input
+ System.out.println( "------------\nWill now read the rest of the input\n------------" );
+ try {
+ int c = 0;
+ while ( ( c = parser.read() ) != -1 ) {
+ System.out.print( (char)c );
+ }
+ } catch ( Exception e ) {
+ System.err.println("Exception: " + e);
+ }
+
+ }
+
+
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/SafeProcess.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/SafeProcess.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/SafeProcess.java (revision 31635)
@@ -0,0 +1,677 @@
+package org.greenstone.gatherer.util;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.Closeable;
+import java.io.File;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+
+import java.util.Arrays;
+
+import org.apache.log4j.*;
+
+// Use this class to run a Java Process. It follows the good and safe practices at
+// http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2
+// to avoid blocking problems that can arise from a Process' input and output streams.
+
+public class SafeProcess {
+
+ public static final int STDERR = 0;
+ public static final int STDOUT = 1;
+ public static final int STDIN = 2;
+
+ ///static Logger logger = Logger.getLogger(org.greenstone.util.SafeProcess.class.getName());
+
+ // input to SafeProcess and initialising it
+ private String command = null;
+ private String[] command_args = null;
+ private String[] envp = null;
+ private File dir = null;
+ private String inputStr = null;
+
+ // output from running SafeProcess.runProcess()
+ private String outputStr = "";
+ private String errorStr = "";
+ private int exitValue = -1;
+
+ // allow callers to process exceptions of the main process thread if they want
+ private ExceptionHandler exceptionHandler = null;
+
+ // whether std/err output should be split at new lines
+ private boolean splitStdOutputNewLines = false;
+ private boolean splitStdErrorNewLines = false;
+
+ // call one of these constructors
+
+ // cmd args version
+ public SafeProcess(String[] cmd_args)
+ {
+ command_args = cmd_args;
+ }
+
+ // cmd string version
+ public SafeProcess(String cmdStr)
+ {
+ command = cmdStr;
+ }
+
+ // cmd args with env version, launchDir can be null.
+ public SafeProcess(String[] cmd_args, String[] envparams, File launchDir)
+ {
+ command_args = cmd_args;
+ envp = envparams;
+ dir = launchDir;
+ }
+
+ // The important methods:
+ // to get the output from std err and std out streams after the process has been run
+ public String getStdOutput() { return outputStr; }
+ public String getStdError() { return errorStr; }
+ public int getExitValue() { return exitValue; }
+
+
+ // set any string to send as input to the process spawned by SafeProcess
+ public void setInputString(String sendStr) {
+ inputStr = sendStr;
+ }
+
+ // register a SafeProcess ExceptionHandler whose gotException() method will
+ // get called for each exception encountered
+ public void setExceptionHandler(ExceptionHandler exception_handler) {
+ exceptionHandler = exception_handler;
+ }
+
+ // set if you want the std output or err output to have \n at each newline read from the stream
+ public void setSplitStdOutputNewLines(boolean split) {
+ splitStdOutputNewLines = split;
+ }
+ public void setSplitStdErrorNewLines(boolean split) {
+ splitStdErrorNewLines = split;
+ }
+
+ private Process doRuntimeExec() throws IOException {
+ Process prcs = null;
+ Runtime rt = Runtime.getRuntime();
+
+ if(this.command != null) {
+ //logger.info("SafeProcess running: " + command);
+ System.err.println("SafeProcess running: " + command_args);
+ prcs = rt.exec(this.command);
+ }
+ else { // at least command_args must be set now
+
+ // http://stackoverflow.com/questions/5283444/convert-array-of-strings-into-a-string-in-java
+ //logger.info("SafeProcess running: " + Arrays.toString(command_args));
+ System.err.println("SafeProcess running: " + Arrays.toString(command_args));
+
+ if(this.envp == null) {
+ prcs = rt.exec(this.command_args);
+ } else { // launch process using cmd str with env params
+
+ if(this.dir == null) {
+ ///logger.info("\twith: " + Arrays.toString(this.envp));
+ ///System.err.println("\twith: " + Arrays.toString(this.envp));
+ prcs = rt.exec(this.command_args, this.envp);
+ } else {
+ ///logger.info("\tfrom directory: " + this.dir);
+ ///logger.info("\twith: " + Arrays.toString(this.envp));
+ ///System.err.println("\tfrom directory: " + this.dir);
+ ///System.err.println("\twith: " + Arrays.toString(this.envp));
+ prcs = rt.exec(this.command_args, this.envp, this.dir);
+ }
+ }
+ }
+
+ return prcs;
+ }
+
+ // Copied from gli's gui/FormatConversionDialog.java
+ private int waitForWithStreams(Process prcs,
+ SafeProcess.OutputStreamGobbler inputGobbler,
+ SafeProcess.InputStreamGobbler outputGobbler,
+ SafeProcess.InputStreamGobbler errorGobbler)
+ throws IOException, InterruptedException
+ {
+
+ // kick off the stream gobblers
+ inputGobbler.start();
+ errorGobbler.start();
+ outputGobbler.start();
+
+ // any error???
+ this.exitValue = prcs.waitFor(); // can throw an InterruptedException if process did not terminate
+
+ ///logger.info("Process exitValue: " + exitValue);
+ ///System.err.println("Process exitValue: " + exitValue);
+
+ // From the comments of
+ // http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2
+ // To avoid running into nondeterministic failures to get the process output
+ // if there's no waiting for the threads, call join() on each Thread (StreamGobbler) object.
+ // From Thread API: join() "Waits for this thread (the thread join() is invoked on) to die."
+ outputGobbler.join();
+ errorGobbler.join();
+ inputGobbler.join();
+
+
+ // set the variables the code that created a SafeProcess object may want to inspect
+ this.outputStr = outputGobbler.getOutput();
+ this.errorStr = errorGobbler.getOutput();
+
+ // Since we didn't have an exception, process should have terminated now (waitFor blocks until then)
+ // Set process to null so we don't forcibly terminate it below with process.destroy()
+ prcs = null;
+
+ return this.exitValue;
+ }
+
+
+ // Run a very basic process: with no reading from or writing to the Process' iostreams,
+ // this just execs the process and waits for it to return
+ public int runBasicProcess() {
+ Process prcs = null;
+ try {
+ // 1. create the process
+ prcs = doRuntimeExec();
+ // 2. basic waitFor the process to finish
+ this.exitValue = prcs.waitFor();
+
+
+ } catch(IOException ioe) {
+ if(exceptionHandler != null) {
+ exceptionHandler.gotException(ioe);
+ } else {
+ //logger.error("IOException: " + ioe.getMessage(), ioe);
+ System.err.println("IOException " + ioe.getMessage());
+ ioe.printStackTrace();
+ }
+ } catch(InterruptedException ie) {
+
+ if(exceptionHandler != null) {
+ exceptionHandler.gotException(ie);
+ } else {
+ //logger.error("Process InterruptedException: " + ie.getMessage(), ie);
+ System.err.println("Process InterruptedException " + ie.getMessage());
+ ///ie.printStackTrace(); // an interrupt here is not an error, it can be a cancel action
+ }
+
+ Thread.currentThread().interrupt();
+ } finally {
+
+ if( prcs != null ) {
+ prcs.destroy(); // see runProcess() below
+ }
+ }
+ return this.exitValue;
+ }
+
+ // Runs a process with default stream processing. Returns the exitValue
+ public int runProcess() {
+ return runProcess(null, null, null); // use default processing of all 3 of the process' iostreams
+ }
+
+ // Run a process with custom stream processing (any custom handlers passed in that are null
+ // will use the default stream processing).
+ // Returns the exitValue from running the Process
+ public int runProcess(CustomProcessHandler procInHandler,
+ CustomProcessHandler procOutHandler,
+ CustomProcessHandler procErrHandler)
+ {
+ Process prcs = null;
+ SafeProcess.OutputStreamGobbler inputGobbler = null;
+ SafeProcess.InputStreamGobbler errorGobbler = null;
+ SafeProcess.InputStreamGobbler outputGobbler = null;
+
+ try {
+ // 1. get the Process object
+ prcs = doRuntimeExec();
+
+
+ // 2. create the streamgobblers and set any specified handlers on them
+
+ // PROC INPUT STREAM
+ if(procInHandler == null) {
+ // send inputStr to process. The following constructor can handle inputStr being null
+ inputGobbler = // WriterToProcessInputStream
+ new SafeProcess.OutputStreamGobbler(prcs.getOutputStream(), this.inputStr);
+ } else { // user will do custom handling of process' InputStream
+ inputGobbler = new SafeProcess.OutputStreamGobbler(prcs.getOutputStream(), procInHandler);
+ }
+
+ // PROC ERR STREAM to monitor for any error messages or expected output in the process' stderr
+ if(procErrHandler == null) {
+ errorGobbler // ReaderFromProcessOutputStream
+ = new SafeProcess.InputStreamGobbler(prcs.getErrorStream(), splitStdErrorNewLines);
+ } else {
+ errorGobbler
+ = new SafeProcess.InputStreamGobbler(prcs.getErrorStream(), procErrHandler);
+ }
+
+ // PROC OUT STREAM to monitor for the expected std output line(s)
+ if(procOutHandler == null) {
+ outputGobbler
+ = new SafeProcess.InputStreamGobbler(prcs.getInputStream(), splitStdOutputNewLines);
+ } else {
+ outputGobbler
+ = new SafeProcess.InputStreamGobbler(prcs.getInputStream(), procOutHandler);
+ }
+
+
+ // 3. kick off the stream gobblers
+ this.exitValue = waitForWithStreams(prcs, inputGobbler, outputGobbler, errorGobbler);
+
+ } catch(IOException ioe) {
+ if(exceptionHandler != null) {
+ exceptionHandler.gotException(ioe);
+ } else {
+ //logger.error("IOexception: " + ioe.getMessage(), ioe);
+ System.err.println("IOexception " + ioe.getMessage());
+ ioe.printStackTrace();
+ }
+ } catch(InterruptedException ie) {
+
+ if(exceptionHandler != null) {
+ exceptionHandler.gotException(ie);
+ } else {
+ //logger.error("Process InterruptedException: " + ie.getMessage(), ie);
+ System.err.println("Process InterruptedException " + ie.getMessage());
+ ///ie.printStackTrace(); // an interrupt here is not an error, it can be a cancel action
+ }
+
+ // propagate interrupts to worker threads here?
+ // unless the interrupt emanated from any of them in any join()...
+ // Only if the thread SafeProcess runs in was interrupted
+ // do we propagate the interrupt to the worker threads.
+ // http://stackoverflow.com/questions/2126997/who-is-calling-the-java-thread-interrupt-method-if-im-not
+ // "I know that in JCiP it is mentioned that you should never interrupt threads you do not own"
+ // But SafeProcess owns the worker threads, so it have every right to interrupt them
+ // Also read http://stackoverflow.com/questions/13623445/future-cancel-method-is-not-working?noredirect=1&lq=1
+ if(Thread.currentThread().isInterrupted()) {
+ inputGobbler.interrupt();
+ errorGobbler.interrupt();
+ outputGobbler.interrupt();
+ }
+
+ // On catchingInterruptedException, re-interrupt the thread.
+ // This is just how InterruptedExceptions tend to be handled
+ // See also http://stackoverflow.com/questions/4906799/why-invoke-thread-currentthread-interrupt-when-catch-any-interruptexception
+ // and https://praveer09.github.io/technology/2015/12/06/understanding-thread-interruption-in-java/
+
+ // http://stackoverflow.com/questions/3976344/handling-interruptedexception-in-java
+ // http://stackoverflow.com/questions/4906799/why-invoke-thread-currentthread-interrupt-when-catch-any-interruptexception
+ // "Only code that implements a thread's interruption policy may swallow an interruption request. General-purpose task and library code should never swallow interruption requests."
+ // Does that mean that since this code implements this thread's interruption policy, it's ok
+ // to swallow the interrupt this time and not let it propagate by commenting out the next line?
+ Thread.currentThread().interrupt(); // re-interrupt the thread - which thread? Infinite loop?
+ } finally {
+
+ // Moved into here from GS2PerlConstructor which said
+ // "I need to somehow kill the child process. Unfortunately Thread.stop() and Process.destroy() both fail to do this. But now, thankx to the magic of Michaels 'close the stream suggestion', it works fine."
+ // http://steveliles.github.io/invoking_processes_from_java.html
+ // http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2
+ // http://mark.koli.ch/leaky-pipes-remember-to-close-your-streams-when-using-javas-runtimegetruntimeexec
+ if( prcs != null ) {
+ prcs.destroy();
+ }
+ }
+
+ return this.exitValue;
+ }
+
+ public int runProcess(LineByLineHandler outLineByLineHandler, LineByLineHandler errLineByLineHandler)
+ {
+ Process prcs = null;
+ SafeProcess.OutputStreamGobbler inputGobbler = null;
+ SafeProcess.InputStreamGobbler errorGobbler = null;
+ SafeProcess.InputStreamGobbler outputGobbler = null;
+
+ try {
+ // 1. get the Process object
+ prcs = doRuntimeExec();
+
+
+ // 2. create the streamgobblers and set any specified handlers on them
+
+ // PROC INPUT STREAM
+ // send inputStr to process. The following constructor can handle inputStr being null
+ inputGobbler = // WriterToProcessInputStream
+ new SafeProcess.OutputStreamGobbler(prcs.getOutputStream(), this.inputStr);
+
+ // PROC ERR STREAM to monitor for any error messages or expected output in the process' stderr
+ errorGobbler // ReaderFromProcessOutputStream
+ = new SafeProcess.InputStreamGobbler(prcs.getErrorStream(), splitStdErrorNewLines);
+ // PROC OUT STREAM to monitor for the expected std output line(s)
+ outputGobbler
+ = new SafeProcess.InputStreamGobbler(prcs.getInputStream(), splitStdOutputNewLines);
+
+
+ // 3. register line by line handlers, if any were set, for the process stderr and stdout streams
+ if(outLineByLineHandler != null) {
+ outputGobbler.setLineByLineHandler(outLineByLineHandler);
+ }
+ if(errLineByLineHandler != null) {
+ errorGobbler.setLineByLineHandler(errLineByLineHandler);
+ }
+
+
+ // 4. kick off the stream gobblers
+ this.exitValue = waitForWithStreams(prcs, inputGobbler, outputGobbler, errorGobbler);
+
+ } catch(IOException ioe) {
+ if(exceptionHandler != null) {
+ exceptionHandler.gotException(ioe);
+ } else {
+ //logger.error("IOexception: " + ioe.getMessage(), ioe);
+ System.err.println("IOexception " + ioe.getMessage());
+ ioe.printStackTrace();
+ }
+ } catch(InterruptedException ie) {
+
+ if(exceptionHandler != null) {
+ exceptionHandler.gotException(ie);
+ } else {
+ //logger.error("Process InterruptedException: " + ie.getMessage(), ie);
+ System.err.println("Process InterruptedException " + ie.getMessage());
+ ///ie.printStackTrace(); // an interrupt here is not an error, it can be a cancel action
+ }
+
+ // propagate interrupts to worker threads here?
+ // unless the interrupt emanated from any of them in any join()...
+ // Only if the thread SafeProcess runs in was interrupted
+ // do we propagate the interrupt to the worker threads.
+ // http://stackoverflow.com/questions/2126997/who-is-calling-the-java-thread-interrupt-method-if-im-not
+ // "I know that in JCiP it is mentioned that you should never interrupt threads you do not own"
+ // But SafeProcess owns the worker threads, so it have every right to interrupt them
+ // Also read http://stackoverflow.com/questions/13623445/future-cancel-method-is-not-working?noredirect=1&lq=1
+ if(Thread.currentThread().isInterrupted()) {
+ inputGobbler.interrupt();
+ errorGobbler.interrupt();
+ outputGobbler.interrupt();
+ }
+
+ // On catchingInterruptedException, re-interrupt the thread.
+ // This is just how InterruptedExceptions tend to be handled
+ // See also http://stackoverflow.com/questions/4906799/why-invoke-thread-currentthread-interrupt-when-catch-any-interruptexception
+ // and https://praveer09.github.io/technology/2015/12/06/understanding-thread-interruption-in-java/
+
+ // http://stackoverflow.com/questions/3976344/handling-interruptedexception-in-java
+ // http://stackoverflow.com/questions/4906799/why-invoke-thread-currentthread-interrupt-when-catch-any-interruptexception
+ // "Only code that implements a thread's interruption policy may swallow an interruption request. General-purpose task and library code should never swallow interruption requests."
+ // Does that mean that since this code implements this thread's interruption policy, it's ok
+ // to swallow the interrupt this time and not let it propagate by commenting out the next line?
+ Thread.currentThread().interrupt(); // re-interrupt the thread - which thread? Infinite loop?
+ } finally {
+
+ // Moved into here from GS2PerlConstructor which said
+ // "I need to somehow kill the child process. Unfortunately Thread.stop() and Process.destroy() both fail to do this. But now, thankx to the magic of Michaels 'close the stream suggestion', it works fine."
+ // http://steveliles.github.io/invoking_processes_from_java.html
+ // http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2
+ // http://mark.koli.ch/leaky-pipes-remember-to-close-your-streams-when-using-javas-runtimegetruntimeexec
+ if( prcs != null ) {
+ prcs.destroy();
+ }
+ }
+
+ return this.exitValue;
+ }
+
+
+//******************** Inner class and interface definitions ********************//
+// Static inner classes can be instantiated without having to instantiate an object of the outer class first
+
+// Can have public static interfaces too,
+// see http://stackoverflow.com/questions/71625/why-would-a-static-nested-interface-be-used-in-java
+// Implementors need to take care that the implementations are thread safe
+// http://stackoverflow.com/questions/14520814/why-synchronized-method-is-not-included-in-interface
+public static interface ExceptionHandler {
+
+ // when implementing ExceptionHandler.gotException(), if it manipulates anything that's
+ // not threadsafe, declare gotException() as a synchronized method to ensure thread safety
+ public void gotException(Exception e); // can't declare as synchronized in interface method declaration
+}
+
+// Write your own run() body for any StreamGobbler. You need to create an instance of a class
+// extending CustomProcessHandler for EACH IOSTREAM of the process that you want to handle.
+// Do not create a single CustomProcessHandler instance and reuse it for all three streams,
+// i.e. don't call SafeProcess' runProcess(x, x, x); It should be runProcess(x, y, z).
+// Make sure your implementation is threadsafe if you're sharing immutable objects between the threaded streams
+// example implementation is in the GS2PerlConstructor.SynchronizedProcessHandler class.
+// CustomProcessHandler is made an abstract class instead of an interface to force classes that want
+// to use a CustomProcessHandler to create a separate class that extends CustomProcessHandler, rather than
+// that the classes that wish to use it "implementing" the CustomProcessHandler interface itself: the
+// CustomProcessHandler.run() method may then be called in the major thread from which the Process is being
+// executed, rather than from the individual threads that deal with each iostream of the Process.
+public static abstract class CustomProcessHandler {
+
+ protected final int source;
+
+ protected CustomProcessHandler(int src) {
+ this.source = src; // STDERR or STDOUT or STDIN
+ }
+
+ public abstract void run(Closeable stream); //InputStream or OutputStream
+}
+
+// When using the default stream processing to read from a process' stdout or stderr stream, you can
+// create a class extending LineByLineHandler for the process' err stream and one for its output stream
+// to do something on a line by line basis, such as sending the line to a log
+public static abstract class LineByLineHandler {
+ protected final int source;
+
+ protected LineByLineHandler(int src) {
+ this.source = src; // STDERR or STDOUT
+ }
+
+ public abstract void gotLine(String line); // first non-null line
+ public abstract void gotException(Exception e); // for when an exception occurs instead of getting a line
+}
+
+
+//**************** StreamGobbler Inner class definitions (stream gobblers copied from GLI) **********//
+
+// http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2
+// This class is used in FormatConversionDialog to properly read from the stdout and stderr
+// streams of a Process, Process.getInputStream() and Process.getErrorSream()
+public static class InputStreamGobbler extends Thread
+{
+ private InputStream is = null;
+ private StringBuffer outputstr = new StringBuffer();
+ private boolean split_newlines = false;
+ private CustomProcessHandler customHandler = null;
+ private LineByLineHandler lineByLineHandler = null;
+
+ public InputStreamGobbler(InputStream is)
+ {
+ this.is = is;
+ this.split_newlines = false;
+ }
+
+ public InputStreamGobbler(InputStream is, boolean split_newlines)
+ {
+ this.is = is;
+ this.split_newlines = split_newlines;
+ }
+
+ public InputStreamGobbler(InputStream is, CustomProcessHandler customHandler)
+ {
+ this.is = is;
+ this.customHandler = customHandler;
+ }
+
+ public void setLineByLineHandler(LineByLineHandler lblHandler) {
+ this.lineByLineHandler = lblHandler;
+ }
+
+ // default run() behaviour
+ public void runDefault()
+ {
+ BufferedReader br = null;
+ try {
+ br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+ String line=null;
+ while ( (line = br.readLine()) != null ) {
+
+ if(this.isInterrupted()) { // should we not instead check if SafeProcess thread was interrupted?
+ //logger.info("Got interrupted when reading lines from process err/out stream.");
+ //System.err.println("InputStreamGobbler.runDefault() Got interrupted when reading lines from process err/out stream.");
+ break; // will go to finally block
+ }
+
+ //System.out.println("@@@ GOT LINE: " + line);
+ outputstr.append(line);
+ if(split_newlines) {
+ outputstr.append(Utility.NEWLINE); // "\n" is system dependent (Win must be "\r\n")
+ }
+
+ if(lineByLineHandler != null) { // let handler deal with newlines
+ lineByLineHandler.gotLine(line);
+ }
+ }
+ } catch (IOException ioe) {
+ if(lineByLineHandler != null) {
+ lineByLineHandler.gotException(ioe);
+ } else {
+ //logger.error("Exception when reading from a process' stdout/stderr stream: ", ioe);
+ System.err.println("Exception when reading from a process' stdout/stderr stream: ");
+ ioe.printStackTrace();
+ }
+ } finally {
+ SafeProcess.closeResource(br);
+ }
+ }
+
+ public void runCustom() {
+ this.customHandler.run(is);
+ }
+
+ public void run() {
+ if(this.customHandler == null) {
+ runDefault();
+ } else {
+ runCustom();
+ }
+ }
+
+ public String getOutput() {
+ return outputstr.toString(); // implicit toString() call anyway. //return outputstr;
+ }
+} // end static inner class InnerStreamGobbler
+
+
+// http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2
+// This class is used in FormatConversionDialog to properly write to the inputstream of a Process
+// Process.getOutputStream()
+public static class OutputStreamGobbler extends Thread
+{
+ private OutputStream os = null;
+ private String inputstr = "";
+ private CustomProcessHandler customHandler = null;
+
+ public OutputStreamGobbler(OutputStream os) {
+ this.os = os;
+ }
+
+ public OutputStreamGobbler(OutputStream os, String inputstr)
+ {
+ this.os = os;
+ this.inputstr = inputstr;
+ }
+
+ public OutputStreamGobbler(OutputStream os, CustomProcessHandler customHandler) {
+ this.os = os;
+ this.customHandler = customHandler;
+ }
+
+ // default run() behaviour
+ public void runDefault() {
+
+ if (inputstr == null) {
+ return;
+ }
+
+ BufferedWriter osw = null;
+ try {
+ osw = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
+ //System.out.println("@@@ SENDING LINE: " + inputstr);
+ osw.write(inputstr, 0, inputstr.length());
+ osw.newLine();//osw.write("\n");
+ osw.flush();
+
+ // Don't explicitly send EOF when using StreamGobblers as below,
+ // as the EOF char is echoed to output.
+ // Flushing the write handle and/or closing the resource seems
+ // to already send EOF silently.
+
+ /*if(Utility.isWindows()) {
+ osw.write("\032"); // octal for Ctrl-Z, EOF on Windows
+ } else { // EOF on Linux/Mac is Ctrl-D
+ osw.write("\004"); // octal for Ctrl-D, see http://www.unix-manuals.com/refs/misc/ascii-table.html
+ }
+ osw.flush();
+ */
+ } catch (IOException ioe) {
+ //logger.error("Exception writing to SafeProcess' inputstream: ", ioe);
+ System.err.println("Exception writing to SafeProcess' inputstream: ");
+ ioe.printStackTrace();
+ } finally {
+ SafeProcess.closeResource(osw);
+ }
+ }
+
+ // call the user's custom handler for the run() method
+ public void runCustom() {
+ this.customHandler.run(os);
+ }
+
+ public void run()
+ {
+ if(this.customHandler == null) {
+ runDefault();
+ } else {
+ runCustom();
+ }
+
+ }
+} // end static inner class OutputStreamGobbler
+
+
+//**************** Useful static methods. Copied from GLI's Utility.java ******************
+ // For safely closing streams/handles/resources.
+ // For examples of use look in the Input- and OutputStreamGobbler classes.
+ // http://docs.oracle.com/javase/tutorial/essential/exceptions/finally.html
+ // http://stackoverflow.com/questions/481446/throws-exception-in-finally-blocks
+ public static boolean closeResource(Closeable resourceHandle) {
+ boolean success = false;
+ try {
+ if(resourceHandle != null) {
+ resourceHandle.close();
+ resourceHandle = null;
+ success = true;
+ }
+ } catch(Exception e) {
+ //logger.error("Exception closing resource: ", e);
+ System.err.println("Exception closing resource: " + e.getMessage());
+ e.printStackTrace();
+ resourceHandle = null;
+ success = false;
+ } finally {
+ return success;
+ }
+ }
+
+ public static boolean closeProcess(Process prcs) {
+ boolean success = true;
+ if( prcs != null ) {
+ success = success && closeResource(prcs.getErrorStream());
+ success = success && closeResource(prcs.getOutputStream());
+ success = success && closeResource(prcs.getInputStream());
+ prcs.destroy();
+ }
+ return success;
+ }
+
+} // end class SafeProcess
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/StaticStrings.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/StaticStrings.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/StaticStrings.java (revision 31635)
@@ -0,0 +1,296 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.util;
+
+
+/** Were you to guess that this is a class object choc-a-block full of static stringy goodness, you'd be right. They come in several flavours: Those ending _STR are strings you might find as values in XML, _ELEMENT are element names, _ATTRIBUTE are attribute names, _CHAR are particular characters while _CHARACTER are also characters but expressed as strings (for regex purposes ie startsWith, endsWith, indexOf and replaceAll); _PATTERN are strings which require extra escaping to put them through regex. Finally anything else is just a static string used within GLI. */
+public class StaticStrings {
+ static final public char CLOSEBRACKET_CHAR = ']';
+ static final public char DOUBLEQUOTE_CHAR = '\"';
+ static final public char BACKSLASH_CHAR = '\\';
+ static final public char FORWARDSLASH_CHAR = '/';
+ static final public char NEW_LINE_CHAR = '\n';
+ static final public char NS_SEP = '.';
+ static final public char OPENBRACKET_CHAR = '[';
+ static final public char SINGLEQUOTE_CHAR = '\'';
+ static final public char SPACE_CHAR = ' ';
+ static final public char STAR_CHAR = '*';
+ static final public String ACCENTFOLD_OPTION_STR = "accentfold";
+ static final public String ALLFIELDS_STR = "allfields";
+ static final public String ARCPLUG_STR = "ArchivesInfPlugin";
+ static final public String ASSIGNED_ATTRIBUTE = "assigned";
+ static final public String ASSOCIATIONS_ELEMENT = "Associations";
+ static final public String ATTRIBUTE_ELEMENT = "Attribute";
+ static final public String BROWSE_STR = "browse";
+ static final public String BUILDTYPE_ELEMENT = "BuildType";
+ static final public String BUILDTYPE_STR = "buildtype";
+ static final public String CASEFOLD_OPTION_STR = "casefold";
+ static final public String CCS_STR = "ccs";
+ static final public String CLASSIFIER_STR = "classifier";
+ static final public String CLASSIFY_ELEMENT = "Classify";
+ static final public String CLASSIFY_STR = "classify";
+ static final public String CLOSE_PARENTHESIS_CHARACTER = ")";
+ static final public String CODE_ATTRIBUTE = "code";
+ static final public String COLNAME_PATTERN = "";
+ static final public String COLLECT_CFG = "collect.cfg";
+ static final public String COLLECTION_CFG_XML = "collectionConfig.xml";
+ static final public String COLLECTDIR_ARGUMENT = "-collectdir";
+ static final public String COLLECTION_ELEMENT = "Collection";
+ static final public String COLLECTION_STR = "Collection: ";
+ static final public String COLLECTIONMETADATA_COLLECTIONNAME_STR = "collectionname";
+ static final public String COLLECTIONMETADATA_COLLECTIONEXTRA_STR = "collectionextra";
+ static final public String COLLECTIONMETADATA_CREATOR_ELEMENT = "Creator";
+ static final public String COLLECTIONMETADATA_CREATOR_STR = "creator";
+ static final public String COLLECTIONMETADATA_ELEMENT = "CollectionMetadata";
+ static final public String COLLECTIONMETADATA_ICONCOLLECTION_STR = "iconcollection";
+ static final public String COLLECTIONMETADATA_ICONCOLLECTIONSMALL_STR = "iconcollectionsmall";
+ static final public String COLLECTIONMETADATA_MAINTAINER_ELEMENT = "Maintainer";
+ static final public String COLLECTIONMETADATA_MAINTAINER_STR = "maintainer";
+ static final public String COLLECTIONMETADATA_PUBLIC_ELEMENT = "Public";
+ static final public String COLLECTIONMETADATA_PUBLIC_STR = "public";
+ static final public String COLLECTIONMETADATA_COLLECTGROUP_STR = "collectgroup";
+ static final public String COLLECTIONMETADATA_STR = "collectionmeta";
+ static final public String COLON_CHARACTER = ":";
+ static final public String COMMA_CHARACTER = ",";
+ static final public String COMMENTED_INDEXES_STR = "#indexes";
+ static final public String COMMENTED_INDEX_DEFAULT_STR = "#defaultindex";
+ static final public String COMMENTED_LEVELS_STR = "#levels";
+ static final public String COMMENTED_LEVEL_DEFAULT_STR = "#defaultlevel";
+ static final public String COMMENTED_INDEXOPTIONS_STR = "#indexoptions";
+ static final public String CONTENT_ATTRIBUTE = "content";
+ static final public String CONTENT_ELEMENT = "content";
+ static final public String DATABASETYPE_ELEMENT = "infodbtype";
+ static final public String DATABASETYPE_STR = "infodbtype";
+ static final public String DATELIST_CLASSIFIER = "DateList";
+ static final public String DEBUG_ARGUMENT = "-debug";
+ static final public String DESCRIPTION_ELEMENT = "Description";
+ static final public String DESCRIPTION_STR = "description";
+ static final public String DICTIONARY_ATTRIBUTE = "dictionary";
+ static final public String DIRECTORY_MAPPINGS_ELEMENT = "DirectoryMappings";
+ static final public String DISPLAYITEMLIST_STR = "displayItemList";
+ static final public String DISPLAY_STR = "display";
+ static final public String DISPLAYITEM_STR = "displayItem";
+ static final public String DISPLAYITEM_ELEMENT = "DisplayItem"; // Note: 1st letter is titlecase
+ static final public String DOCUMENT_STR = "document";
+ static final public String DOT_CHARACTER = ".";
+ static final public String EMBEDMETAPLUG_STR = "EmbeddedMetadataPlugin";
+ static final public String EMPTY_STR = "";
+ static final public String ENGLISH_LANGUAGE_STR = "en";
+ static final public String ENTRY_ELEMENT = "Entry";
+ static final public String ENUM_STR = "enum";
+ static final public String ENUM_STRING_STR = "enumstring";
+ static final public String EQUALS_CHARACTER = "=";
+ static final public String EXCLAMATION_CHARACTER = "!";
+ static final public String EXCLUDE_STR = "exclude";
+ static final public String EXTENSION_ATTRIBUTE = "extension";
+ static final public String EXTRACTED_NAMESPACE = "ex.";
+ static final public String FACET_ELEMENT = "Facet";
+ static final public String FACET_STR = "Facet";
+ static final public String FACET_LOW_STR = "facet";
+ static final public String FALSE_STR = "false";
+ static final public String FEEDBACK_ARGUMENT = "-feedback";
+ static final public String FILE_ATTRIBUTE = "file";
+ static final public String FILENAME_STR = "Filename";
+ static final public String FILTER_ATTRIBUTE = "filter";
+ static final public String FLAG_STR = "flag";
+ static final public String FORMAT_ELEMENT = "Format";
+ static final public String FORMAT_STR = "format";
+ static final public String FORMAT_START_TAG = "";
+ static final public String FORMAT_END_TAG = " ";
+ static final public String FURTHER_DIALOG_INDICATOR = "...";
+ static final public String FEDORA_MODE= "-fedora";
+ static final public String FEDORA_HOME = "-fedora_home";
+ static final public String FEDORA_VERSION = "-fedora_version";
+ static final public String FEDORA_HOSTNAME = "-fedora_hostname";
+ static final public String FEDORA_PORT = "-fedora_port";
+ static final public String FEDORA_USERNAME = "-fedora_username";
+ static final public String FEDORA_PASSWORD = "-fedora_password";
+ static final public String FEDORA_PROTOCOL = "-fedora_protocol";
+ static final public String GLI_ATTRIBUTE = "gli";
+ static final public String GLISERVER_URL_ARGUMENT = "-gliserver_url";
+ static final public String GLOBAL_STR = "global";
+ static final public String GREATER_THAN_CHARACTER = ">";
+ static final public String GSDL_ARGUMENT = "-gsdl";
+ static final public String GSDL3_ARGUMENT = "-gsdl3";
+ static final public String GSDL3_SRC_ARGUMENT = "-gsdl3src";
+ static final public String GSDLOS_ARGUMENT = "-gsdlos";
+ static final public String HFILE_ARGUMENT = "-hfile";
+ static final public String HIERARCHY_CLASSIFIER = "Hierarchy";
+ static final public String HIERARCHY_STR = "hierarchy";
+ static final public String HELP_ARGUMENT = "-help";
+ static final public String HTM_FILE_EXTENSION = ".htm";
+ static final public String ICON_STR = "icon";
+ static final public String ID_STR = "id";
+ static final public String IMAGES_PATH_RELATIVE_TO_GSDL_PREFIX = "_httpprefix_/collect//images/";
+ static final public String INCLUDE_STR = "include";
+ static final public String INDEX_ELEMENT = "Index";
+ static final public String INDEX_LOW_STR = "index";
+ static final public String INDEX_DEFAULT_ELEMENT = "DefaultIndex";
+ static final public String INDEX_DEFAULT_ELEMENT_LOWERCASE = "defaultIndex";
+ static final public String INDEX_DEFAULT_STR = "defaultindex";
+ static final public String INDEX_STR = "indexes";
+ static final public String INDEXES_ELEMENT = "Indexes";
+ static final public String INDEXOPTION_ELEMENT = "Option";
+ static final public String INDEXOPTIONS_ELEMENT = "IndexOption";
+ static final public String INDEXOPTION_STR = "indexOption";
+ static final public String INDEXOPTIONS_STR = "indexoptions";
+ static final public String INDEXOPTION_DEFAULT_ELEMENT = "DefaultIndexOption";
+ static final public String INFODB_STR = "infodb";
+ static final public String INT_STR = "int";
+ static final public String IMPORT_STR = "import";
+ static final public String IMPORT_OPTION_STR = "importOption";
+ static final public String BUILD_OPTION_STR = "buildOption";
+
+ static final public String[] KEEP_PLUG = { "GreenstoneXMLPlugin", "GreenstoneMETSPlugin", "EmbeddedMetadataPlugin" };
+ static final public String KEY_ATTRIBUTE = "key";
+ static final public String LANGUAGE_ARGUMENT = "l=";
+ static final public String LANGUAGE_ATTRIBUTE = "language";
+ static final public String LANGUAGE_ELEMENT = "Language";
+ static final public String LANGUAGE_DEFAULT_ELEMENT = "DefaultLanguage";
+ static final public String LANGUAGE_DEFAULT_INDEX_ELEMENT = "defaultIndexLanguage";
+ static final public String LANGUAGE_DEFAULT_STR = "defaultlanguage";
+ static final public String LANGUAGE_INDEX_ELEMENT = "indexLanguage";
+ static final public String LANGUAGE_METADATA_ELEMENT = "LanguageMetadata";
+ static final public String LANGUAGE_METADATA_ELEMENT_STR = "languageMetadata";
+ static final public String LANGUAGE_METADATA_STR = "languagemetadata";
+ static final public String LANG_STR = "lang";
+ static final public String LANG_ATTRIBUTE = "lang";
+ static final public String LANGUAGE_STR = "language";
+ static final public String LANGUAGES_ELEMENT = "Languages";
+ static final public String LANGUAGES_STR = "languages";
+ static final public String LBRACKET_CHARACTER = "[";
+ static final public String LESS_THAN_CHARACTER = "<";
+ static final public String LEVEL_ATTRIBUTE = "level";
+ static final public String LEVEL_ELEMENT = "level";
+ static final public String LEVEL_DEFAULT_ELEMENT = "defaultLevel";
+ static final public String LEVEL_DEFAULT_STR = "defaultlevel";
+ static final public String LEVELS_STR = "levels";
+ static final public String LIBRARY_URL_ARGUMENT = "-library_url";
+ static final public String LINUX_OPEN_COMMAND = "xdg-open %1";
+ static final public String LOCAL_LIBRARY_ARGUMENT = "-local_library";
+ static final public String LOAD_ARGUMENT = "-load";
+ static final public String MAC_OPEN_COMMAND = "open %1";
+ static final public String MAPPING_ELEMENT = "Mapping";
+ static final public String MDS_ATTRIBUTE = "mds";
+ static final public String METADATA_ARGUMENT = "-metadata";
+ static final public String METADATA_ELEMENT = "Metadata";
+ static final public String METADATA_SET_EXTENSION = ".mds";
+ static final public String METADATA_STR = "metadata";
+ static final public String METADATALIST_STR = "metadataList";
+ static final public String METADATALIST_ELEMENT = "MetadataList";
+ static final public String METADATA_TYPE_STR = "metadata";
+ static final public String METADATA_XML = "metadata.xml";
+ static final public String METADATAXMLPLUG_STR = "MetadataXMLPlugin";
+ static final public String MGPP_ATTRIBUTE = "mgpp_enabled";
+ static final public String MINUS_CHARACTER = "-";
+ static final public String MODE_STR = "Mode: ";
+ static final public String MODEL_COLLECTION_NAME = "modelcol";
+ static final public String NAME_ATTRIBUTE = "name";
+ static final public String NAME_STR = "name";
+ static final public String NAME_ELEMENT = "Name";
+ static final public String NEWLINE_CHARACTER = "\\";
+ static final public String NO_LOAD_ARGUMENT = "-no_load";
+ static final public String ONE_CHARACTER = "1";
+ static final public String OPEN_PARENTHESIS_CHARACTER = "(";
+ static final public String OPTION_ELEMENT = "Option";
+ static final public String OPTION_STR = "option";
+
+ static final public String OPTIONS_ATTRIBUTE = "options";
+ static final public String PARAGRAPH_STR = "paragraph";
+ static final public String PERL_ARGUMENT = "-perl";
+ static final public String PHIND_CLASSIFIER = "Phind";
+ static final public String PLUGIN_ELEMENT = "Plugin";
+ static final public String PLUGIN_STR = "plugin";
+ static final public String PLUGINLIST_STR = "pluginList";
+ static final public String PREDEFINED_METADATA_ATTRIBUTE = "predefined";
+ static final public String RBRACKET_CHARACTER = "]";
+ static final public String RECPLUG_STR = "DirectoryPlugin";
+ static final public String REGEXP_STR = "regexp";
+ static final public String REPLACELIST_STR = "replaceList";
+ static final public String REPLACELISTREF_STR = "replaceListRef";
+ static final public String RESTRICTED_METADATA_ATTRIBUTE = "restricted";
+ static final public String SEARCHTYPE_ELEMENT = "searchType";
+ static final public String SEARCHTYPE_STR = "searchtype";
+ static final public String SEARCH_STR = "search";
+ static final public String SECURITY_STR = "security";
+ static final public String SECTION_ELEMENT = "Section";
+ static final public String SECTION_STR = "section";
+ static final public String SEPARATE_CJK_OPTION_STR = "separate_cjk";
+ static final public String SEPARATOR_ATTRIBUTE = "separator";
+ static final public String SEPARATOR_CHARACTER = "/";
+ static final public String SERVICE_RACK_LIST_ELEMENT = "serviceRackList";
+ static final public String SERVLET_ARGUMENT ="-servlet";
+ static final public String SINGLE_QUOTE_CHARACTER = "'";
+ static final public String SITE_ARGUMENT = "-site";
+ static final public String SMALLICON_STR = "smallicon";
+ static final public String SOLR_ELEMENT = "Solr";
+ static final public String SORT_ELEMENT = "Sort";
+ static final public String SORT_STR = "Sort";
+ static final public String SORT_LOW_STR = "sort";
+ static final public String SORT_DEFAULT_ELEMENT = "defaultSort";
+ static final public String SPACE_CHARACTER = " ";
+ static final public String SPECIAL_ATTRIBUTE = "special";
+ static final public String SPEECH_CHARACTER = "\"";
+ static final public String STEM_OPTION_STR = "stem";
+ static final public String STOP_CHARACTER = ".";
+ static final public String SUBCOLLECTION_ELEMENT = "Subcollection";
+ static final public String SUBCOLLECTION_STR = "subcollection";
+ static final public String SUBCOLLECTION_DEFAULT_INDEX_ELEMENT = "DefaultSubcollection";
+ static final public String SUBCOLLECTION_DEFAULT_INDEX_STR = "defaultsubcollection";
+ static final public String SUBCOLLECTION_INDEX_STR = "indexsubcollections";
+ static final public String SUBCOLLECTION_INDEX_ELEMENT = "indexSubcollection";
+ static final public String SUBCOLLECTION_INDEXES_ELEMENT = "SubcollectionIndexes";
+ static final public String SUPERCOLLECTION_ELEMENT = "Supercollection";
+ static final public String SUPERCOLLECTION_STR = "supercollection";
+ static final public String TAB_CHARACTER = "\t";
+ static final public String TEXT_NODE = "#text";
+ static final public String TEXT_STR = "text";
+ static final public String TIMESTAMP_ARGUMENT = "&uq=";
+ static final public String TITLE_ELEMENT = "Title";
+ static final public String SOURCE_ELEMENT = "Source";
+ static final public String TRUE_STR = "true";
+ static final public String TYPE_ATTRIBUTE = "type";
+ static final public String UNKNOWN_ELEMENT = "Unknown";
+ static final public String UNKNOWNPLUG_STR = "UnknownPlugin";
+ static final public String USE_METADATA_FILES_ARGUMENT = "use_metadata_files";
+ static final public String USE_REMOTE_GREENSTONE_ARGUMENT = "-use_remote_greenstone";
+ static final public String URL_SEPARATOR_CHARACTER = "/";
+ static final public String VALUE_ATTRIBUTE = "value";
+ static final public String VERSION_ATTRIBUTE = "version";
+ static final public String WIN_9X_OPEN_COMMAND = "command.com /c start \"%1\"";
+ static final public String WIN_OPEN_COMMAND = "cmd.exe /c start \"\" \"%1\"";
+ static final public String YES_STR = "yes";
+ static final public String ZERO_CHARACTER = "0";
+ static final public String METADATA_PATH = "-metadata_path";
+ static final public String NEW_METADATASET = "-new_set";
+ static final public String CACHE_FOLDER = "cache";
+ static final public String CUSTOM_ATTRIBUTE = "custom";
+ static final public String MG_STR = "mg";
+ static final public String MGPP_STR = "mgpp";
+ static final public String LUCENE_STR = "lucene";
+
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/SynchronizedTreeModelTools.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/SynchronizedTreeModelTools.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/SynchronizedTreeModelTools.java (revision 31635)
@@ -0,0 +1,137 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+package org.greenstone.gatherer.util;
+
+import java.lang.Runnable;
+import javax.swing.SwingUtilities;
+import javax.swing.tree.*;
+import org.greenstone.gatherer.DebugStream;
+import org.greenstone.gatherer.metadata.FilenameEncoding;
+import org.greenstone.gatherer.collection.CollectionTreeNode;
+
+/** Due to the TreeModel objects not having any synchronization, certain assumptions, such as the model state remaining constant during a repaint, don't always hold - especially given that I'm changing the tree model on a different thread. In order to get around this I will use the latest swing paradigm wherein you flag a section of code to be executed by the AWT GUI Event queue, as soon as other gui tasks have finished. This way I shouldn't have tree redraws throwing NPEs because the array size of the children of a certain node has changed -while- the repaint call was made, i.e. repaint() calls getChildCount() = 13, removeNodeFromParent() called, repaint calls getChildAt(12) = ArrayIndexOutOfBoundsException.
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.3c
+ */
+public class SynchronizedTreeModelTools {
+ /** Adds an insertNodeInto model update onto the AWT Event queue. This gets around the lack of synchronization illustrated above. */
+ static final public Runnable insertNodeInto(DefaultTreeModel model, MutableTreeNode parent, MutableTreeNode target_node) {
+ return insertNodeInto(model, parent, target_node, true);
+ }
+
+ static final public Runnable insertNodeInto(final DefaultTreeModel model, final MutableTreeNode parent, final MutableTreeNode target_node, final boolean wait_allowed) {
+ final Runnable doInsertNodeInto = new Runnable() {
+ public void run() {
+ ///ystem.err.print("Running task... ");
+ DebugStream.println("insertNodeInto(" + model + ", " + parent + ", " + target_node + ", " + wait_allowed);
+ int index = -1;
+ int pos = 0;
+ while(index == -1 && pos < parent.getChildCount()) {
+ TreeNode node = parent.getChildAt(pos);
+ int result = 0;
+ ///ystem.err.println("Compare " + target_node + " to " + node);
+ if((target_node.isLeaf() && node.isLeaf()) || (!target_node.isLeaf() && !node.isLeaf())) {
+ result = target_node.toString().toLowerCase().compareTo(node.toString().toLowerCase());
+ }
+ else if(target_node.isLeaf()) {
+ result = 1;
+ }
+ else {
+ result = -1;
+ }
+ if(result > 0) {
+ ///ystem.err.println("Keep searching...");
+ pos++;
+ }
+ else {
+ ///ystem.err.println("Found!");
+ index = pos;
+ }
+ }
+ if(index == -1) {
+ index = parent.getChildCount();
+ }
+ model.insertNodeInto(target_node, parent, index);
+ }
+ };
+ ///ystem.err.print("Queuing Task... ");
+ try {
+ if(wait_allowed && !SwingUtilities.isEventDispatchThread()) {
+ ///ystem.err.print("In another thread - invoke and wait... ");
+ SwingUtilities.invokeAndWait(doInsertNodeInto);
+ }
+ else {
+ ///ystem.err.print("In Event Thread or wait not allowed - invoke later... ");
+ SwingUtilities.invokeLater(doInsertNodeInto);
+ }
+ }
+ catch (Exception exception) {
+ DebugStream.printStackTrace(exception);
+ }
+ ///ystem.err.print("Added Task... ");
+ return doInsertNodeInto;
+ }
+
+
+ /** Adds a removeNodeFromParent model update onto the AWT Event queue. This gets around the lack of synchronization illustrated above.
+ * @param model The GTreeModel we want to remove the node from.
+ * @param target_node The GTreeNode to remove.
+ */
+ static final public void removeNodeFromParent(final DefaultTreeModel model, final MutableTreeNode target_node) {
+ ///ystem.err.println("Remove " + target_node + " from parent in model " + model);
+ final Runnable doRemoveNodeFromParent = new Runnable() {
+ public void run() {
+ // If we're dealing with a collection tree node, it may have
+ // gs.FilenameEncoding assigned, so we remove its entry from the map.
+ // Needs to be done here because the tree is constantly changing
+ // when nodes are being removed, renamed and deleted, and this
+ // affects lookup queries sent to the map.
+ // Don't need to do a recursive reset on this coltreenode, because
+ // Delete/Move/Rename FileJobs were created for *each* node
+ if(target_node instanceof CollectionTreeNode) {
+ CollectionTreeNode colNode = (CollectionTreeNode)target_node;
+ FilenameEncoding.map.remove(colNode.getURLEncodedFilePath());
+ }
+
+ model.removeNodeFromParent(target_node);
+ }
+ };
+ try {
+ //SwingUtilities.invokeLater(doRemoveNodeFromParent);
+ SwingUtilities.invokeAndWait(doRemoveNodeFromParent);
+ }
+ catch (Exception exception) {
+ DebugStream.printStackTrace(exception);
+ }
+ // If we've thrown an error because we tried to invoke the runnable task and wait, when we are in the AWTEvent thread already, then try agin but with an invoke later.
+ catch (java.lang.Error error) {
+ if(error.toString().equals("java.lang.Error: Cannot call invokeAndWait from the event dispatcher thread")) {
+ SwingUtilities.invokeLater(doRemoveNodeFromParent);
+ }
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/TableUtils.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/TableUtils.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/TableUtils.java (revision 31635)
@@ -0,0 +1,92 @@
+package org.greenstone.gatherer.util;
+
+import java.awt.*;
+import javax.swing.*;
+import javax.swing.table.*;
+
+/**
+ * Utilites for manipulating JTables.
+ *
+ * @author Marc Hedlund
+ * <marc@precipice.org>
+ * @version $Revision$
+ */
+
+public class TableUtils {
+
+ // This is based on code found at
+ // ,
+ // somewhat modified.
+
+// public static void setPreferredCellSizes(JTable table) {
+// TableModel model = table.getModel();
+// TableColumnModel colModel = table.getColumnModel();
+
+// for (int i = 0; i < model.getColumnCount(); i++) {
+// TableColumn column = colModel.getColumn(i);
+
+// int longestCell = 0;
+// int highestCell = 0;
+
+// for (int j = 0; j < model.getRowCount(); j++) {
+// Object value = model.getValueAt(j, i);
+// if (value == null) continue;
+
+// Component cell =
+// table.getDefaultRenderer(model.getColumnClass(i)).
+// getTableCellRendererComponent(table, value,
+// false, false, j, i);
+
+// int width = cell.getPreferredSize().width;
+// int height = cell.getPreferredSize().height;
+
+// if (width > longestCell) longestCell = width;
+// if (height > highestCell) highestCell = height;
+// }
+
+// Component headerComp = column.getHeaderRenderer().
+// getTableCellRendererComponent(table, column.getHeaderValue(),
+// false, false, 0, 0);
+
+// int headerWidth = headerComp.getPreferredSize().width;
+// int headerHeight = headerComp.getPreferredSize().height;
+
+// column.setPreferredWidth(Math.max(headerWidth, longestCell));
+
+// int currentHeight = table.getRowHeight();
+// int preferredHeight = Math.max(headerHeight, highestCell);
+
+// table.setRowHeight(Math.max(currentHeight, preferredHeight));
+// }
+// }
+
+ /**
+ * Takes a column in an existing table and makes it fixed-width.
+ * Specifically, it sets the column's minimum and maximum widths to
+ * its preferred width, and disables auto-resize for the table as a
+ * whole.
+ *
+ *
+ *
+ * Later on this should take a column array for efficiency.
+ *
+ * @param table JTable The table to modify
+ * @param colIndex int Which column to fix
+ * @return int The width of the column as it was fixed
+ */
+
+ public static int fixColumnToPreferredWidth(JTable table, int colIndex) {
+ table.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN);
+
+ TableColumnModel tcm = table.getColumnModel();
+ TableColumn col = tcm.getColumn(colIndex);
+ int width = col.getPreferredWidth();
+
+ col.setMaxWidth(width);
+ col.setMinWidth(width);
+
+ //table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
+
+ return width;
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/UnzipTools.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/UnzipTools.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/UnzipTools.java (revision 31635)
@@ -0,0 +1,98 @@
+/**
+ *############################################################################
+ * A component of the Greenstone Librarian Interface, part of the Greenstone
+ * digital library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ * Author: David Bainbridge, NZDL Project, University of Waikato, NZ
+ *
+ * Copyright (C) 2005 New Zealand Digital Library Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *############################################################################
+ */
+
+package org.greenstone.gatherer.util;
+
+
+import java.io.*;
+import java.util.*;
+import java.util.zip.*;
+
+
+public class UnzipTools
+{
+ static public void unzipFile(String zip_file_path, String base_directory_path)
+ {
+ try {
+ ZipFile zip_file = new ZipFile(new File(zip_file_path), ZipFile.OPEN_READ);
+
+ Enumeration e = zip_file.entries();
+ while (e.hasMoreElements()) {
+ ZipEntry zip_entry = (ZipEntry) e.nextElement();
+ File zip_entry_file = new File(base_directory_path + zip_entry.getName());
+ // System.err.println(" Unzipping: " + zip_entry_file.getAbsolutePath());
+
+ // Directory case
+ if (zip_entry.isDirectory()) {
+ // Create named directory, if it doesn't already exist
+ if (!zip_entry_file.exists() && !zip_entry_file.mkdirs()) {
+ System.err.println("Error: unable to create directory " + zip_entry_file);
+ }
+ }
+
+ // File case
+ else {
+ // Write out file to disk
+
+ // Make sure its parent directory exists.
+ File dir = new File(zip_entry_file.getParent());
+ dir.mkdirs();
+
+ // Don't need to unzip files called empty which were created during the zipping process
+ // in order to be able to generate empty directories in a zip (see remote.ZipTools)
+ if(!zip_entry_file.getName().equals("empty")) {
+ // Set up input stream
+ InputStream zis = zip_file.getInputStream(zip_entry);
+ BufferedInputStream bzis = new BufferedInputStream(zis);
+ DataInputStream dbzis = new DataInputStream(bzis);
+
+ // Set up output stream
+ FileOutputStream fzos = new FileOutputStream(zip_entry_file);
+ BufferedOutputStream bfzos = new BufferedOutputStream(fzos);
+
+ byte[] buf = new byte[1024];
+ int len;
+ while ((len = dbzis.read(buf)) >= 0) {
+ bfzos.write(buf,0,len);
+ }
+
+ dbzis.close();
+ bzis.close();
+ zis.close();
+
+ bfzos.close();
+ fzos.close();
+ }
+ }
+ }
+
+ zip_file.close();
+ }
+ catch (Exception exception) {
+ exception.printStackTrace();
+ }
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/Utility.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/Utility.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/Utility.java (revision 31635)
@@ -0,0 +1,603 @@
+/**
+ *#########################################################################
+ *
+ * A component of the Gatherer application, part of the Greenstone digital
+ * library suite from the New Zealand Digital Library Project at the
+ * University of Waikato, New Zealand.
+ *
+ *
+ *
+ * Author: John Thompson, Greenstone Digital Library, University of Waikato
+ *
+ *
+ *
+ * Copyright (C) 1999 New Zealand Digital Library Project
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *########################################################################
+ */
+
+package org.greenstone.gatherer.util;
+
+
+// Don't even think about adding import java.awt.* here!
+// The functions in this class should not use any graphical classes. Put your function somewhere else buster!
+import java.io.*;
+import java.net.*;
+import java.util.*;
+// Don't even think about adding import javax.swing.* here!
+// The functions in this class should not use any graphical classes. Put your function somewhere else buster!
+import org.greenstone.gatherer.Dictionary;
+// Don't even think about adding import org.greenstone.gatherer.Gatherer in here!
+// The functions in this class should be independent of the Gatherer class. Put your function somewhere else buster!
+import org.greenstone.gatherer.util.SafeProcess; // for the closeResource() static method
+
+/** To provide a library of common methods, in a static context, for use in the Gatherer.
+ * @author John Thompson, Greenstone Digital Library, University of Waikato
+ * @version 2.3b
+ */
+public class Utility
+{
+ /** Definition of an important directory name, in this case the file the collection configuration is expect to be in. */
+ static final public String CONFIG_FILE = "etc" + File.separator + "collect.cfg";
+ static final public String CONFIG_GS3_FILE = "etc" + File.separator + "collectionConfig.xml";
+ static final public String COLLECT_CFG = "collect.cfg";
+ static final public String COLLECT_BAK = "collect.bak";
+ static final public String COLLECTION_CONFIG_XML = "collectionConfig.xml";
+ static final public String COLLECTION_CONFIG_BAK = "collectionConfig.bak";
+ static final public String GS3MODE_ARGUMENT = "-gs3mode";
+ static final public String BUILD_CFG = "build.cfg";
+ static final public String BUILD_CONFIG_XML = "buildConfig.xml";
+
+ /** The default name of the perl executable under unix. */
+ static final public String PERL_EXECUTABLE_UNIX = "perl";
+ /** The default name of the perl executable under windows. */
+ static final public String PERL_EXECUTABLE_WINDOWS = "Perl.exe";
+
+ /** Platform independent NEWLINE character */
+ public static final String NEWLINE;
+
+ // NEWLINE related code copied across from GS3 src code
+ // Before Java 7, no System.lineSeparator() or System.getProperty("line.separator")
+ // And on local linux, am compiling with JDK 6, so need this.
+ // http://stackoverflow.com/questions/207947/how-do-i-get-a-platform-dependent-new-line-character
+ // http://stackoverflow.com/questions/2591083/getting-java-version-at-runtime
+ // https://www.tutorialspoint.com/java/lang/package_getspecificationversion.htm
+ // https://docs.oracle.com/javase/tutorial/essential/environment/sysprop.html
+ // Can initialise static final vars on declaration or in static initialisation code block
+ // http://stackoverflow.com/questions/2339932/java-can-final-variables-be-initialized-in-static-initialization-block
+ // Initialise object member final vars on declaration or in constructors
+ static {
+ double java_version = Double.parseDouble(System.getProperty("java.specification.version"));
+ if(java_version >= 1.7) {
+ NEWLINE = System.getProperty("line.separator");
+ } else {
+ NEWLINE = isWindows() ? "\r\n" : "\n";
+ }
+ }
+
+ /**
+ * Reads in a text file and returns the contents as a String
+ */
+ static public String readFile(File file) {
+ BufferedReader fin = null;
+ StringBuffer contents = new StringBuffer();
+
+ try {
+ fin = new BufferedReader(new FileReader(file));
+ String line = null;
+ while((line = fin.readLine()) != null) {
+ contents.append(line);
+ contents.append("\n");
+ }
+ } catch(IOException e) {
+ System.err.println("*** Could not read in file: " + file.toString());
+ System.err.println("*** Exception occurred: " + e.getMessage());
+ } finally {
+ SafeProcess.closeResource(fin);
+ }
+
+ return contents.toString();
+ }
+
+ /**
+ * Delete a file or directory
+ * @param file The File you want to delete.
+ * @return A boolean which is true if the file specified was successfully deleted, false otherwise.
+ */
+ static public boolean delete(File file)
+ {
+ // Nothing to do if it doesn't exist
+ if (!file.exists()) {
+ return true;
+ }
+
+ return deleteInternal(file);
+ }
+
+
+ /** Convenience function. */
+ static public boolean delete(String filename)
+ {
+ return delete(new File(filename));
+ }
+
+
+ /** In Java you have to make sure a directory is empty before you delete it, so recursively delete. */
+ static private boolean deleteInternal(File file)
+ {
+ // If file is a directory, we have to recursively delete its contents first
+ if (file.isDirectory()) {
+ File files[] = file.listFiles();
+ for (int i = 0; i < files.length; i++) {
+ if (deleteInternal(files[i]) == false) {
+ System.err.println("Error: Could not delete folder " + file);
+ return false;
+ }
+ }
+ }
+
+ // Delete file
+ if (file.delete() == false) {
+ System.err.println("Error: Could not delete file " + file);
+ return false;
+ }
+
+ return true;
+ }
+
+
+ /** Convert a long, detailing the length of a file in bytes, into a nice human readable string using b, kb, Mb and Gb. */
+ static final public String BYTE_SUFFIX = " b";
+ static final public long GIGABYTE = 1024000000l;
+ static final public String GIGABYTE_SUFFIX = " Gb";
+ static final public long KILOBYTE = 1024l;
+ static final public String KILOBYTE_SUFFIX = " kb";
+ static final public long MEGABYTE = 1024000l;
+ static final public String MEGABYTE_SUFFIX = " Mb";
+ static final public String formatFileLength(long length) {
+ StringBuffer result = new StringBuffer("");
+ float number = 0f;
+ String suffix = null;
+ // Determine the floating point number and the suffix (radix) used.
+ if(length >= GIGABYTE) {
+ number = (float) length / (float) GIGABYTE;
+ suffix = GIGABYTE_SUFFIX;
+ }
+ else if(length >= MEGABYTE) {
+ number = (float) length / (float) MEGABYTE;
+ suffix = MEGABYTE_SUFFIX;
+ }
+ else if(length >= KILOBYTE) {
+ number = (float) length / (float) KILOBYTE;
+ suffix = KILOBYTE_SUFFIX;
+ }
+ else {
+ // Don't need to do anything fancy if the file is smaller than a kilobyte
+ return length + BYTE_SUFFIX;
+ }
+ // Create the formatted string remembering to round the number to 2.d.p. To do this copy everything in the number string from the start to the first occurance of '.' then copy two more digits. Finally search for and print anything that appears after (and including) the optional 'E' delimter.
+ String number_str = Float.toString(number);
+ char number_char[] = number_str.toCharArray();
+ int pos = 0;
+ // Print the characters up to the '.'
+ while(number_char != null && pos < number_char.length && number_char[pos] != '.') {
+ result.append(number_char[pos]);
+ pos++;
+ }
+ if(pos < number_char.length) {
+ // Print the '.' and at most two characters after it
+ result.append(number_char[pos]);
+ pos++;
+ for(int i = 0; i < 2 && pos < number_char.length; i++, pos++) {
+ result.append(number_char[pos]);
+ }
+ // Search through the remaining string for 'E'
+ while(pos < number_char.length && number_char[pos] != 'E') {
+ pos++;
+ }
+ // If we still have string then we found an E. Copy the remaining string.
+ while(pos < number_char.length) {
+ result.append(number_char[pos]);
+ pos++;
+ }
+ }
+ // Add suffix
+ result.append(suffix);
+ // Done
+ return result.toString();
+ }
+
+ /** This method formats a given string, using HTML markup, so its width does not exceed the given width and its appearance if justified.
+ * @param text The String requiring formatting.
+ * @param width The maximum width per line as an int .
+ * @return A String formatted so as to have no line longer than the specified width.
+ * TODO Currently HTML formatting tags are simply removed from the text, as the effects of spreading HTML tags over a break are undetermined. To solve this we need to associate tags with a certain text token so if it gets broken on to the next line the tags go with it, or if the tags cover a sequence of words that are broken we need to close then reopen the tags. However all this is a major task and well beyond anything I have time to 'muck-round' on.
+ */
+ static public String formatHTMLWidth(String text, int width) {
+ if(text == null) {
+ return "Error";
+ }
+ HTMLStringTokenizer html = new HTMLStringTokenizer(text);
+ int current_width = 0;
+ int threshold = width / 2;
+ Stack lines = new Stack();
+ String line = "";
+ while(html.hasMoreTokens()) {
+ String token = html.nextToken();
+ while(token != null) {
+ if(html.isTag()) {
+ // Insert smart HTML tag code here.
+ token = null;
+ }
+ else {
+ // If the token is bigger than two thirds width, before we've even started break it down.
+ if(current_width + 1 + token.length() > width && token.length() > threshold) {
+ if(width == current_width) {
+ lines.push(line);
+ line = token;
+ current_width = token.length();
+ }
+ else {
+ String prefix = token.substring(0, width - 1 - current_width);
+ token = token.substring(prefix.length());
+ if(current_width == 0) {
+ line = line + prefix;
+ }
+ else {
+ line = line + " " + prefix;
+ }
+ lines.push(line);
+ line = "";
+ current_width = 0;
+ }
+ }
+ // If adding the next token would push us over the maximum line width.
+ else if(current_width + 1 + token.length() > width) {
+ lines.push(line);
+ line = token;
+ current_width = token.length();
+ token = null;
+ }
+ // Otherwise we should be able to just add the token, give or take.
+ else {
+ if(current_width == 0) {
+ line = line + token;
+ current_width = token.length();
+ }
+ else {
+ // Special case for standard punctuation which may exist after a tag like so:
+ // My name is Slim Shady . <-- Annoying punctuation.
+ if(token.equals(".") || token.equals(",") || token.equals("!") || token.equals("?")) {
+ line = line + token;
+ current_width = current_width + 1;
+ }
+ else {
+ line = line + " " + token;
+ current_width = current_width + 1 + token.length();
+ }
+ }
+ token = null;
+ }
+ }
+ }
+ }
+ String result = line;
+ while(!lines.empty()) {
+ result = (String)lines.pop() + " " + result;
+ }
+ // Replace ' ' with " "
+ boolean tag = false;
+ int pos = 0;
+ while(pos < result.length()) {
+ if(result.charAt(pos) == '<') {
+ tag = true;
+ }
+ else if(result.charAt(pos) == '>') {
+ tag = false;
+ }
+ else if(result.charAt(pos) == ' ' && !tag) {
+ String prefix = result.substring(0, pos);
+ String suffix = result.substring(pos + 1);
+ result = prefix + " " + suffix;
+ }
+ pos++;
+ }
+ result = "" + result + "";
+ return result;
+ }
+
+
+ static public String getDateString() {
+ Calendar current = Calendar.getInstance();
+ String day_name = null;
+ switch(current.get(Calendar.DAY_OF_WEEK)) {
+ case Calendar.MONDAY: day_name = "Dates.Mon"; break;
+ case Calendar.TUESDAY: day_name = "Dates.Tue"; break;
+ case Calendar.WEDNESDAY: day_name = "Dates.Wed"; break;
+ case Calendar.THURSDAY: day_name = "Dates.Thu"; break;
+ case Calendar.FRIDAY: day_name = "Dates.Fri"; break;
+ case Calendar.SATURDAY: day_name = "Dates.Sat"; break;
+ case Calendar.SUNDAY: day_name = "Dates.Sun"; break;
+ default: day_name = "";
+ }
+ String month_name = null;
+ switch(current.get(Calendar.MONTH)) {
+ case Calendar.JANUARY: month_name = "Dates.Jan"; break;
+ case Calendar.FEBRUARY: month_name = "Dates.Feb"; break;
+ case Calendar.MARCH: month_name = "Dates.Mar"; break;
+ case Calendar.APRIL: month_name = "Dates.Apr"; break;
+ case Calendar.MAY: month_name = "Dates.May"; break;
+ case Calendar.JUNE: month_name = "Dates.Jun"; break;
+ case Calendar.JULY: month_name = "Dates.Jul"; break;
+ case Calendar.AUGUST: month_name = "Dates.Aug"; break;
+ case Calendar.SEPTEMBER: month_name = "Dates.Sep"; break;
+ case Calendar.OCTOBER: month_name = "Dates.Oct"; break;
+ case Calendar.NOVEMBER: month_name = "Dates.Nov"; break;
+ case Calendar.DECEMBER: month_name = "Dates.Dec"; break;
+ default: month_name = "";
+ }
+ int day = current.get(Calendar.DAY_OF_MONTH);
+ int hour = current.get(Calendar.HOUR_OF_DAY);
+ int minute = current.get(Calendar.MINUTE);
+ int second = current.get(Calendar.SECOND);
+ int year = current.get(Calendar.YEAR);
+
+ return Dictionary.get(day_name) + " " + Dictionary.get(month_name) + " " + day + " " + year + " " + Utility.pad(String.valueOf(hour), 2, '0', true) + ":" + Utility.pad(String.valueOf(minute), 2, '0', true) + ":" + Utility.pad(String.valueOf(second), 2, '0', true);
+ }
+
+
+ /** Determine this machines name.
+ * @return The name as a String .
+ */
+ static public String getMachineName() {
+ try {
+ return InetAddress.getLocalHost().getHostName();
+ }
+ catch(UnknownHostException ex) {
+ }
+ return "Unknown Machine";
+ }
+
+
+ static public String getSitesDir(String gsdl3_path) {
+ return gsdl3_path + "sites" + File.separator;
+
+ }
+
+ /** @return the OSdir foldername: windows, linux or darwin */
+ public static String getOSdirName() {
+ if(Utility.isWindows()) {
+ return "windows";
+ }
+ if(Utility.isMac()) {
+ return "darwin";
+ }
+ return "linux"; // else assume it's a linux machine, OSdirname = linux
+ }
+
+
+ /** Method to determine if the host system is MacOS based.
+ * @return a boolean which is true if the platform is MacOS, false otherwise
+ */
+ public static boolean isMac() {
+ Properties props = System.getProperties();
+ String os_name = props.getProperty("os.name","");
+ if(os_name.startsWith("Mac OS")) {
+ return true;
+ }
+ return false;
+ }
+
+
+ /** Method to determine if the host system is Microsoft Windows based.
+ * @return A boolean which is true if the platform is Windows, false otherwise.
+ */
+ public static boolean isWindows() {
+ Properties props = System.getProperties();
+ String os_name = props.getProperty("os.name","");
+ if(os_name.startsWith("Windows")) {
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean isWindows9x() {
+ Properties props = System.getProperties();
+ String os_name = props.getProperty("os.name","");
+ if(os_name.startsWith("Windows") && os_name.indexOf("9") != -1) {
+ return true;
+ }
+ return false;
+ }
+ /** Takes a string and a desired length and pads out the string to the length by adding spaces to the left.
+ * @param str The target String that needs to be padded.
+ * @param length The desired length of the string as an int .
+ * @return A String made from appending space characters with the string until it has a length equal to length.
+ */
+ static private String pad(String str_raw, int length, char fill, boolean end) {
+ StringBuffer str = new StringBuffer(str_raw);
+ while(str.length() < length) {
+ if(end) {
+ str.insert(0, fill);
+ }
+ else {
+ str.append(fill);
+ }
+ }
+ return str.toString();
+ }
+
+
+ /** Builds the cache dir by appending the user path and 'cache'.
+ * @return a File representing the path to the private file cache within the current collection.
+ */
+ public static File getCacheDir() {
+ return new File(getGLIUserFolder(), StaticStrings.CACHE_FOLDER);
+ }
+
+ /** Method which constructs the log directory given a certain collection.
+ * @param col_dir The location of the collection directory as a String .
+ * @return The location of the given collections log directory, also as a String .
+ */
+ public static String getLogDir(String col_dir) {
+ if(col_dir != null) {
+ return col_dir + LOG_DIR;
+ }
+ else {
+ return getGLIUserFolder().getAbsolutePath() + File.separator + LOG_DIR;
+ }
+ }
+
+ static final private String APPLICATION_DATA_FOLDER = "Application Data";
+ static final private String UNIX_GLI_CONFIG_FOLDER = ".gli";
+ static final private String USER_HOME_PROPERTY = "user.home";
+ static final private String WIN_GLI_CONFIG_FOLDER = "Greenstone" + File.separator + "GLI";
+ /** Definition of an important directory name, in this case the log directory for the collection. */
+ static final public String LOG_DIR = "log" + File.separator;
+
+ static public File getGLIUserFolder()
+ {
+ if (Utility.isWindows()) {
+ return new File(System.getProperty(USER_HOME_PROPERTY) + File.separator + APPLICATION_DATA_FOLDER + File.separator + WIN_GLI_CONFIG_FOLDER + File.separator);
+ }
+ else {
+ return new File(System.getProperty(USER_HOME_PROPERTY) + File.separator + UNIX_GLI_CONFIG_FOLDER + File.separator);
+ }
+ }
+
+ static private HashMap plugin_map = null;
+
+ static private void setUpPluginNameMap() {
+ plugin_map = new HashMap();
+ plugin_map.put("GAPlug", "GreenstoneXMLPlugin");
+ plugin_map.put("RecPlug", "DirectoryPlugin");
+ plugin_map.put("ArcPlug","ArchivesInfPlugin");
+ plugin_map.put("TEXTPlug","TextPlugin");
+ plugin_map.put("XMLPlug","ReadXMLFile");
+ plugin_map.put("EMAILPlug","EmailPlugin");
+ plugin_map.put("SRCPlug","SourceCodePlugin");
+ plugin_map.put("NULPlug","NulPlugin");
+ plugin_map.put("W3ImgPlug","HTMLImagePlugin");
+ plugin_map.put("PagedImgPlug","PagedImagePlugin");
+ plugin_map.put("METSPlug", "GreenstoneMETSPlugin");
+ plugin_map.put("DBPlug", "DatabasePlugin");
+ plugin_map.put("PPTPlug", "PowerPointPlugin");
+ plugin_map.put("PSPlug", "PostScriptPlugin");
+ }
+
+ static public String ensureNewPluginName(String plugin) {
+ if (plugin.endsWith("Plugin")) return plugin;
+ if (plugin_map == null) {
+ setUpPluginNameMap();
+ }
+ String new_name = (String)plugin_map.get(plugin);
+ if (new_name != null) return new_name;
+ new_name = plugin.replaceAll("Plug", "Plugin");
+ return new_name;
+ }
+
+
+ /** Write out a property line--a (property, value) pair--to the gsdl(3)site.cfg file.
+ * If the file already contains the line as-is, it is not re-written.
+ * If the file doesn't contain the line, it is appended.
+ * If the file contained a different value for the property, the line is corrected
+ * and the file is written out.
+ * If the propertyValue parameter is null, the property line is removed from the file.
+ * Not using the Properties class, as we want to keep the file's contents in the
+ * same order and preserve all the comments in as they're meant to help the user.
+ * Return the old value for the property, if it existed, else "".
+ */
+ public static String updatePropertyConfigFile(
+ String filename, String propertyName, String propertyValue)
+ {
+ File propFile = new File(filename);
+ String oldValue = "";
+ if(!propFile.exists()) {
+ System.err.println("*** Unable to update property " + propertyName + " in file "
+ + filename + " to\n" + propertyValue + ". File does not (yet) exist.\n");
+ return oldValue;
+ }
+ BufferedReader fin = null;
+ BufferedWriter fout = null;
+ StringBuffer contents = new StringBuffer();
+ String insertLine = null;
+ if(propertyValue != null) {
+ insertLine = propertyName+"\t"+propertyValue+"\n"; // new line after every propertyLine
+ }
+ boolean found = false;
+ try {
+ fin = new BufferedReader(new FileReader(filename));
+ String line = "";
+ while((line = fin.readLine()) != null) {
+ line = line.trim(); // remove any preceding (surrounding) whitespace
+ if(line.startsWith(propertyName)) { // won't match comment
+ found = true;
+ // store the previous value for the property
+ oldValue = line;
+ oldValue = oldValue.substring(propertyName.length());
+ oldValue = oldValue.trim();
+
+ if(propertyValue != null) { // line should be removed if propertyValue == null
+ if(line.equals(insertLine)) { // file is already correct, nothing to do
+ fin.close();
+ fin = null;
+ break;
+ } else {
+ contents.append(insertLine);
+ }
+ }
+ } else { // any other line
+ contents.append(line);
+ contents.append("\n"); // ensures the required new line at end of file
+ }
+ }
+
+ if(fin != null) { // need to write something out to the file
+ fin.close();
+ fin = null;
+
+ // if collecthome/property wasn't already specified in the file, append it
+ // but only if we have a value to write out to the file
+ if(!found && propertyValue != null) {
+ fout = new BufferedWriter(new FileWriter(filename, true)); // append mode
+ fout.write(insertLine, 0, insertLine.length());
+ } else {
+ fout = new BufferedWriter(new FileWriter(filename)); // hopefully this will overwrite
+ fout.write(contents.toString(), 0, contents.length());
+ }
+
+ fout.close();
+ fout = null;
+
+ } // else the file is fine
+ } catch(IOException e) {
+ System.err.println("*** Could not update file: " + filename);
+ System.err.println("with the " + propertyName + " property set to " + propertyValue);
+ System.err.println("Exception occurred: " + e.getMessage());
+ } finally {
+ SafeProcess.closeResource(fin);
+ SafeProcess.closeResource(fout);
+
+ }
+ return oldValue;
+ }
+}
Index: /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/XMLTools.java
===================================================================
--- /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/XMLTools.java (revision 31635)
+++ /main/tags/GS3-src-SafeProcess/gli-src/org/greenstone/gatherer/util/XMLTools.java (revision 31635)
@@ -0,0 +1,1269 @@
+package org.greenstone.gatherer.util;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import org.apache.xerces.parsers.*;
+import org.apache.xml.serialize.*;
+import org.greenstone.gatherer.DebugStream;
+import org.w3c.dom.*;
+import org.xml.sax.*;
+
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter; // for elementToString()
+
+// SAX
+import org.xml.sax.XMLReader;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.InputSource;
+
+// JAXP
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.FactoryConfigurationError;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+// for elementToString():
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+
+/** This class is a static class containing useful XML functions */
+public class XMLTools
+{
+ /** extracts the text out of a node */
+ public static Node getNodeTextNode(Element param)
+ {
+ param.normalize();
+ Node n = param.getFirstChild();
+ while (n != null && n.getNodeType() != Node.TEXT_NODE)
+ {
+ n = n.getNextSibling();
+ }
+ return n;
+ }
+
+ /** extracts the text out of a node */
+ public static String getNodeText(Element param)
+ {
+ Node text_node = getNodeTextNode(param);
+ if (text_node == null)
+ {
+ return "";
+ }
+ return text_node.getNodeValue();
+ }
+
+ public static void setNodeText(Element elem, String text)
+ {
+ Node old_text_node = getNodeTextNode(elem);
+ if (old_text_node != null)
+ {
+ elem.removeChild(old_text_node);
+ }
+ Text t = elem.getOwnerDocument().createTextNode(text);
+ elem.appendChild(t);
+ }
+
+ /** returns the (first) child element with the given name */
+ public static Node getChildByTagName(Node n, String name)
+ {
+
+ Node child = n.getFirstChild();
+ while (child != null)
+ {
+ if (child.getNodeName().equals(name))
+ {
+ return child;
+ }
+ child = child.getNextSibling();
+ }
+ return null; //not found
+ }
+
+ /**
+ * returns the (nth) child element with the given name index numbers start
+ * at 0
+ */
+ public static Node getChildByTagNameIndexed(Node n, String name, int index)
+ {
+ if (index == -1)
+ {
+ return getChildByTagName(n, name);
+ }
+ int count = 0;
+ Node child = n.getFirstChild();
+ while (child != null)
+ {
+ if (child.getNodeName().equals(name))
+ {
+ if (count == index)
+ {
+ return child;
+ }
+ else
+ {
+ count++;
+ }
+ }
+ child = child.getNextSibling();
+ }
+ return null; //not found
+ }
+
+ /**
+ * returns the element parent/node_name[@attribute_name='attribute_value']
+ */
+ public static Element getNamedElement(Element parent, String node_name, String attribute_name, String attribute_value)
+ {
+
+ NodeList children = parent.getChildNodes();
+ for (int i = 0; i < children.getLength(); i++)
+ {
+ Node child = children.item(i);
+ //logger.debug("getnamed elem, node nmae="+child.getNodeName());
+ if (child.getNodeName().equals(node_name))
+ {
+ if (((Element) child).getAttribute(attribute_name).equals(attribute_value))
+ return (Element) child;
+ }
+ }
+ // not found
+ return null;
+ }
+
+ /**
+ * returns a list of elements
+ * parent/node_name[@attribute_name='attribute_value']
+ */
+ public static ArrayList getNamedElementList(Element parent, String node_name, String attribute_name, String attribute_value)
+ {
+ ArrayList elements = new ArrayList();
+ NodeList children = parent.getChildNodes();
+ for (int i = 0; i < children.getLength(); i++)
+ {
+ //System.out.println("getNamedElementList");
+ Node child = children.item(i);
+ //logger.debug("getnamed elem, node nmae="+child.getNodeName());
+ if (child.getNodeName().equals(node_name))
+ {
+ if (((Element) child).getAttribute(attribute_name).equals(attribute_value))
+ elements.add((Element) child);
+ }
+ }
+ // not found
+ if (elements.size() == 0)
+ {
+ elements = null;
+ }
+ return elements;
+ }
+
+ public static void copyAllChildren(Element to, Element from)
+ {
+
+ Document to_doc = to.getOwnerDocument();
+ Node child = from.getFirstChild();
+ while (child != null)
+ {
+ to.appendChild(to_doc.importNode(child, true));
+ child = child.getNextSibling();
+ }
+ }
+
+ /** duplicates all elements in list elements and appends to toElement */
+ public static void duplicateElementList(Document owner, Element toElement, NodeList elements, boolean with_attributes) {
+ int num_elems = elements.getLength();
+ if (num_elems < 1)
+ {
+ return;
+ }
+ for (int i = 0; i < num_elems; i++)
+ {
+ Element to_element = XMLTools.duplicateElement(owner, (Element) elements.item(i), with_attributes);
+ toElement.appendChild(to_element);
+ }
+
+ }
+ /** Duplicates an element */
+ public static Element duplicateElement(Document owner, Element element, boolean with_attributes)
+ {
+ return duplicateElementNS(owner, element, null, with_attributes);
+ }
+
+ /** Duplicates an element */
+ public static Element duplicateElementNS(Document owner, Element element, String namespace_uri, boolean with_attributes)
+ {
+ Element duplicate;
+ if (namespace_uri == null)
+ {
+ duplicate = owner.createElement(element.getTagName());
+ }
+ else
+ {
+ duplicate = owner.createElementNS(namespace_uri, element.getTagName());
+ }
+ // Copy element attributes
+ if (with_attributes)
+ {
+ NamedNodeMap attributes = element.getAttributes();
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ Node attribute = attributes.item(i);
+ duplicate.setAttribute(attribute.getNodeName(), attribute.getNodeValue());
+ }
+ }
+
+ // Copy element children
+ NodeList children = element.getChildNodes();
+ for (int i = 0; i < children.getLength(); i++)
+ {
+ Node child = children.item(i);
+ duplicate.appendChild(owner.importNode(child, true));
+ }
+
+ return duplicate;
+ }
+
+ /** Remove all of the child nodes from a certain node. */
+ static final public void clear(Node node)
+ {
+ while (node.hasChildNodes())
+ {
+ node.removeChild(node.getFirstChild());
+ }
+ }
+
+ static public ArrayList getChildElementsByTagName(Element parent_element, String element_name)
+ {
+ ArrayList child_elements = new ArrayList();
+
+ NodeList children_nodelist = parent_element.getChildNodes();
+ for (int i = 0; i < children_nodelist.getLength(); i++)
+ {
+ Node child_node = children_nodelist.item(i);
+ if (child_node.getNodeType() == Node.ELEMENT_NODE && child_node.getNodeName().equals(element_name))
+ {
+ child_elements.add(child_node);
+ }
+ }
+
+ return child_elements;
+ }
+
+ static public String getElementTextValue(Element element)
+ {
+ // Find the first text node child
+ NodeList children_nodelist = element.getChildNodes();
+ for (int i = 0; i < children_nodelist.getLength(); i++)
+ {
+ Node child_node = children_nodelist.item(i);
+ if (child_node.getNodeType() == Node.TEXT_NODE)
+ {
+ return child_node.getNodeValue();
+ }
+ }
+
+ // None found
+ return "";
+ }
+
+ /**
+ * Method to retrieve the value of a given node.
+ *
+ * @param element
+ * The Element whose value we wish to find. Soon
+ * to be deprecated!
+ */
+ static final public String getValue(Node element)
+ {
+ if (element == null)
+ {
+ return "";
+ }
+ // If we've been given a subject node first retrieve its value node.
+ if (element.getNodeName().equals("Subject"))
+ {
+ element = getNodeFromNamed(element, "Value");
+ }
+ // If we've got a value node, then reconstruct the text. Remember that DOM will split text over 256 characters into several text nodes
+ if (element != null && element.hasChildNodes())
+ {
+ StringBuffer text_buffer = new StringBuffer();
+ NodeList text_nodes = element.getChildNodes();
+ for (int i = 0; i < text_nodes.getLength(); i++)
+ {
+ Node possible_text = text_nodes.item(i);
+ if (possible_text.getNodeName().equals(StaticStrings.TEXT_NODE))
+ {
+ text_buffer.append(possible_text.getNodeValue());
+ }
+ }
+ return text_buffer.toString();
+ }
+ return "";
+ }
+
+ /**
+ * Method to retrieve from the node given, a certain child node with the
+ * specified name.
+ *
+ * @param parent
+ * The Node whose children should be searched.
+ * @param name
+ * The required nodes name as a String .
+ * @return The requested Node if it is found, null
+ * otherwise. Soon to be deprecated!
+ */
+ static final public Node getNodeFromNamed(Node parent, String name)
+ {
+ Node child = null;
+ for (Node i = parent.getFirstChild(); i != null && child == null; i = i.getNextSibling())
+ {
+ if (i.getNodeName().equals(name))
+ {
+ child = i;
+ }
+ }
+ return child;
+ }
+
+ static final public String WELLFORMED = "well-formed !";
+ static final public String NOTWELLFORMED = "not well-formed";
+ static final private String HEADER = "";
+ static final private String FOOTER = " ";
+
+
+ public static Document getDOM(String xml_str)
+ {
+ Document doc = null;
+ try {
+
+ DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ InputSource is = new InputSource();
+ is.setCharacterStream(new StringReader(xml_str));
+ doc = db.parse(is);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return doc;
+ }
+
+ public static String parse(String xml_str)
+ {
+ String validation_msg = WELLFORMED;
+ xml_str = HEADER + xml_str + FOOTER;
+ try
+ {
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+ factory.setNamespaceAware(true);
+ //factory.setValidating (true);
+ SAXParser parser = factory.newSAXParser();
+ InputSource iSource = new InputSource(new StringReader(xml_str));
+ // parser.parse (iSource, new DefaultHandler ());
+
+ org.xml.sax.XMLReader reader = parser.getXMLReader();
+ reader.setContentHandler(new DefaultHandler());
+ reader.setErrorHandler(new DefaultHandler());
+ reader.parse(iSource);
+ }
+ catch (FactoryConfigurationError e)
+ {
+ validation_msg = "unable to get a document builder factory";
+ }
+ catch (ParserConfigurationException e)
+ {
+ validation_msg = "unable to configure parser";
+ }
+ catch (SAXParseException e)
+ {
+ validation_msg = NOTWELLFORMED + getLocationString(e) + e.getMessage();
+ }
+ catch (SAXException e)
+ {
+ validation_msg += " Fatal error: " + e.toString();
+ }
+ catch (IOException e)
+ {
+ validation_msg = "Unable to read the input, i/o error";
+ }
+
+ return validation_msg;
+ }
+
+ //In this method, the parsed string xml_str is not wrapped by the header and footer strings.
+ public static String parseDOM(String xml_str)
+ {
+ String validation_msg = WELLFORMED;
+
+ try
+ {
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+ factory.setNamespaceAware(true);
+ //factory.setValidating (true);
+ SAXParser parser = factory.newSAXParser();
+ InputSource iSource = new InputSource(new StringReader(xml_str));
+ // parser.parse (iSource, new DefaultHandler ());
+
+ org.xml.sax.XMLReader reader = parser.getXMLReader();
+ reader.setContentHandler(new DefaultHandler());
+ reader.setErrorHandler(new DefaultHandler());
+ reader.parse(iSource);
+ }
+ catch (FactoryConfigurationError e)
+ {
+ validation_msg = "unable to get a document builder factory";
+ }
+ catch (ParserConfigurationException e)
+ {
+ validation_msg = "unable to configure parser";
+ }
+ catch (SAXParseException e)
+ {
+ validation_msg = NOTWELLFORMED + getLocationString(e) + e.getMessage();
+ }
+ catch (SAXException e)
+ {
+ validation_msg += " " + e.toString();
+ }
+ catch (IOException e)
+ {
+ validation_msg = "Unable to read the input, i/o error";
+ }
+
+ return validation_msg;
+ }
+
+ public static String parse(File xml_file)
+ {
+ String validation_msg = WELLFORMED;
+
+ try
+ {
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+ factory.setNamespaceAware(true);
+ //factory.setValidating (true);
+ SAXParser parser = factory.newSAXParser();
+ FileReader r = new FileReader(xml_file);
+ InputSource iSource = new InputSource(r);
+ XMLReader reader = parser.getXMLReader();
+ reader.setContentHandler(new DefaultHandler());
+ reader.setErrorHandler(new DefaultHandler());
+ reader.parse(iSource);
+ }
+ catch (FactoryConfigurationError e)
+ {
+ validation_msg = "unable to get a document builder factory";
+ }
+ catch (ParserConfigurationException e)
+ {
+ validation_msg = "unable to configure parser";
+ }
+ catch (SAXParseException e)
+ {
+ validation_msg = NOTWELLFORMED + getLocationString(e) + e.getMessage();
+ }
+ catch (SAXException e)
+ {
+ validation_msg += " Fatal error: " + e.toString();
+ }
+ catch (IOException e)
+ {
+ validation_msg = "Unable to read the input, i/o error";
+ }
+
+ return validation_msg;
+ }
+
+ /** Returns a string of the location. */
+ private static String getLocationString(SAXParseException ex)
+ {
+ StringBuffer str = new StringBuffer();
+
+ String systemId = ex.getSystemId();
+ if (systemId != null)
+ {
+ int index = systemId.lastIndexOf('/');
+ if (index != -1)
+ systemId = systemId.substring(index + 1);
+ str.append(systemId);
+ }
+ str.append("(line ");
+ str.append(ex.getLineNumber() - 1);
+ str.append(", column ");
+ str.append(ex.getColumnNumber());
+ str.append("): ");
+
+ return str.toString();
+
+ } // getLocationString(SAXParseException):String
+
+ /** Parse an XML document from a given file path */
+ static public Document parseXMLFile(String xml_file_path, boolean use_class_loader)
+ {
+ if (use_class_loader == true)
+ {
+ InputStream is = JarTools.getResourceAsStream("/" + xml_file_path);
+ if (is != null)
+ {
+ return parseXML(is);
+ }
+ }
+
+ // Try the file outside the classes directory
+ return parseXMLFile(new File(xml_file_path));
+ }
+
+ /** Parse an XML document from a given file */
+ static public Document parseXMLFile(File xml_file)
+ {
+ // No file? No point trying!
+ if (xml_file.exists() == false)
+ {
+ // System.err.println("@@@ file " + xml_file + " does not exist.");
+ return null;
+ }
+
+ try
+ {
+ return parseXML(new FileInputStream(xml_file));
+ }
+ catch (Exception exception)
+ {
+ DebugStream.printStackTrace(exception);
+ return null;
+ }
+ }
+
+ /** Parse an XML document from a given input stream */
+ static public Document parseXML(InputStream xml_input_stream)
+ {
+ Document document = null;
+
+ try
+ {
+ InputStreamReader isr = new InputStreamReader(xml_input_stream, "UTF-8");
+ document = parseXML(isr);
+ isr.close();
+ xml_input_stream.close();
+ }
+ catch (Exception exception)
+ {
+ DebugStream.printStackTrace(exception);
+ }
+
+ return document;
+ }
+
+ /** Parse an XML document from a given reader */
+ static public Document parseXML(Reader xml_reader)
+ {
+ Document document = null;
+
+ // If debugging, the following will store the XML contents to be parsed,
+ // which can then be inspected upon encountering a SAXException (need to run GLI with -debug on)
+ String xmlContents = "";
+
+ try
+ {
+ Reader reader = null;
+
+ // (1) By default, GLI will remove any contents preceeding (and invalidating)
+ // the XML and present these lines separately to the user
+ if (!DebugStream.isDebuggingEnabled())
+ {
+ try
+ {
+ reader = new BufferedReader(new RemoveContentBeforeRootElementXMLReader(xml_reader));
+ }
+ catch (Exception e)
+ {
+ System.err.println("Exception while wrapping the reader in parseXML(Reader)");
+ e.printStackTrace();
+ }
+ }
+
+ // (2) If we are running GLI in debug mode:
+ // In case parsing exceptions are thrown (SAX Exceptions), we want to get some
+ // idea of where things went wrong. This will print the "XML" contents to either
+ // system.out (if debugging is off) or to the DebugStream otherwise.
+ // We need to read the XML twice to know the line where things went wrong, so
+ // do the additional reading only if we're debugging
+ else
+ {
+ StringBuffer buf = new StringBuffer();
+ char[] buffer = new char[500];
+ int numCharsRead = xml_reader.read(buffer, 0, buffer.length);
+ while (numCharsRead != -1)
+ {
+ buf.append(buffer, 0, numCharsRead);
+ numCharsRead = xml_reader.read(buffer, 0, buffer.length);
+ }
+ xmlContents = buf.toString();
+ xml_reader.close(); // closing the old Reader
+ xml_reader = null;
+ buffer = null;
+ buf = null;
+ // we need a Reader to parse the same contents as the Reader that was just closed
+ reader = new BufferedReader(new StringReader(xmlContents));
+ //System.err.println("xmlContents:\n" + xmlContents);
+ }
+
+ // (2) The actual XML parsing
+ InputSource isc = new InputSource(reader);
+ DOMParser parser = new DOMParser();
+ parser.setFeature("http://xml.org/sax/features/validation", false);
+ parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+ // May or may not be ignored, the documentation for Xerces is contradictory. If it works then parsing -should- be faster.
+ parser.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", true);
+ parser.setFeature("http://apache.org/xml/features/dom/include-ignorable-whitespace", false);
+ parser.setEntityResolver(new GLIEntityResolver());
+ parser.parse(isc);
+ document = parser.getDocument();
+
+ }
+ catch (SAXParseException e)
+ {
+ showXMLParseFailureLine(e, xmlContents);
+ }
+ catch (SAXException exception)
+ {
+ System.err.println("SAX exception: " + exception.getMessage());
+ if (DebugStream.isDebuggingEnabled())
+ {
+ DebugStream.println("Encountered a SAX exception when parsing the following:\n*********START\n" + xmlContents + "\n************END\n");
+ // Exit to let the user view the erroneous line/xml before it goes past the screen buffer?
+ DebugStream.println("Debug mode: Exiting the program as there was trouble parsing the XML...");
+ System.exit(-1);
+ }
+ // else, not running in debug mode, so don't exit after exception
+ System.out.println("***Turn debugging on (run GLI with -debug) to view the XML contents that could not be parsed.");
+ DebugStream.printStackTrace(exception);
+ }
+ catch (Exception exception)
+ {
+ DebugStream.printStackTrace(exception);
+ }
+
+ return document;
+ }
+
+ /**
+ * Displays the line (string) where the SAXParseException occurred, given a
+ * String of the entire xml that was being parsed and the SAXParseException
+ * object that was caught. The messages are printed to DebugStream, so run
+ * GLI/FLI with -debug to view this output.
+ *
+ * @param xmlContents
+ * is the entire xml that was being parsed when the exception
+ * occurred
+ * @param e
+ * is the SAXParseException object that was thrown upon parsing
+ * the xmlContents.
+ */
+ public static void showXMLParseFailureLine(SAXParseException e, String xmlContents)
+ {
+
+ // There should be no characters at all that preceed the ... bit.
+ // The first check is for starting spaces:
+ if (xmlContents.startsWith("\n") || xmlContents.startsWith(" ") || xmlContents.startsWith("\t"))
+ {
+ DebugStream.println("ERROR: illegal start of XML. Space/tab/newline should not preceed xml declaration.\n");
+ DebugStream.println("xmlContents (length is " + xmlContents.length() + "):\n" + xmlContents);
+ return; // nothing more to do, first error identified
+ }
+
+ // the actual line (String literal) where parsing failed and the SAXParseException occurred.
+ String line = "";
+ int linenumber = e.getLineNumber();
+ DebugStream.print("\n****SAXParseException on LINE NUMBER: " + linenumber);
+ if (DebugStream.isDebuggingEnabled())
+ {
+ if (linenumber != -1)
+ {
+ String[] lines = xmlContents.split("\n");
+ if (lines.length > 0)
+ {
+ DebugStream.println(" (number of lines: " + lines.length + ")");
+ if (lines.length >= linenumber)
+ {
+ line = lines[linenumber - 1];
+ }
+ else
+ { // error is past the last line
+ line = "Error is past the last line (" + lines.length + "): " + lines[lines.length - 1];
+ }
+ }
+ else
+ {
+ DebugStream.print("\n");
+ }
+ lines = null;
+
+ DebugStream.println("The parsing error occurred on this line:\n***********START\n" + line + "\n***********END");
+ DebugStream.println("SAXParseException message: " + e.getMessage() + "\n");
+
+ // Uncomment if you want to print out the entire contents of the XML doc:
+ //DebugStream.println("\n\nThis was the XML:\n*********START\n"
+ // + xmlContents + "\n************END\n");
+ }
+ else
+ { // no particular line number, print out all the xml so debugger can inspect it
+ DebugStream.println("Encountered a SAX exception when parsing the following:\n*********START\n" + xmlContents + "\n************END\n");
+ }
+ // Exit to let the user view the erroneous line/xml before it goes past the screen buffer?
+ DebugStream.println("\nDebug mode: Exiting the program as there was trouble parsing the XML...");
+ System.exit(-1);
+ }
+ else
+ { // not running in debug mode
+ System.out.println("***Turn debugging on (run GLI with -debug) to view the XML contents/line that could not be parsed.");
+ }
+ }
+
+ static public StringBuffer readXMLStream(InputStream input_stream)
+ {
+ StringBuffer xml = new StringBuffer("");
+
+ try
+ {
+ InputStreamReader isr = new InputStreamReader(input_stream, "UTF-8");
+ BufferedReader buffered_in = new BufferedReader(isr);
+
+ String line = "";
+ boolean xml_content = false;
+ while ((line = buffered_in.readLine()) != null)
+ {
+ if (xml_content)
+ {
+ xml.append(line);
+ xml.append("\n");
+ }
+ else if (line.trim().startsWith("= 0x20 && character <= 0xD7FF) || character == 0x09 || character == 0x0A || character == 0x0D || (character >= 0xE000 && character <= 0xFFFD) || (character >= 0x10000 && character <= 0x10FFFF))
+ {
+ safe_characters[j] = character;
+ j++;
+ }
+ }
+
+ return new String(safe_characters, 0, j);
+ }
+
+ static public void setElementTextValue(Element element, String text)
+ {
+ // Remove all text node children
+ NodeList children_nodelist = element.getChildNodes();
+ for (int i = children_nodelist.getLength() - 1; i >= 0; i--)
+ {
+ Node child_node = children_nodelist.item(i);
+ if (child_node.getNodeType() == Node.TEXT_NODE)
+ {
+ element.removeChild(child_node);
+ }
+ }
+
+ // Add a new text node
+ if (text != null)
+ {
+ element.appendChild(element.getOwnerDocument().createTextNode(text));
+ }
+ }
+
+ /**
+ * Set the #text node value of some element.
+ *
+ * @param element
+ * the Element whose value we wish to set
+ * @param value
+ * the new value for the element as a String Soon to be
+ * deprecated!
+ */
+ static final public void setValue(Element element, String value)
+ {
+ // Remove any existing child node(s)
+ clear(element);
+ // Add new text node.
+ if (value != null)
+ {
+ element.appendChild(element.getOwnerDocument().createTextNode(value));
+ }
+ }
+
+ static public void indentXML(Element elem, int depth)
+ {
+ Document doc = elem.getOwnerDocument();
+
+ String startIndentString = "\n";
+ for (int i = 0; i < depth; i++)
+ {
+ startIndentString += "\t";
+ }
+ Node startTextNode = doc.createTextNode(startIndentString);
+
+ String endIndentString = "\n";
+ for (int i = 0; i < depth - 1; i++)
+ {
+ endIndentString += "\t";
+ }
+ Node endTextNode = doc.createTextNode(endIndentString);
+
+ boolean found = false;
+ Node child = elem.getFirstChild();
+ while (child != null)
+ {
+ // first clear all empty text nodes (those containing space characters like \n,\r,\t and such)
+ if(child.getNodeType() == Node.TEXT_NODE && child.getNodeValue().matches("^\\s*$"))
+ {
+ Node spaceTextNode = child;
+ child = child.getNextSibling();
+ elem.removeChild(spaceTextNode);
+
+ if(child == null) break;
+ }
+
+ // now process normal element nodes as intended
+ if (child.getNodeType() == Node.ELEMENT_NODE)
+ {
+ found = true;
+ break;
+ }
+ child = child.getNextSibling();
+ }
+
+ if (found)
+ {
+ elem.appendChild(endTextNode);
+ }
+
+ child = elem.getFirstChild();
+ while (child != null)
+ {
+ // Again, need to first clear all empty text nodes (those containing space characters like \n,\r,\t and such)
+ // because the first while loop above would break out when it found an element node and wouldn't have got rid
+ // of all the empty text nodes yet.
+ // This time, beware not to delete the special end and start empty textnodes just added, since
+ // they've been created and inserted specifically.
+ if(child != endTextNode && child != startTextNode
+ && child.getNodeType() == Node.TEXT_NODE && child.getNodeValue().matches("^\\s*$"))
+ {
+ Node spaceTextNode = child;
+ child = child.getNextSibling();
+ elem.removeChild(spaceTextNode);
+
+ if(child == null) break;
+ }
+
+ // go back to processing normal element nodes as intended
+ if (child.getNodeType() == Node.ELEMENT_NODE)
+ {
+ elem.insertBefore(startTextNode.cloneNode(false), child);
+ indentXML((Element) child, depth + 1);
+ }
+ child = child.getNextSibling();
+ }
+ }
+
+ /**
+ * Write an XML document to a given file with the text node of the specified
+ * element unescaped
+ */
+ static public void writeXMLFile(File xml_file, Document document, String[] nonEscapingTagNames)
+ {
+ indentXML(document.getDocumentElement(), 1);
+ try
+ {
+ OutputStream os = new FileOutputStream(xml_file);
+ // Create an output format for our document.
+ OutputFormat f = new OutputFormat(document);
+ f.setEncoding("UTF-8");
+ f.setIndenting(true);
+ f.setLineWidth(0); // Why isn't this working!
+ f.setPreserveSpace(true);
+ if (nonEscapingTagNames != null)
+ {
+ f.setNonEscapingElements(nonEscapingTagNames);
+ }
+ // Create the necessary writer stream for serialization.
+ OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
+ Writer w = new BufferedWriter(osw);
+ // Generate a new serializer from the above.
+ XMLSerializer s = new XMLSerializer(w, f);
+ s.asDOMSerializer();
+ // Finally serialize the document to file.
+ s.serialize(document);
+ // And close.
+ os.close();
+ }
+ catch (Exception exception)
+ {
+ DebugStream.printStackTrace(exception);
+ }
+ }
+
+ /** Write an XML document to a given file */
+ static public void writeXMLFile(File xml_file, Document document)
+ {
+ writeXMLFile(xml_file, document, null);
+ }
+
+ public static void printXMLNode(Node e)
+ {
+ printXMLNode(e, 0);
+ }
+
+ public static void printXMLNode(Node e, int depth)
+ { //recursive method call using DOM API...
+
+ for (int i = 0; i < depth; i++)
+ System.out.print(' ');
+
+ if (e.getNodeType() == Node.TEXT_NODE)
+ {
+ //System.out.println("text") ;
+ if (e.getNodeValue() != "")
+ {
+ System.out.println(e.getNodeValue());
+ }
+ return;
+ }
+
+ System.out.print('<');
+ System.out.print(e.getNodeName());
+ NamedNodeMap attrs = e.getAttributes();
+ if (attrs != null)
+ {
+ for (int i = 0; i < attrs.getLength(); i++)
+ {
+ Node attr = attrs.item(i);
+ System.out.print(' ');
+ System.out.print(attr.getNodeName());
+ System.out.print("=\"");
+ System.out.print(attr.getNodeValue());
+ System.out.print('"');
+ }
+ }
+ NodeList children = e.getChildNodes();
+
+ if (children == null || children.getLength() == 0)
+ System.out.println("/>");
+ else
+ {
+
+ System.out.println('>');
+
+ int len = children.getLength();
+ for (int i = 0; i < len; i++)
+ {
+ printXMLNode(children.item(i), depth + 1);
+ }
+
+ for (int i = 0; i < depth; i++)
+ System.out.print(' ');
+
+ System.out.println("" + e.getNodeName() + ">");
+ }
+
+ }
+
+ public static String xmlNodeToString(Node e)
+ {
+ StringBuffer sb = new StringBuffer("");
+ xmlNodeToString(sb, e, true, "\t", 2);
+ return sb.toString();
+ }
+
+ public static void xmlNodeToString(StringBuffer sb, Node e, boolean indent, String indentString, int depth)
+ {
+
+ if (e.getNodeType() == Node.CDATA_SECTION_NODE)
+ {
+ if (e.getNodeValue() != "")
+ {
+ String text = e.getNodeValue();
+ sb.append("");
+ }
+ return;
+ }
+
+ if (e.getNodeType() == Node.TEXT_NODE)
+ {
+ if (e.getNodeValue() != "")
+ {
+ String text = e.getNodeValue();
+ text = text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("[\\n\\r\\t\\s]*$", "");
+ for (Character c : text.toCharArray())
+ {
+ if (c.equals('\n'))
+ {
+ text = text.replaceAll("^[\\n\\r\\t\\s]*", "");
+ break;
+ }
+
+ if (!Character.isWhitespace(c))
+ {
+ break;
+ }
+ }
+ sb.append(text);
+ }
+ return;
+ }
+
+ if (e.getNodeType() == Node.COMMENT_NODE)
+ {
+ if (e.getNodeValue() != "")
+ {
+ sb.append("\n");
+ }
+ return;
+ }
+
+ if (indent)
+ {
+ for (int i = 0; i < depth; i++)
+ {
+ sb.append(indentString);
+ }
+ }
+
+ sb.append('<');
+ sb.append(e.getNodeName());
+ NamedNodeMap attrs = e.getAttributes();
+ if (attrs != null)
+ {
+ for (int i = 0; i < attrs.getLength(); i++)
+ {
+ Node attr = attrs.item(i);
+ sb.append(' ');
+ sb.append(attr.getNodeName());
+ sb.append("=\"");
+ sb.append(attr.getNodeValue().replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">"));
+ sb.append('"');
+ }
+ }
+ NodeList children = e.getChildNodes();
+
+ boolean hasElements = false;
+ boolean indentSwapped = false;
+ for (int i = 0; i < children.getLength(); i++)
+ {
+ if (children.item(i).getNodeType() == Node.ELEMENT_NODE)
+ {
+ hasElements = true;
+ }
+ if ((children.item(i).getNodeType() == Node.TEXT_NODE || children.item(i).getNodeType() == Node.CDATA_SECTION_NODE) && indent)
+ {
+ if (children.item(i).getNodeValue().trim().length() > 0)
+ {
+ indentSwapped = true;
+ indent = false;
+ }
+ }
+ }
+
+ if (children == null || children.getLength() == 0)
+ {
+ sb.append("/>");
+
+ if (indent)
+ {
+ sb.append("\n");
+ }
+ }
+ else
+ {
+ sb.append(">");
+ if (hasElements && indent)
+ {
+ sb.append("\n");
+ }
+
+ int len = children.getLength();
+ for (int i = 0; i < len; i++)
+ {
+ xmlNodeToString(sb, children.item(i), indent, indentString, depth + 1);
+ }
+
+ if (indent)
+ {
+ for (int i = 0; i < depth; i++)
+ {
+ sb.append(indentString);
+ }
+ }
+
+ sb.append("" + e.getNodeName() + ">");
+
+ if ((hasElements && indent) || indentSwapped)
+ {
+ sb.append("\n");
+ }
+ }
+ }
+
+ public static String xmlNodeToStringWithoutIndenting(Node e)
+ {
+ StringBuffer sb = new StringBuffer("");
+ xmlNodeToStringWithoutNewline(sb, e, -1);
+ return sb.toString();
+ }
+
+ public static String xmlNodeToStringWithoutNewline(Node e)
+ {
+ StringBuffer sb = new StringBuffer("");
+ xmlNodeToStringWithoutNewline(sb, e, 0);
+ return sb.toString();
+ }
+
+ private static void xmlNodeToStringWithoutNewline(StringBuffer sb, Node e, int depth)
+ {
+
+ for (int i = 0; i < depth; i++)
+ {
+ sb.append(' ');
+ }
+
+ if (e.getNodeType() == Node.TEXT_NODE)
+ {
+ if (e.getNodeValue() != "")
+ {
+ sb.append(e.getNodeValue().replaceAll("&", "&").replaceAll("<", "<").replace(">", ">"));
+ }
+ return;
+ }
+
+ if (e.getNodeType() == Node.COMMENT_NODE)
+ {
+ if (e.getNodeValue() != "")
+ {
+ sb.append("");
+ }
+ return;
+ }
+
+ sb.append('<');
+ sb.append(e.getNodeName());
+ NamedNodeMap attrs = e.getAttributes();
+ if (attrs != null)
+ {
+ for (int i = 0; i < attrs.getLength(); i++)
+ {
+ Node attr = attrs.item(i);
+ sb.append(' ');
+ sb.append(attr.getNodeName());
+ sb.append("=\"");
+ sb.append(attr.getNodeValue());
+ sb.append('"');
+ }
+ }
+ NodeList children = e.getChildNodes();
+
+ if (children == null || children.getLength() == 0)
+ sb.append("/>");
+ else
+ {
+
+ sb.append(">");
+
+ int len = children.getLength();
+ for (int i = 0; i < len; i++)
+ {
+ if (depth >= 0)
+ {
+ xmlNodeToStringWithoutNewline(sb, children.item(i), depth + 1);
+ }
+ else
+ {
+ xmlNodeToStringWithoutNewline(sb, children.item(i), depth);
+ }
+ }
+
+ for (int i = 0; i < depth; i++)
+ sb.append(' ');
+
+ sb.append("" + e.getNodeName() + ">");
+ }
+ }
+
+
+
+ // This method will convert an Element to a String too, like xmlNodeToString() above.
+ // But for a document root element (doc.getDocumentElement()), this method will additionally
+ // return its processing instruction line at the start ().
+ // This method copied into GLI from src/java/org/greenstone/gsdl3/util/GSXML.java
+ public static String elementToString(Element e, boolean indent)
+ {
+ String str = "";
+ try
+ {
+ TransformerFactory tf = TransformerFactory.newInstance();
+ Transformer trans = tf.newTransformer();
+ StringWriter sw = new StringWriter();
+ if (indent)
+ {
+ trans.setOutputProperty(OutputKeys.INDENT, "yes");
+ }
+ else
+ {
+ trans.setOutputProperty(OutputKeys.INDENT, "no");
+ }
+ trans.transform(new DOMSource(e), new StreamResult(sw));
+ str = sw.toString();
+ }
+ catch (Exception ex)
+ {
+ str += "Exception: couldn't write " + e + " to log";
+ }
+ finally
+ {
+ return str;
+ }
+ }
+}