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 &lt;object&gt;.
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}