001/* 002 * Copyright (c) 2013 Nu Echo Inc. All rights reserved. 003 */ 004 005package com.nuecho.rivr.voicexml.turn.output; 006 007import static com.nuecho.rivr.core.util.Assert.*; 008import static com.nuecho.rivr.voicexml.rendering.voicexml.VoiceXmlDomUtil.*; 009import static java.util.Collections.*; 010 011import java.util.*; 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.output.fetch.*; 021import com.nuecho.rivr.voicexml.util.json.*; 022 023/** 024 * An {@link ObjectCall} is a {@link VoiceXmlOutputTurn} used to exploit 025 * platform-specific functionality. 026 * 027 * @author Nu Echo Inc. 028 * @see <a 029 * href="https://www.w3.org/TR/voicexml20/#dml2.3.5">https://www.w3.org/TR/voicexml20/#dml2.3.5</a> 030 */ 031public class ObjectCall extends VoiceXmlOutputTurn { 032 public static final String OBJECT_RESULT_VARIABLE_NAME = "object"; 033 034 private static final String OBJECT_TURN_TYPE = "object"; 035 036 private static final String ARCHIVES_PROPERTY = "archives"; 037 private static final String PARAMETERS_PROPERTY = "parameters"; 038 private static final String POST_OBJECT_SCRIPT_PROPERTY = "postObjectScript"; 039 private static final String TYPE_PROPERTY = "type"; 040 private static final String DATA_PROPERTY = "data"; 041 private static final String CODE_TYPE_PROPERTY = "codeType"; 042 private static final String CODE_BASE_PROPERTY = "codeBase"; 043 private static final String CLASS_ID_PROPERTY = "classId"; 044 private static final String FETCH_CONFIGURATION_PROPERTY = "fetchConfiguration"; 045 046 private String mClassId; 047 private String mCodeBase; 048 private String mCodeType; 049 private String mData; 050 private String mType; 051 private List<String> mArchives; 052 private FetchConfiguration mFetchConfiguration; 053 private List<Parameter> mParameters = Collections.emptyList(); 054 private String mPostObjectScript; 055 056 /** 057 * @param name The name of this turn. Not empty. 058 */ 059 public ObjectCall(String name) { 060 super(name); 061 } 062 063 /** 064 * @param classId The URI specifying the location of the object's 065 * implementation. 066 */ 067 public final void setClassId(String classId) { 068 mClassId = classId; 069 } 070 071 /** 072 * @param codeBase The base path used to resolve relative URIs specified by 073 * classid, data, and archive. 074 */ 075 public final void setCodeBase(String codeBase) { 076 mCodeBase = codeBase; 077 } 078 079 /** 080 * @param codeType The content type of data expected when downloading the 081 * object specified by classid. 082 */ 083 public final void setCodeType(String codeType) { 084 mCodeType = codeType; 085 } 086 087 /** 088 * @param data The URI specifying the location of the object's data. 089 */ 090 public final void setData(String data) { 091 mData = data; 092 } 093 094 /** 095 * @param type The content type of the data specified by the data attribute 096 */ 097 public final void setType(String type) { 098 mType = type; 099 } 100 101 /** 102 * @param archives A list of URIs for archives containing resources relevant 103 * to the object. 104 */ 105 public final void setArchives(List<String> archives) { 106 mArchives = archives; 107 } 108 109 /** 110 * @param fetchConfiguration The object {@link FetchConfiguration}. 111 */ 112 public final void setFetchConfiguration(FetchConfiguration fetchConfiguration) { 113 mFetchConfiguration = fetchConfiguration; 114 } 115 116 /** 117 * @param parameters A list of parameters passed when invoking object. Not 118 * null. 119 */ 120 public final void setParameters(List<Parameter> parameters) { 121 Assert.noNullValues(parameters, "parameters"); 122 mParameters = new ArrayList<Parameter>(parameters); 123 } 124 125 /** 126 * @param parameters A list of parameters passed when invoking object. Not 127 * null. 128 */ 129 public final void setParameters(Parameter... parameters) { 130 setParameters(asListChecked(parameters)); 131 } 132 133 /** 134 * @param postObjectScript The ECMAScript script to execute after object 135 * invocation. 136 */ 137 public final void setPostObjectScript(String postObjectScript) { 138 mPostObjectScript = postObjectScript; 139 } 140 141 public final String getClassId() { 142 return mClassId; 143 } 144 145 public final String getCodeBase() { 146 return mCodeBase; 147 } 148 149 public final String getCodeType() { 150 return mCodeType; 151 } 152 153 public final String getData() { 154 return mData; 155 } 156 157 public final String getType() { 158 return mType; 159 } 160 161 public final List<String> getArchives() { 162 return mArchives; 163 } 164 165 public final FetchConfiguration getFetchConfiguration() { 166 return mFetchConfiguration; 167 } 168 169 public final List<Parameter> getParameters() { 170 return unmodifiableList(mParameters); 171 } 172 173 public final String getPostObjectScript() { 174 return mPostObjectScript; 175 } 176 177 @Override 178 protected final String getOuputTurnType() { 179 return OBJECT_TURN_TYPE; 180 } 181 182 @Override 183 protected void addTurnProperties(JsonObjectBuilder builder) { 184 JsonUtils.add(builder, CLASS_ID_PROPERTY, mClassId); 185 JsonUtils.add(builder, CODE_BASE_PROPERTY, mCodeBase); 186 JsonUtils.add(builder, CODE_TYPE_PROPERTY, mCodeType); 187 JsonUtils.add(builder, DATA_PROPERTY, mData); 188 JsonUtils.add(builder, TYPE_PROPERTY, mType); 189 JsonUtils.add(builder, POST_OBJECT_SCRIPT_PROPERTY, mPostObjectScript); 190 191 JsonUtils.add(builder, PARAMETERS_PROPERTY, JsonUtils.toJson(mParameters)); 192 JsonUtils.add(builder, FETCH_CONFIGURATION_PROPERTY, mFetchConfiguration); 193 194 if (mArchives != null) { 195 JsonArrayBuilder archiveBuilder = JsonUtils.createArrayBuilder(); 196 for (String archive : mArchives) { 197 archiveBuilder.add(archive); 198 } 199 JsonUtils.add(builder, ARCHIVES_PROPERTY, archiveBuilder); 200 } 201 } 202 203 @Override 204 protected void fillVoiceXmlDocument(Document document, Element formElement, VoiceXmlDialogueContext dialogueContext) 205 throws VoiceXmlDocumentRenderingException { 206 207 Element objectElement = DomUtils.appendNewElement(formElement, OBJECT_ELEMENT); 208 objectElement.setAttribute(NAME_ATTRIBUTE, OBJECT_FORM_ITEM_NAME); 209 210 List<String> archives = getArchives(); 211 if (archives != null && !archives.isEmpty()) { 212 objectElement.setAttribute(ARCHIVE_ATTRIBUTE, StringUtils.join(archives, " ")); 213 } 214 215 setAttribute(objectElement, CLASS_ID_ATTRIBUTE, mClassId); 216 setAttribute(objectElement, CODE_BASE_ATTRIBUTE, mCodeBase); 217 setAttribute(objectElement, CODE_TYPE_ATTRIBUTE, mCodeType); 218 setAttribute(objectElement, DATA_ATTRIBUTE, mData); 219 setAttribute(objectElement, TYPE_ATTRIBUTE, mType); 220 221 for (Parameter parameter : mParameters) { 222 Element paramElement = DomUtils.appendNewElement(objectElement, PARAM_ELEMENT); 223 paramElement.setAttribute(NAME_ATTRIBUTE, parameter.getName()); 224 225 setAttribute(paramElement, VALUE_ATTRIBUTE, parameter.getValue()); 226 setAttribute(paramElement, EXPR_ATTRIBUTE, parameter.getExpression()); 227 setAttribute(paramElement, TYPE_ATTRIBUTE, parameter.getType()); 228 ParameterValueType valueType = parameter.getValueType(); 229 if (valueType != null) { 230 paramElement.setAttribute(VALUE_TYPE_ATTRIBUTE, valueType.name()); 231 } 232 } 233 234 FetchConfiguration fetchConfiguration = mFetchConfiguration; 235 if (fetchConfiguration != null) { 236 applyRessourceFetchConfiguration(objectElement, fetchConfiguration); 237 } 238 239 Element filledElement = DomUtils.appendNewElement(objectElement, FILLED_ELEMENT); 240 241 createVarElement(filledElement, OBJECT_RESULT_VARIABLE_NAME, "dialog." + OBJECT_FORM_ITEM_NAME); 242 243 if (getPostObjectScript() != null) { 244 createScript(filledElement, mPostObjectScript); 245 } 246 247 createScript(filledElement, RIVR_SCOPE_OBJECT + ".addValueResult(" + OBJECT_RESULT_VARIABLE_NAME + ");"); 248 createGotoSubmit(filledElement); 249 } 250 251 /** 252 * {@link ObjectCall} parameter, can be created with a string value, a 253 * {@link JsonValue} or an expression. 254 */ 255 public static final class Parameter implements JsonSerializable { 256 private static final String NAME_PROPERTY = "name"; 257 private static final String VALUE_PROPERTY = "value"; 258 @SuppressWarnings("hiding") 259 private static final String TYPE_PROPERTY = "type"; 260 private static final String VALUE_TYPE_PROPERTY = "valueType"; 261 private static final String EXPRESSION_PROPERTY = "expression"; 262 263 private final String mName; 264 private String mExpression; 265 private String mValue; 266 private ParameterValueType mValueType; 267 private String mType; 268 269 /** 270 * @param name The name of the parameter. Not empty. 271 * @param value The string value of the parameter. Not null. 272 * @return The newly created object parameter 273 */ 274 public static Parameter createWithValue(String name, String value) { 275 Assert.notEmpty(name, "name"); 276 Assert.notNull(value, "value"); 277 278 Parameter parameter = new Parameter(name); 279 parameter.mValue = value; 280 return parameter; 281 } 282 283 /** 284 * @param name The name of the parameter. Not empty. 285 * @param expression The ECMAScript expression of the parameter. Not 286 * null. 287 * @return The newly created object parameter 288 */ 289 public static Parameter createWithExpression(String name, String expression) { 290 Assert.notEmpty(name, "name"); 291 Assert.notNull(expression, "expression"); 292 293 Parameter parameter = new Parameter(name); 294 parameter.mExpression = expression; 295 return parameter; 296 } 297 298 /** 299 * @param name The name of the parameter. Not empty. 300 * @param json The JSON value of the parameter. Not null. 301 * @return The newly created object parameter 302 */ 303 public static Parameter createWithJson(String name, JsonValue json) { 304 Assert.notEmpty(name, "name"); 305 Assert.notNull(json, "json"); 306 307 return createWithExpression(name, json.toString()); 308 } 309 310 private Parameter(String name) { 311 mName = name; 312 } 313 314 /** 315 * @param valueType One of {@link ParameterValueType#data} or 316 * {@link ParameterValueType#ref}. Indicates to an object if 317 * the value associated with name is data or a URI. 318 * <code>null</code> to use the VoiceXML platform default. 319 */ 320 public void setValueType(ParameterValueType valueType) { 321 mValueType = valueType; 322 } 323 324 /** 325 * @param type The media type of the result provided by a URI if the 326 * value type is {@link ParameterValueType#ref}. 327 * <code>null</code> to use the VoiceXML platform default 328 */ 329 public void setType(String type) { 330 mType = type; 331 } 332 333 public String getName() { 334 return mName; 335 } 336 337 public String getExpression() { 338 return mExpression; 339 } 340 341 public String getValue() { 342 return mValue; 343 } 344 345 public ParameterValueType getValueType() { 346 return mValueType; 347 } 348 349 public String getType() { 350 return mType; 351 } 352 353 @Override 354 public JsonValue asJson() { 355 JsonObjectBuilder builder = JsonUtils.createObjectBuilder(); 356 JsonUtils.add(builder, NAME_PROPERTY, mName); 357 JsonUtils.add(builder, EXPRESSION_PROPERTY, mExpression); 358 JsonUtils.add(builder, VALUE_PROPERTY, mValue); 359 JsonUtils.add(builder, TYPE_PROPERTY, mType); 360 JsonUtils.add(builder, VALUE_TYPE_PROPERTY, mValueType); 361 return builder.build(); 362 } 363 } 364 365 /** 366 * Types of parameter for <object>. 367 * 368 * @author Nu Echo Inc. 369 */ 370 public enum ParameterValueType implements JsonSerializable { 371 data, ref; 372 373 @Override 374 public JsonValue asJson() { 375 return JsonUtils.wrap(name()); 376 } 377 } 378 379 /** 380 * Builder used to ease the creation of instances of {@link ObjectCall}. 381 */ 382 public static class Builder { 383 384 private final String mName; 385 private String mClassId; 386 private String mCodeBase; 387 private String mCodeType; 388 private String mData; 389 private String mType; 390 private final List<String> mArchives = new ArrayList<String>(); 391 private final List<Parameter> mParameters = new ArrayList<Parameter>(); 392 private FetchConfiguration mFetchConfiguration; 393 private String mPostObjectScript; 394 395 public Builder(String name) { 396 mName = name; 397 } 398 399 public Builder setClassId(String classId) { 400 Assert.notNull(classId, "classId"); 401 mClassId = classId; 402 return this; 403 } 404 405 public Builder setCodeBase(String codeBase) { 406 mCodeBase = codeBase; 407 return this; 408 } 409 410 public Builder setData(String data) { 411 mData = data; 412 return this; 413 } 414 415 public Builder setType(String type) { 416 mType = type; 417 return this; 418 } 419 420 public Builder setCodeType(String codeType) { 421 mCodeType = codeType; 422 return this; 423 } 424 425 public Builder setFetchConfiguration(FetchConfiguration fetchConfiguration) { 426 mFetchConfiguration = fetchConfiguration; 427 return this; 428 } 429 430 public Builder addArchive(String archive) { 431 Assert.notNull(archive, "archive"); 432 mArchives.add(archive); 433 return this; 434 } 435 436 public Builder addParameter(Parameter parameter) { 437 Assert.notNull(parameter, "parameter"); 438 mParameters.add(parameter); 439 return this; 440 } 441 442 public Builder setPostObjectScript(String postObjectScript) { 443 mPostObjectScript = postObjectScript; 444 return this; 445 } 446 447 public ObjectCall build() { 448 ObjectCall objectCall = new ObjectCall(mName); 449 objectCall.setArchives(mArchives); 450 objectCall.setClassId(mClassId); 451 objectCall.setCodeBase(mCodeBase); 452 objectCall.setCodeType(mCodeType); 453 objectCall.setData(mData); 454 objectCall.setFetchConfiguration(mFetchConfiguration); 455 objectCall.setParameters(mParameters); 456 objectCall.setPostObjectScript(mPostObjectScript); 457 objectCall.setType(mType); 458 return objectCall; 459 } 460 } 461 462 @Override 463 public int hashCode() { 464 final int prime = 31; 465 int result = super.hashCode(); 466 result = prime * result + ((mArchives == null) ? 0 : mArchives.hashCode()); 467 result = prime * result + ((mClassId == null) ? 0 : mClassId.hashCode()); 468 result = prime * result + ((mCodeBase == null) ? 0 : mCodeBase.hashCode()); 469 result = prime * result + ((mCodeType == null) ? 0 : mCodeType.hashCode()); 470 result = prime * result + ((mData == null) ? 0 : mData.hashCode()); 471 result = prime * result + ((mFetchConfiguration == null) ? 0 : mFetchConfiguration.hashCode()); 472 result = prime * result + ((mParameters == null) ? 0 : mParameters.hashCode()); 473 result = prime * result + ((mPostObjectScript == null) ? 0 : mPostObjectScript.hashCode()); 474 result = prime * result + ((mType == null) ? 0 : mType.hashCode()); 475 return result; 476 } 477 478 @Override 479 public boolean equals(Object obj) { 480 if (this == obj) return true; 481 if (!super.equals(obj)) return false; 482 if (getClass() != obj.getClass()) return false; 483 ObjectCall other = (ObjectCall) obj; 484 if (mArchives == null) { 485 if (other.mArchives != null) return false; 486 } else if (!mArchives.equals(other.mArchives)) return false; 487 if (mClassId == null) { 488 if (other.mClassId != null) return false; 489 } else if (!mClassId.equals(other.mClassId)) return false; 490 if (mCodeBase == null) { 491 if (other.mCodeBase != null) return false; 492 } else if (!mCodeBase.equals(other.mCodeBase)) return false; 493 if (mCodeType == null) { 494 if (other.mCodeType != null) return false; 495 } else if (!mCodeType.equals(other.mCodeType)) return false; 496 if (mData == null) { 497 if (other.mData != null) return false; 498 } else if (!mData.equals(other.mData)) return false; 499 if (mFetchConfiguration == null) { 500 if (other.mFetchConfiguration != null) return false; 501 } else if (!mFetchConfiguration.equals(other.mFetchConfiguration)) return false; 502 if (mParameters == null) { 503 if (other.mParameters != null) return false; 504 } else if (!mParameters.equals(other.mParameters)) return false; 505 if (mPostObjectScript == null) { 506 if (other.mPostObjectScript != null) return false; 507 } else if (!mPostObjectScript.equals(other.mPostObjectScript)) return false; 508 if (mType == null) { 509 if (other.mType != null) return false; 510 } else if (!mType.equals(other.mType)) return false; 511 return true; 512 } 513 514}