    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    }