001 // This file is part of the Attempto Java Packages. 002 // Copyright 2008-2009, 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 019 /** 020 * This class represents a grammatical category. 021 * 022 * @author Tobias Kuhn 023 */ 024 public abstract class Category { 025 026 private final String name; 027 private FeatureMap featureMap = new FeatureMap(); 028 029 /** 030 * Creates a new category. 031 * 032 * @param name The name of the category. 033 */ 034 public Category(String name) { 035 this.name = name; 036 } 037 038 /** 039 * Returns true if this category is a reference. 040 * 041 * @return true if this is a reference. 042 */ 043 public boolean isReference() { 044 if (name.equals(">>>")) return true; 045 if (name.equals("<<<")) return true; 046 if (name.equals("</<")) return true; 047 return false; 048 } 049 050 /** 051 * Returns the name of the category. 052 * 053 * @return The name of the category. 054 */ 055 public String getName() { 056 return name; 057 } 058 059 /** 060 * Returns the map of features of this category. 061 * 062 * @return The feature map. 063 */ 064 public FeatureMap getFeatureMap() { 065 return featureMap; 066 } 067 068 /** 069 * Sets a feature. 070 * 071 * @param featureName The feature name 072 * @param featureValue The string reference that points to the value of the feature. 073 */ 074 public void setFeature(String featureName, StringRef featureValue) { 075 featureMap.setFeature(featureName, featureValue); 076 } 077 078 /** 079 * Sets a feature. 080 * 081 * @param featureName The feature name 082 * @param featureValue The value of the feature. 083 */ 084 public void setFeature(String featureName, String featureValue) { 085 featureMap.setFeature(featureName, new StringRef(featureValue)); 086 } 087 088 /** 089 * Returns a feature value. 090 * 091 * @param featureName The name of the feature. 092 * @return The value of the feature. 093 */ 094 public StringRef getFeature(String featureName) { 095 return featureMap.getFeature(featureName); 096 } 097 098 /** 099 * Unifies this category with another category. Two categories can unify if and only if they have 100 * the same names and they have no features with conflicting values. If the unification fails, a 101 * UnificationFailedException is thrown. In this case, the two categories remain partly unified, 102 * i.e. no backtracking is done. Thus, this operation should be perfomed only if it is certain that 103 * the unification succeeds, or if the operation is performed on copies of objects that are not 104 * used anymore afterwards. 105 * 106 * @param category The category to be unified with this category. 107 * @throws UnificationFailedException If unification fails. 108 */ 109 public void unify(Category category) throws UnificationFailedException { 110 if (!name.equals(category.name)) { 111 throw new UnificationFailedException(); 112 } 113 featureMap.unify(category.featureMap); 114 } 115 116 /** 117 * Tries to unify this category with another category. If unification is not possible, an exception 118 * is thrown. In the case unification would be possible, the unification is not performed completely. 119 * In any case the two categories remain in an unconsistent state afterwards. Thus, this operation should 120 * be performed only on copies of objects that are not used anymore afterwards. 121 * 122 * @param category The category to be unified with this category. 123 * @throws UnificationFailedException If unification fails. 124 */ 125 public void tryToUnify(Category category) throws UnificationFailedException { 126 if (!name.equals(category.name)) { 127 throw new UnificationFailedException(); 128 } 129 featureMap.tryToUnify(category.featureMap); 130 } 131 132 /** 133 * This method detects whether this category can unify with the given category. Neither of the two 134 * categories are changed. 135 * 136 * @param category The category for the unification check. 137 * @return true if the two categories can unify. 138 */ 139 public boolean canUnify(Category category) { 140 if (!isSimilar(category)) return false; 141 Category thisC = deepCopy(); 142 Category otherC = category.deepCopy(); 143 try { 144 thisC.tryToUnify(otherC); 145 } catch (UnificationFailedException ex) { 146 return false; 147 } 148 return true; 149 } 150 151 /** 152 * Skolemizes the feature values of this category. 153 */ 154 public void skolemize() { 155 featureMap.skolemize(); 156 } 157 158 /** 159 * Creates a deep copy of this category. 160 * 161 * @return A deep copy. 162 */ 163 public Category deepCopy() { 164 return deepCopy(new HashMap<Integer, StringEntity>()); 165 } 166 167 /** 168 * Creates a deep copy of this category using the given string entities. This method is 169 * usually called form another deepCopy-method. 170 * 171 * @param entities The string entities to be used. 172 * @return A deep copy. 173 */ 174 Category deepCopy(HashMap<Integer, StringEntity> entities) { 175 Category c; 176 if (this instanceof Terminal) { 177 c = new Terminal(name); 178 } else { 179 c = new Nonterminal(name); 180 } 181 c.featureMap = featureMap.deepCopy(entities); 182 return c; 183 } 184 185 /** 186 * This methods checks whether two categories are similar. Two categories are similar if and 187 * only if the categories have the same name and no feature with the same name is present in 188 * both categories with values that do not unify locally. 189 * 190 * @param c The category for which similarity with this category should be checked. 191 * @return true if the two categories are similar. 192 */ 193 public boolean isSimilar(Category c) { 194 if (!name.equals(c.name)) return false; 195 return featureMap.isSimilar(c.featureMap); 196 } 197 198 /** 199 * This method returns true if this category subsumes (i.e. is more general than) the given category, 200 * or false otherwise. 201 * 202 * @param category The category for which it is checked whether this category subsumes it. 203 * @return true if this category subsumes the given category. 204 */ 205 public boolean subsumes(Category category) { 206 if (!isSimilar(category)) return false; 207 208 // Both categories are copied to keep the existing categories untouched: 209 Category category1C = deepCopy(); 210 Category category2C = category.deepCopy(); 211 212 // Category 1 subsumes category 2 iff 1 unifies with 2 after the skolemization of 2. 213 category2C.skolemize(); 214 try { 215 category1C.tryToUnify(category2C); 216 return true; 217 } catch (UnificationFailedException ex) { 218 return false; 219 } 220 } 221 222 public boolean equals(Object obj) { 223 if (!(obj instanceof Category)) return false; 224 return toString().equals(obj.toString()); 225 } 226 227 public String toString() { 228 return name + featureMap.toString(); 229 } 230 231 }