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 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 subsumes(Category category) { 168 if (!name.equals(category.name)) return false; 169 170 Category category1C = deepCopy(); 171 Category category2C = category.deepCopy(); 172 category2C.skolemize(); 173 try { 174 category1C.tryToUnify(category2C); 175 return true; 176 } catch (UnificationFailedException ex) { 177 return false; 178 } 179 } 180 181 public boolean equals(Object obj) { 182 if (!(obj instanceof Category)) return false; 183 return toString().equals(obj.toString()); 184 } 185 186 public String toString() { 187 String s = name; 188 Set<String> featureKeys = features.keySet(); 189 if (featureKeys.size() > 0) s += "("; 190 for (String feature : features.keySet()) { 191 StringRef sr = features.get(feature); 192 s += feature + ":"; 193 if (sr.getString() == null) { 194 s += sr.getID(); 195 } else { 196 s += sr.getString(); 197 } 198 s += ","; 199 } 200 if (featureKeys.size() > 0) { 201 s = s.substring(0, s.length()-1) + ")"; 202 } 203 return s; 204 } 205 206 }