    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    }