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.HashSet; 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 371 while (i < args.length) { 372 Option option = null; 373 String value = null; 374 /* Recognizes 375 * --optiongroup-optionname=value 376 * --optionname=value 377 * --optiongroup-optionname (unary option) 378 * --optionname (unary option) 379 */ 380 if (args[i].startsWith("--")) { 381 if (args[i].length() == 2) { 382 throw new OptionException("The argument contains only '--', please check the user guide to see the correct format. "); 383 } 384 String optionstring; 385 String optiongroup; 386 String optionname; 387 int indexEqualSign = args[i].indexOf('='); 388 if (indexEqualSign != -1) { 389 value = args[i].substring(indexEqualSign+1); 390 optionstring = args[i].substring(2, indexEqualSign); 391 } else { 392 value = null; 393 optionstring = args[i].substring(2); 394 } 395 int indexMinusSign = optionstring.indexOf('-'); 396 if (indexMinusSign != -1) { 397 optionname = optionstring.substring(indexMinusSign+1); 398 optiongroup = optionstring.substring(0, indexMinusSign); 399 } else { 400 optiongroup = null; 401 optionname = optionstring; 402 } 403 404 option = optionDescriptions.getOption(optiongroup, optionname); 405 if (option instanceof UnaryOption) { 406 value = "used"; 407 } 408 i++; 409 } 410 /* Recognizes 411 * -optionflag value 412 * -optionflag (unary option) 413 */ 414 else if (args[i].startsWith("-")) { 415 if (args[i].length() < 2) { 416 throw new OptionException("Wrong use of option flag '"+args[i]+"', please check the user guide to see the correct format. "); 417 } 418 option = optionDescriptions.getOption(args[i].substring(1)); 419 420 if (option instanceof UnaryOption) { 421 value = "used"; 422 } else { 423 i++; 424 if (args.length > i) { 425 value = args[i]; 426 } else { 427 throw new OptionException("Could not find the corresponding value for -"+option.getFlag()+". "); 428 } 429 } 430 i++; 431 } else { 432 throw new OptionException("The option should starts with a minus sign (-), error at argument '"+args[i]+"'"); 433 } 434 Object optionvalue = option.getValueObject(value); 435 optionValues.addOptionValue(OptionContainer.COMMANDLINE, containerIndex, option, optionvalue); 436 } 437 return true; 438 } 439 440 441 /** 442 * Parses the option file for option values. 443 * 444 * @param fileName The option file name (must be a xml file). 445 * @throws OptionException 446 */ 447 public void parseOptionInstanceXMLfile(String fileName) throws MaltChainedException { 448 File file = new File(fileName); 449 450 try { 451 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 452 DocumentBuilder db = dbf.newDocumentBuilder(); 453 454 Element root = db.parse(file).getDocumentElement(); 455 NodeList containers = root.getElementsByTagName("optioncontainer"); 456 Element container; 457 for (int i = 0; i < containers.getLength(); i++) { 458 container = (Element)containers.item(i); 459 parseOptionValues(container, i); 460 } 461 } catch (IOException e) { 462 throw new OptionException("Can't find the file "+fileName+". ", e); 463 } catch (OptionException e) { 464 throw new OptionException("Problem parsing the file "+fileName+". ", e); 465 } catch (ParserConfigurationException e) { 466 throw new OptionException("Problem parsing the file "+fileName+". ", e); 467 } catch (SAXException e) { 468 throw new OptionException("Problem parsing the file "+fileName+". ", e); 469 } 470 } 471 472 /** 473 * Parses an option container for option values. 474 * 475 * @param container a reference to an individual option container in the DOM tree. 476 * @param containerName the name of this container. 477 * @throws OptionException 478 */ 479 private void parseOptionValues(Element container, int containerIndex) throws MaltChainedException { 480 NodeList optiongroups = container.getElementsByTagName("optiongroup"); 481 Element optiongroup; 482 for (int i = 0; i < optiongroups.getLength(); i++) { 483 optiongroup = (Element)optiongroups.item(i); 484 String groupname = optiongroup.getAttribute("groupname").toLowerCase(); 485 if (groupname == null) { 486 throw new OptionException("The option group name is missing. "); 487 } 488 NodeList optionvalues = optiongroup.getElementsByTagName("option"); 489 Element optionvalue; 490 491 for (int j = 0; j < optionvalues.getLength(); j++) { 492 optionvalue = (Element)optionvalues.item(j); 493 String optionname = optionvalue.getAttribute("name").toLowerCase(); 494 String value = optionvalue.getAttribute("value"); 495 496 if (optionname == null) { 497 throw new OptionException("The option name is missing. "); 498 } 499 500 Option option = optionDescriptions.getOption(groupname, optionname); 501 502 if (option instanceof UnaryOption) { 503 value = "used"; 504 } 505 if (value == null) { 506 throw new OptionException("The option value is missing. "); 507 } 508 Object ovalue = option.getValueObject(value); 509 optionValues.addOptionValue(OptionContainer.OPTIONFILE, containerIndex, option, ovalue); 510 } 511 } 512 } 513 514 /** 515 * Returns a string representation of all option value, except the options in a option group specified 516 * by the excludeGroup argument. 517 * 518 * @param containerIndex The index of the option container (0..n and -1 is default values). 519 * @param excludeGroups a set of option group names that should by excluded in the string representation 520 * @return a string representation of all option value 521 * @throws MaltChainedException 522 */ 523 public String toStringPrettyValues(int containerIndex, HashSet<String> excludeGroups) throws MaltChainedException { 524 int reservedSpaceForOptionName = 30; 525 OptionGroup.toStringSetting = OptionGroup.WITHGROUPNAME; 526 StringBuilder sb = new StringBuilder(); 527 if (containerIndex == OptionManager.DEFAULTVALUE) { 528 for (String groupname : optionDescriptions.getOptionGroupNameSet()) { 529 if (excludeGroups.contains(groupname)) continue; 530 sb.append(groupname+"\n"); 531 for (Option option : optionDescriptions.getOptionGroupList(groupname)) { 532 int nSpaces = reservedSpaceForOptionName - option.getName().length(); 533 if (nSpaces <= 1) { 534 nSpaces = 1; 535 } 536 sb.append(new Formatter().format(" %s (%4s)%"+nSpaces+"s %s\n", option.getName(), "-"+option.getFlag()," ", option.getDefaultValueString())); 537 } 538 } 539 } else { 540 for (String groupname : optionDescriptions.getOptionGroupNameSet()) { 541 if (excludeGroups.contains(groupname)) continue; 542 sb.append(groupname+"\n"); 543 for (Option option : optionDescriptions.getOptionGroupList(groupname)) { 544 String value = optionValues.getOptionValueString(containerIndex, option); 545 int nSpaces = reservedSpaceForOptionName - option.getName().length(); 546 if (nSpaces <= 1) { 547 nSpaces = 1; 548 } 549 550 if (value == null) { 551 sb.append(new Formatter().format(" %s (%4s)%"+nSpaces+"s %s\n", option.getName(), "-"+option.getFlag(), " ", option.getDefaultValueString())); 552 } else { 553 sb.append(new Formatter().format(" %s (%4s)%"+nSpaces+"s %s\n", option.getName(), "-"+option.getFlag(), " ", value)); 554 } 555 } 556 } 557 } 558 return sb.toString(); 559 } 560 561 /* (non-Javadoc) 562 * @see java.lang.Object#toString() 563 */ 564 public String toString() { 565 StringBuilder sb = new StringBuilder(); 566 sb.append(optionDescriptions+"\n"); 567 sb.append(optionValues+"\n"); 568 return sb.toString(); 569 } 570 }