Changeset 24824 for main/trunk/gli

Show
Ignore:
Timestamp:
30.11.2011 15:36:35 (8 years ago)
Author:
sjm84
Message:

Reformatting this file ahead of some changes

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • main/trunk/gli/src/org/greenstone/gatherer/cdm/CollectionConfiguration.java

    r22970 r24824  
    4444import org.w3c.dom.*; 
    4545 
    46 /** This class provides access to an xml-type view of the collect.cfg file. This is useful as it allows the manipulation and free form editing of a collect.cfg file while still allowing the various CDM data managers to base themselves directly on this model (whereas they used to be independant ListModels which clobbered the ordering of unparsed commands). 
     46/** 
     47 * This class provides access to an xml-type view of the collect.cfg file. This 
     48 * is useful as it allows the manipulation and free form editing of a 
     49 * collect.cfg file while still allowing the various CDM data managers to base 
     50 * themselves directly on this model (whereas they used to be independant 
     51 * ListModels which clobbered the ordering of unparsed commands). 
     52 *  
    4753 * @author John Thompson, Greenstone Digital Library, University of Waikato 
    4854 */ 
    49 public class CollectionConfiguration { 
    50   static final public String ENCODING = "UTF-8"; 
    51   static final public String NEWLINE_ELEMENT = "NewLine"; 
    52      
    53   static private Document document; 
    54   static private String saved_config_file_string = null; 
    55      
    56   // may be collec.cfg (GS2) or collectionConfig.xml (GS3) 
    57   private File collect_config_file; 
    58   private String collect_config_filename; 
    59    
    60   // This method is initilised in CollectionDesignManager.java constructor 
    61   public CollectionConfiguration (File collect_config_file) { 
    62     this.collect_config_file = collect_config_file; 
    63     this.collect_config_filename = collect_config_file.getName(); 
    64     // parse the XML template 
    65     document = XMLTools.parseXMLFile ("xml/CollectionConfig.xml", true); 
    66     String filename = collect_config_filename.toLowerCase (); 
    67          
    68     if (filename.endsWith (".cfg")) { 
    69       saved_config_file_string = CollectCfgReadWrite.parse (collect_config_file, document); 
    70     } 
    71     else if (filename.endsWith (".xml")) { 
    72       CollectionConfigXMLReadWrite.parse (collect_config_file, document); 
    73     } 
    74          
    75     //XMLTools.printXMLNode(document.getDocumentElement()); 
    76   } 
    77  
    78   static public Element createElement (String element_name) { 
    79     return document.createElement (element_name); 
    80   } 
    81      
    82   /** Gives the preferred ordering of commands */ 
    83   static final public String[] COMMAND_ORDER = 
    84   {StaticStrings.COLLECTIONMETADATA_CREATOR_ELEMENT, StaticStrings.COLLECTIONMETADATA_MAINTAINER_ELEMENT, StaticStrings.COLLECTIONMETADATA_PUBLIC_ELEMENT, StaticStrings.BUILDTYPE_ELEMENT, StaticStrings.PLUGIN_ELEMENT, StaticStrings.INDEXES_ELEMENT, StaticStrings.INDEX_DEFAULT_ELEMENT, StaticStrings.INDEXOPTIONS_ELEMENT, StaticStrings.INDEXOPTION_DEFAULT_ELEMENT, StaticStrings.LANGUAGES_ELEMENT, StaticStrings.LANGUAGE_DEFAULT_ELEMENT, StaticStrings.LANGUAGE_METADATA_ELEMENT, StaticStrings.SUBCOLLECTION_ELEMENT, StaticStrings.SUBCOLLECTION_INDEXES_ELEMENT, StaticStrings.SUBCOLLECTION_DEFAULT_INDEX_ELEMENT, StaticStrings.SUPERCOLLECTION_ELEMENT, StaticStrings.CLASSIFY_ELEMENT, StaticStrings.FORMAT_ELEMENT, StaticStrings.COLLECTIONMETADATA_ELEMENT}; 
    85       
    86   /** Find the best insertion position for the given DOM Element. This should try to match command tag, and if found should then try to group by name or type (eg CollectionMeta), or append to end is no such grouping exists (eg Plugins). Failing a command match it will check against the command order for the best insertion location. 
    87    * @param target_element the command Element to be inserted 
    88    * @return the Element which the given command should be inserted before, or null to append to end of list 
    89    */ 
    90   static public Node findInsertionPoint (Element target_element) { 
    91     ///ystem.err.println("Find insertion point: " + target_element.getNodeName()); 
    92     String target_element_name = target_element.getNodeName (); 
    93     Element document_element = document.getDocumentElement (); 
    94     // Try to find commands with the same tag. 
    95     NodeList matching_elements = document_element.getElementsByTagName (target_element_name); 
    96     // If we found matching elements, then we have our most likely insertion location, so check within for groupings 
    97     if(matching_elements.getLength () != 0) { 
    98       ///ystem.err.println("Found matching elements."); 
    99       // Only CollectionMeta are grouped. 
    100       if(target_element_name.equals (StaticStrings.COLLECTIONMETADATA_ELEMENT)) { 
    101     ///ystem.err.println("Dealing with collection metadata"); 
    102     // Special case: CollectionMeta can be added at either the start or end of a collection configuration file. However the start position is reserved for special metadata, so if no non-special metadata can be found we must append to the end. 
    103     // So if the command to be added is special add it immediately after any other special command 
    104     if(target_element.getAttribute (StaticStrings.SPECIAL_ATTRIBUTE).equals (StaticStrings.TRUE_STR)) { 
    105       int index = 0; 
    106       Element matched_element = (Element) matching_elements.item (index); 
    107       Element sibling_element = (Element) matched_element.getNextSibling (); 
    108       while(sibling_element.getAttribute (StaticStrings.SPECIAL_ATTRIBUTE).equals (StaticStrings.TRUE_STR)) { 
    109         index++; 
    110         matched_element = (Element) matching_elements.item (index); 
    111         sibling_element = (Element) matched_element.getNextSibling (); 
    112       } 
    113       if(sibling_element.getNodeName ().equals (NEWLINE_ELEMENT)) { 
    114         Element newline_element = document.createElement (NEWLINE_ELEMENT); 
    115         document_element.insertBefore (newline_element, sibling_element); 
    116       } 
    117       return sibling_element; 
    118     } 
    119     // Otherwise try to find a matching 'name' and add after the last one in that group. 
    120     else { 
    121       int index = 0; 
    122       target_element_name = target_element.getAttribute (StaticStrings.NAME_ATTRIBUTE); 
    123       boolean found = false; 
    124       // Skip all of the special metadata 
    125       Element matched_element = (Element) matching_elements.item (index); 
    126       while(matched_element.getAttribute (StaticStrings.SPECIAL_ATTRIBUTE).equals (StaticStrings.TRUE_STR)) { 
    127         index++; 
    128         matched_element = (Element) matching_elements.item (index); 
    129       } 
    130       // Begin search 
    131       while(!found && matched_element != null) { 
    132         if(matched_element.getAttribute (StaticStrings.NAME_ATTRIBUTE).equals (target_element_name)) { 
    133           found = true; 
    134         } 
    135         else { 
    136           index++; 
    137           matched_element = (Element) matching_elements.item (index); 
    138         } 
    139       } 
    140       // If we found a match, we need to continue checking until we find the last name match. 
    141       if(found) { 
    142         index++; 
    143         Element previous_sibling = matched_element; 
    144         Element sibling_element = (Element) matching_elements.item (index); 
    145         while(sibling_element != null && sibling_element.getAttribute (StaticStrings.NAME_ATTRIBUTE).equals (target_element_name)) { 
    146           previous_sibling = sibling_element; 
    147           index++; 
    148           sibling_element = (Element) matching_elements.item (index); 
    149         } 
    150         // Previous sibling now holds the command immediately before where we want to add, so find its next sibling and add to that. In this one case we can ignore new lines! 
    151         return previous_sibling.getNextSibling (); 
    152       } 
    153       // If not found we just add after last metadata element 
    154       else { 
    155         Element last_element = (Element) matching_elements.item (matching_elements.getLength () - 1); 
    156         return last_element.getNextSibling (); 
    157       } 
    158     } 
    159                  
    160       } 
    161       else { 
    162     ///ystem.err.println("Not dealing with collection meta."); 
    163     Element matched_element = (Element) matching_elements.item (matching_elements.getLength () - 1); 
    164     // One final quick test. If the matched element is immediately followed by a NewLine command, then we insert another NewLine after the matched command, then return the NewLine instead (thus the about to be inserted command will be placed between the two NewLines) 
    165     Node sibling_element = matched_element.getNextSibling (); 
    166     if(sibling_element != null && sibling_element.getNodeName ().equals (NEWLINE_ELEMENT)) { 
    167       Element newline_element = document.createElement (NEWLINE_ELEMENT); 
    168       document_element.insertBefore (newline_element, sibling_element); 
    169     } 
    170     return sibling_element; // Note that this may be null 
    171       } 
    172     } 
    173     ///ystem.err.println("No matching elements found."); 
    174     // Locate where this command is in the ordering 
    175     int command_index = -1; 
    176     for(int i = 0; command_index == -1 && i < COMMAND_ORDER.length; i++) { 
    177       if(COMMAND_ORDER[i].equals (target_element_name)) { 
    178     command_index = i; 
    179       } 
    180     } 
    181     ///ystem.err.println("Command index is: " + command_index); 
    182     // Now move forward, checking for existing elements in each of the preceeding command orders. 
    183     int preceeding_index = command_index - 1; 
    184     ///ystem.err.println("Searching before the target command."); 
    185     while(preceeding_index >= 0) { 
    186       matching_elements = document_element.getElementsByTagName (COMMAND_ORDER[preceeding_index]); 
    187       // If we've found a match 
    188       if(matching_elements.getLength () > 0) { 
    189     // We add after the last element 
    190     Element matched_element = (Element) matching_elements.item (matching_elements.getLength () - 1); 
    191     // One final quick test. If the matched element is immediately followed by a NewLine command, then we insert another NewLine after the matched command, then return the NewLine instead (thus the about to be inserted command will be placed between the two NewLines) 
    192     Node sibling_element = matched_element.getNextSibling (); 
    193     if(sibling_element != null && sibling_element.getNodeName ().equals (NEWLINE_ELEMENT)) { 
    194       Element newline_element = document.createElement (NEWLINE_ELEMENT); 
    195       document_element.insertBefore (newline_element, sibling_element); 
    196     } 
    197     return sibling_element; // Note that this may be null 
    198       } 
    199       preceeding_index--; 
    200     } 
    201     // If all that fails, we now move backwards through the commands 
    202     int susceeding_index = command_index + 1; 
    203     ///ystem.err.println("Searching after the target command."); 
    204     while(susceeding_index < COMMAND_ORDER.length) { 
    205       matching_elements = document_element.getElementsByTagName (COMMAND_ORDER[susceeding_index]); 
    206       // If we've found a match 
    207       if(matching_elements.getLength () > 0) { 
    208     // We add before the first element 
    209     Element matched_element = (Element) matching_elements.item (0); 
    210     // One final quick test. If the matched element is immediately preceeded by a NewLine command, then we insert another NewLine before the matched command, then return this new NewLine instead (thus the about to be inserted command will be placed between the two NewLines) 
    211     Node sibling_element = matched_element.getPreviousSibling (); 
    212     if(sibling_element != null && sibling_element.getNodeName ().equals (NEWLINE_ELEMENT)) { 
    213       Element newline_element = document.createElement (NEWLINE_ELEMENT); 
    214       document_element.insertBefore (newline_element, sibling_element); 
    215     } 
    216     return sibling_element; // Note that this may be null 
    217       } 
    218       susceeding_index++; 
    219     } 
    220     // Well. Apparently there are no other commands in this collection configuration. So append away... 
    221     return null; 
    222   } 
    223      
    224      
    225   static public NodeList getElementsByTagName (String element_name) { 
    226     return document.getDocumentElement ().getElementsByTagName (element_name); 
    227   } 
    228         
    229   public Element getDocumentElement () { 
    230     return document.getDocumentElement (); 
    231   } 
    232      
    233   /** This debug facility shows the currently loaded collect.cfg or CollectConfig.xml file as a DOM tree. */ 
    234   public void display () { 
    235     JDialog dialog = new JDialog (Gatherer.g_man, "Collection Configuration", false); 
    236     dialog.setSize (400,400); 
    237     JPanel content_pane = (JPanel) dialog.getContentPane (); 
    238     final DOMTree tree = new DOMTree (document); 
    239     JButton refresh_button = new GLIButton ("Refresh Tree"); 
    240     refresh_button.addActionListener (new ActionListener () { 
    241     public void actionPerformed (ActionEvent event) { 
    242       tree.setDocument (document); 
    243     } 
    244       }); 
    245     content_pane.setBorder (BorderFactory.createEmptyBorder (5,5,5,5)); 
    246     content_pane.setLayout (new BorderLayout ()); 
    247     content_pane.add (new JScrollPane (tree), BorderLayout.CENTER); 
    248     content_pane.add (refresh_button, BorderLayout.SOUTH); 
    249     dialog.setVisible (true); 
    250   } 
    251      
    252          
    253      
    254   public File getFile () { 
    255     return collect_config_file; 
    256   } 
    257      
    258   public Element getCreator () { 
    259     Element element = getOrCreateElementByTagName (StaticStrings.COLLECTIONMETADATA_CREATOR_ELEMENT, null, null); 
    260     element.setAttribute (StaticStrings.NAME_ATTRIBUTE, StaticStrings.COLLECTIONMETADATA_CREATOR_STR); 
    261     element.setAttribute (StaticStrings.SPECIAL_ATTRIBUTE, StaticStrings.TRUE_STR); 
    262     return element; 
    263   } 
    264  
    265   public Element getMaintainer () { 
    266     Element element = getOrCreateElementByTagName (StaticStrings.COLLECTIONMETADATA_MAINTAINER_ELEMENT, null, null); 
    267     element.setAttribute (StaticStrings.NAME_ATTRIBUTE, StaticStrings.COLLECTIONMETADATA_MAINTAINER_STR); 
    268     element.setAttribute (StaticStrings.SPECIAL_ATTRIBUTE, StaticStrings.TRUE_STR); 
    269     return element; 
    270   } 
    271  
    272   /** Retrieve or create the languages Element. */ 
    273   public Element getLanguages () { 
    274     return getOrCreateElementByTagName (StaticStrings.LANGUAGES_ELEMENT, null, null); 
    275   } 
    276      
    277   public Element getLanguageMetadata () { 
    278     return getOrCreateElementByTagName (StaticStrings.LANGUAGE_METADATA_ELEMENT, null, null); 
    279   } 
    280      
    281   public Element getLevels () { 
    282     return getOrCreateElementByTagName (StaticStrings.INDEXOPTIONS_ELEMENT, StaticStrings.NAME_ATTRIBUTE, StaticStrings.LEVELS_STR); 
    283   } 
    284      
    285   public Element getLevelDefault () { 
    286     return getOrCreateElementByTagName (StaticStrings.INDEXOPTION_DEFAULT_ELEMENT, StaticStrings.NAME_ATTRIBUTE, StaticStrings.LEVEL_DEFAULT_STR); 
    287   } 
    288      
    289   public Element getIndexOptions () { 
    290     return getOrCreateElementByTagName (StaticStrings.INDEXOPTIONS_ELEMENT, StaticStrings.NAME_ATTRIBUTE, StaticStrings.INDEXOPTIONS_STR); 
    291   } 
    292      
    293         
    294   /** Retrieve or create the indexes Element. Note that this method behaves differently from the other getBlah methods, in that it also has to keep in mind that indexes come in two flavours, MG and MGPP. */ 
    295   public Element getMGIndexes () { 
    296     return getOrCreateElementByTagName (StaticStrings.INDEXES_ELEMENT, StaticStrings.MGPP_ATTRIBUTE, StaticStrings.FALSE_STR); 
    297   } 
    298      
    299   public Element getMGPPIndexes () { 
    300     return getOrCreateElementByTagName (StaticStrings.INDEXES_ELEMENT, StaticStrings.MGPP_ATTRIBUTE, StaticStrings.TRUE_STR); 
    301   } 
    302      
    303   public Element getPublic () { 
    304     Element element = getOrCreateElementByTagName (StaticStrings.COLLECTIONMETADATA_PUBLIC_ELEMENT, null, null); 
    305     element.setAttribute (StaticStrings.NAME_ATTRIBUTE, StaticStrings.COLLECTIONMETADATA_PUBLIC_STR); 
    306     element.setAttribute (StaticStrings.SPECIAL_ATTRIBUTE, StaticStrings.TRUE_STR); 
    307     return element; 
    308   } 
    309      
    310   public Element getBuildType () { 
    311     Element element = getOrCreateElementByTagName (StaticStrings.BUILDTYPE_ELEMENT, null, null); 
    312     element.setAttribute (StaticStrings.NAME_ATTRIBUTE, StaticStrings.BUILDTYPE_STR); 
    313     element.setAttribute (StaticStrings.SPECIAL_ATTRIBUTE, StaticStrings.TRUE_STR); 
    314     return element; 
    315          
    316   }   
    317  
    318 public Element getDatabaseType () { 
    319     Element element = getOrCreateElementByTagName (StaticStrings.DATABASETYPE_ELEMENT, null, null); 
    320     element.setAttribute (StaticStrings.NAME_ATTRIBUTE, StaticStrings.DATABASETYPE_STR); 
    321     element.setAttribute (StaticStrings.SPECIAL_ATTRIBUTE, StaticStrings.TRUE_STR); 
    322     return element; 
    323          
    324   } 
    325      
    326   /** Retrieve or create the subindexes Element. */ 
    327   public Element getSubIndexes () { 
    328     return getOrCreateElementByTagName (StaticStrings.SUBCOLLECTION_INDEXES_ELEMENT, null, null); 
    329   } 
    330      
    331   /** Retrieve or create the supercollections Element. */ 
    332   public Element getSuperCollection () { 
    333     return getOrCreateElementByTagName (StaticStrings.SUPERCOLLECTION_ELEMENT, null, null); 
    334   } 
    335      
    336   public boolean ready () { 
    337     return document != null; 
    338   } 
    339      
    340      
    341      
    342      
    343   /** ************************** Private Methods ***************************/ 
    344      
    345    
    346   /** Retrieve or create the indexes Element. */ 
    347   static private Element getOrCreateElementByTagName (String name, String conditional_attribute, String required_value) { 
    348     Element document_element = document.getDocumentElement (); 
    349     NodeList elements = document_element.getElementsByTagName (name); 
    350     int elements_length = elements.getLength (); 
    351     if(elements_length > 0) { 
    352       if(conditional_attribute == null) { 
    353     document_element = null; 
    354     return (Element) elements.item (0); 
    355       } 
    356       else { 
    357     for(int i = 0; i < elements_length; i++) { 
    358       Element element = (Element) elements.item (i); 
    359       if(element.getAttribute (conditional_attribute).equals (required_value)) { 
    360         document_element = null; 
    361         return element; 
    362       } 
    363       element = null; 
    364     } 
    365       } 
    366     } 
    367     // Create the element 
    368     Element element = document.createElement (name); 
    369     // If there was a property set it 
    370     if(conditional_attribute != null) { 
    371       element.setAttribute (conditional_attribute, required_value); 
    372     } 
    373     Node target_node = findInsertionPoint (element); 
    374     if(target_node != null) { 
    375       document_element.insertBefore (element, target_node); 
    376     } 
    377     else { 
    378       document_element.appendChild (element); 
    379     } 
    380     document_element = null; 
    381     return element; 
    382   } 
    383      
    384  
    385  
    386   
    387  
    388   /** Write the text to the buffer. This is used so we don't have to worry about storing intermediate String values just so we can calaulate length and offset. 
    389    * @param writer the BufferedWriter to which the str will be written 
    390    * @param str the String to be written 
    391    */ 
    392   private void write (BufferedWriter writer, String str) 
    393     throws IOException { 
    394     writer.write (str, 0, str.length ()); 
    395   } 
    396      
    397  
    398   public void saveIfNecessary () { 
    399      
    400     // Generate a string version of internal document 
    401     String config_file_string = null; 
    402     if (Gatherer.GS3) { 
    403       config_file_string = CollectionConfigXMLReadWrite.generateStringVersion(document); 
    404     } else { 
    405       config_file_string = CollectCfgReadWrite.generateStringVersion(document); 
    406     } 
    407     // compare to saved version 
    408     if (saved_config_file_string != null) { 
    409       if (saved_config_file_string.equals(config_file_string)) { 
    410     DebugStream.println (collect_config_filename +" file hasn't changed so no save necessary..."); 
    411     return; 
    412       } 
    413     } 
    414      
    415     // We need to save... 
    416     DebugStream.println (collect_config_filename +" file has changed, saving now..."); 
    417  
    418         
    419     // If we're using the Local Library we must release the collection before writing to the collect.cfg file 
    420     String collection_name = CollectionManager.getLoadedCollectionName (true); // url style slash 
    421     boolean collection_released = false; 
    422     if (Gatherer.c_man.built () && LocalLibraryServer.isRunning () == true) { 
    423       // Release the collection 
    424       LocalLibraryServer.releaseCollection (collection_name); 
    425       collection_released = true; 
    426     } 
    427          
    428     // Make a backup of the existing config file 
    429     if (collect_config_file.exists ()) { 
    430       String config_filename; 
    431       String backup_filename; 
    432       if (Gatherer.GS3) { 
    433     config_filename = Utility.COLLECTION_CONFIG_XML; 
    434     backup_filename = Utility.COLLECTION_CONFIG_BAK; 
    435       } else { 
    436     config_filename = StaticStrings.COLLECT_CFG; 
    437     backup_filename = Utility.COLLECT_BAK; 
    438       }  
    439       File original_file = new File (collect_config_file.getParentFile (), config_filename); 
    440       File backup_file = new File (collect_config_file.getParentFile (), backup_filename); 
    441       if (backup_file.exists ()) { 
    442     backup_file.delete (); 
    443       } 
    444       if (!original_file.renameTo (backup_file)) { 
    445     System.err.println ("Warning: can't rename "+config_filename + " to "+ backup_filename); 
    446       } 
    447     } 
    448  
    449     // now save the file 
    450     if (Gatherer.GS3) { 
    451       CollectionConfigXMLReadWrite.save(collect_config_file, document); 
    452     } else { 
    453       // we have already converted to string, so save here 
    454       try { 
    455     OutputStream ostream = new FileOutputStream (collect_config_file); 
    456     Writer file_writer = new OutputStreamWriter (ostream, ENCODING); 
    457     BufferedWriter buffered_writer = new BufferedWriter (file_writer); 
    458     buffered_writer.write (config_file_string); 
    459     buffered_writer.close (); 
    460       } 
    461       catch (Exception exception) { 
    462     DebugStream.println ("Error in CollectionConfiguration.save(): " + exception); 
    463     DebugStream.printStackTrace (exception); 
    464       } 
    465        
    466     } 
    467  
    468     // save the string version 
    469     saved_config_file_string = config_file_string; 
    470      
    471     // If we're using a remote Greenstone server, upload the new collect.cfg file 
    472     if (Gatherer.isGsdlRemote) { 
    473       Gatherer.remoteGreenstoneServer.uploadCollectionFile (collection_name, collect_config_file); 
    474     } 
    475    
    476          
    477     // Now re-add the collection to the Local Library server 
    478     if (collection_released) { 
    479       LocalLibraryServer.addCollection (collection_name); 
    480     } 
    481   } 
    482  
    483   
    484      
     55public class CollectionConfiguration 
     56{ 
     57    static final public String ENCODING = "UTF-8"; 
     58    static final public String NEWLINE_ELEMENT = "NewLine"; 
     59 
     60    static private Document document; 
     61    static private String saved_config_file_string = null; 
     62 
     63    // may be collec.cfg (GS2) or collectionConfig.xml (GS3) 
     64    private File collect_config_file; 
     65    private String collect_config_filename; 
     66 
     67    // This method is initilised in CollectionDesignManager.java constructor 
     68    public CollectionConfiguration(File collect_config_file) 
     69    { 
     70        this.collect_config_file = collect_config_file; 
     71        this.collect_config_filename = collect_config_file.getName(); 
     72        // parse the XML template 
     73        document = XMLTools.parseXMLFile("xml/CollectionConfig.xml", true); 
     74        String filename = collect_config_filename.toLowerCase(); 
     75 
     76        if (filename.endsWith(".cfg")) 
     77        { 
     78            saved_config_file_string = CollectCfgReadWrite.parse(collect_config_file, document); 
     79        } 
     80        else if (filename.endsWith(".xml")) 
     81        { 
     82            CollectionConfigXMLReadWrite.parse(collect_config_file, document); 
     83        } 
     84 
     85        //XMLTools.printXMLNode(document.getDocumentElement()); 
     86    } 
     87 
     88    static public Element createElement(String element_name) 
     89    { 
     90        return document.createElement(element_name); 
     91    } 
     92 
     93    /** Gives the preferred ordering of commands */ 
     94    static final public String[] COMMAND_ORDER = { StaticStrings.COLLECTIONMETADATA_CREATOR_ELEMENT, StaticStrings.COLLECTIONMETADATA_MAINTAINER_ELEMENT, StaticStrings.COLLECTIONMETADATA_PUBLIC_ELEMENT, StaticStrings.BUILDTYPE_ELEMENT, StaticStrings.PLUGIN_ELEMENT, StaticStrings.INDEXES_ELEMENT, StaticStrings.INDEX_DEFAULT_ELEMENT, StaticStrings.INDEXOPTIONS_ELEMENT, StaticStrings.INDEXOPTION_DEFAULT_ELEMENT, StaticStrings.LANGUAGES_ELEMENT, StaticStrings.LANGUAGE_DEFAULT_ELEMENT, StaticStrings.LANGUAGE_METADATA_ELEMENT, StaticStrings.SUBCOLLECTION_ELEMENT, StaticStrings.SUBCOLLECTION_INDEXES_ELEMENT, StaticStrings.SUBCOLLECTION_DEFAULT_INDEX_ELEMENT, StaticStrings.SUPERCOLLECTION_ELEMENT, StaticStrings.CLASSIFY_ELEMENT, StaticStrings.FORMAT_ELEMENT, StaticStrings.COLLECTIONMETADATA_ELEMENT }; 
     95 
     96    /** 
     97     * Find the best insertion position for the given DOM Element. This should 
     98     * try to match command tag, and if found should then try to group by name 
     99     * or type (eg CollectionMeta), or append to end is no such grouping exists 
     100     * (eg Plugins). Failing a command match it will check against the command 
     101     * order for the best insertion location. 
     102     *  
     103     * @param target_element 
     104     *            the command Element to be inserted 
     105     * @return the Element which the given command should be inserted before, or 
     106     *         null to append to end of list 
     107     */ 
     108    static public Node findInsertionPoint(Element target_element) 
     109    { 
     110        ///ystem.err.println("Find insertion point: " + target_element.getNodeName()); 
     111        String target_element_name = target_element.getNodeName(); 
     112        Element document_element = document.getDocumentElement(); 
     113        // Try to find commands with the same tag. 
     114        NodeList matching_elements = document_element.getElementsByTagName(target_element_name); 
     115        // If we found matching elements, then we have our most likely insertion location, so check within for groupings 
     116        if (matching_elements.getLength() != 0) 
     117        { 
     118            ///ystem.err.println("Found matching elements."); 
     119            // Only CollectionMeta are grouped. 
     120            if (target_element_name.equals(StaticStrings.COLLECTIONMETADATA_ELEMENT)) 
     121            { 
     122                ///ystem.err.println("Dealing with collection metadata"); 
     123                // Special case: CollectionMeta can be added at either the start or end of a collection configuration file. However the start position is reserved for special metadata, so if no non-special metadata can be found we must append to the end. 
     124                // So if the command to be added is special add it immediately after any other special command 
     125                if (target_element.getAttribute(StaticStrings.SPECIAL_ATTRIBUTE).equals(StaticStrings.TRUE_STR)) 
     126                { 
     127                    int index = 0; 
     128                    Element matched_element = (Element) matching_elements.item(index); 
     129                    Element sibling_element = (Element) matched_element.getNextSibling(); 
     130                    while (sibling_element.getAttribute(StaticStrings.SPECIAL_ATTRIBUTE).equals(StaticStrings.TRUE_STR)) 
     131                    { 
     132                        index++; 
     133                        matched_element = (Element) matching_elements.item(index); 
     134                        sibling_element = (Element) matched_element.getNextSibling(); 
     135                    } 
     136                    if (sibling_element.getNodeName().equals(NEWLINE_ELEMENT)) 
     137                    { 
     138                        Element newline_element = document.createElement(NEWLINE_ELEMENT); 
     139                        document_element.insertBefore(newline_element, sibling_element); 
     140                    } 
     141                    return sibling_element; 
     142                } 
     143                // Otherwise try to find a matching 'name' and add after the last one in that group. 
     144                else 
     145                { 
     146                    int index = 0; 
     147                    target_element_name = target_element.getAttribute(StaticStrings.NAME_ATTRIBUTE); 
     148                    boolean found = false; 
     149                    // Skip all of the special metadata 
     150                    Element matched_element = (Element) matching_elements.item(index); 
     151                    while (matched_element.getAttribute(StaticStrings.SPECIAL_ATTRIBUTE).equals(StaticStrings.TRUE_STR)) 
     152                    { 
     153                        index++; 
     154                        matched_element = (Element) matching_elements.item(index); 
     155                    } 
     156                    // Begin search 
     157                    while (!found && matched_element != null) 
     158                    { 
     159                        if (matched_element.getAttribute(StaticStrings.NAME_ATTRIBUTE).equals(target_element_name)) 
     160                        { 
     161                            found = true; 
     162                        } 
     163                        else 
     164                        { 
     165                            index++; 
     166                            matched_element = (Element) matching_elements.item(index); 
     167                        } 
     168                    } 
     169                    // If we found a match, we need to continue checking until we find the last name match. 
     170                    if (found) 
     171                    { 
     172                        index++; 
     173                        Element previous_sibling = matched_element; 
     174                        Element sibling_element = (Element) matching_elements.item(index); 
     175                        while (sibling_element != null && sibling_element.getAttribute(StaticStrings.NAME_ATTRIBUTE).equals(target_element_name)) 
     176                        { 
     177                            previous_sibling = sibling_element; 
     178                            index++; 
     179                            sibling_element = (Element) matching_elements.item(index); 
     180                        } 
     181                        // Previous sibling now holds the command immediately before where we want to add, so find its next sibling and add to that. In this one case we can ignore new lines! 
     182                        return previous_sibling.getNextSibling(); 
     183                    } 
     184                    // If not found we just add after last metadata element 
     185                    else 
     186                    { 
     187                        Element last_element = (Element) matching_elements.item(matching_elements.getLength() - 1); 
     188                        return last_element.getNextSibling(); 
     189                    } 
     190                } 
     191 
     192            } 
     193            else 
     194            { 
     195                ///ystem.err.println("Not dealing with collection meta."); 
     196                Element matched_element = (Element) matching_elements.item(matching_elements.getLength() - 1); 
     197                // One final quick test. If the matched element is immediately followed by a NewLine command, then we insert another NewLine after the matched command, then return the NewLine instead (thus the about to be inserted command will be placed between the two NewLines) 
     198                Node sibling_element = matched_element.getNextSibling(); 
     199                if (sibling_element != null && sibling_element.getNodeName().equals(NEWLINE_ELEMENT)) 
     200                { 
     201                    Element newline_element = document.createElement(NEWLINE_ELEMENT); 
     202                    document_element.insertBefore(newline_element, sibling_element); 
     203                } 
     204                return sibling_element; // Note that this may be null 
     205            } 
     206        } 
     207        ///ystem.err.println("No matching elements found."); 
     208        // Locate where this command is in the ordering 
     209        int command_index = -1; 
     210        for (int i = 0; command_index == -1 && i < COMMAND_ORDER.length; i++) 
     211        { 
     212            if (COMMAND_ORDER[i].equals(target_element_name)) 
     213            { 
     214                command_index = i; 
     215            } 
     216        } 
     217        ///ystem.err.println("Command index is: " + command_index); 
     218        // Now move forward, checking for existing elements in each of the preceeding command orders. 
     219        int preceeding_index = command_index - 1; 
     220        ///ystem.err.println("Searching before the target command."); 
     221        while (preceeding_index >= 0) 
     222        { 
     223            matching_elements = document_element.getElementsByTagName(COMMAND_ORDER[preceeding_index]); 
     224            // If we've found a match 
     225            if (matching_elements.getLength() > 0) 
     226            { 
     227                // We add after the last element 
     228                Element matched_element = (Element) matching_elements.item(matching_elements.getLength() - 1); 
     229                // One final quick test. If the matched element is immediately followed by a NewLine command, then we insert another NewLine after the matched command, then return the NewLine instead (thus the about to be inserted command will be placed between the two NewLines) 
     230                Node sibling_element = matched_element.getNextSibling(); 
     231                if (sibling_element != null && sibling_element.getNodeName().equals(NEWLINE_ELEMENT)) 
     232                { 
     233                    Element newline_element = document.createElement(NEWLINE_ELEMENT); 
     234                    document_element.insertBefore(newline_element, sibling_element); 
     235                } 
     236                return sibling_element; // Note that this may be null 
     237            } 
     238            preceeding_index--; 
     239        } 
     240        // If all that fails, we now move backwards through the commands 
     241        int susceeding_index = command_index + 1; 
     242        ///ystem.err.println("Searching after the target command."); 
     243        while (susceeding_index < COMMAND_ORDER.length) 
     244        { 
     245            matching_elements = document_element.getElementsByTagName(COMMAND_ORDER[susceeding_index]); 
     246            // If we've found a match 
     247            if (matching_elements.getLength() > 0) 
     248            { 
     249                // We add before the first element 
     250                Element matched_element = (Element) matching_elements.item(0); 
     251                // One final quick test. If the matched element is immediately preceeded by a NewLine command, then we insert another NewLine before the matched command, then return this new NewLine instead (thus the about to be inserted command will be placed between the two NewLines) 
     252                Node sibling_element = matched_element.getPreviousSibling(); 
     253                if (sibling_element != null && sibling_element.getNodeName().equals(NEWLINE_ELEMENT)) 
     254                { 
     255                    Element newline_element = document.createElement(NEWLINE_ELEMENT); 
     256                    document_element.insertBefore(newline_element, sibling_element); 
     257                } 
     258                return sibling_element; // Note that this may be null 
     259            } 
     260            susceeding_index++; 
     261        } 
     262        // Well. Apparently there are no other commands in this collection configuration. So append away... 
     263        return null; 
     264    } 
     265 
     266    static public NodeList getElementsByTagName(String element_name) 
     267    { 
     268        return document.getDocumentElement().getElementsByTagName(element_name); 
     269    } 
     270 
     271    public Element getDocumentElement() 
     272    { 
     273        return document.getDocumentElement(); 
     274    } 
     275 
     276    /** 
     277     * This debug facility shows the currently loaded collect.cfg or 
     278     * CollectConfig.xml file as a DOM tree. 
     279     */ 
     280    public void display() 
     281    { 
     282        JDialog dialog = new JDialog(Gatherer.g_man, "Collection Configuration", false); 
     283        dialog.setSize(400, 400); 
     284        JPanel content_pane = (JPanel) dialog.getContentPane(); 
     285        final DOMTree tree = new DOMTree(document); 
     286        JButton refresh_button = new GLIButton("Refresh Tree"); 
     287        refresh_button.addActionListener(new ActionListener() 
     288        { 
     289            public void actionPerformed(ActionEvent event) 
     290            { 
     291                tree.setDocument(document); 
     292            } 
     293        }); 
     294        content_pane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 
     295        content_pane.setLayout(new BorderLayout()); 
     296        content_pane.add(new JScrollPane(tree), BorderLayout.CENTER); 
     297        content_pane.add(refresh_button, BorderLayout.SOUTH); 
     298        dialog.setVisible(true); 
     299    } 
     300 
     301    public File getFile() 
     302    { 
     303        return collect_config_file; 
     304    } 
     305 
     306    public Element getCreator() 
     307    { 
     308        Element element = getOrCreateElementByTagName(StaticStrings.COLLECTIONMETADATA_CREATOR_ELEMENT, null, null); 
     309        element.setAttribute(StaticStrings.NAME_ATTRIBUTE, StaticStrings.COLLECTIONMETADATA_CREATOR_STR); 
     310        element.setAttribute(StaticStrings.SPECIAL_ATTRIBUTE, StaticStrings.TRUE_STR); 
     311        return element; 
     312    } 
     313 
     314    public Element getMaintainer() 
     315    { 
     316        Element element = getOrCreateElementByTagName(StaticStrings.COLLECTIONMETADATA_MAINTAINER_ELEMENT, null, null); 
     317        element.setAttribute(StaticStrings.NAME_ATTRIBUTE, StaticStrings.COLLECTIONMETADATA_MAINTAINER_STR); 
     318        element.setAttribute(StaticStrings.SPECIAL_ATTRIBUTE, StaticStrings.TRUE_STR); 
     319        return element; 
     320    } 
     321 
     322    /** Retrieve or create the languages Element. */ 
     323    public Element getLanguages() 
     324    { 
     325        return getOrCreateElementByTagName(StaticStrings.LANGUAGES_ELEMENT, null, null); 
     326    } 
     327 
     328    public Element getLanguageMetadata() 
     329    { 
     330        return getOrCreateElementByTagName(StaticStrings.LANGUAGE_METADATA_ELEMENT, null, null); 
     331    } 
     332 
     333    public Element getLevels() 
     334    { 
     335        return getOrCreateElementByTagName(StaticStrings.INDEXOPTIONS_ELEMENT, StaticStrings.NAME_ATTRIBUTE, StaticStrings.LEVELS_STR); 
     336    } 
     337 
     338    public Element getLevelDefault() 
     339    { 
     340        return getOrCreateElementByTagName(StaticStrings.INDEXOPTION_DEFAULT_ELEMENT, StaticStrings.NAME_ATTRIBUTE, StaticStrings.LEVEL_DEFAULT_STR); 
     341    } 
     342 
     343    public Element getIndexOptions() 
     344    { 
     345        return getOrCreateElementByTagName(StaticStrings.INDEXOPTIONS_ELEMENT, StaticStrings.NAME_ATTRIBUTE, StaticStrings.INDEXOPTIONS_STR); 
     346    } 
     347 
     348    /** 
     349     * Retrieve or create the indexes Element. Note that this method behaves 
     350     * differently from the other getBlah methods, in that it also has to keep 
     351     * in mind that indexes come in two flavours, MG and MGPP. 
     352     */ 
     353    public Element getMGIndexes() 
     354    { 
     355        return getOrCreateElementByTagName(StaticStrings.INDEXES_ELEMENT, StaticStrings.MGPP_ATTRIBUTE, StaticStrings.FALSE_STR); 
     356    } 
     357 
     358    public Element getMGPPIndexes() 
     359    { 
     360        return getOrCreateElementByTagName(StaticStrings.INDEXES_ELEMENT, StaticStrings.MGPP_ATTRIBUTE, StaticStrings.TRUE_STR); 
     361    } 
     362 
     363    public Element getPublic() 
     364    { 
     365        Element element = getOrCreateElementByTagName(StaticStrings.COLLECTIONMETADATA_PUBLIC_ELEMENT, null, null); 
     366        element.setAttribute(StaticStrings.NAME_ATTRIBUTE, StaticStrings.COLLECTIONMETADATA_PUBLIC_STR); 
     367        element.setAttribute(StaticStrings.SPECIAL_ATTRIBUTE, StaticStrings.TRUE_STR); 
     368        return element; 
     369    } 
     370 
     371    public Element getBuildType() 
     372    { 
     373        Element element = getOrCreateElementByTagName(StaticStrings.BUILDTYPE_ELEMENT, null, null); 
     374        element.setAttribute(StaticStrings.NAME_ATTRIBUTE, StaticStrings.BUILDTYPE_STR); 
     375        element.setAttribute(StaticStrings.SPECIAL_ATTRIBUTE, StaticStrings.TRUE_STR); 
     376        return element; 
     377 
     378    } 
     379 
     380    public Element getDatabaseType() 
     381    { 
     382        Element element = getOrCreateElementByTagName(StaticStrings.DATABASETYPE_ELEMENT, null, null); 
     383        element.setAttribute(StaticStrings.NAME_ATTRIBUTE, StaticStrings.DATABASETYPE_STR); 
     384        element.setAttribute(StaticStrings.SPECIAL_ATTRIBUTE, StaticStrings.TRUE_STR); 
     385        return element; 
     386 
     387    } 
     388 
     389    /** Retrieve or create the subindexes Element. */ 
     390    public Element getSubIndexes() 
     391    { 
     392        return getOrCreateElementByTagName(StaticStrings.SUBCOLLECTION_INDEXES_ELEMENT, null, null); 
     393    } 
     394 
     395    /** Retrieve or create the supercollections Element. */ 
     396    public Element getSuperCollection() 
     397    { 
     398        return getOrCreateElementByTagName(StaticStrings.SUPERCOLLECTION_ELEMENT, null, null); 
     399    } 
     400 
     401    public boolean ready() 
     402    { 
     403        return document != null; 
     404    } 
     405 
     406    /** ************************** Private Methods ***************************/ 
     407 
     408    /** Retrieve or create the indexes Element. */ 
     409    static private Element getOrCreateElementByTagName(String name, String conditional_attribute, String required_value) 
     410    { 
     411        Element document_element = document.getDocumentElement(); 
     412        NodeList elements = document_element.getElementsByTagName(name); 
     413        int elements_length = elements.getLength(); 
     414        if (elements_length > 0) 
     415        { 
     416            if (conditional_attribute == null) 
     417            { 
     418                document_element = null; 
     419                return (Element) elements.item(0); 
     420            } 
     421            else 
     422            { 
     423                for (int i = 0; i < elements_length; i++) 
     424                { 
     425                    Element element = (Element) elements.item(i); 
     426                    if (element.getAttribute(conditional_attribute).equals(required_value)) 
     427                    { 
     428                        document_element = null; 
     429                        return element; 
     430                    } 
     431                    element = null; 
     432                } 
     433            } 
     434        } 
     435        // Create the element 
     436        Element element = document.createElement(name); 
     437        // If there was a property set it 
     438        if (conditional_attribute != null) 
     439        { 
     440            element.setAttribute(conditional_attribute, required_value); 
     441        } 
     442        Node target_node = findInsertionPoint(element); 
     443        if (target_node != null) 
     444        { 
     445            document_element.insertBefore(element, target_node); 
     446        } 
     447        else 
     448        { 
     449            document_element.appendChild(element); 
     450        } 
     451        document_element = null; 
     452        return element; 
     453    } 
     454 
     455    /** 
     456     * Write the text to the buffer. This is used so we don't have to worry 
     457     * about storing intermediate String values just so we can calaulate length 
     458     * and offset. 
     459     *  
     460     * @param writer 
     461     *            the BufferedWriter to which the str will be written 
     462     * @param str 
     463     *            the String to be written 
     464     */ 
     465    private void write(BufferedWriter writer, String str) throws IOException 
     466    { 
     467        writer.write(str, 0, str.length()); 
     468    } 
     469 
     470    public void saveIfNecessary() 
     471    { 
     472 
     473        // Generate a string version of internal document 
     474        String config_file_string = null; 
     475        if (Gatherer.GS3) 
     476        { 
     477            config_file_string = CollectionConfigXMLReadWrite.generateStringVersion(document); 
     478        } 
     479        else 
     480        { 
     481            config_file_string = CollectCfgReadWrite.generateStringVersion(document); 
     482        } 
     483        // compare to saved version 
     484        if (saved_config_file_string != null) 
     485        { 
     486            if (saved_config_file_string.equals(config_file_string)) 
     487            { 
     488                DebugStream.println(collect_config_filename + " file hasn't changed so no save necessary..."); 
     489                return; 
     490            } 
     491        } 
     492 
     493        // We need to save... 
     494        DebugStream.println(collect_config_filename + " file has changed, saving now..."); 
     495 
     496        // If we're using the Local Library we must release the collection before writing to the collect.cfg file 
     497        String collection_name = CollectionManager.getLoadedCollectionName(true); // url style slash 
     498        boolean collection_released = false; 
     499        if (Gatherer.c_man.built() && LocalLibraryServer.isRunning() == true) 
     500        { 
     501            // Release the collection 
     502            LocalLibraryServer.releaseCollection(collection_name); 
     503            collection_released = true; 
     504        } 
     505 
     506        // Make a backup of the existing config file 
     507        if (collect_config_file.exists()) 
     508        { 
     509            String config_filename; 
     510            String backup_filename; 
     511            if (Gatherer.GS3) 
     512            { 
     513                config_filename = Utility.COLLECTION_CONFIG_XML; 
     514                backup_filename = Utility.COLLECTION_CONFIG_BAK; 
     515            } 
     516            else 
     517            { 
     518                config_filename = StaticStrings.COLLECT_CFG; 
     519                backup_filename = Utility.COLLECT_BAK; 
     520            } 
     521            File original_file = new File(collect_config_file.getParentFile(), config_filename); 
     522            File backup_file = new File(collect_config_file.getParentFile(), backup_filename); 
     523            if (backup_file.exists()) 
     524            { 
     525                backup_file.delete(); 
     526            } 
     527            if (!original_file.renameTo(backup_file)) 
     528            { 
     529                System.err.println("Warning: can't rename " + config_filename + " to " + backup_filename); 
     530            } 
     531        } 
     532 
     533        // now save the file 
     534        if (Gatherer.GS3) 
     535        { 
     536            CollectionConfigXMLReadWrite.save(collect_config_file, document); 
     537        } 
     538        else 
     539        { 
     540            // we have already converted to string, so save here 
     541            try 
     542            { 
     543                OutputStream ostream = new FileOutputStream(collect_config_file); 
     544                Writer file_writer = new OutputStreamWriter(ostream, ENCODING); 
     545                BufferedWriter buffered_writer = new BufferedWriter(file_writer); 
     546                buffered_writer.write(config_file_string); 
     547                buffered_writer.close(); 
     548            } 
     549            catch (Exception exception) 
     550            { 
     551                DebugStream.println("Error in CollectionConfiguration.save(): " + exception); 
     552                DebugStream.printStackTrace(exception); 
     553            } 
     554 
     555        } 
     556 
     557        // save the string version 
     558        saved_config_file_string = config_file_string; 
     559 
     560        // If we're using a remote Greenstone server, upload the new collect.cfg file 
     561        if (Gatherer.isGsdlRemote) 
     562        { 
     563            Gatherer.remoteGreenstoneServer.uploadCollectionFile(collection_name, collect_config_file); 
     564        } 
     565 
     566        // Now re-add the collection to the Local Library server 
     567        if (collection_released) 
     568        { 
     569            LocalLibraryServer.addCollection(collection_name); 
     570        } 
     571    } 
     572 
    485573} 
    486  
    487