simplenlg-master/src/main/java/simplenlg/phrasespec/VPPhraseSpec.java
/*
 * The contents of this file are subject to the Mozilla Public License
 * Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * https://www.mozilla.org/en-US/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 Westwater, Roman Kutlak, Margaret Mitchell, and Saad Mahamood.
 */
package simplenlg.phrasespec;

import java.util.List;

import simplenlg.features.*;
import simplenlg.framework.*;

/**
 * <p>
 * This class defines a verb phrase.  It is essentially
 * a wrapper around the <code>PhraseElement</code> class, with methods
 * for setting common constituents such as Objects.
 * For example, the <code>setVerb</code> method in this class sets
 * the head of the element to be the specified verb
 * <p>
 * From an API perspective, this class is a simplified version of the SPhraseSpec
 * class in simplenlg V3.  It provides an alternative way for creating syntactic
 * structures, compared to directly manipulating a V4 <code>PhraseElement</code>.
 * <p>
 * Methods are provided for setting and getting the following constituents:
 * <UL>
 * <LI>PreModifier		(eg, "reluctantly")
 * <LI>Verb				(eg, "gave")
 * <LI>IndirectObject	(eg, "Mary")
 * <LI>Object	        (eg, "an apple")
 * <LI>PostModifier     (eg, "before school")
 * </UL>
 * <p>
 * NOTE: If there is a complex verb group, a preModifer set at the VP level appears before
 * the verb, while a preModifier set at the clause level appears before the verb group.  Eg
 * "Mary unfortunately will eat the apple"  ("unfortunately" is clause preModifier)
 * "Mary will happily eat the apple"  ("happily" is VP preModifier)
 * <p>
 * NOTE: The setModifier method will attempt to automatically determine whether
 * a modifier should be expressed as a PreModifier or PostModifier
 * <p>
 * Features (such as negated) must be accessed via the <code>setFeature</code> and
 * <code>getFeature</code> methods (inherited from <code>NLGElement</code>).
 * Features which are often set on VPPhraseSpec include
 * <UL>
 * <LI>Modal    (eg, "John eats an apple" vs "John can eat an apple")
 * <LI>Negated  (eg, "John eats an apple" vs "John does not eat an apple")
 * <LI>Passive  (eg, "John eats an apple" vs "An apple is eaten by John")
 * <LI>Perfect  (eg, "John ate an apple" vs "John has eaten an apple")
 * <LI>Progressive  (eg, "John eats an apple" vs "John is eating an apple")
 * <LI>Tense    (eg, "John ate" vs "John eats" vs "John will eat")
 * </UL>
 * Note that most VP features can be set on an SPhraseSpec, they will automatically
 * be propogated to the VP
 *
 * <code>VPPhraseSpec</code> are produced by the <code>createVerbPhrase</code>
 * method of a <code>PhraseFactory</code>
 * </p>
 *
 * @author E. Reiter, University of Aberdeen.
 * @version 4.1
 */
public class VPPhraseSpec extends PhraseElement {

	/**
	 * create an empty {@link VPPhraseSpec} clause.
	 *
	 * @param phraseFactory -- The {@link NLGFactory} to create this {@link VPPhraseSpec} object.
	 */
	public VPPhraseSpec(NLGFactory phraseFactory) {
		super(PhraseCategory.VERB_PHRASE);
		this.setFactory(phraseFactory);

		// set default feature values
		setFeature(Feature.PERFECT, false);
		setFeature(Feature.PROGRESSIVE, false);
		setFeature(Feature.PASSIVE, false);
		setFeature(Feature.NEGATED, false);
		setFeature(Feature.TENSE, Tense.PRESENT);
		setFeature(Feature.PERSON, Person.THIRD);
		setPlural(false);
		setFeature(Feature.FORM, Form.NORMAL);
		setFeature(InternalFeature.REALISE_AUXILIARY, true);
	}

	/**
	 * sets the verb (head) of a verb phrase.
	 * Extract particle from verb if necessary
	 *
	 * @param verb -- The head verb {@link Object} to use for this {@link VPPhraseSpec}.
	 */
	public void setVerb(Object verb) {
		NLGElement verbElement;

		if(verb instanceof String) { // if String given, check for particle
			int space = ((String) verb).indexOf(' ');

			if(space == -1) { // no space, so no particle
				verbElement = getFactory().createWord(verb, LexicalCategory.VERB);

			} else { // space, so break up into verb and particle
				verbElement = getFactory().createWord(((String) verb).substring(0, space), LexicalCategory.VERB);
				setFeature(Feature.PARTICLE, ((String) verb).substring(space + 1, ((String) verb).length()));
			}
		} else { // Object is not a String
			verbElement = getFactory().createNLGElement(verb, LexicalCategory.VERB);
		}
		setHead(verbElement);
	}

	/**
	 * Returns the head verb of this {@link VPPhraseSpec}.
	 *
	 * @return verb (head) of verb phrase
	 */
	public NLGElement getVerb() {
		return getHead();
	}

	/**
	 * Sets the direct object of a clause  (assumes this is the only direct object)
	 *
	 * @param object -- The object to use in this {@link VPPhraseSpec}.
	 */
	public void setObject(Object object) {
		NLGElement objectPhrase;
		if(object instanceof PhraseElement || object instanceof CoordinatedPhraseElement)
			objectPhrase = (NLGElement) object;
		else
			objectPhrase = getFactory().createNounPhrase(object);

		objectPhrase.setFeature(InternalFeature.DISCOURSE_FUNCTION, DiscourseFunction.OBJECT);
		setComplement(objectPhrase);
	}

	/**
	 * Returns the direct object of a clause (assumes there is only one)
	 *
	 * @return subject of clause (assume only one)
	 */
	public NLGElement getObject() {
		List<NLGElement> complements = getFeatureAsElementList(InternalFeature.COMPLEMENTS);
		for(NLGElement complement : complements)
			if(complement.getFeature(InternalFeature.DISCOURSE_FUNCTION) == DiscourseFunction.OBJECT)
				return complement;
		return null;
	}

	/**
	 * Set the indirect object of a clause (assumes this is the only direct indirect object)
	 *
	 * @param indirectObject -- The indirect object to use in this {@link VPPhraseSpec}.
	 */
	public void setIndirectObject(Object indirectObject) {
		NLGElement indirectObjectPhrase;
		if(indirectObject instanceof PhraseElement || indirectObject instanceof CoordinatedPhraseElement)
			indirectObjectPhrase = (NLGElement) indirectObject;
		else
			indirectObjectPhrase = getFactory().createNounPhrase(indirectObject);

		indirectObjectPhrase.setFeature(InternalFeature.DISCOURSE_FUNCTION, DiscourseFunction.INDIRECT_OBJECT);
		setComplement(indirectObjectPhrase);
	}

	/**
	 * Returns the indirect object of a clause (assumes there is only one)
	 *
	 * @return subject of clause (assume only one)
	 */
	public NLGElement getIndirectObject() {
		List<NLGElement> complements = getFeatureAsElementList(InternalFeature.COMPLEMENTS);
		for(NLGElement complement : complements)
			if(complement.getFeature(InternalFeature.DISCOURSE_FUNCTION) == DiscourseFunction.INDIRECT_OBJECT)
				return complement;
		return null;
	}

	// note that addFrontModifier, addPostModifier, addPreModifier are inherited from PhraseElement
	// likewise getFrontModifiers, getPostModifiers, getPreModifiers

	/**
	 * Add a modifier to a verb phrase
	 * Use heuristics to decide where it goes
	 */
	@Override
	public void addModifier(Object modifier) {
		// adverb is preModifier
		// string which is one lexicographic word is looked up in lexicon,
		// if it is an adverb than it becomes a preModifier
		// Everything else is postModifier

		if(modifier == null)
			return;

		// get modifier as NLGElement if possible
		NLGElement modifierElement = null;
		if(modifier instanceof NLGElement)
			modifierElement = (NLGElement) modifier;
		else if(modifier instanceof String) {
			String modifierString = (String) modifier;
			if(modifierString.length() > 0 && !modifierString.contains(" "))
				modifierElement = getFactory().createWord(modifier, LexicalCategory.ANY);
		}

		// if no modifier element, must be a complex string
		if(modifierElement == null) {
			addPostModifier((String) modifier);
			return;
		}

		// extract WordElement if modifier is a single word
		WordElement modifierWord = null;
		if(modifierElement != null && modifierElement instanceof WordElement)
			modifierWord = (WordElement) modifierElement;
		else if(modifierElement != null && modifierElement instanceof InflectedWordElement)
			modifierWord = ((InflectedWordElement) modifierElement).getBaseWord();

		if(modifierWord != null && modifierWord.getCategory() == LexicalCategory.ADVERB) {
			addPreModifier(modifierWord);
			return;
		}

		// default case
		addPostModifier(modifierElement);
	}

}
