001package org.maltparser.core.options; 002 003import java.io.BufferedReader; 004import java.io.BufferedWriter; 005import java.io.File; 006import java.io.FileInputStream; 007import java.io.FileNotFoundException; 008import java.io.FileOutputStream; 009import java.io.IOException; 010import java.io.InputStreamReader; 011import java.io.OutputStreamWriter; 012import java.io.UnsupportedEncodingException; 013import java.net.URL; 014 015import java.util.Formatter; 016import java.util.HashMap; 017import java.util.Set; 018import java.util.regex.Pattern; 019 020import javax.xml.parsers.DocumentBuilder; 021import javax.xml.parsers.DocumentBuilderFactory; 022import javax.xml.parsers.ParserConfigurationException; 023 024import org.maltparser.core.exception.MaltChainedException; 025import org.maltparser.core.options.option.EnumOption; 026import org.maltparser.core.options.option.ClassOption; 027import org.maltparser.core.options.option.StringEnumOption; 028import org.maltparser.core.options.option.Option; 029import org.maltparser.core.options.option.UnaryOption; 030import org.maltparser.core.plugin.PluginLoader; 031import org.w3c.dom.Element; 032import org.w3c.dom.NodeList; 033import org.xml.sax.SAXException; 034 035 036/** 037 * Option Manager is the management class for all option handling. All queries and manipulations of an option or an option value 038 * should go through this class. 039 * 040 * @author Johan Hall 041 * @since 1.0 042**/ 043public class OptionManager { 044 public static final int DEFAULTVALUE = -1; 045 private final OptionDescriptions optionDescriptions; 046 private final OptionValues optionValues; 047 private static OptionManager uniqueInstance = new OptionManager(); 048 049 /** 050 * Creates the Option Manager 051 */ 052 private OptionManager() { 053 optionValues = new OptionValues(); 054 optionDescriptions = new OptionDescriptions(); 055 } 056 057 /** 058 * Returns a reference to the single instance. 059 */ 060 public static OptionManager instance() { 061 return uniqueInstance; 062 } 063 064 /** 065 * Loads the option description file <code>/appdata/options.xml</code> 066 * 067 * @throws MaltChainedException 068 */ 069 public void loadOptionDescriptionFile() throws MaltChainedException { 070 optionDescriptions.parseOptionDescriptionXMLfile(getClass().getResource("/appdata/options.xml")); 071 } 072 073 074 /** 075 * Loads the option description file 076 * 077 * @param url URL of the option description file 078 * @throws MaltChainedException 079 */ 080 public void loadOptionDescriptionFile(URL url) throws MaltChainedException { 081 optionDescriptions.parseOptionDescriptionXMLfile(url); 082 } 083 084 /** 085 * Returns the option description 086 * 087 * @return the option description 088 */ 089 public OptionDescriptions getOptionDescriptions() { 090 return optionDescriptions; 091 } 092 093 public boolean hasOptions() { 094 return optionDescriptions.hasOptions(); 095 } 096 097 /** 098 * Returns the option value for an option that is specified by the option group name and option name. The 099 * container name points out the specific option container. 100 * 101 * 102 * @param containerIndex The index of the option container (0..n and -1 is default values). 103 * @param optiongroup The name of the option group. 104 * @param optionname The name of the option. 105 * @return an object that contains the value of the option, <i>null</i> if the option value could not be found. 106 * @throws OptionException 107 */ 108 public Object getOptionValue(int containerIndex, String optiongroup, String optionname) throws MaltChainedException { 109 Option option = optionDescriptions.getOption(optiongroup, optionname); 110 111 if (containerIndex == OptionManager.DEFAULTVALUE) { 112 return option.getDefaultValueObject(); 113 } 114 Object value = optionValues.getOptionValue(containerIndex, option); 115 if (value == null) { 116 value = option.getDefaultValueObject(); 117 } 118 return value; 119 } 120 121 public Object getOptionDefaultValue(String optiongroup, String optionname) throws MaltChainedException { 122 Option option = optionDescriptions.getOption(optiongroup, optionname); 123 return option.getDefaultValueObject(); 124 } 125 126 public Object getOptionValueNoDefault(int containerIndex, String optiongroup, String optionname) throws MaltChainedException { 127 Option option = optionDescriptions.getOption(optiongroup, optionname); 128 129 if (containerIndex == OptionManager.DEFAULTVALUE) { 130 return option.getDefaultValueObject(); 131 } 132 return optionValues.getOptionValue(containerIndex, option); 133 } 134 135 /** 136 * Returns a string representation of the option value for an option that is specified by the option group name and the option name. The 137 * container name points out the specific option container. 138 * 139 * @param containerIndex The index of the option container (0..n and -1 is default values). 140 * @param optiongroup The name of the option group. 141 * @param optionname The name of the option. 142 * @return a string representation of the option value 143 * @throws MaltChainedException 144 */ 145 public String getOptionValueString(int containerIndex, String optiongroup, String optionname) throws MaltChainedException { 146 Option option = optionDescriptions.getOption(optiongroup, optionname); 147 String value = optionValues.getOptionValueString(containerIndex, option); 148 if (value == null) { 149 value = option.getDefaultValueString(); 150 } 151 return value; 152 } 153 154 public String getOptionValueStringNoDefault(int containerIndex, String optiongroup, String optionname) throws MaltChainedException { 155 return optionValues.getOptionValueString(containerIndex, optionDescriptions.getOption(optiongroup, optionname)); 156 } 157 158 public void addLegalValue(String optiongroup, String optionname, String value, String desc, String target) throws MaltChainedException { 159 Option option = optionDescriptions.getOption(optiongroup, optionname); 160 if (option != null) { 161 if (option instanceof EnumOption) { 162 ((EnumOption)option).addLegalValue(value, desc); 163 } else if (option instanceof ClassOption) { 164 ((ClassOption)option).addLegalValue(value, desc, target); 165 } else if (option instanceof StringEnumOption) { 166 ((StringEnumOption)option).addLegalValue(value, desc, target); 167 } 168 } 169 } 170 171 /** 172 * Overloads the option value specified by the container index, the option group name, the option name. 173 * This method is used to override option that have specific dependencies. 174 * 175 * @param containerIndex the index of the option container (0..n and -1 is default values). 176 * @param optiongroup the name of the option group. 177 * @param optionname the name of the option. 178 * @param value the option value that should replace the current option value. 179 * @throws MaltChainedException 180 */ 181 public void overloadOptionValue(int containerIndex, String optiongroup, String optionname, String value) throws MaltChainedException { 182// Option option = optionDescriptions.getOption(optiongroup, optionname); 183// if (value == null) { 184// throw new OptionException("The option value is missing. "); 185// } 186// Object ovalue = option.getValueObject(value); 187// optionValues.addOptionValue(OptionContainer.DEPENDENCIES_RESOLVED, containerIndex, option, ovalue); 188 overloadOptionValue(containerIndex, OptionContainer.DEPENDENCIES_RESOLVED, optiongroup, optionname, value); 189 } 190 191 public void overloadOptionValue(int containerIndex, int containerType, String optiongroup, String optionname, String value) throws MaltChainedException { 192 Option option = optionDescriptions.getOption(optiongroup, optionname); 193 if (value == null) { 194 throw new OptionException("The option value is missing. "); 195 } 196 Object ovalue = option.getValueObject(value); 197 optionValues.addOptionValue(containerType, containerIndex, option, ovalue); 198 } 199 200 /** 201 * Returns the number of option values for a particular option container. 202 * 203 * @param containerIndex The index of the option container (0..n). 204 * @return the number of option values for a particular option container. 205 */ 206 public int getNumberOfOptionValues(int containerIndex) { 207 return optionValues.getNumberOfOptionValues(containerIndex); 208 } 209 210 /** 211 * Returns a sorted set of container names. 212 * 213 * @return a sorted set of container names. 214 */ 215 public Set<Integer> getOptionContainerIndices() { 216 return optionValues.getOptionContainerIndices(); 217 } 218 /** 219 * Loads the saved options (options that are marked with <code>usage=save</code>). 220 * 221 * @param fileName The path to the file where to load the saved options. 222 * @throws MaltChainedException 223 */ 224 public void loadOptions(int containerIndex, String fileName) throws MaltChainedException { 225 try { 226 loadOptions(containerIndex, new InputStreamReader(new FileInputStream(fileName), "UTF-8")); 227 } catch (FileNotFoundException e) { 228 throw new OptionException("The saved option file '"+fileName+"' cannot be found. ", e); 229 } catch (UnsupportedEncodingException e) { 230 throw new OptionException("The charset is unsupported. ", e); 231 } 232 } 233 234 235 /** 236 * Loads the saved options (options that are marked with <code>usage=Option.SAVE</code>). 237 * 238 * @param isr the input stream reader of the saved options file. 239 * @throws MaltChainedException 240 */ 241 public void loadOptions(int containerIndex, InputStreamReader isr) throws MaltChainedException { 242 try { 243 BufferedReader br = new BufferedReader(isr); 244 String line = null; 245 Option option = null; 246 Pattern tabPattern = Pattern.compile("\t"); 247 while ((line = br.readLine()) != null) { 248 String[] items = tabPattern.split(line); 249 if (items.length < 3 || items.length > 4) { 250 throw new OptionException("Could not load the saved option. "); 251 } 252 option = optionDescriptions.getOption(items[1], items[2]); 253 Object ovalue; 254 if (items.length == 3) { 255 ovalue = new String(""); 256 } else { 257 if (option instanceof ClassOption) { 258 if (items[3].startsWith("class ")) { 259 Class<?> clazz = null; 260 if (PluginLoader.instance() != null) { 261 clazz = PluginLoader.instance().getClass(items[3].substring(6)); 262 } 263 if (clazz == null) { 264 clazz = Class.forName(items[3].substring(6)); 265 } 266 ovalue = option.getValueObject(((ClassOption)option).getLegalValueString(clazz)); 267 } else { 268 ovalue = option.getValueObject(items[3]); 269 } 270 } else { 271 ovalue = option.getValueObject(items[3]); 272 } 273 } 274 optionValues.addOptionValue(OptionContainer.SAVEDOPTION, containerIndex, option, ovalue); 275 } 276 277 br.close(); 278 } catch (ClassNotFoundException e) { 279 throw new OptionException("The class cannot be found. ", e); 280 } catch (NumberFormatException e) { 281 throw new OptionException("Option container index isn't an integer value. ", e); 282 } catch (IOException e) { 283 throw new OptionException("Error when reading the saved options. ", e); 284 } 285 } 286 287 /** 288 * Saves all options that are marked as <code>usage=Option.SAVE</code> 289 * 290 * @param fileName The path to the file where the saveOption should by saved. 291 */ 292 public void saveOptions(String fileName) throws MaltChainedException { 293 try { 294 saveOptions(new OutputStreamWriter(new FileOutputStream(fileName), "UTF-8")); 295 } catch (FileNotFoundException e) { 296 throw new OptionException("The file '"+fileName+"' cannot be created. ", e); 297 } catch (UnsupportedEncodingException e) { 298 throw new OptionException("The charset 'UTF-8' is unsupported. ", e); 299 } 300 301 } 302 303 /** 304 * Saves all options that are marked as <code>usage=Option.SAVE</code> 305 * 306 * @param osw the output stream writer of the saved option file 307 * @throws MaltChainedException 308 */ 309 public void saveOptions(OutputStreamWriter osw) throws MaltChainedException { 310 try { 311 BufferedWriter bw = new BufferedWriter(osw); 312 Set<Option> optionToSave = optionDescriptions.getSaveOptionSet(); 313 314 Object value = null; 315 for (Integer index : optionValues.getOptionContainerIndices()) { 316 for (Option option : optionToSave) { 317 value = optionValues.getOptionValue(index, option); 318 if (value == null) { 319 value = option.getDefaultValueObject(); 320 } 321 bw.append(index+"\t"+option.getGroup().getName()+"\t"+option.getName()+"\t"+value+"\n"); 322 } 323 } 324 bw.flush(); 325 bw.close(); 326 } catch (IOException e) { 327 throw new OptionException("Error when saving the saved options. ", e); 328 } 329 } 330 331 /** 332 * Saves all options that are marked as usage=Option.SAVE for a particular option container. 333 * 334 * @param containerIndex The index of the option container (0..n). 335 * @param fileName The path to the file where the saveOption should by saved. 336 */ 337 public void saveOptions(int containerIndex, String fileName) throws MaltChainedException { 338 try { 339 saveOptions(containerIndex, new OutputStreamWriter(new FileOutputStream(fileName), "UTF-8")); 340 } catch (FileNotFoundException e) { 341 throw new OptionException("The file '"+fileName+"' cannot be found.", e); 342 } catch (UnsupportedEncodingException e) { 343 throw new OptionException("The charset 'UTF-8' is unsupported. ", e); 344 } 345 } 346 347 /** 348 * Saves all options that are marked as usage=Option.SAVE for a particular option container. 349 * 350 * @param containerIndex The index of the option container (0..n). 351 * @param osw the output stream writer of the saved option file 352 * @throws MaltChainedException 353 */ 354 public void saveOptions(int containerIndex, OutputStreamWriter osw) throws MaltChainedException { 355 try { 356 BufferedWriter bw = new BufferedWriter(osw); 357 Set<Option> optionToSave = optionDescriptions.getSaveOptionSet(); 358 359 Object value = null; 360 for (Option option : optionToSave) { 361 value = optionValues.getOptionValue(containerIndex, option); 362 if (value == null) { 363 value = option.getDefaultValueObject(); 364 } 365 bw.append(containerIndex+"\t"+option.getGroup().getName()+"\t"+option.getName()+"\t"+value+"\n"); 366 } 367 368 bw.flush(); 369 bw.close(); 370 } catch (IOException e) { 371 throw new OptionException("Error when saving the saved options.", e); 372 } 373 } 374 375 /** 376 * Creates several option maps for fast access to individual options. 377 * 378 * @throws OptionException 379 */ 380 public void generateMaps() throws MaltChainedException { 381 optionDescriptions.generateMaps(); 382 } 383 384 public boolean parseCommandLine(String argString, int containerIndex) throws MaltChainedException { 385 return parseCommandLine(argString.split(" "), containerIndex); 386 } 387 388 /** 389 * Parses the command line arguments. 390 * 391 * @param args An array of arguments that are supplied when starting the application. 392 * @throws OptionException 393 */ 394 public boolean parseCommandLine(String[] args, int containerIndex) throws MaltChainedException { 395 if (args == null || args.length == 0) { 396 return false; 397 } 398 int i = 0; 399 HashMap<String,String> oldFlags = new HashMap<String,String>(); 400 oldFlags.put("llo", "lo"); oldFlags.put("lso", "lo"); 401 oldFlags.put("lli", "li"); oldFlags.put("lsi", "li"); 402 oldFlags.put("llx", "lx"); oldFlags.put("lsx", "lx"); 403 oldFlags.put("llv", "lv"); oldFlags.put("lsv", "lv"); 404 while (i < args.length) { 405 Option option = null; 406 String value = null; 407 /* Recognizes 408 * --optiongroup-optionname=value 409 * --optionname=value 410 * --optiongroup-optionname (unary option) 411 * --optionname (unary option) 412 */ 413 if (args[i].startsWith("--")) { 414 if (args[i].length() == 2) { 415 throw new OptionException("The argument contains only '--', please check the user guide to see the correct format. "); 416 } 417 String optionstring; 418 String optiongroup; 419 String optionname; 420 int indexEqualSign = args[i].indexOf('='); 421 if (indexEqualSign != -1) { 422 value = args[i].substring(indexEqualSign+1); 423 optionstring = args[i].substring(2, indexEqualSign); 424 } else { 425 value = null; 426 optionstring = args[i].substring(2); 427 } 428 int indexMinusSign = optionstring.indexOf('-'); 429 if (indexMinusSign != -1) { 430 optionname = optionstring.substring(indexMinusSign+1); 431 optiongroup = optionstring.substring(0, indexMinusSign); 432 } else { 433 optiongroup = null; 434 optionname = optionstring; 435 } 436 437 option = optionDescriptions.getOption(optiongroup, optionname); 438 if (option instanceof UnaryOption) { 439 value = "used"; 440 } 441 i++; 442 } 443 /* Recognizes 444 * -optionflag value 445 * -optionflag (unary option) 446 */ 447 else if (args[i].startsWith("-")) { 448 if (args[i].length() < 2) { 449 throw new OptionException("Wrong use of option flag '"+args[i]+"', please check the user guide to see the correct format. "); 450 } 451 String flag = ""; 452 if (oldFlags.containsKey(args[i].substring(1))) { 453 flag = oldFlags.get(args[i].substring(1)); 454 } else { 455 flag = args[i].substring(1); 456 } 457 458 // Error message if the old flag '-r' (root handling) is used 459 if (args[i].substring(1).equals("r")) { 460 throw new OptionException("The flag -r (root_handling) is replaced with two flags -nr (allow_root) and -ne (allow_reduce) since MaltParser 1.7. Read more about these changes in the user guide."); 461 } 462 463 option = optionDescriptions.getOption(flag); 464 465 if (option instanceof UnaryOption) { 466 value = "used"; 467 } else { 468 i++; 469 if (args.length > i) { 470 value = args[i]; 471 } else { 472 throw new OptionException("Could not find the corresponding value for -"+option.getFlag()+". "); 473 } 474 } 475 i++; 476 } else { 477 throw new OptionException("The option should starts with a minus sign (-), error at argument '"+args[i]+"'"); 478 } 479 Object optionvalue = option.getValueObject(value); 480 optionValues.addOptionValue(OptionContainer.COMMANDLINE, containerIndex, option, optionvalue); 481 } 482 return true; 483 } 484 485 486 /** 487 * Parses the option file for option values. 488 * 489 * @param fileName The option file name (must be a xml file). 490 * @throws OptionException 491 */ 492 public void parseOptionInstanceXMLfile(String fileName) throws MaltChainedException { 493 File file = new File(fileName); 494 495 try { 496 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 497 DocumentBuilder db = dbf.newDocumentBuilder(); 498 499 Element root = db.parse(file).getDocumentElement(); 500 NodeList containers = root.getElementsByTagName("optioncontainer"); 501 Element container; 502 for (int i = 0; i < containers.getLength(); i++) { 503 container = (Element)containers.item(i); 504 parseOptionValues(container, i); 505 } 506 } catch (IOException e) { 507 throw new OptionException("Can't find the file "+fileName+". ", e); 508 } catch (OptionException e) { 509 throw new OptionException("Problem parsing the file "+fileName+". ", e); 510 } catch (ParserConfigurationException e) { 511 throw new OptionException("Problem parsing the file "+fileName+". ", e); 512 } catch (SAXException e) { 513 throw new OptionException("Problem parsing the file "+fileName+". ", e); 514 } 515 } 516 517 /** 518 * Parses an option container for option values. 519 * 520 * @param container a reference to an individual option container in the DOM tree. 521 * @param containerName the name of this container. 522 * @throws OptionException 523 */ 524 private void parseOptionValues(Element container, int containerIndex) throws MaltChainedException { 525 NodeList optiongroups = container.getElementsByTagName("optiongroup"); 526 Element optiongroup; 527 for (int i = 0; i < optiongroups.getLength(); i++) { 528 optiongroup = (Element)optiongroups.item(i); 529 String groupname = optiongroup.getAttribute("groupname").toLowerCase(); 530 if (groupname == null) { 531 throw new OptionException("The option group name is missing. "); 532 } 533 NodeList optionvalues = optiongroup.getElementsByTagName("option"); 534 Element optionvalue; 535 536 for (int j = 0; j < optionvalues.getLength(); j++) { 537 optionvalue = (Element)optionvalues.item(j); 538 String optionname = optionvalue.getAttribute("name").toLowerCase(); 539 String value = optionvalue.getAttribute("value"); 540 541 if (optionname == null) { 542 throw new OptionException("The option name is missing. "); 543 } 544 545 Option option = optionDescriptions.getOption(groupname, optionname); 546 547 if (option instanceof UnaryOption) { 548 value = "used"; 549 } 550 if (value == null) { 551 throw new OptionException("The option value is missing. "); 552 } 553 Object ovalue = option.getValueObject(value); 554 optionValues.addOptionValue(OptionContainer.OPTIONFILE, containerIndex, option, ovalue); 555 } 556 } 557 } 558 559 /** 560 * Returns a string representation of all option value, except the options in a option group specified 561 * by the excludeGroup argument. 562 * 563 * @param containerIndex The index of the option container (0..n and -1 is default values). 564 * @param excludeGroups a set of option group names that should by excluded in the string representation 565 * @return a string representation of all option value 566 * @throws MaltChainedException 567 */ 568 public String toStringPrettyValues(int containerIndex, Set<String> excludeGroups) throws MaltChainedException { 569 int reservedSpaceForOptionName = 30; 570 OptionGroup.toStringSetting = OptionGroup.WITHGROUPNAME; 571 StringBuilder sb = new StringBuilder(); 572 if (containerIndex == OptionManager.DEFAULTVALUE) { 573 for (String groupname : optionDescriptions.getOptionGroupNameSet()) { 574 if (excludeGroups.contains(groupname)) continue; 575 sb.append(groupname+"\n"); 576 for (Option option : optionDescriptions.getOptionGroupList(groupname)) { 577 int nSpaces = reservedSpaceForOptionName - option.getName().length(); 578 if (nSpaces <= 1) { 579 nSpaces = 1; 580 } 581 sb.append(new Formatter().format(" %s (%4s)%"+nSpaces+"s %s\n", option.getName(), "-"+option.getFlag()," ", option.getDefaultValueString())); 582 } 583 } 584 } else { 585 for (String groupname : optionDescriptions.getOptionGroupNameSet()) { 586 if (excludeGroups.contains(groupname)) continue; 587 sb.append(groupname+"\n"); 588 for (Option option : optionDescriptions.getOptionGroupList(groupname)) { 589 String value = optionValues.getOptionValueString(containerIndex, option); 590 int nSpaces = reservedSpaceForOptionName - option.getName().length(); 591 if (nSpaces <= 1) { 592 nSpaces = 1; 593 } 594 595 if (value == null) { 596 sb.append(new Formatter().format(" %s (%4s)%"+nSpaces+"s %s\n", option.getName(), "-"+option.getFlag(), " ", option.getDefaultValueString())); 597 } else { 598 sb.append(new Formatter().format(" %s (%4s)%"+nSpaces+"s %s\n", option.getName(), "-"+option.getFlag(), " ", value)); 599 } 600 } 601 } 602 } 603 return sb.toString(); 604 } 605 606 /* (non-Javadoc) 607 * @see java.lang.Object#toString() 608 */ 609 public String toString() { 610 StringBuilder sb = new StringBuilder(); 611 sb.append(optionDescriptions+"\n"); 612 sb.append(optionValues+"\n"); 613 return sb.toString(); 614 } 615}