001/* 002 * Copyright (c) 2013 Nu Echo Inc. All rights reserved. 003 */ 004package com.nuecho.rivr.voicexml.turn.output; 005 006import static com.nuecho.rivr.core.util.Assert.*; 007import static com.nuecho.rivr.voicexml.rendering.voicexml.VoiceXmlDomUtil.*; 008import static java.util.Collections.*; 009 010import java.util.*; 011import java.util.Map.Entry; 012 013import javax.json.*; 014 015import org.w3c.dom.*; 016 017import com.nuecho.rivr.core.util.*; 018import com.nuecho.rivr.voicexml.dialogue.*; 019import com.nuecho.rivr.voicexml.rendering.voicexml.*; 020import com.nuecho.rivr.voicexml.turn.*; 021import com.nuecho.rivr.voicexml.turn.output.fetch.*; 022import com.nuecho.rivr.voicexml.util.json.*; 023 024/** 025 * A {@link SubdialogueCall} is a {@link VoiceXmlOutputTurn} that invokes 026 * another external dialogue. 027 * <p> 028 * Parameters may be passed to the subdialogue and return values may be 029 * retrieved if the invoked subdialogue ends with a <code><return></code> 030 * instruction. 031 * 032 * @author Nu Echo Inc. 033 * @see Parameter 034 * @see <a 035 * href="https://www.w3.org/TR/voicexml20/#dml2.3.4">https://www.w3.org/TR/voicexml20/#dml2.3.4</a> 036 */ 037public class SubdialogueCall extends VoiceXmlOutputTurn { 038 public static final String SUBDIALOGUE_RESULT_VARIABLE_NAME = "subdialogue"; 039 040 private static final String SUBDIALOGUE_INVOCATION_TURN_TYPE = "subdialogue"; 041 042 private static final String POST_DIALOGUE_SCRIPT_PROPERTY = "postDialogueScript"; 043 private static final String SUBDIALOGUE_PARAMETERS_PROPERTY = "subdialogueParameters"; 044 private static final String SUBMIT_URI_PROPERTY = "uri"; 045 private static final String SUBMIT_PARAMETERS_PROPERTY = "submitParameters"; 046 private static final String SUBMIT_METHOD_PROPERTY = "submitMethod"; 047 private static final String FETCH_CONFIGURATION_PROPERTY = "fetchConfiguration"; 048 049 private final String mUri; 050 private Collection<Parameter> mParameters = Collections.emptyList(); 051 private VariableList mSubmitParameters = new VariableList(); 052 private SubmitMethod mMethod = SubmitMethod.get; 053 private DocumentFetchConfiguration mFetchConfiguration; 054 private String mPostDialogueScript; 055 056 /** 057 * @param name The name of this turn. Not empty. 058 * @param uri The URI of the subdialogue. Not empty. 059 */ 060 public SubdialogueCall(String name, String uri) { 061 super(name); 062 Assert.notEmpty(uri, "uri"); 063 mUri = uri; 064 } 065 066 /** 067 * @param parameters A list of {@link Parameter} that will be passed to the 068 * subdialogue. Not null. 069 */ 070 public final void setSubdialogueParameters(Collection<Parameter> parameters) { 071 Assert.noNullValues(parameters, "parameters"); 072 mParameters = new ArrayList<Parameter>(parameters); 073 } 074 075 /** 076 * @param subdialogueParameters A list of {@link Parameter} that will be 077 * passed to the subdialogue. Not null. 078 */ 079 public final void setSubdialogueParameters(Parameter... subdialogueParameters) { 080 setSubdialogueParameters(asListChecked(subdialogueParameters)); 081 } 082 083 /** 084 * @param submitParameters A list of variable to submit when invoking the 085 * URI. Not null. 086 */ 087 public final void setSubmitParameters(VariableList submitParameters) { 088 Assert.notNull(submitParameters, "submitParameters"); 089 mSubmitParameters = submitParameters; 090 } 091 092 /** 093 * @param method The HTTP method used to invoke the subdialogue (GET or 094 * POST). <code>null</code> to use the VoiceXML platform default 095 * @see SubmitMethod 096 */ 097 public final void setMethod(SubmitMethod method) { 098 mMethod = method; 099 } 100 101 /** 102 * @param fetchConfiguration The {@link DocumentFetchConfiguration}. 103 * <code>null</code> to use the VoiceXML platform default. 104 */ 105 public final void setFetchConfiguration(DocumentFetchConfiguration fetchConfiguration) { 106 mFetchConfiguration = fetchConfiguration; 107 } 108 109 /** 110 * @param postDialogueScript The ECMAScript script to execute after 111 * subdialogue invocation. 112 */ 113 public final void setPostDialogueScript(String postDialogueScript) { 114 mPostDialogueScript = postDialogueScript; 115 } 116 117 public final String getUri() { 118 return mUri; 119 } 120 121 public final Collection<Parameter> getVoiceXmlParameters() { 122 return unmodifiableCollection(mParameters); 123 } 124 125 public final VariableList getSubmitParameters() { 126 return mSubmitParameters; 127 } 128 129 public final SubmitMethod getMethod() { 130 return mMethod; 131 } 132 133 public final String getPostDialogueScript() { 134 return mPostDialogueScript; 135 } 136 137 public final DocumentFetchConfiguration getFetchConfiguration() { 138 return mFetchConfiguration; 139 } 140 141 @Override 142 protected final String getOuputTurnType() { 143 return SUBDIALOGUE_INVOCATION_TURN_TYPE; 144 } 145 146 @Override 147 protected void addTurnProperties(JsonObjectBuilder builder) { 148 JsonUtils.add(builder, SUBMIT_URI_PROPERTY, mUri); 149 JsonUtils.add(builder, SUBMIT_METHOD_PROPERTY, mMethod.name()); 150 JsonUtils.add(builder, SUBMIT_PARAMETERS_PROPERTY, mSubmitParameters); 151 JsonUtils.add(builder, SUBDIALOGUE_PARAMETERS_PROPERTY, JsonUtils.toJson(mParameters)); 152 JsonUtils.add(builder, FETCH_CONFIGURATION_PROPERTY, mFetchConfiguration); 153 JsonUtils.add(builder, POST_DIALOGUE_SCRIPT_PROPERTY, getPostDialogueScript()); 154 } 155 156 @Override 157 protected void fillVoiceXmlDocument(Document document, Element formElement, VoiceXmlDialogueContext dialogueContext) 158 throws VoiceXmlDocumentRenderingException { 159 160 List<String> submitNameList = new ArrayList<String>(); 161 VariableList submitVariableList = mSubmitParameters; 162 if (submitVariableList != null) { 163 addVariables(formElement, submitVariableList); 164 165 for (Entry<String, String> entry : mSubmitParameters) { 166 submitNameList.add(entry.getKey()); 167 } 168 } 169 170 Element subdialogueElement = DomUtils.appendNewElement(formElement, SUBDIALOG_ELEMENT); 171 subdialogueElement.setAttribute(NAME_ATTRIBUTE, SUBDIALOGUE_FORM_ITEM_NAME); 172 subdialogueElement.setAttribute(SRC_ATTRIBUTE, mUri); 173 174 if (!submitNameList.isEmpty()) { 175 subdialogueElement.setAttribute(NAME_LIST_ATTRIBUTE, StringUtils.join(submitNameList, " ")); 176 } 177 178 for (Parameter parameter : mParameters) { 179 Element paramElement = DomUtils.appendNewElement(subdialogueElement, PARAM_ELEMENT); 180 paramElement.setAttribute(NAME_ATTRIBUTE, parameter.getName()); 181 setAttribute(paramElement, VALUE_ATTRIBUTE, parameter.getValue()); 182 setAttribute(paramElement, EXPR_ATTRIBUTE, parameter.getExpression()); 183 } 184 185 SubmitMethod submitMethod = mMethod; 186 if (submitMethod != null) { 187 subdialogueElement.setAttribute(METHOD_ATTRIBUTE, submitMethod.name()); 188 } 189 190 DocumentFetchConfiguration fetchConfiguration = mFetchConfiguration; 191 if (fetchConfiguration != null) { 192 applyFetchAudio(subdialogueElement, fetchConfiguration.getFetchAudio()); 193 applyRessourceFetchConfiguration(subdialogueElement, fetchConfiguration); 194 } 195 196 Element filledElement = DomUtils.appendNewElement(subdialogueElement, FILLED_ELEMENT); 197 198 createVarElement(filledElement, SUBDIALOGUE_RESULT_VARIABLE_NAME, "dialog." + SUBDIALOGUE_FORM_ITEM_NAME); 199 200 if (mPostDialogueScript != null) { 201 createScript(filledElement, mPostDialogueScript); 202 } 203 204 createScript(filledElement, RIVR_SCOPE_OBJECT + ".addValueResult(" + SUBDIALOGUE_RESULT_VARIABLE_NAME + ");"); 205 createGotoSubmit(filledElement); 206 } 207 208 /** 209 * {@link SubdialogueCall} parameter, can be created with a string value, a 210 * {@link JsonValue} or an expression. 211 */ 212 public static final class Parameter implements JsonSerializable { 213 private static final String NAME_PROPERTY = "name"; 214 private static final String VALUE_PROPERTY = "value"; 215 private static final String EXPRESSION_PROPERTY = "expression"; 216 217 private final String mName; 218 private String mExpression; 219 private String mValue; 220 221 /** 222 * @param name The name of the parameter. Not empty. 223 * @param value The string value of the parameter. Not null. 224 * @return The newly created subdialogue parameter 225 */ 226 public static Parameter createWithValue(String name, String value) { 227 Assert.notNull(value, "value"); 228 229 Parameter parameter = new Parameter(name); 230 parameter.mValue = value; 231 return parameter; 232 } 233 234 /** 235 * @param name The name of the parameter. Not empty. 236 * @param json The JSON value of the parameter. Not null. 237 * @return The newly created subdialogue parameter 238 */ 239 public static Parameter createWithJson(String name, JsonValue json) { 240 Assert.notNull(json, "json"); 241 return createWithExpression(name, json.toString()); 242 } 243 244 /** 245 * @param name The name of the parameter. Not empty. 246 * @param expression The ECMAScript expression of the parameter. Not 247 * null. 248 * @return The newly created subdialogue parameter 249 */ 250 public static Parameter createWithExpression(String name, String expression) { 251 Assert.notNull(expression, "expression"); 252 253 Parameter parameter = new Parameter(name); 254 parameter.mExpression = expression; 255 return parameter; 256 } 257 258 private Parameter(String name) { 259 Assert.notEmpty(name, "name"); 260 mName = name; 261 } 262 263 public String getName() { 264 return mName; 265 } 266 267 public String getExpression() { 268 return mExpression; 269 } 270 271 public String getValue() { 272 return mValue; 273 } 274 275 @Override 276 public JsonValue asJson() { 277 JsonObjectBuilder builder = JsonUtils.createObjectBuilder(); 278 JsonUtils.add(builder, NAME_PROPERTY, mName); 279 JsonUtils.add(builder, EXPRESSION_PROPERTY, mExpression); 280 JsonUtils.add(builder, VALUE_PROPERTY, mValue); 281 return builder.build(); 282 } 283 } 284 285 /** 286 * Builder used to ease the creation of instances of {@link SubdialogueCall} 287 * . 288 */ 289 public static class Builder { 290 291 private final String mName; 292 private String mUri; 293 private final List<Parameter> mParameters = Collections.emptyList(); 294 private final VariableList mSubmitParameters = new VariableList(); 295 private SubmitMethod mMethod = SubmitMethod.get; 296 private DocumentFetchConfiguration mFetchConfiguration; 297 private String mPostDialogueScript; 298 299 public Builder(String name) { 300 mName = name; 301 } 302 303 public Builder uri(String uri) { 304 Assert.notEmpty(uri, "uri"); 305 mUri = uri; 306 return this; 307 } 308 309 public Builder addVoiceXmlParameter(Parameter parameter) { 310 Assert.notNull(parameter, "parameter"); 311 mParameters.add(parameter); 312 return this; 313 } 314 315 public Builder addSubmitParameterExpression(String name, String expression) { 316 Assert.notEmpty(name, "name"); 317 mSubmitParameters.addWithExpression(name, expression); 318 return this; 319 } 320 321 public Builder addSubmitParameterString(String name, String string) { 322 Assert.notEmpty(name, "name"); 323 mSubmitParameters.addWithString(name, string); 324 return this; 325 } 326 327 public Builder setMethod(SubmitMethod method) { 328 Assert.notNull(method, "method"); 329 mMethod = method; 330 return this; 331 } 332 333 public Builder setFetchConfiguration(DocumentFetchConfiguration fetchConfiguration) { 334 mFetchConfiguration = fetchConfiguration; 335 return this; 336 } 337 338 public Builder setPostDialogueScript(String postDialogueScript) { 339 mPostDialogueScript = postDialogueScript; 340 return this; 341 } 342 343 public SubdialogueCall build() { 344 SubdialogueCall subdialogueCall = new SubdialogueCall(mName, mUri); 345 subdialogueCall.setMethod(mMethod); 346 subdialogueCall.setPostDialogueScript(mPostDialogueScript); 347 subdialogueCall.setFetchConfiguration(mFetchConfiguration); 348 subdialogueCall.setSubdialogueParameters(mParameters); 349 subdialogueCall.setSubmitParameters(mSubmitParameters); 350 return subdialogueCall; 351 } 352 } 353 354 @Override 355 public int hashCode() { 356 final int prime = 31; 357 int result = super.hashCode(); 358 result = prime * result + ((mFetchConfiguration == null) ? 0 : mFetchConfiguration.hashCode()); 359 result = prime * result + ((mMethod == null) ? 0 : mMethod.hashCode()); 360 result = prime * result + ((mParameters == null) ? 0 : mParameters.hashCode()); 361 result = prime * result + ((mPostDialogueScript == null) ? 0 : mPostDialogueScript.hashCode()); 362 result = prime * result + ((mSubmitParameters == null) ? 0 : mSubmitParameters.hashCode()); 363 result = prime * result + ((mUri == null) ? 0 : mUri.hashCode()); 364 return result; 365 } 366 367 @Override 368 public boolean equals(Object obj) { 369 if (this == obj) return true; 370 if (!super.equals(obj)) return false; 371 if (getClass() != obj.getClass()) return false; 372 SubdialogueCall other = (SubdialogueCall) obj; 373 if (mFetchConfiguration == null) { 374 if (other.mFetchConfiguration != null) return false; 375 } else if (!mFetchConfiguration.equals(other.mFetchConfiguration)) return false; 376 if (mMethod != other.mMethod) return false; 377 if (mParameters == null) { 378 if (other.mParameters != null) return false; 379 } else if (!mParameters.equals(other.mParameters)) return false; 380 if (mPostDialogueScript == null) { 381 if (other.mPostDialogueScript != null) return false; 382 } else if (!mPostDialogueScript.equals(other.mPostDialogueScript)) return false; 383 if (mSubmitParameters == null) { 384 if (other.mSubmitParameters != null) return false; 385 } else if (!mSubmitParameters.equals(other.mSubmitParameters)) return false; 386 if (mUri == null) { 387 if (other.mUri != null) return false; 388 } else if (!mUri.equals(other.mUri)) return false; 389 return true; 390 } 391 392}