    001    // This file is part of the Attempto Java Packages.
    002    // Copyright 2008, Attempto Group, University of Zurich (see http://attempto.ifi.uzh.ch).
    003    //
    004    // The Attempto Java Packages is free software: you can redistribute it and/or modify it under the
    005    // terms of the GNU Lesser General Public License as published by the Free Software Foundation,
    006    // either version 3 of the License, or (at your option) any later version.
    007    //
    008    // The Attempto Java Packages is distributed in the hope that it will be useful, but WITHOUT ANY
    009    // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
    010    // PURPOSE. See the GNU Lesser General Public License for more details.
    011    //
    012    // You should have received a copy of the GNU Lesser General Public License along with the Attempto
    013    // Java Packages. If not, see http://www.gnu.org/licenses/.
    014
    015    package ch.uzh.ifi.attempto.chartparser;
    016
    017    import java.util.HashMap;
    018    import java.util.Set;
    019
    020    /**
    021     * This class represents a grammatical category.
    022     *
    023     * @author Tobias Kuhn
    024     */
    025    public abstract class Category {
    026
    027            private static int skNumber = 0;
    028
    029            private final String name;
    030            private HashMap<String, StringRef> features = new HashMap<String, StringRef>();
    031
    032            /**
    033             * Creates a new category.
    034             *
    035             * @param name The name of the category.
    036             */
    037            public Category(String name) {
    038                    this.name = name;
    039            }
    040
    041            /**
    042             * Returns the name of the category.
    043             *
    044             * @return The name of the category.
    045             */
    046            public String getName() {
    047                    return name;
    048            }
    049
    050            /**
    051             * Sets a feature.
    052             *
    053             * @param featureName The feature name
    054             * @param featureValue
    055             */
    056            public void setFeature(String featureName, StringRef featureValue) {
    057                    features.put(featureName, featureValue);
    058            }
    059
    060            /**
    061             * Returns a feature value.
    062             *
    063             * @param featureName The name of the feature.
    064             * @return The value of the feature.
    065             */
    066            public StringRef getFeature(String featureName) {
    067                    StringRef featureValue = features.get(featureName);
    068                    if (featureValue == null) {
    069                            featureValue = new StringRef();
    070                            features.put(featureName, featureValue);
    071                    }
    072                    return featureValue;
    073            }
    074
    075            /**
    076             * Unifies this category with another category. Two categories can unify if and only if they have
    077             * the same names and they have no features with conflicting values. If the unification fails, a
    078             * UnificationFailedException is thrown. In this case, the two categories remain partly unified,
    079             * i.e. no backtracking is done. Thus, this operation should be perfomed only if it is certain that
    080             * the unification succeeds, or if the operation is performed on copies of objects that are not
    081             * used anymore afterwards.
    082             *
    083             * @param category The category to be unified with this category.
    084             * @throws UnificationFailedException If unification fails.
    085             */
    086            public void unify(Category category) throws UnificationFailedException {
    087                    if (!name.equals(category.name)) {
    088                            throw new UnificationFailedException();
    089                    }
    090                    for (String f : features.keySet()) {
    091                            getFeature(f).unify(category.getFeature(f));
    092                    }
    093                    for (String f : category.features.keySet()) {
    094                            getFeature(f).unify(category.getFeature(f));
    095                    }
    096            }
    097
    098            /**
    099             * Tries to unify this category with another category. If unification is not possible, an exception
    100             * is thrown. In the case unification would be possible, the unification is not performed completely.
    101             * In any case the two categories remain in an unconsistent state afterwards. Thus, this operation should
    102             * be performed only on copies of objects that are not used anymore afterwards.
    103             *
    104             * @param category The category to be unified with this category.
    105             * @throws UnificationFailedException If unification fails.
    106             */
    107            public void tryToUnify(Category category) throws UnificationFailedException {
    108                    if (!name.equals(category.name)) {
    109                            throw new UnificationFailedException();
    110                    }
    111                    for (String f : features.keySet()) {
    112                            features.get(f).unify(category.getFeature(f));
    113                    }
    114            }
    115
    116            /**
    117             * Skolemizes the feature values of this category.
    118             */
    119            public void skolemize() {
    120                    for (String feature : features.keySet()) {
    121                            StringRef s = features.get(feature);
    122                            if (s.getString() == null) {
    123                                    try {
    124                                            s.unify(new StringRef("$SK" + skNumber++));
    125                                    } catch (UnificationFailedException ex) {}
    126                            }
    127                    }
    128            }
    129
    130            /**
    131             * Creates a deep copy of this category.
    132             *
    133             * @return A deep copy.
    134             */
    135            public Category deepCopy() {
    136                    return deepCopy(new HashMap<Integer, StringEntity>());
    137            }
    138
    139            /**
    140             * Creates a deep copy of this category using the given string entities. This method is
    141             * usually called form another deepCopy-method.
    142             *
    143             * @param entities The string entities to be used.
    144             * @return A deep copy.
    145             */
    146            public Category deepCopy(HashMap<Integer, StringEntity> entities) {
    147                    Category c;
    148                    if (this instanceof Terminal) {
    149                            c = new Terminal(name);
    150                    } else {
    151                            c = new Nonterminal(name);
    152                    }
    153                    for (String feature : features.keySet()) {
    154                            StringRef s = features.get(feature);
    155                            StringEntity se = entities.get(s.getID());
    156                            if (se != null) {
    157                                    c.setFeature(feature, se.newStringRef());
    158                            } else {
    159                                    StringRef sr = new StringRef(s.getString());
    160                                    c.setFeature(feature, sr);
    161                                    entities.put(s.getID(), sr.getStringEntity());
    162                            }
    163                    }
    164                    return c;
    165            }
    166
    167            public boolean equals(Object obj) {
    168                    if (!(obj instanceof Category)) return false;
    169                    return toString().equals(obj.toString());
    170            }
    171
    172            public String toString() {
    173                    String s = name;
    174                    Set<String> featureKeys = features.keySet();
    175                    if (featureKeys.size() > 0) s += "(";
    176                    for (String feature : features.keySet()) {
    177                            StringRef sr = features.get(feature);
    178                            s += feature + ":";
    179                            if (sr.getString() == null) {
    180                                    s += sr.getID();
    181                            } else {
    182                                    s += sr.getString();
    183                            }
    184                            s += ",";
    185                    }
    186                    if (featureKeys.size() > 0) {
    187                            s = s.substring(0, s.length()-1) + ")";
    188                    }
    189                    return s;
    190            }
    191
    192    }