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 }