All Downloads are FREE. Search and download functionalities are using the official Maven repository.

simplenlg.framework.NLGElement Maven / Gradle / Ivy

/*
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is "Simplenlg".
 *
 * The Initial Developer of the Original Code is Ehud Reiter, Albert Gatt and Dave Westwater.
 * Portions created by Ehud Reiter, Albert Gatt and Dave Westwater are Copyright (C) 2010-11 The University of Aberdeen. All Rights Reserved.
 *
 * Contributor(s): Ehud Reiter, Albert Gatt, Dave Wewstwater, Roman Kutlak, Margaret Mitchell.
 */
package simplenlg.framework;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Map;

import simplenlg.features.Feature;
import simplenlg.features.NumberAgreement;
import simplenlg.features.Tense;

/**
 * 

* NLGElement is the base class that all elements extend from. This * is abstract and cannot therefore be instantiated itself. The additional * element classes should be used to correctly identify the type of element * required. *

* *

* Realisation in SimpleNLG revolves around a tree structure. Each node in the * tree is represented by a NLGElement, which in turn may have * child nodes. The job of the processors is to replace various types of * elements with other elements. The eventual goal, once all the processors have * been run, is to produce a single string element representing the final * realisation. *

* *

* The features are stored in a Map of String (the * feature name) and Object (the value of the feature). *

* * * @author D. Westwater, University of Aberdeen. * @version 4.0 */ public abstract class NLGElement { /** The category of this element. */ private ElementCategory category; /** The features of this element. */ protected HashMap features = new HashMap(); /** The parent of this element. */ private NLGElement parent; /** The realisation of this element. */ private String realisation; /** The NLGFactory which created this element */ private NLGFactory factory; /** * Sets the category of this element. * * @param newCategory * the new ElementCategory for this element. */ public void setCategory(ElementCategory newCategory) { this.category = newCategory; } /** * Retrieves the category for this element. * * @return the category as a ElementCategory. */ public ElementCategory getCategory() { return this.category; } /** * Adds a feature to the feature map. If the feature already exists then it * is given the new value. If the value provided is null the * feature is removed from the map. * * @param featureName * the name of the feature. * @param featureValue * the new value of the feature or null if the * feature is to be removed. */ public void setFeature(String featureName, Object featureValue) { if (featureName != null) { if (featureValue == null) { this.features.remove(featureName); } else { this.features.put(featureName, featureValue); } } } /** * A convenience method for setting boolean features. * * @param featureName * the name of the feature. * @param featureValue * the boolean value of the feature. */ public void setFeature(String featureName, boolean featureValue) { if (featureName != null) { this.features.put(featureName, new Boolean(featureValue)); } } /** * A convenience method for setting integer features. * * @param featureName * the name of the feature. * @param featureValue * the int value of the feature. */ public void setFeature(String featureName, int featureValue) { if (featureName != null) { this.features.put(featureName, new Integer(featureValue)); } } /** * A convenience method for setting long integer features. * * @param featureName * the name of the feature. * @param featureValue * the long value of the feature. */ public void setFeature(String featureName, long featureValue) { if (featureName != null) { this.features.put(featureName, new Long(featureValue)); } } /** * A convenience method for setting floating point number features. * * @param featureName * the name of the feature. * @param featureValue * the float value of the feature. */ public void setFeature(String featureName, float featureValue) { if (featureName != null) { this.features.put(featureName, new Float(featureValue)); } } /** * A convenience method for setting double precision floating point number * features. * * @param featureName * the name of the feature. * @param featureValue * the double value of the feature. */ public void setFeature(String featureName, double featureValue) { if (featureName != null) { this.features.put(featureName, new Double(featureValue)); } } /** * Retrieves the value of the feature. * * @param featureName * the name of the feature. * @return the Object value of the feature. */ public Object getFeature(String featureName) { return featureName != null ? this.features.get(featureName) : null; } /** * Retrieves the value of the feature as a string. If the feature doesn't * exist then null is returned. * * @param featureName * the name of the feature. * @return the String representation of the value. This is * taken by calling the object's toString() method. */ public String getFeatureAsString(String featureName) { Object value = getFeature(featureName); String stringValue = null; if (value != null) { stringValue = value.toString(); } return stringValue; } /** *

* Retrieves the value of the feature as a list of elements. If the feature * is a single NLGElement then it is wrapped in a list. If the * feature is a Collection then each object in the collection * is checked and only NLGElements are returned in the list. *

*

* If the feature does not exist then an empty list is returned. *

* * @param featureName * the name of the feature. * @return the List of NLGElements */ public List getFeatureAsElementList(String featureName) { List list = new ArrayList(); Object value = getFeature(featureName); if (value instanceof NLGElement) { list.add((NLGElement) value); } else if (value instanceof Collection) { Iterator iterator = ((Collection) value).iterator(); Object nextObject = null; while (iterator.hasNext()) { nextObject = iterator.next(); if (nextObject instanceof NLGElement) { list.add((NLGElement) nextObject); } } } return list; } /** *

* Retrieves the value of the feature as a list of java objects. If the feature * is a single element, the list contains only this element. * If the feature is a Collection each object in the collection is * returned in the list. *

*

* If the feature does not exist then an empty list is returned. *

* * @param featureName * the name of the feature. * @return the List of Objects */ public List getFeatureAsList(String featureName) { List values = new ArrayList(); Object value = getFeature(featureName); if (value != null) { if (value instanceof Collection) { Iterator iterator = ((Collection) value).iterator(); Object nextObject = null; while (iterator.hasNext()) { nextObject = iterator.next(); values.add(nextObject); } } else { values.add(value); } } return values; } /** *

* Retrieves the value of the feature as a list of strings. If the feature * is a single element, then its toString() value is wrapped in * a list. If the feature is a Collection then the * toString() value of each object in the collection is * returned in the list. *

*

* If the feature does not exist then an empty list is returned. *

* * @param featureName * the name of the feature. * @return the List of Strings */ public List getFeatureAsStringList(String featureName) { List values = new ArrayList(); Object value = getFeature(featureName); if (value != null) { if (value instanceof Collection) { Iterator iterator = ((Collection) value).iterator(); Object nextObject = null; while (iterator.hasNext()) { nextObject = iterator.next(); values.add(nextObject.toString()); } } else { values.add(value.toString()); } } return values; } /** * Retrieves the value of the feature as an Integer. If the * feature does not exist or cannot be converted to an integer then * null is returned. * * @param featureName * the name of the feature. * @return the Integer representation of the value. Numbers are * converted to integers while Strings are parsed for integer * values. Any other type will return null. */ public Integer getFeatureAsInteger(String featureName) { Object value = getFeature(featureName); Integer intValue = null; if (value instanceof Integer) { intValue = (Integer) value; } else if (value instanceof Number) { intValue = new Integer(((Number) value).intValue()); } else if (value instanceof String) { try { intValue = new Integer((String) value); } catch (NumberFormatException exception) { intValue = null; } } return intValue; } /** * Retrieves the value of the feature as a Long. If the feature * does not exist or cannot be converted to a long then null is * returned. * * @param featureName * the name of the feature. * @return the Long representation of the value. Numbers are * converted to longs while Strings are parsed for long values. Any * other type will return null. */ public Long getFeatureAsLong(String featureName) { Object value = getFeature(featureName); Long longValue = null; if (value instanceof Long) { longValue = (Long) value; } else if (value instanceof Number) { longValue = new Long(((Number) value).longValue()); } else if (value instanceof String) { try { longValue = new Long((String) value); } catch (NumberFormatException exception) { longValue = null; } } return longValue; } /** * Retrieves the value of the feature as a Float. If the * feature does not exist or cannot be converted to a float then * null is returned. * * @param featureName * the name of the feature. * @return the Float representation of the value. Numbers are * converted to floats while Strings are parsed for float values. * Any other type will return null. */ public Float getFeatureAsFloat(String featureName) { Object value = getFeature(featureName); Float floatValue = null; if (value instanceof Float) { floatValue = (Float) value; } else if (value instanceof Number) { floatValue = new Float(((Number) value).floatValue()); } else if (value instanceof String) { try { floatValue = new Float((String) value); } catch (NumberFormatException exception) { floatValue = null; } } return floatValue; } /** * Retrieves the value of the feature as a Double. If the * feature does not exist or cannot be converted to a double then * null is returned. * * @param featureName * the name of the feature. * @return the Double representation of the value. Numbers are * converted to doubles while Strings are parsed for double values. * Any other type will return null. */ public Double getFeatureAsDouble(String featureName) { Object value = getFeature(featureName); Double doubleValue = null; if (value instanceof Double) { doubleValue = (Double) value; } else if (value instanceof Number) { doubleValue = new Double(((Number) value).doubleValue()); } else if (value instanceof String) { try { doubleValue = new Double((String) value); } catch (NumberFormatException exception) { doubleValue = null; } } return doubleValue; } /** * Retrieves the value of the feature as a Boolean. If the * feature does not exist or is not a boolean then * Boolean.FALSE is returned. * * @param featureName * the name of the feature. * @return the Boolean representation of the value. Any * non-Boolean type will return Boolean.FALSE. */ public Boolean getFeatureAsBoolean(String featureName) { Object value = getFeature(featureName); Boolean boolValue = Boolean.FALSE; if (value instanceof Boolean) { boolValue = (Boolean) value; } return boolValue; } /** * Retrieves the value of the feature as a NLGElement. If the * value is a string then it is wrapped in a StringElement. If * the feature does not exist or is of any other type then null * is returned. * * @param featureName * the name of the feature. * @return the NLGElement. */ public NLGElement getFeatureAsElement(String featureName) { Object value = getFeature(featureName); NLGElement elementValue = null; if (value instanceof NLGElement) { elementValue = (NLGElement) value; } else if (value instanceof String) { elementValue = new StringElement((String) value); } return elementValue; } /** * Retrieves the map containing all the features for this element. * * @return a Map of String, Object. */ public Map getAllFeatures() { return this.features; } /** * Checks the feature map to see if the named feature is present in the map. * * @param featureName * the name of the feature to look for. * @return true if the feature exists, false * otherwise. */ public boolean hasFeature(String featureName) { return featureName != null ? this.features.containsKey(featureName) : false; } /** * Deletes the named feature from the map. * * @param featureName * the name of the feature to be removed. */ public void removeFeature(String featureName) { this.features.remove(featureName); } /** * Deletes all the features in the map. */ public void clearAllFeatures() { this.features.clear(); } /** * Sets the parent element of this element. * * @param newParent * the NLGElement that is the parent of this * element. */ public void setParent(NLGElement newParent) { this.parent = newParent; } /** * Retrieves the parent of this element. * * @return the NLGElement that is the parent of this element. */ public NLGElement getParent() { return this.parent; } /** * Sets the realisation of this element. * * @param realised * the String representing the final realisation for * this element. */ public void setRealisation(String realised) { this.realisation = realised; } /** * Retrieves the final realisation of this element. * * @return the String representing the final realisation for * this element. */ public String getRealisation() { int start = 0; int end = 0; if (null != this.realisation) { end = this.realisation.length(); while (start < this.realisation.length() && ' ' == this.realisation.charAt(start)) { start++; } if (start == this.realisation.length()) { this.realisation = null; } else { while (end > 0 && ' ' == this.realisation.charAt(end - 1)) { end--; } } } // AG: changed this to return the empty string if the realisation is // null // avoids spurious nulls appearing in output for empty phrases. return this.realisation == null ? "" : this.realisation.substring( start, end); } @Override public String toString() { StringBuffer buffer = new StringBuffer("{realisation=").append(this.realisation); //$NON-NLS-1$ if (this.category != null) { buffer.append(", category=").append(this.category.toString()); //$NON-NLS-1$ } if (this.features != null) { buffer.append(", features=").append(this.features.toString()); //$NON-NLS-1$ } buffer.append('}'); return buffer.toString(); } public boolean isA(ElementCategory checkCategory) { boolean isA = false; if (this.category != null) { isA = this.category.equalTo(checkCategory); } else if (checkCategory == null) { isA = true; } return isA; } /** * Retrieves the children for this element. This method needs to be * overridden for each specific type of element. Each type of element will * have its own way of determining the child elements. * * @return a List of NLGElements representing the * children of this element. */ public abstract List getChildren(); /** * Retrieves the set of features currently contained in the feature map. * * @return a Set of Strings representing the * feature names. The set is unordered. */ public Set getAllFeatureNames() { return this.features.keySet(); } public String printTree(String indent) { String thisIndent = indent == null ? " |-" : indent + " |-"; //$NON-NLS-1$ //$NON-NLS-2$ String childIndent = indent == null ? " |-" : indent + " |-"; //$NON-NLS-1$ //$NON-NLS-2$ StringBuffer print = new StringBuffer(); print.append("NLGElement: ").append(toString()).append('\n'); //$NON-NLS-1$ List children = getChildren(); if (children != null) { for (NLGElement eachChild : getChildren()) { print.append(thisIndent).append( eachChild.printTree(childIndent)); } } return print.toString(); } /** * Determines if this element has its realisation equal to the given string. * * @param elementRealisation * the string to check against. * @return true if the string matches the element's * realisation, false otherwise. */ public boolean equals(String elementRealisation) { boolean match = false; if (elementRealisation == null && this.realisation == null) { match = true; } else if (elementRealisation != null && this.realisation != null) { match = elementRealisation.equals(this.realisation); } return match; } /** * Sets the number agreement on this element. This method is added for * convenience and not all element types will make use of the number * agreement feature. The method is identical to calling {@code * setFeature(Feature.NUMBER, NumberAgreement.PLURAL)} for plurals or * {@code setFeature(Feature.NUMBER, NumberAgreement.SINGULAR)} for the * singular. * * @param isPlural * true if this element is to be treated as a * plural, false otherwise. */ public void setPlural(boolean isPlural) { if (isPlural) { setFeature(Feature.NUMBER, NumberAgreement.PLURAL); } else { setFeature(Feature.NUMBER, NumberAgreement.SINGULAR); } } /** * Determines if this element is to be treated as a plural. This is a * convenience method and not all element types make use of number * agreement. * * @return true if the Feature.NUMBER feature has * the value NumberAgreement.PLURAL, false * otherwise. */ public boolean isPlural() { return NumberAgreement.PLURAL.equals(getFeature(Feature.NUMBER)); } // Following should be deleted at some point, as it makes more sense to have // them in SPhraseSpec /** * Retrieves the tense for this element. The method is identical to calling * {@code getFeature(Feature.TENSE)} and casting the result as * Tense. * * * WARNING: You should use getFeature(Feature.TENSE) * getTense will be dropped from simplenlg at some point * * @return the Tense of this element. */ @Deprecated public Tense getTense() { Tense tense = Tense.PRESENT; Object tenseValue = getFeature(Feature.TENSE); if (tenseValue instanceof Tense) { tense = (Tense) tenseValue; } return tense; } /** * Sets the tense on this element. The method is identical to calling * {@code setFeature(Feature.TENSE, newTense)}. * * WARNING: You should use setTense(Feature.TENSE, tense) setTense will be * dropped from simplenlg at some point * * @param newTense * the new tense for this element. */ @Deprecated public void setTense(Tense newTense) { setFeature(Feature.TENSE, newTense); } /** * Sets the negation on this element. The method is identical to calling * {@code setFeature(Feature.NEGATED, isNegated)}. * * WARNING: You should use setFeature(Feature.NEGATED, isNegated) setNegated * will be dropped from simplenlg at some point * * @param isNegated * true if the element is to be negated, * false otherwise. */ @Deprecated public void setNegated(boolean isNegated) { setFeature(Feature.NEGATED, isNegated); } /** * Determines if this element is to be treated as a negation. This method * just examines the value of the NEGATED feature * * WARNING: You should use getFeature(Feature.NEGATED) getNegated will be * dropped from simplenlg at some point * * @return true if the Feature.NEGATED feature * exists and has the value Boolean.TRUE, * false is returned otherwise. */ @Deprecated public boolean isNegated() { return getFeatureAsBoolean(Feature.NEGATED).booleanValue(); } /** * @return the NLG factory */ public NLGFactory getFactory() { return factory; } /** * @param factory * the NLG factory to set */ public void setFactory(NLGFactory factory) { this.factory = factory; } /** * An NLG element is equal to some object if the object is an NLGElement, * they have the same category and the same features. */ @Override public boolean equals(Object o) { boolean eq = false; if (o instanceof NLGElement) { NLGElement element = (NLGElement) o; eq = this.category == element.category && this.features.equals(element.features); } return eq; } }