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 }