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 }