Changeset 4365
- Timestamp:
- 2003-05-27T15:49:22+12:00 (21 years ago)
- Location:
- trunk/gli/src/org/greenstone/gatherer
- Files:
-
- 31 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/gli/src/org/greenstone/gatherer/help/ContentModel.java
r4293 r4365 38 38 */ 39 39 public class ContentModel 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 40 extends DefaultTreeModel { 41 /** Constructor that builds the contents model. 42 * @see org.greenstone.gatherer.help.HelpItem 43 */ 44 public ContentModel() { 45 super(new HelpItem("Contents")); 46 // Load the contents file and build the contents structure found within. 47 try { 48 FileReader file_reader = new FileReader(Utility.HELP_DIR + "index.txt"); 49 BufferedReader buffered_reader = new BufferedReader(file_reader); 50 String line = null; 51 HashMap known_entries = new HashMap(); 52 while((line = buffered_reader.readLine()) != null) { 53 // We'll use my ever popular command tokenizer to ensure we can parse index | title. 54 CommandTokenizer tokenizer = new CommandTokenizer(line); 55 // The first thing off the tokenizer is an index of the form xx.xx.xx 56 String index_str = tokenizer.nextToken(); 57 // And the second is a title for this entry 58 String title = tokenizer.nextToken(); 59 // Now we determine the parent of this entry. In general this involves removing the last portion of the index so 2.3 becomes 2. However there is a special case when the index has only one part, ie 2, in which case it get added to the root, then the index is replaced with 2.0. 60 int position = -1; 61 if((position = index_str.lastIndexOf(".")) != -1) { 62 // Find parent index 63 String parent_index = index_str.substring(0, position); 64 // Retrieve parent item 65 HelpItem parent_item = (HelpItem) known_entries.get(parent_index); 66 // Otherwise append ".0" and search again... 67 if(parent_item == null) { 68 parent_item = (HelpItem) known_entries.get(parent_index + ".0"); 69 } 70 // Otherwise the parent item must be the root. 71 if(parent_item == null) { 72 parent_item = (HelpItem) root; 73 } 74 // Filename has - instead of . 75 String file_name = index_str.replace('.', '-'); 76 // Create the new item 77 HelpItem item; 78 File temp_file = new File(Utility.HELP_DIR + file_name + ".xml"); 79 if(temp_file.exists()) { 80 item = new HelpItem(index_str + " " + title, Utility.HELP_DIR + file_name + ".xml"); 81 } 82 else { 83 item = new HelpItem(index_str + " " + title, Utility.HELP_DIR + file_name + ".htm"); 84 } 85 temp_file = null; 86 // Insert into parent 87 insertNodeInto(item, parent_item, parent_item.getChildCount()); 88 // Record in known entries 89 known_entries.put(index_str, item); 90 item = null; 91 file_name = null; 92 parent_item = null; 93 parent_index = null; 94 } 95 title = null; 96 index_str = null; 97 tokenizer = null; 98 } 99 known_entries.clear(); 100 known_entries = null; 101 line = null; 102 buffered_reader.close(); 103 buffered_reader = null; 104 file_reader.close(); 105 file_reader = null; 106 } 107 catch (Exception error) { 108 System.err.println("Cannot build help contents. Possible error in index.txt"); 109 error.printStackTrace(); 110 } 111 } 112 112 } 113 -
trunk/gli/src/org/greenstone/gatherer/help/HelpFrame.java
r4293 r4365 75 75 */ 76 76 public class HelpFrame 77 78 79 80 77 extends JFrame { 78 /** The html rendering pane. */ 79 private CalHTMLPane view = null; 80 private ContentModel model = null; 81 81 82 83 84 85 86 87 88 89 90 91 92 82 private JSplitPane split_pane = null; 83 /** A contents tree for the left of the frame. */ 84 private JTree contents = null; 85 /** The size of a button on this pane. */ 86 static final Dimension BUTTON_SIZE = new Dimension(100,50); 87 /** The minimum size of any component in this frame. */ 88 static final Dimension MIN_SIZE = new Dimension(100,100); 89 /** The size of the frame itself. */ 90 static final Dimension SIZE = new Dimension(760,560); 91 /** Constructor. 92 * @param file_name The file name, as a <Strong>String</strong> of the help page to initially show. 93 93 */ 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 94 public HelpFrame() { 95 setDefaultCloseOperation(HIDE_ON_CLOSE); 96 setSize(SIZE); 97 model = new ContentModel(); 98 contents = new JTree(model); 99 contents.addTreeSelectionListener(new ContentsListener()); 100 contents.setExpandsSelectedPaths(true); 101 view = new CalHTMLPane(); 102 // Creation 103 JPanel content_pane = (JPanel) this.getContentPane(); 104 split_pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); 105 JScrollPane index_scroll = new JScrollPane(contents); 106 JScrollPane view_scroll = new JScrollPane(view); 107 // Layout 108 split_pane.add(index_scroll, JSplitPane.LEFT); 109 split_pane.add(view_scroll, JSplitPane.RIGHT); 110 content_pane.setLayout(new BorderLayout()); 111 content_pane.add(split_pane, BorderLayout.CENTER); 112 // Center 113 Dimension screen_size = Toolkit.getDefaultToolkit().getScreenSize(); 114 setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2); 115 } 116 /** Destructor. Removes all listeners, closes streams and explicitly sets data members to null. */ 117 public void destroy() { 118 view = null; 119 contents = null; 120 } 121 public void setView(String index) { 122 try { 123 123 // Retrieve the node at index 124 125 126 127 128 129 130 131 132 133 134 124 HelpItem current = (HelpItem) model.getRoot(); 125 StringTokenizer tokenizer = new StringTokenizer(index, "."); 126 while(tokenizer.hasMoreTokens()) { 127 String token = tokenizer.nextToken(); 128 int position = Integer.parseInt(token); 129 if(position > 0 && position <= current.getChildCount()) { 130 current = (HelpItem) current.getChildAt(position - 1); 131 } 132 token = null; 133 } 134 tokenizer = null; 135 135 // Ensure path is selected 136 137 138 139 136 TreePath path = new TreePath(current.getPath()); 137 contents.setSelectionPath(path); 138 contents.scrollPathToVisible(path); 139 path = null; 140 140 // Build url 141 142 141 URL url = current.getURL(); 142 current = null; 143 143 // Show help dialog 144 145 144 show(); 145 split_pane.setDividerLocation(0.2); 146 146 // Display help page 147 148 149 150 151 152 153 154 155 156 147 if(url != null) { 148 view.showHTMLDocument(url); 149 } 150 url = null; 151 } 152 catch (Exception error) { 153 show(); 154 split_pane.setDividerLocation(0.2); 155 } 156 } 157 157 158 158 /** This listener is used to lister for selection changes in the contents tree, and to show the appropriate page as required. 159 159 */ 160 161 162 163 160 private class ContentsListener 161 implements TreeSelectionListener { 162 /** Any implementation of <i>TreeSelectionListener</i> must include this method so we can be informed when the tree selection changes. 163 * @param event A <strong>TreeSelectionEvent</strong> containing al the relevant information about the event itself. 164 164 */ 165 166 167 168 169 170 171 172 173 165 public void valueChanged(TreeSelectionEvent event) { 166 TreePath path = event.getPath(); 167 HelpItem node = (HelpItem)path.getLastPathComponent(); 168 URL url = node.getURL(); 169 if(url != null) { 170 view.showHTMLDocument(url); 171 } 172 } 173 } 174 174 } -
trunk/gli/src/org/greenstone/gatherer/help/HelpItem.java
r4293 r4365 59 59 /** This class provides a wrapper around a <strong>DefaultMutableTreeNode</strong> which provides the ability to set an html page to be loaded when this node is selected. */ 60 60 public class HelpItem 61 62 63 64 65 66 67 68 69 70 71 61 extends DefaultMutableTreeNode { 62 /** The file name of the html page to load. */ 63 public String file_name = null; 64 /** The title to be displayed for this tree node. */ 65 public String title = null; 66 /** Default Construtor with no file and no title. */ 67 public HelpItem() { 68 this.file_name = null; 69 this.title = "No Content"; 70 } 71 /** Constructor. 72 72 * @param title The title to be shown for this node as a <strong>String</strong>. 73 73 */ 74 75 76 77 78 74 public HelpItem(String title) { 75 this.file_name = null; 76 this.title = title; 77 } 78 /** Constructor. 79 79 * @param title The title to be shown for this node as a <strong>String</strong>. 80 80 * @param file_name The name of the html file as a <strong>String</strong>. 81 81 */ 82 83 84 82 public HelpItem(String title, String file_name) { 83 this.file_name = file_name; 84 this.title = title; 85 85 86 87 88 89 86 file_name = file_name.replace('/', File.separatorChar); 87 file_name = file_name.replace('\\', File.separatorChar); 88 } 89 /** This method reads in the html file then returns it as one long string. 90 90 * @return A <strong>String</strong> containing the entire html files contents. 91 91 */ 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 92 public String content() { 93 String content = ""; 94 try { 95 File file = new File(file_name); 96 FileReader in_reader = new FileReader(file); 97 BufferedReader in = new BufferedReader(in_reader, 98 Utility.BUFFER_SIZE); 99 String line; 100 while((line = in.readLine()) != null) { 101 content = content + line; 102 } 103 } 104 catch (Exception e) { 105 } 106 return content; 107 } 108 /** Method to get the file name. 109 109 * @return The file name as a <strong>String</strong>. 110 110 */ 111 112 113 114 111 public String getFileName() { 112 return file_name; 113 } 114 /** Method to create a url from the file name. This method is preferred over the <i>content()</i> method, as the url can then be directly parsed by the html renderer. 115 115 * @return A <strong>URL</strong> that points to the files location as determined by the file name. 116 116 */ 117 118 119 120 121 122 123 124 125 126 127 128 129 117 public URL getURL() { 118 URL url = null; 119 if(file_name != null) { 120 try { 121 File file = new File(file_name); 122 url = file.toURL(); 123 } 124 catch (Exception e) { 125 } 126 } 127 return url; 128 } 129 /** Method to acquire a string representation of this node, which in this case is its title. 130 130 * @return A <strong>String</strong> which shows how this node should be displayed in the tree for example. 131 131 */ 132 133 134 132 public String toString() { 133 return title; 134 } 135 135 } 136 137 138 -
trunk/gli/src/org/greenstone/gatherer/mem/Attribute.java
r4293 r4365 44 44 45 45 public class Attribute 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 46 implements Comparable { 47 public String language = null; 48 public String name = null; 49 public String value = null; 50 public String text = null; 51 private boolean language_specific = false; 52 public Attribute(String name, String value) { 53 this.name = name; 54 this.value = value; 55 } 56 public Attribute(String name, String language, String value) { 57 this.language = language; 58 this.language_specific = true; 59 this.name = name; 60 this.value = value; 61 } 62 public int compareTo(Object other) { 63 return toString().compareTo(other.toString()); 64 } 65 public boolean equals(Object other) { 66 return toString().equals(other.toString()); 67 } 68 public String get(int column) { 69 switch(column) { 70 case 0: 71 return name; 72 case 1: 73 if(language_specific) { 74 return language; 75 } 76 default: 77 return value; 78 } 79 } 80 public String toString() { 81 if(text == null) { 82 if(language_specific) { 83 text = name + " [" + language + "] = " + value; 84 } 85 else { 86 text = name + " = " + value; 87 } 88 } 89 return text; 90 } 91 91 } -
trunk/gli/src/org/greenstone/gatherer/mem/AttributeTableModel.java
r4293 r4365 48 48 */ 49 49 public class AttributeTableModel 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 50 extends AbstractTableModel { 51 private Attribute last_attribute = null; 52 private int last_index = -1; 53 private int longest[] = null; 54 private int column_zero_width = 0; 55 private JScrollPane scroll_pane = null; 56 private JTable table = null; 57 private String default_value = null; 58 private String names[] = null; 59 private TreeSet attributes = null; 60 static final private int table_width = 550; 61 62 public AttributeTableModel(TreeSet attributes, String one, String two, String default_value) { 63 super(); 64 this.attributes = attributes; 65 this.default_value = default_value; 66 this.longest = new int[2]; 67 this.names = new String[2]; 68 names[0] = one; 69 names[1] = two; 70 } 71 72 public AttributeTableModel(TreeSet attributes, String one, String two, String three, String default_value) { 73 super(); 74 this.attributes = attributes; 75 this.default_value = default_value; 76 this.longest = new int[3]; 77 this.names = new String[3]; 78 names[0] = one; 79 names[1] = two; 80 names[2] = three; 81 } 82 public void add(Attribute attribute) { 83 attributes.add(attribute); 84 // Determine the row index 85 boolean found = false; 86 int index = 0; 87 Iterator iterator = attributes.iterator(); 88 for(int i = 0; !found && iterator.hasNext(); i++) { 89 if(attribute == iterator.next()) { 90 found = true; 91 index = i; 92 } 93 } 94 // Fire event 95 fireTableRowsInserted(index, index); 96 } 97 98 public boolean contains(String value, int column) { 99 boolean result = false; 100 Iterator iterator = attributes.iterator(); 101 while(iterator.hasNext() && !result) { 102 Attribute attribute = (Attribute) iterator.next(); 103 if(value.equals(attribute.get(column))) { 104 result = true; 105 } 106 } 107 return result; 108 } 109 110 public Attribute getAttribute(int row) { 111 if(row != last_index) { 112 last_index = row; 113 last_attribute = null; 114 114 // Retrieve the value from the set using its iterator 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 115 Iterator iterator = attributes.iterator(); 116 for(int i = 0; i < row && iterator.hasNext(); i++) { 117 iterator.next(); 118 } 119 if(iterator.hasNext()) { 120 last_attribute = (Attribute) iterator.next(); 121 } 122 iterator = null; 123 } 124 return last_attribute; 125 } 126 127 public int getColumnCount() { 128 return names.length; 129 } 130 131 public String getColumnName(int col) { 132 return names[col]; 133 } 134 135 public int getRowCount() { 136 int count = 0; 137 if(attributes != null) { 138 count = attributes.size(); 139 } 140 return count; 141 } 142 143 public Object getValueAt(int row, int col) { 144 String value = null; 145 Attribute attribute = getAttribute(row); 146 if(attribute != null) { 147 value = attribute.get(col); 148 } 149 if(value == null) { 150 value = default_value; 151 } 152 // Update table column width if necessary 153 if(value.length() > longest[col]) { 154 Component component = table.getCellRenderer(row, col).getTableCellRendererComponent(table, value, false, false, 0, 0); 155 int width = component.getPreferredSize().width + 10; 156 TableColumn column = table.getColumnModel().getColumn(col); 157 if(width > column.getPreferredWidth()) { 158 // Calculate default column width 159 int default_width = 0; 160 int total_width = scroll_pane.getSize().width; 161 if(names.length == 2) { 162 if(col == 0) { 163 default_width = total_width / 3; 164 } 165 else { 166 default_width = 2 * (total_width / 3); 167 } 168 } 169 else { 170 switch(col) { 171 case 0: 172 default_width = (2 * total_width) / 9; 173 break; 174 case 1: 175 default_width = total_width / 9; 176 break; 177 default: 178 default_width = 2 * (total_width / 3); 179 break; 180 } 181 } 182 if(width > default_width) { 183 column.setPreferredWidth(width); 184 } 185 else { 186 column.setPreferredWidth(default_width); 187 } 188 } 189 longest[col] = value.length(); 190 } 191 return Utility.stripNL(value); 192 } 193 public void removeRow(final int row) { 194 ///ystem.err.println("Remove row " + row); 195 Attribute attribute = getAttribute(row); 196 if(attribute != null) { 197 attributes.remove(attribute); 198 198 // Arg... this caused me a headache for hours. 199 200 201 202 203 204 205 206 207 208 209 199 last_index = -1; 200 fireTableRowsDeleted(row, row); 201 } 202 } 203 public void setScrollPane(JScrollPane scroll_pane) { 204 this.scroll_pane = scroll_pane; 205 } 206 public void setTable(JTable table) { 207 this.table = table; 208 this.longest = new int[getColumnCount()]; 209 } 210 210 } -
trunk/gli/src/org/greenstone/gatherer/mem/MEMModel.java
r4293 r4365 46 46 */ 47 47 public class MEMModel 48 48 extends DefaultTreeModel { 49 49 50 51 52 50 public MEMModel() { 51 super(new MEMNode()); 52 } 53 53 54 55 56 57 58 59 60 54 public void add(MEMNode parent, Object object, int type) { 55 if(parent == null) { 56 parent = (MEMNode) root; 57 } 58 MEMNode new_node = new MEMNode(type, object, null); 59 SynchronizedTreeModelTools.insertNodeInto(this, parent, new_node); 60 } 61 61 62 63 64 65 62 public MEMNode getProfileNode() { 63 MEMNode node = (MEMNode) root; 64 return (MEMNode) node.getChildAt(node.getChildCount() - 1); 65 } 66 66 67 68 69 70 71 72 73 74 75 67 public void remove(String name, int type) { 68 MEMNode current = (MEMNode) root; 69 for(int i = 0; i < current.getChildCount(); i++) { 70 MEMNode child = (MEMNode) current.getChildAt(i); 71 if(child.toString().equals(name) && type == MEMNode.SET) { 72 // Place on AWT event thread to avoid errors. 73 SynchronizedTreeModelTools.removeNodeFromParent(this, child); 74 return; 75 } 76 76 // This may be the profiler root node, in which case we descend into its children if we're looking for collection file matches. 77 78 79 80 81 82 83 84 85 86 77 else if(type == MEMNode.COLLECTION && child.getType() == MEMNode.PROFILER) { 78 for(int j = 0; j < child.getChildCount(); j++) { 79 MEMNode inner_child = (MEMNode) child.getChildAt(j); 80 if(inner_child.toString().equals(name)) { 81 // Place on AWT event thread to avoid errors. 82 SynchronizedTreeModelTools.removeNodeFromParent(this, inner_child); 83 return; 84 } 85 } 86 } 87 87 // If this is a set and we are looking for an element, then iterate through its children. Can't really do this recursively. 88 89 90 91 92 93 94 95 96 97 98 99 88 else if(type == MEMNode.ELEMENT && child.getType() == MEMNode.SET) { 89 for(int j = 0; j < child.getChildCount(); j++) { 90 MEMNode inner_child = (MEMNode) child.getChildAt(j); 91 if(inner_child.toString().equals(name)) { 92 // Place on AWT event thread to avoid errors. 93 SynchronizedTreeModelTools.removeNodeFromParent(this, inner_child); 94 return; 95 } 96 } 97 } 98 } 99 } 100 100 } -
trunk/gli/src/org/greenstone/gatherer/mem/MEMNode.java
r4293 r4365 49 49 */ 50 50 public class MEMNode 51 52 53 54 55 56 57 58 51 extends DefaultMutableTreeNode { 52 private AttributeTableModel model = null; 53 private int type = 0; 54 static final public int COLLECTION = 0; 55 static final public int ELEMENT = 1; 56 static final public int PROFILER = 2; 57 static final public int ROOT = 3; 58 static final public int SET = 4; 59 59 60 61 62 60 public MEMNode() { 61 this.type = ROOT; 62 } 63 63 64 65 66 67 68 64 public MEMNode(int type, Object object, MEMNode parent) { 65 this.userObject = object; 66 this.parent = parent; 67 this.type = type; 68 } 69 69 70 71 72 73 74 75 70 public Enumeration children() { 71 if(children == null) { 72 mapChildren(); 73 } 74 return children.elements(); 75 } 76 76 77 78 79 77 public boolean getAllowsChildren() { 78 return true; 79 } 80 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 81 public TreeNode getChildAt(int index) { 82 if(children == null) { 83 mapChildren(); 84 } 85 return (TreeNode) children.get(index); 86 } 87 public int getChildCount() { 88 if(children == null) { 89 mapChildren(); 90 } 91 return children.size(); 92 } 93 public ElementWrapper getElement() { 94 ElementWrapper result = null; 95 if(type == ELEMENT) { 96 result = (ElementWrapper) userObject; 97 } 98 return result; 99 } 100 public int getIndex(TreeNode node) { 101 if(children == null) { 102 mapChildren(); 103 } 104 return children.indexOf(node); 105 } 106 public AttributeTableModel getModel() { 107 return model; 108 } 109 public TreeNode getParent() { 110 return parent; 111 } 112 public MetadataSet getSet() { 113 MetadataSet result = null; 114 if(type == SET) { 115 result = (MetadataSet) userObject; 116 } 117 return result; 118 } 119 public int getType() { 120 return type; 121 } 122 public boolean isLeaf() { 123 if(children == null) { 124 mapChildren(); 125 } 126 return children.size() == 0; 127 } 128 public void setModel(AttributeTableModel model) { 129 this.model = model; 130 } 131 public String toString() { 132 String text = "error"; 133 if(userObject != null) { 134 if(userObject instanceof ElementWrapper) { 135 text = ((ElementWrapper)userObject).getName(); 136 } 137 else { 138 text = userObject.toString(); 139 } 140 } 141 return text; 142 } 143 private void mapChildren() { 144 ///ystem.err.println("Mapping the children of " + this); 145 children = new Vector(); 146 // How we build children depends on the node type 147 switch(type) { 148 case PROFILER: // Add the collections as children 149 ArrayList a = Gatherer.c_man.msm.profiler.getCollections(); 150 for(int i = 0; i < a.size(); i++) { 151 children.add(new MEMNode(COLLECTION, a.get(i), this)); 152 } 153 a = null; 154 break; 155 case ROOT: 156 Vector v = Gatherer.c_man.msm.getSets(); 157 for(int i = 0; i < v.size(); i++) { 158 children.add(new MEMNode(SET, v.get(i), this)); 159 } 160 v = null; 161 161 // Add the profile set. 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 162 children.add(new MEMNode(PROFILER, Gatherer.dictionary.get("MEM.Profiles"), this)); 163 break; 164 case SET: // Add the elements as children 165 MetadataSet set = (MetadataSet) userObject; 166 NodeList elements = set.getElements(); 167 set = null; 168 for(int i = 0; i < elements.getLength(); i++) { 169 children.add(new MEMNode(ELEMENT, new ElementWrapper((Element) elements.item(i)), this)); 170 } 171 elements = null; 172 break; 173 case COLLECTION: 174 case ELEMENT: 175 default: 176 } 177 } 178 178 } -
trunk/gli/src/org/greenstone/gatherer/mem/MetadataEditorManager.java
r4293 r4365 63 63 */ 64 64 public class MetadataEditorManager 65 66 67 68 69 70 71 /** The class used to handle add or edit value actions has to be globally available so that we can dispose of its dialog properly. */72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 65 extends JDialog { 66 private AddElementActionListener add_element_action_listener = null; 67 private AddFileActionListener add_file_action_listener = null; 68 private AddSetActionListener add_set_action_listener = null; 69 /** The class used to handle add or edit attribute actions has to be globally available so that we can dispose of its dialog properly. */ 70 private AddOrEditAttributeActionListener add_or_edit_attribute_action_listener = null; 71 /** The class used to handle add or edit value actions has to be globally available so that we can dispose of its dialog properly. */ 72 private AddOrEditValueActionListener add_or_edit_value_action_listener = null; 73 private boolean ignore = false; 74 /** A card layout is used to switch between the two differing detail views. */ 75 private CardLayout card_layout = null; 76 /** Um, the size of the screen I'd guess. */ 77 private Dimension screen_size = null; 78 private ElementWrapper current_element = null; 79 private GValueNode current_value_node = null; 80 /** A reference to ourselves so our inner classes can dispose of us. */ 81 private MetadataEditorManager self = null; 82 private int current_attribute = -1; 83 private JButton add_attribute = null; 84 private JButton add_element = null; 85 private JButton add_file = null; 86 private JButton add_set = null; 87 private JButton add_value = null; 88 private JButton close = null; 89 private JButton edit_attribute = null; 90 private JButton edit_value = null; 91 private JButton remove_attribute = null; 92 private JButton remove_element = null; 93 private JButton remove_file = null; 94 private JButton remove_set = null; 95 private JButton remove_value = null; 96 private JLabel element_name = null; 97 private JLabel profile_name = null; 98 private JLabel set_name = null; 99 private JScrollPane element_attributes_scroll = null; 100 private JScrollPane profile_attributes_scroll = null; 101 private JScrollPane set_attributes_scroll = null; 102 private JPanel details_pane = null; 103 private MEMModel model = null; 104 private MEMNode current_node = null; 105 private MetadataSet current_set = null; 106 private Object target = null; 107 private SmarterTable element_attributes = null; 108 private SmarterTable profile_attributes = null; 109 private SmarterTable set_attributes = null; 110 private SmarterTree element_values = null; 111 /** A tree that represents the current metadata sets associated with this collection. */ 112 private SmarterTree mds_tree = null; 113 private String current_collection_file = null; 114 private String dialog_options[] = null; 115 /** The default size of the editor dialog. */ 116 static final private Dimension ADD_ELEMENT_SIZE = new Dimension(400,125); 117 static final private Dimension ADD_FILE_SIZE = new Dimension(400,125); 118 static final private Dimension ADD_SET_SIZE = new Dimension(400,125); 119 static final private Dimension ADD_OR_EDIT_ATTRIBUTE_SIZE = new Dimension(600,325); 120 static final private Dimension ADD_OR_EDIT_VALUE_SIZE = new Dimension(600,440); 121 static final private Dimension LABEL_SIZE = new Dimension(100,30); 122 static final private Dimension SIZE = new Dimension(800,480); 123 static final private String BLANK = "blank"; 124 static final private String ELEMENT = "element"; 125 static final private String PROFILE = "profile"; 126 static final private String SET = "set"; 127 /** Constructor. */ 128 public MetadataEditorManager() { 129 super(Gatherer.g_man); 130 this.dialog_options = new String[2]; 131 this.screen_size = Gatherer.config.screen_size; 132 this.self = this; 133 134 dialog_options[0] = Gatherer.dictionary.get("General.OK"); 135 dialog_options[1] = Gatherer.dictionary.get("General.Cancel"); 136 // Creation 137 setDefaultCloseOperation(DISPOSE_ON_CLOSE); 138 setModal(true); 139 setSize(SIZE); 140 setTitle(get("Title")); 141 JPanel content_pane = (JPanel) getContentPane(); 142 content_pane.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", false)); 143 144 JPanel upper_pane = new JPanel(); 145 upper_pane.setOpaque(false); 146 147 model = new MEMModel(); 148 149 JPanel mds_tree_pane = new JPanel(); 150 mds_tree_pane.setOpaque(false); 151 mds_tree_pane.setPreferredSize(new Dimension(200,500)); 152 mds_tree = new SmarterTree(model); 153 mds_tree.setCellRenderer(new MEMTreeCellRenderer()); 154 mds_tree.setRootVisible(false); 155 mds_tree.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 156 mds_tree.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false)); 157 mds_tree.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false)); 158 mds_tree.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false)); 159 160 details_pane = new JPanel(); 161 details_pane.setOpaque(false); 162 card_layout = new CardLayout(); 163 164 JPanel set_details_pane = new JPanel(); 165 set_details_pane.setOpaque(false); 166 167 JPanel set_name_pane = new JPanel(); 168 set_name_pane.setOpaque(false); 169 JLabel set_name_label = new JLabel(get("Name")); 170 set_name_label.setOpaque(false); 171 set_name_label.setPreferredSize(LABEL_SIZE); 172 set_name = new JLabel(); 173 set_name.setBorder(BorderFactory.createLoweredBevelBorder()); 174 set_name.setOpaque(false); 175 176 JPanel set_attributes_pane = new JPanel(); 177 set_attributes_pane.setOpaque(false); 178 set_attributes_scroll = new JScrollPane(); 179 set_attributes_scroll.getViewport().setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 180 set_attributes_scroll.setOpaque(true); 181 set_attributes = new SmarterTable(); 182 set_attributes.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 183 set_attributes.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 184 set_attributes.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false)); 185 set_attributes.setHeadingBackground(Gatherer.config.getColor("coloring.collection_heading_background", false)); 186 set_attributes.setHeadingForeground(Gatherer.config.getColor("coloring.collection_heading_foreground", false)); 187 set_attributes.setOpaque(false); 188 set_attributes.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false)); 189 set_attributes.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false)); 190 set_attributes.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 191 192 JPanel element_details_pane = new JPanel(); 193 element_details_pane.setOpaque(false); 194 195 JPanel element_name_pane = new JPanel(); 196 element_name_pane.setOpaque(false); 197 JLabel element_name_label = new JLabel(get("Name")); 198 element_name_label.setOpaque(false); 199 element_name_label.setPreferredSize(LABEL_SIZE); 200 element_name = new JLabel(); 201 element_name.setBorder(BorderFactory.createLoweredBevelBorder()); 202 element_name.setOpaque(false); 203 204 JPanel element_inner_pane = new JPanel(); 205 element_inner_pane.setOpaque(false); 206 207 JPanel element_attributes_pane = new JPanel(); 208 element_attributes_pane.setOpaque(false); 209 element_attributes_scroll = new JScrollPane(); 210 element_attributes_scroll.getViewport().setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 211 element_attributes_scroll.setOpaque(true); 212 element_attributes = new SmarterTable(); 213 element_attributes.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 214 element_attributes.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 215 element_attributes.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false)); 216 element_attributes.setHeadingBackground(Gatherer.config.getColor("coloring.collection_heading_background", false)); 217 element_attributes.setHeadingForeground(Gatherer.config.getColor("coloring.collection_heading_foreground", false)); 218 element_attributes.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false)); 219 element_attributes.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false)); 220 element_attributes.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 221 222 JPanel element_values_pane = new JPanel(); 223 element_values_pane.setOpaque(false); 224 element_values = new SmarterTree(); 225 element_values.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 226 element_values.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false)); 227 element_values.setRootVisible(false); 228 element_values.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false)); 229 element_values.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false)); 230 231 JPanel profile_details_pane = new JPanel(); 232 profile_details_pane.setOpaque(false); 233 JPanel profile_name_pane = new JPanel(); 234 profile_name_pane.setOpaque(false); 235 JLabel profile_name_label = new JLabel(get("Name")); 236 profile_name_label.setOpaque(false); 237 profile_name_label.setPreferredSize(LABEL_SIZE); 238 profile_name = new JLabel(); 239 profile_name.setBorder(BorderFactory.createLoweredBevelBorder()); 240 profile_name.setOpaque(false); 241 242 JPanel profile_attributes_pane = new JPanel(); 243 profile_attributes_pane.setOpaque(false); 244 profile_attributes_scroll = new JScrollPane(); 245 profile_attributes_scroll.getViewport().setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 246 profile_attributes_scroll.setOpaque(true); 247 profile_attributes = new SmarterTable(); 248 profile_attributes.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 249 profile_attributes.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 250 profile_attributes.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false)); 251 profile_attributes.setHeadingBackground(Gatherer.config.getColor("coloring.collection_heading_background", false)); 252 profile_attributes.setHeadingForeground(Gatherer.config.getColor("coloring.collection_heading_foreground", false)); 253 profile_attributes.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false)); 254 profile_attributes.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false)); 255 profile_attributes.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 256 257 JPanel button_pane = new JPanel(); 258 button_pane.setOpaque(false); 259 260 JPanel button_label_pane = new JPanel(); 261 button_label_pane.setOpaque(false); 262 JLabel attribute_label = new JLabel(get("Attribute")); 263 attribute_label.setOpaque(false); 264 //attribute_label.setHorizontalAlignment(JLabel.CENTER); 265 attribute_label.setPreferredSize(LABEL_SIZE); 266 JLabel element_label = new JLabel(get("Element")); 267 element_label.setOpaque(false); 268 //element_label.setHorizontalAlignment(JLabel.CENTER); 269 element_label.setPreferredSize(LABEL_SIZE); 270 JLabel file_label = new JLabel(get("File")); 271 file_label.setOpaque(false); 272 //element_label.setHorizontalAlignment(JLabel.CENTER); 273 file_label.setPreferredSize(LABEL_SIZE); 274 JLabel set_label = new JLabel(get("Set")); 275 set_label.setOpaque(false); 276 //set_label.setHorizontalAlignment(JLabel.CENTER); 277 set_label.setPreferredSize(LABEL_SIZE); 278 JLabel value_label = new JLabel(get("Value")); 279 value_label.setOpaque(false); 280 //value_label.setHorizontalAlignment(JLabel.CENTER); 281 value_label.setPreferredSize(LABEL_SIZE); 282 283 JPanel inner_button_pane = new JPanel(); 284 inner_button_pane.setOpaque(false); 285 add_attribute = new JButton(get("Add")); 286 add_element = new JButton(get("Add")); 287 add_file = new JButton(get("Add")); 288 add_set = new JButton(get("Add")); 289 add_value = new JButton(get("Add")); 290 edit_attribute = new JButton(get("Edit")); 291 edit_value = new JButton(get("Edit")); 292 remove_attribute = new JButton(get("Remove")); 293 remove_element = new JButton(get("Remove")); 294 remove_file = new JButton(get("Remove")); 295 remove_set = new JButton(get("Remove")); 296 remove_value = new JButton(get("Remove")); 297 setControls(false, false, false, false, false, false, false, false, false, false, false, false); 298 299 close = new JButton(get("General.Close")); 300 301 add_element_action_listener = new AddElementActionListener(); 302 add_file_action_listener = new AddFileActionListener(); 303 add_set_action_listener = new AddSetActionListener(); 304 add_or_edit_attribute_action_listener = new AddOrEditAttributeActionListener(); 305 add_or_edit_value_action_listener = new AddOrEditValueActionListener(); 306 307 // Some blank panel 308 JPanel blank_pane = new JPanel(); 309 blank_pane.setOpaque(false); 310 JPanel edit_file_pane = new JPanel(); 311 edit_file_pane.setOpaque(false); 312 JPanel edit_element_pane = new JPanel(); 313 edit_element_pane.setOpaque(false); 314 JPanel edit_set_pane = new JPanel(); 315 edit_set_pane.setOpaque(false); 316 // Connection 317 318 add_attribute.addActionListener(add_or_edit_attribute_action_listener); 319 add_element.addActionListener(add_element_action_listener); 320 add_file.addActionListener(add_file_action_listener); 321 add_set.addActionListener(add_set_action_listener); 322 add_value.addActionListener(add_or_edit_value_action_listener); 323 close.addActionListener(new CloseActionListener()); 324 edit_attribute.addActionListener(add_or_edit_attribute_action_listener); 325 edit_value.addActionListener(add_or_edit_value_action_listener); 326 remove_attribute.addActionListener(new RemoveAttributeActionListener()); 327 remove_element.addActionListener(new RemoveElementActionListener()); 328 remove_file.addActionListener(new RemoveFileActionListener()); 329 remove_set.addActionListener(new RemoveSetActionListener()); 330 remove_value.addActionListener(new RemoveValueActionListener()); 331 element_attributes.getSelectionModel().addListSelectionListener(new AttributesListSelectionListener(element_attributes)); 332 profile_attributes.getSelectionModel().addListSelectionListener(new AttributesListSelectionListener(profile_attributes)); 333 set_attributes.getSelectionModel().addListSelectionListener(new AttributesListSelectionListener(set_attributes)); 334 element_values.addTreeSelectionListener(new ElementValuesTreeSelectionListener()); 335 mds_tree.addTreeSelectionListener(new MDSTreeSelectionListener()); 336 // Layout 337 mds_tree_pane.setLayout(new BorderLayout()); 338 mds_tree_pane.add(new JScrollPane(mds_tree), BorderLayout.CENTER); 339 340 set_name_pane.setLayout(new BorderLayout()); 341 set_name_pane.add(set_name_label, BorderLayout.WEST); 342 set_name_pane.add(set_name, BorderLayout.CENTER); 343 343 344 345 346 347 348 344 set_attributes_scroll.setViewportView(set_attributes); 345 346 set_attributes_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(get("Attributes")), BorderFactory.createEmptyBorder(2,2,2,2))); 347 set_attributes_pane.setLayout(new BorderLayout()); 348 set_attributes_pane.add(set_attributes_scroll, BorderLayout.CENTER); 349 349 350 351 352 353 354 355 356 357 350 set_details_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(get("Set_Details")), BorderFactory.createEmptyBorder(2,2,2,2))); 351 set_details_pane.setLayout(new BorderLayout()); 352 //set_details_pane.add(set_name_pane, BorderLayout.NORTH); 353 set_details_pane.add(set_attributes_pane, BorderLayout.CENTER); 354 355 element_name_pane.setLayout(new BorderLayout()); 356 element_name_pane.add(element_name_label, BorderLayout.WEST); 357 element_name_pane.add(element_name, BorderLayout.CENTER); 358 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 359 element_attributes_scroll.setViewportView(element_attributes); 360 361 element_attributes_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(get("Attributes")), BorderFactory.createEmptyBorder(2,2,2,2))); 362 element_attributes_pane.setLayout(new BorderLayout()); 363 element_attributes_pane.add(element_attributes_scroll, BorderLayout.CENTER); 364 365 element_values_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(get("Values")), BorderFactory.createEmptyBorder(2,2,2,2))); 366 element_values_pane.setLayout(new BorderLayout()); 367 element_values_pane.add(new JScrollPane(element_values), BorderLayout.CENTER); 368 369 element_inner_pane.setLayout(new GridLayout(2,1)); 370 element_inner_pane.add(element_attributes_pane); 371 element_inner_pane.add(element_values_pane); 372 373 element_details_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(get("Element_Details")), BorderFactory.createEmptyBorder(2,2,2,2))); 374 element_details_pane.setLayout(new BorderLayout()); 375 //element_details_pane.add(element_name_pane, BorderLayout.NORTH); 376 element_details_pane.add(element_inner_pane, BorderLayout.CENTER); 377 378 profile_name_pane.setLayout(new BorderLayout()); 379 profile_name_pane.add(profile_name_label, BorderLayout.WEST); 380 profile_name_pane.add(profile_name, BorderLayout.CENTER); 381 381 382 383 384 385 386 382 profile_attributes_scroll.setViewportView(profile_attributes); 383 384 profile_attributes_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(get("Profiles")), BorderFactory.createEmptyBorder(2,2,2,2))); 385 profile_attributes_pane.setLayout(new BorderLayout()); 386 profile_attributes_pane.add(profile_attributes_scroll, BorderLayout.CENTER); 387 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 388 profile_details_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(get("Profile_Details")), BorderFactory.createEmptyBorder(2,2,2,2))); 389 profile_details_pane.setLayout(new BorderLayout()); 390 //profile_details_pane.add(profile_name_pane, BorderLayout.NORTH); 391 profile_details_pane.add(profile_attributes_pane, BorderLayout.CENTER); 392 393 details_pane.setBorder(BorderFactory.createEmptyBorder(0,5,0,0)); 394 details_pane.setLayout(card_layout); 395 details_pane.add(blank_pane, BLANK); 396 details_pane.add(set_details_pane, SET); 397 details_pane.add(element_details_pane, ELEMENT); 398 details_pane.add(profile_details_pane, PROFILE); 399 400 upper_pane.setLayout(new BorderLayout()); 401 upper_pane.add(mds_tree_pane, BorderLayout.WEST); 402 upper_pane.add(details_pane, BorderLayout.CENTER); 403 404 button_label_pane.setLayout(new GridLayout(5,1,0,2)); 405 button_label_pane.add(set_label); 406 button_label_pane.add(file_label); 407 button_label_pane.add(element_label); 408 button_label_pane.add(attribute_label); 409 button_label_pane.add(value_label); 410 411 inner_button_pane.setLayout(new GridLayout(5,3,0,2)); 412 inner_button_pane.add(add_set); 413 inner_button_pane.add(edit_set_pane); 414 inner_button_pane.add(remove_set); 415 inner_button_pane.add(add_file); 416 inner_button_pane.add(edit_file_pane); 417 inner_button_pane.add(remove_file); 418 inner_button_pane.add(add_element); 419 inner_button_pane.add(edit_element_pane); 420 inner_button_pane.add(remove_element); 421 inner_button_pane.add(add_attribute); 422 inner_button_pane.add(edit_attribute); 423 inner_button_pane.add(remove_attribute); 424 inner_button_pane.add(add_value); 425 inner_button_pane.add(edit_value); 426 inner_button_pane.add(remove_value); 427 428 button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0)); 429 button_pane.setLayout(new BorderLayout()); 430 button_pane.add(button_label_pane, BorderLayout.WEST); 431 button_pane.add(inner_button_pane, BorderLayout.CENTER); 432 button_pane.add(close, BorderLayout.EAST); 433 434 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 435 content_pane.setLayout(new BorderLayout()); 436 content_pane.add(upper_pane, BorderLayout.CENTER); 437 content_pane.add(button_pane, BorderLayout.SOUTH); 438 439 // Display 440 setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2); 441 show(); 442 } 443 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 444 public void dispose() { 445 // Destructor 446 card_layout = null; 447 screen_size = null; 448 self = null; 449 add_attribute = null; 450 add_element = null; 451 add_file = null; 452 add_set = null; 453 add_value = null; 454 close = null; 455 edit_attribute = null; 456 edit_value = null; 457 remove_attribute = null; 458 remove_element = null; 459 remove_file = null; 460 remove_set = null; 461 remove_value = null; 462 details_pane = null; 463 element_attributes = null; 464 set_attributes = null; 465 element_name = null; 466 set_name = null; 467 element_values = null; 468 mds_tree = null; 469 target = null; 470 // Dispose of inner dialogs 471 add_element_action_listener.dispose(); 472 add_or_edit_attribute_action_listener.dispose(); 473 add_or_edit_value_action_listener.dispose(); 474 super.dispose(); 475 } 476 477 private String get(String key) { 478 return get(key, (String[])null); 479 } 480 480 481 482 483 484 485 486 487 488 489 490 491 492 481 private String get(String key, String arg) { 482 String[] args = new String[1]; 483 args[0] = arg; 484 return get(key, args); 485 } 486 487 private String get(String key, String[] args) { 488 if(key.indexOf(".") == -1) { 489 key = "MEM." + key; 490 } 491 return Gatherer.dictionary.get(key, args); 492 } 493 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 494 private void setControls(boolean a_s, boolean r_s, boolean a_f, boolean r_f, boolean a_e, boolean r_e, boolean a_a, boolean e_a, boolean r_a, boolean a_v, boolean e_v, boolean r_v) { 495 add_attribute.setEnabled(a_a); 496 add_element.setEnabled(a_e); 497 add_file.setEnabled(true); // Always true 498 add_set.setEnabled(true); // Always true 499 add_value.setEnabled(a_v); 500 edit_attribute.setEnabled(e_a); 501 edit_value.setEnabled(e_v); 502 remove_attribute.setEnabled(r_a); 503 remove_element.setEnabled(r_e); 504 remove_file.setEnabled(r_f); 505 remove_set.setEnabled(r_s); 506 remove_value.setEnabled(r_v); 507 } 508 509 private class AddOrEditAttributeActionListener 510 extends JDialog 511 implements ActionListener { 512 private boolean add_type = true; 513 private ComboArea value = null; 514 private JButton cancel_button = null; 515 private JButton ok_button = null; 516 private JComboBox language_box = null; 517 private GComboBox name = null; 518 private HashMap name_to_values = null; 519 private JLabel target = null; 520 /** Constructor. */ 521 public AddOrEditAttributeActionListener() { 522 super(self); 523 setModal(true); 524 setSize(ADD_OR_EDIT_ATTRIBUTE_SIZE); 525 name_to_values = new HashMap(); 526 526 // Creation 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 527 JPanel content_pane = (JPanel) getContentPane(); 528 content_pane.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", false)); 529 JPanel upper_pane = new JPanel(); 530 upper_pane.setOpaque(false); 531 532 JPanel target_pane = new JPanel(); 533 target_pane.setOpaque(false); 534 JLabel target_label = new JLabel(get("Target")); 535 target_label.setOpaque(false); 536 target_label.setPreferredSize(LABEL_SIZE); 537 target = new JLabel(); 538 target.setOpaque(false); 539 540 541 JPanel name_pane = new JPanel(); 542 name_pane.setOpaque(false); 543 JLabel name_label = new JLabel(get("Name")); 544 name_label.setOpaque(false); 545 name_label.setPreferredSize(LABEL_SIZE); 546 name = new GComboBox(); 547 name.setEditable(true); 548 549 JPanel language_pane = new JPanel(); 550 language_pane.setOpaque(false); 551 JLabel language_label = new JLabel(get("Language")); 552 language_label.setOpaque(false); 553 language_label.setPreferredSize(LABEL_SIZE); 554 language_box = new JComboBox(Gatherer.g_man.config_pane.getLanguageCodes().toArray()); 555 556 JPanel center_pane = new JPanel(); 557 center_pane.setOpaque(false); 558 value = new ComboArea(get("Values"), LABEL_SIZE); 559 value.setOpaque(false); 560 JTextArea v_text_area = (JTextArea) value.getTextComponent(); 561 v_text_area.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 562 v_text_area.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false)); 563 v_text_area.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false)); 564 v_text_area.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false)); 565 JPanel button_pane = new JPanel(); 566 button_pane.setOpaque(false); 567 ok_button = new JButton(get("General.OK")); 568 cancel_button = new JButton(get("General.Cancel")); 569 569 // Connection 570 571 572 573 574 570 TextFieldEnabler ok_button_enabler = new TextFieldEnabler(ok_button); 571 cancel_button.addActionListener(this); 572 name.addActionListener(this); 573 ok_button.addActionListener(this); 574 ok_button_enabler.add((JTextComponent)name.getEditor()); // Assuming the default editor is a JTextField! 575 575 // Layout 576 577 578 579 576 target_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0)); 577 target_pane.setLayout(new BorderLayout()); 578 target_pane.add(target_label, BorderLayout.WEST); 579 target_pane.add(target, BorderLayout.CENTER); 580 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 581 language_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0)); 582 language_pane.setLayout(new BorderLayout()); 583 language_pane.add(language_label, BorderLayout.WEST); 584 language_pane.add(language_box, BorderLayout.CENTER); 585 586 name_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0)); 587 name_pane.setLayout(new BorderLayout()); 588 name_pane.add(name_label, BorderLayout.WEST); 589 name_pane.add(name, BorderLayout.CENTER); 590 591 upper_pane.setLayout(new GridLayout(3,1)); 592 upper_pane.add(target_pane); 593 upper_pane.add(name_pane); 594 upper_pane.add(language_pane); 595 596 value.setBorder(BorderFactory.createEmptyBorder(0,0,5,0)); 597 598 button_pane.setLayout(new GridLayout(1,2,5,0)); 599 button_pane.add(ok_button); 600 button_pane.add(cancel_button); 601 602 center_pane.setLayout(new BorderLayout()); 603 center_pane.add(value, BorderLayout.CENTER); 604 center_pane.add(button_pane, BorderLayout.SOUTH); 605 606 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 607 content_pane.setLayout(new BorderLayout()); 608 content_pane.add(upper_pane, BorderLayout.NORTH); 609 content_pane.add(center_pane, BorderLayout.CENTER); 610 611 setLocation((Gatherer.config.screen_size.width - ADD_OR_EDIT_ATTRIBUTE_SIZE.width) / 2, (Gatherer.config.screen_size.height - ADD_OR_EDIT_ATTRIBUTE_SIZE.height) / 2); 612 } 613 /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to add a new attribute to the selected set or element. 614 * @param event An <strong>ActionEvent</strong> containing information about the event. 615 */ 616 public void actionPerformed(ActionEvent event) { 617 Object source = event.getSource(); 618 if(source == ok_button) { 619 boolean cont = true; 620 // Now we add the new or improved values, removing old values if this is an edit. 621 // Depends on what we are adding the attribute to. 622 if(current_collection_file != null) { 623 String name_str = name.getSelectedItem().toString(); 624 String value_str = value.getText(); 625 AttributeTableModel model = (AttributeTableModel) profile_attributes.getModel(); 626 // Remove the existing one if this is an edit. 627 if(!add_type && current_attribute != -1) { 628 String old_source = (String) profile_attributes.getValueAt(current_attribute, 0); 629 Gatherer.c_man.msm.profiler.removeAction(current_collection_file, old_source); 630 old_source = null; 631 model.removeRow(current_attribute); 632 } 633 // First thing we check from profiles is that there isn't already an entry for this name. 634 if(!model.contains(name_str, 0)) { 635 // Add profile 636 Gatherer.c_man.msm.profiler.addAction(current_collection_file, name_str, value_str); 637 // Update attribute table. 638 model.add(new Attribute(name_str, value_str)); 639 } 640 else { 641 // Show an error message and do not proceed. 642 cont = false; 643 JOptionPane.showMessageDialog(self, get("Attribute_Already_Exists"), get("General.Error"), JOptionPane.ERROR_MESSAGE); 644 } 645 model = null; 646 value_str = null; 647 name_str = null; 648 } 649 else if(current_element != null) { 650 // Add the attribute, even if one of the same name already exists. Maybe one day someone would like to enforce the occurance rules but not me and not today. 651 String name_str = name.getSelectedItem().toString(); 652 String language_code = ((Language)language_box.getSelectedItem()).getCode(); 653 String value_str = value.getText(); 654 AttributeTableModel model = (AttributeTableModel) element_attributes.getModel(); 655 // Remove the existing one if this is an edit. 656 if(!add_type && current_attribute != -1) { 657 model.removeRow(current_attribute); 658 } 659 // Add attribute 660 current_element.addAttribute(name_str, language_code, value_str); 661 // Update the attribute table 662 model.add(new Attribute(name_str, language_code, value_str)); 663 model = null; 664 value_str = null; 665 language_code = null; 666 name_str = null; 667 } 668 else if(current_set != null) { 669 String name_str = name.getSelectedItem().toString(); 670 String value_str = value.getText(); 671 // First thing we check from profiles is that there isn't already an entry for this name. 672 AttributeTableModel model = (AttributeTableModel) set_attributes.getModel(); 673 // Remove the existing one if this is an edit. 674 if(!add_type && current_attribute != -1) { 675 model.removeRow(current_attribute); 676 } 677 // First thing we check from profiles is that there isn't already an entry for this name. 678 if(!model.contains(name_str, 0)) { 679 // Add profile 680 current_set.addAttribute(name_str, value_str); 681 // Update attribute table. 682 model.add(new Attribute(name_str, value_str)); 683 } 684 // Otherwise provide an error message and do not proceed. 685 else { 686 cont = false; 687 JOptionPane.showMessageDialog(self, get("Attribute_Already_Exists"), get("General.Error"), JOptionPane.ERROR_MESSAGE); 688 } 689 model = null; 690 value_str = null; 691 name_str = null; 692 } 693 // Hide dialog if we are allowed to continue. 694 if(cont) { 695 hide(); 696 } 697 } 698 else if(source == cancel_button) { 699 // Hide dialog 700 hide(); 701 } 702 else if(source == name) { 703 Object object = name.getSelectedItem(); 704 if(object != null) { 705 java.util.List values = (java.util.List) name_to_values.get(object.toString()); 706 if(values == null) { // Only for collection files, otherwise values != null && values.size() == 0 707 values = Gatherer.c_man.msm.getElements(); 708 } 709 value.clear(); 710 for(int i = 0; i < values.size(); i++) { 711 value.add(values.get(i)); 712 } 713 } 714 } 715 else { 716 name_to_values.clear(); 717 name.clear(); 718 value.clear(); 719 value.setText(""); 720 // Build the correct name_to_values mapping 721 // Attributes are slightly tricky as they can come from several sources. Has a collection file been selected... 722 if(current_collection_file != null) { 723 target.setText(current_collection_file); 724 // Name is empty in this one, however values must be the current elements in the collection. 725 language_box.setEnabled(false); 726 } 727 // or has an element been selected 728 else if(current_element != null) { 729 target.setText(current_element.toString()); 730 // Develop a model for name based on known attributes from all other elements. 731 java.util.List elements = Gatherer.c_man.msm.getElements(); 732 for(int i = 0; i < elements.size(); i++) { 733 ElementWrapper element = (ElementWrapper) elements.get(i); 734 TreeSet attributes = element.getAttributes(); 735 for(Iterator attribute_iterator = attributes.iterator(); attribute_iterator.hasNext(); ) { 736 Attribute attribute = (Attribute) attribute_iterator.next(); 737 java.util.List values = (java.util.List) name_to_values.get(attribute.name); 738 if(values == null) { 739 values = new ArrayList(); 740 name_to_values.put(attribute.name, values); 741 } 742 if(!values.contains(attribute.value)) { 743 values.add(attribute.value); 744 } 745 values = null; 746 attribute = null; 747 } 748 attributes = null; 749 element = null; 750 } 751 elements = null; 752 language_box.setEnabled(true); 753 } 754 else if(current_set != null) { 755 target.setText(current_set.toString()); 756 // Develop a model for name based on known attributes from all other metadata sets. At the same time build a hashmap mapping attribute name to lists of values. 757 java.util.List sets = Gatherer.c_man.msm.getSets(); 758 for(int i = 0; i < sets.size(); i++) { 759 MetadataSet set = (MetadataSet) sets.get(i); 760 NamedNodeMap attributes = set.getAttributes(); 761 for(int j = 0; j < attributes.getLength(); j++) { 762 Attr attribute = (Attr) attributes.item(j); 763 String name_str = attribute.getName(); 764 String value_str = attribute.getValue(); 765 java.util.List values = (java.util.List) name_to_values.get(name_str); 766 if(values == null) { 767 values = new ArrayList(); 768 name_to_values.put(name_str, values); 769 } 770 if(!values.contains(value_str)) { 771 values.add(value_str); 772 } 773 values = null; 774 value_str = null; 775 name_str = null; 776 attribute = null; 777 } 778 attributes = null; 779 set = null; 780 language_box.setEnabled(false); 781 } 782 sets = null; 783 // If this is an add remove all the attributes already present in the current set. 784 if(source == add_attribute) { 785 name.setEnabled(true); 786 NamedNodeMap attributes = current_set.getAttributes(); 787 for(int i = 0; i < attributes.getLength(); i++) { 788 Attr attribute = (Attr) attributes.item(i); 789 String name_str = attribute.getName(); 790 name_to_values.remove(name_str); 791 name_str = null; 792 attribute = null; 793 } 794 attributes = null; 795 } 796 } 797 // Otherwise we actually disable the name combobox 798 else { 799 name.setEnabled(false); 800 } 801 // Now name_to_values should contain a list of unique attribute names each mapping to a list of attribute values. 802 for(Iterator name_iterator = name_to_values.keySet().iterator(); name_iterator.hasNext(); ) { 803 name.add(name_iterator.next()); 804 } 805 // Now pritty up the dialog depending on the action type 806 if(source == add_attribute) { 807 add_type = true; 808 setTitle(get("AddAttribute")); 809 if(current_collection_file != null) { 810 // Name is empty in this one, however values must be the current elements in the collection. 811 java.util.List values = Gatherer.c_man.msm.getElements(); 812 for(int i = 0; i < values.size(); i++) { 813 value.add(new NameElementWrapperEntry(values.get(i))); 814 } 815 values = null; 816 } 817 show(); 818 } 819 else if(current_attribute != -1) { 820 add_type = false; 821 setTitle(get("EditAttribute")); 822 String name_str = (String) profile_attributes.getValueAt(current_attribute, 0); 823 String value_str = (String) profile_attributes.getValueAt(current_attribute, 1); 824 // Retrieve the appropriate value model 825 java.util.List values = (java.util.List) name_to_values.get(name_str); 826 // Only possible for collection file selections. 827 if(values == null) { 828 values = Gatherer.c_man.msm.getElements(); 829 } 830 name.setSelectedItem(name_str); 831 name_str = null; 832 for(int i = 0; i < values.size(); i++) { 833 value.add(new NameElementWrapperEntry(values.get(i))); 834 } 835 values = null; 836 value.setSelectedItem(value_str); 837 value_str = null; 838 show(); 839 } 840 } 841 source = null; 842 } 843 844 public void dispose() { 845 cancel_button = null; 846 name = null; 847 name_to_values = null; 848 ok_button = null; 849 target = null; 850 value = null; 851 } 852 } 853 853 854 855 856 857 858 859 860 861 862 863 864 854 private class AddElementActionListener 855 extends JDialog 856 implements ActionListener { 857 private JButton cancel_button = null; 858 private JButton ok_button = null; 859 private JLabel set_field = null; 860 private JTextField name_field = null; 861 public AddElementActionListener() { 862 super(self); 863 setModal(true); 864 setSize(ADD_ELEMENT_SIZE); 865 865 // Creation 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 866 JPanel content_pane = (JPanel) getContentPane(); 867 content_pane.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", false)); 868 JPanel center_pane = new JPanel(); 869 center_pane.setOpaque(false); 870 JPanel set_pane = new JPanel(); 871 set_pane.setOpaque(false); 872 JLabel set_label = new JLabel(get("Set")); 873 set_label.setOpaque(false); 874 set_label.setPreferredSize(LABEL_SIZE); 875 set_field = new JLabel(); 876 set_field.setOpaque(false); 877 JPanel name_pane = new JPanel(); 878 name_pane.setOpaque(false); 879 JLabel name_label = new JLabel(get("Name")); 880 name_label.setOpaque(false); 881 name_label.setPreferredSize(LABEL_SIZE); 882 name_field = new JTextField(); 883 name_field.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 884 name_field.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false)); 885 name_field.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false)); 886 name_field.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false)); 887 JPanel button_pane = new JPanel(); 888 button_pane.setOpaque(false); 889 ok_button = new JButton(get("General.OK")); 890 cancel_button = new JButton(get("General.Cancel")); 891 TextFieldEnabler ok_button_enabler = new TextFieldEnabler(ok_button); 892 892 // Connection 893 894 895 893 cancel_button.addActionListener(this); 894 ok_button.addActionListener(this); 895 ok_button_enabler.add(name_field); 896 896 // Layout 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 897 set_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0)); 898 set_pane.setLayout(new BorderLayout()); 899 set_pane.add(set_label, BorderLayout.WEST); 900 set_pane.add(set_field, BorderLayout.CENTER); 901 902 name_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0)); 903 name_pane.setLayout(new BorderLayout()); 904 name_pane.add(name_label, BorderLayout.WEST); 905 name_pane.add(name_field, BorderLayout.CENTER); 906 907 center_pane.setLayout(new GridLayout(2,1,0,0)); 908 center_pane.add(set_pane); 909 center_pane.add(name_pane); 910 911 button_pane.setLayout(new GridLayout(1,2,0,5)); 912 button_pane.add(ok_button); 913 button_pane.add(cancel_button); 914 915 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 916 content_pane.setLayout(new BorderLayout()); 917 content_pane.add(center_pane, BorderLayout.CENTER); 918 content_pane.add(button_pane, BorderLayout.SOUTH); 919 920 setLocation((Gatherer.config.screen_size.width - ADD_ELEMENT_SIZE.width) / 2, (Gatherer.config.screen_size.height - ADD_ELEMENT_SIZE.height) / 2); 921 } 922 /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to 923 * @param event An <strong>ActionEvent</strong> containing information about the event. 924 */ 925 public void actionPerformed(ActionEvent event) { 926 Object source = event.getSource(); 927 if(source == ok_button) { 928 // Add then dispose 929 String name_str = name_field.getText(); 930 // If this element doesn't already exist. 931 if(!current_set.containsElement(name_str)) { 932 // Add it, 933 ElementWrapper element = current_set.addElement(name_str); 934 // Then update the tree 935 model.add(current_node, element, MEMNode.ELEMENT); 936 // Done 937 element = null; 938 hide(); 939 } 940 // Otherwise show an error message and do not proceed. 941 else { 942 JOptionPane.showMessageDialog(self, get("Element_Already_Exists"), get("General.Error"), JOptionPane.ERROR_MESSAGE); 943 } 944 name_str = null; 945 } 946 else if(source == cancel_button) { 947 // Dispose 948 hide(); 949 } 950 else { 951 if(current_set != null) { 952 // You can't manually add elements to the Greenstone metadata set. 953 if(!current_set.getNamespace().equals("")) { 954 set_field.setText(current_set.toString()); 955 name_field.setText(""); 956 // Display 957 show(); 958 } 959 // Warn the user that they can't do that dave. 960 else { 961 JOptionPane.showMessageDialog(self, get("Cannot_Add_Elements_To_Greenstone_MDS"), get("General.Error"), JOptionPane.ERROR_MESSAGE); 962 } 963 } 964 } 965 source = null; 966 } 967 968 public void dispose() { 969 cancel_button = null; 970 ok_button = null; 971 name_field = null; 972 set_field = null; 973 } 974 } 975 975 976 977 978 979 980 981 982 983 984 985 976 private class AddFileActionListener 977 extends JDialog 978 implements ActionListener { 979 private JButton cancel_button = null; 980 private JButton ok_button = null; 981 private JTextField name_field = null; 982 public AddFileActionListener() { 983 super(self); 984 setModal(true); 985 setSize(ADD_FILE_SIZE); 986 986 // Creation 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 987 JPanel content_pane = (JPanel) getContentPane(); 988 content_pane.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", false)); 989 JPanel center_pane = new JPanel(); 990 center_pane.setOpaque(false); 991 JPanel profile_pane = new JPanel(); 992 profile_pane.setOpaque(false); 993 JLabel profile_label = new JLabel(get("Profile")); 994 profile_label.setOpaque(false); 995 profile_label.setPreferredSize(LABEL_SIZE); 996 JPanel name_pane = new JPanel(); 997 name_pane.setOpaque(false); 998 JLabel name_label = new JLabel(get("Name")); 999 name_label.setOpaque(false); 1000 name_label.setPreferredSize(LABEL_SIZE); 1001 name_field = new JTextField(); 1002 name_field.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 1003 name_field.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false)); 1004 name_field.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false)); 1005 name_field.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false)); 1006 JPanel button_pane = new JPanel(); 1007 button_pane.setOpaque(false); 1008 ok_button = new JButton(get("General.OK")); 1009 cancel_button = new JButton(get("General.Cancel")); 1010 TextFieldEnabler ok_button_enabler = new TextFieldEnabler(ok_button); 1011 1011 // Connection 1012 1013 1014 1012 cancel_button.addActionListener(this); 1013 ok_button.addActionListener(this); 1014 ok_button_enabler.add(name_field); 1015 1015 // Layout 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1016 profile_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0)); 1017 profile_pane.setLayout(new BorderLayout()); 1018 profile_pane.add(profile_label, BorderLayout.CENTER); 1019 1020 name_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0)); 1021 name_pane.setLayout(new BorderLayout()); 1022 name_pane.add(name_label, BorderLayout.WEST); 1023 name_pane.add(name_field, BorderLayout.CENTER); 1024 1025 center_pane.setLayout(new GridLayout(2,1,0,0)); 1026 center_pane.add(profile_pane); 1027 center_pane.add(name_pane); 1028 1029 button_pane.setLayout(new GridLayout(1,2,0,5)); 1030 button_pane.add(ok_button); 1031 button_pane.add(cancel_button); 1032 1033 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 1034 content_pane.setLayout(new BorderLayout()); 1035 content_pane.add(center_pane, BorderLayout.CENTER); 1036 content_pane.add(button_pane, BorderLayout.SOUTH); 1037 1038 setLocation((Gatherer.config.screen_size.width - ADD_FILE_SIZE.width) / 2, (Gatherer.config.screen_size.height - ADD_FILE_SIZE.height) / 2); 1039 } 1040 /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to 1041 * @param event An <strong>ActionEvent</strong> containing information about the event. 1042 */ 1043 public void actionPerformed(ActionEvent event) { 1044 Object source = event.getSource(); 1045 if(source == ok_button) { 1046 String name_str = name_field.getText(); 1047 // Ensure that this source doesn't already exist. 1048 if(!Gatherer.c_man.msm.profiler.containsSource(name_str)) { 1049 // Add source with empty hashmap of actions. 1050 Gatherer.c_man.msm.profiler.addSource(name_str); 1051 // Add to tree 1052 model.add(model.getProfileNode(), name_str, MEMNode.COLLECTION); 1053 hide(); 1054 } 1055 // Otherwise warn the user and don't hide the prompt. 1056 else { 1057 JOptionPane.showMessageDialog(self, get("File_Already_Exists"), get("General.Error"), JOptionPane.ERROR_MESSAGE); 1058 } 1059 name_str = null; 1060 } 1061 else if(source == cancel_button) { 1062 hide(); 1063 } 1064 else { 1065 name_field.setText(""); 1066 show(); 1067 } 1068 source = null; 1069 } 1070 1071 public void dispose() { 1072 cancel_button = null; 1073 ok_button = null; 1074 name_field = null; 1075 } 1076 } 1077 1078 private class AddSetActionListener 1079 extends JDialog 1080 implements ActionListener { 1081 private JButton cancel_button = null; 1082 private JButton ok_button = null; 1083 private JTextField name_field = null; 1084 private JTextField namespace_field = null; 1085 public AddSetActionListener() { 1086 super(self); 1087 setModal(true); 1088 setSize(ADD_SET_SIZE); 1089 setTitle(get("AddSet")); 1090 1090 // Creation 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1091 JPanel content_pane = (JPanel) getContentPane(); 1092 content_pane.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", false)); 1093 JPanel center_pane = new JPanel(); 1094 center_pane.setOpaque(false); 1095 1096 JPanel namespace_pane = new JPanel(); 1097 namespace_pane.setOpaque(false); 1098 JLabel namespace_label = new JLabel(get("Namespace")); 1099 namespace_label.setOpaque(false); 1100 namespace_label.setPreferredSize(LABEL_SIZE); 1101 namespace_field = new JTextField(); 1102 namespace_field.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 1103 namespace_field.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false)); 1104 namespace_field.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false)); 1105 namespace_field.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false)); 1106 1107 JPanel name_pane = new JPanel(); 1108 name_pane.setOpaque(false); 1109 JLabel name_label = new JLabel(get("Name")); 1110 name_label.setOpaque(false); 1111 name_label.setPreferredSize(LABEL_SIZE); 1112 name_field = new JTextField(); 1113 name_field.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 1114 name_field.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false)); 1115 name_field.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false)); 1116 name_field.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false)); 1117 1118 JPanel button_pane = new JPanel(); 1119 button_pane.setOpaque(false); 1120 ok_button = new JButton(get("General.OK")); 1121 ok_button.setEnabled(false); 1122 cancel_button = new JButton(get("General.Cancel")); 1123 TextFieldEnabler ok_button_enabler = new TextFieldEnabler(ok_button); 1124 1124 // Connection 1125 1126 1127 1128 1125 cancel_button.addActionListener(this); 1126 ok_button.addActionListener(this); 1127 ok_button_enabler.add(name_field); 1128 ok_button_enabler.add(namespace_field); 1129 1129 // Layout 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1130 namespace_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0)); 1131 namespace_pane.setLayout(new BorderLayout()); 1132 namespace_pane.add(namespace_label, BorderLayout.WEST); 1133 namespace_pane.add(namespace_field, BorderLayout.CENTER); 1134 1135 name_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0)); 1136 name_pane.setLayout(new BorderLayout()); 1137 name_pane.add(name_label, BorderLayout.WEST); 1138 name_pane.add(name_field, BorderLayout.CENTER); 1139 1140 center_pane.setLayout(new GridLayout(2,1)); 1141 center_pane.add(namespace_pane); 1142 center_pane.add(name_pane); 1143 1144 button_pane.setLayout(new GridLayout(1,2,0,5)); 1145 button_pane.add(ok_button); 1146 button_pane.add(cancel_button); 1147 1148 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 1149 content_pane.setLayout(new BorderLayout()); 1150 content_pane.add(center_pane, BorderLayout.CENTER); 1151 content_pane.add(button_pane, BorderLayout.SOUTH); 1152 1153 setLocation((Gatherer.config.screen_size.width - ADD_SET_SIZE.width) / 2, (Gatherer.config.screen_size.height - ADD_SET_SIZE.height) / 2); 1154 } 1155 /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to 1156 * @param event An <strong>ActionEvent</strong> containing information about the event. 1157 */ 1158 public void actionPerformed(ActionEvent event) { 1159 Object source = event.getSource(); 1160 if(source == ok_button) { 1161 String namespace_str = namespace_field.getText(); 1162 String name_str = name_field.getText(); 1163 // Ensure the set doesn't already exist 1164 if(Gatherer.c_man.msm.getSet(name_str) == null) { 1165 MetadataSet set = Gatherer.c_man.msm.addSet(namespace_str, name_str); 1166 // Update tree. 1167 model.add(null, set, MEMNode.SET); 1168 // Done 1169 set = null; 1170 hide(); 1171 } 1172 // Otherwise show a warning. 1173 else { 1174 JOptionPane.showMessageDialog(self, get("Set_Already_Exists"), get("General.Error"), JOptionPane.ERROR_MESSAGE); 1175 } 1176 name_str = null; 1177 namespace_str = null; 1178 } 1179 else if(source == cancel_button) { 1180 hide(); 1181 } 1182 else { 1183 name_field.setText(""); 1184 show(); 1185 } 1186 } 1187 1188 public void dispose() { 1189 cancel_button = null; 1190 ok_button = null; 1191 name_field = null; 1192 } 1193 } 1194 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1195 private class AddOrEditValueActionListener 1196 extends JDialog 1197 implements ActionListener, TreeSelectionListener { 1198 private boolean add_type = true; 1199 private boolean ignore = false; 1200 private GValueNode subject_node = null; 1201 private JButton cancel_button = null; 1202 private JButton ok_button = null; 1203 private JTextArea value = null; 1204 private JTextField alias = null; 1205 private SmarterTree subject_tree = null; 1206 1207 /** Constructor. */ 1208 public AddOrEditValueActionListener() { 1209 super(self); 1210 this.setModal(true); 1211 this.setSize(ADD_OR_EDIT_VALUE_SIZE); 1212 1212 // Create 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1213 JPanel content_pane = (JPanel) getContentPane(); 1214 content_pane.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", false)); 1215 JPanel center_pane = new JPanel(); 1216 center_pane.setOpaque(false); 1217 JPanel inner_pane = new JPanel(); 1218 inner_pane.setOpaque(false); 1219 JPanel subject_tree_pane = new JPanel(); 1220 subject_tree_pane.setOpaque(false); 1221 JLabel subject_tree_label = new JLabel(get("Subject")); 1222 subject_tree_label.setOpaque(false); 1223 subject_tree = new SmarterTree(); 1224 subject_tree.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 1225 subject_tree.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false)); 1226 subject_tree.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false)); 1227 subject_tree.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false)); 1228 JPanel value_pane = new JPanel(); 1229 value_pane.setOpaque(false); 1230 JLabel value_label = new JLabel(get("Value")); 1231 value_label.setOpaque(false); 1232 value = new JTextArea(); 1233 value.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 1234 value.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false)); 1235 value.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false)); 1236 value.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false)); 1237 JPanel alias_pane = new JPanel(); 1238 alias_pane.setOpaque(false); 1239 JLabel alias_label = new JLabel(get("Alias")); 1240 alias_label.setOpaque(false); 1241 alias_label.setPreferredSize(LABEL_SIZE); 1242 alias = new JTextField(); 1243 alias.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 1244 alias.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false)); 1245 alias.setSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false)); 1246 alias.setSelectedTextColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false)); 1247 JPanel button_pane = new JPanel(); 1248 button_pane.setOpaque(false); 1249 ok_button = new JButton(get("General.OK")); 1250 ok_button.setEnabled(false); 1251 cancel_button = new JButton(get("General.Cancel")); 1252 1252 // Connect 1253 1254 1255 1256 1257 1253 TextFieldEnabler ok_button_enabler = new TextFieldEnabler(ok_button); 1254 cancel_button.addActionListener(this); 1255 ok_button.addActionListener(this); 1256 subject_tree.addTreeSelectionListener(this); 1257 ok_button_enabler.add(value); 1258 1258 // Layout 1259 1260 1261 1262 1263 1264 1265 1266 1267 1259 subject_tree_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 1260 subject_tree_pane.setLayout(new BorderLayout()); 1261 subject_tree_pane.add(subject_tree_label, BorderLayout.NORTH); 1262 subject_tree_pane.add(new JScrollPane(subject_tree), BorderLayout.CENTER); 1263 1264 value_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5)); 1265 value_pane.setLayout(new BorderLayout()); 1266 value_pane.add(value_label, BorderLayout.NORTH); 1267 value_pane.add(new JScrollPane(value), BorderLayout.CENTER); 1268 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1269 inner_pane.setLayout(new GridLayout(2,1)); 1270 inner_pane.add(subject_tree_pane); 1271 inner_pane.add(value_pane); 1272 1273 alias_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5)); 1274 alias_pane.setLayout(new BorderLayout()); 1275 alias_pane.add(alias_label, BorderLayout.WEST); 1276 alias_pane.add(alias, BorderLayout.CENTER); 1277 1278 center_pane.setLayout(new BorderLayout()); 1279 center_pane.add(inner_pane, BorderLayout.CENTER); 1280 center_pane.add(alias_pane, BorderLayout.SOUTH); 1281 1282 button_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5)); 1283 button_pane.setLayout(new GridLayout(1,2,5,5)); 1284 button_pane.add(ok_button); 1285 button_pane.add(cancel_button); 1286 1287 content_pane.setLayout(new BorderLayout()); 1288 content_pane.add(center_pane, BorderLayout.CENTER); 1289 content_pane.add(button_pane, BorderLayout.SOUTH); 1290 setLocation((Gatherer.config.screen_size.width - ADD_OR_EDIT_VALUE_SIZE.width) / 2, (Gatherer.config.screen_size.height - ADD_OR_EDIT_VALUE_SIZE.height) / 2); 1291 } 1292 /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to add a new metadata value to the assigned values tree. 1293 * @param event An <strong>ActionEvent</strong> containing information about the event. 1294 */ 1295 public void actionPerformed(ActionEvent event) { 1296 Object source = event.getSource(); 1297 if(source == ok_button) { 1298 // Now we action as necessary 1299 GValueModel model = Gatherer.c_man.msm.getValueTree(current_element); 1300 if(add_type) { 1301 // Add this value to the tree. 1302 GValueNode parent = null; 1303 model.addValue(value.getText(), subject_node, alias.getText()); 1304 parent = null; 1305 } 1306 else { 1307 // Rewrite this value in the DOM model. Note that this will automatically rewrite all assignments as they are live references. 1308 current_value_node.setValue(value.getText()); 1309 current_value_node.setAlias(alias.getText()); 1310 // Now insert the node into the currently selected subject, but only if the parent has changed. 1311 if(subject_node != current_value_node.getParent()) { 1312 GValueNode old_subject_node = (GValueNode) current_value_node.getParent(); 1313 // Find the new values position in subject_nodes children. 1314 GValueNode sibling = null; 1315 int index = 0; 1316 boolean found = false; 1317 while(index < subject_node.getChildCount() && !found) { 1318 sibling = (GValueNode) subject_node.getChildAt(index); 1319 int order = current_value_node.toString().compareToIgnoreCase(sibling.toString()); 1320 // If the sibling is 'greater than' or comes after current value then insert. 1321 if(order < 0) { 1322 // Insert. This will coincidently remove from original parent. 1323 Node parent_node = subject_node.getElement(); 1324 Node child_node = current_value_node.getElement(); 1325 Node sibling_node = sibling.getElement(); 1326 parent_node.insertBefore(child_node, sibling_node); 1327 found = true; 1328 } 1329 // The value already exists exactly as is. In theory this case can never happenm but just incase do nothing more. 1330 else if(order == 0) { 1331 found = true; 1332 } 1333 // The sibling is 'less than' or before the current value, keep looking. 1334 else { 1335 index++; 1336 } 1337 } 1338 // If we haven't done so yet, insert the current node. This will coincidently remove from original parent. 1339 if(!found) { 1340 Node parent_node = subject_node.getElement(); 1341 Node child_node = current_value_node.getElement(); 1342 parent_node.appendChild(child_node); 1343 } 1344 // Inform the tree model what two nodes structures have changed (origin and destination). 1345 subject_node.unmap(); 1346 old_subject_node.unmap(); 1347 model.nodeStructureChanged(old_subject_node); 1348 model.nodeStructureChanged(subject_node); 1349 } 1350 // And if a data change was made tell the tree its data model is ka-bluey. 1351 else { 1352 model.nodeChanged(current_value_node); 1353 } 1354 } 1355 model = null; 1356 // Hide dialog 1357 hide(); 1358 } 1359 else if(source == cancel_button) { 1360 // Hide dialog 1361 hide(); 1362 } 1363 else { 1364 // Reset dialog 1365 // current_value_node 1366 GValueModel model = Gatherer.c_man.msm.getValueTree(current_element); 1367 subject_tree.setModel(model); 1368 // Task specific 1369 if(source == add_value) { 1370 add_type = true; 1371 if(current_value_node != null) { 1372 subject_node = current_value_node; 1373 } 1374 else { 1375 subject_node = (GValueNode) model.getRoot(); 1376 } 1377 value.setText(""); 1378 alias.setText(""); 1379 setTitle(get("AddValue")); 1380 } 1381 else { 1382 add_type = false; 1383 if(current_value_node != null) { 1384 subject_node = (GValueNode) current_value_node.getParent(); 1385 } 1386 else { 1387 subject_node = (GValueNode) model.getRoot(); 1388 } 1389 value.setText(current_value_node.toString()); 1390 alias.setText(current_value_node.getAlias()); 1391 setTitle(get("EditValue")); 1392 } 1393 model = null; 1394 if(subject_node != null) { 1395 TreePath path = new TreePath(subject_node.getPath()); 1396 subject_tree.scrollPathToVisible(path); 1397 subject_tree.setSelectionPath(path); 1398 path = null; 1399 } 1400 // Display 1401 show(); 1402 } 1403 } 1404 1405 public void dispose() { 1406 alias = null; 1407 cancel_button = null; 1408 ok_button = null; 1409 subject_node = null; 1410 subject_tree = null; 1411 value = null; 1412 } 1413 1414 public void valueChanged(TreeSelectionEvent event) { 1415 if(subject_tree.getSelectionCount() > 0 && !ignore) { 1416 ignore = true; 1417 TreePath selected_path = subject_tree.getSelectionPath(); 1418 GValueNode requested_node = (GValueNode) selected_path.getLastPathComponent(); 1419 // Ensure the requested node is not a descendant of the current_value_node 1420 if(current_value_node != null) { 1421 if(!add_type && current_value_node.isNodeDescendant(requested_node)) { 1422 TreePath path = new TreePath(subject_node.getPath()); 1423 subject_tree.scrollPathToVisible(path); 1424 subject_tree.setSelectionPath(path); 1425 } 1426 else { 1427 subject_node = requested_node; 1428 } 1429 } 1430 selected_path = null; 1431 ignore = false; 1432 } 1433 } 1434 } 1435 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 // Clear selection1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1436 private class CloseActionListener 1437 extends JDialog 1438 implements ActionListener { 1439 /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to close the editor dialog. 1440 * @param event An <strong>ActionEvent</strong> containing information about the event. 1441 */ 1442 public void actionPerformed(ActionEvent event) { 1443 self.dispose(); 1444 } 1445 } 1446 1447 private class RemoveAttributeActionListener 1448 implements ActionListener { 1449 /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to 1450 * @param event An <strong>ActionEvent</strong> containing information about the event. 1451 */ 1452 public void actionPerformed(ActionEvent event) { 1453 if(current_attribute != -1) { 1454 int result = JOptionPane.showOptionDialog(self, get("MEM.Confirm_Removal", get("Attribute")), get("Confirm_Removal_Title"), JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, dialog_options, dialog_options[0]); 1455 // Remove this attribute 1456 if(result == 0) { 1457 ignore = true; 1458 // Attributes are tricky little beasties, as they can come from several different places and depend upon what other little beasties have been selected 1459 // Has a collection file been selected... 1460 if(current_collection_file != null) { 1461 // Remove a profile 1462 String source = (String) profile_attributes.getValueAt(current_attribute, 0); 1463 Gatherer.c_man.msm.profiler.removeAction(current_collection_file, source); 1464 source = null; 1465 // Refresh table 1466 ((AttributeTableModel)profile_attributes.getModel()).removeRow(current_attribute); 1467 } 1468 // or has an element been selected 1469 else if(current_element != null) { 1470 // Remove element attribute 1471 String name = (String) element_attributes.getValueAt(current_attribute, 0); 1472 String value = (String) element_attributes.getValueAt(current_attribute, 1); 1473 current_element.removeAttribute(name, value); // Can be multiple authors for instance 1474 // Refresh table 1475 ((AttributeTableModel)element_attributes.getModel()).removeRow(current_attribute); 1476 } 1477 else if(current_set != null) { 1478 String name = (String) set_attributes.getValueAt(current_attribute, 0); 1479 // Remove set attribute 1480 current_set.removeAttribute(name); 1481 // Refresh table 1482 ((AttributeTableModel)set_attributes.getModel()).removeRow(current_attribute); 1483 } 1484 // Disable buttons 1485 edit_attribute.setEnabled(false); 1486 remove_attribute.setEnabled(false); 1487 ignore = false; 1488 } 1489 } 1490 } 1491 } 1492 1493 private class RemoveElementActionListener 1494 implements ActionListener { 1495 /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to provide an editing prompt. 1496 * @param event An <strong>ActionEvent</strong> containing information about the event. 1497 */ 1498 public void actionPerformed(ActionEvent event) { 1499 if(current_element != null) { 1500 int result = JOptionPane.showOptionDialog(self, get("MEM.Confirm_Removal", get("Element")), get("Confirm_Removal_Title"), JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, dialog_options, dialog_options[0]); 1501 // Remove this attribute 1502 if(result == 0) { 1503 ignore = true; 1504 Gatherer.c_man.msm.removeElement(current_element); 1505 // Clear selection 1506 mds_tree.clearSelection(); 1507 model.remove(current_element.toString(), MEMNode.ELEMENT); 1508 // Meanwhile disable/enable controls given we had an element selected, but no longer do. 1509 remove_element.setEnabled(false); 1510 // Show a blank panel. 1511 card_layout.show(details_pane, BLANK); 1512 ignore = false; 1513 } 1514 } 1515 else { 1516 ///ystem.err.println("No current element selected."); 1517 } 1518 } 1519 } 1520 1521 private class RemoveFileActionListener 1522 implements ActionListener { 1523 /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to provide an editing prompt. 1524 * @param event An <strong>ActionEvent</strong> containing information about the event. 1525 */ 1526 public void actionPerformed(ActionEvent event) { 1527 if(current_collection_file != null) { 1528 int result = JOptionPane.showOptionDialog(self, get("MEM.Confirm_Removal", get("File")), get("Confirm_Removal_Title"), JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, dialog_options, dialog_options[0]); 1529 // Remove the current collection file profile from the profiler. 1530 if(result == 0) { 1531 ignore = true; 1532 Gatherer.c_man.msm.profiler.removeProfile(current_collection_file); 1533 // Clear selection 1534 mds_tree.clearSelection(); 1535 model.remove(current_collection_file, MEMNode.COLLECTION); 1536 // Meanwhile disable/enable controls given we had a set selected, but no longer do. 1537 remove_file.setEnabled(false); 1538 // Show a blank panel. 1539 card_layout.show(details_pane, BLANK); 1540 ignore = false; 1541 } 1542 } 1543 } 1544 } 1545 1546 private class RemoveSetActionListener 1547 implements ActionListener { 1548 /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to 1549 * @param event An <strong>ActionEvent</strong> containing information about the event. 1550 */ 1551 public void actionPerformed(ActionEvent event) { 1552 if(current_set != null) { 1553 int result = JOptionPane.showOptionDialog(self, get("MEM.Confirm_Removal", get("Set")), get("Confirm_Removal_Title"), JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, dialog_options, dialog_options[0]); 1554 // Remove the currently selected set 1555 if(result == 0) { 1556 ignore = true; 1557 Gatherer.c_man.msm.removeSet(current_set); 1558 // Clear selection 1559 mds_tree.clearSelection(); 1560 model.remove(current_set.toString(), MEMNode.SET); 1561 // Meanwhile disable/enable controls given we had a set selected, but no longer do. 1562 remove_set.setEnabled(false); 1563 // Show a blank panel. 1564 card_layout.show(details_pane, BLANK); 1565 ignore = false; 1566 } 1567 } 1568 } 1569 } 1570 /** This class will remove the currently selected metadata or profile value when any registered control is actioned. Note that removing a value acts differently from the other removes in that if you remove a value which is still assigned somewhere the values are restored next time said assignment is viewed. This turned out far easier and reasonable to code than attempting to remove all remaining values when you delete its reference in the GValueModel. */ 1571 private class RemoveValueActionListener 1572 implements ActionListener { 1573 /** Any implementation of ActionListener must include this method so that we can be informed when an action as occured on our registered component, allowing us to 1574 * @param event An <strong>ActionEvent</strong> containing information about the event. 1575 */ 1576 public void actionPerformed(ActionEvent event) { 1577 if(current_value_node != null) { 1578 int result = JOptionPane.showOptionDialog(self, get("MEM.Confirm_Removal", get("Value")), get("Confirm_Removal_Title"), JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, dialog_options, dialog_options[0]); 1579 // Remove the current selected value 1580 if(result == 0) { 1581 ignore = true; 1582 GValueModel model = (GValueModel) element_values.getModel(); 1583 model.removeValue(current_value_node); 1584 // Meanwhile disable/enable controls given we had a value selected, but no longer do. 1585 edit_value.setEnabled(false); 1586 remove_value.setEnabled(false); 1587 ignore = false; 1588 } 1589 } 1590 } 1591 } 1592 1592 1593 /** Used to enable a certain target component if and only if all the registered text fields contain non-zero length strings. */ 1594 private class TextFieldEnabler 1595 extends KeyAdapter { 1596 private Component target = null; 1597 private JTextComponent[] fields = null; 1598 public TextFieldEnabler(Component target) { 1599 super(); 1600 this.target = target; 1601 } 1602 public void add(JTextComponent field) { 1603 field.addKeyListener(this); 1604 if(fields == null) { 1605 fields = new JTextComponent[1]; 1606 fields[0] = field; 1593 /** Used to enable a certain target component if and only if all the registered text fields contain non-zero length strings. */ 1594 private class TextFieldEnabler 1595 extends KeyAdapter { 1596 private Component target = null; 1597 private JTextComponent[] fields = null; 1598 public TextFieldEnabler(Component target) { 1599 super(); 1600 this.target = target; 1601 } 1602 public void add(JTextComponent field) { 1603 field.addKeyListener(this); 1604 if(fields == null) { 1605 fields = new JTextComponent[1]; 1606 fields[0] = field; 1607 } 1608 else { 1609 JTextComponent[] temp = new JTextComponent[fields.length + 1]; 1610 System.arraycopy(fields, 0, temp, 0, fields.length); 1611 temp[fields.length] = field; 1612 fields = temp; 1613 } 1614 } 1615 public void keyReleased(KeyEvent event) { 1616 boolean enable = true; 1617 for(int i = 0; i < fields.length; i++) { 1618 enable = enable && fields[i].getText().length() > 0; 1619 } 1620 target.setEnabled(enable); 1621 } 1622 } 1623 1624 private class AttributesListSelectionListener 1625 implements ListSelectionListener { 1626 private JTable table = null; 1627 public AttributesListSelectionListener(JTable table) { 1628 this.table = table; 1629 } 1630 public void valueChanged(ListSelectionEvent event) { 1631 if((current_attribute = table.getSelectedRow()) != -1) { 1632 edit_attribute.setEnabled(true); 1633 remove_attribute.setEnabled(true); 1634 } 1635 else { 1636 current_attribute = -1; 1637 edit_attribute.setEnabled(false); 1638 remove_attribute.setEnabled(false); 1639 } 1640 } 1641 } 1642 1643 private class ElementValuesTreeSelectionListener 1644 implements TreeSelectionListener { 1645 public void valueChanged(TreeSelectionEvent event) { 1646 if(element_values.getSelectionCount() > 0) { 1647 // Retrieve the selected value node. 1648 TreePath selected_path = element_values.getSelectionPath(); 1649 current_value_node = (GValueNode) selected_path.getLastPathComponent(); 1650 edit_value.setEnabled(true); 1651 remove_value.setEnabled(true); 1652 } 1653 else { 1654 current_value_node = null; 1655 edit_value.setEnabled(false); 1656 remove_value.setEnabled(false); 1657 } 1658 } 1659 } 1660 1661 private class MDSTreeSelectionListener 1662 implements TreeSelectionListener { 1663 public void valueChanged(TreeSelectionEvent event) { 1664 if(!ignore) { 1665 // Clear all variables based on previous tree selection 1666 current_collection_file = null; 1667 current_element = null; 1668 current_set = null; 1669 // Now process the node selected 1670 TreePath path = event.getPath(); 1671 current_node = (MEMNode)path.getLastPathComponent(); 1672 // What we show depends on the node type... 1673 AttributeTableModel atm = null; 1674 TreeSet attributes = null; 1675 switch(current_node.getType()) { 1676 case MEMNode.COLLECTION: 1677 current_collection_file = current_node.toString(); 1678 atm = current_node.getModel(); 1679 if(atm == null) { 1680 ArrayList sources = Gatherer.c_man.msm.profiler.getSources(current_collection_file); 1681 attributes = new TreeSet(); 1682 for(int i = 0; i < sources.size(); i++) { 1683 String source = (String) sources.get(i); 1684 String action = Gatherer.c_man.msm.profiler.getAction(current_collection_file, source); 1685 if(action == null) { 1686 action = get("Ignore"); 1687 } 1688 attributes.add(new Attribute(source, action)); 1607 1689 } 1608 else { 1609 JTextComponent[] temp = new JTextComponent[fields.length + 1]; 1610 System.arraycopy(fields, 0, temp, 0, fields.length); 1611 temp[fields.length] = field; 1612 fields = temp; 1690 atm = new AttributeTableModel(attributes, get("Source"), get("Target"), get("Ignore")); 1691 //current_node.setModel(atm); 1692 } 1693 profile_attributes.setModel(atm); 1694 atm.setScrollPane(profile_attributes_scroll); 1695 atm.setTable(profile_attributes); 1696 1697 card_layout.show(details_pane, PROFILE); 1698 setControls(true, false, false, true, false, false, true, false, false, false, false, false); 1699 break; 1700 case MEMNode.ELEMENT: 1701 current_element = current_node.getElement(); 1702 atm = current_node.getModel(); 1703 if(atm == null) { 1704 atm = new AttributeTableModel(current_element.getAttributes(), get("Name"), get("Language_Code"), get("Value"), ""); 1705 //current_node.setModel(atm); 1706 } 1707 element_attributes.setModel(atm); 1708 atm.setScrollPane(element_attributes_scroll); 1709 atm.setTable(element_attributes); 1710 1711 element_values.setModel(Gatherer.c_man.msm.getValueTree(current_element)); 1712 card_layout.show(details_pane, ELEMENT); 1713 // Meanwhile disable/enable controls depending on this Element selection. 1714 setControls(true, false, false, false, false, true, true, false, false, true, false, false); 1715 break; 1716 case MEMNode.SET: 1717 current_set = current_node.getSet(); 1718 atm = current_node.getModel(); 1719 if(atm == null) { 1720 NamedNodeMap temp = current_set.getAttributes(); 1721 attributes = new TreeSet(); 1722 for(int i = 0; i < temp.getLength(); i++) { 1723 Attr attribute = (Attr) temp.item(i); 1724 // We don't show the namespace attribute, as it is used as a unique primary key and should never be changed or removed in itself. 1725 if(!attribute.getName().equals("namespace")) { 1726 attributes.add(new Attribute(attribute.getName(), attribute.getValue())); 1727 } 1728 attribute = null; 1613 1729 } 1614 } 1615 public void keyReleased(KeyEvent event) { 1616 boolean enable = true; 1617 for(int i = 0; i < fields.length; i++) { 1618 enable = enable && fields[i].getText().length() > 0; 1619 } 1620 target.setEnabled(enable); 1621 } 1622 } 1623 1624 private class AttributesListSelectionListener 1625 implements ListSelectionListener { 1626 private JTable table = null; 1627 public AttributesListSelectionListener(JTable table) { 1628 this.table = table; 1629 } 1630 public void valueChanged(ListSelectionEvent event) { 1631 if((current_attribute = table.getSelectedRow()) != -1) { 1632 edit_attribute.setEnabled(true); 1633 remove_attribute.setEnabled(true); 1634 } 1635 else { 1636 current_attribute = -1; 1637 edit_attribute.setEnabled(false); 1638 remove_attribute.setEnabled(false); 1639 } 1640 } 1641 } 1642 1643 private class ElementValuesTreeSelectionListener 1644 implements TreeSelectionListener { 1645 public void valueChanged(TreeSelectionEvent event) { 1646 if(element_values.getSelectionCount() > 0) { 1647 // Retrieve the selected value node. 1648 TreePath selected_path = element_values.getSelectionPath(); 1649 current_value_node = (GValueNode) selected_path.getLastPathComponent(); 1650 edit_value.setEnabled(true); 1651 remove_value.setEnabled(true); 1652 } 1653 else { 1654 current_value_node = null; 1655 edit_value.setEnabled(false); 1656 remove_value.setEnabled(false); 1657 } 1658 } 1659 } 1660 1661 private class MDSTreeSelectionListener 1662 implements TreeSelectionListener { 1663 public void valueChanged(TreeSelectionEvent event) { 1664 if(!ignore) { 1665 // Clear all variables based on previous tree selection 1666 current_collection_file = null; 1667 current_element = null; 1668 current_set = null; 1669 // Now process the node selected 1670 TreePath path = event.getPath(); 1671 current_node = (MEMNode)path.getLastPathComponent(); 1672 // What we show depends on the node type... 1673 AttributeTableModel atm = null; 1674 TreeSet attributes = null; 1675 switch(current_node.getType()) { 1676 case MEMNode.COLLECTION: 1677 current_collection_file = current_node.toString(); 1678 atm = current_node.getModel(); 1679 if(atm == null) { 1680 ArrayList sources = Gatherer.c_man.msm.profiler.getSources(current_collection_file); 1681 attributes = new TreeSet(); 1682 for(int i = 0; i < sources.size(); i++) { 1683 String source = (String) sources.get(i); 1684 String action = Gatherer.c_man.msm.profiler.getAction(current_collection_file, source); 1685 if(action == null) { 1686 action = get("Ignore"); 1687 } 1688 attributes.add(new Attribute(source, action)); 1689 } 1690 atm = new AttributeTableModel(attributes, get("Source"), get("Target"), get("Ignore")); 1691 //current_node.setModel(atm); 1692 } 1693 profile_attributes.setModel(atm); 1694 atm.setScrollPane(profile_attributes_scroll); 1695 atm.setTable(profile_attributes); 1696 1697 card_layout.show(details_pane, PROFILE); 1698 setControls(true, false, false, true, false, false, true, false, false, false, false, false); 1699 break; 1700 case MEMNode.ELEMENT: 1701 current_element = current_node.getElement(); 1702 atm = current_node.getModel(); 1703 if(atm == null) { 1704 atm = new AttributeTableModel(current_element.getAttributes(), get("Name"), get("Language_Code"), get("Value"), ""); 1705 //current_node.setModel(atm); 1706 } 1707 element_attributes.setModel(atm); 1708 atm.setScrollPane(element_attributes_scroll); 1709 atm.setTable(element_attributes); 1710 1711 element_values.setModel(Gatherer.c_man.msm.getValueTree(current_element)); 1712 card_layout.show(details_pane, ELEMENT); 1713 // Meanwhile disable/enable controls depending on this Element selection. 1714 setControls(true, false, false, false, false, true, true, false, false, true, false, false); 1715 break; 1716 case MEMNode.SET: 1717 current_set = current_node.getSet(); 1718 atm = current_node.getModel(); 1719 if(atm == null) { 1720 NamedNodeMap temp = current_set.getAttributes(); 1721 attributes = new TreeSet(); 1722 for(int i = 0; i < temp.getLength(); i++) { 1723 Attr attribute = (Attr) temp.item(i); 1724 // We don't show the namespace attribute, as it is used as a unique primary key and should never be changed or removed in itself. 1725 if(!attribute.getName().equals("namespace")) { 1726 attributes.add(new Attribute(attribute.getName(), attribute.getValue())); 1727 } 1728 attribute = null; 1729 } 1730 atm = new AttributeTableModel(attributes, get("Name"), get("Value"), ""); 1731 //current_node.setModel(atm); 1732 temp = null; 1733 } 1734 set_attributes.setModel(atm); 1735 atm.setScrollPane(set_attributes_scroll); 1736 atm.setTable(set_attributes); 1730 atm = new AttributeTableModel(attributes, get("Name"), get("Value"), ""); 1731 //current_node.setModel(atm); 1732 temp = null; 1733 } 1734 set_attributes.setModel(atm); 1735 atm.setScrollPane(set_attributes_scroll); 1736 atm.setTable(set_attributes); 1737 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1738 card_layout.show(details_pane, SET); 1739 attributes = null; 1740 // Meanwhile disable/enable controls depending on this Element selection. 1741 setControls(true, true, false, false, true, false, true, false, false, false, false, false); 1742 break; 1743 case MEMNode.PROFILER: 1744 // Meanwhile disable/enable controls depending on this Element selection. 1745 setControls(true, false, true, false, false, false, false, false, false, false, false, false); 1746 default: 1747 // Show a blank panel. 1748 card_layout.show(details_pane, BLANK); 1749 } 1750 attributes = null; 1751 path = null; 1752 atm = null; 1753 } 1754 } 1755 } 1756 1757 private class MEMTreeCellRenderer 1758 extends DefaultTreeCellRenderer { 1759 public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { 1760 super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus); 1761 setToolTipText(value.toString()); 1762 return this; 1763 } 1764 } 1765 1766 /** Wrap a ElementWrapper so we get its name when we ask for toString(), not its identifier. */ 1767 private class NameElementWrapperEntry 1768 implements Comparable { 1769 private ElementWrapper element_wrapper = null; 1770 public NameElementWrapperEntry(Object object) { 1771 this.element_wrapper = (ElementWrapper) object; 1772 } 1773 public int compareTo(Object object) { 1774 return element_wrapper.compareTo(object); 1775 } 1776 public boolean equals(Object object) { 1777 return element_wrapper.equals(object); 1778 } 1779 public String toString() { 1780 return element_wrapper.getName(); 1781 } 1782 } 1783 1783 } 1784 -
trunk/gli/src/org/greenstone/gatherer/msm/Declarations.java
r4293 r4365 55 55 */ 56 56 final public class Declarations { 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 57 /** An element of the action type enumeration. */ 58 static public final int NO_ACTION = 0; 59 /** An element of the action type enumeration. */ 60 static public final int ADD = 1; 61 /** An element of the action type enumeration. */ 62 static public final int CANCEL = 2; 63 /** An element of the action type enumeration. This action merges two unrelated elements, as requested by the user. */ 64 static public final int FORCE_MERGE = 3; 65 /** An element of the action type enumeration. */ 66 static public final int MERGE = 4; 67 /** An element of the action type enumeration. */ 68 static public final int RENAME = 5; 69 /** An element of the action type enumeration. */ 70 static public final int REPLACE = 6; 71 /** An element of the action type enumeration. */ 72 static public final int SKIP = 7; 73 /** An element of the action type enumeration. */ 74 static public final int HFILE = 8; 75 /** An element of the action type enumeration. This special value is used internally to indicate a certain element has been removed from the metadata sets. */ 76 static public final int DELETE = 9; 77 77 } -
trunk/gli/src/org/greenstone/gatherer/msm/ElementWrapper.java
r4314 r4365 60 60 */ 61 61 public class ElementWrapper 62 63 64 65 66 67 68 62 implements Comparable { 63 /** The DOM element this wrapper is wrapped around. */ 64 private Element element = null; 65 /** A string prefix identifying the metadata set namespace. */ 66 private String namespace = null; 67 /** Constructor for elements with no namespace necessary. 68 * @param element The DOM <strong>Element</strong> this is to be based on. 69 69 */ 70 71 72 73 70 public ElementWrapper(Element element) { 71 this.element = element; 72 } 73 /** Constructor. 74 74 * @param element The DOM <strong>Element</strong> this is to be based on. 75 75 * @param namespace_required <i>true</i> if this element wrapper should always attempt to show a namespace prefix (retrieving it from the DOM if necessary). 76 76 * @deprecated 77 77 */ 78 79 80 81 82 83 84 85 86 87 88 89 90 78 public ElementWrapper(Element element, boolean namespace_required) { 79 this(element); 80 } 81 82 public void addAttribute(String name, String value) { 83 MSMUtils.addElementAttribute(element, name, "en", value); 84 } 85 86 public void addAttribute(String name, String language, String value) { 87 MSMUtils.addElementAttribute(element, name, language, value); 88 } 89 90 /** Create a copy of this element wrapper. 91 91 * @return A new <strong>ElementWrapper</strong> based on the same element as this one. 92 92 */ 93 94 95 96 97 93 public ElementWrapper copy() { 94 Element element_copy = (Element) element.cloneNode(true); 95 return new ElementWrapper(element_copy); 96 } 97 /** Compare two element wrappers for ordering. 98 98 * @param object An <strong>Object</strong> which is most likely another element wrapper to be compared with. 99 99 * @return An <i>int</i> indicating order, -1, 0, 1 if this element wrapper is less than, equal to or greater than the given object. 100 100 */ 101 102 103 104 105 * @see org.greenstone.gatherer.msm.MSMUtils 106 */ 107 108 109 110 101 public int compareTo(Object object) { 102 return toString().compareTo(object.toString()); 103 } 104 /** Decrement the number of occurances of this metadata element. 105 * @see org.greenstone.gatherer.msm.MSMUtils 106 */ 107 public void dec() { 108 MSMUtils.setOccurance(element, -1); 109 } 110 /** Test if two ElementWrappers are equal. 111 111 * @param object The <strong>Object</strong> to test against. 112 112 */ 113 114 115 116 117 118 119 120 121 113 public boolean equals(Object object) { 114 if(object instanceof ElementWrapper) { 115 String our_full_name = MSMUtils.getFullName(element); 116 String their_full_name = MSMUtils.getFullName(((ElementWrapper)object).getElement()); 117 return our_full_name.equals(their_full_name); 118 } 119 return toString().equals(object.toString()); 120 } 121 /** Retrieve the attributes associated with the element this element wrapper is built around. 122 122 * @return A <strong>TreeSet</strong> of the attributes. 123 123 * @see org.greenstone.gatherer.msm.MSMUtils 124 124 */ 125 126 127 128 125 public TreeSet getAttributes() { 126 return MSMUtils.getAttributes(element); 127 } 128 /** Retrieve the element this is wrapped around. 129 129 * @return A DOM <strong>Element</strong> which represents a metadata element. 130 130 */ 131 132 133 134 131 public Element getElement() { 132 return element; 133 } 134 /** Retrieve the identity of this element (not necessary the same as this elements name). Identity is language and locale dependant. 135 135 * @return The identity as a <strong>String</strong>. 136 136 * @see org.greenstone.gatherer.msm.MSMUtils 137 137 */ 138 139 140 141 138 public String getIdentity() { 139 return MSMUtils.getIdentifier(element); 140 } 141 /** Retrieve the name of this element. Name is unique. 142 142 * @return The name as a <strong>String</strong>. 143 143 * @see org.greenstone.gatherer.msm.MSMUtils 144 144 */ 145 146 147 148 145 public String getName() { 146 return MSMUtils.getFullName(element); 147 } 148 /** Retrieve the namespace prefix for this element wrapper. 149 149 * @return A <strong>String</strong> containing the namespace or "" if there is no namespace for this element. 150 150 * @see org.greenstone.gatherer.msm.MSMUtils 151 151 */ 152 153 154 155 156 157 158 159 160 161 162 152 public String getNamespace() { 153 String name = getName(); 154 int pos; 155 if((pos = name.indexOf(MSMUtils.NS_SEP)) != -1) { 156 return name.substring(0, pos); 157 } 158 else { 159 return ""; 160 } 161 } 162 /** Look for the occurances 'field' of the element and return it if found. 163 163 * @return An <i>int</i> which matches the number in the occurances attribute of the element, or 0 if no such attribute. 164 164 * @see org.greenstone.gatherer.msm.MSMUtils 165 165 */ 166 167 168 169 166 public int getOccurances() { 167 return MSMUtils.getOccurances(element); 168 } 169 /** This method is essentially the same as getDescription() in that it does indeed return this metaelements description. However this method uses the Utility function formatHTMLWidth() to ensure the String can be displayed in a tool-tip window using html markup. 170 170 * @return A String containing the HTML formatted versions of definition and content (comment). 171 171 * @see org.greenstone.gatherer.msm.MSMUtils 172 172 * @see org.greenstone.gatherer.util.Utility 173 173 */ 174 175 176 177 178 179 * @see org.greenstone.gatherer.msm.MSMUtils 180 */ 181 182 183 184 185 186 187 188 189 190 191 192 174 public String getToolTip() { 175 // Add HTML formatting 176 return Utility.formatHTMLWidth(MSMUtils.getDescription(element), 60); 177 } 178 /** Increment the number of occurances of this metadata element. 179 * @see org.greenstone.gatherer.msm.MSMUtils 180 */ 181 public void inc() { 182 MSMUtils.setOccurance(element, 1); 183 } 184 185 public boolean isHierarchy() { 186 return element.getAttribute("hierarchy").equalsIgnoreCase("true"); 187 } 188 189 public void removeAttribute(String name, String value) { 190 Element attributes[] = MSMUtils.getAttributeNodesNamed(element, name); 191 // For each of the attribute nodes which match the requested name... 192 for(int i = 0; attributes != null && i < attributes.length; i++) { 193 193 // Retrieve the value for this node... 194 194 String current = MSMUtils.getValue(attributes[i]); 195 195 // And if it matches our removal value, remove the attribute node. 196 197 198 199 200 201 202 203 204 205 206 207 196 if(current.equals(value)) { 197 element.removeChild(attributes[i]); 198 current = null; 199 attributes = null; 200 return; 201 } 202 current = null; 203 } 204 // If we get this far then theres no match. Boo-hoo. 205 attributes = null; 206 } 207 /** Set the value of the namespace required flag. 208 208 * @param namespace_required The new value as a <i>boolean</i>. 209 209 * @see org.greenstone.gatherer.msm.MSMUtils 210 210 * @deprecated 211 211 */ 212 213 214 215 216 212 public void setNamespaceRequired(boolean namespace_required) { 213 } 214 public String toString() { 215 return MSMUtils.getFullIdentifier(element); 216 } 217 217 } 218 219 -
trunk/gli/src/org/greenstone/gatherer/msm/ExistingMetadataLoader.java
r4293 r4365 48 48 */ 49 49 public class ExistingMetadataLoader { 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 50 /** A list of currently loaded metadata parsers. */ 51 private ArrayList parsers = null; 52 /** Constructor. 53 * @see org.greenstone.gatherer.msm.MetadataParser 54 * @see org.greenstone.gatherer.util.Utility 55 */ 56 public ExistingMetadataLoader() { 57 this.parsers = new ArrayList(); 58 ArrayList load_these = new ArrayList(); 59 // Find any metadata parsers found in parsers directory 60 File file = new File(Utility.BASE_DIR + "classes" + File.separator + "org" + File.separator + "greenstone" + File.separator + "gatherer" + File.separator + "msm" + File.separator + "parsers"); 61 File children[] = file.listFiles(); 62 for(int i = 0; children != null && i < children.length; i++) { 63 String name = children[i].getName(); 64 if(name.endsWith(".class") && name.indexOf("$") == -1) { 65 name = name.substring(0, name.length() - 6); 66 load_these.add("org.greenstone.gatherer.msm.parsers." + name); 67 Gatherer.println("Loaded metadata parser: " + name); 68 } 69 name = null; 70 } 71 children = null; 72 file = null; 73 // Find any metadata parsers found in the jar file (if present) 74 File jar_file = new File("Gatherer.jar"); 75 if(jar_file.exists()) { 76 try { 77 JarFile jar = new JarFile(jar_file); 78 for(Enumeration entries = jar.entries(); entries.hasMoreElements(); ) { 79 String name = entries.nextElement().toString(); 80 if(name.startsWith("org/greenstone/gatherer/msm/parsers/") && name.endsWith(".class") && name.indexOf("$") == -1) { 81 name = name.substring(0, name.length() - 6); 82 name = name.replace('/', '.'); 83 if(!load_these.contains(name)) { 84 load_these.add(name); 85 Gatherer.println("Loaded metadata parser: " + name); 86 } 87 } 88 name = null; 89 } 90 jar = null; 91 } 92 catch (Exception error) { 93 error.printStackTrace(); 94 } 95 } 96 jar_file = null; 97 // Load create instances of the parsers found. 98 for(int i = 0; i < load_these.size(); i++) { 99 try { 100 String parser_name = (String) load_these.get(i); 101 Class custom_class = Class.forName((String)load_these.get(i)); 102 MetadataParser custom_parser = (MetadataParser)custom_class.newInstance(); 103 parsers.add(custom_parser); 104 custom_parser = null; 105 custom_class = null; 106 parser_name = null; 107 } 108 catch (Exception error) { 109 error.printStackTrace(); 110 } 111 } 112 } 113 /** Locates and assigns metadata for the given file by sequentially applying all active metadata parsers. */ 114 public boolean searchForMetadata(FileNode destination, FileNode source, boolean folder_level, boolean dummy_run) { 115 boolean dialog_cancelled = false; 116 int size = parsers.size(); 117 for(int i = 0; !dialog_cancelled && i < size; i++) { 118 MetadataParser parser = (MetadataParser) parsers.get(i); 119 dialog_cancelled = parser.process(destination, source, folder_level, dummy_run); 120 parser = null; 121 } 122 return dialog_cancelled; 123 } 124 124 } 125 126 -
trunk/gli/src/org/greenstone/gatherer/msm/ExportMDSPrompt.java
r4293 r4365 50 50 */ 51 51 final public class ExportMDSPrompt 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 /** The default size for this dialog window. */81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 52 extends JDialog 53 implements ActionListener, KeyListener { 54 /** Is this an export prompt or an import one? */ 55 private boolean export; 56 /** The file we wish to export the metadata set to. */ 57 private File file = null; 58 /** The action the user has chosen from the dialog (either -1, if cancelled, or EXPORT). */ 59 private int action = -1; 60 /** The button used to browse the local file system. */ 61 private JButton browse_button = null; 62 /** Used to cancel the dialog. */ 63 private JButton cancel_button = null; 64 /** Used to initiate the export, then dispose of the dialog. */ 65 private JButton export_button = null; 66 /** The metadata sets available for export. */ 67 private JComboBox sets = null; 68 /** The destination file name as a string. */ 69 private JTextField file_name = null; 70 /** Ths button that, if selected, signifies you wish to export the metadata set with all values. */ 71 private JToggleButton all_values = null; 72 /** The button that, if selected, signifies you wish to export the metadata set without any values (ie no mdv files). */ 73 private JToggleButton no_values = null; 74 /** The button that, if selected, signifies you wish to export the metadata set with only those values that are subject nodes in the hierarchy. */ 75 private JToggleButton structure_only = null; 76 /** A reference to the metadata set manager. */ 77 private MetadataSetManager manager; 78 /** The default size for this dialog window. */ 79 final static public Dimension EXPORT_SIZE = new Dimension(500,220); 80 /** The default size for this dialog window. */ 81 final static public Dimension IMPORT_SIZE = new Dimension(500,120); 82 /** The default export action (there are several depending on how much information you wish to export). */ 83 final static public int EXPORT = 0; 84 /** Constructor. 85 * @param manager A reference to the <strong>MetadataSetManager</strong>. 86 * @see org.greenstone.gatherer.Configuration 87 * @see org.greenstone.gatherer.util.Utility 88 */ 89 public ExportMDSPrompt(MetadataSetManager manager, boolean export) { 90 super(); 91 this.export = export; 92 // Creation 93 setModal(true); 94 if(export) { 95 setSize(EXPORT_SIZE); 96 setTitle(get("Export_Title")); 97 } 98 else { 99 setSize(IMPORT_SIZE); 100 setTitle(get("Import_Title")); 101 } 102 JPanel content_pane = (JPanel) getContentPane(); 103 JPanel control_pane = new JPanel(); 104 JLabel set_label = new JLabel(get("Export_Set")); 105 sets = new JComboBox(manager.getSets()); 106 JLabel condition_label = new JLabel(get("Export_Conditions")); 107 JPanel condition_pane = new JPanel(); 108 ButtonGroup condition_group = new ButtonGroup(); 109 all_values = new JToggleButton(get("Export_All_Values")); 110 condition_group.add(all_values); 111 no_values = new JToggleButton(get("Export_No_Values")); 112 condition_group.add(no_values); 113 structure_only = new JToggleButton(get("Export_Subjects_Only")); 114 condition_group.add(structure_only); 115 all_values.setSelected(true); 116 JLabel file_label = new JLabel(get("Export_File")); 117 JPanel file_pane = new JPanel(); 118 file_name = new JTextField(Utility.METADATA_DIR); 119 browse_button = new JButton(get("General.Browse")); 120 JPanel button_pane = new JPanel(); 121 if(export) { 122 export_button = new JButton(get("File_Export")); 123 export_button.setEnabled(false); 124 } 125 else { 126 export_button = new JButton(get("File_Import")); 127 export_button.setEnabled(true); 128 } 129 cancel_button = new JButton(get("General.Cancel")); 130 // Listeners 131 browse_button.addActionListener(this); 132 cancel_button.addActionListener(this); 133 export_button.addActionListener(this); 134 file_name.addKeyListener(this); 135 // Layout 136 condition_pane.setLayout(new GridLayout(1,3)); 137 condition_pane.add(all_values); 138 condition_pane.add(structure_only); 139 condition_pane.add(no_values); 140 141 file_pane.setLayout(new BorderLayout()); 142 file_pane.add(file_name, BorderLayout.CENTER); 143 file_pane.add(browse_button, BorderLayout.EAST); 144 145 if(export) { 146 control_pane.setLayout(new GridLayout(6, 1)); 147 control_pane.add(set_label); 148 control_pane.add(sets); 149 control_pane.add(condition_label); 150 control_pane.add(condition_pane); 151 control_pane.add(file_label); 152 control_pane.add(file_pane); 153 } 154 else { 155 control_pane.setLayout(new GridLayout(2,1)); 156 control_pane.add(condition_label); 157 control_pane.add(condition_pane); 158 } 159 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 160 button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0)); 161 button_pane.setLayout(new GridLayout(1,2)); 162 button_pane.add(export_button); 163 button_pane.add(cancel_button); 164 165 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 166 content_pane.setLayout(new BorderLayout()); 167 content_pane.add(control_pane, BorderLayout.CENTER); 168 content_pane.add(button_pane, BorderLayout.SOUTH); 169 // Display 170 Dimension screen_size = Gatherer.config.screen_size; 171 setLocation((screen_size.width - getSize().width) / 2, (screen_size.height - getSize().height) / 2); 172 } 173 /** Whenever one of the buttons in the dialog is actioned this method is called to trigger the appropriate effects. 174 174 * @param event An <strong>ActionEvent</strong> containing information about the action performed. 175 175 * @see org.greenstone.gatherer.Gatherer … … 177 177 * @see org.greenstone.gatherer.util.Utility 178 178 */ 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 179 public void actionPerformed(ActionEvent event) { 180 if(event.getSource() == export_button) { 181 action = EXPORT; 182 dispose(); 183 } 184 else if(event.getSource() == cancel_button) { 185 action = -1; 186 dispose(); 187 } 188 else { 189 JFileChooser chooser = new JFileChooser(new File(Utility.METADATA_DIR)); 190 javax.swing.filechooser.FileFilter filter = new MDSFileFilter(); 191 chooser.setApproveButtonText(get("General.OK")); 192 chooser.setFileFilter(filter); 193 int returnVal = chooser.showSaveDialog(Gatherer.g_man); 194 if(returnVal == JFileChooser.APPROVE_OPTION) { 195 file = chooser.getSelectedFile(); 196 if(file.getName().indexOf(".") == -1) { 197 file = new File(file.getName() + ".mds"); 198 } 199 file_name.setText(file.getAbsolutePath()); 200 export_button.setEnabled(true); 201 } 202 } 203 } 204 /** Show the prompt and get the user input. 205 205 * @return An <i>int</i> specifying what action the user has choosen. 206 206 */ 207 208 209 210 211 207 public int display() { 208 show(); 209 return action; 210 } 211 /** Get the current value of condition. 212 212 * @return The value as an <i>int</i>. 213 213 * @see org.greenstone.gatherer.msm.MetadataSet 214 214 */ 215 216 217 218 219 220 221 222 223 224 225 226 215 public int getSelectedCondition() { 216 if(all_values.isSelected()) { 217 return MetadataSet.ALL_VALUES; 218 } 219 else if(no_values.isSelected()) { 220 return MetadataSet.NO_VALUES; 221 } 222 else { 223 return MetadataSet.SUBJECTS_ONLY; 224 } 225 } 226 /** Get the current value of file. 227 227 * @return The value as a <strong>File</strong>. 228 228 */ 229 230 231 232 229 public File getSelectedFile() { 230 return file; 231 } 232 /** Get the current value of set. 233 233 * @return The value as a <strong>MetadataSet</strong>. 234 234 */ 235 236 237 238 235 public MetadataSet getSelectedSet() { 236 return (MetadataSet) sets.getSelectedItem(); 237 } 238 /** Any implementation of KeyListener must include this method so that we can be informed when a key has been pressed. In this case we ignore it. 239 239 * @param event A <strong>KeyEvent</strong> containing information about the key pressed. 240 240 */ 241 242 243 241 public void keyPressed(KeyEvent event) { 242 } 243 /** Any implementation of KeyListener must include this method so that we can be informed once a key has been released. This is the earliest the VK code becomes stable and usable, so we will check if the file named in file_name can be written to and if so enable the export button. 244 244 * @param event A <strong>KeyEvent</strong> containing information about the key typed. 245 245 */ 246 247 248 249 250 251 252 253 254 255 256 257 258 246 public void keyReleased(KeyEvent event) { 247 String pos_file = file_name.getText(); 248 if(pos_file.indexOf(".") != -1) { 249 file = new File(file_name.getText()); 250 if(file.canWrite() || !file.exists()) { 251 export_button.setEnabled(true); 252 } 253 else if(export) { 254 export_button.setEnabled(false); 255 } 256 } 257 } 258 /** Any implementation of KeyListener must include this method so that we can be informed when a key has been typed. In this case we ignore it. 259 259 * @param event A <strong>KeyEvent</strong> containing information about the key typed. 260 260 */ 261 262 263 264 265 266 267 268 269 270 271 272 261 public void keyTyped(KeyEvent event) { 262 } 263 264 private String get(String key) { 265 return get(key, null); 266 } 267 private String get(String key, String[] args) { 268 if(key.indexOf(".") == -1) { 269 key = "MSMPrompt." + key; 270 } 271 return Gatherer.dictionary.get(key, args); 272 } 273 273 } -
trunk/gli/src/org/greenstone/gatherer/msm/GDMDocument.java
r4358 r4365 41 41 */ 42 42 public class GDMDocument { 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 43 /** Record if the document this object is based on is up to date. */ 44 private boolean up_to_date = true; 45 /** The document this class sources its data from. */ 46 private Document base_document; 47 static final private String ACCUMULATE = "accumulate"; 48 /** The pattern to match when searching for directory level assignments. */ 49 static final private String DIRECTORY_FILENAME = ".*"; 50 static final private String DESCRIPTION_ELEMENT = "Description"; 51 static final private String FILENAME_ELEMENT = "FileName"; 52 static final private String FILESET_ELEMENT = "FileSet"; 53 static final private String HVALUE_ATTRIBUTE = "hvalue"; 54 static final private String METADATA_ELEMENT = "Metadata"; 55 static final private String MODE_ATTRIBUTE = "mode"; 56 static final private String NAME_ATTRIBUTE = "name"; 57 static final private String OVERWRITE = "overwrite"; 58 /** Constructor which creates a brand new metadata.xml document. */ 59 public GDMDocument() { 60 // Create new document. We do this by loading a copy of the template. */ 61 this.base_document = Utility.parse(Utility.GREENSTONEDIRECTORYMETADATA_TEMPLATE, true); 62 } 63 /** Constructor which parses an existing metadata.xml document. */ 64 public GDMDocument(File file) { 65 try { 66 this.base_document = Utility.parse(file.getAbsolutePath(), false); 67 } 68 catch (Exception error) { 69 69 // Poorly formed, or completely invalid metadata.xml file! 70 71 72 73 74 75 76 77 78 79 70 } 71 } 72 /** Constructor which wraps around an existing metadata.xml document. */ 73 public GDMDocument(Document base_document) { 74 this.base_document = base_document; 75 } 76 /** Add this metadata to the named file. There is one tricky thing to consider. Whenever a metadata entry is added it is taken to be accumulating except if it is the first added, in which case it overwrites! */ 77 public void addMetadata(String filename, Metadata metadata) { 78 System.err.println("Add '" + metadata + "' to " + (filename != null ? filename : "directory.")); 79 try { 80 80 // Retrieve the document element. 81 81 Element directorymetadata_element = base_document.getDocumentElement(); 82 82 // Iterate through the filesets looking for one that matches the given filename. 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 83 Element fileset_element = null; 84 NodeList fileset_elements = directorymetadata_element.getElementsByTagName(FILESET_ELEMENT); 85 for(int i = 0; i < fileset_elements.getLength(); i++) { 86 fileset_element = (Element) fileset_elements.item(i); 87 NodeList filename_elements = fileset_element.getElementsByTagName(FILENAME_ELEMENT); 88 boolean found = false; 89 for(int j = 0; !found && j < filename_elements.getLength(); j++) { 90 Element filename_element = (Element) filename_elements.item(j); 91 String filename_pattern = MSMUtils.getValue(filename_element); 92 // Have we found a match. If so break out of for loop. 93 if(filename != null && filename.matches(filename_pattern) && !filename_pattern.equals(DIRECTORY_FILENAME)) { 94 System.err.println("Adding to existing file fileset!"); 95 found = true; 96 } 97 else if(filename == null && filename_pattern.equals(DIRECTORY_FILENAME)) { 98 System.err.println("Adding to existing folder fileset!"); 99 ///ystem.err.println("filename_pattern = '" + filename_pattern + "'"); 100 found = true; 101 } 102 // No match. On to the next one. 103 else { 104 fileset_element = null; 105 } 106 filename_pattern = null; 107 filename_element = null; 108 } 109 } 110 fileset_elements = null; 111 111 // If we still haven't found an existing fileset, then its time to create one. 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 112 if(fileset_element == null) { 113 System.err.println("Creating a new fileset."); 114 fileset_element = base_document.createElement(FILESET_ELEMENT); 115 Element filename_element = base_document.createElement(FILENAME_ELEMENT); 116 Element description_element = base_document.createElement(DESCRIPTION_ELEMENT); 117 fileset_element.appendChild(filename_element); 118 fileset_element.appendChild(description_element); 119 Text filename_text = null; 120 // If the filename is null then we add a directory metadata set as directorymetadata_element's first child 121 if(filename == null) { 122 filename_text = base_document.createTextNode(DIRECTORY_FILENAME); 123 if(directorymetadata_element.hasChildNodes()) { 124 directorymetadata_element.insertBefore(fileset_element, directorymetadata_element.getFirstChild()); 125 } 126 else { 127 directorymetadata_element.appendChild(fileset_element); 128 } 129 } 130 // Otherwise we just append the new fileset to directorymetadata_element's children. 131 else { 132 filename_text = base_document.createTextNode(filename); 133 directorymetadata_element.appendChild(fileset_element); 134 } 135 filename_element.appendChild(filename_text); 136 filename_text = null; 137 description_element = null; 138 filename_element = null; 139 } 140 140 // Now, finally, we can add the metadata. 141 142 141 Element metadata_element = base_document.createElement(METADATA_ELEMENT); 142 metadata_element.setAttribute(NAME_ATTRIBUTE, metadata.getElement().getName()); 143 143 144 144 // To determine if this metadata entry should overwrite or accumulate we check if there are other entries with the same element in this fileset. 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 145 boolean will_accumulate = false; 146 NodeList sibling_description_elements = fileset_element.getElementsByTagName(DESCRIPTION_ELEMENT); 147 for(int k = 0; !will_accumulate && k < sibling_description_elements.getLength(); k++) { 148 Element sibling_description_element = (Element) sibling_description_elements.item(k); 149 NodeList sibling_metadata_elements = sibling_description_element.getElementsByTagName(METADATA_ELEMENT); 150 for(int l = 0; !will_accumulate && l < sibling_metadata_elements.getLength(); l++) { 151 Element sibling_metadata_element = (Element) sibling_metadata_elements.item(l); 152 will_accumulate = sibling_metadata_element.getAttribute(NAME_ATTRIBUTE).equals(metadata_element.getAttribute(NAME_ATTRIBUTE)); 153 sibling_metadata_element = null; 154 } 155 sibling_metadata_elements = null; 156 sibling_description_element = null; 157 } 158 sibling_description_elements = null; 159 if(will_accumulate) { //mode.equals(ACCUMULATE)) { 160 metadata_element.setAttribute(MODE_ATTRIBUTE, ACCUMULATE); 161 } 162 162 // As we can't possibly store all the metadata in memory, nor can we ensure that the indexes written to file remain the same until the new time we look at this file, and to avoid having to open a rewrite every collection document whenever any value tree changes, I'm adding a new attribute called hvalue which indicates the hierarchy value path as a '\' separated string. 163 164 165 166 167 163 GValueModel model = Gatherer.c_man.getCollection().msm.getValueTree(metadata.getElement()); 164 if(model != null && model.isHierarchy()) { 165 metadata_element.setAttribute(HVALUE_ATTRIBUTE, metadata.getValueNode().getFullPath()); 166 } 167 metadata_element.appendChild(base_document.createTextNode(metadata.getAbsoluteValue())); 168 168 // Retrieve the first description element for this fileset (there should only be one, but I'll play it safe). 169 NodeList description_elements = fileset_element.getElementsByTagName("Description"); 170 Element description_element = (Element) description_elements.item(0); 171 description_element.appendChild(metadata_element); 172 description_element = null; 169 NodeList description_elements = fileset_element.getElementsByTagName("Description"); 170 Element description_element = (Element) description_elements.item(0); 171 description_element.appendChild(metadata_element); 172 description_element = null; 173 metadata_element = null; 174 //mode = null; 175 fileset_element = null; 176 directorymetadata_element = null; 177 up_to_date = false; 178 } 179 catch (Exception error) { 180 Gatherer.printStackTrace(error); 181 } 182 } 183 184 /** Retrieve the document this class is wrapping. */ 185 public Document getDocument() { 186 return base_document; 187 } 188 /** Get all of the metadata, including directory level, associated with this file. */ 189 public ArrayList getMetadata(String filename, boolean remove, ArrayList metadatum_so_far, File file) { 190 return getMetadata(filename, remove, metadatum_so_far, file, false); 191 } 192 /** Retrieve the metadata associated with the given filename. Keep track of what metadata should be overwritten and what should be accumulated. Also make note of the source file, and remove the metadata if required. Finally if purge is set retrieve every piece of metadata in this file. */ 193 public ArrayList getMetadata(String filename, boolean remove, ArrayList metadatum_so_far, File file, boolean purge) { 194 ///ystem.err.println("Get metadata for " + filename); 195 ArrayList metadatum = null; 196 if(metadatum_so_far == null) { 197 metadatum = new ArrayList(); 198 } 199 else { 200 metadatum = metadatum_so_far; 201 } 202 try { 203 // Retrieve the document element. 204 Element directorymetadata_element = base_document.getDocumentElement(); 205 // Iterate through the filesets, checking the FileName child element against the target file's name using regular expression matching. 206 NodeList fileset_elements = directorymetadata_element.getElementsByTagName(FILESET_ELEMENT); 207 for(int i = 0; i < fileset_elements.getLength(); i++) { 208 Element fileset_element = (Element) fileset_elements.item(i); 209 NodeList filename_elements = fileset_element.getElementsByTagName(FILENAME_ELEMENT); 210 for(int j = 0; j < filename_elements.getLength(); j++) { 211 Element filename_element = (Element) filename_elements.item(j); 212 String filename_text = MSMUtils.getValue(filename_element); 213 if((filename != null && filename.matches(filename_text)) || filename_text.equals(DIRECTORY_FILENAME) || purge) { 214 // If they match add all of the metadata found in the Description child element, remembering to abide by desired mode (accumulate vs. overwrite). 215 NodeList description_elements = fileset_element.getElementsByTagName(DESCRIPTION_ELEMENT); 216 for(int k = 0; k < description_elements.getLength(); k++) { 217 Element description_element = (Element) description_elements.item(k); 218 NodeList metadata_elements = description_element.getElementsByTagName(METADATA_ELEMENT); 219 for(int l = 0; l < metadata_elements.getLength(); l++) { 220 Element metadata_element = (Element) metadata_elements.item(l); 221 String raw_element = metadata_element.getAttribute(NAME_ATTRIBUTE); 222 //String language = metadata_element.getAttribute("language"); 223 String mode = metadata_element.getAttribute(MODE_ATTRIBUTE); 224 String raw_value = metadata_element.getAttribute(HVALUE_ATTRIBUTE); 225 if(raw_value == null || raw_value.length() == 0) { 226 raw_value = MSMUtils.getValue(metadata_element); 227 } 228 // Using the element string and value, retrieve a matching Metadata object from the cache 229 Metadata metadata = null; 230 // If this element has hierarchy values then we must ensure the raw value is a full path, not an index. 231 if(GDMManager.metadata_cache.contains(raw_element, raw_value)) { 232 ///ystem.err.println("HIT! Retrieve metadata from cache: " + raw_element + " -> " + raw_value + "\n"); 233 metadata = (Metadata) GDMManager.metadata_cache.get(raw_element, raw_value); 234 } 235 else { 236 ElementWrapper element = Gatherer.c_man.getCollection().msm.getElement(raw_element); 237 GValueNode value = Metadata.getDefaultValueNode(element, raw_value); 238 ///ystem.err.println("Miss. Create new metadata: " + raw_element + " -> " + raw_value + "\n"); 239 metadata = new Metadata(element, value); 240 GDMManager.metadata_cache.put(raw_element, raw_value, metadata); 241 ///ystem.err.println("Added metadata to cache: " + raw_element + " -> " + raw_value + "\n"); 242 value = null; 243 element = null; 244 } 245 // We determine whether this metadata is file or folder level 246 if(filename != null) { 247 ///ystem.err.println("Filename = " + filename); 248 ///ystem.err.println("filename_text = " + filename_text); 249 // If can only be file level if there is no folder path details in filename and if the filename matched the filename text node (it may have matched .* instead)! 250 if(filename.indexOf(File.separator) == -1 && filename.equals(filename_text)) { 251 metadata.setFileLevel(true); 252 ///ystem.err.println("File level!!!"); 253 } 254 else { 255 metadata.setFileLevel(false); 256 ///ystem.err.println("Inherited!!!"); 257 } 258 } 259 else { 260 ///ystem.err.println("Filename is null therefore this is file level metadata."); 261 metadata.setFileLevel(true); 262 } 263 metadata.setFile(file); 264 265 // If mode is overwrite, then remove any previous values for this metadata element. 266 if(mode.equals("accumulate")) { 267 metadata.setAccumulate(true); 268 } 269 else { 270 metadata.setAccumulate(false); 271 ///ystem.err.println("Metadata overwrites: " + metadata); 272 for(int m = metadatum.size() - 1; m >= 0; m--) { 273 Metadata old_metadata = (Metadata) metadatum.get(m); 274 if(old_metadata.getElement().equals(metadata.getElement())) { 275 metadatum.remove(m); 276 ///ystem.err.println("Removing overridden metadata: " + old_metadata); 277 } 278 old_metadata = null; 279 } 280 } 281 mode = null; 282 283 // Add the completed metadata and clean up 284 ///ystem.err.println("Adding metadata: " + metadata); 285 metadatum.add(metadata); 286 287 // Having found our metadata check if the value from the xml matches the one from the gvaluenode. If not update it. This happens whenever hierarchy information is involved (indexes rapidly become obsolete). 288 // If remove was set, remove it. We can only remove pure file level metadata, or folder level iff we were asked for folder level. 289 if(remove && ((filename != null && filename.matches(filename_text) && !filename_text.equals(DIRECTORY_FILENAME)) || (filename == null && filename_text.equals(DIRECTORY_FILENAME)))) { 290 ///ystem.err.println("Removing " + metadata + " from " + file); 291 description_element.removeChild(metadata_element); 292 // Remove the description element if empty. 293 if(!description_element.hasChildNodes()) { 294 fileset_element.removeChild(description_element); 295 } 296 } 297 else { 298 String current_value = metadata.getAbsoluteValue(); 299 if(!raw_value.equals(current_value)) { 300 // Remove old text 301 while(metadata_element.hasChildNodes()) { 302 metadata_element.removeChild(metadata_element.getFirstChild()); 303 } 304 // Add new. 305 metadata_element.appendChild(base_document.createTextNode(current_value)); 306 } 307 } 308 309 metadata = null; 310 raw_value = null; 311 raw_element = null; 173 312 metadata_element = null; 174 //mode = null; 175 fileset_element = null; 176 directorymetadata_element = null; 177 up_to_date = false; 178 } 179 catch (Exception error) { 180 Gatherer.printStackTrace(error); 181 } 182 } 183 184 /** Retrieve the document this class is wrapping. */ 185 public Document getDocument() { 186 return base_document; 187 } 188 /** Get all of the metadata, including directory level, associated with this file. */ 189 public ArrayList getMetadata(String filename, boolean remove, ArrayList metadatum_so_far, File file) { 190 return getMetadata(filename, remove, metadatum_so_far, file, false); 191 } 192 /** Retrieve the metadata associated with the given filename. Keep track of what metadata should be overwritten and what should be accumulated. Also make note of the source file, and remove the metadata if required. Finally if purge is set retrieve every piece of metadata in this file. */ 193 public ArrayList getMetadata(String filename, boolean remove, ArrayList metadatum_so_far, File file, boolean purge) { 194 ///ystem.err.println("Get metadata for " + filename); 195 ArrayList metadatum = null; 196 if(metadatum_so_far == null) { 197 metadatum = new ArrayList(); 198 } 199 else { 200 metadatum = metadatum_so_far; 201 } 202 try { 313 } 314 metadata_elements = null; 315 description_element = null; 316 } 317 description_elements = null; 318 } 319 filename_text = null; 320 filename_element = null; 321 } 322 // If the file set no longer has any description entries, remove it entirely 323 NodeList description_elements = fileset_element.getElementsByTagName(DESCRIPTION_ELEMENT); 324 if(description_elements.getLength() == 0) { 325 directorymetadata_element.removeChild(fileset_element); 326 } 327 description_elements = null; 328 filename_elements = null; 329 fileset_element = null; 330 } 331 fileset_elements = null; 332 directorymetadata_element = null; 333 } 334 catch (Exception error) { 335 Gatherer.self.printStackTrace(error); 336 } 337 ///ystem.err.println("Found " + metadatum.size() + " pieces of metadata."); 338 return metadatum; 339 } 340 341 /** Determine if this document has been saved recently, and thus xml file version is up to date. */ 342 public boolean isUpToDate() { 343 return false; 344 } 345 346 /** Determine is this is a valid Greenstone Directory Metadata file. It may of course just be some xml file with the name metadata.xml. */ 347 public boolean isValid() { 348 // Just determine if the doctype is GreenstoneDirectoryMetadata and root node is called DirectoryMetadata. 349 String doctype_name = base_document.getDoctype().getName(); 350 String root_name = base_document.getDocumentElement().getTagName(); 351 return ((doctype_name.equals("GreenstoneDirectoryMetadata") && root_name.equals("GreenstoneDirectoryMetadata")) || (doctype_name.equals("DirectoryMetadata") && root_name.equals("DirectoryMetadata"))); 352 } 353 /** Remove the given directory level metadata from this document. All directory level metadata is available under the FileSet with filename '.*'. There is at least one nasty case to consider, where the first overwriting metadata entry, of several with the same element, is removed. In this case the next entry must become overwrite to ensure proper inheritance. */ 354 public void removeMetadata(String filename, Metadata metadata) { 355 try { 356 boolean found = false; 357 boolean first_metadata_element_found = true; 358 boolean make_next_metadata_element_overwrite = false; 203 359 // Retrieve the document element. 204 Element directorymetadata_element = base_document.getDocumentElement(); 205 // Iterate through the filesets, checking the FileName child element against the target file's name using regular expression matching. 206 NodeList fileset_elements = directorymetadata_element.getElementsByTagName(FILESET_ELEMENT); 207 for(int i = 0; i < fileset_elements.getLength(); i++) { 208 Element fileset_element = (Element) fileset_elements.item(i); 209 NodeList filename_elements = fileset_element.getElementsByTagName(FILENAME_ELEMENT); 210 for(int j = 0; j < filename_elements.getLength(); j++) { 211 Element filename_element = (Element) filename_elements.item(j); 212 String filename_text = MSMUtils.getValue(filename_element); 213 if((filename != null && filename.matches(filename_text)) || filename_text.equals(DIRECTORY_FILENAME) || purge) { 214 // If they match add all of the metadata found in the Description child element, remembering to abide by desired mode (accumulate vs. overwrite). 215 NodeList description_elements = fileset_element.getElementsByTagName(DESCRIPTION_ELEMENT); 216 for(int k = 0; k < description_elements.getLength(); k++) { 217 Element description_element = (Element) description_elements.item(k); 218 NodeList metadata_elements = description_element.getElementsByTagName(METADATA_ELEMENT); 219 for(int l = 0; l < metadata_elements.getLength(); l++) { 220 Element metadata_element = (Element) metadata_elements.item(l); 221 String raw_element = metadata_element.getAttribute(NAME_ATTRIBUTE); 222 //String language = metadata_element.getAttribute("language"); 223 String mode = metadata_element.getAttribute(MODE_ATTRIBUTE); 224 String raw_value = metadata_element.getAttribute(HVALUE_ATTRIBUTE); 225 if(raw_value == null || raw_value.length() == 0) { 226 raw_value = MSMUtils.getValue(metadata_element); 227 } 228 // Using the element string and value, retrieve a matching Metadata object from the cache 229 Metadata metadata = null; 230 // If this element has hierarchy values then we must ensure the raw value is a full path, not an index. 231 if(GDMManager.metadata_cache.contains(raw_element, raw_value)) { 232 ///ystem.err.println("HIT! Retrieve metadata from cache: " + raw_element + " -> " + raw_value + "\n"); 233 metadata = (Metadata) GDMManager.metadata_cache.get(raw_element, raw_value); 234 } 235 else { 236 ElementWrapper element = Gatherer.c_man.getCollection().msm.getElement(raw_element); 237 GValueNode value = Metadata.getDefaultValueNode(element, raw_value); 238 ///ystem.err.println("Miss. Create new metadata: " + raw_element + " -> " + raw_value + "\n"); 239 metadata = new Metadata(element, value); 240 GDMManager.metadata_cache.put(raw_element, raw_value, metadata); 241 ///ystem.err.println("Added metadata to cache: " + raw_element + " -> " + raw_value + "\n"); 242 value = null; 243 element = null; 244 } 245 // We determine whether this metadata is file or folder level 246 if(filename != null) { 247 ///ystem.err.println("Filename = " + filename); 248 ///ystem.err.println("filename_text = " + filename_text); 249 // If can only be file level if there is no folder path details in filename and if the filename matched the filename text node (it may have matched .* instead)! 250 if(filename.indexOf(File.separator) == -1 && filename.equals(filename_text)) { 251 metadata.setFileLevel(true); 252 ///ystem.err.println("File level!!!"); 253 } 254 else { 255 metadata.setFileLevel(false); 256 ///ystem.err.println("Inherited!!!"); 257 } 258 } 259 else { 260 ///ystem.err.println("Filename is null therefore this is file level metadata."); 261 metadata.setFileLevel(true); 262 } 263 metadata.setFile(file); 264 265 // If mode is overwrite, then remove any previous values for this metadata element. 266 if(mode.equals("accumulate")) { 267 metadata.setAccumulate(true); 268 } 269 else { 270 metadata.setAccumulate(false); 271 ///ystem.err.println("Metadata overwrites: " + metadata); 272 for(int m = metadatum.size() - 1; m >= 0; m--) { 273 Metadata old_metadata = (Metadata) metadatum.get(m); 274 if(old_metadata.getElement().equals(metadata.getElement())) { 275 metadatum.remove(m); 276 ///ystem.err.println("Removing overridden metadata: " + old_metadata); 277 } 278 old_metadata = null; 279 } 280 } 281 mode = null; 282 283 // Add the completed metadata and clean up 284 ///ystem.err.println("Adding metadata: " + metadata); 285 metadatum.add(metadata); 286 287 // Having found our metadata check if the value from the xml matches the one from the gvaluenode. If not update it. This happens whenever hierarchy information is involved (indexes rapidly become obsolete). 288 // If remove was set, remove it. We can only remove pure file level metadata, or folder level iff we were asked for folder level. 289 if(remove && ((filename != null && filename.matches(filename_text) && !filename_text.equals(DIRECTORY_FILENAME)) || (filename == null && filename_text.equals(DIRECTORY_FILENAME)))) { 290 ///ystem.err.println("Removing " + metadata + " from " + file); 291 description_element.removeChild(metadata_element); 292 // Remove the description element if empty. 293 if(!description_element.hasChildNodes()) { 294 fileset_element.removeChild(description_element); 295 } 296 } 297 else { 298 String current_value = metadata.getAbsoluteValue(); 299 if(!raw_value.equals(current_value)) { 300 // Remove old text 301 while(metadata_element.hasChildNodes()) { 302 metadata_element.removeChild(metadata_element.getFirstChild()); 303 } 304 // Add new. 305 metadata_element.appendChild(base_document.createTextNode(current_value)); 306 } 307 } 308 309 metadata = null; 310 raw_value = null; 311 raw_element = null; 312 metadata_element = null; 313 } 314 metadata_elements = null; 315 description_element = null; 316 } 317 description_elements = null; 318 } 319 filename_text = null; 320 filename_element = null; 321 } 322 // If the file set no longer has any description entries, remove it entirely 323 NodeList description_elements = fileset_element.getElementsByTagName(DESCRIPTION_ELEMENT); 324 if(description_elements.getLength() == 0) { 325 directorymetadata_element.removeChild(fileset_element); 326 } 327 description_elements = null; 328 filename_elements = null; 329 fileset_element = null; 330 } 331 fileset_elements = null; 332 directorymetadata_element = null; 333 } 334 catch (Exception error) { 335 Gatherer.self.printStackTrace(error); 336 } 337 ///ystem.err.println("Found " + metadatum.size() + " pieces of metadata."); 338 return metadatum; 339 } 340 341 /** Determine if this document has been saved recently, and thus xml file version is up to date. */ 342 public boolean isUpToDate() { 343 return false; 344 } 345 346 /** Determine is this is a valid Greenstone Directory Metadata file. It may of course just be some xml file with the name metadata.xml. */ 347 public boolean isValid() { 348 // Just determine if the doctype is GreenstoneDirectoryMetadata and root node is called DirectoryMetadata. 349 String doctype_name = base_document.getDoctype().getName(); 350 String root_name = base_document.getDocumentElement().getTagName(); 351 return ((doctype_name.equals("GreenstoneDirectoryMetadata") && root_name.equals("GreenstoneDirectoryMetadata")) || (doctype_name.equals("DirectoryMetadata") && root_name.equals("DirectoryMetadata"))); 352 } 353 /** Remove the given directory level metadata from this document. All directory level metadata is available under the FileSet with filename '.*'. There is at least one nasty case to consider, where the first overwriting metadata entry, of several with the same element, is removed. In this case the next entry must become overwrite to ensure proper inheritance. */ 354 public void removeMetadata(String filename, Metadata metadata) { 355 try { 356 boolean found = false; 357 boolean first_metadata_element_found = true; 358 boolean make_next_metadata_element_overwrite = false; 359 // Retrieve the document element. 360 Element directorymetadata_element = base_document.getDocumentElement(); 360 Element directorymetadata_element = base_document.getDocumentElement(); 361 361 // Iterate through the filesets looking for the directory level one. 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 362 NodeList fileset_elements = directorymetadata_element.getElementsByTagName(FILESET_ELEMENT); 363 for(int i = 0; i < fileset_elements.getLength(); i++) { 364 Element fileset_element = (Element) fileset_elements.item(i); 365 NodeList filename_elements = fileset_element.getElementsByTagName(FILENAME_ELEMENT); 366 for(int j = 0; j < filename_elements.getLength(); j++) { 367 Element filename_element = (Element) filename_elements.item(j); 368 String filename_text = MSMUtils.getValue(filename_element); 369 if((filename != null && filename.matches(filename_text)) || filename_text.equals(DIRECTORY_FILENAME)) { 370 // Retrieve the Metadata Element for this fileset, and iterate through them looking for the one which we are to remove. 371 NodeList description_elements = fileset_element.getElementsByTagName("Description"); 372 for(int k = 0; k < description_elements.getLength(); k++) { 373 Element description_element = (Element) description_elements.item(k); 374 NodeList metadata_elements = description_element.getElementsByTagName("Metadata"); 375 for(int l = 0; !found && !make_next_metadata_element_overwrite && l < metadata_elements.getLength(); l++) { 376 Element metadata_element = (Element) metadata_elements.item(l); 377 String element = metadata_element.getAttribute("name"); 378 String value = MSMUtils.getValue(metadata_element); 379 // See if this is the metadata we wish to remove 380 if(element.equals(metadata.getElement().getName())) { 381 if(value.equals(metadata.getAbsoluteValue())) { 382 // Remove it 383 System.err.println("Remove " + element + "-" + value); 384 description_element.removeChild(metadata_element); 385 found = true; 386 // If this was the first metadata with this element found, and it was set to overwrite, then we have to ensure that the next metadata with this element found (if any) is changed to be overwrite now. 387 if(first_metadata_element_found && !metadata.accumulates()) { 388 make_next_metadata_element_overwrite = true; 389 } 390 } 391 // If this was the first metadata we've found with the element of the one to be removed set first found to false. 392 else if(first_metadata_element_found) { 393 first_metadata_element_found = false; 394 } 395 // Otherwise we should make this metadata overwrite as requested. 396 else if(make_next_metadata_element_overwrite) { 397 metadata_element.setAttribute(MODE_ATTRIBUTE, ""); 398 } 399 } 400 value = null; 401 element = null; 402 metadata_element = null; 403 } 404 metadata_elements = null; 405 description_element = null; 406 } 407 description_elements = null; 408 } 409 filename_text = null; 410 filename_element = null; 411 } 412 filename_elements = null; 413 fileset_element = null; 414 } 415 fileset_elements = null; 416 directorymetadata_element = null; 417 up_to_date = false; 418 } 419 catch (Exception error) { 420 Gatherer.printStackTrace(error); 421 } 422 } 423 424 /** Change the up to date flag. */ 425 public void setUpToDate(boolean up_to_date) { 426 this.up_to_date = up_to_date; 427 } 428 429 /** Decode a string that was previously made Perl safe. */ 430 private String decode(String safe) { 431 return safe.replaceAll("\\\\.","."); 432 } 433 434 /** Encodes unsafe filename characters (such as the . before the file extension) into Perl safe ones. */ 435 private String encode(String dangerous) { 436 return dangerous.replaceAll("\\.", "\\\\."); 437 } 438 438 } 439 -
trunk/gli/src/org/greenstone/gatherer/msm/GDMManager.java
r4359 r4365 53 53 import org.w3c.dom.*; 54 54 /** This object manages the metadata attached to file records. By storing all of the metadata in one place you garner several advantages. Firstly only one copy of each metadata object is retained, all actual entries are converted to references. Next you can immediately determine what metadata is assigned to an entire directory, thus the metadata.xml files can be built more effeciently (whereas the current 'optimal' method uses recursion through the tree contents). Finally, and perhaps most importantly, it allows for dynamic 'on demand' lookup of metadata. This is especially necessary with large collections, where the raw, unconnected metadata files could range into the tens of megabytes of memory and require hundreds of megabytes to read by in using serialization. Dynamic loading allows you to connect the metadata objects on load, reducing value node paths (possibly of hundreds of characters) down to a single reference pointer! At the very worst this object uses far less memory than the current method, and given that the current method is completely incapable of handling large collections, is necessary. The trade off of course is in time needed to load metadata.xml on demand, the worst possible case being the user selecting the root node of the collection tree of a large collection immediately after opening the collection. The subsequent attempt to build the metadata table will result in the metadata being loaded for every single file. But since this process is sequential and a small cache of metadata.xml files is implemented, and given that the table will actually be build on a separate thread, the wait should not be too arduous.<BR> 55 As for the size of the GDMParser cache, I was at first tempted to put around five. However after analysis of cache usage, I determined that no gain occured because of caching (in fact if everythings working as it should there should only ever be one call for a certain metadata.xml).55 As for the size of the GDMParser cache, I was at first tempted to put around five. However after analysis of cache usage, I determined that no gain occured because of caching (in fact if everythings working as it should there should only ever be one call for a certain metadata.xml). 56 56 */ 57 57 public class GDMManager 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 58 extends HashMap 59 implements MSMListener { 60 /** A list of the known metadata instances, thus we only store one of each unique metadata, and reference the rest. */ 61 static public HashMap3D metadata_cache = null; 62 /** The root file node. */ 63 private TreeNode root = null; 64 /** The threaded object responsible for loading all of the existing metadata.xml files prior to any save action. This is necessary so that hierarchy indexes within the metadata.xml files stay fresh. */ 65 private GDMLoader gdm_loader = null; 66 static final private String METADATA_XML = "metadata.xml"; 67 /** Constructor. */ 68 public GDMManager() { 69 super(); 70 this.metadata_cache = new HashMap3D(Gatherer.c_man.getCollection().msm.getSize()); 71 // Connect 72 Gatherer.c_man.getCollection().msm.addMSMListener(this); 73 // Now create and start the synchronous GDM loader. 74 gdm_loader = new GDMLoader(); 75 gdm_loader.start(); 76 ///atherer.println("New GDMManager created."); 77 } 78 79 /** This may seem a little odd but this method doesn't add the given metadata directly, instead calling fireMetadataChanged in MetadataSetManager so as to recursively add metadata if necessary, and to ensure that all listeners who are interested in data change (such as the Metadata Table and Save listeners) can be up to date. */ 80 public void addMetadata(FileNode node, ArrayList metadatum) { 81 for(int i = 0; i < metadatum.size(); i++) { 82 Metadata metadata = (Metadata) metadatum.get(i); 83 Gatherer.c_man.getCollection().msm.fireMetadataChanged(node, (Metadata)null, metadata); 84 } 85 } 86 /** Destructor necessary for clean exit, subsequent to saving of metadata.xml files. 87 87 * @see org.greenstone.gatherer.Gatherer 88 88 * @see org.greenstone.gatherer.collection.CollectionManager … … 90 90 * @see org.greenstone.gatherer.msm.MetadataSetManager 91 91 */ 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 92 public void destroy() { 93 // Destroy all the cached documents. 94 /* 95 Iterator iterator = keySet().iterator(); 96 while(iterator.hasNext()) { 97 File file = (File) iterator.next(); 98 GDMDocument document = (GDMDocument) get(file); 99 document.destroy(file); 100 } 101 */ 102 // Deregister as listener 103 Gatherer.c_man.getCollection().msm.removeMSMListener(this); 104 // Deallocate all data members 105 metadata_cache.clear(); 106 metadata_cache = null; 107 // Finally clear self 108 clear(); 109 // Done! 110 } 111 /** Method that is called whenever an element within a set is changed or modified. Ensure all cached GDMDocuments are marked as stale. 112 112 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired. 113 113 */ 114 115 116 117 118 119 120 121 114 public void elementChanged(MSMEvent event) { 115 for(Iterator values = values().iterator(); values.hasNext(); ) { 116 GDMDocument document = (GDMDocument) values.next(); 117 document.setUpToDate(false); 118 document = null; 119 } 120 } 121 /** Retrieve the GreenstoneMetadataDocument that is associated with a certain file. If the document is in cache returns it. If the document exists but isn't in cache loads, caches, then returns it. Otherwise it creates a brand new document, caches it, then returns it. 122 122 * @see org.greenstone.gatherer.msm.GDMParser 123 123 */ 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 124 public GDMDocument getDocument(File file) { 125 ///ystem.err.println("Get the GDMDocument for " + file.getAbsolutePath()); 126 GDMDocument metadata_xml = null; 127 // Determine the name of the target files metadata.xml file. 128 File metadata_file = null; 129 if(file.isFile()) { 130 metadata_file = new File(file.getParentFile(), METADATA_XML); 131 } 132 else { 133 metadata_file = new File(file, METADATA_XML); 134 } 135 // Then try to retrieve it from cache. First we consider the case of a cache hit. 136 if(containsKey(metadata_file)) { 137 metadata_xml = (GDMDocument) get(metadata_file); 138 } 139 else { 140 140 // Now the two potential cache misses. The first requires us to load an existing metadata.xml 141 142 143 141 if(metadata_file.exists()) { 142 metadata_xml = new GDMDocument(metadata_file); 143 } 144 144 // The final case is where no current metadata.xml exists. Create a new one just by creating a new GDMDocument. 145 146 147 148 145 else { 146 metadata_xml = new GDMDocument(); 147 } 148 put(metadata_file, metadata_xml); 149 149 //gatherer.debug(null, "[0ms]\tCached " + metadata_file); 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 150 } 151 return metadata_xml; 152 } 153 154 /** Recover the metadata associated with a particular file. Note that this call is synchronized, so that all of the data holders don't need to be. */ 155 public synchronized ArrayList getMetadata(File file) { 156 return getMetadata(file, false); 157 } 158 159 public synchronized ArrayList getMetadata(File file, ElementWrapper element) { 160 ArrayList metadata = getMetadata(file, false); 161 ArrayList values = new ArrayList(); 162 for(int i = 0; i < metadata.size(); i++) { 163 Metadata data = (Metadata) metadata.get(i); 164 if(element.equals(data.getElement())) { 165 values.add(data.getValue()); 166 } 167 } 168 if(values.size() > 0) { 169 Collections.sort(values); 170 } 171 return values; 172 } 173 174 private ArrayList getMetadata(File file, boolean remove) { 175 ArrayList metadata = null; 176 String filename = null; 177 if(file.isFile()) { 178 filename = file.getName(); 179 file = file.getParentFile(); 180 } 181 GDMDocument document = getDocument(file); 182 if(document != null) { 183 metadata = document.getMetadata(filename, remove, metadata, file); 184 document = null; 185 } 186 return metadata; 187 } 188 189 public synchronized ArrayList getAllMetadata(File file) { // boolean remove) { 190 ///ystem.err.println("getMetadata(" + file.getAbsolutePath() + ")"); 191 ArrayList metadata = null; 192 // Build up a list of all the metadata xml files we have to check for metadata. 193 ArrayList search_files = new ArrayList(); 194 String filename = null; 195 File start_file = file; 196 if(file.isFile()) { 197 filename = file.getName(); 198 start_file = file.getParentFile(); 199 } 200 File collection_dir = new File(Gatherer.c_man.getCollectionDirectory()); 201 ///ystem.err.println("Collection directory = " + collection_dir.getAbsolutePath()); 202 ///ystem.err.println("Start directory = " + start_file.getAbsolutePath()); 203 while(!start_file.equals(collection_dir)) { 204 204 ///ystem.err.println("Blip!"); 205 206 207 208 209 210 211 212 205 search_files.add(0, new MetadataXMLFileSearch(start_file, filename)); 206 if(filename != null) { 207 filename = start_file.getName() + "/" + filename; 208 } 209 else { 210 filename = start_file.getName() + "/"; 211 } 212 start_file = start_file.getParentFile(); 213 213 ///ystem.err.println("Start directory = " + start_file.getAbsolutePath()); 214 215 216 217 214 } 215 // Now search each of these metadata xml for metadata, remembering to accumulate or overwrite as we go along. 216 for(int i = 0; i < search_files.size(); i++) { 217 MetadataXMLFileSearch a_search = (MetadataXMLFileSearch) search_files.get(i); 218 218 ///ystem.err.println("Search " + a_search.file.getAbsolutePath() + File.separator + "metadata.xml for " + (a_search.filename != null ? a_search.filename : "directory metadata")); 219 219 // Retrieve the document 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 220 GDMDocument document = getDocument(a_search.file); 221 if(document != null) { 222 // There is one piece of slight of hand here. You can never remove metadata during a get all metadata. 223 metadata = document.getMetadata(a_search.filename, false, metadata, a_search.file); 224 ///ystem.err.println("Current metadata: " + toString(metadata)); 225 document = null; 226 } 227 a_search = null; 228 } 229 start_file = null; 230 collection_dir = null; 231 filename = null; 232 search_files.clear(); 233 search_files = null; 234 return metadata; 235 } 236 237 public String toString(ArrayList list) { 238 StringBuffer text = new StringBuffer("("); 239 for(int i = 0; list != null && i < list.size(); i++) { 240 text.append((list.get(i)).toString()); 241 if(i < list.size() - 1) { 242 text.append(", "); 243 } 244 } 245 text.append(")"); 246 return text.toString(); 247 } 248 249 private class MetadataXMLFileSearch { 250 public File file; 251 public String filename; 252 public MetadataXMLFileSearch(File file, String filename) { 253 this.file = file; 254 this.filename = filename; 255 } 256 } 257 258 /** Called whenever the metadata value changes in some way, such as the addition of a new value. This is the only event type we care about, but we care about it a lot. It tells us what metadata to add, remove, etc from the cached metadata.xml files. Note that this method is synchronized so that the data objects don't need to be. 259 259 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired. 260 260 * @see org.greenstone.gatherer.msm.GDMDocument … … 262 262 * @see org.greenstone.gatherer.util.HashMap3D 263 263 */ 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 264 public synchronized void metadataChanged(MSMEvent event) { 265 System.err.println("Recieved Event: " + event.toString()); 266 File file = event.getFile(); 267 if(file == null) { 268 FileNode record = event.getRecord(); 269 file = record.getFile(); 270 } 271 Metadata new_metadata = event.getNewMetadata(); 272 Metadata old_metadata = event.getOldMetadata(); 273 // These metadata objects may be new instances of metadata objects that already exist. Replace them if they are. 274 new_metadata = checkCache(new_metadata); 275 old_metadata = checkCache(old_metadata); 276 // Now apply the change to the document in question. 277 GDMDocument metadata_xml = getDocument(file); 278 if(metadata_xml != null) { 279 if(old_metadata != null) { 280 // File level 281 if(file.isFile()) { 282 metadata_xml.removeMetadata(file.getName(), old_metadata); 283 } 284 // Folder level 285 else { 286 metadata_xml.removeMetadata(null, old_metadata); 287 } 288 } 289 if(new_metadata != null) { 290 // File level 291 if(file.isFile()) { 292 metadata_xml.addMetadata(file.getName(), new_metadata); 293 } 294 else { 295 metadata_xml.addMetadata(null, new_metadata); 296 } 297 } 298 } 299 } 300 301 public ArrayList removeMetadata(File file) { 302 return getMetadata(file, true); 303 } 304 305 /** Causes all currently loaded GDMDocuments to write themselves out. 306 306 * @see org.greenstone.gatherer.msm.GDMDocument 307 307 */ 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 308 public void save() { 309 Iterator iterator = keySet().iterator(); 310 while(iterator.hasNext()) { 311 File file = (File) iterator.next(); 312 GDMDocument document = (GDMDocument) get(file); 313 if(!document.isUpToDate()) { 314 // First purge any old references. 315 document.getMetadata(null, false, null, null, true); 316 // Now write the xml 317 Utility.export(document.getDocument(), file); 318 document.setUpToDate(true); 319 } 320 } 321 } 322 /** Used to cause the document associated with a particular file to write the latest copy of itself to disk. */ 323 public void save(FileNode node) { 324 File file = node.getFile(); 325 if(file != null && file.isFile()) { 326 GDMDocument document = getDocument(file); 327 File xml_file; 328 if(file.isFile()) { 329 xml_file = new File(file.getParentFile(), "metadata.xml"); 330 } 331 else { 332 xml_file = new File(file, "metadata.xml"); 333 } 334 if(document != null && !document.isUpToDate()) { 335 // First purge any old references. 336 document.getMetadata(null, false, null, null, true); 337 // Now write the xml 338 Utility.export(document.getDocument(), xml_file); 339 document.setUpToDate(true); 340 } 341 xml_file = null; 342 document = null; 343 } 344 file = null; 345 } 346 347 /** Method that is called whenever the metadata set collection changes in some way, such as the addition of a new set or the merging of two sets. If a set changes, mark all cached GDMDocuments as being stale. 348 348 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired. 349 349 */ 350 351 352 353 354 355 356 357 350 public void setChanged(MSMEvent event) { 351 for(Iterator values = values().iterator(); values.hasNext(); ) { 352 GDMDocument document = (GDMDocument) values.next(); 353 document.setUpToDate(false); 354 document = null; 355 } 356 } 357 /** Called whenever the value tree of an metadata element changes in some way, such as the addition of a new value. --While the comments below are now obsolete, I'll keep them just to remind me of how easy it is to back yourself into a corner with issues such as caching and persisitant references--. Such an action would require us to painstakingly reload every metadata.xml file using the value model prior to the change, then painstakingly write out each metadata.xml file again using the modified model, but I'm a glutton for punishment so thats ok. The alternative is to not do this and watch in horror as heirarchy references quickly fall into disarray, pointing to the wrong place. This task gets even more complicated by three facts:<br>1. We want to do this is a seperate thread, as we don't want the program to come to a screaming halt while we're updating metadata.xml files.<br>2. We have to prevent any metadata.xml files being removed from cache while we're doing this, as if we encounter these more recently written files their heirarchy references will already be correct and that will balls up our little process. Note that this means the saving process may have to block while pending metadata heirarchy updates are in progress.<br>3. Regarding (2) we don't have to rewrite any metadata.xml files already in cache as they will be correctly written out whenever they happen to be dumped from cache.<br>4. We need the ability to pre-empt the general update to load a user demanded metadata.xml and store it in cache, using the old value tree model as per usual, and<br>5. We have to store a cue of these events, and process them one at a time. Perhaps one day when I'm feeling masacistic I'll figure out someway to merge several updates into one, but for now we have to change the tree one node at a time in order for references to remain correct.<br>Ok, so thats five facts, but you get the gist. Not an easy task, but crucial for accurate storage and recall of metadata heirarchies. 358 358 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired. 359 359 */ 360 361 362 363 364 365 366 367 360 public void valueChanged(MSMEvent event) {} 361 362 public void waitUntilComplete() { 363 gdm_loader.waitUntilComplete(); 364 } 365 366 private Metadata checkCache(Metadata metadata) { 367 if(metadata != null) { 368 368 ///ystem.err.println("Search for " + metadata.toString()); 369 370 371 372 373 374 375 376 377 378 379 380 369 if(metadata_cache.contains(metadata.getElement(), metadata.getValueNode())) { 370 metadata = (Metadata) metadata_cache.get(metadata.getElement(), metadata.getValueNode()); 371 } 372 } 373 return metadata; 374 } 375 376 /** A separately threaded class to load all of the current metadata.xml files. Note that files can still be loaded on demand if they're not already in the cache. Also provides the functionality to block any other thread until the loading is complete, such as is necessary when moving values about in the value tree hierarchy. */ 377 private class GDMLoader 378 extends Thread { 379 private boolean complete = false; 380 public void run() { 381 381 // Can't open a collections metadata when the collection isn't open! 382 383 384 385 386 387 388 382 while(!Gatherer.c_man.ready()) { 383 try { 384 wait(100); 385 } 386 catch(Exception error) { 387 } 388 } 389 389 // Now for each non-file directory in the tree, ask it to load its metadata 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 390 ArrayList remaining = new ArrayList(); 391 remaining.add((FileNode)Gatherer.c_man.getRecordSet().getRoot()); 392 int remaining_size = 0; 393 while((remaining_size = remaining.size()) > 0) { 394 FileNode record = (FileNode) remaining.remove(remaining_size - 1); 395 if(!record.isLeaf()) { 396 ///atherer.println("Retrieving metadata.xml for " + record); 397 getMetadata(record.getFile()); 398 for(int i = 0; i < record.getChildCount(); i++) { 399 remaining.add(record.getChildAt(i)); 400 } 401 } 402 record = null; 403 } 404 remaining = null; 405 complete = true; 406 } 407 public void waitUntilComplete() { 408 try { 409 while(!complete) { 410 sleep(100); // 1 second hopefully. 411 } 412 } 413 catch(Exception error) { 414 Gatherer.printStackTrace(error); 415 } 416 } 417 } 418 418 } 419 420 -
trunk/gli/src/org/greenstone/gatherer/msm/GDMParser.java
r4293 r4365 77 77 // #################################################################################### 78 78 public class GDMParser 79 80 81 82 83 84 85 86 87 88 89 90 91 79 extends LinkedHashMap { 80 /** A list of file names that we know do not actually belong to valid GDM xml files, so there not much point in ever trying to read them again. */ 81 private ArrayList ignore = null; 82 /** The actual xerces parser used to read in xml documents. */ 83 private DOMParser parser = null; 84 /** The default maximum cache size if max size not explicitly set. */ 85 private int max_size = 25; 86 /** Default constructor. */ 87 public GDMParser() { 88 super(); 89 this.ignore = new ArrayList(); 90 try { 91 parser = new DOMParser(); 92 92 // Don't let it import external dtds. If it does it'll probably spit the dummy. If people try to use a poorly formated xml file more fool them. 93 94 93 parser.setFeature("http://xml.org/sax/features/validation", false); 94 parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); 95 95 // May or may not be ignored, the documentation for Xerces is contradictory. If it works then parsing -should- be faster. 96 97 98 96 parser.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", true); 97 } 98 catch(Exception error) { 99 99 ///ystem.err.println("Fatal Error in GDMParser.init(): " + error); 100 101 102 103 104 100 error.printStackTrace(); 101 System.exit(1); 102 } 103 } 104 /** Constructor with maximum size set. 105 105 * @param max_size The maximum size of the cache, as an <i>int</i>. 106 106 */ 107 108 109 110 111 112 113 114 115 116 117 118 107 public GDMParser(int max_size) { 108 this(); 109 this.max_size = max_size; 110 } 111 /** Destructor, clears cache and remove persistant global references. */ 112 public void destroy() { 113 ignore.clear(); 114 ignore = null; 115 parser = null; 116 clear(); 117 } 118 /** Fetches the document for the given xml file. This may mean (re)parsing it or simply fetching it from cache. 119 119 * @param file The metadata.xml <strong>File</strong> you wish to get the document for. 120 120 * @return A <strong>Document</strong> which is sourced from file. 121 121 */ 122 123 124 125 122 public Document parse(File file) { 123 ///ystem.err.println("Parse: " + file.getAbsolutePath()); 124 Document result = null; 125 if(file.exists()) { 126 126 // Check if we've already parsed this file in an earlier attempt. 127 128 129 130 131 132 133 134 135 136 137 127 if(containsKey(file)) { 128 ///ystem.err.println("Already cached previously."); 129 //result = (Document) get(file); 130 SoftReference reference = (SoftReference) get(file); 131 if(reference != null) { 132 result = (Document) reference.get(); 133 } 134 else { 135 ///ystem.err.println("Reference expired."); 136 } 137 } 138 138 // Check the ignore list and see if we've already detected this isn't a greenstone metadata file. 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 139 if(result == null && !ignore.contains(file)) { 140 ///ystem.err.println("Reparse file."); 141 // Of course we may not have, or it may have expired so... 142 try { 143 // Display progress dialog. 144 InputStream is = new FileInputStream(file); 145 InputStreamReader isr = new InputStreamReader(is); 146 Reader r = new BufferedReader(isr); 147 InputSource isc = new InputSource(r); 148 parser.parse(isc); // Slow. 149 Document document = parser.getDocument(); 150 // First test. Check we have a GreenstoneDirectoryMetadata file, or for the older version DirectoryMetadata. 151 if(!document.getDoctype().getName().equals("GreenstoneDirectoryMetadata") && !document.getDoctype().getName().equals("DirectoryMetadata")) { 152 ///ystem.err.println("Adding to ignore list."); 153 // Add to ignore list. Not a gdm file. 154 ignore.add(file); 155 } 156 // Cache document. 157 else { 158 ///ystem.err.println("Adding to cache."); 159 put(file, new SoftReference(document)); 160 result = document; 161 } 162 } 163 catch (Exception error) { 164 ///ystem.err.println("Error! " + error); 165 error.printStackTrace(); 166 } 167 } 168 else { 169 ///ystem.err.println("File on ignore list."); 170 } 171 } 172 else { 173 173 ///ystem.err.println("File does not exist!"); 174 175 176 177 174 } 175 return result; 176 } 177 /** Automatically called by the LinkedHashMap object whenever an object is added, to determine whether it should remove the oldest entry. 178 178 * @param eldest The eldest <strong>Map.Entry</strong> which may mean in terms of age, or in terms of usage. 179 179 * @return <i>true</i> if the given entry should be removed, <i>false</i> otherwise. 180 180 */ 181 182 183 181 protected boolean removeEldestEntry(Map.Entry eldest) { 182 return size() > max_size; 183 } 184 184 } -
trunk/gli/src/org/greenstone/gatherer/msm/GreenstoneArchiveParser.java
r4319 r4365 56 56 public class GreenstoneArchiveParser { 57 57 58 58 private GShell shell; 59 59 60 60 static final String ignore_list[] = {"assocfilepath","gsdl","Identifier","Source","URL"}; 61 61 62 63 64 65 62 public GreenstoneArchiveParser(GShellProgressMonitor progress, GShell shell) { 63 // We can only extract metadata if an extracted metadata set exists in our collection. 64 if(Gatherer.c_man.msm.getSet("") != null) { 65 this.shell = shell; 66 66 // Determine the collection archive directory. 67 67 File archive_directory = new File(Gatherer.c_man.getCollectionArchive()); 68 68 // For each of the hash coded directories within. 69 File document_directories[] = archive_directory.listFiles(); 70 for(int i = 0; i < document_directories.length; i++) { 71 // Find the doc.xml file within 72 if(document_directories[i].isDirectory()) { 73 File document_file = new File(document_directories[i], "doc.xml"); 74 // Then extract the metadata from it. 75 if(document_file.exists()) { 76 extractMetadata(document_file); 77 // Display a pretty progress message. 78 shell.fireMessage(GShell.IMPORT, shell.typeAsString(GShell.IMPORT) + "> " + Gatherer.dictionary.get("GShell.Extracted", document_directories[i].getName()), GShell.OK); 79 progress.increment(); 80 } 81 } 69 File document_directories[] = archive_directory.listFiles(); 70 for(int i = 0; i < document_directories.length; i++) { 71 // Find the doc.xml file within 72 if(document_directories[i].isDirectory()) { 73 File document_file = new File(document_directories[i], "doc.xml"); 74 // Then extract the metadata from it. 75 if(document_file.exists()) { 76 extractMetadata(document_file); 77 // Display a pretty progress message. 78 shell.fireMessage(GShell.IMPORT, shell.typeAsString(GShell.IMPORT) + "> " + Gatherer.dictionary.get("GShell.Extracted", document_directories[i].getName()), GShell.OK); 79 progress.increment(); 80 } 81 } 82 } 83 } 84 // All done. Outta here like a bald man. 85 } 86 87 private void extractMetadata(File file) { 88 // Retrieve the DOM of the file. 89 Document document = Utility.parse(file, false); 90 // If we successfully parsed the document, then it is time to search through the DOM for the Metadata tags. 91 if(document != null) { 92 String file_path = null; 93 Element archive_element = document.getDocumentElement(); 94 // Retrieve all of the Metadata sections. 95 NodeList metadata_elements = archive_element.getElementsByTagName("Metadata"); 96 // Now for each Metadata entry retrieved... 97 for(int i = 0; i < metadata_elements.getLength(); i++) { 98 Element metadata_element = (Element) metadata_elements.item(i); 99 String name = metadata_element.getAttribute("name"); 100 // There is a special case when the metadata name is gsdlsourcefilename, as we use this to find the FileRecord we want to add metadata to. 101 if(name.equals("gsdlsourcefilename")) { 102 file_path = MSMUtils.getValue(metadata_element); 103 } 104 else { 105 // Check if its name starts with, or is equal to, one of the values in our ignore list, and if so ignore this metadata. 106 boolean ignore = (name.indexOf(".") != -1); 107 for(int j = 0; !ignore && j < ignore_list.length; j++) { 108 ignore = name.startsWith(ignore_list[j]); 109 } 110 // Otherwise ensure the metadata is present in our collection. 111 if(!ignore && file_path != null) { 112 // If we successfully retrieved a record we can continue. 113 if(file_path != null) { 114 // We now retrieve the appropriate element. If no such element exists we create a new one in the greenstone mds. Remember that no element in the greenstone mds has an associated value tree, so it is perfect for metadata elements with a small number of repeated values but where the values have no relation between files (such as encoding, where many files will be iso_8859_1, but if you change one you don't intend to change them all). 115 ElementWrapper element = Gatherer.c_man.msm.getElement(name); 116 if(element == null) { 117 MetadataSet extracted_mds = Gatherer.c_man.msm.getSet("ex"); 118 element = extracted_mds.addElement(name); 119 } 120 // If we successfully retrieved an element (and we should have) we can continue. 121 if(element != null) { 122 // Retrieve the metadata for the current file 123 File target_file = new File(file_path); 124 ArrayList metadatum = Gatherer.c_man.getCollection().gdm.getMetadata(target_file); 125 // If no metadata exists for the current element, add it 126 boolean found = false; 127 for(int k = 0; !found && k < metadatum.size(); k++) { 128 Metadata sibling = (Metadata) metadatum.get(k); 129 found = element.equals(sibling.getElement()); 82 130 } 83 } 84 // All done. Outta here like a bald man. 85 } 86 87 private void extractMetadata(File file) { 88 // Retrieve the DOM of the file. 89 Document document = Utility.parse(file, false); 90 // If we successfully parsed the document, then it is time to search through the DOM for the Metadata tags. 91 if(document != null) { 92 String file_path = null; 93 Element archive_element = document.getDocumentElement(); 94 // Retrieve all of the Metadata sections. 95 NodeList metadata_elements = archive_element.getElementsByTagName("Metadata"); 96 // Now for each Metadata entry retrieved... 97 for(int i = 0; i < metadata_elements.getLength(); i++) { 98 Element metadata_element = (Element) metadata_elements.item(i); 99 String name = metadata_element.getAttribute("name"); 100 // There is a special case when the metadata name is gsdlsourcefilename, as we use this to find the FileRecord we want to add metadata to. 101 if(name.equals("gsdlsourcefilename")) { 102 file_path = MSMUtils.getValue(metadata_element); 103 } 104 else { 105 // Check if its name starts with, or is equal to, one of the values in our ignore list, and if so ignore this metadata. 106 boolean ignore = (name.indexOf(".") != -1); 107 for(int j = 0; !ignore && j < ignore_list.length; j++) { 108 ignore = name.startsWith(ignore_list[j]); 109 } 110 // Otherwise ensure the metadata is present in our collection. 111 if(!ignore && file_path != null) { 112 // If we successfully retrieved a record we can continue. 113 if(file_path != null) { 114 // We now retrieve the appropriate element. If no such element exists we create a new one in the greenstone mds. Remember that no element in the greenstone mds has an associated value tree, so it is perfect for metadata elements with a small number of repeated values but where the values have no relation between files (such as encoding, where many files will be iso_8859_1, but if you change one you don't intend to change them all). 115 ElementWrapper element = Gatherer.c_man.msm.getElement(name); 116 if(element == null) { 117 MetadataSet extracted_mds = Gatherer.c_man.msm.getSet("ex"); 118 element = extracted_mds.addElement(name); 119 } 120 // If we successfully retrieved an element (and we should have) we can continue. 121 if(element != null) { 122 // Retrieve the metadata for the current file 123 File target_file = new File(file_path); 124 ArrayList metadatum = Gatherer.c_man.getCollection().gdm.getMetadata(target_file); 125 // If no metadata exists for the current element, add it 126 boolean found = false; 127 for(int k = 0; !found && k < metadatum.size(); k++) { 128 Metadata sibling = (Metadata) metadatum.get(k); 129 found = element.equals(sibling.getElement()); 130 } 131 metadatum = null; 132 if(!found) { 133 String value = ""; 134 try { 135 value = Utility.decodeGreenstone(URLDecoder.decode(MSMUtils.getValue(metadata_element), "UTF-8")); 136 } 137 catch(UnsupportedEncodingException error) { 138 Gatherer.printStackTrace(error); 139 } 140 // If we successfully retrieved a value we can continue. 141 if(value != null) { 142 // Create a new metadata object. 143 GValueModel value_tree = Gatherer.c_man.msm.getValueTree(element); 144 GValueNode value_node = null; 145 if(value_tree != null) { 146 value_node = value_tree.getValue(value); 147 } 148 else { 149 value_node = new GValueNode(element.toString(), value); 150 } 151 Metadata metadata = new Metadata(element, value_node); 152 Gatherer.c_man.getCollection().gdm.metadataChanged(new MSMEvent(this, System.currentTimeMillis(), target_file, null, metadata)); 153 // All done. On to next metadata. 154 } 155 } 156 target_file = null; 157 } 158 else { 159 Gatherer.println("Cannot retrieve metadata element " + name); 160 } 161 } 162 } 163 } 131 metadatum = null; 132 if(!found) { 133 String value = ""; 134 try { 135 value = Utility.decodeGreenstone(URLDecoder.decode(MSMUtils.getValue(metadata_element), "UTF-8")); 136 } 137 catch(UnsupportedEncodingException error) { 138 Gatherer.printStackTrace(error); 139 } 140 // If we successfully retrieved a value we can continue. 141 if(value != null) { 142 // Create a new metadata object. 143 GValueModel value_tree = Gatherer.c_man.msm.getValueTree(element); 144 GValueNode value_node = null; 145 if(value_tree != null) { 146 value_node = value_tree.getValue(value); 147 } 148 else { 149 value_node = new GValueNode(element.toString(), value); 150 } 151 Metadata metadata = new Metadata(element, value_node); 152 Gatherer.c_man.getCollection().gdm.metadataChanged(new MSMEvent(this, System.currentTimeMillis(), target_file, null, metadata)); 153 // All done. On to next metadata. 154 } 164 155 } 165 } 166 } 156 target_file = null; 157 } 158 else { 159 Gatherer.println("Cannot retrieve metadata element " + name); 160 } 161 } 162 } 163 } 164 } 165 } 166 } 167 167 } -
trunk/gli/src/org/greenstone/gatherer/msm/MDSFileFilter.java
r4293 r4365 41 41 /** A custom <strong>FileFilter</strong> for the file choosers. */ 42 42 final public class MDSFileFilter 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 43 extends javax.swing.filechooser.FileFilter { 44 private String description = null; 45 /** Constructor. 46 * @see org.greenstone.gatherer.Dictionary 47 */ 48 public MDSFileFilter() { 49 description = Gatherer.dictionary.get("MSMPrompt.File_Filter_Description"); 50 } 51 /** Override this method to return <i>true</i> only if the file extension is .mds. 52 * @param f A possible mds <strong>File</strong>. 53 * @return <i>true</i> if we should show this file, <i>false</i> otherwise. 54 */ 55 public boolean accept(File f) { 56 String file_name = f.getName(); 57 file_name = file_name.toLowerCase(); 58 if(file_name.endsWith(".mds") || file_name.indexOf(".") == -1) { 59 return true; 60 } 61 return false; 62 } 63 /** Retrieve the description for this filter. 64 * @return The description as a <strong>String</strong>. 65 */ 66 public String getDescription() { 67 return description; 68 } 69 69 } -
trunk/gli/src/org/greenstone/gatherer/msm/MSMAction.java
r4293 r4365 55 55 /** This class provides all the information required to correctly perform metadata actions, with regard to the users previously indicated preferences in terms of importing and renaming. */ 56 56 final public class MSMAction { 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 57 private boolean hfile = false; 58 /** The type of action required in subsequent actions involving the specified set and source element. */ 59 private int action = Declarations.NO_ACTION; 60 private String set = null; 61 private String source = null; 62 /** The fully qualified name of the target element (which must be currently in the loaded metadata sets, otherwise the set it belongs to cannot be determined. */ 63 private String target = null; 64 /** Constructor. Note that the standard action FORCE_MERGE is only named so for clarity and is translated into its actual action RENAME when this profile is created. 65 * @param action An <i>int</i> representing what action needs to be taken in subsequent access of the given set and source element. 66 * @param target A <strong>String</strong> representing the fully qualified name of the target element. 67 * @param hfile If this action was created from the hfile parsing stage, does the element require a replacement to change its alias value into a real one. Note that this profile item may be created for fully namespaced elements as well, to ensure aliases are always remapped. 68 */ 69 public MSMAction(int action, String target) { 70 this.target = target; 71 switch(action) { 72 case Declarations.DELETE: 73 this.action = action; 74 break; 75 case Declarations.FORCE_MERGE: 76 this.action = Declarations.RENAME; 77 break; 78 case Declarations.RENAME: 79 this.action = action; 80 break; 81 case Declarations.SKIP: 82 this.action = action; 83 this.target = null; 84 break; 85 default: 86 this.action = Declarations.NO_ACTION; 87 } 88 } 89 /** Constructor. Note that the standard action FORCE_MERGE is only named so for clarity and is translated into its actual action RENAME when this profile is created. 90 * @param action An <i>int</i> representing what action needs to be taken in subsequent access of the given set and source element. 91 * @param target A <strong>String</strong> representing the fully qualified name of the target element. 92 * @param hfile If this action was created from the hfile parsing stage, does the element require a replacement to change its alias value into a real one. Note that this profile item may be created for fully namespaced elements as well, to ensure aliases are always remapped. 93 */ 94 public MSMAction(int action, String target, boolean hfile) { 95 this.hfile = hfile; 96 this.target = target; 97 switch(action) { 98 case Declarations.FORCE_MERGE: 99 this.action = Declarations.RENAME; 100 break; 101 case Declarations.RENAME: 102 this.action = action; 103 break; 104 case Declarations.SKIP: 105 this.action = action; 106 this.target = null; 107 break; 108 default: 109 this.action = Declarations.NO_ACTION; 110 } 111 } 112 112 113 114 115 116 117 113 public MSMAction(String set, String source, int action, String target) { 114 this(action, target); 115 this.set = set; 116 this.source = source; 117 } 118 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 119 /** Method for retrieving the appropriate action from this class. 120 * @return An <i>int</i> specifying a particular action. 121 */ 122 public int getAction() { 123 return action; 124 } 125 public String getSet() { 126 return set; 127 } 128 public String getSource() { 129 return source; 130 } 131 /** Method for retrieving the target element of this action. 132 * @return A <strong>String</strong> stating the fully qualified name of the target element, within the namespace of one of the currently loaded metadata sets. 133 */ 134 public String getTarget() { 135 return target; 136 } 137 public boolean isHFile() { 138 return hfile; 139 } 140 140 } -
trunk/gli/src/org/greenstone/gatherer/msm/MSMAdapter.java
r4293 r4365 57 57 /** The default adapter for a MSMListener doesn't actually do anything, thus users can override whatever methods they want. */ 58 58 public class MSMAdapter 59 implements MSMListener { 60 /** Method that is called whenever an element within a set is changed or modified. 59 implements MSMListener { 60 /** Method that is called whenever an element within a set is changed or modified. 61 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired. 62 */ 63 public void elementChanged(MSMEvent event){} 64 /** Called whenever the metadata value changes in some way, such as the addition of a new value. 61 65 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired. 62 66 */ 63 public void elementChanged(MSMEvent event){}64 /** Called whenever the metadata value changes in some way, such as the addition of a new value.67 public void metadataChanged(MSMEvent event){} 68 /** Method that is called whenever the metadata set collection changes in some way, such as the addition of a new set or the merging of two sets. 65 69 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired. 66 70 */ 67 public void metadataChanged(MSMEvent event){}68 /** Method that is called whenever the metadata set collection changes in some way, such as the addition of a new set or the merging of two sets.71 public void setChanged(MSMEvent event){} 72 /** Called whenever the value tree of an metadata element changes in some way, such as the addition of a new value. 69 73 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired. 70 74 */ 71 public void setChanged(MSMEvent event){} 72 /** Called whenever the value tree of an metadata element changes in some way, such as the addition of a new value. 73 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired. 74 */ 75 public void valueChanged(MSMEvent event){} 75 public void valueChanged(MSMEvent event){} 76 76 } -
trunk/gli/src/org/greenstone/gatherer/msm/MSMEvent.java
r4361 r4365 59 59 /** An event object which contains useful information about the event that generated it, which in this case is probably an element update (this means a MSMAction object should be provided). */ 60 60 public class MSMEvent 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 */ 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 61 extends AWTEvent { 62 /** The element associated with this event. */ 63 private ElementWrapper element = null; 64 /** The file associated with this event. */ 65 private File file = null; 66 /** The record associated with this event. */ 67 private FileNode record = null; 68 /** The value tree model after adding a new value. */ 69 private GValueModel new_model = null; 70 /** The value tree model prior to adding a new value. */ 71 private GValueModel old_model = null; 72 /** A unique id given to any file or metadata task so it can be undone in its entirity. */ 73 private long id; 74 /** The new metadata associated with this event. */ 75 private Metadata new_metadata = null; 76 /** The old metadata associated with this event. */ 77 private Metadata old_metadata = null; 78 /** The profile associated with this event. */ 79 private MSMAction profile = null; 80 /** The value associated with this event. */ 81 private String value = null; 82 /** The message counter. */ 83 static private int count = 0; 84 /** Constructor. 85 * @param source The <strong>Object</strong> that caused this event. 86 * @param id An <i>long</i> identifier. 87 * @param element The <strong>ElementWrapper</strong> affected. 88 */ 89 public MSMEvent(Object source, long id, ElementWrapper element, GValueModel old_model, GValueModel new_model) { 90 super(source, 0); 91 this.element = element; 92 this.id = id; 93 this.new_model = new_model; 94 this.old_model = old_model; 95 this.profile = null; 96 this.record = null; 97 } 98 /** Constructor for a metadata change event involving a file. */ 99 public MSMEvent(Object source, long id, File file, Metadata old_metadata, Metadata new_metadata) { 100 super(source, 0); 101 this.element = null; 102 this.file = file; 103 this.id = id; 104 this.new_metadata = new_metadata; 105 this.old_metadata = old_metadata; 106 this.profile = null; 107 } 108 /** Constructor for a metadata change event involving a filenode. */ 109 public MSMEvent(Object source, long id, FileNode record, Metadata old_metadata, Metadata new_metadata) { 110 super(source, 0); 111 this.element = null; 112 this.id = id; 113 this.new_metadata = new_metadata; 114 this.old_metadata = old_metadata; 115 this.record = record; 116 this.profile = null; 117 } 118 /** Constructor. 119 119 * @param source The <strong>Object</strong> that caused this event. 120 120 * @param id An <i>long</i> identifier. 121 121 * @param profile The <strong>MSMAction</strong> profile if one is applicable. 122 122 */ 123 124 125 126 127 128 129 130 123 public MSMEvent(Object source, long id, MSMAction profile) { 124 super(source, 00); 125 this.element = null; 126 this.id = id; 127 this.record = null; 128 this.profile = profile; 129 } 130 /** Constructor for a metadata element changed event. 131 131 * @param source The <strong>Object</strong> that caused this event. 132 132 * @param element A reference to the effected metadata <strong>ElementWrapper</strong>, or <i>null</i> if element no longer exists. 133 133 * @param value The old name of the element (if its name has changed). If <i>null</i> old name defaults to element.toString(). 134 134 */ 135 136 137 138 139 140 135 public MSMEvent(Object source, ElementWrapper element, String value) { 136 super(source, count++); 137 this.element = element; 138 this.value = value; 139 } 140 /** Method to retrieve of the element associated with this event. 141 141 * @return A <strong>ElementWrapper</strong>, or <i>null</i> if there is none. 142 142 */ 143 144 145 146 147 148 149 150 143 public ElementWrapper getElement() { 144 return element; 145 } 146 147 public File getFile() { 148 return file; 149 } 150 /** Method to retrieve the new metadata associated with this event. 151 151 * @return A <strong>Metadata</strong>, or <i>null</i> if there is none. 152 152 */ 153 154 155 156 157 158 159 153 public Metadata getNewMetadata() { 154 return new_metadata; 155 } 156 public GValueModel getNewModel() { 157 return new_model; 158 } 159 /** Method to retrieve the old metadata associated with this event. 160 160 * @return A <strong>Metadata</strong>, or <i>null</i> if there is none. 161 161 */ 162 163 164 165 166 167 168 162 public Metadata getOldMetadata() { 163 return old_metadata; 164 } 165 public GValueModel getOldModel() { 166 return old_model; 167 } 168 /** Method to retrieve of the profile associated with this event. 169 169 * @return A <strong>MSMAction</strong> which details the action profile, or <i>null</i> if no profile exists. 170 170 */ 171 172 173 174 175 176 177 178 171 public MSMAction getProfile() { 172 return profile; 173 } 174 /** Retrieve the record associated with this event. */ 175 public FileNode getRecord() { 176 return record; 177 } 178 /** Retrieve the associated with this event. 179 179 * @return A <strong>String</strong>. 180 180 */ 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 181 public String getValue() { 182 String result = null; 183 if(value != null) { 184 result = value; 185 } 186 else if(element != null) { 187 result = element.toString(); 188 } 189 return result; 190 } 191 public long ID() { 192 return id; 193 } 194 195 public String toString() { 196 StringBuffer text = new StringBuffer(""); 197 if(old_metadata != null) { 198 text.append(old_metadata.toString()); 199 } 200 text.append(" => "); 201 if(new_metadata != null) { 202 text.append(new_metadata.toString()); 203 } 204 return text.toString(); 205 } 206 206 } -
trunk/gli/src/org/greenstone/gatherer/msm/MSMListener.java
r4293 r4365 53 53 /** This class provides an interface for classes who wish to listener for events within the <strong>MetadataSetManager</strong>. There are events which are fired whenever a set or an element within a set changes. The events are either very broad, of the <i>metadataSetChanged()</i> variaty down to the more explicit <i>elementChanged(Profile change)</i> which actually details what change occured so the GUI etc can update just that element. */ 54 54 public interface MSMListener { 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 55 /** Method that is called whenever an element within a set is changed or modified. 56 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired. 57 */ 58 public void elementChanged(MSMEvent event); 59 /** Called whenever the metadata value changes in some way, such as the addition of a new value. 60 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired. 61 */ 62 public void metadataChanged(MSMEvent event); 63 /** Method that is called whenever the metadata set collection changes in some way, such as the addition of a new set or the merging of two sets. 64 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired. 65 */ 66 public void setChanged(MSMEvent event); 67 /** Called whenever the value tree of an metadata element changes in some way, such as the addition of a new value. 68 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired. 69 */ 70 public void valueChanged(MSMEvent event); 71 71 } -
trunk/gli/src/org/greenstone/gatherer/msm/MSMListenerAdapter.java
r4293 r4365 54 54 /** This class provides an interface for classes who wish to listener for events within the <strong>MetadataSetManager</strong>. There are events which are fired whenever a set or an element within a set changes. The events are either very broad, of the <i>metadataSetChanged()</i> variaty down to the more explicit <i>elementChanged(Profile change)</i> which actually details what change occured so the GUI etc can update just that element. */ 55 55 public class MSMListenerAdapter 56 implements MSMListener { 57 /** Method that is called whenever an element within a set is changed or modified. 56 implements MSMListener { 57 /** Method that is called whenever an element within a set is changed or modified. 58 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired. 59 */ 60 public void elementChanged(MSMEvent event) { 61 } 62 /** Called whenever the metadata value changes in some way, such as the addition of a new value. 58 63 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired. 59 64 */ 60 public void elementChanged(MSMEvent event) {61 62 /** Called whenever the metadata value changes in some way, such as the addition of a new value.65 public void metadataChanged(MSMEvent event) { 66 } 67 /** Method that is called whenever the metadata set collection changes in some way, such as the addition of a new set or the merging of two sets. 63 68 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired. 64 69 */ 65 public void metadataChanged(MSMEvent event) {66 67 /** Method that is called whenever the metadata set collection changes in some way, such as the addition of a new set or the merging of two sets.70 public void setChanged(MSMEvent event) { 71 } 72 /** Called whenever the value tree of an metadata element changes in some way, such as the addition of a new value. 68 73 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired. 69 74 */ 70 public void setChanged(MSMEvent event) { 71 } 72 /** Called whenever the value tree of an metadata element changes in some way, such as the addition of a new value. 73 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired. 74 */ 75 public void valueChanged(MSMEvent event) { 76 } 75 public void valueChanged(MSMEvent event) { 76 } 77 77 } -
trunk/gli/src/org/greenstone/gatherer/msm/MSMProfiler.java
r4293 r4365 57 57 */ 58 58 public class MSMProfiler 59 60 61 62 63 64 65 59 extends MSMAdapter { 60 /** The file the profile should be loaded from and saved to. */ 61 private File profile_file = null; 62 /** A mapping of known profile actions. */ 63 private HashMap3D profiles = null; 64 static final private String IGNORE = "\nIGNORE\n"; 65 /** The constructor loads the previous profile for this collection if there is one. 66 66 * @see org.greenstone.gatherer.collection.CollectionManager 67 67 * @see org.greenstone.gatherer.msm.MetadataSetManager 68 68 */ 69 70 71 72 73 74 75 76 77 78 79 80 81 69 public MSMProfiler() { 70 this.profile_file = new File(Gatherer.c_man.getCollectionMetadata(), "profile.xml"); 71 this.profiles = new HashMap3D(); 72 // Load an existing profile if there is one. 73 if(profile_file.exists()) { 74 Document document = Utility.parse(profile_file, false); 75 if(document != null) { 76 load(document); 77 document = null; 78 } 79 } 80 } 81 /** Adds a new action mapping to the profile. Such a mapping records that for a certain collection (file) and metadata element a specific action must occur. The action is either a renaming to a new metadata element name, also provided, or an instruction to ignore this particular metadata element, is the target is null. 82 82 * @param collection_file The aboslute path name to the collection where the metadata was sourced, as a <strong>String</strong>. 83 83 * @param source A <strong>String</strong> containing the fully qualified name of the source metadata element. 84 84 * @param target Another <strong>String</strong> which is either the fully qualified name of the target element, or <i>null</i> if this is actually an ignore action addition. 85 85 */ 86 87 88 89 90 91 92 93 94 95 96 97 86 public void addAction(String collection_file, String source, String target) { 87 if(target == null) { 88 target = IGNORE; 89 } 90 profiles.put(collection_file, source, target); 91 } 92 93 public void addSource(String collection_file) { 94 profiles.put(collection_file, new HashMap()); 95 } 96 97 /** Determine if an action exists for the given collection and source. 98 98 * @param collection_file The aboslute path name to the collection where the metadata was sourced, as a <strong>String</strong>. 99 99 * @param source A <strong>String</strong> containing the fully qualified name of the source metadata element. 100 100 * @return <i>true</i> if such an action exists, <i>false</i> otherwise. 101 101 */ 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 102 public boolean containsAction(String collection_file, String source) { 103 return profiles.contains(collection_file, source); 104 } 105 106 public boolean containsSource(String collection_file) { 107 return profiles.containsKey(collection_file); 108 } 109 110 /** Destructor to ensure that no memory leaks. */ 111 public void destroy() { 112 //save(); 113 profiles.clear(); 114 profile_file = null; 115 profiles = null; 116 } 117 /** Method that is called whenever an element within a set is changed or modified, in which case we must modify the action profiles to suit. If an element was removed, remove all action profiles with this element as the target. If an elements name changes, update all matching targets to reflect new name. 118 118 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired. 119 119 */ 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 120 public void elementChanged(MSMEvent event) { 121 Gatherer.println("Element changed: " + event); 122 String new_name = null; 123 String old_name = event.getValue(); 124 ElementWrapper element = event.getElement(); 125 boolean delete = false; 126 boolean rename = false; 127 // First we check if the element has been removed. 128 if(element == null) { 129 delete = true; 130 } 131 // Next we determine if its name has changed 132 else if(!(new_name = element.toString()).equals(old_name)) { 133 rename = true; 134 } 135 // Perform any action we need to. 136 if(delete || rename) { 137 Iterator iterator_value_one = profiles.values().iterator(); 138 while(iterator_value_one.hasNext()) { 139 HashMap map = (HashMap) iterator_value_one.next(); 140 Iterator iterator_key_two = map.keySet().iterator(); 141 while(iterator_key_two.hasNext()) { 142 String key_two = (String) iterator_key_two.next(); 143 String value = (String) map.get(key_two); 144 if(value.equals(old_name)) { 145 if(delete) { 146 map.remove(key_two); 147 } 148 else { 149 map.put(key_two, new_name); 150 } 151 } 152 key_two = null; 153 value = null; 154 } 155 map = null; 156 iterator_key_two = null; 157 } 158 iterator_value_one = null; 159 } 160 // Otherwise nothing for us to do (must be an add, or possibly a move if I can ever get round to the editor). 161 element = null; 162 old_name = null; 163 new_name = null; 164 } 165 /** Search the profilerer for any previous actions regarding the indicated collection and metadata element. 166 166 * @param collection_file The absolute path name to the collection where the metadata was sourced, as a <strong>String</strong>. 167 167 * @param source A <strong>String</strong> containing the fully qualified name of the source metadata element. … … 169 169 * @see org.greenstone.gatherer.msm.Declarations 170 170 */ 171 172 173 174 175 176 177 178 179 180 181 182 171 public String getAction(String collection_file, String source) { 172 ///atherer.println("Get action."); 173 String result = (String) profiles.get(collection_file, source); 174 if(result.equals(IGNORE)) { 175 result = null; 176 } 177 return result; 178 } 179 180 public HashMap getActions(String collection_file) { 181 return (HashMap) profiles.get(collection_file); 182 } 183 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 184 public ArrayList getCollections() { 185 return new ArrayList(profiles.keySet()); 186 } 187 188 public ArrayList getSources(String collection_file) { 189 ArrayList result = new ArrayList(); 190 HashMap map = (HashMap) profiles.get(collection_file); 191 if(map != null) { 192 result.addAll(map.keySet()); 193 } 194 return result; 195 } 196 197 public void removeProfile(String collection_file) { 198 profiles.remove(collection_file); 199 } 200 201 public void removeAction(String collection_file, String source) { 202 HashMap map = (HashMap) profiles.get(collection_file); 203 if(map != null) { 204 map.remove(source); 205 } 206 } 207 208 /** Save the profile document. */ 209 public void save() { 210 // While we're at it save the profiles. 211 try { 212 212 // Create backup. 213 214 215 216 217 218 219 220 213 if(profile_file.exists()) { 214 File backup = new File(profile_file.getAbsolutePath() + "~"); 215 backup.deleteOnExit(); 216 if(!profile_file.renameTo(backup)) { 217 Gatherer.println("Error in MSMProfiler.save(): FileRenameException"); 218 } 219 backup = null; 220 } 221 221 // Build the profile document. 222 223 224 225 226 227 228 229 222 StringBuffer buffer = new StringBuffer("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); 223 buffer.append("<!DOCTYPE Profiles [\n"); 224 buffer.append("<!ELEMENT Profiles (Action*)>\n"); 225 buffer.append("<!ELEMENT Action (#PCDATA)>\n"); 226 buffer.append("<!ATTLIST Action\n"); 227 buffer.append(" collection CDATA #REQUIRED\n"); 228 buffer.append(" source CDATA #REQUIRED\n"); 229 buffer.append(" target CDATA \"\">\n"); 230 230 buffer.append("]>\n"); 231 231 buffer.append("<Profiles>\n"); 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 232 Iterator iterator_key_one = profiles.keySet().iterator(); 233 while(iterator_key_one.hasNext()) { 234 String key_one = (String) iterator_key_one.next(); 235 HashMap map = (HashMap) profiles.get(key_one); 236 Iterator iterator_key_two = map.keySet().iterator(); 237 while(iterator_key_two.hasNext()) { 238 String key_two = (String) iterator_key_two.next(); 239 String value = (String) map.get(key_two); 240 buffer.append(" <Action collection=\""); 241 buffer.append(key_one); 242 buffer.append("\" source=\""); 243 buffer.append(key_two); 244 buffer.append("\" target=\""); 245 buffer.append(value); 246 buffer.append("\"> </Action>\n"); 247 key_two = null; 248 value = null; 249 } 250 key_one = null; 251 map = null; 252 iterator_key_two = null; 253 } 254 iterator_key_one = null; 255 255 buffer.append("</Profiles>"); 256 257 256 String buffer_str = buffer.toString(); 257 buffer = null; 258 258 // Now write it to file. 259 260 261 262 259 FileOutputStream fos = new FileOutputStream(profile_file); 260 OutputStreamWriter osw = new OutputStreamWriter(fos); 261 int position = 0; 262 int BLOCK_SIZE = 1024; 263 263 // Write x block sized chunks to file. 264 265 266 267 264 while(position + BLOCK_SIZE < buffer_str.length()) { 265 osw.write(buffer_str, position, BLOCK_SIZE); 266 position = position + BLOCK_SIZE; 267 } 268 268 // Write the remainder of the buffer. 269 270 271 272 273 274 275 276 277 278 279 280 281 282 269 if(position < buffer_str.length()) { 270 osw.write(buffer_str, position, buffer_str.length() - position); 271 } 272 osw.close(); 273 fos.close(); 274 fos = null; 275 osw = null; 276 buffer_str = null; 277 } 278 catch (Exception error) { 279 Gatherer.printStackTrace(error); 280 } 281 } 282 /** Reload the mapping information stored in this document. 283 283 * @param document The <strong>Document</strong> to parse. 284 284 */ 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 285 private void load(Document document) { 286 Element root = document.getDocumentElement(); 287 NodeList actions = root.getElementsByTagName("Action"); 288 for(int i = 0; i < actions.getLength(); i++) { 289 Element action = (Element) actions.item(i); 290 String collection = action.getAttribute("collection"); 291 String source = action.getAttribute("source"); 292 String target = action.getAttribute("target"); 293 if(collection.length() > 0 && source.length() > 0) { 294 if(target.length() == 0) { 295 target = null; 296 } 297 profiles.put(collection, source, target); 298 } 299 action = null; 300 collection = null; 301 source = null; 302 target = null; 303 } 304 root = null; 305 actions = null; 306 } 307 307 } 308 309 310 311 312 313 -
trunk/gli/src/org/greenstone/gatherer/msm/MSMPrompt.java
r4293 r4365 54 54 */ 55 55 public class MSMPrompt 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 56 implements ActionListener { 57 58 private boolean dialog_cancelled = false; 59 private Dimension screen_size = null; 60 private int action = Declarations.NO_ACTION; 61 private JButton add = null; 62 private JButton cancel = null; 63 private JButton merge = null; 64 private JButton rename = null; 65 private JButton replace = null; 66 private JButton skip = null; 67 private JDialog off_screen = null; 68 private JDialog on_screen = null; 69 private JDialog dialog = null; 70 private JLabel current_details_label = null; 71 private JLabel new_details_label = null; 72 private JLabel progress_label = null; 73 private JProgressBar progress = null; 74 private JTextArea current_details = null; 75 private JTextArea new_details = null; 76 private MetadataSetManager manager = null; 77 private Object result = null; 78 79 final static private Dimension MDE_SIZE = new Dimension(500,200); 80 final static private Dimension MDS_SIZE = new Dimension(800,400); 81 final static private Dimension PROGRESS_SIZE = new Dimension(500,80); 82 final static private Dimension RENAME_LABEL_SIZE = new Dimension(100,25); 83 final static private Dimension RENAME_SIZE = new Dimension(300,120); 84 final static private Dimension SELECT_ELEMENT_SIZE = new Dimension(500,280); 85 final static private Dimension SELECT_LABEL_SIZE = new Dimension(175, 25); 86 final static private Dimension SELECT_SET_SIZE = new Dimension(600,210); 87 final static private Dimension SELECT_SIZE = new Dimension(200,200); 88 final static private int SELECT_LINE_COUNT = 8; 89 89 90 91 92 93 94 95 96 97 90 public MSMPrompt(MetadataSetManager manager) { 91 this.manager = manager; 92 // Create components 93 94 add = new JButton(get("Add")); 95 add.addActionListener(this); 96 add.setEnabled(false); 97 add.setMnemonic(KeyEvent.VK_A); 98 98 99 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 /** When called this method produces a nice error message on the screen, advising the user that the add action has failed.142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 100 cancel = new JButton(get("Cancel")); 101 cancel.addActionListener(this); 102 cancel.setEnabled(true); 103 cancel.setMnemonic(KeyEvent.VK_C); 104 105 current_details = new JTextArea(get("No_Details")); 106 107 current_details_label = new JLabel(get("Current_Details")); 108 109 merge = new JButton(get("Merge")); 110 merge.addActionListener(this); 111 merge.setEnabled(true); 112 merge.setMnemonic(KeyEvent.VK_M); 113 114 new_details = new JTextArea(get("No_Details")); 115 116 new_details_label = new JLabel(get("New_Details")); 117 118 progress = new JProgressBar(); 119 progress.setStringPainted(true); 120 121 progress_label = new JLabel(get("Progress")); 122 123 rename = new JButton(get("Rename")); 124 rename.addActionListener(this); 125 rename.setEnabled(false); 126 rename.setMnemonic(KeyEvent.VK_N); 127 128 replace = new JButton(get("Replace")); 129 replace.addActionListener(this); 130 replace.setEnabled(false); 131 replace.setMnemonic(KeyEvent.VK_R); 132 133 skip = new JButton(get("Skip")); 134 skip.addActionListener(this); 135 skip.setEnabled(true); 136 skip.setMnemonic(KeyEvent.VK_S); 137 138 screen_size = Gatherer.config.screen_size; 139 } 140 141 /** When called this method produces a nice error message on the screen, advising the user that the add action has failed. 142 * @param mde_new The <strong>Element</strong> that we were attempting to add. 143 * @param reason The phrase key for the reason the add failed, as a <strong>String</strong>. 144 */ 145 public void addFailed(Element mde_new, String reason) { 146 String args[] = new String[2]; 147 args[0] = mde_new.getAttribute("name"); 148 args[1] = get(reason, null); 149 JOptionPane.showMessageDialog(off_screen, get("Add_Failed", args), get("Add_Failed_Title", null), JOptionPane.ERROR_MESSAGE); 150 } 151 152 /** Any implementation of <i>ActionListener</i> must include this method so that we can be informed when an action has occured. 153 * @param event An <strong>ActionEvent</strong> containing information gatherer when this event occured. 154 */ 155 public void actionPerformed(ActionEvent event) { 156 Object source = event.getSource(); 157 action = Declarations.NO_ACTION; 158 if(source == add) { 159 action = Declarations.ADD; 160 } 161 else if(source == cancel) { 162 action = Declarations.CANCEL; 163 } 164 else if(source == merge) { 165 action = Declarations.MERGE; 166 } 167 else if(source == rename) { 168 action = Declarations.RENAME; 169 } 170 else if(source == replace) { 171 action = Declarations.REPLACE; 172 } 173 else if(source == skip) { 174 action = Declarations.SKIP; 175 } 176 on_screen.hide(); 177 } 178 /** Method called when the merging process is complete and the progress bar is no longer needed. 179 */ 180 public void endMerge() { 181 off_screen.dispose(); 182 off_screen = null; 183 } 184 /** Method to indicate that yet another element has been succesfully merged, so that progress bar should reflect this. 185 */ 186 public void incrementMerge() { 187 progress.setValue(progress.getValue() + 1); 188 String percent = ((100 * progress.getValue()) / progress.getMaximum()) + "%"; 189 progress.setString(percent); 190 } 191 /** Method to display the metadata element merging prompt, wherein the user determines how the attributes within an element should be merged. 192 * @param mde_cur The current <strong>Element</strong> we are merging against. 193 * @param att_cur The attribute <strong>Element</strong> of the current element in question. 194 * @param mde_new The <strong>Element</strong> which we have choosen to merge in, and whose attributes are being examined. 195 * @param att_new And the new elements attribute <strong>Element</strong>. 196 * @return An <i>int</i> specifying what further action should be undertaken, if any. 197 */ 198 public int mDEPrompt(Element mde_cur, Element att_cur, Element mde_new, Element att_new) { 199 action = Declarations.NO_ACTION; 200 // Construction and configuration 201 JDialog dialog = new JDialog(); 202 dialog.setModal(true); 203 dialog.setSize(MDE_SIZE); 204 dialog.setTitle(get("Merge_MDE")); 205 JPanel content_pane = (JPanel)dialog.getContentPane(); 206 207 JLabel cur_name_label = new JLabel(get("Element_Name")); 208 209 JLabel cur_name = new JLabel(MSMUtils.getFullName(mde_cur)); 210 cur_name.setBackground(Color.white); 211 cur_name.setOpaque(true); 212 213 JLabel cur_att_label = new JLabel(get("Attribute")); 214 215 JLabel cur_att = new JLabel(att_cur.getAttribute("name") +" = " + MSMUtils.getValue(att_cur)); 216 cur_att.setBackground(Color.white); 217 cur_att.setOpaque(true); 218 219 JLabel new_name_label = new JLabel(cur_name_label.getText()); 220 221 JLabel new_name = new JLabel(MSMUtils.getFullName(mde_new)); 222 new_name.setBackground(Color.white); 223 new_name.setOpaque(true); 224 225 JLabel new_att_label = new JLabel(cur_att_label.getText()); 226 227 JLabel new_att = new JLabel(att_new.getAttribute("name") +" = " + MSMUtils.getValue(att_new)); 228 new_att.setBackground(Color.white); 229 new_att.setOpaque(true); 230 231 replace.setEnabled(true); 232 skip.setEnabled(true); 233 234 // Layout 235 JPanel current_pane = new JPanel(); 236 current_pane.setBorder(BorderFactory.createTitledBorder(get("Current_Element"))); 237 current_pane.setLayout(new GridLayout(2,2)); 238 current_pane.add(cur_name_label); 239 current_pane.add(cur_name); 240 current_pane.add(cur_att_label); 241 current_pane.add(cur_att); 242 243 JPanel new_pane = new JPanel(); 244 new_pane.setBorder(BorderFactory.createTitledBorder(get("New_Element"))); 245 new_pane.setLayout(new GridLayout(2,2)); 246 new_pane.add(new_name_label); 247 new_pane.add(new_name); 248 new_pane.add(new_att_label); 249 new_pane.add(new_att); 250 251 JPanel central_pane = new JPanel(); 252 central_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 253 central_pane.setLayout(new GridLayout(2,1)); 254 central_pane.add(current_pane); 255 central_pane.add(new_pane); 256 257 JPanel button_pane = new JPanel(); 258 button_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5)); 259 button_pane.setLayout(new GridLayout(1,3)); 260 button_pane.add(replace); 261 button_pane.add(skip); 262 button_pane.add(cancel); 263 264 content_pane.setLayout(new BorderLayout()); 265 content_pane.add(central_pane, BorderLayout.CENTER); 266 content_pane.add(button_pane, BorderLayout.SOUTH); 267 268 // Display 269 dialog.setLocation((screen_size.width - MDE_SIZE.width) / 2, (screen_size.height - MDE_SIZE.height) / 2); 270 on_screen = dialog; 271 off_screen.hide(); 272 on_screen.show(); 273 off_screen.show(); 274 on_screen.dispose(); 275 276 content_pane = null; 277 cur_name_label = null; 278 cur_name = null; 279 cur_att_label = null; 280 cur_att = null; 281 new_name_label = null; 282 new_name = null; 283 new_att_label = null; 284 new_att = null; 285 current_pane = null; 286 new_pane = null; 287 central_pane = null; 288 button_pane = null; 289 screen_size = null; 290 on_screen = null; 291 dialog = null; 292 293 return action; 294 } 295 /** This method displays the metadata data set merging prompt, wherein the user determines how the elements within a set should be merged. 296 * @param mds_cur The current <strong>MetadataSet</strong> containing our document. 297 * @param mde_cur The current <strong>Element</strong> we are merging against. 298 * @param mds_new The <strong>MetadataSet</strong> we are merging in. 299 * @param mde_new The <strong>Element</strong> which we have choosen to merge in. 300 * @return An <i>int</i> specifying what further action should be undertaken, if any. 301 */ 302 public int mDSPrompt(MetadataSet mds_cur, Element mde_cur, MetadataSet mds_new, Element mde_new) { 303 action = Declarations.NO_ACTION; 304 // Construction and configuration 305 JDialog dialog = new JDialog(); 306 dialog.setModal(true); 307 dialog.setSize(MDS_SIZE); 308 dialog.setTitle(get("Merge_MDS")); 309 JPanel content_pane = (JPanel)dialog.getContentPane(); 310 if(mde_cur != null) { 311 add.setEnabled(false); 312 cancel.setEnabled(true); 313 merge.setEnabled(true); 314 rename.setEnabled(true); 315 replace.setEnabled(true); 316 skip.setEnabled(true); 317 317 // Current details. 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 318 String str_cur[] = MSMUtils.getStructuralDetails(mde_cur); 319 String opt_cur[] = MSMUtils.getOptionListDetails(mde_cur); 320 String ass_cur[] = MSMUtils.getAssignedValuesDetails(mds_cur, mde_cur); 321 String details_cur = get("Structural"); 322 if(opt_cur != null) { 323 details_cur = details_cur + "\n" + get("OptionList", opt_cur); 324 } 325 if(ass_cur != null) { 326 details_cur = details_cur + "\n" + get("AssignedValues", ass_cur); 327 } 328 current_details.setText(details_cur); 329 str_cur = null; 330 opt_cur = null; 331 ass_cur = null; 332 details_cur = null; 333 } 334 else { 335 add.setEnabled(true); 336 cancel.setEnabled(true); 337 merge.setEnabled(true); 338 rename.setEnabled(false); 339 replace.setEnabled(false); 340 skip.setEnabled(true); 341 current_details.setText(get("No_Element")); 342 } 343 // New details. 344 String str_new[] = MSMUtils.getStructuralDetails(mde_new); 345 String opt_new[] = MSMUtils.getOptionListDetails(mde_new); 346 String ass_new[] = MSMUtils.getAssignedValuesDetails(mds_new, mde_new); 347 String details_new = get("Structural", str_new); 348 if(opt_new != null) { 349 details_new = details_new + "\n" + get("OptionList", opt_new); 350 } 351 if(ass_new != null) { 352 details_new = details_new + "\n" + get("AssignedValues", ass_new); 353 } 354 new_details.setText(details_new); 355 // Layout 356 JPanel current_details_pane = new JPanel(); 357 current_details_pane.setLayout(new BorderLayout()); 358 current_details_pane.add(current_details_label, BorderLayout.NORTH); 359 current_details_pane.add(new JScrollPane(current_details), BorderLayout.CENTER); 360 361 JPanel new_details_pane = new JPanel(); 362 new_details_pane.setLayout(new BorderLayout()); 363 new_details_pane.add(new_details_label, BorderLayout.NORTH); 364 new_details_pane.add(new JScrollPane(new_details), BorderLayout.CENTER); 365 366 JPanel details_pane = new JPanel(); 367 details_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 368 details_pane.setLayout(new GridLayout(1,2)); 369 details_pane.add(current_details_pane); 370 details_pane.add(new_details_pane); 371 372 JPanel button_pane = new JPanel(); 373 button_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5)); 374 button_pane.setLayout(new GridLayout(1,6)); 375 button_pane.add(add); 376 button_pane.add(merge); 377 button_pane.add(rename); 378 button_pane.add(replace); 379 button_pane.add(skip); 380 button_pane.add(cancel); 381 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 382 JPanel control_pane = new JPanel(); 383 control_pane.setLayout(new BorderLayout()); 384 control_pane.add(details_pane, BorderLayout.CENTER); 385 control_pane.add(button_pane, BorderLayout.SOUTH); 386 387 JPanel progress_pane = new JPanel(); 388 progress_pane.setBorder(BorderFactory.createEmptyBorder(5,5,0,5)); 389 progress_pane.setLayout(new BorderLayout()); 390 progress_pane.add(progress_label, BorderLayout.NORTH); 391 progress_pane.add(progress, BorderLayout.CENTER); 392 393 content_pane.setLayout(new BorderLayout()); 394 content_pane.add(progress_pane, BorderLayout.NORTH); 395 content_pane.add(control_pane, BorderLayout.CENTER); 396 397 // Display 398 dialog.setLocation((screen_size.width - MDS_SIZE.width) / 2, (screen_size.height - MDS_SIZE.height) / 2); 399 on_screen = dialog; 400 off_screen.hide(); 401 on_screen.show(); // Blocks until hidden. 402 off_screen.show(); 403 on_screen.dispose(); 404 on_screen = null; 405 406 content_pane = null; 407 str_new = null; 408 opt_new = null; 409 ass_new = null; 410 details_new = null; 411 current_details_pane = null; 412 new_details_pane = null; 413 details_pane = null; 414 button_pane = null; 415 control_pane = null; 416 progress_pane = null; 417 dialog = null; 418 419 if(mde_cur == null && action == Declarations.MERGE) { 420 action = Declarations.FORCE_MERGE; 421 } 422 return action; 423 } 424 /** This method creates an initial JDialog which simply shows the MSMs progress towards merging the metadata sets. 425 * @param element_count An <i>int</i> specifying the total number of elements to be merged. 426 */ 427 public void startMerge(int element_count) { 428 action = Declarations.NO_ACTION; 429 JDialog dialog = new JDialog(); 430 dialog.setModal(false); 431 dialog.setSize(PROGRESS_SIZE); 432 dialog.setTitle(get("Merge_Progress")); 433 JPanel content_pane = (JPanel)dialog.getContentPane(); 434 progress.setMaximum(element_count); 435 progress.setMinimum(0); 436 progress.setString("0%"); 437 progress.setValue(0); 438 // Layout. 439 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 440 content_pane.setLayout(new BorderLayout()); 441 content_pane.add(progress_label, BorderLayout.NORTH); 442 content_pane.add(progress, BorderLayout.CENTER); 443 // Display. 444 dialog.setLocation((screen_size.width - PROGRESS_SIZE.width) / 2, (screen_size.height - PROGRESS_SIZE.height) / 2); 445 off_screen = dialog; 446 dialog.show(); 447 content_pane = null; 448 dialog = null; 449 } 450 /** This method creates a prompt for renaming an Element. 451 * @param mde_new The <strong>Element</strong> we wish to rename. 452 * @return The new name as a <strong>String</strong>, <i>null</i> if cancelled. 453 */ 454 public String rename(Element mde_new) { 455 action = Declarations.NO_ACTION; 456 // Create 457 JDialog dialog = new JDialog(); 458 dialog.setModal(true); 459 dialog.setSize(RENAME_SIZE); 460 dialog.setTitle(get("Rename")); 461 462 JLabel old_name_label = new JLabel(get("Old_Name")); 463 old_name_label.setPreferredSize(RENAME_LABEL_SIZE); 464 465 JLabel old_name = new JLabel(mde_new.getAttribute("name")); 466 old_name.setBackground(Color.white); 467 old_name.setOpaque(true); 468 469 JLabel new_name_label = new JLabel(get("New_Name")); 470 new_name_label.setPreferredSize(RENAME_LABEL_SIZE); 471 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 472 JTextField new_name = new JTextField(""); 473 new_name.grabFocus(); 474 475 JButton ok = new JButton(get("General.OK")); 476 ok.addActionListener(this); 477 ok.setMnemonic(KeyEvent.VK_O); 478 479 JPanel content_pane = (JPanel) dialog.getContentPane(); 480 481 // Layout 482 JPanel old_name_pane = new JPanel(); 483 old_name_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 484 old_name_pane.setLayout(new BorderLayout()); 485 old_name_pane.add(old_name_label, BorderLayout.WEST); 486 old_name_pane.add(old_name, BorderLayout.CENTER); 487 488 JPanel new_name_pane = new JPanel(); 489 new_name_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5)); 490 new_name_pane.setLayout(new BorderLayout()); 491 new_name_pane.add(new_name_label, BorderLayout.WEST); 492 new_name_pane.add(new_name, BorderLayout.CENTER); 493 494 JPanel control_pane = new JPanel(); 495 control_pane.setLayout(new GridLayout(2,1)); 496 control_pane.add(old_name_pane); 497 control_pane.add(new_name_pane); 498 499 JPanel button_pane = new JPanel(); 500 button_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5)); 501 button_pane.setLayout(new GridLayout(1,2)); 502 button_pane.add(ok); 503 button_pane.add(cancel); 504 505 content_pane.setLayout(new BorderLayout()); 506 content_pane.add(control_pane, BorderLayout.CENTER); 507 content_pane.add(button_pane, BorderLayout.SOUTH); 508 // Display 509 dialog.setLocation((screen_size.width - RENAME_SIZE.width) / 2, (screen_size.height - RENAME_SIZE.height) / 2); 510 on_screen = dialog; 511 off_screen.hide(); 512 on_screen.show(); 513 off_screen.show(); 514 on_screen.dispose(); 515 on_screen = null; 516 517 button_pane = null; 518 control_pane = null; 519 new_name_pane = null; 520 old_name_pane = null; 521 content_pane = null; 522 ok.removeActionListener(this); 523 ok = null; 524 new_name = null; 525 new_name_label = null; 526 old_name = null; 527 old_name_label = null; 528 dialog = null; 529 530 if(action == Declarations.CANCEL) { 531 return null; 532 } 533 action = Declarations.NO_ACTION; 534 dialog = null; 535 return new_name.getText(); 536 } 537 538 /** When called this method produces a nice error message on the screen, advising the user that the remove action has failed. 539 * @param mde_cur The <strong>Element</strong> that we are attempting to remove. 540 * @param reason The phrase key for the reason the rename failed, also as a <strong>String</strong>. 541 */ 542 public void removeFailed(Element mde_cur, String reason) { 543 String args[] = new String[2]; 544 args[0] = mde_cur.getAttribute("name"); 545 args[1] = get(reason, null); 546 JOptionPane.showMessageDialog(off_screen, get("Remove_Failed", args), get("Remove_Failed_Title", null), JOptionPane.ERROR_MESSAGE); 547 } 548 549 /** When called this method produces a nice error message on the screen, advising the user that the rename action has failed. 550 * @param mde_new The <strong>Element</strong> that we are attempting to add. 551 * @param new_name The name that we attempted to add it under, as a <strong>String</strong>. 552 * @param reason The phrase key for the reason the rename failed, also as a <strong>String</strong>. 553 */ 554 public void renameFailed(Element mde_new, String new_name, String reason) { 555 String args[] = new String[3]; 556 args[0] = mde_new.getAttribute("name"); 557 args[1] = new_name; 558 args[2] = get(reason, null); 559 JOptionPane.showMessageDialog(off_screen, get("Rename_Failed", args), get("Rename_Failed_Title", null), JOptionPane.ERROR_MESSAGE); 560 } 561 562 /** Method to display a prompt asking the user to select a specific element to merge with. 563 * @param mds_cur The current <strong>MetadataSet</strong>. 564 * @return The selected <strong>Element</strong> or <i>null</i> if the user cancels the action. 565 */ 566 public Element selectElement(MetadataSet mds_cur) { 567 action = Declarations.NO_ACTION; 568 // Create 569 JDialog dialog = new JDialog(); 570 dialog.setModal(true); 571 dialog.setSize(SELECT_SIZE); 572 dialog.setTitle(get("Select")); 573 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 574 JButton ok = new JButton(get("General.OK")); 575 ok.addActionListener(this); 576 ok.setMnemonic(KeyEvent.VK_O); 577 ok.setEnabled(false); 578 579 Node elements_raw[] = ArrayTools.nodeListToNodeArray(mds_cur.getElements()); 580 ElementWrapper elements[] = new ElementWrapper[elements_raw.length]; 581 for(int i = 0; i < elements_raw.length; i++) { 582 elements[i] = new ElementWrapper((Element)elements_raw[i]); 583 } 584 JList list = new JList(elements); 585 ElementListListener element_list_listener = new ElementListListener(list, ok); 586 list.addListSelectionListener(element_list_listener); 587 JPanel content_pane = (JPanel) dialog.getContentPane(); 588 // Layout 589 JPanel list_pane = new JPanel(); 590 list_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 591 list_pane.setLayout(new BorderLayout()); 592 list_pane.add(new JScrollPane(list), BorderLayout.CENTER); 593 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 594 JPanel button_pane = new JPanel(); 595 button_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5)); 596 button_pane.setLayout(new GridLayout(1,2)); 597 button_pane.add(ok); 598 button_pane.add(cancel); 599 600 content_pane.setLayout(new BorderLayout()); 601 content_pane.add(list_pane, BorderLayout.CENTER); 602 content_pane.add(button_pane, BorderLayout.SOUTH); 603 // Display 604 dialog.setLocation((screen_size.width - SELECT_SIZE.width) / 2, (screen_size.height - SELECT_SIZE.height) / 2); 605 on_screen = dialog; 606 off_screen.hide(); 607 on_screen.show(); 608 off_screen.show(); 609 on_screen.dispose(); 610 on_screen = null; 611 // Deallocate stuff since JDK1.4 won't. 612 ok.removeActionListener(this); 613 ok = null; 614 elements_raw = null; 615 elements = null; 616 list.removeListSelectionListener(element_list_listener); 617 list = null; 618 element_list_listener = null; 619 content_pane = null; 620 list_pane = null; 621 button_pane = null; 622 dialog = null; 623 // Return selected element. 624 if(action == Declarations.CANCEL) { 625 return null; 626 } 627 return ((ElementWrapper)list.getSelectedValue()).getElement(); 628 } 629 630 /** Prompts the user to choose how to import a metadata element. Gives the option of renaming the element to a certain value of a certain set, or adding to a selected set. 631 * @param name The name of the original metadata as a String. 632 * @param set The set previously choosen as the default set for this metadata (which it doesn't match somehow). 633 * @return An ElementWrapper around the element that it has been matched to. 634 * @see org.greenstone.gatherer.msm.MSMPrompt.MSMDialog 635 * @see org.greenstone.gatherer.util.MED 636 */ 637 public ElementWrapper selectElement(String name) { 638 dialog_cancelled = false; 639 result = null; 640 String args[] = new String[1]; 641 args[0] = name; 642 // Create 643 MSMDialog dialog = new MSMDialog(); 644 dialog.setModal(true); 645 dialog.setSize(SELECT_ELEMENT_SIZE); 646 dialog.setTitle(get("Select_Element_Title")); 647 JPanel content_pane = (JPanel) dialog.getContentPane(); 648 JPanel control_pane = new JPanel(); 649 JTextArea instructions = new JTextArea(get("Select_Element_Instructions", args)); 650 instructions.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 651 instructions.setEditable(false); 652 instructions.setLineWrap(true); 653 instructions.setRows(SELECT_LINE_COUNT); 654 instructions.setWrapStyleWord(true); 655 JPanel original_pane = new JPanel(); 656 JLabel original_label = new JLabel(get("Select_Element_Original")); 657 original_label.setPreferredSize(SELECT_LABEL_SIZE); 658 JTextField original = new JTextField(name); 659 original.setEditable(false); 660 JPanel element_pane = new JPanel(); 661 JLabel element_label = new JLabel(get("Select_Element_Element")); 662 element_label.setPreferredSize(SELECT_LABEL_SIZE); 663 JComboBox element = new JComboBox(); 664 element.setBackground(Color.white); 665 JButton add_button = new JButton(get("Select_Element_Add")); 666 add_button.setEnabled(false); 667 add_button.setMnemonic(KeyEvent.VK_A); 668 JButton cancel_button = new JButton(get("General.Cancel")); 669 cancel_button.setMnemonic(KeyEvent.VK_C); 670 JButton merge_button = new JButton(get("Select_Element_Merge")); 671 merge_button.setEnabled(false); 672 merge_button.setMnemonic(KeyEvent.VK_M); 673 JButton ignore_button = new JButton(get("Select_Element_Ignore")); 674 ignore_button.setMnemonic(KeyEvent.VK_I); 675 JPanel set_pane = new JPanel(); 676 JLabel set_label = new JLabel(get("Select_Element_Set")); 677 set_label.setPreferredSize(SELECT_LABEL_SIZE); 678 JComboBox set = new JComboBox(manager.getSets(false)); // Don't include the greenstone metadata set. 679 set.setBackground(Color.white); 680 JPanel button_pane = new JPanel(); 681 // Connect 682 AddListener add_listener = new AddListener(dialog, name, set); 683 CancelListener cancel_listener = new CancelListener(dialog); 684 IgnoreListener ignore_listener = new IgnoreListener(dialog); 685 MergeListener merge_listener = new MergeListener(dialog, element); 686 SetListener set_listener = new SetListener(set, element, name, add_button, merge_button); 687 688 add_button.addActionListener(add_listener); 689 cancel_button.addActionListener(cancel_listener); 690 merge_button.addActionListener(merge_listener); 691 ignore_button.addActionListener(ignore_listener); 692 set.addActionListener(set_listener); 693 694 // Init controls 695 if(set.getItemCount() > 0) { 696 696 // We now try to determine the existing metadata element that is the closest match to the one we are trying to merge. 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 697 MetadataSet closest_set = null; 698 ElementWrapper closest_element = null; 699 int med = -1; 700 for(int i = 0; i < set.getItemCount(); i++) { 701 MetadataSet mds = (MetadataSet) set.getItemAt(i); 702 for(int j = 0; j < mds.size(); j++) { 703 ElementWrapper mde = new ElementWrapper(mds.getElement(j)); 704 int new_med = MED.LD(name, mde.toString()); 705 if(med == -1 || new_med < med) { 706 med = new_med; 707 closest_element = mde; 708 closest_set = mds; 709 } 710 mde = null; 711 } 712 mds = null; 713 } 714 if(closest_set != null && closest_element != null) { 715 set.setSelectedItem(closest_set); 716 element.setSelectedItem(closest_element); 717 set_listener.actionPerformed(); 718 } 719 else { 720 set_listener.actionPerformed(); 721 } 722 closest_set = null; 723 closest_element = null; 724 } 725 merge_button.setEnabled(element.getItemCount() > 0); 726 // Layout 727 original_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5)); 728 original_pane.setLayout(new BorderLayout()); 729 original_pane.add(original_label, BorderLayout.WEST); 730 original_pane.add(original, BorderLayout.CENTER); 731 set_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5)); 732 set_pane.setLayout(new BorderLayout()); 733 set_pane.add(set_label, BorderLayout.WEST); 734 set_pane.add(set, BorderLayout.CENTER); 735 element_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5)); 736 element_pane.setLayout(new BorderLayout()); 737 element_pane.add(element_label, BorderLayout.WEST); 738 element_pane.add(element, BorderLayout.CENTER); 739 control_pane.setBorder(BorderFactory.createEmptyBorder(0,0,0,0)); 740 control_pane.setLayout(new GridLayout(3,1)); 741 control_pane.add(original_pane); 742 control_pane.add(set_pane); 743 control_pane.add(element_pane); 744 button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0)); 745 button_pane.setLayout(new GridLayout(1,4)); 746 button_pane.add(add_button); 747 button_pane.add(merge_button); 748 button_pane.add(ignore_button); 749 button_pane.add(cancel_button); 750 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 751 content_pane.setLayout(new BorderLayout()); 752 content_pane.add(new JScrollPane(instructions), BorderLayout.NORTH); 753 content_pane.add(control_pane, BorderLayout.CENTER); 754 content_pane.add(button_pane, BorderLayout.SOUTH); 755 // Display 756 dialog.setLocation((screen_size.width - SELECT_ELEMENT_SIZE.width) / 2, (screen_size.height - SELECT_ELEMENT_SIZE.height) / 2); 757 dialog.show(); 758 // Deallocate everything because JDK1.4 won't. 759 // Why, oh why did I do this? 760 add_button.removeActionListener(add_listener); 761 merge_button.removeActionListener(merge_listener); 762 ignore_button.removeActionListener(ignore_listener); 763 add_button = null; 764 merge_button = null; 765 ignore_button = null; 766 add_listener = null; 767 merge_listener = null; 768 ignore_listener = null; 769 // References 770 args = null; 771 content_pane = null; 772 control_pane = null; 773 instructions = null; 774 original_pane = null; 775 original_pane = null; 776 original = null; 777 element_pane = null; 778 element_label = null; 779 element = null; 780 set_pane = null; 781 set_label = null; 782 set.removeActionListener(set_listener); 783 set_listener = null; 784 set = null; 785 button_pane = null; 786 dialog.dispose(); 787 dialog.destroy(); 788 dialog = null; 789 // Dondage. 790 return (ElementWrapper)result; 791 } 792 793 public boolean wasDialogCancelled() { 794 return dialog_cancelled; 795 } 796 797 private String get(String key) { 798 return get(key, null); 799 } 800 801 private String get(String key, String args[]) { 802 if(key.indexOf(".") == -1) { 803 key = "MSMPrompt." + key; 804 } 805 return Gatherer.dictionary.get(key, args); 806 } 807 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 808 /** Prompts the user to select a metadata set from the given list. Uses the name parameter to attempt to automatically select the correct collection (ie the only collection that has an element with the same name). 809 * @param name The name of the metadata element whose set name is unknown. 810 * @return The metadata set the user has selected or null if no set selected. 811 */ 812 final public MetadataSet selectSet(String name) { 813 String args[] = new String[1]; 814 args[0] = name; 815 // Creation 816 MSMDialog dialog = new MSMDialog(); 817 dialog.setModal(true); 818 dialog.setSize(SELECT_SET_SIZE); 819 dialog.setTitle(get("Select_Set_Title")); 820 JPanel content_pane = (JPanel) dialog.getContentPane(); 821 JPanel control_pane = new JPanel(); 822 JTextArea instructions = new JTextArea(get("Select_Set_Instructions", args)); 823 instructions.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 824 instructions.setEditable(false); 825 instructions.setLineWrap(true); 826 instructions.setRows(SELECT_LINE_COUNT); 827 instructions.setWrapStyleWord(true); 828 JComboBox set = new JComboBox(); 829 set.setBackground(Color.white); 830 set.addItem(get("Select_Set_None")); 831 Vector sets = manager.getSets(); 832 for(int i = sets.size() - 1; i >= 0; i--) { 833 set.addItem(sets.get(i)); 834 } 835 JButton ok = new JButton(get("General.OK")); 836 ActionListener ok_listener = new IgnoreListener(dialog); 837 ok.addActionListener(ok_listener); // Doesn't really ignore. Just disposes() 838 // Select most likely set. 839 if(name.indexOf(".") != -1) { 840 String set_name = name.substring(0, name.indexOf(".")); 841 MetadataSet metadata_set = manager.getSet(set_name); 842 if(metadata_set != null) { 843 set.setSelectedItem(metadata_set); 844 } 845 metadata_set = null; 846 set_name = null; 847 } 848 else { 849 Vector matches = manager.setsThatContain(name); 850 if(matches.size() == 1) { 851 set.setSelectedItem(matches.get(0)); 852 } 853 matches = null; 854 } 855 // Layout 856 control_pane.setLayout(new GridLayout(2,1)); 857 control_pane.add(set); 858 control_pane.add(ok); 859 content_pane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); 860 content_pane.setLayout(new BorderLayout()); 861 content_pane.add(new JScrollPane(instructions), BorderLayout.CENTER); 862 content_pane.add(control_pane, BorderLayout.SOUTH); 863 // Display 864 dialog.setLocation((screen_size.width - SELECT_SET_SIZE.width) / 2, (screen_size.height - SELECT_SET_SIZE.height) / 2); 865 dialog.show(); 866 Object value = set.getSelectedItem(); 867 if(value instanceof MetadataSet) { 868 return (MetadataSet) value; 869 } 870 value = null; 871 ok.removeActionListener(ok_listener); 872 ok_listener = null; 873 ok = null; 874 content_pane = null; 875 control_pane = null; 876 instructions = null; 877 set = null; 878 dialog.destroy(); 879 dialog = null; 880 return null; 881 } 882 883 private class AddListener 884 implements ActionListener { 885 private JComboBox set = null; 886 private JDialog dialog = null; 887 private String name = null; 888 public AddListener(JDialog dialog, String name, JComboBox set) { 889 this.dialog = dialog; 890 this.name = name; 891 this.set = set; 892 } 893 public void actionPerformed(ActionEvent event) { 894 894 // Get the currently selected metadata set. 895 896 895 MetadataSet mds = (MetadataSet) set.getSelectedItem(); 896 String n = name; 897 897 // If we've been forced to go this far then any given namespace is complete bollocks. 898 899 900 898 while(n.indexOf(".") != -1 && !n.equals(".")) { 899 n = n.substring(n.indexOf(".") + 1); 900 } 901 901 // However, before we attempt to add a new element to the set, we should check that none already exists. 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 902 Element element = mds.getElement(name); 903 if(element == null) { 904 result = mds.addElement(n); 905 } 906 else { 907 result = (ElementWrapper) element; 908 } 909 mds = null; 910 n = null; 911 element = null; 912 dialog.dispose(); 913 } 914 } 915 /** This listener listens for selections within the select element to merge prompt, and once one has been made enables the ok button.*/ 916 private class ElementListListener 917 implements ListSelectionListener { 918 /** The button we either enable or disable depending on user selection. */ 919 private JButton ok = null; 920 /** The list from whom we are listening for selection events. */ 921 private JList list = null; 922 /** Constructor. 923 * @param list The <strong>JList</Strong> from whom we are listening for selection events. 924 * @param ok The <Strong>JButton</strong> we either enable or disable depending on user selection. 925 */ 926 public ElementListListener(JList list, JButton ok) { 927 this.list = list; 928 this.ok = ok; 929 } 930 /** Any implementation of <i>ListSelectionListener</i> must include this method so we can be informed when a list selection event has occured. 931 * @param event A <strong>ListSelectionEvent</strong> generated by the event. 932 */ 933 public void valueChanged(ListSelectionEvent event) { 934 if(list.getSelectedValue() != null) { 935 ok.setEnabled(true); 936 } 937 else { 938 ok.setEnabled(false); 939 } 940 } 941 } 942 943 private class CancelListener 944 implements ActionListener { 945 private JDialog dialog = null; 946 public CancelListener(JDialog dialog) { 947 this.dialog = dialog; 948 } 949 public void actionPerformed(ActionEvent event) { 950 dialog_cancelled = true; 951 dialog.dispose(); 952 } 953 } 954 955 private class IgnoreListener 956 implements ActionListener { 957 private JDialog dialog = null; 958 public IgnoreListener(JDialog dialog) { 959 this.dialog = dialog; 960 } 961 public void actionPerformed(ActionEvent event) { 962 dialog.dispose(); 963 } 964 } 965 966 private class MergeListener 967 implements ActionListener { 968 private JComboBox element = null; 969 private JDialog dialog = null; 970 public MergeListener(JDialog dialog, JComboBox element) { 971 this.dialog = dialog; 972 this.element = element; 973 } 974 public void actionPerformed(ActionEvent event) { 975 975 // Return the currently selected element 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 976 result = (ElementWrapper)element.getSelectedItem(); 977 dialog.dispose(); 978 } 979 } 980 981 private class MSMDialog 982 extends JDialog { 983 public void destroy() { 984 rootPane = null; 985 } 986 } 987 988 private class SetListener 989 implements ActionListener { 990 private JButton add_button = null; 991 private JButton merge_button = null; 992 private JComboBox element = null; 993 private JComboBox set = null; 994 private String name = null; 995 public SetListener(JComboBox set, JComboBox element, String name, JButton add_button, JButton merge_button) { 996 this.add_button = add_button; 997 this.element = element; 998 this.merge_button = merge_button; 999 this.name = name.toLowerCase(); 1000 this.set = set; 1001 } 1002 public void actionPerformed() { 1003 element.removeAllItems(); 1004 MetadataSet mds = (MetadataSet) set.getSelectedItem(); 1005 System.err.println("Set selected: " + mds); 1006 if(mds != null) { 1007 NodeList elements = mds.getElements(); 1008 Vector temp = new Vector(); 1009 for(int i = elements.getLength() - 1; i >= 0; i--) { 1010 temp.add(new ElementWrapper((Element)elements.item(i))); 1011 } 1012 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1013 // The add button is only enabled if an element with the same name doesn't already exist in the destination set. 1014 boolean add_button_enabled = true; 1015 ///ystem.err.println("Checking for " + name); 1016 for(int j = 0; j < temp.size(); j++) { 1017 ElementWrapper an_element = (ElementWrapper)temp.get(j); 1018 // Can only add if no file with the same name exists. 1019 String check = an_element.toString().toLowerCase(); 1020 int position = -1; 1021 if((position = check.indexOf(".")) != -1) { 1022 check = check.substring(position + 1); 1023 } 1024 ///ystem.err.println("does it match " + check + "? " + name.equals(check)); 1025 add_button_enabled = add_button_enabled && !name.equals(check); 1026 check = null; 1027 element.addItem(an_element); 1028 an_element = null; 1029 } 1030 add_button.setEnabled(add_button_enabled); 1031 1032 // The merge button is only enabled if there is more than one element in the destination set. 1033 if(temp.size() > 0) { 1034 merge_button.setEnabled(true); 1035 } 1036 else { 1037 merge_button.setEnabled(false); 1038 } 1039 temp = null; 1040 elements = null; 1041 } 1042 else { 1043 add_button.setEnabled(false); 1044 merge_button.setEnabled(false); 1045 } 1046 mds = null; 1047 } 1048 public void actionPerformed(ActionEvent item) { 1049 actionPerformed(); 1050 } 1051 } 1052 1052 } 1053 -
trunk/gli/src/org/greenstone/gatherer/msm/MSMUtils.java
r4330 r4365 67 67 */ 68 68 public class MSMUtils { 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 69 /** Used to order metadata according to set standard element order then alphabetically by value. */ 70 static public MetadataComparator METADATA_COMPARATOR = new MetadataComparator(); 71 /** An element of the enumeration of type filter. */ 72 static public final int NONE = 0; 73 /** An element of the enumeration of type filter. */ 74 static public final int VALUES = 1; 75 /** An element of the enumeration of type filter. */ 76 static public final int ALIASES = 2; 77 /** An element of the enumeration of type filter. */ 78 static public final int BOTH = 3; 79 /** The character used to separate name space from metadata element. */ 80 static public final char NS_SEP= '.'; 81 /** Method to add one node as a child of another, after migrating into the target document. 82 * @param parent The <strong>Node</strong> we are inserting into. 83 * @param child The original <strong>Node</strong> we are inserting. Must first be cloned into the parents document. 84 */ 85 static final public void add(Node parent, Node child) { 86 Document document = parent.getOwnerDocument(); 87 Node new_child = document.importNode(child, true); 88 parent.appendChild(new_child); 89 } 90 91 static final public void addElementAttribute(Node node, String name, String language, String value) { 92 Document document = node.getOwnerDocument(); 93 Element attribute_node = document.createElementNS("", "Attribute"); 94 attribute_node.setAttribute("name", name); 95 attribute_node.setAttribute("language", language); 96 node.appendChild(attribute_node); 97 Node attribute_text = document.createTextNode(value); 98 attribute_node.appendChild(attribute_text); 99 } 100 101 /** A method for comparing two AssignedValues trees. This compares not only the Subject hierarchies but also the values themselves. 102 * @param avt A <strong>Node</strong> which is the root of an AssignedValues tree. 103 * @param bvt The <strong>Node</strong> which is the root of the tree we wish to compare it to. 104 * @return <i>true</i> if the two trees are equal, <i>false</i> otherwise. 105 */ 106 static final public boolean assignedValuesEqual(Node avt, Node bvt) { 107 if(avt == null && bvt == null) { 108 return true; // Both are null so both are equal. 109 } 110 else if(avt == null || bvt == null) { 111 111 // One is null and the other isn't. 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 112 return false; 113 } 114 else { 115 Hashtable a_map = new Hashtable(); 116 getValueMappings(avt, null, a_map); 117 Hashtable b_map = new Hashtable(); 118 getValueMappings(bvt, null, b_map); 119 if(a_map.size() == b_map.size()) { 120 /** @TODO - Figure out what to do now. */ 121 return true; 122 } 123 } 124 return false; 125 } 126 /** A method for comparing two attribute nodes of type Node not Attr as you might think. Attr objects are used to describe the attributes of tags themselves, while in a metadata set we intend attribute nodes to describe qualities of metadata elements. It's just confusing because the two systems (DOM model and Dublin Core) are quite similar. 127 * @param an A <strong>Node</strong> representing some attribute of an element. 128 * @param bn The <strong>Node</strong> we wish to compare it to. 129 * @return <i>true</i> if and only if the attributes are equal. 130 */ 131 static final public boolean attributesEqual(Node an, Node bn) { 132 // Check we are comparing apples and apples... 133 if(an.getNodeName().equals("Attribute") && bn.getNodeName().equals("Attribute")) { 134 Element ae = (Element) an; 135 Element be = (Element) bn; 136 136 // Ensure we are comparing the same type of attribute. 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 137 if(ae.getAttribute("name").equals(be.getAttribute("name"))) { 138 // And finally retrieve and compare the values. 139 if(getValue(ae).equals(getValue(be))) { 140 // We have a match. 141 return true; 142 } 143 } 144 } 145 // And if anything goes wrong we can't be dealing with equal attributes. 146 return false; 147 } 148 /** Method to determine if a certain path of elements can be found in another tree. This method tests by comparing node names, and finally the #text node past the leaf end of the path. 149 * @param tree The root <strong>Node</strong> of the tree to be searched. 150 * @param path The <strong>Node[]</strong> path to be found. 151 * @return <i>true</i> if the path was found (including matching #text elements at the leaf), or <i>false</i> otherwise. 152 */ 153 static final public boolean containsPath(Node tree, Node path[]) { 154 // If there is no tree then there are no values. 155 if(tree == null) { 156 return false; 157 } 158 // Ensure we are comparing equivent things. 159 if(tree.getNodeName().equals("AssignedValues") && path[0].getNodeName().equals("AssignedValues")) { 160 int index = 1; 161 while(index < path.length && tree != null) { 162 Node next = null; 163 for(Node n = tree.getFirstChild(); n != null && next == null; n = n.getNextSibling()) { 164 if(n.getNodeName().equals(path[index].getNodeName())) { 165 next = n; 166 index++; 167 } 168 tree = next; 169 } 170 } 171 171 // Tree may now be pointing at the same node as 172 172 // path[path.length - 1] so we should test their child text nodes. 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 173 if(tree != null) { 174 Node tree_text = tree.getFirstChild(); 175 Node path_text = path[path.length - 1].getFirstChild(); 176 if(tree_text.getNodeValue().equals(path_text.getNodeValue())) { 177 // Path found! 178 return true; 179 } 180 } 181 } 182 return false; 183 } 184 /** Method to compare two metadata elements (of type Element, which is bound to get more than a bit confusing) for equality. This test may only check the structural (ie pretty much unchanging) consistancy, or may include the AssignedValue tree as well (which will be different for each collection I'd imagine). 185 * @param a_set The <strong>MetadataSet</strong> a comes from. 186 * @param a An <strong>Element</strong>. 187 * @param b_set The <strong>MetadataSet</strong> b comes from. 188 * @param b The <strong>Element</strong> to compare it to. 189 * @param values <i>true</i> if the AssignedValues tree should also be compared, <i>false</i> otherwise. 190 * @return <i>true</i> if the elements are equal, <i>false</i> otherwise. 191 */ 192 static final public boolean elementsEqual(MetadataSet a_set, Element ae, MetadataSet b_set, Element be, boolean values) { 193 // Compare Element Attr(ibutes) within the DOM, not to be confused with comparing element attributes in a Dublin Core sense... 194 NamedNodeMap aas = ae.getAttributes(); 195 NamedNodeMap bas = be.getAttributes(); 196 // For each attribute in a... 197 for(int i = 0; i < aas.getLength(); i++) { 198 Attr aa = (Attr)aas.item(i); 199 199 // Try to retrieve an attribute of the same name from b. 200 200 Attr ba = (Attr)bas.getNamedItem(aa.getNodeName()); 201 201 // Now if there was no such attribute, or if the values for the 202 202 // two attributes are different the structures different. 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 203 if(ba == null || (!aa.getValue().equals(ba.getValue()))) { 204 //ystem.err.println("Attributes are not equal"); 205 return false; 206 } 207 } 208 // Quickest test of children is to see we have the same number in 209 // each. Remember to modify for missing AssignedValues which have 210 // nothing to do with structure. 211 int anc = getAttributeCount(ae); 212 int bnc = getAttributeCount(be); 213 if(anc != bnc) { 214 return false; 215 } 216 // Now we compare the child nodes of the two Elements taking into 217 // account three special cases... 218 // 1. We don't test the AssignedValues element here. 219 // 2. Remember OptionList node. 220 // 3. The Attributes of each metadata element. 221 // For each child node of a. 222 for(Node an = ae.getFirstChild(); an !=null; an =an.getNextSibling()) { 223 if(an.getNodeName().equals("OptionList")) { 224 //ystem.err.println("Matching OptionLists."); 225 Node bn = getNodeFromNamed(be, "OptionList"); 226 if(bn == null || !optionListsEqual(an, bn)) { 227 //ystem.err.println("OptionLists are not equal"); 228 return false; 229 } 230 } 231 231 // Matching attributes. 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 232 else if(an.getNodeName().equals("Attribute")) { 233 //ystem.err.println("Matching Attributes."); 234 boolean matched = false; 235 for(Node bn = be.getFirstChild(); bn != null && !matched; 236 bn = bn.getNextSibling()) { 237 if(bn.getNodeName().equals("Attribute")) { 238 matched = attributesEqual(an, bn); 239 } 240 } 241 if(!matched) { 242 //ystem.err.println("Cannot match attribute."); 243 return false; 244 } 245 } 246 } 247 // Finally, if we've been asked to compares value trees (for some unknown reason) go ahead and compare them too. 248 if(values) { 249 GValueModel avt = a_set.getValueTree(new ElementWrapper(ae)); 250 GValueModel bvt = b_set.getValueTree(new ElementWrapper(be)); 251 return assignedValuesEqual(avt.getDocument().getDocumentElement(), bvt.getDocument().getDocumentElement()); 252 } 253 // If we've got this far the elements match! 254 return true; 255 } 256 /** This method extracts the assigned value trees details, if any, from a certain element and stores them in an array ready to be passed as arguments to the Dictionary. 257 * @param element The <strong>Element</strong> whose values we wish to view. 258 * @return A <strong>String[]</strong> containing the details of the assigned values tree. 259 * @see org.greenstone.gatherer.Dictionary 260 */ 261 static final public String[] getAssignedValuesDetails(MetadataSet mds, Element element) { 262 String details[] = null; 263 //Node avt = getNodeFromNamed(element, "AssignedValues"); 264 GValueModel avt = mds.getValueTree(new ElementWrapper(element)); 265 if(avt != null) { 266 Hashtable mapping = new Hashtable(); 267 getValueMappings(avt.getDocument().getDocumentElement(), null, mapping); 268 ArrayList values = new ArrayList(mapping.keySet()); 269 Collections.sort(values); 270 details = new String[1]; 271 for(int i = 0; i < values.size(); i++) { 272 if(details[0] == null) { 273 details[0] = " " + values.get(i); 274 } 275 else { 276 details[0] = details[0] + "\n " + values.get(i); 277 } 278 } 279 mapping = null; 280 values = null; 281 } 282 avt = null; 283 return details; 284 } 285 286 static final public TreeSet getAttributes(Element element) { 287 TreeSet attributes = new TreeSet(); 288 for(Node n = element.getFirstChild(); n != null; n = n.getNextSibling()) { 289 if(n.getNodeName().equals("Attribute")) { 290 Element e = (Element)n; 291 attributes.add(new Attribute(e.getAttribute("name"), e.getAttribute("language"), getValue(e))); 292 } 293 } 294 return attributes; 295 } 296 297 /** Method to count the number of Attribute nodes under a certain Element. This ignores other nodes such as #text, OptionList and AssignedValues nodes. 298 * @param element The <strong>Element</strong> whose attributes you want to count. 299 * @return An <i>int</i> which is the number of attribute nodes. 300 */ 301 static final public int getAttributeCount(Node element) { 302 int count = 0; 303 for(Node n = element.getFirstChild(); n != null; 304 n = n.getNextSibling()) { 305 if(n.getNodeName().equals("Attribute")) { 306 count++; 307 } 308 } 309 return count; 310 } 311 312 /*************************************************************************/ 313 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 314 /** This method is a slight variation on getNodeNamed in that it is especially written to retrieve the attribute Nodes of a certain name present under the given element. Note that this method is language specific. 315 * @param element The target element <strong>Node</strong>. 316 * @param name The name of the attribute you wish to return. 317 * @return An <strong>Element[]</strong> containing the attributes you requested, or <i>null</i> if no such attributes exists. 318 */ 319 static final public Element getAttributeNodeNamed(Node element, String name) { 320 Element attribute = null; 321 String language_code = Locale.getDefault().getLanguage(); 322 for(Node n = element.getFirstChild(); n != null; n = n.getNextSibling()) { 323 if(n.getNodeName().equals("Attribute")) { 324 Element e = (Element)n; 325 if(e.getAttribute("name").equals(name)) { 326 if(e.getAttribute("language").equalsIgnoreCase(language_code)) { 327 return e; 328 } 329 else if(e.getAttribute("language").equalsIgnoreCase("en")) { 330 attribute = e; 331 } 332 else if(attribute == null) { 333 attribute = e; 334 } 335 } 336 e = null; 337 } 338 } 339 return attribute; 340 } 341 342 /** This method is a slight variation on getNodeNamed in that it is especially written to retrieve the attribute Nodes of a certain name present under the given element. 343 * @param element The target element <strong>Node</strong>. 344 * @param name The name of the attribute you wish to return. 345 * @return An <strong>Element[]</strong> containing the attributes you requested, or <i>null</i> if no such attributes exists. 346 */ 347 static final public Element[] getAttributeNodesNamed(Node element, String name) { 348 Element attributes[] = null; 349 for(Node n = element.getFirstChild(); n != null; n = n.getNextSibling()) { 350 if(n.getNodeName().equals("Attribute")) { 351 Element e = (Element)n; 352 if(e.getAttribute("name").equals(name)) { 353 if(attributes == null) { 354 attributes = new Element[1]; 355 attributes[0] = e; 356 } 357 else { 358 Element temp[] = attributes; 359 attributes = new Element[temp.length + 1]; 360 System.arraycopy(temp, 0, attributes, 0, temp.length); 361 attributes[temp.length] = e; 362 temp = null; 363 } 364 } 365 e = null; 366 } 367 } 368 return attributes; 369 } 370 371 /** Method to construct an elements description by retrieving the correct attribute. 372 * @param element The <strong>Element</strong> whose name we wish to retrieve. 373 * @return A <strong>String</strong> which is the elements description, or an empty string if no description exists. 374 */ 375 static final public String getDescription(Node element) { 376 String definition = ""; 377 Element definition_node = getAttributeNodeNamed(element, "definition"); 378 if(definition_node != null) { 379 definition = getValue(definition_node); 380 } 381 String comment = ""; 382 Element comment_node = getAttributeNodeNamed(element, "comment"); 383 if(comment_node != null) { 384 comment = getValue(comment_node); 385 } 386 String description = definition + comment; 387 return Utility.stripNL(description.trim()); 388 } 389 390 /** Extracts the file name pattern from within a fileset of a Greenstone Directory Metadata model. 391 * @param fileset The fileset Node in question. 392 * @return The pattern as a String. 393 */ 394 static final public String getFileNamePattern(Node fileset) { 395 // Locate the child node called filename 396 for(Node child = fileset.getFirstChild(); child != null; child = child.getNextSibling()) { 397 if(child.getNodeName().equalsIgnoreCase("FileName")) { 398 // Find the file string. 399 return MSMUtils.getValue(child); 400 } 401 } 402 return null; 403 } 404 405 /*************************************************************************/ 406 /** Method to create the fully namespace quantified identifier for this element. 407 * @param element The <strong>Node</strong> in question. 408 * @return A fully qualified identifier as a <strong>String</strong> 409 */ 410 static final public String getFullIdentifier(Node element) { 411 if(element == null) { 412 return "Error"; 413 } 414 // Get namespace for given element. 415 Element root = (Element)element.getParentNode(); 416 if(root != null) { 417 String identifier = root.getAttribute("namespace"); 418 418 // Get name and return it. 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 419 if(identifier.equals("")) { 420 identifier = Utility.EXTRACTED_METADATA_NAMESPACE; 421 } 422 return identifier + NS_SEP + getIdentifier(element); 423 } 424 return getIdentifier(element); // No namespace anymore 425 } // static public String getFullIdentier(Node element) 426 427 /*************************************************************************/ 428 /** Method to construct an elements fully qualified name. Note that this is different from a nodes identifier. Think of name as a short, unique reference to an metadata element, whereas identifier can be much longer, language specific and non-unique. 429 * @param element An <strong>Element</strong> whose name we are interested in. 430 * @return A <strong>String</strong> representing this given elements fully namespace qualified name. 431 */ 432 static final public String getFullName(Element element) { 433 if(element == null) { 434 return null; 435 } 436 // Get namespace for given element. 437 Element root = (Element)element.getParentNode(); 438 String identifier = root.getAttribute("namespace") + NS_SEP + element.getAttribute("name"); 439 // Get name and return it. 440 if(identifier.startsWith(".")) { 441 identifier = identifier.substring(1); 442 } 443 return identifier; 444 } // static public String getFullName(Element element) 445 446 /** Method to construct an elements identifier by retrieving the correct attribute. Language specific, based on default Locale. 447 * @param element The <strong>Element</strong> whose name we wish to retrieve. 448 * @return A <strong>String</strong> which is the elements identifier, or an empty string if no identifier exists. 449 */ 450 static final public String getIdentifier(Node element) { 451 String identifier = null; 452 // Determine locale code. 453 String language_code = Locale.getDefault().getLanguage(); 454 // Get the 'identifier' Element with the correct locale 455 for(Node node = element.getFirstChild(); node != null; 456 node = node.getNextSibling()) { 457 if(node.getNodeName().equals("Attribute")) { 458 Element target = (Element)node; 459 if(target.getAttribute("name").equals("identifier")) { 460 Node text = target.getFirstChild(); 461 if(target.getAttribute("language").equalsIgnoreCase(language_code)) { 462 return text.getNodeValue(); 463 } 464 else if(target.getAttribute("language").equalsIgnoreCase("en")) { 465 identifier = text.getNodeValue(); 466 } 467 else if(identifier == null) { 468 identifier = text.getNodeValue(); 469 } 470 text = null; 471 } 472 target = null; 473 } 474 } 475 language_code = null; 476 // We may have harvested some identifier from the file. 477 if(identifier != null) { 478 return identifier; 479 } 480 // Failing the above we return the nodes name instead. 481 return ((Element)element).getAttribute("name"); 482 } 483 484 /** Retrieve the metadata description element from this fileset node. 485 * @param fileset The fileset in question. 486 * @return The description node or null if no such node. 487 */ 488 static final public Node getMetadataDescription(Node fileset) { 489 // Locate the child node called filename 490 for(Node child = fileset.getLastChild(); child != null; child = child.getPreviousSibling()) { 491 if(child.getNodeName().equalsIgnoreCase("Description")) { 492 return child; 493 } 494 } 495 return null; 496 } 497 498 /** Method to retrieve from the node given, a certain child node with the specified name. 499 * @param parent The <strong>Node</strong> whose children should be searched. 500 * @param name The required nodes name as a <strong>String</strong>. 501 * @return The requested <strong>Node</strong> if it is found, <i>null</i> otherwise. 502 */ 503 static final public Node getNodeFromNamed(Node parent, String name) { 504 Node child = null; 505 for(Node i = parent.getFirstChild(); i != null && child == null; 506 i = i.getNextSibling()) { 507 if(i.getNodeName().equals(name)) { 508 child = i; 509 } 510 } 511 return child; 512 } 513 /** Look for the occurances 'field' of the element and return it if found. 514 * @return An <i>int</i> which matches the number in the occurances attribute of the element, or 0 if no such attribute. 515 */ 516 static final public int getOccurances(Element element) { 517 int count = 0; 518 String number = null; 519 if((number = element.getAttribute("occurances")) != null) { 520 try { 521 count = Integer.parseInt(number); 522 } 523 catch(Exception error) { 524 count = 0; 525 } 526 } 527 return count; 528 } 529 /** This method extracts the option list details, if any, from a certain element and stores them in an array ready to be passed as arguments to the <strong>Dictionary</strong>. 530 * @param element The <strong>Element</strong> whose option list we wish to view. 531 * @return A <strong>String[]</strong> containing the details of the option list. 532 * TODO implement. 533 * @see org.greenstone.gatherer.Dictionary 534 */ 535 static final public String[] getOptionListDetails(Element element) { 536 return null; 537 } 538 539 /** This method traces the path between the 'root' node given and the target node. This path is returned starting at the root. 540 * @param root The root <strong>Node</strong> to start this path at. 541 * @param target The final <strong>Node</strong> to end up at. 542 * @return A <strong>Node[]</strong> that contains the sequence of nodes from root to target. 543 */ 544 static final public Node[] getPath(Node root, Node target) { 545 if(root == target) { 546 return ArrayTools.add(null, root); 547 } 548 else { 549 return ArrayTools.add(getPath(root, target.getParentNode()), target); 550 } 551 } 552 553 /** This method extracts the structural details from a certain element and stores them in an array, all ready for passing to the <strong>Dictionary</strong>. 554 * @param element The <Strong>Element</strong> whose details we wish to gather. 555 * @return A <strong>String[]</strong> containing the structural details. 556 */ 557 static final public String[] getStructuralDetails(Element element) { 558 String details[] = new String[4]; 559 Element root = (Element)element.getParentNode(); 560 details[0] = root.getAttribute("name"); 561 details[1] = root.getAttribute("namespace"); 562 details[2] = getFullName(element); 563 details[3] = null; 564 // Get attributes 565 Vector attributes = new Vector(); 566 for(Node n=element.getFirstChild(); n!=null; n=n.getNextSibling()) { 567 if(n.getNodeName().equals("Attribute")) { 568 Element temp = (Element)n; 569 attributes.add(temp.getAttribute("name") + "=" + getValue(n)); 570 } 571 } 572 // Sort attributes 573 Collections.sort(attributes); 574 // Add attributes to details. 575 for(int i = 0; i < attributes.size(); i++) { 576 if(details[3] == null) { 577 details[3] = " " + attributes.get(i); 578 } 579 else { 580 details[3] = details[3] + "\n " + attributes.get(i); 581 } 582 } 583 return details; 584 } 585 586 /** Method to retrieve the value of a given node (not the assigned values tree!). 587 * @param element The <strong>Element</strong> whose value we wish to find. 588 * @return The value found as a <strong>String</strong>, or <i>null</i> if this element has no value. 589 */ 590 static final public String getValue(Node element) { 591 // If we've been given a subject node first retrieve its value node. 592 if(element.getNodeName().equals("Subject")) { 593 element = getNodeFromNamed(element, "Value"); 594 } 595 if(element != null && element.hasChildNodes()) { 596 Node text = element.getFirstChild(); 597 return text.getNodeValue(); 598 } 599 return ""; 600 } 601 602 /** Method to traverse the given value tree, and build up a hashtable of mappings between the value path key names and the Subject nodes of the tree. 603 * @param current The root <strong>Node</strong> of a subtree of the AssignedValues tree. 604 * @param prefix The value path key <strong>String</string>, which shows the path from the root of the AssignedValue tree to <i>current</i>s parent using '\' as a separator. 605 * @param values A <strong>Hashtable</strong> containing the mapping discovered so far in our tree traversal. 606 */ 607 static final public void getValueMappings(Node current, String prefix, Hashtable values) { 608 if(current != null) { 609 String name = current.getNodeName(); 610 String new_prefix = prefix; 611 611 // If we've found the outer layer of a new value, add it to our 612 612 // mapping. 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 613 if(name.equals("Subject")) { 614 Node value_node = getNodeFromNamed(current, "Value"); 615 String value = getValue(value_node); 616 if(new_prefix != null) { 617 new_prefix = new_prefix + "\\" + value; 618 } 619 else { 620 new_prefix = value; 621 } 622 values.put(new_prefix, current); 623 } 624 if(name.equals("Subject") || name.equals("AssignedValues")) { 625 for(Node child = current.getFirstChild(); child != null; 626 child = child.getNextSibling()) { 627 getValueMappings(child, new_prefix, values); 628 } 629 } 630 } 631 } 632 /** Parses the value tree template file. 633 * @return The Document parsed. 634 */ 635 static final public Document getValueTreeTemplate() { 636 return Utility.parse(Utility.METADATA_VALUE_TEMPLATE, true); 637 } 638 /** Method to compare two OptionsLists for equality. 639 * @param al A <strong>Node</strong> which represents an OptionList. 640 * @param bl The <strong>Node</strong> we wish to test against. 641 * @return A <i>boolean</i> which is <i>true</i> if the two option lists are equal, <i>false</i> otherwise. 642 * TODO Implementation 643 */ 644 static final public boolean optionListsEqual(Node al, Node bl) { 645 // Compare the 'restricted' attribute of the two lists. 646 Element ae = (Element) al; 647 Element be = (Element) bl; 648 if(!ae.getAttribute("restricted").equals 649 (be.getAttribute("restricted"))) { 650 return false; 651 } 652 // Compare the Values under each list. 653 for(Node an = al.getFirstChild(); an != null; 654 an = an.getNextSibling()){ 655 if(an.getNodeName().equals("Value")) { 656 boolean matched = false; 657 for(Node bn = bl.getFirstChild(); bn != null && !matched; 658 bn = bn.getNextSibling()) { 659 if(bn.getNodeName().equals("Value")) { 660 matched = valuesEqual(an, bn); 661 } 662 } 663 if(!matched) { 664 return false; 665 } 666 } 667 } 668 return true; 669 } 670 671 static final public void setIdentifier(Node element, String value) { 672 // Get the 'identifier' Element 673 for(Node node = element.getFirstChild(); node != null; 674 node = node.getNextSibling()) { 675 if(node.getNodeName().equals("Attribute")) { 676 Element target = (Element)node; 677 if(target.getAttribute("name").equals("identifier")) { 678 Node text = target.getFirstChild(); 679 text.setNodeValue(value); 680 } 681 } 682 } 683 } 684 /** Set the value of the element attribute occurances. 685 * @param element The <strong>Element</strong> to change. 686 * @param value The value to change by as an <i>int</i>. 687 */ 688 static final public void setOccurance(Element element, int value) { 689 Integer new_value = new Integer(getOccurances(element) + value); 690 element.setAttribute("occurances", new_value.toString()); 691 } 692 /** This method also traverses the tree, but this one is used to gather all the values and aliases at once, and to 'prune' the tree if necessary. 693 * @param current The current root <strong>Node</strong> of this AssignedValues tree subtree. 694 * @param return_filter This <i>int</i> specifies what nodes from the tree should be returned, where;<br>VALUES = return values only<br>ALIASES = return aliases only<br>BOTH = return both values and aliases<br>NONE = return nothing. 695 * @param remove_leaves A leaf node is a subject that contains no child subjects. If this <i>boolean</i> is set to <i>true</i> then the leaf nodes will be removed from the tree. 696 * @return A <strong>Node[]</strong> containing whatever values or aliases have been found during the trees traversal. 697 */ 698 static final public Node[] traverseTree(Node current, int return_filter, boolean remove_leaves) { 699 Node leaves[] = null; 700 String name = current.getNodeName(); 701 if(name.equals("Value") && (return_filter == VALUES || return_filter == BOTH)) { 702 leaves = ArrayTools.add(leaves, current); 703 } 704 else if(name.equals("Alias") && (return_filter == ALIASES || return_filter == BOTH)) { 705 leaves = ArrayTools.add(leaves, current); 706 } 707 else if(name.equals("Subject")) { 708 boolean has_subject_child = false; 709 Node children[] = ArrayTools.nodeListToNodeArray(current.getChildNodes()); 710 for(int i = 0; i < children.length; i++) { 711 if(children[i].getNodeName().equals("Subject")) { 712 has_subject_child = true; 713 } 714 leaves = ArrayTools.add(leaves, traverseTree(children[i], return_filter, remove_leaves)); 715 } 716 if(!has_subject_child && remove_leaves) { 717 Node parent = current.getParentNode(); 718 parent.removeChild(current); 719 } 720 } 721 else if(name.equals("AssignedValues")) { 722 Node children[] = ArrayTools.nodeListToNodeArray(current.getChildNodes()); 723 for(int i = 0; i < children.length; i++) { 724 leaves = ArrayTools.add(leaves, traverseTree(children[i], return_filter, remove_leaves)); 725 } 726 } 727 return leaves; 728 } 729 730 /** This method is used to systematically merge two AssignedValues tree. Both trees have their current values mapped, then the new tree is searched for key paths that don't exist in the current tree. If such a key is found, the Subject <strong>Node</strong> it maps to is retrieved and then imported and added to whatever was the closest available node (in terms of tree path) in the current tree. 731 * @param a_set The MetadataSet from which the Element a came from. 732 * @param a The Element at the root of the current AssignedValues tree. 733 * @param b_set The MetadataSet from which the Element b came from. 734 * @param b The root Element of the tree that is being merged. 735 * @return A <i>boolean</i> which is <i>true</i> if the trees merged without error, <i>false</i> otherwise. 736 */ 737 static final public boolean updateValueTree(MetadataSet a_set, Element a, MetadataSet b_set, Element b) { 738 GValueModel avt = a_set.getValueTree(new ElementWrapper(a)); 739 GValueModel bvt = b_set.getValueTree(new ElementWrapper(b)); 740 // If neither element even has a value tree, we're all done. 741 if(avt == null && bvt == null) { 742 avt = null; 743 bvt = null; 744 return true; 745 } 746 // If the new element has no value tree then nothing needs to be done. 747 else if(avt != null && bvt == null) { 748 avt = null; 749 bvt = null; 750 return true; 751 } 752 // If only the new element has a value tree, then add all of its values 753 // immediately. 754 else if(avt == null && bvt != null) { 755 a_set.addValueTree(new ElementWrapper(a), bvt); 756 avt = null; 757 bvt = null; 758 return true; 759 } 760 // We have both trees for both elements, time to merge. 761 else { 762 Document document = avt.getDocument(); 763 Hashtable a_map = new Hashtable(); 764 getValueMappings(document.getDocumentElement(), null, a_map); 765 Hashtable b_map = new Hashtable(); 766 getValueMappings(bvt.getDocument().getDocumentElement(), null, b_map); 767 767 // For each new entry in b_map 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 768 for(Enumeration b_keys = b_map.keys(); b_keys.hasMoreElements(); ) { 769 String b_key = (String)b_keys.nextElement(); 770 // Test if there is already an entry in a_map. 771 if(!a_map.containsKey(b_key)) { 772 // If not, search through a_map for the longest match. 773 Node target = document.getDocumentElement(); 774 String last_match = null; 775 for(Enumeration a_keys = a_map.keys(); 776 a_keys.hasMoreElements(); ) { 777 String a_key = (String)a_keys.nextElement(); 778 if(b_key.startsWith(a_key)) { 779 if(last_match == null || a_key.length() > last_match.length()) { 780 last_match = a_key; 781 target = (Node)a_map.get(a_key); 782 } 783 } 784 a_key = null; 785 } 786 // Now import the node at b_key and add it to target. 787 Node subtree = (Node)b_map.get(b_key); 788 subtree = document.importNode(subtree, true); 789 // Find the node to insert before... 790 String name = getValue(subtree); 791 Node move = null; 792 for(Node n = target.getFirstChild(); n != null && move == null; n = n.getNextSibling()) { 793 if(n.getNodeName().equals("Subject")) { 794 if(name.compareTo(getValue(n)) <= 0) { 795 move = n; 796 } 797 } 798 } 799 if(move == null) { 800 target.appendChild(subtree); 801 } 802 else { 803 target.insertBefore(subtree, move); 804 } 805 target = null; 806 last_match = null; 807 subtree = null; 808 name = null; 809 move = null; 810 } 811 b_key = null; 812 } 813 document = null; 814 a_map = null; 815 b_map = null; 816 avt = null; 817 bvt = null; 818 return true; 819 } 820 } 821 822 /** Method to determine if two Value nodes are equal. 823 * @param av A <strong>Node</strong> representing a value. 824 * @param bv The <strong>Node</strong> we want to compare it to. 825 * @return A <i>boolean</i> which is <i>true</i> if the two value nodes are equal, <i>false</i> otherwise. 826 */ 827 static final public boolean valuesEqual(Node av, Node bv) { 828 // Check we are comparing apples and apples... 829 if(av.getNodeName().equals("Value") && 830 bv.getNodeName().equals("Value")) { 831 831 // Retrieve and then compare their text values. 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 832 Node at = av.getFirstChild(); 833 Node bt = bv.getFirstChild(); 834 if(at.getNodeValue().equals(bt.getNodeValue())) { 835 return true; 836 } 837 } 838 return false; 839 } 840 841 /** A comparator for sorting metadata element-value pairs into their standard order (elements) then alphabetical order (values). */ 842 static final private class MetadataComparator 843 implements Comparator { 844 /** Compares its two arguments for order. */ 845 public int compare(Object o1, Object o2) { 846 int result = 0; 847 Metadata m1 = (Metadata) o1; 848 Metadata m2 = (Metadata) o2; 849 849 ///ystem.err.println("MSMUtils.compare(" + m1 + ", " + m2 + ") = "); 850 851 850 ElementWrapper e1 = m1.getElement(); 851 ElementWrapper e2 = m2.getElement(); 852 852 // First we compare the namespaces 853 854 855 856 857 858 859 860 861 862 863 864 853 result = e1.getNamespace().compareTo(e2.getNamespace()); 854 if(result == 0) { 855 // Now, given both elements are in the same set, we compare the element ordering using methods in MetadataSet 856 MetadataSet set = Gatherer.c_man.getCollection().msm.getSet(e1.getNamespace()); 857 ///ystem.err.print("MetadataSet.compare(" + e1 + ", " + e2 + ") = "); 858 result = set.compare(e1.getElement(), e2.getElement()); 859 ///ystem.err.println(result); 860 if(result == 0) { 861 // Finally we compare the values alphabetically. 862 result = m1.getValue().compareTo(m2.getValue()); 863 } 864 } 865 865 ///ystem.err.println("Result: " + result); 866 867 866 return result; 867 } 868 868 869 870 871 872 873 869 /** Indicates whether some other object is "equal to" this Comparator. */ 870 public boolean equals(Object obj) { 871 return compare(this, obj) == 0; 872 } 873 } 874 874 } -
trunk/gli/src/org/greenstone/gatherer/msm/Metadata.java
r4293 r4365 50 50 */ 51 51 public class Metadata 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 */ 68 69 70 71 72 73 52 implements Comparable { 53 /** Is this an accumulating piece of metadata? */ 54 transient private boolean accumulate = false; 55 /** Indicates what level of metadata this is, relative to the file it was retrieved for. */ 56 transient private boolean file_level = true; 57 /** The file for which this metadata was retrieved. This allows us to determine the source of folder level metadata. */ 58 transient private File file; 59 /** The value node this metadata maps to. */ 60 private GValueNode value = null; 61 /** Used to count the number of references to this piece of metadata within the metadata table. */ 62 private int count = 1; 63 /** The metadata element this metadata maps to. */ 64 private ElementWrapper element = null; 65 /** Constructs a new Metadata object with no current value. 66 * @param element The <strong>ElementWrapper</strong> associated with this metadata. 67 */ 68 public Metadata(ElementWrapper element) { 69 super(); 70 this.element = element; 71 this.value = null; 72 } 73 /** Default constructor simply creates a new Metadata object based on the given parameters. 74 74 * @param element The <strong>ElementWrapper</strong> associated with this metadata. 75 75 * @param value The assigned value for the given element, as a <strong>GValueNode</strong>. 76 76 */ 77 78 79 80 81 82 77 public Metadata(ElementWrapper element, GValueNode value) { 78 super(); 79 this.element = element; 80 this.value = value; 81 } 82 /** Constructs a new Metadata object, given the value. The metadata element is extracted from the value tree information. 83 83 * @param value The assigned value for the given element, as a <strong>GValueNode</strong>. 84 84 * @see org.greenstone.gatherer.Gatherer … … 86 86 * @see org.greenstone.gatherer.msm.MetadataSetManager 87 87 */ 88 89 90 91 92 93 94 95 96 97 88 public Metadata(GValueNode value) { 89 super(); 90 this.element = Gatherer.c_man.getCollection().msm.getElement(value.getMetadataElementName()); 91 this.value = value; 92 } 93 /** Determine if this metadata overwrites or accumulates. */ 94 public boolean accumulates() { 95 return accumulate; 96 } 97 /** Compares two Metadata objects for ordering purposes. 98 98 * @param object The other metadata as an <strong>Object</strong>. 99 99 * @return An <i>int</i> value as specified in java.lang.String#compareTo 100 100 * @see java.lang.String#compareTo 101 101 */ 102 103 104 105 106 107 108 109 102 public int compareTo(Object object) { 103 return toString().compareTo(object.toString()); 104 } 105 /** Decrease the reference count by 1. */ 106 public void dec() { 107 count--; 108 } 109 /** Tests to metadata objects for equality. 110 110 * @param object The other metadata as an <strong>Object</strong>. 111 111 * @return <i>true</i> if the metadata are equal, <i>false</i> otherwise. 112 112 */ 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 113 public boolean equals(Object object) { 114 if(compareTo(object) == 0) { 115 return true; 116 } 117 return false; 118 } 119 /** Determine the absolute path value of this metadata, taking into account whether this value is hierarchical. */ 120 public String getAbsoluteValue() { 121 String abs_value = getValue(); 122 // What actually gets written as the value depends on whether this is a hierarchy based element. 123 GValueModel model = Gatherer.c_man.getCollection().msm.getValueTree(element); 124 if(model != null && model.isHierarchy()) { 125 if(value != null) { 126 GValueNode node = (GValueNode)value.getParent(); 127 while(node != null) { 128 GValueNode next = (GValueNode)node.getParent(); 129 if(next != null) { 130 abs_value = node.toString() + "\\" + abs_value; 131 } 132 node = next; 133 } 134 } 135 abs_value = model.getHIndex(abs_value); 136 } 137 // Return the result 138 return abs_value; 139 } 140 /** Retrieve the reference count. 141 141 * @return The count as an <i>int</i>. 142 142 */ 143 144 145 146 143 public int getCount() { 144 return count; 145 } 146 /** Retrieve the element associated with this metadata. 147 147 * @return An <strong>ElementWrapper</strong>. 148 148 */ 149 150 151 152 153 154 155 156 157 158 149 public ElementWrapper getElement() { 150 return element; 151 } 152 153 /** Retrieve the source file if any. */ 154 public File getFile() { 155 return file; 156 } 157 158 /** Get the textual value of this metadata. 159 159 * @return A <strong>String</strong> representing the value of this Metadata. Note that if the value node is null, then the String returned is "". 160 160 */ 161 162 163 164 165 166 167 168 161 public String getValue() { 162 String result = ""; 163 if(value != null) { 164 result = value.toString(); 165 } 166 return result; 167 } 168 /** Retrieve the value node associated with this metadata. 169 169 * @return A <strong>GValueNode</strong>. 170 170 */ 171 172 173 174 175 176 177 171 public GValueNode getValueNode() { 172 return value; 173 } 174 /** Increase the reference count by 1. */ 175 public void inc() { 176 count++; 177 } 178 178 179 180 181 182 183 184 185 186 187 188 179 /** Determine if this metadata is file level or folder level. */ 180 public boolean isFileLevel() { 181 return file_level; 182 } 183 184 /** Inform this metadata whether it will accumulate with any other metadata of the same type. */ 185 public void setAccumulate(boolean accumulate) { 186 this.accumulate = accumulate; 187 } 188 /** Sets the reference count. 189 189 * @param value The new value of count as an <i>int</i>. 190 190 */ 191 192 193 194 195 196 197 198 199 200 201 202 203 204 191 public void setCount(int value) { 192 count = value; 193 } 194 /** Set the level of this metadata (whether it is associated with this file explicitly or found in some folder above this file). */ 195 public void setFileLevel(boolean file_level) { 196 this.file_level = file_level; 197 } 198 199 /** Set the current source file for this metadata to the given collection. This value should not be counted are remaining valid at any time other than immediately after a getMetadata call as it is relative the the file that metadata call was for. */ 200 public void setFile(File file) { 201 this.file = file; 202 } 203 204 /** Translates this object into a string representation. 205 205 * @return A <strong>String</strong>. 206 206 */ 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 207 public String toString() { 208 return element.toString() + "=" + getValue(); 209 } 210 /** Retrieve the value node associated with a certain value string. */ 211 static final public GValueNode getDefaultValueNode(ElementWrapper element, String value) { 212 // Retrieve the GValueNode 213 GValueModel model = Gatherer.c_man.getCollection().msm.getValueTree(element); 214 GValueNode value_node; 215 if(model != null) { 216 value_node = model.getValue(value); 217 } 218 else { 219 value_node = new GValueNode(element.getName(), value); 220 } 221 model = null; 222 return value_node; 223 } 224 224 } -
trunk/gli/src/org/greenstone/gatherer/msm/MetadataComboBoxModel.java
r4293 r4365 53 53 /** A combobox model that contains information from an array of NodeLists. */ 54 54 public class MetadataComboBoxModel 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 55 extends DefaultComboBoxModel 56 implements MSMListener { 57 private MetadataSetManager msm = null; 58 /** Constructor. 59 * @param nodelists The <strong>NodeList[]</strong> this model is proxied to. 60 */ 61 public MetadataComboBoxModel(MetadataSetManager msm) { 62 super(); 63 this.msm = msm; 64 init(); 65 } 66 /** Called whenever a metadata element is changed. 67 * @param event A <strong>MSMEvent</strong> containing pertinent information about the event. 68 */ 69 public void elementChanged(MSMEvent event) { 70 init(); 71 } 72 /** Called whenever a metadata value is changed. 73 * @param event A <strong>MSMEvent</strong> containing pertinent information about the event. 74 */ 75 public void metadataChanged(MSMEvent event) { 76 // Don't care. 77 } 78 /** Called whenever a metadata set is changed. 79 * @param event A <strong>MSMEvent</strong> containing pertinent information about the event. 80 */ 81 public void setChanged(MSMEvent event) { 82 init(); 83 } 84 /** Called whenever the value tree of a certain element is significantly changed. 85 * @param event A <strong>MSMEvent</strong> containing information about the event. 86 */ 87 public void valueChanged(MSMEvent event) { 88 // Don't care. 89 } 90 private void init() { 91 removeAllElements(); 92 Vector elements = msm.getElements(); 93 Collections.sort(elements); 94 for(int k = 0; k < elements.size(); k++) { 95 addElement(elements.get(k)); 96 } 97 } 98 98 } 99 100 101 102 103 -
trunk/gli/src/org/greenstone/gatherer/msm/MetadataParser.java
r4293 r4365 43 43 */ 44 44 public interface MetadataParser { 45 46 45 /** Locate and import any metadata parsed by this metadata parser given the file involved and its previous incarnation. The last parameter indicates that the parser is forced to assign otherwise folder level metadata to the desired -files-. If the filenode was a directory folder_level is automatically true.*/ 46 public boolean process(FileNode destination, FileNode origin, boolean folder_level, boolean dummy_run); 47 47 } -
trunk/gli/src/org/greenstone/gatherer/msm/MetadataSet.java
r4331 r4365 50 50 */ 51 51 public class MetadataSet { 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 52 /** The <Strong>Document</strong> of the DOM model. */ 53 private Document document = null; 54 /** The document <strong>Element</strong> of the DOM model. */ 55 private Element root = null; 56 /** The <strong>File</strong> this metadata set was loaded from. */ 57 private File file = null; 58 /** A mapping from metadata elements to the root element of the value trees for that element. */ 59 private Hashtable value_trees = null; 60 /** The list of metadata elements which are, of course, children of the root node. */ 61 private NodeList elements = null; 62 /** The description of this metadata set. Cached as it takes more computation time. */ 63 private String description = null; 64 /** The name of this metadata set. Cached as it takes more computation time. */ 65 private String name = null; 66 /** An element of the tree pruning filter enumeration, that indicates all nodes in the tree should be retained. */ 67 static final int ALL_VALUES = 1; 68 /** An element of the tree pruning filter enumeration, that indicates only metadata Subject nodes or higher should remain after pruning. */ 69 static final int SUBJECTS_ONLY = 2; 70 /** An element of the tree pruning filter enumeration, that indicates no value nodes i.e. the entire AssignedValues subtree, should remain after pruning. */ 71 static final int NO_VALUES = 3; 72 73 public MetadataSet(String metadata_template) { 74 URL url = ClassLoader.getSystemResource(metadata_template); 75 try { 76 init(new File(URLDecoder.decode(url.getFile(), "UTF-8"))); 77 } 78 catch(UnsupportedEncodingException exception) { 79 Gatherer.printStackTrace(exception); 80 } 81 } 82 83 /** Constructor. 84 * @param file The file the metadata set should be loaded from. 85 */ 86 public MetadataSet(File file) { 87 init(file); 88 } 89 90 /** Metadata Set already parsed constructor. 91 * @param file The file the metadata was loaded from. 92 * @param document The DOM model <strong>Document</strong> containing the metadata set. 93 */ 94 public MetadataSet(File file, Document document) { 95 this.document = document; 96 this.elements = document.getElementsByTagName("Element"); 97 this.file = file; 98 this.root = document.getDocumentElement(); 99 this.value_trees = new Hashtable(); 100 // Now for each element read in its value tree if present. 101 for(int i = elements.getLength() - 1; i >= 0; i--) { 102 ElementWrapper value_element = new ElementWrapper((Element)elements.item(i)); 103 File value_file = new File(file.getParentFile(), value_element.toString() + ".mdv"); 104 104 ///ystem.err.println("Searching for " + value_file.getAbsolutePath()); 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 105 if(value_file.exists()) { 106 Document value_document = Utility.parse(value_file, false); 107 if(value_document != null) { 108 value_trees.put(value_element, new GValueModel(value_element, value_document)); 109 } 110 else { 111 Gatherer.println("Error! Missing mdv file: " + value_file.getAbsolutePath()); 112 } 113 } 114 } 115 } 116 /** Copy constructor. 117 * @param original The original metadata set to copy from. 118 */ 119 public MetadataSet(MetadataSet original) { 120 this.value_trees = new Hashtable(); 121 // We have to create a new document. 122 document = new DocumentImpl(original.getDocument().getDoctype()); 123 root = (Element) document.importNode(original.getDocument().getDocumentElement(), true); 124 document.appendChild(root); 125 elements = root.getElementsByTagName("Element"); 126 file = original.getFile(); 127 // Now for each element read in its value tree if present. 128 for(int i = elements.getLength() - 1; i >= 0; i--) { 129 ElementWrapper value_element_wrapper = new ElementWrapper((Element)elements.item(i)); 130 GValueModel value_tree = original.getValueTree(value_element_wrapper); 131 Document value_document = value_tree.getDocument(); 132 Document value_document_copy = new DocumentImpl(value_document.getDoctype()); 133 Element value_element = value_document.getDocumentElement(); 134 Element value_element_copy = (Element) value_document_copy.importNode(value_element, true); 135 value_document_copy.appendChild(value_element_copy); 136 GValueModel value_tree_copy = new GValueModel(value_element_wrapper, value_document_copy); 137 value_trees.put(value_element_wrapper, value_tree_copy); 138 } 139 } 140 /** Conditional copy constructor. 141 * @param original The original metadata set to copy from. 142 * @param condition An <i>int</i> which matches one of the tree pruning filter types. 143 */ 144 public MetadataSet(MetadataSet original, int condition) { 145 this(original); 146 // Now based on condition, we may have to remove some nodes from 147 // this model. 148 switch(condition) { 149 case ALL_VALUES: 150 150 // Do nothing. 151 152 151 break; 152 case SUBJECTS_ONLY: 153 153 // For each element retrieve its AssignedValues element. 154 155 156 157 158 159 160 161 162 163 154 for(Enumeration keys = value_trees.keys(); keys.hasMoreElements(); ) { 155 ElementWrapper value_element = (ElementWrapper)keys.nextElement(); 156 GValueModel value_tree = (GValueModel)value_trees.get(value_element); 157 Document value_tree_document = value_tree.getDocument(); 158 Element value_tree_root_element = value_tree_document.getDocumentElement(); 159 // Traverse tree and remove leaf nodes. 160 MSMUtils.traverseTree(value_tree_root_element, MSMUtils.NONE, true); 161 } 162 break; 163 case NO_VALUES: 164 164 // Remove assigned values trees. 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 165 value_trees.clear(); 166 break; 167 } 168 } 169 170 /** Add a mds level attribute. 171 * @param name The name of the attribute to add as a <Strong>String</strong>. 172 * @param value The value as a <strong>String</strong>. 173 */ 174 public void addAttribute(String name, String value) { 175 root.setAttribute(name, value); 176 } 177 178 /** Add a new default metadata element with the given name to this metadata set. 179 * @param name The name of this element as a <strong>String</strong>. 180 * @return An <strong>ElementWrapper</strong> around the newly created element or null if the element was not created. 181 */ 182 public ElementWrapper addElement(String name) { 183 Text text = document.createTextNode(name); 184 Element identifier = document.createElementNS("","Attribute"); 185 identifier.setAttribute("name","identifier"); 186 identifier.appendChild(text); 187 Element element = document.createElementNS("","Element"); 188 element.setAttribute("name",name); 189 element.appendChild(identifier); 190 root.appendChild(element); 191 ElementWrapper wrapper = new ElementWrapper(element); 192 ///ystem.err.println("Added a new element " + wrapper); 193 return wrapper; 194 } 195 196 /** Method to add a new metadata element to this metadata set, if and only if the element is not already present. 197 * @param others_element An <strong>Element</strong> we wish to add to this metadata set, that currently belongs to some other set. 198 * @return <i>null</i> if the add is successful, otherwise a <strong>String</strong> containing an error message (phrase key). 199 */ 200 public String addElement(Element others_element) { 201 if(!containsElement(others_element.getAttribute("name"))) { 202 202 // First get ownership of the new element, then add it. 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 203 Node our_element = document.importNode(others_element, true); 204 root.appendChild(our_element); 205 return null; 206 } 207 else { 208 return "Element name already exists!"; 209 } 210 } 211 212 /** Method to add a new metadata element with the specified new name to this metadata set, if and only if the name is not already in use. 213 * @param others_element An <strong>Element</strong> we wish to add to this metadata set, that currently belongs to some other set. 214 * @param new_name The new name to be given this element, as a <strong>String</strong>. 215 * @return <i>null</i> if the add is successful, otherwise a <strong>String</strong> containing an error message (phrase key). 216 */ 217 public String addElement(Element others_element, String new_name) { 218 if(!containsElement(new_name)) { 219 219 // First get ownership of the new element, then add it. 220 221 220 Element our_element = 221 (Element) document.importNode(others_element, true); 222 222 // Change name 223 223 our_element.setAttribute("name", new_name); 224 224 // Add it 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 225 root.appendChild(our_element); 226 return null; 227 } 228 else { 229 return "MSMPrompt.Name_Exists"; 230 } 231 } 232 /** Add a value tree to a given metadata element. 233 * @param element The <strong>ElementWrapper</strong> containing the element you wish to add a value tree for. 234 * @param value_tree The root <strong>Element</strong> of the value tree. 235 */ 236 public void addValueTree(ElementWrapper element, GValueModel model) { 237 ///ystem.err.println("Adding value tree for " + element.toString()); 238 value_trees.put(element, model); 239 } 240 241 public int compare(Element e1, Element e2) { 242 int result = 0; 243 // Check that they're not the same element. 244 if(e1 != e2) { 245 int index_e1 = -1; 246 int index_e2 = -1; 247 247 // Locate the indexes for each element. 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 248 for(int i = 0; i < elements.getLength(); i++) { 249 Node element = elements.item(i); 250 if(element == e1) { 251 index_e1 = i; 252 } 253 if(elements == e2) { 254 index_e2 = i; 255 } 256 } 257 if(index_e1 < index_e2) { 258 result = -1; 259 } 260 else { 261 result = 1; 262 } 263 } 264 return result; 265 } 266 267 /** A method to determine if this metadata set contains an element with a certain name. 268 * @param name A <strong>String</strong> which is the name of the element whose presence we are checking. 269 * @return A <i>boolean</i> which is <i>true</i> if the named element exists, <i>false</i> otherwise. 270 */ 271 public boolean containsElement(String name) { 272 for(int i = 0; i < elements.getLength(); i++) { 273 Element sibling = (Element) elements.item(i); 274 String sibling_name = sibling.getAttribute("name"); 275 if(sibling_name.equals(name)) { 276 return true; 277 } 278 } 279 return false; 280 } 281 282 public NamedNodeMap getAttributes() { 283 return root.getAttributes(); 284 } 285 286 /** Method to retrieve the contact address of the metadata set creator. 287 * @return A <strong>String</strong> containing the address. 288 */ 289 public String getContact() { 290 return root.getAttribute("contact"); 291 } 292 /** Method to retrieve the name of the creator of this metadata set. 293 * @return A <strong>String</strong> containing the name. 294 */ 295 public String getCreator() { 296 return root.getAttribute("creator"); 297 } 298 /** Method to retrieve the description of this metadata set. Note that this is language specific, so we determine the desired language from the Dictionary. If no such entry exists, first try returning the english version and failing that the first description found. 299 * @return The description as a <strong>String</strong>. 300 */ 301 public String getDescription() { 302 if(description == null) { 303 303 // Determine the code. 304 304 String language_code = Gatherer.dictionary.getLanguage(); 305 305 // Recover all Description elements 306 306 NodeList descriptions = document.getElementsByTagName("Description"); 307 307 // Iterate through the available descriptions looking for the appropriate one. Also make note of the first description, then overwrite it with any english one. 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 308 boolean found = false; 309 for(int i = 0; !found && i < descriptions.getLength(); i++) { 310 Element pos_description = (Element) descriptions.item(i); 311 String pos_description_code = pos_description.getAttribute("language"); 312 if(pos_description_code.equalsIgnoreCase(language_code)) { 313 description = MSMUtils.getValue(pos_description); 314 found = true; 315 } 316 else if(pos_description_code.equalsIgnoreCase("en")) { 317 description = MSMUtils.getValue(pos_description); 318 } 319 else if(description == null) { 320 description = MSMUtils.getValue(pos_description); 321 } 322 pos_description_code = null; 323 pos_description = null; 324 } 325 descriptions = null; 326 language_code = null; 327 327 // Failing all that set an error message 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 328 if(description == null) { 329 description = Gatherer.dictionary.get("MSM.No_Description"); 330 } 331 } 332 return description; 333 } 334 /** Method to retrieve the <strong>Document</strong> associated with this metadata set. 335 * @return The <strong>Document</strong> representing this metadata set. 336 */ 337 public Document getDocument() { 338 return document; 339 } 340 /** Method to retrieve the metadata element indicated by an index. 341 * @param index An <i>int</i> specifying the required element. 342 * @return The <strong>Element</strong> at the index. 343 */ 344 public Element getElement(int index) { 345 return (Element)elements.item(index); 346 } 347 /** This method is used to acquire a reference to the element which matches the given metadata. Note that this is not the same as <i>metadata.getElement()</i> as the reference returned by it may now be obsolete. 348 * @param metadata A <strong>Metadata</strong> object representing an element and value assignment. 349 * @return A 'live' reference to an <strong>Element</strong> which is the same as that referenced by the given metadata, or <i>null</i> if there is no such element. 350 */ 351 public Element getElement(Metadata metadata) { 352 return metadata.getElement().getElement(); 353 } 354 /** This method is used to acquire a reference to the element which has the name specified. Note that this is not the same as <i>metadata.getElement()</i> as the reference returned by it may now be obsolete. 355 * @param name A <strong>String</strong> stating the desired objects name. 356 * @return A 'live' reference to an <strong>Element</strong> which is the same as that referenced by the given metadata, or <i>null</i> if there is no such element. 357 */ 358 public Element getElement(String name) { 359 // Strip any namespace. 360 while(name.indexOf(".") != -1 && !name.equals(".")) { 361 name = name.substring(name.indexOf(".") + 1); 362 } 363 ///ystem.err.println("Get element named " + name); 364 for(int i = 0; i < elements.getLength(); i++) { 365 Element element = (Element) elements.item(i); 366 366 ///ystem.err.println("Compare to: " + element.getAttribute("name")); 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 367 if(element.getAttribute("name").equals(name)) { 368 return element; 369 } 370 } 371 return null; 372 } 373 /** Method to acquire a list of all the elements in this metadata set. 374 * @return A <strong>NodeList</strong> containing all of this sets elements. 375 */ 376 public NodeList getElements() { 377 return elements; 378 } 379 /** Method to retrieve the original file this metadata set was created from. 380 * @return A <strong>File</strong>. 381 */ 382 public File getFile() { 383 return file; 384 } 385 /** Get the last changed attribute. 386 * @return Last changed as a <strong>String</strong>. 387 */ 388 public String getLastChanged() { 389 return root.getAttribute("lastchanged"); 390 } 391 /** Method to get this metadata sets name. Note that this is language specific, so we determine the desired language from the Dictionary. If no such entry exists, first try returning the english version and failing that the first name found. 392 * @return A <strong>String</strong> which contains its name. 393 */ 394 public String getName() { 395 if(name == null) { 396 396 // Determine the code. 397 397 String language_code = Gatherer.dictionary.getLanguage(); 398 398 // Recover all Name elements 399 399 NodeList names = document.getElementsByTagName("Name"); 400 400 // Iterate through the available names looking for the appropriate one. Also make note of the first name, then overwrite it with any english one. 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 401 boolean found = false; 402 for(int i = 0; !found && i < names.getLength(); i++) { 403 Element pos_name = (Element) names.item(i); 404 String pos_name_code = pos_name.getAttribute("language"); 405 if(pos_name_code.equalsIgnoreCase(language_code)) { 406 name = MSMUtils.getValue(pos_name); 407 found = true; 408 } 409 else if(pos_name_code.equalsIgnoreCase("en")) { 410 name = MSMUtils.getValue(pos_name); 411 } 412 else if(name == null) { 413 name = MSMUtils.getValue(pos_name); 414 } 415 pos_name_code = null; 416 pos_name = null; 417 } 418 names = null; 419 language_code = null; 420 420 // Failing all that set an error message 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 421 if(name == null) { 422 name = Gatherer.dictionary.get("MSM.No_Name"); 423 } 424 } 425 return name; 426 } 427 /** Method to retrieve this metadata sets namespace. 428 * @return The namespace as a <strong>String</strong>. 429 */ 430 public String getNamespace() { 431 return root.getAttribute("namespace"); 432 } 433 /** Method to retrieve the root element, i.e. the Document Element, of the DOM model behind this metadata set. 434 * @return An <strong>Element</strong> which is at the root of the modal. 435 */ 436 public Element getRoot() { 437 return root; 438 } 439 /** Retrieve the value tree from this set that matches the given element. 440 * @param element The target <strong>ElementWrapper</strong>. 441 * @return A <strong>GValueModel</strong> value tree, or <i>null</i> if no such element or value tree. 442 */ 443 public GValueModel getValueTree(ElementWrapper element) { 444 GValueModel value_tree = null; 445 ///ystem.err.println("Retrieving value tree for element: " + element.toString()); 446 // Stinking hashtable get doesn't use the overridden equals. So I'll do a loop, which should be pretty small ie O(n) for n metadata elements. 447 for(Enumeration keys = value_trees.keys(); keys.hasMoreElements(); ) { 448 ElementWrapper sibling = (ElementWrapper) keys.nextElement(); 449 if(sibling.equals(element)) { 450 value_tree = (GValueModel) value_trees.get(sibling); 451 } 452 } 453 // If we've found no value tree, create a new one. 454 if(value_tree == null) { 455 value_tree = new GValueModel(element); 456 value_trees.put(element, value_tree); 457 } 458 return value_tree; 459 } 460 /** Remove a mds level attribute. 461 * @param name The name of the attribute to remove. 462 */ 463 public void removeAttribute(String name) { 464 root.removeAttribute(name); 465 } 466 /** Method to remove the given element from this metadata set. 467 * @param element The <strong>Element</strong> to be removed. 468 */ 469 public void removeElement(Element element) { 470 root.removeChild(element); 471 } 472 /** Used to remove the value tree for a specific element. 473 * @param element The <strong>ElementWrapper</strong> whose tree you wish to remove. 474 * @return The <strong>Element</strong> at the root of the value tree we just removed. 475 */ 476 public Element removeValueTree(ElementWrapper element) { 477 for(Enumeration keys = value_trees.keys(); keys.hasMoreElements(); ) { 478 ElementWrapper sibling = (ElementWrapper) keys.nextElement(); 479 if(sibling.equals(element)) { 480 Element tree_root = (Element) value_trees.get(sibling); 481 value_trees.remove(sibling); 482 return tree_root; 483 } 484 } 485 return null; 486 } 487 /** Set one of the mds level attributes. 488 * @param name The attribute to change. 489 * @param value its new value. 490 */ 491 public void setAttribute(String name, String value) { 492 root.setAttribute(name, value); 493 } 494 /** Once the metadata set has been saved to a different location, this is used to update the file parameter. 495 * @param file The new location of this metadata set <strong>File</strong>. 496 */ 497 public void setFile(File file) { 498 this.file = file; 499 } 500 501 public void setName(String name) { 502 // Retrieve the name element. We look for the first english one. 503 Element name_element = null; 504 Element metadataset_element = document.getDocumentElement(); 505 NodeList name_elements = metadataset_element.getElementsByTagName(Utility.NAME_ELEMENT); 506 for(int i = 0; i < name_elements.getLength(); i++) { 507 Element possible_name_element = (Element) name_elements.item(i); 508 if(possible_name_element.getAttribute(Utility.LANGUAGE_ATTRIBUTE).equals(Utility.ENGLISH_VALUE)) { 509 // Found it. 510 name_element = possible_name_element; 511 } 512 } 513 // If there is none add one. Note that we can only add english metadata sets. Although others can edit them to add further names as necessary. 514 if(name_element == null) { 515 name_element = document.createElement(Utility.NAME_ELEMENT); 516 name_element.setAttribute(Utility.LANGUAGE_ATTRIBUTE, Utility.ENGLISH_VALUE); 517 metadataset_element.insertBefore(name_element, metadataset_element.getFirstChild()); 518 } 519 // Replace the text node 520 while(name_element.hasChildNodes()) { 521 name_element.removeChild(name_element.getFirstChild()); 522 } 523 name_element.appendChild(document.createTextNode(name)); 524 } 525 526 /** Method to determine the number of elements in this set. 527 * @return An <i>int</i> specifying the element count. 528 */ 529 public int size() { 530 return elements.getLength(); 531 } 532 /** Method to translate this class into a meaningful string, which in this case is the metadata sets name. 533 * @return The metadata sets name as a <strong>String</strong>. 534 */ 535 public String toString() { 536 String name = getName(); 537 // If there is no given name, then use the namespace as there is garaunteed to be one of them. 538 if(name == null || name.length() == 0) { 539 name = root.getAttribute("namespace"); 540 } 541 // Append namespace 542 String namespace = root.getAttribute("namespace"); 543 if(namespace == null || namespace.equals("")) { 544 namespace = Utility.EXTRACTED_METADATA_NAMESPACE; 545 } 546 name = name + " (" + namespace + ")"; 547 return name; 548 } 549 550 private void init(File file) { 551 this.file = file; 552 this.value_trees = new Hashtable(); 553 this.document = Utility.parse(file, false); 554 if(document != null) { 555 this.elements = document.getElementsByTagName("Element"); 556 this.root = document.getDocumentElement(); 557 557 // Now for each element read in its value tree if present. 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 558 for(int i = elements.getLength() - 1; i >= 0; i--) { 559 ElementWrapper value_element = new ElementWrapper((Element)elements.item(i)); 560 File value_file = new File(file.getParentFile(), value_element.toString() + ".mdv"); 561 ///ystem.err.println("Searching for " + value_file.getAbsolutePath()); 562 if(value_file.exists()) { 563 Document value_document = Utility.parse(value_file, false); 564 if(value_document != null) { 565 value_trees.put(value_element, new GValueModel(value_element, value_document)); 566 } 567 else { 568 Gatherer.println("Error! Missing mdv file: " + value_file.getAbsolutePath()); 569 } 570 } 571 } 572 } 573 else { 574 Gatherer.println("Error! Missing mds file: " + file.getAbsolutePath()); 575 } 576 } 577 577 } -
trunk/gli/src/org/greenstone/gatherer/msm/MetadataSetManager.java
r4362 r4365 66 66 public class MetadataSetManager { 67 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 68 /** The name of the hidden, or system, metadata set. */ 69 static final public String HIDDEN = "hidden"; 70 71 /** A mapping from metadata namespace to metadata set. */ 72 static private Hashtable mds_hashtable = new Hashtable(); 73 74 /** The class responsible for creating and maintaining all the visual components of the metadata management package. */ 75 public MSMPrompt prompt = null; 76 /** The profiler is responsible for remembering what actions a user has requested when importing metadata, so as to prevent the user needlessly re-entering this information for each import. */ 77 public MSMProfiler profiler = null; 78 79 /** Records all of the changes made to metadata as part of the current metadata change. Entries map from a particular FileNode to an ArrayList of the modified metadata for that node. Not to be confused with the undo managers idea of undo which records a list of all metadata changes requested. */ 80 private HashMap undo_buffer = new HashMap(); 81 /** The loader responsible for sequentially loading and attempting to use all registered metadata parsers to extract existing metadata from new files. */ 82 private ExistingMetadataLoader loader = null; 83 /** Specialized parser for parsing GreenstoneDirectoryMetadata files, which not only caches entries, but also breaks up massive metadata.xml files into reasonable sizes. */ 84 private GDMParser gdm_parser = null; 85 /** A list of classes who are interested in changes to the loaded metadata sets. */ 86 private Vector listeners = null; 87 88 /** Constructor. */ 89 public MetadataSetManager() { 90 this.gdm_parser = new GDMParser(); 91 this.listeners = new Vector(); 92 this.loader = new ExistingMetadataLoader(); 93 loadProfiler(); 94 this.prompt = new MSMPrompt(this); 95 } 96 97 /** Attach a piece of metadata to a record or records, ensuring the value tree is built properly, and correct messaging fired. 98 * @param records A FileNode[] of records, or directories, to add the specified metadata to. 99 * @param element The metadata element, contained within an ElementWrapper to base metadata on. 100 * @param value The value to assign to the metadata as a String. 101 */ 102 public void addMetadata(long id, FileNode records[], ElementWrapper element, String value_str) { 103 addMetadata(id, records, element, value_str, MetaEditPrompt.CONFIRM); 104 } 105 public void addMetadata(long id, FileNode records[], ElementWrapper element, String value_str, int action) { 106 // Retrieve the appropriate value node from the value tree for this element, creating it if necessary. 107 GValueModel model = getValueTree(element); 108 GValueNode value = null; 109 if(model != null) { 110 value = model.addValue(value_str); // Only adds if not already present, otherwise just returns existing node. 111 } 112 else { 113 value = new GValueNode(element.toString(), value_str); 114 } 115 // Create new metadata. 116 Metadata metadata = new Metadata(value); 117 // Reset the undo buffer. 118 undo_buffer.clear(); 119 // Assign to records. Note that we must watch for responses from the user prompts, and cease loop if break signalled. 120 // Now add the metadata to each selected file node. 121 for(int i = 0; action != MetaEditPrompt.CANCEL && i < records.length; i++) { 122 action = addMetadata(id, records[i], metadata, action, (records.length > 1)); 123 } 124 // If we were cancelled we should undo any changes so far 125 if(action == MetaEditPrompt.CANCEL) { 126 for(Iterator keys = undo_buffer.keySet().iterator(); keys.hasNext(); ) { 127 FileNode record = (FileNode) keys.next(); 128 undoAdd(id, record); 129 } 130 } 131 } 132 133 /** Adds a metadata set listener to this set, if it isn't alreay listening. 134 * @param listener The new MSMListener. 135 */ 136 public void addMSMListener(MSMListener listener) { 137 if(!listeners.contains(listener)) { 138 listeners.add(listener); 139 } 140 } 141 142 public MetadataSet addSet(String namespace, String name) { 143 MetadataSet mds = new MetadataSet(Utility.METADATA_SET_TEMPLATE); 144 mds.setAttribute("creator","The Gatherer"); 145 // Calculate lastchanged to right now on this machine by this user 146 String user_name = System.getProperty("user.name"); 147 String machine_name = Utility.getMachineName(); 148 mds.setAttribute("lastchanged", Utility.getDateString() + " - " + user_name + " on " + machine_name); 149 // And the remaining attributes. 150 //mds.setAttribute("name", name); 151 mds.setAttribute("namespace", namespace); 152 mds_hashtable.put(namespace, mds); 153 // Add the name element. 154 mds.setName(name); 155 fireSetChanged(mds); 156 return mds; 157 } 158 159 /** Add a value tree to a given metadata element. 160 * @param element The ElementWrapper containing the element you wish to add a value tree for. 161 * @param value_tree The root Element of the value tree. 162 */ 163 public void addValueTree(GValueModel model) { 164 ElementWrapper element = model.getElement(); 165 String namespace = element.getNamespace(); 166 MetadataSet mds = (MetadataSet) mds_hashtable.get(namespace); 167 if(mds != null) { 168 mds.addValueTree(element, model); 169 } 170 } 171 /** Destructor. 172 * @see org.greenstone.gatherer.msm.MSMProfiler 173 */ 174 public void destroy() { 175 mds_hashtable.clear(); 176 profiler.destroy(); 177 profiler = null; 178 } 179 /** Method called to open a metadata set editing window. 180 * @return A boolean indicating if the edit was successful. 181 */ 182 public boolean editMDS() { 183 /* TODO - implement. Something here like: new MSMEditor() */ 184 MetadataEditorManager mem = new MetadataEditorManager(); 185 mem.dispose(); 186 mem = null; 187 return true; 188 } 189 190 /** This method is called to export a metadata set. First a prompt is displayed to gather necessary details such as which metadata set to export, where to export it to and what conditions should be applied when exporting. Once this information is gathered the static method <i>exportMDS()</i> is called with the appropriate output stream. 191 * @return A boolean which is <i>true</i> if the metadata set has been exported successfully, <i>false</i> otherwise. 192 */ 193 public boolean exportMDS() { 194 ExportMDSPrompt emdsp = new ExportMDSPrompt(this, true); 195 int result = emdsp.display(); 196 MetadataSet set = emdsp.getSelectedSet(); 197 if(result == ExportMDSPrompt.EXPORT && set != null) { 198 File file = emdsp.getSelectedFile(); 199 MetadataSet set_copy = new MetadataSet(set, emdsp.getSelectedCondition()); 200 try { 201 file.getParentFile().mkdirs(); 202 Utility.export(set_copy.getDocument(), file); 203 // Now for each element attempt to save its value tree. 204 NodeList elements = set_copy.getElements(); 205 for(int i = elements.getLength() - 1; i >= 0; i--) { 206 ElementWrapper value_element = new ElementWrapper((Element)elements.item(i)); 207 GValueModel value_tree = set_copy.getValueTree(value_element); 208 if(value_tree != null) { 209 File value_file = new File(file.getParentFile(), value_element.toString() + ".mdv"); 210 ///ystem.err.println("Saving value file: " + value_file.toString()); 211 Utility.export(value_tree.getDocument(), value_file); 212 } 213 } 214 return true; 215 } 216 catch (Exception error) { 217 error.printStackTrace(); 218 } 219 } 220 emdsp.dispose(); 221 emdsp = null; 222 return false; 223 } 224 225 /** Fire an element changed message off to all registered listeners. 226 * @param event An MSMEvent detailing the change. 227 */ 228 public void fireElementChanged(MSMEvent event) { 229 // Then send it to all the listeners. 230 for(int i = 0; i < listeners.size(); i++) { 231 ((MSMListener)listeners.get(i)).elementChanged(event); 232 } 233 } 234 235 /** Fire a metadata value changed message, whose id is to be generated now, off to all registered listeners. */ 236 public void fireMetadataChanged(FileNode node, Metadata old_data, Metadata new_data) { 237 fireMetadataChanged(System.currentTimeMillis(), node, old_data, new_data); 238 } 239 240 /** Fire a metadata value changed message off to all registered listeners. */ 241 public void fireMetadataChanged(long id, FileNode node, Metadata old_data, Metadata new_data) { 242 if(old_data != null) { 243 old_data.getElement().dec(); 244 } 245 if(new_data != null) { 246 new_data.getElement().inc(); 247 } 248 ///ystem.err.println("Metadata changed: " + record + " > '" + old_data + "' -> '" + new_data + "'"); 249 // Create a new MSMEvent based on the record. 250 MSMEvent event = new MSMEvent(this, id, node, old_data, new_data); 251 // Then send it to all the listeners. 252 for(int i = 0; i < listeners.size(); i++) { 253 ((MSMListener)listeners.get(i)).metadataChanged(event); 254 } 255 } 256 257 /** Fire a metadata set changed message off to all registered listeners. 258 * @param set The MetadataSet thats changed. 259 */ 260 public void fireSetChanged(MetadataSet set) { 261 // Create a new MSMEvent, with a MSMAction containing only the new set. 262 MSMEvent event = new MSMEvent(this, 0L, new MSMAction(set.toString(), null, -1, null)); 263 // Then send it to all the listeners. 264 for(int i = 0; i < listeners.size(); i++) { 265 ((MSMListener)listeners.get(i)).setChanged(event); 266 } 267 } 268 /** Called whenever the value tree associated with an element changes significantly. 269 * @param element The metadata element whose value tree has changed, as an ElementWrapper. 270 */ 271 public void fireValueChanged(ElementWrapper element, GValueModel old_model, GValueModel new_model) { 272 // Create a new MSMEvent based on the element wrapper. 273 MSMEvent event = new MSMEvent(this, 0L, element, old_model, new_model); 274 // Then send it to all the listeners. 275 for(int i = 0; i < listeners.size(); i++) { 276 ((MSMListener)listeners.get(i)).valueChanged(event); 277 } 278 } 279 /** Builds a list of elements that have been assigned as metadata in this collection. We go through all of the elements, looking for elements whose occurances are greater than 0. A convenience call to the version with one parameter. 280 * @return A Vector of assigned elements. 281 */ 282 public Vector getAssignedElements() { 283 return getAssignedElements(false); 284 } 285 /** Builds a list of elements that have been assigned as metadata in this collection. We go through all of the elements, looking for elements whose occurances are greater than 0. 286 * @param hierarchy_only <i>true</i> to only return those elements that are both assigned and have hierarchical value trees, <i>false</i> for just assignments. 287 * @return A Vector of assigned elements. 288 */ 289 public Vector getAssignedElements(boolean hierarchy_only) { 290 Vector elements = new Vector(); 291 for(Enumeration keys = mds_hashtable.keys(); keys.hasMoreElements(); ) { 292 MetadataSet mds = (MetadataSet)mds_hashtable.get(keys.nextElement()); 293 if(!mds.getNamespace().equals(HIDDEN)) { 294 NodeList set_elements = mds.getElements(); 295 for(int i = 0; i < set_elements.getLength(); i++) { 296 ElementWrapper element = new ElementWrapper((Element)set_elements.item(i)); 297 elements.add(element); 298 } 299 } 300 } 301 Collections.sort(elements); 302 for(int i = elements.size(); i != 0; i--) { 303 ElementWrapper element = (ElementWrapper) elements.get(i - 1); 304 if(element.getOccurances() == 0 && element.getNamespace().length() > 0 && !element.toString().equals("Source")) { 305 elements.remove(element); 306 } 307 else if(hierarchy_only) { 308 GValueModel model = getValueTree(element); 309 if(!model.isHierarchy()) { 310 elements.remove(element); 311 } 312 } 313 } 314 return elements; 315 } 316 /** Used to get all the (non-hidden) elements in this manager. 317 * @return A Vector of ElementWrappers. 318 */ 319 public Vector getElements() { 320 return getElements(false); 321 } 322 /** Used to get all the elements in this manager. 323 * @param all <i>true</i> if all elements, including hidden, should be returned. 324 * @return A Vector of ElementWrappers. 325 */ 326 public Vector getElements(boolean all) { 327 Vector all_elements = new Vector(); 328 for(Enumeration keys = mds_hashtable.keys(); keys.hasMoreElements(); ) { 329 MetadataSet mds = (MetadataSet)mds_hashtable.get(keys.nextElement()); 330 if(all || !mds.getNamespace().equals(HIDDEN)) { 331 NodeList set_elements = mds.getElements(); 332 ///ystem.err.println("The set " + mds + " has " + set_elements.getLength() + " elements."); 333 for(int i = 0; i < set_elements.getLength(); i++) { 334 ElementWrapper element = new ElementWrapper((Element)set_elements.item(i)); 335 if(!element.toString().equals("Source")) { 336 all_elements.add(element); 337 } 338 } 339 } 340 } 341 Collections.sort(all_elements); 342 return all_elements; 343 } 344 /** Returns all the elements within this set as a combobox model. 345 * @return A MetadataComboBoxModel containing all the metadata elements from all the sets, with namespacing. 346 */ 347 public MetadataComboBoxModel getElementModel() { 348 return new MetadataComboBoxModel(this); 349 } 350 /** Retrieve a metadata element by its index. 351 * @param index The specified index as an int. 352 * @return An ElementWrapper containing the specied element, or <i>null</i> is no such element exists. 353 */ 354 public ElementWrapper getElement(int index) { 355 Vector elements = getElements(false); 356 ElementWrapper result = null; 357 if(0 <= index && index < elements.size()) { 358 result = (ElementWrapper) elements.get(index); 359 } 360 return result; 361 } 362 /** Retrieve a metadata element by looking at the current metadata element. Note that this 'index' element may now be disconnected from the DOM model, so we have to reload the target element by the string method. 363 * @param element The possibly out-of-data MetadataElement. 364 * @return An ElementWrapper containing the specied element, or <i>null</i> is no such element exists. 365 */ 366 public ElementWrapper getElement(ElementWrapper element) { 367 return getElement(element.toString()); 368 } 369 /** Retrieve a metadata element by its fully qualified name. 370 * @param name The elements name as a String. 371 * @return An ElementWrapper containing the specied element, or <i>null</i> is no such element exists. 372 */ 373 public ElementWrapper getElement(String name) { 374 ///ystem.err.println("Retrieve element " + name); 375 if(name == null) { 376 376 ///ystem.err.println("No name!"); 377 378 379 380 381 382 383 384 377 return null; 378 } 379 ElementWrapper result = null; 380 MetadataSet set = null; 381 String element = null; 382 // First we seperate off what set it is in, where we have '<set><namespace_separator><element>'. 383 if(name.indexOf(MSMUtils.NS_SEP) != -1) { 384 String namespace = name.substring(0, name.indexOf(MSMUtils.NS_SEP)); 385 385 // Retrieve the correct set if possible. 386 387 386 set = (MetadataSet)mds_hashtable.get(namespace); 387 namespace = null; 388 388 // Now retrieve the element name. 389 390 391 389 element = name.substring(name.indexOf(MSMUtils.NS_SEP) + 1); 390 } 391 else { 392 392 // No namespace so assume ns = "". 393 394 395 396 393 set = (MetadataSet)mds_hashtable.get(""); 394 element = name; 395 } 396 if(set != null) { 397 397 ///ystem.err.print("Trying to match element " + element +"?"); 398 Element temp = set.getElement(element); 399 if(temp != null) { 400 ///ystem.err.println("Found!"); 401 result = new ElementWrapper(temp); 398 Element temp = set.getElement(element); 399 if(temp != null) { 400 ///ystem.err.println("Found!"); 401 result = new ElementWrapper(temp); 402 } 403 else { 404 ///ystem.err.println("Not found."); 405 } 406 element = null; 407 temp = null; 408 } 409 else { 410 ///ystem.err.println("No such set"); 411 } 412 set = null; 413 if(result == null) { 414 ///ystem.err.println("Shouldn't return null unless this is greenstone archive parsing."); 415 } 416 return result; 417 } 418 /** Retrieve a certain named element from a certain named set. 419 * @param set The metadata set whose element you want. 420 * @param name The name of the element. 421 * @return An ElementWrapper around the requested element, or null if no such set or element. 422 */ 423 public ElementWrapper getElement(String set, String name) { 424 if(mds_hashtable.containsKey(set)) { 425 MetadataSet temp = (MetadataSet)mds_hashtable.get(set); 426 return new ElementWrapper(temp.getElement(name)); 427 } 428 return null; 429 } 430 /** Get all of the metadata elements as an array of nodelists. 431 * @return A NodeList[] of metadata elements. 432 */ 433 public NodeList[] getNodeLists() { 434 NodeList elements[] = null; 435 int index = 0; 436 elements = new NodeList[getSets().size()]; // Remember not to count hidden metadata 437 for(Enumeration keys = mds_hashtable.keys(); keys.hasMoreElements(); ) { 438 MetadataSet mds = (MetadataSet)mds_hashtable.get(keys.nextElement()); 439 if(!mds.getNamespace().equals(HIDDEN)) { 440 elements[index] = mds.getElements(); 441 index++; 442 } 443 } 444 return elements; 445 } 446 447 /** Retrieve the named metadata set. 448 * @param name The sets name as a String. 449 * @return The MetadataSet as named, or null if no such set. 450 */ 451 public MetadataSet getSet(String name) { 452 if(mds_hashtable.containsKey(name)) { 453 return (MetadataSet) mds_hashtable.get(name); 454 } 455 else { 456 ///ystem.err.println("Couldn't find metadata set."); 457 if(name.equals(HIDDEN)) { 458 return createHidden(); 459 } 460 } 461 return null; 462 } 463 464 /** Method to retrieve all of the metadata sets loaded in this collection. 465 * @return A Vector of metadata sets. 466 */ 467 public Vector getSets() { 468 return getSets(true); 469 } 470 public Vector getSets(boolean include_greenstone) { 471 Vector result = new Vector(); 472 for(Enumeration keys = mds_hashtable.keys(); keys.hasMoreElements(); ) { 473 MetadataSet set = (MetadataSet)mds_hashtable.get(keys.nextElement()); 474 if(!set.getNamespace().equals(HIDDEN) && (include_greenstone || !set.getNamespace().equals(""))) { 475 result.add(set); 476 } 477 } 478 return result; 479 } 480 /** Find the total number of elements loaded. 481 * @return The element count as an int. 482 */ 483 public int getSize() { 484 int count = 0; 485 if(mds_hashtable.size() > 0) { 486 for(Enumeration keys = mds_hashtable.keys(); keys.hasMoreElements();) { 487 MetadataSet mds = (MetadataSet)mds_hashtable.get(keys.nextElement()); 488 if(mds.getNamespace().equals(HIDDEN)) { 489 count = count + mds.size(); 490 } 491 } 492 } 493 return count; 494 } 495 /** Get the value tree that matches the given element. 496 * @param element The ElementWrapper representing the element. 497 * @return The GValueModel representing the value tree or null. 498 */ 499 public GValueModel getValueTree(ElementWrapper element) { 500 GValueModel value_tree = null; 501 if(element != null) { 502 String namespace = element.getNamespace(); 503 if(namespace.length() > 0) { 504 MetadataSet mds = (MetadataSet) mds_hashtable.get(namespace); 505 if(mds != null) { 506 value_tree = mds.getValueTree(element); 507 } 508 } 509 } 510 return value_tree; 511 } 512 /** This method is called to import a metadata set. First a prompt is displayed to gather necessary details such as which metadata set to import. Once this information is gathered the method <i>importMDS(File)</i> is called with the appropriate filename. 513 * @return A boolean which is <i>true</i> if the metadata set has been imported successfully, <i>false</i> otherwise. 514 */ 515 public boolean importMDS() { 516 JFileChooser chooser = new JFileChooser(new File(Utility.METADATA_DIR)); 517 javax.swing.filechooser.FileFilter filter = new MDSFileFilter(); 518 chooser.setFileFilter(filter); 519 int returnVal = chooser.showDialog(Gatherer.g_man, Gatherer.dictionary.get("MSMPrompt.File_Import")); 520 if(returnVal == JFileChooser.APPROVE_OPTION) { 521 return importMDS(chooser.getSelectedFile(), true); 522 } 523 return false; 524 } 525 526 public boolean importMDS(File mds_file, boolean user_driven) { 527 // 1. Parse the new file. 528 MetadataSet mds_new = new MetadataSet(mds_file); 529 // Display a prompt asking how much of the value structure the user wishes to import. 530 if(user_driven) { 531 ExportMDSPrompt imdsp = new ExportMDSPrompt(this, false); 532 int result = imdsp.display(); 533 if(result == ExportMDSPrompt.EXPORT) { // Here export means the user didn't cancel. 534 switch(imdsp.getSelectedCondition()) { 535 case MetadataSet.NO_VALUES: 536 mds_new = new MetadataSet(mds_new, MetadataSet.NO_VALUES); 537 break; 538 case MetadataSet.SUBJECTS_ONLY: 539 mds_new = new MetadataSet(mds_new, MetadataSet.SUBJECTS_ONLY); 540 break; 541 default: // ALL_VALUES 542 // Don't do anything. 543 } 544 } 545 else { 546 mds_new = null; 547 } 548 imdsp.dispose(); 549 imdsp = null; 550 } 551 // Carry on importing the new collection 552 if(mds_new != null && mds_new.getDocument() != null) { 553 String family = mds_new.getNamespace(); 554 // 2. See if we have another metadata set of the same family 555 // already. If so retrieve it and merge. 556 boolean matched = false; 557 for(Enumeration keys = mds_hashtable.keys(); 558 keys.hasMoreElements();) { 559 String key = (String)keys.nextElement(); 560 if(key.equals(family)) { 561 matched = true; 562 MetadataSet mds_cur = (MetadataSet)mds_hashtable.get(key); 563 //ystem.err.println("Merging " + mds_new + " into " + mds_cur); 564 mergeMDS(mds_cur, mds_new); 565 } 566 } 567 if(!matched) { 568 ///ystem.err.println("Mapping " + family + " to " + mds_new); 569 mds_hashtable.put(family, mds_new); 570 } 571 // Fire setChanged() message. 572 fireSetChanged(mds_new); 573 return true; 574 } 575 // else we cancelled for some reason. 576 return false; 577 } 578 579 /** This method reloads all of the metadata sets that have been marked as included in this collection by entries in the collection configuration file. 580 */ 581 public void load() { 582 File source = new File(Gatherer.c_man.getCollectionMetadata()); 583 File files[] = source.listFiles(); 584 for(int i = 0; files != null && i < files.length; i++) { 585 if(files[i].getName().endsWith(".mds")) { 586 importMDS(files[i], false); 587 } 588 } 589 // If no current 'hidden' metadata set exists, create one. 590 if(getSet(HIDDEN) == null) { 591 createHidden(); 592 } 593 } 594 /** This method takes two metadata sets, the current one and a new one, and merges them. This merge takes place at an element level falling to lower levels as necessary (using <i>mergeMDE()</i> to merge elements and <i>mergeMDV()</i> to merge value trees. 595 * @param mds_current The currently loaded MetadataSet. 596 * @param mds_new A new MetadataSet you wish to merge in. 597 * @return A boolean with value <i>true</i> indicating if the merge was successful, otherwise <i>false</i> if errors were detected. 598 */ 599 public boolean mergeMDS(MetadataSet mds_cur, MetadataSet mds_new) { 600 // For a super quick check for equivelent trees, we compare the last changed values. 601 if(mds_cur.getLastChanged().equals(mds_new.getLastChanged())) { 602 // Exactly the same. Nothing to change. 603 return true; 604 } 605 // Show initial progress prompt. 606 prompt.startMerge(mds_new.size()); 607 boolean cancel = false; 608 // For each element in the new set 609 for(int i = 0; !cancel && i < mds_new.size(); i++) { 610 boolean cont = false; 611 Element mde_new = mds_new.getElement(i); 612 // See if the element already exists in the current set 613 Element mde_cur = mds_cur.getElement(mde_new.getAttribute("name")); 614 int option = Declarations.NO_ACTION; 615 while(!cont && !cancel) { 616 // We may be dealing with a brand new element, or possibly a 617 // renamed one. 618 if(mde_cur == null) { 619 // Provide merge, rename and skip options. 620 option = prompt.mDSPrompt(mds_cur, null, mds_new, mde_new); 621 } 622 else { 623 // If the two elements have equal structure we only have 624 // to worry about merging the values. 625 if(MSMUtils.elementsEqual(mds_cur, mde_cur, mds_new, mde_new, false)) { 626 cancel = !mergeMDV(mds_cur, mde_cur, mds_new, mde_new); 627 cont = true; 628 } 629 else { 630 // Provide add, merge and skip options. 631 option = prompt.mDSPrompt(mds_cur, mde_cur, mds_new, mde_new); 632 } 633 } 634 String reason = null; 635 switch(option) { 636 case Declarations.ADD: 637 // Only available to brand new elements, this options 638 // simply adds the element to the set. 639 reason = mds_cur.addElement(mde_new); 640 if(reason == null) { 641 cont = true; 642 } 643 else { 644 prompt.addFailed(mde_new, reason); 645 cont = false; 646 } 647 break; 648 case Declarations.CANCEL: 649 cancel = true; 650 cont = true; 651 break; 652 case Declarations.FORCE_MERGE: 653 // If the mde_cur is null, that means the users has asked 654 // to merge but hasn't choosen any element to merge with. 655 // Make the user select an element. 656 mde_cur = prompt.selectElement(mds_cur); 657 case Declarations.MERGE: 658 // This case in turn calls the mergeMDE method to perform 659 // the actual merging of the Elements. 660 if(mde_cur != null) { 661 cancel = !mergeMDE(mds_cur, mde_cur, mds_new, mde_new); 662 } 663 cont = true; 664 break; 665 case Declarations.RENAME: 666 ///ystem.err.println("Rename element"); 667 // This case adds the Element, but requires the user to 668 // enter a unique name. 669 String new_name = prompt.rename(mde_new); 670 if(new_name != null && new_name.length() > 0) { 671 reason = mds_cur.addElement(mde_new, new_name); 672 if(reason == null) { 673 mde_cur = mds_cur.getElement(new_name); 674 cont = true; 675 } 676 else { 677 prompt.renameFailed(mde_new, new_name, reason); 678 cont = false; 679 } 680 } 681 else { 682 if(new_name != null) { 683 prompt.renameFailed(mde_new, new_name, 684 "MSMPrompt.Invalid_Name"); 685 } 686 cont = false; 687 } 688 break; 689 case Declarations.REPLACE: 690 // Removes the existing Element then adds the new. 691 mds_cur.removeElement(mde_cur); 692 reason = mds_cur.addElement(mde_new); 693 if(reason == null) { 694 cont = true; 695 } 696 else { 697 prompt.removeFailed(mde_cur, reason); 698 cont = false; 699 } 700 break; 701 case Declarations.SKIP: 702 // Does not change the set. 703 cont = true; 704 break; 705 } 706 // Add this action to profile for later reference. 707 if(profiler == null) { 708 ///ystem.err.println("No Profiler"); 709 } 710 //profiler.addAction(mds_new.getFile().getAbsolutePath(), MSMUtils.getFullName(mde_new), option, MSMUtils.getFullName(mde_cur)); 711 } 712 prompt.incrementMerge(); 713 } 714 prompt.endMerge(); 715 return true; 716 } 717 718 /** This method allows for two metadata elements to be merged. Essentially merging existing elements give the users such options as keeping or replacing attribute elements as they are merged. 719 * @param mde_cur The Element that already exists in the current metadata sets. 720 * @param mde_new A new Element which has the same name as the current one but different data. 721 * @return A boolean that if <i>true</i> indicats the action was completed. If <i>false</i> then an error or user action has prevented the merge. 722 * TODO Implement 723 */ 724 public boolean mergeMDE(MetadataSet mds_cur, Element mde_cur, MetadataSet mds_new, Element mde_new) { 725 while(true) { 726 for(Node mdn_new = mde_new.getFirstChild(); mdn_new != null; mdn_new = mdn_new.getNextSibling()) { 727 // Only merge the nodes whose name is 'Attribute' 728 if(mdn_new.getNodeName().equals("Attribute")) { 729 Element att_new = (Element) mdn_new; 730 // Unfortunately some attributes, such as author, can have several occurances, so match each in turn. 731 Element temp[] = MSMUtils.getAttributeNodesNamed(mde_cur, att_new.getAttribute("name")); 732 for(int i = 0; temp != null && i < temp.length; i++) { 733 Element att_cur = temp[i]; 734 boolean cont = false; 735 int action = Declarations.NO_ACTION; 736 while(!cont) { 737 if(att_cur != null) { 738 if(!MSMUtils.attributesEqual(att_cur, att_new)) { 739 action = prompt.mDEPrompt(mde_cur, att_cur, mde_new, att_new); 402 740 } 403 741 else { 404 ///ystem.err.println("Not found.");742 action = Declarations.SKIP; 405 743 } 406 element = null; 407 temp = null; 408 } 409 else { 410 ///ystem.err.println("No such set"); 411 } 412 set = null; 413 if(result == null) { 414 ///ystem.err.println("Shouldn't return null unless this is greenstone archive parsing."); 415 } 416 return result; 417 } 418 /** Retrieve a certain named element from a certain named set. 419 * @param set The metadata set whose element you want. 420 * @param name The name of the element. 421 * @return An ElementWrapper around the requested element, or null if no such set or element. 422 */ 423 public ElementWrapper getElement(String set, String name) { 424 if(mds_hashtable.containsKey(set)) { 425 MetadataSet temp = (MetadataSet)mds_hashtable.get(set); 426 return new ElementWrapper(temp.getElement(name)); 427 } 428 return null; 429 } 430 /** Get all of the metadata elements as an array of nodelists. 431 * @return A NodeList[] of metadata elements. 432 */ 433 public NodeList[] getNodeLists() { 434 NodeList elements[] = null; 435 int index = 0; 436 elements = new NodeList[getSets().size()]; // Remember not to count hidden metadata 437 for(Enumeration keys = mds_hashtable.keys(); keys.hasMoreElements(); ) { 438 MetadataSet mds = (MetadataSet)mds_hashtable.get(keys.nextElement()); 439 if(!mds.getNamespace().equals(HIDDEN)) { 440 elements[index] = mds.getElements(); 441 index++; 442 } 443 } 444 return elements; 445 } 446 447 /** Retrieve the named metadata set. 448 * @param name The sets name as a String. 449 * @return The MetadataSet as named, or null if no such set. 450 */ 451 public MetadataSet getSet(String name) { 452 if(mds_hashtable.containsKey(name)) { 453 return (MetadataSet) mds_hashtable.get(name); 454 } 455 else { 456 ///ystem.err.println("Couldn't find metadata set."); 457 if(name.equals(HIDDEN)) { 458 return createHidden(); 459 } 460 } 461 return null; 462 } 463 464 /** Method to retrieve all of the metadata sets loaded in this collection. 465 * @return A Vector of metadata sets. 466 */ 467 public Vector getSets() { 468 return getSets(true); 469 } 470 public Vector getSets(boolean include_greenstone) { 471 Vector result = new Vector(); 472 for(Enumeration keys = mds_hashtable.keys(); keys.hasMoreElements(); ) { 473 MetadataSet set = (MetadataSet)mds_hashtable.get(keys.nextElement()); 474 if(!set.getNamespace().equals(HIDDEN) && (include_greenstone || !set.getNamespace().equals(""))) { 475 result.add(set); 476 } 477 } 478 return result; 479 } 480 /** Find the total number of elements loaded. 481 * @return The element count as an int. 482 */ 483 public int getSize() { 484 int count = 0; 485 if(mds_hashtable.size() > 0) { 486 for(Enumeration keys = mds_hashtable.keys(); keys.hasMoreElements();) { 487 MetadataSet mds = (MetadataSet)mds_hashtable.get(keys.nextElement()); 488 if(mds.getNamespace().equals(HIDDEN)) { 489 count = count + mds.size(); 490 } 491 } 492 } 493 return count; 494 } 495 /** Get the value tree that matches the given element. 496 * @param element The ElementWrapper representing the element. 497 * @return The GValueModel representing the value tree or null. 498 */ 499 public GValueModel getValueTree(ElementWrapper element) { 500 GValueModel value_tree = null; 501 if(element != null) { 502 String namespace = element.getNamespace(); 503 if(namespace.length() > 0) { 504 MetadataSet mds = (MetadataSet) mds_hashtable.get(namespace); 505 if(mds != null) { 506 value_tree = mds.getValueTree(element); 507 } 508 } 509 } 510 return value_tree; 511 } 512 /** This method is called to import a metadata set. First a prompt is displayed to gather necessary details such as which metadata set to import. Once this information is gathered the method <i>importMDS(File)</i> is called with the appropriate filename. 513 * @return A boolean which is <i>true</i> if the metadata set has been imported successfully, <i>false</i> otherwise. 514 */ 515 public boolean importMDS() { 516 JFileChooser chooser = new JFileChooser(new File(Utility.METADATA_DIR)); 517 javax.swing.filechooser.FileFilter filter = new MDSFileFilter(); 518 chooser.setFileFilter(filter); 519 int returnVal = chooser.showDialog(Gatherer.g_man, Gatherer.dictionary.get("MSMPrompt.File_Import")); 520 if(returnVal == JFileChooser.APPROVE_OPTION) { 521 return importMDS(chooser.getSelectedFile(), true); 522 } 523 return false; 524 } 525 526 public boolean importMDS(File mds_file, boolean user_driven) { 527 // 1. Parse the new file. 528 MetadataSet mds_new = new MetadataSet(mds_file); 529 // Display a prompt asking how much of the value structure the user wishes to import. 530 if(user_driven) { 531 ExportMDSPrompt imdsp = new ExportMDSPrompt(this, false); 532 int result = imdsp.display(); 533 if(result == ExportMDSPrompt.EXPORT) { // Here export means the user didn't cancel. 534 switch(imdsp.getSelectedCondition()) { 535 case MetadataSet.NO_VALUES: 536 mds_new = new MetadataSet(mds_new, MetadataSet.NO_VALUES); 537 break; 538 case MetadataSet.SUBJECTS_ONLY: 539 mds_new = new MetadataSet(mds_new, MetadataSet.SUBJECTS_ONLY); 540 break; 541 default: // ALL_VALUES 542 // Don't do anything. 543 } 544 } 545 else { 546 mds_new = null; 547 } 548 imdsp.dispose(); 549 imdsp = null; 550 } 551 // Carry on importing the new collection 552 if(mds_new != null && mds_new.getDocument() != null) { 553 String family = mds_new.getNamespace(); 554 // 2. See if we have another metadata set of the same family 555 // already. If so retrieve it and merge. 556 boolean matched = false; 557 for(Enumeration keys = mds_hashtable.keys(); 558 keys.hasMoreElements();) { 559 String key = (String)keys.nextElement(); 560 if(key.equals(family)) { 561 matched = true; 562 MetadataSet mds_cur = (MetadataSet)mds_hashtable.get(key); 563 //ystem.err.println("Merging " + mds_new + " into " + mds_cur); 564 mergeMDS(mds_cur, mds_new); 565 } 566 } 567 if(!matched) { 568 ///ystem.err.println("Mapping " + family + " to " + mds_new); 569 mds_hashtable.put(family, mds_new); 570 } 571 // Fire setChanged() message. 572 fireSetChanged(mds_new); 573 return true; 574 } 575 // else we cancelled for some reason. 576 return false; 577 } 578 579 /** This method reloads all of the metadata sets that have been marked as included in this collection by entries in the collection configuration file. 580 */ 581 public void load() { 582 File source = new File(Gatherer.c_man.getCollectionMetadata()); 583 File files[] = source.listFiles(); 584 for(int i = 0; files != null && i < files.length; i++) { 585 if(files[i].getName().endsWith(".mds")) { 586 importMDS(files[i], false); 587 } 588 } 589 // If no current 'hidden' metadata set exists, create one. 590 if(getSet(HIDDEN) == null) { 591 createHidden(); 592 } 593 } 594 /** This method takes two metadata sets, the current one and a new one, and merges them. This merge takes place at an element level falling to lower levels as necessary (using <i>mergeMDE()</i> to merge elements and <i>mergeMDV()</i> to merge value trees. 595 * @param mds_current The currently loaded MetadataSet. 596 * @param mds_new A new MetadataSet you wish to merge in. 597 * @return A boolean with value <i>true</i> indicating if the merge was successful, otherwise <i>false</i> if errors were detected. 598 */ 599 public boolean mergeMDS(MetadataSet mds_cur, MetadataSet mds_new) { 600 // For a super quick check for equivelent trees, we compare the last changed values. 601 if(mds_cur.getLastChanged().equals(mds_new.getLastChanged())) { 602 // Exactly the same. Nothing to change. 603 return true; 604 } 605 // Show initial progress prompt. 606 prompt.startMerge(mds_new.size()); 607 boolean cancel = false; 608 // For each element in the new set 609 for(int i = 0; !cancel && i < mds_new.size(); i++) { 610 boolean cont = false; 611 Element mde_new = mds_new.getElement(i); 612 // See if the element already exists in the current set 613 Element mde_cur = mds_cur.getElement(mde_new.getAttribute("name")); 614 int option = Declarations.NO_ACTION; 615 while(!cont && !cancel) { 616 // We may be dealing with a brand new element, or possibly a 617 // renamed one. 618 if(mde_cur == null) { 619 // Provide merge, rename and skip options. 620 option = prompt.mDSPrompt(mds_cur, null, mds_new, mde_new); 621 } 622 else { 623 // If the two elements have equal structure we only have 624 // to worry about merging the values. 625 if(MSMUtils.elementsEqual(mds_cur, mde_cur, mds_new, mde_new, false)) { 626 cancel = !mergeMDV(mds_cur, mde_cur, mds_new, mde_new); 627 cont = true; 628 } 629 else { 630 // Provide add, merge and skip options. 631 option = prompt.mDSPrompt(mds_cur, mde_cur, mds_new, mde_new); 632 } 633 } 634 String reason = null; 635 switch(option) { 636 case Declarations.ADD: 637 // Only available to brand new elements, this options 638 // simply adds the element to the set. 639 reason = mds_cur.addElement(mde_new); 640 if(reason == null) { 641 cont = true; 642 } 643 else { 644 prompt.addFailed(mde_new, reason); 645 cont = false; 646 } 647 break; 648 case Declarations.CANCEL: 649 cancel = true; 650 cont = true; 651 break; 652 case Declarations.FORCE_MERGE: 653 // If the mde_cur is null, that means the users has asked 654 // to merge but hasn't choosen any element to merge with. 655 // Make the user select an element. 656 mde_cur = prompt.selectElement(mds_cur); 657 case Declarations.MERGE: 658 // This case in turn calls the mergeMDE method to perform 659 // the actual merging of the Elements. 660 if(mde_cur != null) { 661 cancel = !mergeMDE(mds_cur, mde_cur, mds_new, mde_new); 662 } 663 cont = true; 664 break; 665 case Declarations.RENAME: 666 ///ystem.err.println("Rename element"); 667 // This case adds the Element, but requires the user to 668 // enter a unique name. 669 String new_name = prompt.rename(mde_new); 670 if(new_name != null && new_name.length() > 0) { 671 reason = mds_cur.addElement(mde_new, new_name); 672 if(reason == null) { 673 mde_cur = mds_cur.getElement(new_name); 674 cont = true; 675 } 676 else { 677 prompt.renameFailed(mde_new, new_name, reason); 678 cont = false; 679 } 680 } 681 else { 682 if(new_name != null) { 683 prompt.renameFailed(mde_new, new_name, 684 "MSMPrompt.Invalid_Name"); 685 } 686 cont = false; 687 } 688 break; 689 case Declarations.REPLACE: 690 // Removes the existing Element then adds the new. 691 mds_cur.removeElement(mde_cur); 692 reason = mds_cur.addElement(mde_new); 693 if(reason == null) { 694 cont = true; 695 } 696 else { 697 prompt.removeFailed(mde_cur, reason); 698 cont = false; 699 } 700 break; 701 case Declarations.SKIP: 702 // Does not change the set. 703 cont = true; 704 break; 705 } 706 // Add this action to profile for later reference. 707 if(profiler == null) { 708 ///ystem.err.println("No Profiler"); 709 } 710 //profiler.addAction(mds_new.getFile().getAbsolutePath(), MSMUtils.getFullName(mde_new), option, MSMUtils.getFullName(mde_cur)); 711 } 712 prompt.incrementMerge(); 713 } 714 prompt.endMerge(); 715 return true; 716 } 717 718 /** This method allows for two metadata elements to be merged. Essentially merging existing elements give the users such options as keeping or replacing attribute elements as they are merged. 719 * @param mde_cur The Element that already exists in the current metadata sets. 720 * @param mde_new A new Element which has the same name as the current one but different data. 721 * @return A boolean that if <i>true</i> indicats the action was completed. If <i>false</i> then an error or user action has prevented the merge. 722 * TODO Implement 723 */ 724 public boolean mergeMDE(MetadataSet mds_cur, Element mde_cur, MetadataSet mds_new, Element mde_new) { 725 while(true) { 726 for(Node mdn_new = mde_new.getFirstChild(); mdn_new != null; mdn_new = mdn_new.getNextSibling()) { 727 // Only merge the nodes whose name is 'Attribute' 728 if(mdn_new.getNodeName().equals("Attribute")) { 729 Element att_new = (Element) mdn_new; 730 // Unfortunately some attributes, such as author, can have several occurances, so match each in turn. 731 Element temp[] = MSMUtils.getAttributeNodesNamed(mde_cur, att_new.getAttribute("name")); 732 for(int i = 0; temp != null && i < temp.length; i++) { 733 Element att_cur = temp[i]; 734 boolean cont = false; 735 int action = Declarations.NO_ACTION; 736 while(!cont) { 737 if(att_cur != null) { 738 if(!MSMUtils.attributesEqual(att_cur, att_new)) { 739 action = prompt.mDEPrompt(mde_cur, att_cur, mde_new, att_new); 740 } 741 else { 742 action = Declarations.SKIP; 743 } 744 } 745 else { 746 action = Declarations.ADD; 747 } 748 switch (action) { 749 case Declarations.REPLACE: 750 // Out with the old. 751 mde_cur.removeChild(att_cur); 752 case Declarations.ADD: 753 // Simply add the new attribute. No clash is possible as we have already tested for it. 754 MSMUtils.add(mde_cur, att_new); 755 cont = true; 756 break; 757 case Declarations.SKIP: 758 // Do nothing. Move on to next attribute. 759 cont = true; 760 break; 761 case Declarations.CANCEL: 762 return false; 763 default: 764 cont = false; 765 } 766 } 767 att_cur = null; 768 } 769 temp = null; 770 att_new = null; 771 } 772 } 773 return mergeMDV(mds_cur, mde_cur, mds_new, mde_new); 774 } 775 } 776 /** Merge two metadata value trees. 777 * @param mds_cur The current MetadataSet. 778 * @param mde_cur The current Element which acts as a value tree root. 779 * @param mds_new The MetadataSet we are merging in. 780 * @param mde_new The Element which acts as a value tree that we are merging in. 781 */ 782 public boolean mergeMDV(MetadataSet mds_cur, Element mde_cur, 783 MetadataSet mds_new, Element mde_new) { 784 // Remember we may be asked to merge with a current mdv of null. 785 return MSMUtils.updateValueTree(mds_cur, mde_cur, mds_new, mde_new); 786 } 787 788 public void removeElement(ElementWrapper element) { 789 // Retrieve the metadata set this element belongs to. 790 String namespace = element.getNamespace(); 791 MetadataSet set = (MetadataSet) mds_hashtable.get(namespace); 792 if(set != null) { 744 } 745 else { 746 action = Declarations.ADD; 747 } 748 switch (action) { 749 case Declarations.REPLACE: 750 // Out with the old. 751 mde_cur.removeChild(att_cur); 752 case Declarations.ADD: 753 // Simply add the new attribute. No clash is possible as we have already tested for it. 754 MSMUtils.add(mde_cur, att_new); 755 cont = true; 756 break; 757 case Declarations.SKIP: 758 // Do nothing. Move on to next attribute. 759 cont = true; 760 break; 761 case Declarations.CANCEL: 762 return false; 763 default: 764 cont = false; 765 } 766 } 767 att_cur = null; 768 } 769 temp = null; 770 att_new = null; 771 } 772 } 773 return mergeMDV(mds_cur, mde_cur, mds_new, mde_new); 774 } 775 } 776 /** Merge two metadata value trees. 777 * @param mds_cur The current MetadataSet. 778 * @param mde_cur The current Element which acts as a value tree root. 779 * @param mds_new The MetadataSet we are merging in. 780 * @param mde_new The Element which acts as a value tree that we are merging in. 781 */ 782 public boolean mergeMDV(MetadataSet mds_cur, Element mde_cur, 783 MetadataSet mds_new, Element mde_new) { 784 // Remember we may be asked to merge with a current mdv of null. 785 return MSMUtils.updateValueTree(mds_cur, mde_cur, mds_new, mde_new); 786 } 787 788 public void removeElement(ElementWrapper element) { 789 // Retrieve the metadata set this element belongs to. 790 String namespace = element.getNamespace(); 791 MetadataSet set = (MetadataSet) mds_hashtable.get(namespace); 792 if(set != null) { 793 793 // Bugger. Get the old name -before- we remove the element from the set. 794 794 String old_name = element.toString(); 795 795 // Remove the element. 796 796 set.removeElement(element.getElement()); 797 797 // Fire event. 798 799 800 798 fireElementChanged(new MSMEvent(this, null, old_name)); 799 } 800 else { 801 801 ///ystem.err.println("no such set " + namespace); 802 803 804 802 } 803 // No such set. No such element. 804 } 805 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 806 /** Remove a piece of metadata from a record or records[] and fire all the relevant events. 807 * @param records A FileNode[] of records to be changed. 808 * @param metadata The Metadata to remove. 809 */ 810 public void removeMetadata(long id, Metadata metadata, FileNode records[]) { 811 // Reset undo buffer 812 undo_buffer.clear(); 813 // Simplier than the others. Simply remove the metadata. 814 int action = MetaEditPrompt.CONFIRM; 815 // Now remove metadata from the selected file nodes. 816 for(int i = 0; action != MetaEditPrompt.CANCEL && i < records.length; i++) { 817 action = removeMetadata(id, records[i], metadata, action, (records.length > 1)); 818 } 819 // If we were cancelled we should undo any changes so far 820 if(action == MetaEditPrompt.CANCEL) { 821 for(Iterator keys = undo_buffer.keySet().iterator(); keys.hasNext(); ) { 822 FileNode record = (FileNode) keys.next(); 823 undoRemove(id, record); 824 } 825 } 826 } 827 /** Remove the specified listener from ourselves. 828 * @param listener The MSMListener in question. 829 */ 830 public void removeMSMListener(MSMListener listener) { 831 listeners.remove(listener); 832 } 833 834 public void removeSet(MetadataSet set) { 835 mds_hashtable.remove(set.getNamespace()); 836 fireSetChanged(set); 837 } 838 839 /** Rename the identifier of a given element to the name given. 840 * @param element The metadata element effected, as an ElementWrapper. 841 * @param new_name The String to use as the new name. 842 */ 843 public void renameElement(ElementWrapper element, String new_name) { 844 Element e = element.getElement(); 845 String old_name = element.toString(); 846 MSMUtils.setIdentifier(e, new_name); 847 fireElementChanged(new MSMEvent(this, element, old_name)); 848 old_name = null; 849 e = null; 850 } 851 /** A method to save the state of this metadata set manager. First we ensure that the names of all included metadata sets have been added to the collection configuration file, then all of the metadata sets contained are exported with full content to the collect/<col_name>/metadata/ directory. 852 */ 853 public void save() { 854 // Create the correct file to save these sets to... 855 File file = new File(Gatherer.self.getCollectionMetadata()); 856 // And make back ups of all existing metadata set files. 857 File temp[] = file.listFiles(); 858 for(int i = temp.length - 1; i >= 0; i--) { 859 if(temp[i].getName().endsWith(".mds") || temp[i].getName().endsWith(".mdv")) { 860 File backup = new File(temp[i].getAbsolutePath() + "~"); 861 backup.deleteOnExit(); 862 if(!temp[i].renameTo(backup)) { 863 Gatherer.println("Error in MetadataSetManager.save(): FileRenameException"); 864 } 865 } 866 } 867 // Now save the latest versions of the metadata sets. 868 for(Enumeration keys = mds_hashtable.keys(); keys.hasMoreElements(); ) { 869 String namespace = (String) keys.nextElement(); 870 MetadataSet set = (MetadataSet) mds_hashtable.get(namespace); 871 try { 872 File mds_file = null; 873 if(!namespace.equals("")) { 874 mds_file = new File(file, set.getNamespace() + ".mds"); 875 } 876 else { 877 mds_file = new File(file, "greenstone.mds"); 878 } 879 Utility.export(set.getDocument(), mds_file); 880 set.setFile(mds_file); 881 // Now for each element attempt to save its value tree. 882 NodeList elements = set.getElements(); 883 for(int i = elements.getLength() - 1; !namespace.equals("") && i >= 0; i--) { 884 ElementWrapper value_element = new ElementWrapper((Element)elements.item(i)); 885 GValueModel value_tree = set.getValueTree(value_element); 886 if(value_tree != null) { 887 File value_file = new File(file, value_element.toString() + ".mdv"); 888 ///ystem.err.println("Saving value file: " + value_file.toString()); 889 Utility.export(value_tree.getDocument(), value_file); 890 // If this is a hierarchy element, write hierarchy file. 891 if(value_element.getNamespace().equals(MetadataSetManager.HIDDEN) || value_tree.isHierarchy()) { 892 MetadataXML.write(value_tree, this, Gatherer.c_man.getCollectionEtc()); 893 } 894 } 895 else { 896 ///ystem.err.println("No value tree for " + value_element.toString()); 897 } 898 } 899 // Note that filenames might have changed so we better warn everyone. 900 fireSetChanged(set); 901 } 902 catch (Exception error) { 903 error.printStackTrace(); 904 } 905 } 906 profiler.save(); 907 } 908 /** Given a FileNode of the original file and the new FileNode, search for any metadata, either from a greenstone directory archive xml file, or from one of the registered 'plugin' parsers. 909 * @param source The source FileNode. 910 * @param destination The new FileNode. 911 */ 912 public final boolean searchForMetadata(FileNode destination, FileNode source, boolean folder_level) { 913 return searchForMetadata(destination, source, folder_level, false); 914 } 915 public final boolean searchForMetadata(FileNode destination, FileNode source, boolean folder_level, boolean dummy_run) { 916 ///atherer.println("MetadataSetManager.searchForMetadata()"); 917 return loader.searchForMetadata(destination, source, folder_level, dummy_run); 918 } 919 /** Build a vector of all the metadata sets that contain an element with the given name. 920 * @param name The name of an element as a String. 921 * @return A Vector of metadata sets. 922 * @see MSMPrompt (org.greenstone.gatherer.msm.MSMPrompt#selectSet) 923 */ 924 public Vector setsThatContain(String name) { 925 Vector result = new Vector(); 926 for(Enumeration keys = mds_hashtable.keys(); keys.hasMoreElements(); ) { 927 MetadataSet set = (MetadataSet) mds_hashtable.get(keys.nextElement()); 928 if(set.getElement(name) != null) { 929 result.add(set); 930 } 931 } 932 return result; 933 } 934 935 public final int size() { 936 return getSets().size(); 937 } 938 939 /** Update a piece of metadata connected to a record or records, ensuring the value tree is built properly, and correct messaging fired. 940 * @param records A FileNode[] of records, or directories, to add the specified metadata to. 941 * @param element The metadata element, contained within an ElementWrapper to base metadata on. 942 * @param value The value to assign to the metadata as a String. 943 * @param action The default action to take in the prompt. 944 * @param file_level If true then the metadata can be replaced normally, if false then we should actually use an add method instead. 945 * @return The Metadata we just assigned. 946 */ 947 public Metadata updateMetadata(long id, Metadata old_metadata, FileNode records[], String value_str, int action, boolean file_level) { 948 // Retrieve the new value node from the same value tree as the old metadata. 949 ElementWrapper element = old_metadata.getElement(); 950 GValueModel model = getValueTree(element); 951 GValueNode value = null; 952 if(model != null) { 953 value = model.addValue(value_str); 954 } 955 else { 956 value = new GValueNode(element.toString(), value_str); 957 } 958 // Create new metadata. 959 Metadata new_metadata = new Metadata(value); 960 // Reset the undo buffer 961 undo_buffer.clear(); 962 // And update the old with it. 963 if(action == -1) { 964 action = MetaEditPrompt.CONFIRM; 965 } 966 // And then update each selection file node. 967 for(int i = 0; action != MetaEditPrompt.CANCEL && i < records.length; i++) { 968 action = updateMetadata(id, records[i], old_metadata, new_metadata, action, (records.length > 1), file_level); 969 } 970 // If we were cancelled we should undo any changes so far 971 if(action == MetaEditPrompt.CANCEL) { 972 for(Iterator keys = undo_buffer.keySet().iterator(); keys.hasNext(); ) { 973 FileNode record = (FileNode) keys.next(); 974 undoUpdate(id, record); 975 } 976 } 977 // All done. Any events would have been fired within the record recursion. 978 return new_metadata; 979 } 980 981 /** Add a reference to a piece of metadata to the given FileNode. The whole method gets a wee bit messy as we have to allow for several different commands from users such as accumulate / overwrite, skip just this file or cancel the whole batch. Cancelling is especially problematic as we need to rollback any changes (within reason). 982 * It is also worth mentioning that despite its name, no actual metadata is added directly by this method. Instead a call to fireMetadataChanged() is issued, which is in turn processed by the GDMManager (which, given this method may have been called from GDMManager as well, means the cycle is complete. Um, that doesn't mean theres an infinite loop... I hope). 983 * @param id a long unique identifier shared by all actions caused by the same gesture. 984 * @param record the FileNode we are adding the metadata to. 985 * @param data the new Metadata. 986 * @param action the default action as an int. May require user interaction. 987 * @param fire_event <i>true</i> if this action should fire a metadata changed event, <i>false</i> if we are calling this as an affect of a previous event. (Don't want an infinitely recursive loop, do we). 988 * @param multiple_selection <i>true</i> if more than one file or folder was selected. 989 * @return an int specifying the current action. Thus changes in lower parts of the tree continue to effect other disjoint subtrees. 990 */ 991 private int addMetadata(long id, FileNode record, Metadata data, int action, boolean multiple_selection) { 992 // Super special exception for accumulate all action. We are going to add this metadata anyway, regardless of whats already there, so just add it. 993 if(action == MetaEditPrompt.ACCUMULATE_ALL || action == MetaEditPrompt.OVERWRITE_ALL) { 994 fireMetadataChanged(id, record, null, data); 995 } 996 else { 997 997 // Recover the metadata from this file. 998 998 ArrayList metadata = Gatherer.c_man.getCollection().gdm.getMetadata(record.getFile()); 999 999 // Most important test, we don't have to add the metadata if its already there! 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1000 if(!metadata.contains(data)) { 1001 // Record undo information for this file node. 1002 ArrayList undo = new ArrayList(); 1003 // Prepare for MEP 1004 int user_action = MetaEditPrompt.ACCUMULATE; 1005 String values = ""; 1006 // See if there is any existing metadata with the same name. If so make a string from all the values (bob, jim etc). 1007 int metadata_size = metadata.size(); 1008 for(int i = 0; i < metadata_size; i++) { 1009 Metadata current_data = (Metadata)metadata.get(i); 1010 if(current_data.getElement().equals(data.getElement())) { 1011 if(values.length() > 0) { 1012 values = values + ", "; 1013 } 1014 values = values + current_data.getValue(); 1015 } 1016 } 1017 // If we are confirming prompt for user_action. 1018 if(values.length() > 0 && action == MetaEditPrompt.CONFIRM) { 1019 MetaEditPrompt mep = new MetaEditPrompt(MetaEditPrompt.ADD_PROMPT, multiple_selection, record.getFile(), data.getElement().toString(), values, data.getValue()); 1020 user_action = mep.display(); 1021 } 1022 if(user_action == MetaEditPrompt.ACCUMULATE_ALL || user_action == MetaEditPrompt.CANCEL || user_action == MetaEditPrompt.OVERWRITE_ALL) { 1023 action = user_action; 1024 } 1025 // If we are overwriting we first remove all metadata with the same element, unless the metadata is non-file level is which case we leave it, and hope the accumulate vs overwrite will be followed during the determining of metadata assigned. 1026 if(action == MetaEditPrompt.OVERWRITE_ALL || user_action == MetaEditPrompt.OVERWRITE) { 1027 for(int i = metadata_size; i != 0; i--) { 1028 Metadata old_data = (Metadata)metadata.get(i - 1); 1029 if(old_data.getElement().equals(data.getElement()) && old_data.isFileLevel()) { 1030 // We have a match. Remove this metadata. 1031 fireMetadataChanged(id, record, old_data, null); 1032 // Add it to our undo buffer. 1033 undo.add(old_data); 1034 } 1035 } 1036 } 1037 // Ensure the metadata will accumulate or overwrite as the user wishes. 1038 if(user_action == MetaEditPrompt.ACCUMULATE || user_action == MetaEditPrompt.ACCUMULATE_ALL) { 1039 data.setAccumulate(true); 1040 } 1041 else if(user_action == MetaEditPrompt.OVERWRITE || user_action == MetaEditPrompt.OVERWRITE_ALL) { 1042 data.setAccumulate(false); 1043 } 1044 // Unless cancelled, add the metadata after checking we don't already have it in the metadata (obviously not if we're overwriting but we'd better check anyway). Also if we've skipped the file we should do so, but move on to the next child... 1045 if((user_action == MetaEditPrompt.ACCUMULATE || user_action == MetaEditPrompt.ACCUMULATE_ALL || user_action == MetaEditPrompt.OVERWRITE || user_action == MetaEditPrompt.OVERWRITE_ALL) && !metadata.contains(data)) { 1046 ///ystem.err.println("Adding metadata " + data); 1047 fireMetadataChanged(id, record, null, data); 1048 // The last element in undo is the new element. 1049 undo.add(data); 1050 } 1051 // Store the undo list in our undo buffer. 1052 undo_buffer.put(record, undo); 1053 } 1054 } 1055 // If we've been cancelled, roll back the addition. 1056 if(action == MetaEditPrompt.CANCEL) { 1057 undoAdd(id, record); 1058 } 1059 return action; 1060 } 1061 /** addMetadata(long, FileNode, Metadata, int, boolean) */ 1062 1063 /** Create the hidden mds, used for custom classifiers. */ 1064 private MetadataSet createHidden() { 1065 MetadataSet hidden_mds = new MetadataSet(Utility.METADATA_SET_TEMPLATE); 1066 hidden_mds.setAttribute("creator","The Gatherer"); 1067 hidden_mds.setAttribute("contact","gatherer@greenstone"); 1068 hidden_mds.setAttribute("description","A hidden metadata set used to create custom classifiers."); 1069 hidden_mds.setAttribute("family","Gatherer Hidden Metadata"); 1070 hidden_mds.setAttribute("lastchanged",""); 1071 hidden_mds.setAttribute("name","Gatherer Hidden Metadata"); 1072 hidden_mds.setAttribute("namespace",HIDDEN); 1073 mds_hashtable.put(HIDDEN, hidden_mds); 1074 fireSetChanged(hidden_mds); 1075 return hidden_mds; 1076 } 1077 1078 /** Creates a new profiler, which in turn will attempt to load previous profile information. */ 1079 private void loadProfiler() { 1080 profiler = new MSMProfiler(); 1081 addMSMListener(profiler); 1082 } 1083 1084 /** In order to remove metadata from the tree you first call this method providing it with the metadata you want removed. This will remove any occurance of said metadata from the given FileNode (using fireMetadataChanged()). 1085 * @param id a unique long identifier common to all actions caused by a single gesture. 1086 * @param record the FileNode who we are removing metadata from. 1087 * @param data the <strong>Metadata</strong> you wish removed from the tree. 1088 * @param action an <i>int</i> specifying the wanted prompting action. 1089 * @param fire_event <i>true</i> if this action should fire metadata changed events. 1090 * @param multiple_selection the number of records in the selection, as an <i>int</i>. Used to determine prompt controls. 1091 * @return an <i>int</i> specifying the current action. Thus changes in lower parts of the tree continue to effect other disjoint subtrees. 1092 */ 1093 private int removeMetadata(long id, FileNode record, Metadata data, int action, boolean multiple_selection) { 1094 ArrayList metadata = Gatherer.c_man.getCollection().gdm.getMetadata(record.getFile()); 1095 int user_action = MetaEditPrompt.REMOVE; 1096 // See if we even have this metadata. 1097 if(metadata.contains(data)) { 1098 ArrayList undo = new ArrayList(); 1099 1099 // We do have it. If action == CONFIRM, show user prompt. 1100 1101 1102 1103 1100 if(action == MetaEditPrompt.CONFIRM) { 1101 MetaEditPrompt mep = new MetaEditPrompt(MetaEditPrompt.REMOVE_PROMPT, multiple_selection, record.getFile(), data.getElement().toString(), data.getValue(), ""); 1102 user_action = mep.display(); 1103 } 1104 1104 // Set action to match the user_action under certain circumstances. 1105 1106 1107 1108 1109 1110 1111 1105 if(user_action == MetaEditPrompt.CANCEL || user_action == MetaEditPrompt.REMOVE_ALL) { 1106 action = user_action; 1107 } 1108 if(action == MetaEditPrompt.REMOVE_ALL || user_action == MetaEditPrompt.REMOVE) { 1109 fireMetadataChanged(id, record, data, null); 1110 undo.add(data); 1111 } 1112 1112 // Store undo information 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1113 undo_buffer.put(record, undo); 1114 } 1115 // If we've been cancelled higher up, undo action. 1116 if(action == MetaEditPrompt.CANCEL) { 1117 undoRemove(id, record); 1118 } 1119 return action; 1120 } 1121 1122 /** Rollback any changes made as part of a single metadata add process (only valid during the action itself, ie if a user presses cancel). 1123 * @param id the unique identify of all actions created as part of a single gesture, as a <i>long</i>. 1124 * @param record the FileNode whose metadata was changed. 1125 */ 1126 private void undoAdd(long id, FileNode record) { 1127 // Retrieve the undo data from the buffer 1128 ArrayList undo = (ArrayList) undo_buffer.get(record); 1129 // If there is no undo then we can't do anything, but there should be 1130 if(undo != null && undo.size() > 0) { 1131 1131 // The last piece of data in an add actions undo buffer is the metadata that was added 1132 1132 Metadata data = (Metadata) undo.remove(undo.size() - 1); 1133 1133 // Remove the data 1134 1134 fireMetadataChanged(id, record, data, null); 1135 1135 // If we removed other metadata when adding this metadata restore it too 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1136 for(int i = 0; i < undo.size(); i++) { 1137 Metadata old_data = (Metadata) undo.get(i); 1138 fireMetadataChanged(id, record, null, old_data); 1139 } 1140 } 1141 } 1142 /** Rollback any changes made as part of a single metadata remove process (only valid during the action itself, ie if a user presses cancel). 1143 * @param id the unique identify of all actions created as part of a single gesture, as a long. 1144 * @param record the FileNode metadata was removed from. 1145 */ 1146 private void undoRemove(long id, FileNode record) { 1147 // Retrieve undo information 1148 ArrayList undo = (ArrayList) undo_buffer.get(record); 1149 // Ensure that we have something to undo 1150 if(undo != null && undo.size() == 1) { 1151 1151 // The undo buffer should contain exactly one entry, the metadata removed 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1152 Metadata data = (Metadata) undo.get(0); 1153 fireMetadataChanged(id, record, null, data); 1154 } 1155 } 1156 /** Roll back any changes made as part of a single metadata update process (only valid during the action itself, ie if a user presses cancel). 1157 * @param id the unique identify of all actions created as part of a single gesture, as a long. 1158 * @param record the FileNode whose metadata was changed. 1159 */ 1160 private void undoUpdate(long id, FileNode record) { 1161 // Retrieve undo information 1162 ArrayList undo = (ArrayList) undo_buffer.get(record); 1163 if(undo != null && undo.size() == 2) { 1164 Metadata old_data = (Metadata) undo.get(0); 1165 Metadata new_data = (Metadata) undo.get(1); 1166 fireMetadataChanged(id, record, new_data, null); 1167 if(old_data != new_data) { // Correct reference comparison 1168 fireMetadataChanged(id, record, null, old_data); 1169 } 1170 } 1171 } 1172 1173 /** Used to update the values of one of the metadata elements within this node. Has the same trickiness as Add but only half the number of options. 1174 * @param id the unique identify of all actions created as part of a single gesture, as a <i>long</i>. 1175 * @param record the FileNode whose metadata we are changing. 1176 * @param old_data The old existing <strong>Metadata</strong>. 1177 * @param new_data The new updated <strong>Metadata</strong>. 1178 * @param action An <i>int</i> indicating what we are going to do about it. 1179 * @param multiple_selection The number of records in the selection, as an <i>int</i>. Used to determine prompt controls. 1180 * @return An <i>int</i> specifying the current action. Thus changes in lower parts of the tree continue to effect other disjoint subtrees. 1181 */ 1182 private int updateMetadata(long id, FileNode record, Metadata old_data, Metadata new_data, int action, boolean multiple_selection, boolean file_level) { 1183 ArrayList metadata; 1184 if(file_level) { 1185 metadata = Gatherer.c_man.getCollection().gdm.getMetadata(record.getFile()); 1186 } 1187 else { 1188 metadata = Gatherer.c_man.getCollection().gdm.getAllMetadata(record.getFile()); 1189 } 1190 int user_action = MetaEditPrompt.OVERWRITE; 1191 // Standard case of updating an existing metadata value. 1192 if(metadata.contains(old_data)) { 1193 ArrayList undo = new ArrayList(); 1194 1194 // If we are to prompt the user, do so. 1195 1196 1197 1198 1195 if(action == MetaEditPrompt.CONFIRM) { 1196 MetaEditPrompt mep = new MetaEditPrompt(MetaEditPrompt.UPDATE_PROMPT, multiple_selection, record.getFile(), old_data.getElement().toString(), old_data.getValue(), new_data.getValue()); 1197 user_action = mep.display(); 1198 } 1199 1199 // Some user actions should have a continuous effect. 1200 1201 1202 1200 if(user_action == MetaEditPrompt.OVERWRITE_ALL || user_action == MetaEditPrompt.CANCEL) { 1201 action = user_action; 1202 } 1203 1203 // And if the update chose update, do so. 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1204 if(action == MetaEditPrompt.OVERWRITE_ALL || user_action == MetaEditPrompt.OVERWRITE || user_action == MetaEditPrompt.UPDATE_ONCE) { 1205 ///ystem.err.println("Updating:\n"+old_data+"\nto\n"+new_data); 1206 // If this is file level then we can do a normal replace 1207 if(file_level) { 1208 fireMetadataChanged(id, record, old_data, new_data); 1209 undo.add(old_data); 1210 undo.add(new_data); 1211 } 1212 // Otherwise we are dealing with someone attempting to override inherited metadata, so we actually fire an add. To this end we add new data twice to the undo buffer, thus we can detect if this has happened. 1213 else { 1214 fireMetadataChanged(id, record, null, new_data); 1215 undo.add(new_data); 1216 undo.add(new_data); 1217 } 1218 } 1219 1219 // Store the undo information 1220 1221 1222 1223 1224 1225 1226 1227 1220 undo_buffer.put(record, undo); 1221 } 1222 // If we've been cancelled undo. 1223 if(action == MetaEditPrompt.CANCEL) { 1224 undoUpdate(id, record); 1225 } 1226 return action; 1227 } 1228 1228 } -
trunk/gli/src/org/greenstone/gatherer/msm/parsers/GreenstoneMetadataParser.java
r4316 r4365 62 62 */ 63 63 public class GreenstoneMetadataParser 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 64 extends LinkedHashMap 65 implements MetadataParser { 66 67 static final private int MAX_CFG_CACHE_SIZE = 10; 68 static final private int MAX_GDM_CACHE_SIZE = 10; 69 /** The default name and location for a collection configuration file (presuming that a collection file prefix will be added). */ 70 static final private String CONFIG_FILENAME = "etc" + File.separator + "collect.cfg"; 71 /** The pattern to match when searching for directory level assignments. */ 72 static final private String DIRECTORY_FILENAME = ".*"; 73 static final private String DIRECTORY_FILENAME_SUFFIX = "/.*"; 74 static final private String DESCRIPTION_ELEMENT = "Description"; 75 static final private String FILENAME_ELEMENT = "FileName"; 76 static final private String FILESET_ELEMENT = "FileSet"; 77 /** The name of a gdm file. */ 78 static final private String GIMPORT = "gimport"; 79 static final private String IMPORT = "import"; 80 static final private String METADATA_ELEMENT = "Metadata"; 81 static final private String METADATA_XML_FILENAME = "metadata.xml"; 82 static final private String MODE_ATTRIBUTE = "mode"; 83 static final private String NAME_ATTRIBUTE = "name"; 84 static final private String SEPARATOR = "/"; 85 86 /** A list of the collect.cfg paths that we should ignore. */ 87 private ArrayList ignore_list = new ArrayList(); 88 /** Has this process been cancelled. */ 89 private boolean dialog_cancelled = false; 90 /** A cache of previously parsed collection configuration files. */ 91 private CollectCFGCache cfg_cache = new CollectCFGCache(); 92 /** A mapping from BasicMetadata to their fully enabled Metadata incarnation. */ 93 private HashMap transform = new HashMap(); 94 95 /** Default constructor needed for dynamic class loading. */ 96 public GreenstoneMetadataParser() { 97 } 98 /** Locate and import any metadata parsed by this metadata parser given the file involved and its previous incarnation. */ 99 public boolean process(FileNode destination, FileNode origin, boolean folder_level, boolean dummy_run) { 100 ///atherer.println("GreenstoneMetadataParser: Process " + origin + ": "); 101 int counter = 0; 102 dialog_cancelled = false; 103 104 // 1. Determine what collection the file is in, and load/parse the appropriate collect.cfg. Cache collect.cfg object. 105 ///ystem.err.print("1 "); 106 // Start at the origin node file. If its a file get its parent directory. 107 File collection_dir = origin.getFile(); 108 if(collection_dir.isFile()) { 109 collection_dir = collection_dir.getParentFile(); 110 } 111 // We're currently in the importing directory so we'll go one more step up. 112 collection_dir = collection_dir.getParentFile(); 113 // We are looking for a directory which contains a etc/collect.cfg file and either an import or a gimport directory. 114 boolean found = false; 115 while(!found && collection_dir != null) { 116 File possible_cfg_file = new File(collection_dir, CONFIG_FILENAME); 117 File possible_gimport_directory = new File(collection_dir, GIMPORT); 118 File possible_import_directory = new File(collection_dir, IMPORT); 119 if(possible_cfg_file.exists() && (possible_gimport_directory.exists() || possible_import_directory.exists())) { 120 found = true; 121 ///ystem.err.println("Found greenstone collection at " + collection_dir.getAbsolutePath()); 122 } 123 else { 124 collection_dir = collection_dir.getParentFile(); 125 } 126 } 127 128 // Now retrieve the configuration file if there is one. 129 CollectCFG collect_cfg = null; 130 if(collection_dir != null) { 131 File collect_cfg_file = new File(collection_dir, CONFIG_FILENAME); 132 if(collect_cfg_file.exists()) { 133 collect_cfg = cfg_cache.get(collect_cfg_file); 134 } 135 } 136 137 // Continue only if we are sure this is a greenstone collection 138 if(collection_dir != null && collect_cfg != null) { 139 139 // 2. Attempt to merge in any mdses and make note of those that are successfully imported (by removing reference from collect.cfg). 140 140 ///ystem.err.print("2 "); 141 142 143 144 145 146 147 141 ArrayList mdses = collect_cfg.getMetadataSets(); 142 for(int i = 0; i < mdses.size(); i++) { 143 File mds_file = (File) mdses.get(i); 144 Gatherer.c_man.getCollection().msm.importMDS(mds_file, false); 145 } 146 mdses.clear(); 147 mdses = null; 148 148 149 149 // 3. Locate all of the metadata.xml files that may have an affect on the origin file. Make sure the metadata.xml closest to the origin files directory is last (to ensure property inheritance regarding accumulate/overwrite). 150 150 ///ystem.err.print("3 "); 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 151 ArrayList search_files = new ArrayList(); 152 File file = origin.getFile(); 153 String filename = null; 154 if(file.isFile()) { 155 filename = file.getName(); 156 file = file.getParentFile(); 157 } 158 while(!file.equals(collection_dir)) { 159 File test_file = new File(file, Utility.METADATA_XML); 160 if(test_file.exists()) { 161 search_files.add(0, new MetadataXMLFileSearch(test_file, filename)); 162 } 163 if(filename != null) { 164 filename = file.getName() + SEPARATOR + filename; 165 } 166 else { 167 filename = file.getName(); 168 } 169 file = file.getParentFile(); 170 } 171 filename = null; 172 file = null; 173 173 // Start with an initially empty ArrayList of metadata 174 174 ArrayList metadatum = new ArrayList(); 175 175 // Now search each of these metadata xml for metadata, remembering to accumulate or overwrite as we go along. 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 176 for(int i = 0; i < search_files.size(); i++) { 177 MetadataXMLFileSearch a_search = (MetadataXMLFileSearch) search_files.get(i); 178 ///ystem.err.println("Search " + a_search.file.getAbsolutePath() + " for " + (a_search.filename != null ? a_search.filename : ".*")); 179 // Retrieve the document 180 BasicGDMDocument document = getDocument(a_search.file); 181 if(document != null) { 182 // If this is a dummy run, our original source file is actually the metadata.xml file and we retrieve all metadata for this collection, as if accumulated! 183 if(dummy_run) { 184 metadatum = document.getAllMetadata(); 185 } 186 else { 187 metadatum = document.getMetadata(a_search.filename, metadatum, true); 188 } 189 document = null; 190 } 191 a_search = null; 192 } 193 search_files = null; 194 194 // Finally assign the metadata 195 195 ///ystem.err.println("Found " + metadatum.size() + " pieces of metadata for " + destination); 196 if(metadatum.size() > 0) { 197 addMetadata(destination, metadatum, collection_dir, collect_cfg, dummy_run); 196 if(metadatum.size() > 0) { 197 addMetadata(destination, metadatum, collection_dir, collect_cfg, dummy_run); 198 } 199 } 200 else { 201 ///ystem.err.println("Not a greenstone collection (no collect.cfg found)."); 202 } 203 return dialog_cancelled; 204 } 205 206 protected boolean removeEldestEntry(java.util.Map.Entry entry) { 207 return (size() > MAX_GDM_CACHE_SIZE); 208 } 209 210 private void addMetadata(FileNode destination, ArrayList metadatum, File collection_dir, CollectCFG collect_cfg, boolean dummy_run) { 211 ///ystem.err.print("6 "); 212 // Used in a complicated test later on. 213 for(int i = 0; !dialog_cancelled && i < metadatum.size(); i++) { 214 BasicMetadata basic_metadata = (BasicMetadata) metadatum.get(i); 215 BasicMetadata metadata = (BasicMetadata) metadatum.get(i); 216 metadata.collection = collection_dir; 217 Metadata final_metadata = null; 218 // If this BasicMetadata already exists in the transform cache then we can save ourselves a lot of work. 219 SoftReference reference = (SoftReference) transform.get(basic_metadata); 220 if(reference != null) { 221 final_metadata = (Metadata) reference.get(); 222 } 223 if(final_metadata == null) { 224 ///ystem.err.println("No existing Metadata object for BasicMetadata: " + basic_metadata); 225 // 6a. Check if an hfile is associated with this metadata, and if so load it, cache it in the collection.cfg object, then resolve metadata value index. 226 HFile h_file = collect_cfg.getHFile(metadata.element); 227 if(h_file != null && !dummy_run) { 228 ///ystem.err.print(metadata.value + " maps to "); 229 metadata.value = h_file.getValue(metadata.value); 230 ///ystem.err.println(metadata.value); 231 } 232 h_file = null; 233 // 6b. Check if there is a profile regarding the current metadata. 234 ///ystem.err.println("Retrieve existing action: " + collection_dir.getAbsolutePath() + ", " + metadata.element); 235 if(Gatherer.c_man.getCollection().msm.profiler.containsAction(collection_dir.getAbsolutePath(), metadata.element)) { 236 String new_element_name = Gatherer.c_man.getCollection().msm.profiler.getAction(collection_dir.getAbsolutePath(), metadata.element); 237 ///ystem.err.println("Profile result = " + new_element_name); 238 if(new_element_name == null) { 239 metadata = null; 240 } 241 else { 242 metadata.element = new_element_name; 243 } 244 new_element_name = null; 245 } 246 ///atherer.println("Assigning metadata."); 247 if(metadata != null) { 248 // 6c. Try to add metadata. If there is no matching metadata element: 249 ElementWrapper element = Gatherer.c_man.getCollection().msm.getElement(metadata.element); 250 // Arg. The element returned may come from the Greenstone dls, which of course should never be involved during importing. To solve check the namespace isn't "" and if it is nullify the element. Nullify. NULLIFY, Bwuhahahaha... 251 if(element != null && element.getNamespace().equals("")) { 252 element = null; 253 } 254 // 6ci. If no match exists, prompt the user to add/merge with specific metadata element. The user can also choose to ignore this metadata. 255 if(element == null) { 256 element = selectElement(metadata.element); 257 if(!dialog_cancelled) { 258 // 6ciii. If either of the above work, remember to add to profile. 259 if(element == null) { 260 ///ystem.err.println("Adding profile action: " + collection_dir.getAbsolutePath() + ", " + metadata.element + ", null"); 261 Gatherer.c_man.getCollection().msm.profiler.addAction(collection_dir.getAbsolutePath(), metadata.element, null); 262 } 263 else { 264 ///ystem.err.println("Adding profile action: " + collection_dir.getAbsolutePath() + ", " + metadata.element + ", " + element.getName()); 265 Gatherer.c_man.getCollection().msm.profiler.addAction(collection_dir.getAbsolutePath(), metadata.element, element.getName()); 266 } 267 } 268 } 269 // - Add metadata 270 if(!dummy_run && element != null && !dialog_cancelled) { 271 ///ystem.err.println("Retrieve the value tree for " + element.toString()); 272 GValueModel model = Gatherer.c_man.getCollection().msm.getValueTree(element); 273 if(model != null) { 274 GValueNode node = model.addValue(metadata.value); 275 final_metadata = new Metadata(element, node); 276 ///ystem.err.println("Adding final metadata: " + metadata.toString()); 277 node = null; 278 } 279 model = null; 280 } 281 element = null; 282 } 283 // If we have successfully created a Metadata from the BasicMetadata, store it 284 if(final_metadata != null && !dialog_cancelled) { 285 transform.put(basic_metadata, new SoftReference(final_metadata)); 286 ///ystem.err.println("Add a Metadata object for BasicMetadata: " + basic_metadata); 287 } 288 } 289 else { 290 ///ystem.err.println("Found a Metadata object for BasicMetadata: " + basic_metadata); 291 } 292 if(!dummy_run && final_metadata != null && !dialog_cancelled) { 293 final_metadata.setAccumulate(metadata.accumulates); 294 // Now we can finally add the metadata. 295 ///ystem.err.println("Adding Metadata: " + final_metadata); 296 Gatherer.c_man.getCollection().msm.fireMetadataChanged(0, destination, null, final_metadata); 297 } 298 // Otherwise there is no way to add this metadata. No value model no metadata value. 299 final_metadata = null; 300 metadata = null; 301 } 302 } 303 304 /** Determine the different suffix between two string. 305 * @param base_str The base <strong>String</strong>, expected to be the short of the two strings provided. 306 * @param target_str The target <strong>String</strong>, whose differing suffix is returned. 307 * @return A <strong>String</strong> containing the suffix from target which is different from base. 308 */ 309 private String diff(String base_str, String target_str) { 310 StringTokenizer base_tokenizer = new StringTokenizer(base_str, File.separator); 311 StringTokenizer target_tokenizer = new StringTokenizer(target_str, File.separator); 312 String base = null; 313 String target = null; 314 while(base_tokenizer.hasMoreTokens() && (base = base_tokenizer.nextToken()).equals((target = target_tokenizer.nextToken()))) { 315 } 316 StringBuffer result = new StringBuffer(target); 317 while(target_tokenizer.hasMoreTokens()) { 318 result.append(File.separator); 319 result.append(target_tokenizer.nextToken()); 320 } 321 return result.toString(); 322 } 323 324 /** Retrieve the BasicGDMDocument found at the given file, or null if there is no such file or if it isn't a valid BasicGDMDocument. */ 325 private BasicGDMDocument getDocument(File file) { 326 ///ystem.err.println("Get Document at: " + file.getAbsolutePath()); 327 BasicGDMDocument document = null; 328 if(!ignore_list.contains(file) && file.exists()) { 329 // Check cache 330 SoftReference reference = (SoftReference) get(file); 331 if(reference != null) { 332 ///ystem.err.println("Hit!!"); 333 document = (BasicGDMDocument) reference.get(); 334 reference = null; 335 } 336 // If that didn't work try to parse in the document 337 if(document == null) { 338 ///ystem.err.println("Miss or stale reference."); 339 document = new BasicGDMDocument(file); 340 if(document.isValid()) { 341 put(file, new SoftReference(document)); 342 } 343 else { 344 ///ystem.err.println(file.getAbsolutePath() + " is not a valid GDM XML file."); 345 ignore_list.add(file); 346 document = null; 347 } 348 } 349 } 350 else { 351 ///ystem.err.println("Ignoring file or file doesn't exists."); 352 } 353 return document; 354 } 355 356 357 /** Display a prompt allowing a user to select a metadata element to attempt to force add/merge or ignore a metadata element to. For instance an old version of a metadata.xml from the DLS collection might have an assigned metadata value "Publisher=EC Courier", however Publisher won't automatically match to any metadata set. This prompt will be displayed, and some effort will be made to systematically locate the appropriate set. In this case this should be the DLS metadata set as dls.Publisher should be the closest match. Regardless the element selected is returned. 358 * @param element_name The name of the element we are trying to add, as a <strong>String</strong>. 359 * @return The <strong>ElementWrapper</strong> choosen by the user, or <i>null</i> to skip this metadata element. 360 */ 361 private ElementWrapper selectElement(String element_name) { 362 ElementWrapper result = Gatherer.c_man.getCollection().msm.prompt.selectElement(element_name); 363 dialog_cancelled = Gatherer.c_man.getCollection().msm.prompt.wasDialogCancelled(); 364 return result; 365 } 366 367 /** A 'basic' version of the more complete GDMDocument used elsewhere, this object provides the same functionality except that it doesn't use Metadata objects. These objects require live references to elements within the MetadataSetManager and GValueModels, but these may not yet exist (and indeed may never exist) for metadata parsed from metadata.xml's outside of our current collection. Thus this class returns a String (or an ArrayList of Strings) when asked for the metadata associated with a certain file. Also notice that this class provides no constructor method for creating a blank document, nor does it ever need a reference to the Gatherer.*/ 368 private class BasicGDMDocument 369 extends HashMap { 370 /** The document this class sources its data from. */ 371 private Document base_document; 372 /** This constructor takes the original document and parsed out and stores metadata with its association to filenames. */ 373 public BasicGDMDocument(File file) { 374 ///ystem.err.println("New BasicGDMDocument: " + file.getAbsolutePath()); 375 base_document = Utility.parse(file.getAbsolutePath(), false); 376 } 377 /** Retrieve all of the metadata in this file. */ 378 public ArrayList getAllMetadata() { 379 ArrayList metadatum = new ArrayList(); 380 // Don't search the cache as this would never have been added. 381 try { 382 // Retrieve the document element. 383 Element directorymetadata_element = base_document.getDocumentElement(); 384 // Iterate through the filesets, checking the FileName child element against the target file's name using regular expression matching. 385 NodeList fileset_elements = directorymetadata_element.getElementsByTagName(FILESET_ELEMENT); 386 for(int i = 0; i < fileset_elements.getLength(); i++) { 387 Element fileset_element = (Element) fileset_elements.item(i); 388 NodeList filename_elements = fileset_element.getElementsByTagName(FILENAME_ELEMENT); 389 for(int j = 0; j < filename_elements.getLength(); j++) { 390 Element filename_element = (Element) filename_elements.item(j); 391 // If they match add all of the metadata found in the Description child element, overwriting any metadata with the same element 392 NodeList description_elements = fileset_element.getElementsByTagName(DESCRIPTION_ELEMENT); 393 for(int k = 0; k < description_elements.getLength(); k++) { 394 Element description_element = (Element) description_elements.item(k); 395 NodeList metadata_elements = description_element.getElementsByTagName(METADATA_ELEMENT); 396 for(int l = 0; l < metadata_elements.getLength(); l++) { 397 Element metadata_element = (Element) metadata_elements.item(l); 398 String element = metadata_element.getAttribute(NAME_ATTRIBUTE); 399 BasicMetadata metadata = new BasicMetadata(element, Utility.METADATA_XML, true); 400 // Remove any previous values for this metadata element. 401 for(int m = metadatum.size() - 1; m >= 0; m--) { 402 BasicMetadata old_metadata = (BasicMetadata) metadatum.get(m); 403 if(old_metadata.element.equals(element)) { 404 metadatum.remove(m); 405 } 406 old_metadata = null; 198 407 } 199 } 200 else { 201 ///ystem.err.println("Not a greenstone collection (no collect.cfg found)."); 202 } 203 return dialog_cancelled; 204 } 205 206 protected boolean removeEldestEntry(java.util.Map.Entry entry) { 207 return (size() > MAX_GDM_CACHE_SIZE); 208 } 209 210 private void addMetadata(FileNode destination, ArrayList metadatum, File collection_dir, CollectCFG collect_cfg, boolean dummy_run) { 211 ///ystem.err.print("6 "); 212 // Used in a complicated test later on. 213 for(int i = 0; !dialog_cancelled && i < metadatum.size(); i++) { 214 BasicMetadata basic_metadata = (BasicMetadata) metadatum.get(i); 215 BasicMetadata metadata = (BasicMetadata) metadatum.get(i); 216 metadata.collection = collection_dir; 217 Metadata final_metadata = null; 218 // If this BasicMetadata already exists in the transform cache then we can save ourselves a lot of work. 219 SoftReference reference = (SoftReference) transform.get(basic_metadata); 220 if(reference != null) { 221 final_metadata = (Metadata) reference.get(); 408 // Add the completed metadata and clean up 409 metadatum.add(metadata); 410 metadata = null; 411 element = null; 412 metadata_element = null; 413 } 414 metadata_elements = null; 415 description_element = null; 416 } 417 description_elements = null; 418 filename_element = null; 419 } 420 filename_elements = null; 421 fileset_element = null; 422 } 423 fileset_elements = null; 424 directorymetadata_element = null; 425 } 426 catch (Exception error) { 427 Gatherer.self.printStackTrace(error); 428 } 429 return metadatum; 430 } 431 432 /** Retrieve any metadata associated with a certain file. If filename is null we are attempting to find directory level metadata. */ 433 public ArrayList getMetadata(String filename, ArrayList metadatum_so_far, boolean folder_level) { 434 ///ystem.err.println("Retrieving metadata for: " + filename); 435 ArrayList metadatum = null; 436 // We start by attempting to retrieve this metadata from the cache. 437 if(filename != null) { 438 metadatum = (ArrayList) get(filename); 439 } 440 else { 441 metadatum = (ArrayList) get(DIRECTORY_FILENAME); 442 } 443 // If that failed we consult the document for metadata. 444 if(metadatum == null) { 445 metadatum = new ArrayList(); 446 if(metadatum_so_far == null) { 447 metadatum = new ArrayList(); 448 } 449 else { 450 metadatum = metadatum_so_far; 451 } 452 try { 453 // Retrieve the document element. 454 Element directorymetadata_element = base_document.getDocumentElement(); 455 // Iterate through the filesets, checking the FileName child element against the target file's name using regular expression matching. 456 NodeList fileset_elements = directorymetadata_element.getElementsByTagName(FILESET_ELEMENT); 457 for(int i = 0; i < fileset_elements.getLength(); i++) { 458 Element fileset_element = (Element) fileset_elements.item(i); 459 NodeList filename_elements = fileset_element.getElementsByTagName(FILENAME_ELEMENT); 460 for(int j = 0; j < filename_elements.getLength(); j++) { 461 Element filename_element = (Element) filename_elements.item(j); 462 String filename_text = MSMUtils.getValue(filename_element); 463 // Crappy. There are apparently two ways of assigning, say, directory level metadata to anything in the ac01ne directory from a parent directories metadata.xml. 464 // The developers guide way: ac01ne/.* 465 // The dls way: ac01ne 466 // So the three tests are 467 //System.err.println("Check for: " + (filename != null ? filename : ".*")); 468 //System.err.println("Folder level = " + folder_level); 469 //System.err.println("filename != null && '" + filename + "'.matches('" + filename_text + "') = " + (filename != null ? filename.matches(filename_text) : false)); 470 //System.err.println("filename != null && '" + filename + "'.matches('" + filename_text + DIRECTORY_FILENAME_SUFFIX + "') = " + (filename != null ? filename.matches(filename_text + DIRECTORY_FILENAME_SUFFIX) : false)); 471 //System.err.println("filename == null && '" + filename_text + "'.equals('.*') = " + (filename == null ? filename_text.equals(DIRECTORY_FILENAME) : false)); 472 if((filename != null && (filename.matches(filename_text) || filename.matches(filename_text))) 473 || (filename == null && filename_text.equals(DIRECTORY_FILENAME))) { 474 ///ystem.err.println("Match: " + (filename != null ? filename : ".*") + " => " + filename_text); 475 // If they match add all of the metadata found in the Description child element, remembering to abide by desired mode (accumulate vs. overwrite). 476 NodeList description_elements = fileset_element.getElementsByTagName(DESCRIPTION_ELEMENT); 477 for(int k = 0; k < description_elements.getLength(); k++) { 478 Element description_element = (Element) description_elements.item(k); 479 NodeList metadata_elements = description_element.getElementsByTagName(METADATA_ELEMENT); 480 for(int l = 0; l < metadata_elements.getLength(); l++) { 481 Element metadata_element = (Element) metadata_elements.item(l); 482 String element = metadata_element.getAttribute(NAME_ATTRIBUTE); 483 ///ystem.err.println("Found element: " + element); 484 //String language = metadata_element.getAttribute("language"); 485 String mode = metadata_element.getAttribute(MODE_ATTRIBUTE); 486 // Add the new metadata to our list of metadata for this target file. 487 String value = Utility.stripNL(MSMUtils.getValue(metadata_element)); 488 ///ystem.err.println("Found value: " + element); 489 BasicMetadata metadata = new BasicMetadata(element, value, mode.equals("accumulate")); 490 // If mode is overwrite, then remove any previous values for this metadata element. 491 if(!metadata.accumulates) { 492 for(int m = metadatum.size() - 1; m >= 0; m--) { 493 BasicMetadata old_metadata = (BasicMetadata) metadatum.get(m); 494 if(old_metadata.element.equals(element)) { 495 metadatum.remove(m); 496 } 497 old_metadata = null; 498 } 499 } 500 mode = null; 501 502 // Add the completed metadata and clean up 503 metadatum.add(metadata); 504 metadata = null; 505 value = null; 506 element = null; 507 metadata_element = null; 508 } 509 metadata_elements = null; 510 description_element = null; 222 511 } 223 if(final_metadata == null) { 224 ///ystem.err.println("No existing Metadata object for BasicMetadata: " + basic_metadata); 225 // 6a. Check if an hfile is associated with this metadata, and if so load it, cache it in the collection.cfg object, then resolve metadata value index. 226 HFile h_file = collect_cfg.getHFile(metadata.element); 227 if(h_file != null && !dummy_run) { 228 ///ystem.err.print(metadata.value + " maps to "); 229 metadata.value = h_file.getValue(metadata.value); 230 ///ystem.err.println(metadata.value); 231 } 232 h_file = null; 233 // 6b. Check if there is a profile regarding the current metadata. 234 ///ystem.err.println("Retrieve existing action: " + collection_dir.getAbsolutePath() + ", " + metadata.element); 235 if(Gatherer.c_man.getCollection().msm.profiler.containsAction(collection_dir.getAbsolutePath(), metadata.element)) { 236 String new_element_name = Gatherer.c_man.getCollection().msm.profiler.getAction(collection_dir.getAbsolutePath(), metadata.element); 237 ///ystem.err.println("Profile result = " + new_element_name); 238 if(new_element_name == null) { 239 metadata = null; 240 } 241 else { 242 metadata.element = new_element_name; 243 } 244 new_element_name = null; 245 } 246 ///atherer.println("Assigning metadata."); 247 if(metadata != null) { 248 // 6c. Try to add metadata. If there is no matching metadata element: 249 ElementWrapper element = Gatherer.c_man.getCollection().msm.getElement(metadata.element); 250 // Arg. The element returned may come from the Greenstone dls, which of course should never be involved during importing. To solve check the namespace isn't "" and if it is nullify the element. Nullify. NULLIFY, Bwuhahahaha... 251 if(element != null && element.getNamespace().equals("")) { 252 element = null; 253 } 254 // 6ci. If no match exists, prompt the user to add/merge with specific metadata element. The user can also choose to ignore this metadata. 255 if(element == null) { 256 element = selectElement(metadata.element); 257 if(!dialog_cancelled) { 258 // 6ciii. If either of the above work, remember to add to profile. 259 if(element == null) { 260 ///ystem.err.println("Adding profile action: " + collection_dir.getAbsolutePath() + ", " + metadata.element + ", null"); 261 Gatherer.c_man.getCollection().msm.profiler.addAction(collection_dir.getAbsolutePath(), metadata.element, null); 262 } 263 else { 264 ///ystem.err.println("Adding profile action: " + collection_dir.getAbsolutePath() + ", " + metadata.element + ", " + element.getName()); 265 Gatherer.c_man.getCollection().msm.profiler.addAction(collection_dir.getAbsolutePath(), metadata.element, element.getName()); 266 } 267 } 268 } 269 // - Add metadata 270 if(!dummy_run && element != null && !dialog_cancelled) { 271 ///ystem.err.println("Retrieve the value tree for " + element.toString()); 272 GValueModel model = Gatherer.c_man.getCollection().msm.getValueTree(element); 273 if(model != null) { 274 GValueNode node = model.addValue(metadata.value); 275 final_metadata = new Metadata(element, node); 276 ///ystem.err.println("Adding final metadata: " + metadata.toString()); 277 node = null; 278 } 279 model = null; 280 } 281 element = null; 282 } 283 // If we have successfully created a Metadata from the BasicMetadata, store it 284 if(final_metadata != null && !dialog_cancelled) { 285 transform.put(basic_metadata, new SoftReference(final_metadata)); 286 ///ystem.err.println("Add a Metadata object for BasicMetadata: " + basic_metadata); 287 } 288 } 289 else { 290 ///ystem.err.println("Found a Metadata object for BasicMetadata: " + basic_metadata); 291 } 292 if(!dummy_run && final_metadata != null && !dialog_cancelled) { 293 final_metadata.setAccumulate(metadata.accumulates); 294 // Now we can finally add the metadata. 295 ///ystem.err.println("Adding Metadata: " + final_metadata); 296 Gatherer.c_man.getCollection().msm.fireMetadataChanged(0, destination, null, final_metadata); 297 } 298 // Otherwise there is no way to add this metadata. No value model no metadata value. 299 final_metadata = null; 300 metadata = null; 301 } 302 } 303 304 /** Determine the different suffix between two string. 305 * @param base_str The base <strong>String</strong>, expected to be the short of the two strings provided. 306 * @param target_str The target <strong>String</strong>, whose differing suffix is returned. 307 * @return A <strong>String</strong> containing the suffix from target which is different from base. 308 */ 309 private String diff(String base_str, String target_str) { 310 StringTokenizer base_tokenizer = new StringTokenizer(base_str, File.separator); 311 StringTokenizer target_tokenizer = new StringTokenizer(target_str, File.separator); 312 String base = null; 313 String target = null; 314 while(base_tokenizer.hasMoreTokens() && (base = base_tokenizer.nextToken()).equals((target = target_tokenizer.nextToken()))) { 315 } 316 StringBuffer result = new StringBuffer(target); 317 while(target_tokenizer.hasMoreTokens()) { 318 result.append(File.separator); 319 result.append(target_tokenizer.nextToken()); 320 } 321 return result.toString(); 322 } 323 324 /** Retrieve the BasicGDMDocument found at the given file, or null if there is no such file or if it isn't a valid BasicGDMDocument. */ 325 private BasicGDMDocument getDocument(File file) { 326 ///ystem.err.println("Get Document at: " + file.getAbsolutePath()); 327 BasicGDMDocument document = null; 328 if(!ignore_list.contains(file) && file.exists()) { 329 // Check cache 330 SoftReference reference = (SoftReference) get(file); 331 if(reference != null) { 332 ///ystem.err.println("Hit!!"); 333 document = (BasicGDMDocument) reference.get(); 334 reference = null; 335 } 336 // If that didn't work try to parse in the document 337 if(document == null) { 338 ///ystem.err.println("Miss or stale reference."); 339 document = new BasicGDMDocument(file); 340 if(document.isValid()) { 341 put(file, new SoftReference(document)); 342 } 343 else { 344 ///ystem.err.println(file.getAbsolutePath() + " is not a valid GDM XML file."); 345 ignore_list.add(file); 346 document = null; 347 } 348 } 349 } 350 else { 351 ///ystem.err.println("Ignoring file or file doesn't exists."); 352 } 353 return document; 354 } 355 356 357 /** Display a prompt allowing a user to select a metadata element to attempt to force add/merge or ignore a metadata element to. For instance an old version of a metadata.xml from the DLS collection might have an assigned metadata value "Publisher=EC Courier", however Publisher won't automatically match to any metadata set. This prompt will be displayed, and some effort will be made to systematically locate the appropriate set. In this case this should be the DLS metadata set as dls.Publisher should be the closest match. Regardless the element selected is returned. 358 * @param element_name The name of the element we are trying to add, as a <strong>String</strong>. 359 * @return The <strong>ElementWrapper</strong> choosen by the user, or <i>null</i> to skip this metadata element. 360 */ 361 private ElementWrapper selectElement(String element_name) { 362 ElementWrapper result = Gatherer.c_man.getCollection().msm.prompt.selectElement(element_name); 363 dialog_cancelled = Gatherer.c_man.getCollection().msm.prompt.wasDialogCancelled(); 364 return result; 365 } 366 367 /** A 'basic' version of the more complete GDMDocument used elsewhere, this object provides the same functionality except that it doesn't use Metadata objects. These objects require live references to elements within the MetadataSetManager and GValueModels, but these may not yet exist (and indeed may never exist) for metadata parsed from metadata.xml's outside of our current collection. Thus this class returns a String (or an ArrayList of Strings) when asked for the metadata associated with a certain file. Also notice that this class provides no constructor method for creating a blank document, nor does it ever need a reference to the Gatherer.*/ 368 private class BasicGDMDocument 369 extends HashMap { 370 /** The document this class sources its data from. */ 371 private Document base_document; 372 /** This constructor takes the original document and parsed out and stores metadata with its association to filenames. */ 373 public BasicGDMDocument(File file) { 374 ///ystem.err.println("New BasicGDMDocument: " + file.getAbsolutePath()); 375 base_document = Utility.parse(file.getAbsolutePath(), false); 376 } 377 /** Retrieve all of the metadata in this file. */ 378 public ArrayList getAllMetadata() { 379 ArrayList metadatum = new ArrayList(); 380 // Don't search the cache as this would never have been added. 381 try { 382 // Retrieve the document element. 383 Element directorymetadata_element = base_document.getDocumentElement(); 384 // Iterate through the filesets, checking the FileName child element against the target file's name using regular expression matching. 385 NodeList fileset_elements = directorymetadata_element.getElementsByTagName(FILESET_ELEMENT); 386 for(int i = 0; i < fileset_elements.getLength(); i++) { 387 Element fileset_element = (Element) fileset_elements.item(i); 388 NodeList filename_elements = fileset_element.getElementsByTagName(FILENAME_ELEMENT); 389 for(int j = 0; j < filename_elements.getLength(); j++) { 390 Element filename_element = (Element) filename_elements.item(j); 391 // If they match add all of the metadata found in the Description child element, overwriting any metadata with the same element 392 NodeList description_elements = fileset_element.getElementsByTagName(DESCRIPTION_ELEMENT); 393 for(int k = 0; k < description_elements.getLength(); k++) { 394 Element description_element = (Element) description_elements.item(k); 395 NodeList metadata_elements = description_element.getElementsByTagName(METADATA_ELEMENT); 396 for(int l = 0; l < metadata_elements.getLength(); l++) { 397 Element metadata_element = (Element) metadata_elements.item(l); 398 String element = metadata_element.getAttribute(NAME_ATTRIBUTE); 399 BasicMetadata metadata = new BasicMetadata(element, Utility.METADATA_XML, true); 400 // Remove any previous values for this metadata element. 401 for(int m = metadatum.size() - 1; m >= 0; m--) { 402 BasicMetadata old_metadata = (BasicMetadata) metadatum.get(m); 403 if(old_metadata.element.equals(element)) { 404 metadatum.remove(m); 405 } 406 old_metadata = null; 407 } 408 // Add the completed metadata and clean up 409 metadatum.add(metadata); 410 metadata = null; 411 element = null; 412 metadata_element = null; 413 } 414 metadata_elements = null; 415 description_element = null; 416 } 417 description_elements = null; 418 filename_element = null; 419 } 420 filename_elements = null; 421 fileset_element = null; 422 } 423 fileset_elements = null; 424 directorymetadata_element = null; 425 } 426 catch (Exception error) { 427 Gatherer.self.printStackTrace(error); 428 } 429 return metadatum; 430 } 512 description_elements = null; 513 } 514 else { 515 ///ystem.err.println("No Match!"); 516 } 517 filename_text = null; 518 filename_element = null; 519 } 520 filename_elements = null; 521 fileset_element = null; 522 } 523 fileset_elements = null; 524 directorymetadata_element = null; 525 } 526 catch (Exception error) { 527 Gatherer.self.printStackTrace(error); 528 } 529 // Cache the result, given that these external metadata.xmls are taken to be static at the time of reading (if you happen to be sourcing information from a opened collection that someone is working on, too bad. 530 if(filename != null) { 531 put(filename, metadatum); 532 } 533 else { 534 put(DIRECTORY_FILENAME, metadatum); 535 } 536 } 537 return metadatum; 538 } 431 539 432 /** Retrieve any metadata associated with a certain file. If filename is null we are attempting to find directory level metadata. */ 433 public ArrayList getMetadata(String filename, ArrayList metadatum_so_far, boolean folder_level) { 434 ///ystem.err.println("Retrieving metadata for: " + filename); 435 ArrayList metadatum = null; 436 // We start by attempting to retrieve this metadata from the cache. 437 if(filename != null) { 438 metadatum = (ArrayList) get(filename); 439 } 440 else { 441 metadatum = (ArrayList) get(DIRECTORY_FILENAME); 442 } 443 // If that failed we consult the document for metadata. 444 if(metadatum == null) { 445 metadatum = new ArrayList(); 446 if(metadatum_so_far == null) { 447 metadatum = new ArrayList(); 448 } 449 else { 450 metadatum = metadatum_so_far; 451 } 452 try { 453 // Retrieve the document element. 454 Element directorymetadata_element = base_document.getDocumentElement(); 455 // Iterate through the filesets, checking the FileName child element against the target file's name using regular expression matching. 456 NodeList fileset_elements = directorymetadata_element.getElementsByTagName(FILESET_ELEMENT); 457 for(int i = 0; i < fileset_elements.getLength(); i++) { 458 Element fileset_element = (Element) fileset_elements.item(i); 459 NodeList filename_elements = fileset_element.getElementsByTagName(FILENAME_ELEMENT); 460 for(int j = 0; j < filename_elements.getLength(); j++) { 461 Element filename_element = (Element) filename_elements.item(j); 462 String filename_text = MSMUtils.getValue(filename_element); 463 // Crappy. There are apparently two ways of assigning, say, directory level metadata to anything in the ac01ne directory from a parent directories metadata.xml. 464 // The developers guide way: ac01ne/.* 465 // The dls way: ac01ne 466 // So the three tests are 467 //System.err.println("Check for: " + (filename != null ? filename : ".*")); 468 //System.err.println("Folder level = " + folder_level); 469 //System.err.println("filename != null && '" + filename + "'.matches('" + filename_text + "') = " + (filename != null ? filename.matches(filename_text) : false)); 470 //System.err.println("filename != null && '" + filename + "'.matches('" + filename_text + DIRECTORY_FILENAME_SUFFIX + "') = " + (filename != null ? filename.matches(filename_text + DIRECTORY_FILENAME_SUFFIX) : false)); 471 //System.err.println("filename == null && '" + filename_text + "'.equals('.*') = " + (filename == null ? filename_text.equals(DIRECTORY_FILENAME) : false)); 472 if((filename != null && (filename.matches(filename_text) || filename.matches(filename_text))) 473 || (filename == null && filename_text.equals(DIRECTORY_FILENAME))) { 474 ///ystem.err.println("Match: " + (filename != null ? filename : ".*") + " => " + filename_text); 475 // If they match add all of the metadata found in the Description child element, remembering to abide by desired mode (accumulate vs. overwrite). 476 NodeList description_elements = fileset_element.getElementsByTagName(DESCRIPTION_ELEMENT); 477 for(int k = 0; k < description_elements.getLength(); k++) { 478 Element description_element = (Element) description_elements.item(k); 479 NodeList metadata_elements = description_element.getElementsByTagName(METADATA_ELEMENT); 480 for(int l = 0; l < metadata_elements.getLength(); l++) { 481 Element metadata_element = (Element) metadata_elements.item(l); 482 String element = metadata_element.getAttribute(NAME_ATTRIBUTE); 483 ///ystem.err.println("Found element: " + element); 484 //String language = metadata_element.getAttribute("language"); 485 String mode = metadata_element.getAttribute(MODE_ATTRIBUTE); 486 // Add the new metadata to our list of metadata for this target file. 487 String value = Utility.stripNL(MSMUtils.getValue(metadata_element)); 488 ///ystem.err.println("Found value: " + element); 489 BasicMetadata metadata = new BasicMetadata(element, value, mode.equals("accumulate")); 490 // If mode is overwrite, then remove any previous values for this metadata element. 491 if(!metadata.accumulates) { 492 for(int m = metadatum.size() - 1; m >= 0; m--) { 493 BasicMetadata old_metadata = (BasicMetadata) metadatum.get(m); 494 if(old_metadata.element.equals(element)) { 495 metadatum.remove(m); 496 } 497 old_metadata = null; 498 } 499 } 500 mode = null; 501 502 // Add the completed metadata and clean up 503 metadatum.add(metadata); 504 metadata = null; 505 value = null; 506 element = null; 507 metadata_element = null; 508 } 509 metadata_elements = null; 510 description_element = null; 511 } 512 description_elements = null; 513 } 514 else { 515 ///ystem.err.println("No Match!"); 516 } 517 filename_text = null; 518 filename_element = null; 519 } 520 filename_elements = null; 521 fileset_element = null; 522 } 523 fileset_elements = null; 524 directorymetadata_element = null; 525 } 526 catch (Exception error) { 527 Gatherer.self.printStackTrace(error); 528 } 529 // Cache the result, given that these external metadata.xmls are taken to be static at the time of reading (if you happen to be sourcing information from a opened collection that someone is working on, too bad. 530 if(filename != null) { 531 put(filename, metadatum); 532 } 533 else { 534 put(DIRECTORY_FILENAME, metadatum); 535 } 536 } 537 return metadatum; 538 } 539 540 /** Determine is this is a valid Greenstone Directory Metadata file. It may of course just be some xml file with the name metadata.xml. */ 541 public boolean isValid() { 540 /** Determine is this is a valid Greenstone Directory Metadata file. It may of course just be some xml file with the name metadata.xml. */ 541 public boolean isValid() { 542 542 // Just determine if the doctype is GreenstoneDirectoryMetadata and root node is called DirectoryMetadata. 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 543 String doctype_name = base_document.getDoctype().getName(); 544 String root_name = base_document.getDocumentElement().getTagName(); 545 return ((doctype_name.equals("DirectoryMetadata") || doctype_name.equals("GreenstoneDirectoryMetadata")) && (root_name.equals("DirectoryMetadata") || root_name.equals("GreenstoneDirectoryMetadata"))); 546 } 547 548 /** Decode a string that was previously made Perl safe. 549 * @param safe The encoded <strong>String</strong> where dangerous characters have been escaped. 550 * @return A <strong>String</strong> with all the escaping removed. 551 */ 552 private String decode(String safe) { 553 String dangerous = safe.replaceAll("\\\\.","."); 554 return dangerous; 555 } 556 } 557 /** A simplistic version of metadata, with no live references. */ 558 private class BasicMetadata 559 implements Comparable { 560 public boolean accumulates; 561 /** The collection this metadata was extracted from. Important when attempting to map BasicMetadata to its Metadata incarnation. */ 562 public File collection; 563 /** The metadata element. */ 564 public String element = null; 565 /** The value. */ 566 public String value = null; 567 /** Constructor takes initial values for element and value. 568 * @param element The metadata element as a <strong>String</strong>. 569 * @param value The value as a <strong>String</strong>. 570 */ 571 public BasicMetadata(String element, String value, boolean accumulates) { 572 this.accumulates = accumulates; 573 this.element = element; 574 this.value = value; 575 } 576 577 public int compareTo(Object other) { 578 return toString().compareTo(other.toString()); 579 } 580 /** Compare two BasicMetadata objects for equality. 581 * @param object The other <strong>Object</strong>. 582 * @return <i>true</i> if this BasicMetadata matches the given object, <i>false</i> otherwise. 583 */ 584 public boolean equals(Object object) { 585 BasicMetadata other = (BasicMetadata) object; 586 if(collection != null) { 587 return (collection.equals(other.collection) && element.equals(other.element) && value.equals(other.value)); 588 } 589 return (element.equals(other.element) && value.equals(other.value)); 590 } 591 public String toString() { 592 return element + " = " + value; 593 } 594 } 595 596 /** This class provides a cache for the instances of parsed collect.cfg files and their associated data. Assures that the most recently cached CollectCFG will remain available. Older objects are maintained as soft references and are freed at the JVM implementations descretion, but are gareunteed to be garbage collected before an OutOfMemory exception is thrown. */ 597 private class CollectCFGCache 598 extends LinkedHashMap { 599 /** Retrieve the CollectCFG object that matches the given collection file path. 600 * @param collection_file The <strong>File</strong> that references the collection's directory. 601 * @return The <strong>CollectCFG</strong> that belongs to this collection, or <i>null</i> if no such file exists (so we probably aren't in a collection!). 602 */ 603 public CollectCFG get(File collect_cfg_file) { 604 604 ///ystem.err.println("Retrieve the collection configuration file at: " + collect_cfg_file); 605 605 CollectCFG collect_cfg = null; 606 606 // Attempt to load from cache. 607 607 SoftReference reference = (SoftReference) super.get(collect_cfg_file); 608 608 // If is doesn't exist, either because its never been loaded, or thats its cache reference has gone stale, attempt to load it again. 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 609 if(reference == null || (collect_cfg = (CollectCFG)reference.get()) == null) { 610 try { 611 collect_cfg = new CollectCFG(collect_cfg_file); 612 put(collect_cfg_file, new SoftReference(collect_cfg)); 613 } 614 catch(Exception error) { 615 Gatherer.printStackTrace(error); 616 collect_cfg = null; 617 } 618 } 619 return collect_cfg; 620 } 621 622 protected boolean removeEldestEntry(java.util.Map.Entry entry) { 623 return (size() > MAX_CFG_CACHE_SIZE); 624 } 625 } 626 627 /** The CollectCFG object encapsulates important metadata information extracted from a collect.cfg file, such as required metadata sets, and hfile associations. As the former are merged, their references are removed from this object, whereas the for the later references are replaced a representation of the hfile itself. */ 628 private class CollectCFG { 629 /** A list of the metadata sets associated with the collect.cfg file. */ 630 private ArrayList metadatasets = null; 631 /** A hash mapping from metadata element name to hierarchy file, or possibly hierarchy object. */ 632 private HashMap hfiles = null; 633 /** The token at the start of a classify command line within the collect.cfg. */ 634 static final private String CLASSIFY_COMMAND = "classify"; 635 /** The token at the start of a metadataset command line within the collect.cfg. */ 636 static final private String METADATASET_COMMAND = "metadataset"; 637 /** Constructor which takes a file assumed to be the location of a collect.cfg file belonging to a Greenstone Collection. 638 * @param file A <strong>File</strong> referencing a collect.cfg file. 639 */ 640 public CollectCFG(File file) 641 throws Exception { 642 642 ///atherer.println("Loading a new collection configuration file: " + file.getAbsolutePath()); 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 643 File etc_directory = file.getParentFile(); 644 hfiles = new HashMap(); 645 metadatasets = new ArrayList(); 646 FileReader reader = new FileReader(file); 647 BufferedReader in = new BufferedReader(reader); 648 String command = null; 649 while((command = in.readLine()) != null) { 650 CommandTokenizer tokenizer = new CommandTokenizer(command); 651 if(tokenizer.hasMoreTokens()) { 652 String token = tokenizer.nextToken().toLowerCase(); 653 if(token.equals(METADATASET_COMMAND)) { 654 String family_name = tokenizer.nextToken(); 655 String file_str = tokenizer.nextToken(); 656 if(file_str.startsWith("\"") && file_str.endsWith("\"") && !file_str.equals("\"\"")) { 657 file_str = file_str.substring(1, file_str.length() - 1); 658 } 659 // If the file str is -only- the filename then we add <col_dir>/metadata/ 660 File mds_file = null; 661 if(file_str.indexOf(File.separator) == -1) { 662 mds_file = new File(file.getParentFile().getParentFile(), File.separator + "metadata" + File.separator + file_str); 663 } 664 else { 665 mds_file = new File(file_str); 666 } 667 ///ystem.err.println("Attempting to file mds file at " + file.getAbsolutePath()); 668 if(mds_file.exists()) { 669 metadatasets.add(mds_file); 670 } 671 mds_file = null; 672 file_str = null; 673 family_name = null; 674 } 675 // Also look for any classify commands that include an hfile and element 676 else if(token.equals(CLASSIFY_COMMAND)) { 677 String hfile_name = null; 678 String element_name = null; 679 // Drop the classifier name 680 tokenizer.nextToken(); 681 while(tokenizer.hasMoreTokens()) { 682 token = tokenizer.nextToken().toLowerCase(); 683 if(token.equals("-hfile")) { 684 hfile_name = tokenizer.nextToken(); 685 } 686 else if(token.equals("-metadata")) { 687 element_name = tokenizer.nextToken(); 688 } 689 } 690 if(hfile_name != null && element_name != null) { 691 // If hfile_name has no path, append the etc directories one. Either way create a file reference 692 File hfile = null; 693 hfile_name = hfile_name.replace('\\', File.separatorChar); 694 hfile_name = hfile_name.replace('/', File.separatorChar); 695 if(hfile_name.indexOf(File.separator) == -1) { 696 hfile = new File(etc_directory, hfile_name); 697 } 698 else { 699 hfile = new File(hfile_name); 700 } 701 // Add to hfiles 702 ///atherer.println("Adding hfile reference: " + element_name + " -> " + hfile); 703 hfiles.put(element_name, hfile); 704 hfile = null; 705 } 706 element_name = null; 707 hfile_name = null; 708 } 709 tokenizer = null; 710 } 711 } 712 command = null; 713 in.close(); 714 reader.close(); 715 in = null; 716 reader = null; 717 717 // Now we search the etc directory for *.txt files which we attempt to parse as hfiles 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 718 File children[] = etc_directory.listFiles(); // We are sure there is at least one, collect.cfg 719 for(int i = 0; i < children.length; i++) { 720 // If this is a text file, extract the element name and process 721 String name = children[i].getName(); 722 if(children[i].isFile() && name.endsWith(".txt")) { 723 String element_name = name.substring(0, name.lastIndexOf(".")); 724 if(!hfiles.containsKey(element_name)) { 725 ///atherer.println("Adding hfile reference: " + element_name + " -> " + children[i]); 726 hfiles.put(element_name, children[i]); 727 } 728 element_name = null; 729 } 730 name = null; 731 } 732 children = null; 733 etc_directory = null; 734 file = null; 735 } 736 /** Attempts to retrieve the HFile object associated with a certain metadata element. This may have already been cached, or may need to be loaded. Then again it may not even be necessary. 737 * @param element The fully qualified name of a metadata element, as a <strong>String</strong>. 738 * @return The <strong>HFile</strong> associated with the given element, or <i>null</i> if its unnecessary. 739 * @see org.greenstone.gatherer.cdm.CommandTokenizer 740 */ 741 public HFile getHFile(String element) { 742 HFile result = null; 743 Object target = hfiles.get(element); 744 744 // If target is non-null 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 745 if(target != null) { 746 // If we haven't already load and parse the file. 747 if(target instanceof File) { 748 ///ystem.err.println("\nHFILE-MISS!! Loading " + target.toString()); 749 result = new HFile(); 750 try { 751 FileReader in_filereader = new FileReader((File)target); 752 //DecodeHTMLReader in_decodehtmlreader = new DecodeHTMLReader(in_filereader); 753 BufferedReader in = new BufferedReader(in_filereader); 754 String line = null; 755 while((line = in.readLine()) != null) { 756 CommandTokenizer tokenizer = new CommandTokenizer(line); 757 String alias = Utility.decodeGreenstone(tokenizer.nextToken()); 758 String index = tokenizer.nextToken(); 759 String value = Utility.decodeGreenstone(tokenizer.nextToken()); 760 ///ystem.err.println("Read " + index + ", " + alias + ", " + value); 761 if(alias.startsWith("\"") && alias.endsWith("\"") && !alias.equals("\"\"")) { 762 alias = alias.substring(1, alias.length() - 1); 763 } 764 if(value.startsWith("\"") && value.endsWith("\"") && !value.equals("\"\"")) { 765 value = value.substring(1, value.length() - 1); 766 } 767 result.add(index, alias, value); 768 value = null; 769 alias = null; 770 index = null; 771 tokenizer = null; 772 } 773 line = null; 774 in.close(); 775 in = null; 776 //in_decodehtmlreader = null; 777 in_filereader = null; 778 hfiles.put(element, result); 779 } 780 catch (Exception error) { 781 error.printStackTrace(); 782 hfiles.remove(element); 783 } 784 } 785 else { 786 ///ystem.err.print("HFILE-HIT!!! "); 787 result = (HFile) target; 788 } 789 } 790 790 // Else no hfile is needed for this element 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 791 target = null; 792 return result; 793 } 794 /** Retrieve the list of metadata sets associated with this collection. 795 * @return An <strong>ArrayList</strong> of metadata set Files. 796 */ 797 public ArrayList getMetadataSets() { 798 return metadatasets; 799 } 800 } 801 802 /** The HFile object provides a container for the mappings from indexes, of the form 1.1.1, to alias-value pairs. It also provides method to retrieving the alias and value for a certain element, remembering that values must be expressed in terms of their absolute subject heirarchy path. */ 803 private class HFile 804 extends HashMap { 805 /** Construct a new HFile object with no initial values. */ 806 public HFile() { 807 super(); 808 } 809 /** Add a new (index,(alias, value)) mapping. 810 * @param index The index of this mapping as a <strong>String</strong>. 811 * @param alias The alias of this mapping as a <strong>String</strong>. 812 * @param value And finally the value of this mapping as a, you guessed it, <strong>String</strong>. 813 */ 814 public void add(String index, String alias, String value) { 815 Entry entry = new Entry(index, alias, value); 816 816 ///ystem.err.println("Adding entry: " + index + " \"" + alias + "\" \"" + value + "\""); 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 817 put(index, entry); 818 put(alias, entry); 819 } 820 public String getAlias(String index) { 821 String alias = ""; 822 Entry entry = (Entry) get(index); 823 if(entry != null) { 824 alias = entry.alias; 825 } 826 entry = null; 827 return alias; 828 } 829 /** Retrieve the value associated with a certain index. This is harder than it first sounds as you must take into account the parent indexes of this one. 830 * @param index The index whose value you wish to calculate, as a <strong>String</strong>. 831 * @return The fully quantified path to the value that matches index, also as a <strong>String</strong>. Delimitiation between subject layers is denoted by the character '/' 832 */ 833 public String getValue(String index) { 834 834 ///ystem.err.println("Retrieve value for the alias/index: '" + index + "'"); 835 835 StringBuffer value = new StringBuffer(""); 836 836 // If index isn't the index, it must be the alias. Replace it with the index dammit. 837 838 839 840 841 842 843 844 837 Entry entry = null; 838 if(!Utility.isIndex(index)) { 839 ///ystem.err.println("\tThis is an alias."); 840 // Store this for later, as its exactly the same entry we'd get had we found the last component of a proper index. 841 entry = (Entry) get(index); 842 index = entry.index; 843 ///ystem.err.println("\tIndex is actually: " + index); 844 } 845 845 // Now build the hierarchy if necessary. 846 847 848 849 850 851 852 853 854 855 856 857 858 859 846 int dot_index = -1; 847 if((dot_index = index.indexOf(".")) != -1) { 848 ///ystem.err.println("\tHierarchy information required -->"); 849 value.append(getValue(index.substring(0, dot_index))); 850 value.append("\\"); 851 ///ystem.err.println("\t<-- Hierarchy information complete"); 852 } 853 if(entry == null) { 854 entry = (Entry) get(index); 855 } 856 if(entry != null) { 857 value.append(entry.value); 858 } 859 entry = null; 860 860 ///ystem.err.println("\tFinal value is: '" + value.toString() + "'\n"); 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 861 return value.toString(); 862 } 863 864 private class Entry { 865 public String alias = null; 866 public String index = null; 867 public String value = null; 868 public Entry(String index, String alias, String value) { 869 this.alias = alias; 870 this.index = index; 871 this.value = value; 872 } 873 } 874 } 875 876 private class MetadataXMLFileSearch { 877 public File file; 878 public String filename; 879 public MetadataXMLFileSearch(File file, String filename) { 880 this.file = file; 881 this.filename = filename; 882 } 883 } 884 884 }
Note:
See TracChangeset
for help on using the changeset viewer.