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