001/**
002 *
003 * Copyright 2003-2007 Jive Software, 2014-2017 Florian Schmaus
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.jivesoftware.smackx.jingle.element;
019
020import java.util.ArrayList;
021import java.util.Collections;
022import java.util.List;
023
024import org.jivesoftware.smack.packet.IQ;
025import org.jivesoftware.smack.util.Objects;
026import org.jivesoftware.smack.util.StringUtils;
027
028import org.jxmpp.jid.FullJid;
029
030/**
031 * The Jingle element.
032 *
033 * @author Florian Schmaus
034 */
035public final class Jingle extends IQ {
036
037    public static final String NAMESPACE = "urn:xmpp:jingle:1";
038
039    public static final String ACTION_ATTRIBUTE_NAME = "action";
040
041    public static final String INITIATOR_ATTRIBUTE_NAME = "initiator";
042
043    public static final String RESPONDER_ATTRIBUTE_NAME = "responder";
044
045    public static final String SESSION_ID_ATTRIBUTE_NAME = "sid";
046
047    public static final String ELEMENT = "jingle";
048
049    /**
050     * The session ID related to this session. The session ID is a unique identifier generated by the initiator. This
051     * should match the XML Nmtoken production so that XML character escaping is not needed for characters such as &.
052     */
053    private final String sessionId;
054
055    /**
056     * The jingle action. This attribute is required.
057     */
058    private final JingleAction action;
059
060    private final FullJid initiator;
061
062    private final FullJid responder;
063
064    private final JingleReason reason;
065
066    private final List<JingleContent> contents;
067
068    private Jingle(String sessionId, JingleAction action, FullJid initiator, FullJid responder, JingleReason reason,
069                    List<JingleContent> contents) {
070        super(ELEMENT, NAMESPACE);
071        this.sessionId = StringUtils.requireNotNullOrEmpty(sessionId, "Jingle session ID must not be null");
072        this.action = Objects.requireNonNull(action, "Jingle action must not be null");
073        this.initiator = initiator;
074        this.responder = responder;
075        this.reason = reason;
076        if (contents != null) {
077            this.contents = Collections.unmodifiableList(contents);
078        }
079        else {
080            this.contents = Collections.emptyList();
081        }
082        setType(Type.set);
083    }
084
085    /**
086     * Get the initiator. The initiator will be the full JID of the entity that has initiated the flow (which may be
087     * different to the "from" address in the IQ)
088     *
089     * @return the initiator
090     */
091    public FullJid getInitiator() {
092        return initiator;
093    }
094
095    /**
096     * Get the responder. The responder is the full JID of the entity that has replied to the initiation (which may be
097     * different to the "to" address in the IQ).
098     *
099     * @return the responder
100     */
101    public FullJid getResponder() {
102        return responder;
103    }
104
105    /**
106     * Returns the session ID related to the session. The session ID is a unique identifier generated by the initiator.
107     * This should match the XML Nmtoken production so that XML character escaping is not needed for characters such as
108     * &amp;.
109     *
110     * @return Returns the session ID related to the session.
111     */
112    public String getSid() {
113        return sessionId;
114    }
115
116    /**
117     * Get the action specified in the jingle IQ.
118     *
119     * @return the action.
120     */
121    public JingleAction getAction() {
122        return action;
123    }
124
125    public JingleReason getReason() {
126        return reason;
127    }
128
129    /**
130     * Get a List of the contents.
131     *
132     * @return the contents.
133     */
134    public List<JingleContent> getContents() {
135        return contents;
136    }
137
138    /**
139     * Get the only jingle content if one exists, or <code>null</code>. This method will throw an
140     * {@link IllegalStateException} if there is more than one jingle content.
141     *
142     * @return a JingleContent instance or <code>null</code>.
143     * @throws IllegalStateException if there is more than one jingle content.
144     */
145    public JingleContent getSoleContentOrThrow() {
146        if (contents.isEmpty()) {
147            return null;
148        }
149
150        if (contents.size() > 1) {
151            throw new IllegalStateException();
152        }
153
154        return contents.get(0);
155    }
156
157    @Override
158    protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
159        xml.optAttribute(INITIATOR_ATTRIBUTE_NAME, getInitiator());
160        xml.optAttribute(RESPONDER_ATTRIBUTE_NAME, getResponder());
161        xml.optAttribute(ACTION_ATTRIBUTE_NAME, getAction());
162        xml.optAttribute(SESSION_ID_ATTRIBUTE_NAME, getSid());
163        xml.rightAngleBracket();
164
165        xml.optElement(reason);
166
167        xml.append(contents);
168
169        return xml;
170    }
171
172    public static Builder getBuilder() {
173        return new Builder();
174    }
175
176    public static final class Builder {
177        private String sid;
178
179        private JingleAction action;
180
181        private FullJid initiator;
182
183        private FullJid responder;
184
185        private JingleReason reason;
186
187        private List<JingleContent> contents;
188
189        private Builder() {
190        }
191
192        public Builder setSessionId(String sessionId) {
193            StringUtils.requireNotNullOrEmpty(sessionId, "Session ID must not be null or empty");
194            this.sid = sessionId;
195            return this;
196        }
197
198        public Builder setAction(JingleAction action) {
199            this.action = action;
200            return this;
201        }
202
203        public Builder setInitiator(FullJid initator) {
204            this.initiator = initator;
205            return this;
206        }
207
208        public Builder setResponder(FullJid responder) {
209            this.responder = responder;
210            return this;
211        }
212
213        public Builder addJingleContent(JingleContent content) {
214            if (contents == null) {
215                contents = new ArrayList<>(1);
216            }
217            contents.add(content);
218            return this;
219        }
220
221        public Builder setReason(JingleReason.Reason reason) {
222            this.reason = new JingleReason(reason);
223            return this;
224        }
225
226        public Builder setReason(JingleReason reason) {
227            this.reason = reason;
228            return this;
229        }
230
231        public Jingle build() {
232            return new Jingle(sid, action, initiator, responder, reason, contents);
233        }
234    }
235}