Changeset 4932 for trunk/gli/src/org/greenstone/gatherer/cdm/Argument.java
- Timestamp:
- 2003-07-15T13:55:22+12:00 (21 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/gli/src/org/greenstone/gatherer/cdm/Argument.java
r4838 r4932 6 6 * University of Waikato, New Zealand. 7 7 * 8 * <BR><BR>9 *10 8 * Author: John Thompson, Greenstone Digital Library, University of Waikato 11 9 * 12 * <BR><BR>13 *14 10 * Copyright (C) 1999 New Zealand Digital Library Project 15 *16 * <BR><BR>17 11 * 18 12 * This program is free software; you can redistribute it and/or modify … … 21 15 * (at your option) any later version. 22 16 * 23 * <BR><BR>24 *25 17 * This program is distributed in the hope that it will be useful, 26 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 27 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 28 20 * GNU General Public License for more details. 29 *30 * <BR><BR>31 21 * 32 22 * You should have received a copy of the GNU General Public License … … 35 25 *######################################################################## 36 26 */ 37 38 39 40 41 42 43 /* GPL_HEADER */44 27 package org.greenstone.gatherer.cdm; 45 /************************************************************************************** 46 * Title: Gatherer 47 * Description: The Gatherer: a tool for gathering and enriching a digital collection. 48 * Company: The University of Waikato 49 * Written: 01/05/02 50 * Revised: 16/08/02 Optimized and Commented. 51 **************************************************************************************/ 52 import java.io.Serializable; 53 import java.util.ArrayList; 54 import java.util.Iterator; 55 import java.util.HashMap; 28 29 import java.io.*; 30 import java.util.*; 31 import org.greenstone.gatherer.Gatherer; 32 import org.greenstone.gatherer.cdm.CollectionConfiguration; 33 import org.greenstone.gatherer.collection.Collection; 34 import org.greenstone.gatherer.collection.CollectionManager; 56 35 import org.greenstone.gatherer.msm.ElementWrapper; 36 import org.greenstone.gatherer.msm.MetadataSetManager; 57 37 import org.greenstone.gatherer.msm.MSMUtils; 38 import org.greenstone.gatherer.util.StaticStrings; 58 39 import org.greenstone.gatherer.util.Utility; 40 import org.w3c.dom.*; 41 59 42 /** This class contains all the details about a single argument that can be passed to this plugin, including option lists if the parameters are restricted. 60 43 * @author John Thompson, Greenstone Digital Library, University of Waikato 61 44 * @version 2.3 62 45 */ 63 64 // ####################################################################################65 // Optimization Saving66 // ####################################################################################67 // Vector -> ArrayList + Memory, + Processor (pos. - Processor)68 // Hashtable -> HashMap + Processor69 // int -> byte (x7) + Memory (168b)70 // External method calls + Memory, Processor71 // ####################################################################################72 73 46 public class Argument 74 47 implements Comparable, Serializable { 75 /** If this argument has multiple values, then they are stored here. This was originally a Hashtable, but since the synchronization overhead is unwarrented it was changed to a HashMap. */ 76 private ArrayList values = null; 48 /** An element of the argument type enumeration. */ 49 static final public byte ENUM = 0; 50 /** An element of the argument type enumeration. */ 51 static final public byte FLAG = 1; 52 /** An element of the argument type enumeration. */ 53 static final public byte HIERARCHY = 2; 54 /** An element of the argument type enumeration. */ 55 static final public byte INTEGER = 3; 56 /** An element of the argument type enumeration. */ 57 static final public byte LANGUAGE = 4; 58 /** An element of the argument type enumeration. */ 59 static final public byte METADATA = 5; 60 /** An element of the argument type enumeration. */ 61 static final public byte METADATUM = 6; 62 /** An element of the argument type enumeration. */ 63 static final public byte STRING = 7; 77 64 /** <i>true</i> if this argument is required for the applicable script to work properly, <i>false</i> otherwise. */ 78 65 private boolean required = false; 79 66 /** The type of this argument. Initially an int, but bytes are cheaper. */ 80 private byte type = FLAG; 81 82 private ElementWrapper element = null; 67 private byte type = STRING; 68 private Element element; 83 69 /** If the argument is of type ENUM then this map holds all the various options. Each entry is an <option value> -> <description> mapping. */ 84 70 private HashMap list = null; … … 86 72 private String default_value = null; 87 73 /** The text description of this argument parsed from the pluginfo output. */ 88 private String desc = null;74 private String description = null; 89 75 /** The argument flag as it appears in the command. Also used as the unique identifier of an argument. */ 90 76 private String name = null; 91 77 /** The plugin that owns this argument, for the purposes of visualising inheritance. */ 92 78 private String owner = null; 93 /** If this argument has been assigned, what is its value. Must be non-null for the argument to be printed. */ 94 private String value = null; 95 /** An element of the argument type enumeration. */ 96 static final public byte ENUM = 0; 97 /** An element of the argument type enumeration. */ 98 static final public byte FLAG = 1; 99 /** An element of the argument type enumeration. */ 100 static final public byte HIERARCHY = 2; 101 /** An element of the argument type enumeration. */ 102 static final public byte INTEGER = 3; 103 /** An element of the argument type enumeration. */ 104 static final public byte LANGUAGE = 4; 105 /** An element of the argument type enumeration. */ 106 static final public byte METADATA = 5; 107 /** An element of the argument type enumeration. */ 108 static final public byte METADATUM = 6; 109 /** An element of the argument type enumeration. */ 110 static final public byte STRING = 7; 79 111 80 /** Default Constructor. */ 112 81 public Argument() { 113 82 } 114 /** Normal Constructor, based on data parsed from an information script. 115 * @param name The argument flag as it appears in the command, as a <strong>String</strong>. Also used as the unique identifier of an argument. 116 * @param desc The text description of this argument parsed from the output as a <strong>String</strong>. 117 * @param type The type of this argument as a <i>byte</i>. 118 * @param default_value The default value for a parameter type argument, which may include a Perl type regular expression, as a <strong>String</strong>. 119 */ 120 public Argument(String name, String desc, byte type, String default_value) { 121 this.default_value = default_value; 122 this.desc = desc; 123 this.name = name; 124 this.type = type; 125 if(type == ENUM) { 126 this.list = new HashMap(); 127 } 128 if(type == METADATUM) { 129 values = new ArrayList(); 130 } 131 } 83 84 public Argument(Element element) { 85 this.element = element; 86 } 87 132 88 /** Method to add an element to the option list. 133 134 135 89 * @param name The name value of the option as a <strong>String</strong>. 90 * @param desc The description of this options as a <strong>String</strong>. 91 */ 136 92 public void addOption(String name, String desc) { 137 93 if(type == ENUM && name != null) { … … 139 95 desc = ""; 140 96 } 141 if(!list.containsKey(name)) { 142 list.put(name, desc); 143 } 144 } 145 } 97 if(list == null) { 98 list = new HashMap(); 99 } 100 list.put(name, desc); 101 } 102 } 103 146 104 /** Method to compare two arguments for ordering. 147 148 149 150 151 105 * @param object The argument we are comparing to, as an <strong>Object</strong>. 106 * @return An <i>int</i> specifying the argument order, using values as set out in String. 107 * @see java.lang.String#compareTo 108 * @see org.greenstone.gatherer.cdm.Argument 109 */ 152 110 public int compareTo(Object object) { 153 Argument argument = (Argument)object; 154 return getName().compareTo(argument.getName()); 155 } 156 /** Method to deep copy this argument. 157 * @return A newly created <strong>Argument</strong> which has the same details as this one. 158 */ 111 if(object instanceof Argument) { 112 return getName().compareTo(((Argument)object).getName()); 113 } 114 else { 115 return toString().compareTo(object.toString()); 116 } 117 } 118 159 119 public Argument copy() { 160 Argument copy = new Argument(name, desc, type, default_value); 120 Argument copy = new Argument(); 121 copy.setDefaultValue(default_value); 122 copy.setDescription(description); 123 copy.setOptions(list); 124 copy.setOwner(owner); 125 copy.setName(name); 161 126 copy.setRequired(required); 162 if(values != null) { 163 copy.setValues(values); 164 } 165 if(list != null) { 166 HashMap list_deep_copy = new HashMap(); 167 Iterator it = list.keySet().iterator(); 168 while(it.hasNext()) { 169 String key = (String) it.next(); 170 String desc = (String) list.get(key); 171 list_deep_copy.put(new String(key), new String(desc)); 172 } 173 copy.setOptions(list_deep_copy); 174 } 127 copy.setType(type); 175 128 return copy; 176 129 } 130 177 131 /** Method to determine if two arguments are equal. 178 179 180 132 * @param object The argument to test against, as an <strong>Object</strong>. 133 * @return <i>true</i> if the arguments names match, <i>false</i> otherwise. 134 */ 181 135 public boolean equals(Object object) { 182 136 return (compareTo(object) == 0); 183 137 } 138 184 139 /** Method to retrieve the value of default_value. 185 186 140 * @return A <strong>String</strong> containing the default value. 141 */ 187 142 public String getDefaultValue() { 188 143 return default_value; 189 144 } 145 190 146 /** Method to retrieve this arguments description. 191 * @return A <strong>String</strong> containing the description. 192 */ 193 public String getDesc() { 194 return desc; 195 } 147 * @return A <strong>String</strong> containing the description. 148 */ 149 public String getDescription() { 150 return description; 151 } 152 196 153 /** Method to retrieve the description of a certain list option value. 197 198 199 200 public String getDesc (String key) {154 * @param key The <strong>String</strong> whose description we are searching for. 155 * @return The description of the desired key as a <strong>String</strong> which may be empty if no such key exists. 156 */ 157 public String getDescription(String key) { 201 158 if(list.containsKey(key)) { 202 159 return (String)list.get(key); … … 204 161 return ""; 205 162 } 163 206 164 /** Method to retrieve the option list for this argument. 207 208 209 public HashMap get List() {165 * @return A <strong>HashMap</strong> containing <option value> -> <description> entries. 166 */ 167 public HashMap getOptions() { 210 168 return list; 211 169 } 170 212 171 /** Method to retrieve the value of name. 213 214 172 * @return A <strong>String</strong> containing the argument name. 173 */ 215 174 public String getName() { 175 if(name == null && element != null) { 176 name = element.getAttribute(StaticStrings.NAME_ATTRIBUTE); 177 } 216 178 return name; 217 179 } 180 218 181 /** Retrieve the name of the owner of this argument. 219 220 182 * @return The owners name as a <strong>String</strong>. 183 */ 221 184 public String getOwner() { 222 185 return owner; 223 186 } 187 224 188 /** Method to determine the type of this argument. 225 226 189 * @return An <i>byte</i> specifying the type. 190 */ 227 191 public byte getType() { 228 192 return type; 229 193 } 194 230 195 /** Method to retrieve the value of value. 231 232 196 * @return The value of value as a <strong>String</strong>. 197 */ 233 198 public String getValue() { 234 if(element != null) { 235 return element.toString(); 199 String value = null; 200 // Only assigned arguments have values. 201 if(element != null) { 202 value = MSMUtils.getValue(element); 203 // We may have to retrieve the language dependant string for a MSM Element 204 if(type == METADATUM) { 205 ElementWrapper element_wrapper = Gatherer.c_man.getCollection().msm.getElement(value); 206 if(element_wrapper != null) { 207 value = element_wrapper.toString(); 208 } 209 } 236 210 } 237 211 return value; 238 212 } 213 239 214 /** Retrieve the vector of values. 240 241 215 * @return An <strong>ArrayList</strong> of values. 216 */ 242 217 public ArrayList getValues() { 218 ArrayList values = new ArrayList(); 219 // Only assigned arguments have values. 220 if(element != null) { 221 String value = MSMUtils.getValue(element); 222 StringTokenizer tokenizer = new StringTokenizer(value, ","); 223 while(tokenizer.hasMoreTokens()) { 224 String token = tokenizer.nextToken(); 225 if(type == METADATA) { 226 ElementWrapper element_wrapper = Gatherer.c_man.getCollection().msm.getElement(token); 227 if(element_wrapper != null) { 228 token = element_wrapper.toString(); 229 } 230 } 231 values.add(token); 232 } 233 } 243 234 return values; 244 235 } 236 245 237 /** Method to determine if this argument has been assigned. 246 247 238 * @return <i>true</i> if it has, <i>false</i> otherwise. 239 */ 248 240 public boolean isAssigned() { 249 return (required || value != null || (values != null && values.size() > 0)); 250 } 241 return (element != null && element.getAttribute(StaticStrings.ASSIGNED_ATTRIBUTE).equals(StaticStrings.TRUE_STR)); 242 } 243 244 public boolean isCustomArgument() { 245 return (element != null && element.getAttribute(StaticStrings.CUSTOM_ATTRIBUTE).equals(StaticStrings.TRUE_STR)); 246 } 247 251 248 /** Method to determine of this argument is required for the associated script to work. 252 253 249 * @return <i>true</i> if this argument is required, <i>false</i> otherwise. 250 */ 254 251 public boolean isRequired() { 255 252 return required; 256 253 } 254 257 255 /** Method to allow for the activation of arguments that might never have their setValue() method called. 258 * @param new_state The required state as a <i>boolean</i>. 259 */ 260 public void setAssigned(boolean new_state) { 261 if(new_state && (value == null || values == null)) { 262 value = ""; 263 } 264 else { 265 value = null; 266 } 267 } 256 * @param assigned the desired state as a boolean 257 */ 258 public void setAssigned(boolean assigned) { 259 if(element != null) { 260 element.setAttribute(StaticStrings.ASSIGNED_ATTRIBUTE, (assigned ? StaticStrings.TRUE_STR : StaticStrings.FALSE_STR)); 261 } 262 } 263 264 public void setCustomArgument(boolean custom) { 265 if(element != null) { 266 element.setAttribute(StaticStrings.CUSTOM_ATTRIBUTE, (custom ? StaticStrings.TRUE_STR : StaticStrings.FALSE_STR)); 267 } 268 } 269 268 270 /** Sets the value of default_value. 269 * @param new_default_value The new value for default_value as a <strong>String</strong>. 270 */ 271 public void setDefault(String new_default_value) { 272 default_value = new_default_value; 273 } 271 * @param default_value The new value for default_value as a <strong>String</strong>. 272 */ 273 public void setDefaultValue(String default_value) { 274 this.default_value = default_value; 275 } 276 274 277 /** Set the value of desc. 275 * @param new_descThe new value of desc as a <strong>String</strong>.276 277 public void setDesc (String new_desc) {278 desc = new_desc;279 } 280 281 public void setElement Value(ElementWrapperelement) {278 * @param description The new value of desc as a <strong>String</strong>. 279 */ 280 public void setDescription(String description) { 281 this.description = description; 282 } 283 284 public void setElement(Element element) { 282 285 this.element = element; 283 286 } 284 287 285 288 /** Set the value of name. 286 * @param new_name The new value of name as a <strong>String</strong>. 287 */ 288 public void setName(String new_name) { 289 name = new_name; 290 } 289 * @param name The new value of name as a <strong>String</strong>. 290 */ 291 public void setName(String name) { 292 this.name = name; 293 } 294 291 295 /** Sets the value of the options list. 292 * @param new_list The new options list as a <strong>HashMap</strong>. 293 */ 294 public void setOptions(HashMap new_list) { 295 list = new_list; 296 } 296 * @param list The new options list as a <strong>HashMap</strong>. 297 */ 298 public void setOptions(HashMap list) { 299 this.list = list; 300 } 301 297 302 /** Set the owner of this argument. 298 299 303 * @param owner The name of the owner of this argument as a <strong>String</strong>. 304 */ 300 305 public void setOwner(String owner) { 301 306 this.owner = owner; 302 307 } 308 303 309 /** Set the value of required. 304 * @param new_required The new value of required as a <i>boolean</i>. 305 */ 306 public void setRequired(boolean new_required) { 307 required = new_required; 308 } 310 * @param required The new value of required as a <i>boolean</i>. 311 */ 312 public void setRequired(boolean required) { 313 this.required = required; 314 } 315 309 316 /** Set the value of type. 310 * @param new_type The new value of type as an <i>byte</i>. 311 */ 312 public void setType(byte new_type) { 313 type = new_type; 314 if(type == ENUM) { 315 list = new HashMap(); 316 } 317 if(type == METADATUM && values == null) { 318 values = new ArrayList(); 319 } 320 } 317 * @param type The new value of type as an <i>byte</i>. 318 */ 319 public void setType(byte type) { 320 this.type = type; 321 } 322 321 323 /** Set the value of type, by matching a type to the given string. 322 323 324 * @param new_type A <strong>String</strong> which contains the name of a certain argument type. 325 */ 324 326 public void setType(String new_type) { 325 if(new_type.equalsIgnoreCase( "enum")) {327 if(new_type.equalsIgnoreCase(StaticStrings.ENUM_STR)) { 326 328 this.type = ENUM; 327 329 list = new HashMap(); 328 330 } 329 else if(new_type.equalsIgnoreCase( "flag")) {331 else if(new_type.equalsIgnoreCase(StaticStrings.FLAG_STR)) { 330 332 this.type = FLAG; 331 333 } 332 else if(new_type.equalsIgnoreCase( "hierarchy")) {334 else if(new_type.equalsIgnoreCase(StaticStrings.HIERARCHY_STR)) { 333 335 this.type = HIERARCHY; 334 336 } 335 else if(new_type.equalsIgnoreCase( "int")) {337 else if(new_type.equalsIgnoreCase(StaticStrings.INT_STR)) { 336 338 this.type = INTEGER; 337 339 } 338 else if(new_type.equalsIgnoreCase( "language")) {340 else if(new_type.equalsIgnoreCase(StaticStrings.LANGUAGE_STR)) { 339 341 this.type = LANGUAGE; 340 342 } 341 else if(new_type.equalsIgnoreCase( "metadata")) {343 else if(new_type.equalsIgnoreCase(StaticStrings.METADATA_TYPE_STR)) { 342 344 this.type = METADATA; 343 345 } 344 else if(new_type.equalsIgnoreCase( "metadatum")) {346 else if(new_type.equalsIgnoreCase(StaticStrings.METADATUM_TYPE_STR)) { 345 347 this.type = METADATUM; 346 if(values == null) { 347 values = new ArrayList(); 348 } 349 } 350 else if(new_type.equalsIgnoreCase("string")) { 348 } 349 else { 351 350 this.type = STRING; 352 351 } 353 } 354 355 /** Method to set the value of value. 356 * @param new_value The new value of value as a <strong>String</strong>. 357 */ 358 public void setValue(String new_value) { 359 this.value = new_value; 360 } 352 353 } 354 /** Method to set the value of this argument. 355 * @param value 356 * @see org.greenstone.gatherer.Gatherer 357 * @see org.greenstone.gatherer.msm.MSMUtils 358 */ 359 public void setValue(String value) { 360 if(element != null) { 361 MSMUtils.setValue(element, value); 362 } 363 else { 364 Gatherer.println("Argument.setValue(" + value + ") called on a base Argument."); 365 } 366 } 367 361 368 /** Set the values vector to the given values. Currently I just assign the new values, whereas I may later want to implement a deep clone. 362 * @param new_values An <strong>ArrayList</strong> of values. 363 */ 364 public void setValues(ArrayList new_values) { 365 values = new_values; 366 } 369 * @param values an ArrayList of values 370 * @see org.greenstone.gatherer.Gatherer 371 * @see org.greenstone.gatherer.msm.MSMUtils 372 */ 373 public void setValues(ArrayList values) { 374 if(element != null) { 375 StringBuffer value = new StringBuffer(); 376 int value_length = values.size(); 377 for(int i = 0; i < value_length; i++) { 378 value.append(values.get(i)); 379 value.append(StaticStrings.COMMA_CHARACTER); 380 } 381 value.deleteCharAt(value.length() - 1); // Remove last ',' 382 MSMUtils.setValue(element, value.toString()); 383 } 384 else { 385 Gatherer.println("Argument.setValues([" + values.size() + " items]) called on a base Argument."); 386 } 387 } 388 367 389 /** Method for translating the data of this class into a string. 368 * @return A <strong>String</strong> containing a fragment of the total arguments string. 369 */ 390 * @return a String containing a fragment of the total arguments string 391 * @see org.greenstone.gatherer.Gatherer 392 * @see org.greenstone.gatherer.collection.Collection 393 * @see org.greenstone.gatherer.collection.CollectionManager 394 * @see org.greenstone.gatherer.msm.MetadataSetManager 395 * @see org.greenstone.gatherer.msm.MSMUtils 396 * @see org.greenstone.gatherer.util.StaticStrings 397 */ 370 398 public String toString() { 371 switch(type) { 372 case FLAG: 373 return "-" + name; 374 case METADATA: 375 StringBuffer metadata_text = new StringBuffer("-"); 376 metadata_text.append(name); 377 metadata_text.append(" "); 378 if(element != null) { 379 String element_name = element.toString(); 380 if(element_name.indexOf(" ") != -1) { 381 metadata_text.append("\""); 382 metadata_text.append(element_name); 383 metadata_text.append("\""); 399 StringBuffer text = new StringBuffer("-"); 400 if(element != null) { 401 if(name == null) { 402 name = element.getAttribute(StaticStrings.NAME_ATTRIBUTE); 403 } 404 text.append(name); 405 String value = MSMUtils.getValue(element); 406 if(value.length() > 0) { 407 text.append(StaticStrings.SPACE_CHARACTER); 408 // Tokenize the string 409 StringTokenizer tokenizer = new StringTokenizer(value, ","); 410 while(tokenizer.hasMoreTokens()) { 411 String token = tokenizer.nextToken(); 412 if(type == METADATA || type == METADATUM) { 413 ElementWrapper element_wrapper = Gatherer.c_man.getCollection().msm.getElement(token); 414 if(element_wrapper != null) { 415 text.append(element_wrapper.toString()); 416 element_wrapper = null; 417 } 418 else { 419 text.append(token); 420 } 421 } 422 else { 423 text.append(token); 424 } 425 token = null; 426 text.append(StaticStrings.COMMA_CHARACTER); 384 427 } 385 else { 386 metadata_text.append(element_name); 387 } 388 element_name = null; 389 } 390 else { 391 metadata_text.append(value); 392 } 393 return metadata_text.toString(); 394 case METADATUM: 395 StringBuffer metadatum_text = new StringBuffer("-"); 396 metadatum_text.append(name); 397 metadatum_text.append(" "); 398 for(int i = 0; i < values.size(); i++) { 399 metadatum_text.append(values.get(i).toString()); 400 if(i < values.size() - 1) { 401 metadatum_text.append(","); 402 } 403 } 404 return metadatum_text.toString(); 405 case STRING: 406 // we need to put quotes if they are not there 407 if (value != null && value.indexOf(' ') != -1 && value.indexOf('"')==-1) { 408 // if there is a space and we haven't got any quotes 409 return "-" + name + " \"" + value + "\""; 410 } // otherwise it will stuff up but I cant be bothered doing this properly 411 default: 412 return "-" + name + " " + value; 413 } 414 } 415 416 public String toStringConfig() { 417 switch(type) { 418 case METADATA: 419 StringBuffer metadata_text = new StringBuffer("-"); 420 metadata_text.append(name); 421 if(element != null) { 422 metadata_text.append(" "); 423 String element_name = element.getName(); 424 if (element_name.startsWith(Utility.EXTRACTED_METADATA_NAMESPACE+MSMUtils.NS_SEP)) { 425 426 element_name = element_name.substring(element_name.indexOf(MSMUtils.NS_SEP)+1); 427 } 428 metadata_text.append(element_name); 429 element_name = null; 430 } 431 else if(value != null) { 432 metadata_text.append(" "); 433 metadata_text.append(value); 434 } 435 return metadata_text.toString(); 436 case METADATUM: 437 StringBuffer metadatum_text = new StringBuffer("-"); 438 metadatum_text.append(name); 439 metadatum_text.append(" "); 440 for(int i = 0; i < values.size(); i++) { 441 String temp_value = values.get(i).toString(); 442 if (temp_value.startsWith(Utility.EXTRACTED_METADATA_NAMESPACE+MSMUtils.NS_SEP)) { 443 444 temp_value = temp_value.substring(temp_value.indexOf(MSMUtils.NS_SEP)+1); 445 } 446 metadatum_text.append(temp_value); 447 if(i < values.size() - 1) { 448 metadatum_text.append(","); 449 } 450 } 451 return metadatum_text.toString(); 452 default: 453 return toString(); 454 } 455 } 456 428 tokenizer = null; 429 text.deleteCharAt(text.length() - 1); 430 } 431 value = null; 432 } 433 return text.toString(); 434 } 457 435 } 458 459
Note:
See TracChangeset
for help on using the changeset viewer.