Ejemplo n.º 1
0
  public SPhraseSpec getNLForTriple2(Triple t) {
    SPhraseSpec p = nlgFactory.createClause();
    // process subject
    if (t.getSubject().isVariable()) {
      p.setSubject(t.getSubject().toString());
    } else {
      p.setSubject(getNPPhrase(t.getSubject().toString(), false));
    }

    // process predicate
    if (t.getPredicate().isVariable()) {
      p.setVerb("be related via " + t.getPredicate().toString() + " to");
    } else {
      p.setVerb(getVerbFrom(t.getPredicate()));
    }

    // process object
    if (t.getObject().isVariable()) {
      p.setObject(t.getObject().toString());
    } else if (t.getObject().isLiteral()) {
      p.setObject(t.getObject().getLiteralLexicalForm());
    } else {
      p.setObject(getNPPhrase(t.getObject().toString(), false));
    }

    p.setFeature(Feature.TENSE, Tense.PRESENT);
    return p;
  }
Ejemplo n.º 2
0
 private NLGElement getNLFromExpressions(List<Expr> expressions) {
   List<NLGElement> nlgs = new ArrayList<NLGElement>();
   NLGElement elt;
   for (Expr e : expressions) {
     elt = getNLFromSingleExpression(e);
     if (elt != null) {
       nlgs.add(elt);
     }
   }
   // now process
   if (nlgs.isEmpty()) {
     return null;
   }
   if (nlgs.size() == 1) {
     return nlgs.get(0);
   } else {
     CoordinatedPhraseElement cpe;
     cpe = nlgFactory.createCoordinatedPhrase(nlgs.get(0), nlgs.get(1));
     for (int i = 2; i < nlgs.size(); i++) {
       cpe.addComplement(nlgs.get(i));
     }
     cpe.setConjunction("and");
     return cpe;
   }
 }
Ejemplo n.º 3
0
  /**
   * Creates the appropriate pronoun if the subject of the noun phrase is pronominal.
   *
   * @param parent the parent <code>SyntaxProcessor</code> that will do the realisation of the
   *     complementiser.
   * @param phrase the <code>PhraseElement</code> representing this noun phrase.
   * @return the <code>NLGElement</code> representing the pronominal.
   */
  private static NLGElement createPronoun(SyntaxProcessor parent, PhraseElement phrase) {

    String pronoun = "it"; // $NON-NLS-1$
    NLGFactory phraseFactory = phrase.getFactory();
    Object personValue = phrase.getFeature(Feature.PERSON);

    if (Person.FIRST.equals(personValue)) {
      pronoun = "I"; // $NON-NLS-1$
    } else if (Person.SECOND.equals(personValue)) {
      pronoun = "you"; // $NON-NLS-1$
    } else {
      Object genderValue = phrase.getFeature(LexicalFeature.GENDER);
      if (Gender.FEMININE.equals(genderValue)) {
        pronoun = "she"; // $NON-NLS-1$
      } else if (Gender.MASCULINE.equals(genderValue)) {
        pronoun = "he"; // $NON-NLS-1$
      }
    }
    // AG: createWord now returns WordElement; so we embed it in an
    // inflected word element here
    NLGElement element;
    NLGElement proElement = phraseFactory.createWord(pronoun, LexicalCategory.PRONOUN);

    if (proElement instanceof WordElement) {
      element = new InflectedWordElement((WordElement) proElement);
      element.setFeature(
          LexicalFeature.GENDER, ((WordElement) proElement).getFeature(LexicalFeature.GENDER));
      // Ehud - also copy over person
      element.setFeature(Feature.PERSON, ((WordElement) proElement).getFeature(Feature.PERSON));
    } else {
      element = proElement;
    }

    element.setFeature(InternalFeature.DISCOURSE_FUNCTION, DiscourseFunction.SPECIFIER);
    element.setFeature(Feature.POSSESSIVE, phrase.getFeature(Feature.POSSESSIVE));
    element.setFeature(Feature.NUMBER, phrase.getFeature(Feature.NUMBER));

    if (phrase.hasFeature(InternalFeature.DISCOURSE_FUNCTION)) {
      element.setFeature(
          InternalFeature.DISCOURSE_FUNCTION,
          phrase.getFeature(InternalFeature.DISCOURSE_FUNCTION));
    }

    return element;
  }
Ejemplo n.º 4
0
 private NLGElement getNLGFromAggregation(ExprAggregator aggregationExpr) {
   SPhraseSpec p = nlgFactory.createClause();
   Aggregator aggregator = aggregationExpr.getAggregator();
   Expr expr = aggregator.getExpr();
   if (aggregator instanceof AggCountVar) {
     p.setSubject("the number of " + expr);
   }
   return p.getSubject();
 }
Ejemplo n.º 5
0
 /**
  * Generates a natural language representation for a query
  *
  * @param query Input query
  * @return Natural Language Representation
  */
 @Override
 public DocumentElement convert2NLE(Query query) {
   phrases = new ArrayList<NLGElement>();
   if (query.isSelectType() || query.isAskType()) {
     return convertSelectAndAsk(query);
   } else if (query.isDescribeType()) {
     return convertDescribe(query);
   } else {
     SPhraseSpec head = nlgFactory.createClause();
     head.setSubject("This framework");
     head.setVerb("support");
     head.setObject("the input query");
     head.setFeature(Feature.NEGATED, true);
     DocumentElement sentence = nlgFactory.createSentence(head);
     DocumentElement doc = nlgFactory.createParagraph(Arrays.asList(sentence));
     return doc;
   }
 }
Ejemplo n.º 6
0
  @Override
  public SPhraseSpec generate() {
    Lexicon lexicon = Lexicon.getDefaultLexicon();
    NLGFactory nlgFactory = new NLGFactory(lexicon);

    SPhraseSpec s = nlgFactory.createClause();

    s.setFrontModifier("moreover");

    NPPhraseSpec secondDive = nlgFactory.createNounPhrase("dive");
    secondDive.setDeterminer("your");
    secondDive.setPreModifier("second");

    VPPhraseSpec be = nlgFactory.createVerbPhrase("be");
    NLGElement predicative = nlgFactory.createInflectedWord("deep", LexicalCategory.ADVERB);
    predicative.setFeature(Feature.IS_COMPARATIVE, true);
    be.addComplement(predicative);

    PPPhraseSpec than = nlgFactory.createPrepositionPhrase("than");
    NPPhraseSpec firstDive = nlgFactory.createNounPhrase("one");
    firstDive.setDeterminer("the");
    firstDive.setPreModifier("first");
    than.addComplement(firstDive);

    s.setSubject(secondDive);
    s.setVerb(be);
    s.addComplement(than);
    s.setFeature(Feature.TENSE, Tense.PAST);

    SPhraseSpec which = nlgFactory.createClause();
    VPPhraseSpec beNot = nlgFactory.createVerbPhrase("be");
    beNot.setFeature(Feature.NEGATED, true);

    NLGElement recommended = nlgFactory.createAdjectivePhrase("recommended");
    beNot.addComplement(recommended);
    beNot.addModifier("really");
    which.setVerb(beNot);

    which.setFeature(Feature.COMPLEMENTISER, "which");
    s.addComplement(which);

    return s;
  }
Ejemplo n.º 7
0
 public SPhraseSpec getNLForTriple(Triple t) {
   SPhraseSpec p = nlgFactory.createClause();
   // process predicate then return subject is related to
   if (t.getPredicate().isVariable()) {
     if (t.getSubject().isVariable()) {
       p.setSubject(t.getSubject().toString());
     } else {
       p.setSubject(getNPPhrase(t.getSubject().toString(), false));
     }
     p.setVerb("be related via " + t.getPredicate().toString() + " to");
     if (t.getObject().isVariable()) {
       p.setObject(t.getObject().toString());
     } else {
       p.setObject(getNPPhrase(t.getObject().toString(), false));
     }
   } else {
     NLGElement subj;
     if (t.getSubject().isVariable()) {
       subj = nlgFactory.createWord(t.getSubject().toString(), LexicalCategory.NOUN);
     } else {
       subj =
           nlgFactory.createWord(
               uriConverter.convert(t.getSubject().toString()), LexicalCategory.NOUN);
     }
     //        subj.setFeature(Feature.POSSESSIVE, true);
     //        PhraseElement np = nlgFactory.createNounPhrase(subj,
     // getEnglishLabel(t.getPredicate().toString()));
     p.setSubject(
         realiser.realise(subj) + "\'s " + uriConverter.convert(t.getPredicate().toString()));
     p.setVerb("be");
     if (t.getObject().isVariable()) {
       p.setObject(t.getObject().toString());
     } else if (t.getObject().isLiteral()) {
       p.setObject(t.getObject().getLiteralLexicalForm());
     } else {
       p.setObject(getNPPhrase(t.getObject().toString(), false));
     }
   }
   p.setFeature(Feature.TENSE, Tense.PRESENT);
   return p;
 }
Ejemplo n.º 8
0
 public NLGElement getNLForTripleList(List<Triple> triples, String conjunction) {
   if (triples.isEmpty()) {
     return null;
   }
   if (triples.size() == 1) {
     return getNLForTriple(triples.get(0));
   } else {
     CoordinatedPhraseElement cpe;
     Triple t0 = triples.get(0);
     Triple t1 = triples.get(1);
     cpe = nlgFactory.createCoordinatedPhrase(getNLForTriple(t0), getNLForTriple(t1));
     for (int i = 2; i < triples.size(); i++) {
       cpe.addCoordinate(getNLForTriple(triples.get(i)));
     }
     cpe.setConjunction(conjunction);
     return cpe;
   }
 }
Ejemplo n.º 9
0
  /**
   * Processes a list of elements. These can be elements of the where clause or of an optional
   * clause
   *
   * @param e List of query elements
   * @return Conjunctive natural representation of the list of elements.
   */
  public NLGElement getNLFromElements(List<Element> e) {
    if (e.isEmpty()) {
      return null;
    }
    if (e.size() == 1) {
      return getNLFromSingleClause(e.get(0));
    } else {
      CoordinatedPhraseElement cpe;
      cpe =
          nlgFactory.createCoordinatedPhrase(
              getNLFromSingleClause(e.get(0)), getNLFromSingleClause(e.get(1)));
      for (int i = 2; i < e.size(); i++) {
        cpe.addCoordinate(getNLFromSingleClause(e.get(i)));
      }
      cpe.setConjunction("and");

      return cpe;
    }
  }
Ejemplo n.º 10
0
  public NLGElement getNLFromSingleClause(Element e) {
    if (e instanceof ElementPathBlock) {
      ElementPathBlock epb = (ElementPathBlock) e;
      List<Triple> triples = new ArrayList<Triple>();

      // get all triples. We assume that the depth of union is always 1
      for (TriplePath tp : epb.getPattern().getList()) {
        Triple t = tp.asTriple();
        triples.add(t);
      }
      return getNLForTripleList(triples, "and");
    } // if clause is union clause then we generate or statements
    else if (e instanceof ElementUnion) {
      CoordinatedPhraseElement cpe;
      // cast to union
      ElementUnion union = (ElementUnion) e;
      List<Triple> triples = new ArrayList<Triple>();

      // get all triples. We assume that the depth of union is always 1
      for (Element atom : union.getElements()) {
        ElementPathBlock epb = ((ElementPathBlock) (((ElementGroup) atom).getElements().get(0)));
        if (!epb.isEmpty()) {
          Triple t = epb.getPattern().get(0).asTriple();
          triples.add(t);
        }
      }
      return getNLForTripleList(triples, "or");
    } // if it's a filter
    else if (e instanceof ElementFilter) {
      SPhraseSpec p = nlgFactory.createClause();
      ElementFilter filter = (ElementFilter) e;
      Expr expr = filter.getExpr();
      return getNLFromSingleExpression(expr);
    }
    return null;
  }
Ejemplo n.º 11
0
 /**
  * Takes a DBPedia class and returns the correct label for it
  *
  * @param className Name of a class
  * @return Label
  */
 public NPPhraseSpec getNPPhrase(String className, boolean plural) {
   NPPhraseSpec object = null;
   if (className.equals(OWL.Thing.getURI())) {
     object = nlgFactory.createNounPhrase(GenericType.ENTITY.getNlr());
   } else if (className.equals(RDFS.Literal.getURI())) {
     object = nlgFactory.createNounPhrase(GenericType.VALUE.getNlr());
   } else if (className.equals(RDF.Property.getURI())) {
     object = nlgFactory.createNounPhrase(GenericType.RELATION.getNlr());
   } else if (className.equals(RDF.type.getURI())) {
     object = nlgFactory.createNounPhrase(GenericType.TYPE.getNlr());
   } else {
     String label = uriConverter.convert(className);
     if (label != null) {
       object = nlgFactory.createNounPhrase(label);
     } else {
       object = nlgFactory.createNounPhrase(GenericType.ENTITY.getNlr());
     }
   }
   object.setPlural(plural);
   return object;
 }
Ejemplo n.º 12
0
  private NLGElement getNLFromSingleExpression(Expr expr) {
    SPhraseSpec p = nlgFactory.createClause();
    return expressionConverter.convert(expr);
    // process REGEX
    /*if (expr instanceof E_Regex) {
    E_Regex expression;
    expression = (E_Regex) expr;
    String text = expression.toString();
    text = text.substring(6, text.length() - 1);
    String var = text.substring(0, text.indexOf(","));
    String pattern = text.substring(text.indexOf(",") + 1);
    p.setSubject(var);
    p.setVerb("match");
    p.setObject(pattern);
    } else if (expr instanceof ExprFunction1) {
    boolean negated = false;
    if (expr instanceof E_LogicalNot) {
    expr = ((E_LogicalNot) expr).getArg();
    negated = true;
    }
    if (expr instanceof E_Bound) {
    p.setSubject(((E_Bound) expr).getArg().toString());
    p.setVerb("exist");
    }
    if (negated) {
    p.setFeature(Feature.NEGATED, true);
    }
    } //process language filter
    else if (expr instanceof E_Equals) {
    E_Equals expression;
    expression = (E_Equals) expr;
    String text = expression.toString();
    text = text.substring(1, text.length() - 1);
    String[] split = text.split("=");
    String arg1 = split[0].trim();
    String arg2 = split[1].trim();
    if (arg1.startsWith("lang")) {
    String var = arg1.substring(5, arg1.length() - 1);
    p.setSubject(var);
    p.setVerb("be in");
    if (arg2.contains("en")) {
    p.setObject("English");
    }
    } else {
    p.setSubject(arg1);
    p.setVerb("equal");
    p.setObject(arg2);
    }
    } else if (expr instanceof ExprFunction2) {
    Expr left = ((ExprFunction2) expr).getArg1();
    Expr right = ((ExprFunction2) expr).getArg2();

    //invert if right is variable or aggregation and left side not
    boolean inverted = false;
    if (!left.isVariable() && (right.isVariable() || right instanceof ExprAggregator)) {
    Expr tmp = left;
    left = right;
    right = tmp;
    inverted = true;
    }

    //handle subject
    NLGElement subject = null;
    if (left instanceof ExprAggregator) {
    subject = getNLGFromAggregation((ExprAggregator) left);
    } else {
    if (left.isFunction()) {
    ExprFunction function = left.getFunction();
    if (function.getArgs().size() == 1) {
    left = function.getArg(1);
    }
    }
    subject = nlgFactory.createNounPhrase(left.toString());
    }
    p.setSubject(subject);
    //handle object
    if (right.isFunction()) {
    ExprFunction function = right.getFunction();
    if (function.getArgs().size() == 1) {
    right = function.getArg(1);
    }
    }
    if (right.isVariable()) {
    p.setObject(right.toString());
    } else if (right.isConstant()) {
    if (right.getConstant().isIRI()) {
    p.setObject(getEnglishLabel(right.getConstant().getNode().getURI()));
    } else if (right.getConstant().isLiteral()) {
    p.setObject(right.getConstant().asNode().getLiteralLexicalForm());
    }
    }
    //handle verb resp. predicate
    String verb = null;
    if (expr instanceof E_GreaterThan) {
    if (inverted) {
    verb = "be less than";
    } else {
    verb = "be greater than";
    }
    } else if (expr instanceof E_GreaterThanOrEqual) {
    if (inverted) {
    verb = "be less than or equal to";
    } else {
    verb = "be greater than or equal to";
    }
    } else if (expr instanceof E_LessThan) {
    if (inverted) {
    verb = "be greater than";
    } else {
    verb = "be less than";
    }
    } else if (expr instanceof E_LessThanOrEqual) {
    if (inverted) {
    verb = "be greater than or equal to";
    } else {
    verb = "be less than or equal to";
    }
    } else if (expr instanceof E_NotEquals) {
    if (left instanceof E_Lang) {
    verb = "be in";
    if (right.isConstant() && right.getConstant().asString().equals("en")) {
    p.setObject("English");
    }

    } else {
    verb = "be equal to";
    }
    p.setFeature(Feature.NEGATED, true);
    }
    p.setVerb(verb);
    } //not equals
    else {
    return null;
    }
    return p;*/
  }
Ejemplo n.º 13
0
 private NLGElement processTypes(
     Map<String, Set<String>> typeMap, Set<String> vars, boolean count, boolean distinct) {
   List<NPPhraseSpec> objects = new ArrayList<NPPhraseSpec>();
   // process the type information to create the object(s)
   for (String s : typeMap.keySet()) {
     if (vars.contains(s)) {
       // contains the objects to the sentence
       NPPhraseSpec object;
       object = nlgFactory.createNounPhrase("?" + s);
       Set<String> types = typeMap.get(s);
       if (types.size() == 1) {
         NPPhraseSpec np = getNPPhrase(types.iterator().next(), true);
         if (distinct) {
           np.addModifier("distinct");
         }
         object.addPreModifier(np);
       } else {
         Iterator<String> typeIterator = types.iterator();
         String type0 = typeIterator.next();
         String type1 = typeIterator.next();
         NPPhraseSpec np0 = getNPPhrase(type0, true);
         //                        if (distinct) {
         //                            np0.addModifier("distinct");
         //                        }
         NPPhraseSpec np1 = getNPPhrase(type1, true);
         //                        if (distinct) {
         //                            np1.addModifier("distinct");
         //                        }
         CoordinatedPhraseElement cpe = nlgFactory.createCoordinatedPhrase(np0, np1);
         while (typeIterator.hasNext()) {
           NPPhraseSpec np = getNPPhrase(typeIterator.next(), true);
           //                        if (distinct) {
           //                            np.addModifier("distinct");
           //                        }
           cpe.addCoordinate(np);
         }
         cpe.setConjunction("as well as");
         if (distinct) {
           cpe.addPreModifier("distinct");
         }
         object.addPreModifier(cpe);
       }
       object.setFeature(Feature.CONJUNCTION, "or");
       objects.add(object);
     }
   }
   if (objects.size() == 1) {
     // if(count) objects.get(0).addPreModifier("the number of");
     return objects.get(0);
   } else {
     CoordinatedPhraseElement cpe =
         nlgFactory.createCoordinatedPhrase(objects.get(0), objects.get(1));
     if (objects.size() > 2) {
       for (int i = 2; i < objects.size(); i++) {
         cpe.addCoordinate(objects.get(i));
       }
     }
     // if(count) cpe.addPreModifier("the number of");
     return cpe;
   }
 }
Ejemplo n.º 14
0
  /**
   * Generates a natural language representation for SELECT queries
   *
   * @param query Input query
   * @return Natural Language Representation
   */
  public DocumentElement convertSelectAndAsk(Query query) {
    // List of sentences for the output
    List<DocumentElement> sentences = new ArrayList<DocumentElement>();
    //        System.out.println("Input query = " + query);
    // preprocess the query to get the relevant types
    TypeExtractor tEx = new TypeExtractor(endpoint);
    Map<String, Set<String>> typeMap = tEx.extractTypes(query);
    //        System.out.println("Processed query = " + query);
    // contains the beginning of the query, e.g., "this query returns"
    SPhraseSpec head = nlgFactory.createClause();
    String conjunction = "such that";
    NLGElement body;
    NLGElement postConditions;

    List<Element> whereElements = getWhereElements(query);
    List<Element> optionalElements = getOptionalElements(query);
    // first sort out variables
    Set<String> projectionVars = typeMap.keySet();
    Set<String> whereVars = getVars(whereElements, projectionVars);
    // case we only have stuff such as rdf:type queries
    if (whereVars.isEmpty()) {
      whereVars = projectionVars;
    }
    Set<String> optionalVars = getVars(optionalElements, projectionVars);
    // important. Remove variables that have already been declared in first
    // sentence from second sentence
    for (String var : whereVars) {
      if (optionalVars.contains(var)) {
        optionalVars.remove(var);
      }
    }

    // process SELECT queries
    if (query.isSelectType()) {
      // process head
      // we could create a lexicon from which we could read these
      head.setSubject("This query");
      if (!tEx.isCount()) {
        head.setVerb("retrieve");
      } else {
        head.setVerb("retrieve the number of");
      }
    } // process ASK queries
    else {
      // process factual ask queries (no variables at all)
      if (typeMap.isEmpty()) {
        head.setSubject("This query");
        head.setVerb("ask whether");
        head.setObject(getNLFromElements(whereElements));
        head.setFeature(Feature.SUPRESSED_COMPLEMENTISER, true);
        phrases.add(head);
        sentences.add(nlgFactory.createSentence(head));
        return nlgFactory.createParagraph(sentences);
      }
      // process head
      // we could create a lexicon from which we could read these
      head.setSubject("This query");
      head.setVerb("ask for the existence of");
    }
    NLGElement e = processTypes(typeMap, whereVars, tEx.isCount(), query.isDistinct());
    head.setObject(e);
    // now generate body
    if (!whereElements.isEmpty()) {
      body = getNLFromElements(whereElements);
      // now add conjunction
      CoordinatedPhraseElement phrase1 = nlgFactory.createCoordinatedPhrase(head, body);
      phrase1.setConjunction("such that");
      // add as first sentence
      sentences.add(nlgFactory.createSentence(phrase1));
      phrases.add(phrase1);
      // this concludes the first sentence.
    } else {
      sentences.add(nlgFactory.createSentence(head));
      phrases.add(head);
    }

    // The second sentence deals with the optional clause (if it exists)
    if (optionalElements != null && !optionalElements.isEmpty()) {
      // the optional clause exists
      // if no supplementary projection variables are used in the clause
      if (optionalVars.isEmpty()) {
        SPhraseSpec optionalHead = nlgFactory.createClause();
        optionalHead.setSubject("it");
        optionalHead.setVerb("retrieve");
        optionalHead.setObject("data");
        optionalHead.setFeature(Feature.CUE_PHRASE, "Additionally, ");
        NLGElement optionalBody = getNLFromElements(optionalElements);
        CoordinatedPhraseElement optionalPhrase =
            nlgFactory.createCoordinatedPhrase(optionalHead, optionalBody);
        optionalPhrase.setConjunction("such that");
        optionalPhrase.addComplement("if such exist");
        sentences.add(nlgFactory.createSentence(optionalPhrase));

      } // if supplementary projection variables are used in the clause
      else {
        SPhraseSpec optionalHead = nlgFactory.createClause();
        optionalHead.setSubject("it");
        optionalHead.setVerb("retrieve");
        optionalHead.setObject(
            processTypes(typeMap, optionalVars, query.isDistinct(), query.isDistinct()));
        optionalHead.setFeature(Feature.CUE_PHRASE, "Additionally, ");
        if (!optionalElements.isEmpty()) {
          NLGElement optionalBody;
          optionalBody = getNLFromElements(optionalElements);
          // now add conjunction
          CoordinatedPhraseElement optionalPhrase =
              nlgFactory.createCoordinatedPhrase(optionalHead, optionalBody);
          optionalPhrase.setConjunction("such that");
          // add as second sentence
          optionalPhrase.addComplement("if such exist");
          sentences.add(nlgFactory.createSentence(optionalPhrase));
          phrases.add(optionalPhrase);
          // this concludes the second sentence.
        } else {
          optionalHead.addComplement("if such exist");
          sentences.add(nlgFactory.createSentence(optionalHead));
          phrases.add(optionalHead);
        }
      }
    }

    // The last sentence deals with the result modifiers
    if (query.hasHaving()) {
      SPhraseSpec modifierHead = nlgFactory.createClause();
      modifierHead.setSubject("it");
      modifierHead.setVerb("return exclusively");
      modifierHead.setObject("results");
      modifierHead.getObject().setPlural(true);
      modifierHead.setFeature(Feature.CUE_PHRASE, "Moreover, ");
      List<Expr> expressions = query.getHavingExprs();
      CoordinatedPhraseElement phrase =
          nlgFactory.createCoordinatedPhrase(modifierHead, getNLFromExpressions(expressions));
      phrase.setConjunction("such that");
      sentences.add(nlgFactory.createSentence(phrase));
    }
    if (query.hasOrderBy()) {
      SPhraseSpec order = nlgFactory.createClause();
      order.setSubject("The results");
      order.getSubject().setPlural(true);
      order.setVerb("be ordered by");
      List<SortCondition> sc = query.getOrderBy();
      if (sc.size() == 1) {
        Expr expr = sc.get(0).getExpression();
        if (expr instanceof ExprVar) {
          ExprVar ev = (ExprVar) expr;
          order.setObject(ev.toString());
        }
        if (sc.get(0).direction < 0) {
          order.addComplement("in descending order");
        } else {
          order.addComplement("in ascending order");
        }
      }
      phrases.add(order);
      sentences.add(nlgFactory.createSentence(order));
    }
    if (query.hasLimit()) {
      SPhraseSpec limitOffset = nlgFactory.createClause();
      long limit = query.getLimit();
      if (query.hasOffset()) {
        long offset = query.getOffset();
        limitOffset.setSubject("The query");
        limitOffset.setVerb("return");
        limitOffset.setObject("results between number " + limit + " and " + (offset + limit));
      } else {
        limitOffset.setSubject("The query");
        limitOffset.setVerb("return");
        if (limit > 1) {
          if (query.hasOrderBy()) {
            limitOffset.setObject("at most the first " + limit + " results");
          } else {
            limitOffset.setObject("at most " + limit + " results");
          }
        } else {
          if (query.hasOrderBy()) {
            limitOffset.setObject("at most the first result");
          } else {
            limitOffset.setObject("at most one result");
          }
        }
      }
      phrases.add(limitOffset);
      sentences.add(nlgFactory.createSentence(limitOffset));
    }

    DocumentElement result = nlgFactory.createParagraph(sentences);
    return result;
  }