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 // Error message if the old flag '-r' (root handling) is used
430 if (args[i].substring(1).equals("r")) {
431 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.");
432 }
433
434 option = optionDescriptions.getOption(flag);
435
436 if (option instanceof UnaryOption) {
437 value = "used";
438 } else {
439 i++;
440 if (args.length > i) {
441 value = args[i];
442 } else {
443 throw new OptionException("Could not find the corresponding value for -"+option.getFlag()+". ");
444 }
445 }
446 i++;
447 } else {
448 throw new OptionException("The option should starts with a minus sign (-), error at argument '"+args[i]+"'");
449 }
450 Object optionvalue = option.getValueObject(value);
451 optionValues.addOptionValue(OptionContainer.COMMANDLINE, containerIndex, option, optionvalue);
452 }
453 return true;
454 }
455
456
457 /**
458 * Parses the option file for option values.
459 *
460 * @param fileName The option file name (must be a xml file).
461 * @throws OptionException
462 */
463 public void parseOptionInstanceXMLfile(String fileName) throws MaltChainedException {
464 File file = new File(fileName);
465
466 try {
467 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
468 DocumentBuilder db = dbf.newDocumentBuilder();
469
470 Element root = db.parse(file).getDocumentElement();
471 NodeList containers = root.getElementsByTagName("optioncontainer");
472 Element container;
473 for (int i = 0; i < containers.getLength(); i++) {
474 container = (Element)containers.item(i);
475 parseOptionValues(container, i);
476 }
477 } catch (IOException e) {
478 throw new OptionException("Can't find the file "+fileName+". ", e);
479 } catch (OptionException e) {
480 throw new OptionException("Problem parsing the file "+fileName+". ", e);
481 } catch (ParserConfigurationException e) {
482 throw new OptionException("Problem parsing the file "+fileName+". ", e);
483 } catch (SAXException e) {
484 throw new OptionException("Problem parsing the file "+fileName+". ", e);
485 }
486 }
487
488 /**
489 * Parses an option container for option values.
490 *
491 * @param container a reference to an individual option container in the DOM tree.
492 * @param containerName the name of this container.
493 * @throws OptionException
494 */
495 private void parseOptionValues(Element container, int containerIndex) throws MaltChainedException {
496 NodeList optiongroups = container.getElementsByTagName("optiongroup");
497 Element optiongroup;
498 for (int i = 0; i < optiongroups.getLength(); i++) {
499 optiongroup = (Element)optiongroups.item(i);
500 String groupname = optiongroup.getAttribute("groupname").toLowerCase();
501 if (groupname == null) {
502 throw new OptionException("The option group name is missing. ");
503 }
504 NodeList optionvalues = optiongroup.getElementsByTagName("option");
505 Element optionvalue;
506
507 for (int j = 0; j < optionvalues.getLength(); j++) {
508 optionvalue = (Element)optionvalues.item(j);
509 String optionname = optionvalue.getAttribute("name").toLowerCase();
510 String value = optionvalue.getAttribute("value");
511
512 if (optionname == null) {
513 throw new OptionException("The option name is missing. ");
514 }
515
516 Option option = optionDescriptions.getOption(groupname, optionname);
517
518 if (option instanceof UnaryOption) {
519 value = "used";
520 }
521 if (value == null) {
522 throw new OptionException("The option value is missing. ");
523 }
524 Object ovalue = option.getValueObject(value);
525 optionValues.addOptionValue(OptionContainer.OPTIONFILE, containerIndex, option, ovalue);
526 }
527 }
528 }
529
530 /**
531 * Returns a string representation of all option value, except the options in a option group specified
532 * by the excludeGroup argument.
533 *
534 * @param containerIndex The index of the option container (0..n and -1 is default values).
535 * @param excludeGroups a set of option group names that should by excluded in the string representation
536 * @return a string representation of all option value
537 * @throws MaltChainedException
538 */
539 public String toStringPrettyValues(int containerIndex, Set<String> excludeGroups) throws MaltChainedException {
540 int reservedSpaceForOptionName = 30;
541 OptionGroup.toStringSetting = OptionGroup.WITHGROUPNAME;
542 StringBuilder sb = new StringBuilder();
543 if (containerIndex == OptionManager.DEFAULTVALUE) {
544 for (String groupname : optionDescriptions.getOptionGroupNameSet()) {
545 if (excludeGroups.contains(groupname)) continue;
546 sb.append(groupname+"\n");
547 for (Option option : optionDescriptions.getOptionGroupList(groupname)) {
548 int nSpaces = reservedSpaceForOptionName - option.getName().length();
549 if (nSpaces <= 1) {
550 nSpaces = 1;
551 }
552 sb.append(new Formatter().format(" %s (%4s)%"+nSpaces+"s %s\n", option.getName(), "-"+option.getFlag()," ", option.getDefaultValueString()));
553 }
554 }
555 } else {
556 for (String groupname : optionDescriptions.getOptionGroupNameSet()) {
557 if (excludeGroups.contains(groupname)) continue;
558 sb.append(groupname+"\n");
559 for (Option option : optionDescriptions.getOptionGroupList(groupname)) {
560 String value = optionValues.getOptionValueString(containerIndex, option);
561 int nSpaces = reservedSpaceForOptionName - option.getName().length();
562 if (nSpaces <= 1) {
563 nSpaces = 1;
564 }
565
566 if (value == null) {
567 sb.append(new Formatter().format(" %s (%4s)%"+nSpaces+"s %s\n", option.getName(), "-"+option.getFlag(), " ", option.getDefaultValueString()));
568 } else {
569 sb.append(new Formatter().format(" %s (%4s)%"+nSpaces+"s %s\n", option.getName(), "-"+option.getFlag(), " ", value));
570 }
571 }
572 }
573 }
574 return sb.toString();
575 }
576
577 /* (non-Javadoc)
578 * @see java.lang.Object#toString()
579 */
580 public String toString() {
581 StringBuilder sb = new StringBuilder();
582 sb.append(optionDescriptions+"\n");
583 sb.append(optionValues+"\n");
584 return sb.toString();
585 }
586 }