// This file is part of AceWiki. // Copyright 2008-2013, AceWiki developers. // // AceWiki is free software: you can redistribute it and/or modify it under the terms of the GNU // Lesser General Public License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // AceWiki is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without // even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License along with AceWiki. If // not, see http://www.gnu.org/licenses/. package ch.uzh.ifi.attempto.chartparser; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; /** * This class represents a set of features consisting of name/value pairs. * * @author Tobias Kuhn */ public class FeatureMap { /** * This counter is used for skolemization: */ private static int skNumber = 0; private Map<String, StringRef> features = new HashMap<String, StringRef>(); /** * Creates an empty feature map. */ public FeatureMap() { } /** * Sets a feature. If the feature is already present, the feature value is overridden and not * unified. * * @param featureName The name of the feature to set. * @param featureValue The feature value. */ public void setFeature(String featureName, StringRef featureValue) { features.put(featureName, featureValue); } /** * Returns a feature value. * * @param featureName The name of the feature. * @return The value of the feature. */ public StringRef getFeature(String featureName) { StringRef featureValue = features.get(featureName); if (featureValue == null) { featureValue = new StringRef(); features.put(featureName, featureValue); } return featureValue; } /** * Unifies this feature map with another feature map. Two feature maps can unify if and only if * all values of common feature name unify. If the unification fails, a * UnificationFailedException is thrown. In this case, the two feature maps remain partly unified, * i.e. no backtracking is done. Thus, this operation should be perfomed only if it is certain that * the unification succeeds, or if the operation is performed on copies of objects that are not * used anymore afterwards. * * @param featureMap The feature map to be unified with this feature map. * @throws UnificationFailedException If unification fails. */ public void unify(FeatureMap featureMap) throws UnificationFailedException { for (String f : features.keySet()) { getFeature(f).unify(featureMap.getFeature(f)); } for (String f : featureMap.features.keySet()) { getFeature(f).unify(featureMap.getFeature(f)); } } /** * Tries to unify this feature map with another feature map. If unification is not possible, an exception * is thrown. In the case unification would be possible, the unification is not performed completely. * In any case the two feature maps remain in an unconsistent state afterwards. Thus, this operation should * be performed only on copies of objects that are not used anymore afterwards. * * @param featureMap The feature map to be unified with this feature map. * @throws UnificationFailedException If unification fails. */ public void tryToUnify(FeatureMap featureMap) throws UnificationFailedException { if (featureMap == null) { throw new UnificationFailedException(); } for (String f : features.keySet()) { features.get(f).unify(featureMap.getFeature(f)); } } /** * This method detects whether this feature map can unify with the given feature map. Neither of the two * feature maps are changed. * * @param featureMap The feature map for the unification check. * @return true if the two feature map can unify. */ public boolean canUnify(FeatureMap featureMap) { if (!isSimilar(featureMap)) return false; FeatureMap thisC = deepCopy(); FeatureMap otherC = featureMap.deepCopy(); try { thisC.tryToUnify(otherC); } catch (UnificationFailedException ex) { return false; } return true; } /** * Skolemizes the feature values of this feature map. */ public void skolemize() { for (String feature : features.keySet()) { StringRef s = features.get(feature); if (s.getString() == null) { try { s.unify(new StringRef("$SK" + skNumber++)); } catch (UnificationFailedException ex) {} } } } /** * This methods checks whether two feature maps are similar. Two feature maps are similar * if and only if they do not share a feature with the same name but with values that do * not unify locally (i.e. without considering the unifications entailed by other features). * * @param fm The category for which similarity with this category should be checked. * @return true if the two categories are similar. */ public boolean isSimilar(FeatureMap fm) { if (fm == null) return false; for (String v : features.keySet()) { String s1 = features.get(v).getString(); String s2 = null; StringRef sr2 = fm.features.get(v); if (sr2 != null) s2 = sr2.getString(); if (s1 != null && s2 != null && !s1.equals(s2)) return false; } return true; } /** * Creates a deep copy of this feature map. * * @return A deep copy. */ public FeatureMap deepCopy() { return deepCopy(new HashMap<Integer, StringObject>()); } /** * Creates a deep copy of this feature map using the given string objects. This method is * usually called form another deepCopy-method. * * @param stringObjs The string objects to be used. * @return A deep copy. */ FeatureMap deepCopy(HashMap<Integer, StringObject> stringObjs) { FeatureMap fm = new FeatureMap(); for (String feature : features.keySet()) { StringRef s = features.get(feature); StringObject se = stringObjs.get(s.getID()); if (se != null) { fm.setFeature(feature, se.newStringRef()); } else { StringRef sr = new StringRef(s.getString()); fm.setFeature(feature, sr); stringObjs.put(s.getID(), sr.getStringObject()); } } return fm; } /** * Returns the used feature names. * * @return The used feature names. */ public Set<String> getFeatureNames() { return features.keySet(); } /** * Returns the used feature values. * * @return The used feature values. */ public Collection<StringRef> getFeatureValues() { return features.values(); } String getIdentifier(List<Integer> mvars, String[] usedFeatureNames) { String s = "("; int i = 0; for (String n : usedFeatureNames) { i++; if (!getFeatureNames().contains(n)) continue; StringRef v = getFeature(n); if (v.getString() == null) { if (mvars.contains(v.getID())) { s += i + ":" + mvars.indexOf(v.getID()) + ","; } } else { s += i + ":" + v.getString() + ","; } } return s + ")"; } public String toString() { String s = ""; Set<String> featureKeys = features.keySet(); if (featureKeys.size() > 0) s += "("; for (String feature : new TreeSet<String>(features.keySet())) { // traverse the feature names in alphabetical order StringRef sr = features.get(feature); s += feature + ":"; if (sr.getString() == null) { s += sr.getID(); } else { s += sr.getString(); } s += ","; } if (featureKeys.size() > 0) { s = s.substring(0, s.length()-1) + ")"; } return s; } }