001/**
002 *
003 * Copyright 2018 Paul Schaub
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 */
017package org.jivesoftware.smackx.reference.element;
018
019import java.net.URI;
020import java.net.URISyntaxException;
021import java.util.ArrayList;
022import java.util.List;
023
024import org.jivesoftware.smack.packet.ExtensionElement;
025import org.jivesoftware.smack.packet.Stanza;
026import org.jivesoftware.smack.util.Objects;
027import org.jivesoftware.smack.util.XmlStringBuilder;
028
029import org.jivesoftware.smackx.reference.ReferenceManager;
030
031import org.jxmpp.jid.BareJid;
032
033public class ReferenceElement implements ExtensionElement {
034
035    public static final String ELEMENT = "reference";
036    public static final String ATTR_BEGIN = "begin";
037    public static final String ATTR_END = "end";
038    public static final String ATTR_TYPE = "type";
039    public static final String ATTR_ANCHOR = "anchor";
040    public static final String ATTR_URI = "uri";
041
042    public enum Type {
043        mention,
044        data
045    }
046
047    private final Integer begin;
048    private final Integer end;
049    private final Type type;
050    private final String anchor;
051    private final URI uri;
052
053    // Non-XEP-compliant, but needed for SIMS
054    private final ExtensionElement child;
055
056    /**
057     * XEP-incompliant (v0.2) constructor. This is needed for SIMS.
058     *
059     * @param begin TODO javadoc me please
060     * @param end TODO javadoc me please
061     * @param type TODO javadoc me please
062     * @param anchor TODO javadoc me please
063     * @param uri TODO javadoc me please
064     * @param child TODO javadoc me please
065     */
066    public ReferenceElement(Integer begin, Integer end, Type type, String anchor, URI uri, ExtensionElement child) {
067        if (begin != null && begin < 0) {
068            throw new IllegalArgumentException("Attribute 'begin' MUST NOT be smaller than 0.");
069        }
070        if (end != null && end < 0) {
071            throw new IllegalArgumentException("Attribute 'end' MUST NOT be smaller than 0.");
072        }
073        if (begin != null && end != null && begin >= end) {
074            throw new IllegalArgumentException("Attribute 'begin' MUST be smaller than attribute 'end'.");
075        }
076        Objects.requireNonNull(type);
077        // TODO: The uri attribute is not mandatory according to SIMS, but it is according to references.
078        /*if (uri == null) {
079            throw new NullPointerException("Attribute 'uri' MUST NOT be null.");
080        }*/
081        this.begin = begin;
082        this.end = end;
083        this.type = type;
084        this.anchor = anchor;
085        this.uri = uri;
086        this.child = child;
087    }
088
089    /**
090     * XEP-Compliant constructor.
091     *
092     * @param begin TODO javadoc me please
093     * @param end TODO javadoc me please
094     * @param type TODO javadoc me please
095     * @param anchor TODO javadoc me please
096     * @param uri TODO javadoc me please
097     */
098    public ReferenceElement(Integer begin, Integer end, Type type, String anchor, URI uri) {
099        this(begin, end, type, anchor, uri, null);
100    }
101
102    public Integer getBegin() {
103        return begin;
104    }
105
106    public Integer getEnd() {
107        return end;
108    }
109
110    public Type getType() {
111        return type;
112    }
113
114    public String getAnchor() {
115        return anchor;
116    }
117
118    public URI getUri() {
119        return uri;
120    }
121
122    /**
123     * Add a reference to another users bare jid to a stanza.
124     *
125     * @param stanza stanza.
126     * @param begin start index of the mention in the messages body.
127     * @param end end index of the mention in the messages body.
128     * @param jid referenced jid.
129     */
130    public static void addMention(Stanza stanza, int begin, int end, BareJid jid) {
131        URI uri;
132        try {
133            uri = new URI("xmpp:" + jid.toString());
134        } catch (URISyntaxException e) {
135            throw new AssertionError("Cannot create URI from bareJid.");
136        }
137        ReferenceElement reference = new ReferenceElement(begin, end, ReferenceElement.Type.mention, null, uri);
138        stanza.addExtension(reference);
139    }
140
141    /**
142     * Return a list of all reference extensions contained in a stanza.
143     * If there are no reference elements, return an empty list.
144     *
145     * @param stanza stanza
146     * @return list of all references contained in the stanza
147     */
148    public static List<ReferenceElement> getReferencesFromStanza(Stanza stanza) {
149        List<ReferenceElement> references = new ArrayList<>();
150        List<ExtensionElement> extensions = stanza.getExtensions(ReferenceElement.ELEMENT, ReferenceManager.NAMESPACE);
151        for (ExtensionElement e : extensions) {
152            references.add((ReferenceElement) e);
153        }
154        return references;
155    }
156
157    /**
158     * Return true, if the stanza contains at least one reference extension.
159     *
160     * @param stanza stanza
161     * @return true if stanza contains references
162     */
163    public static boolean containsReferences(Stanza stanza) {
164        return getReferencesFromStanza(stanza).size() > 0;
165    }
166
167    @Override
168    public String getNamespace() {
169        return ReferenceManager.NAMESPACE;
170    }
171
172    @Override
173    public String getElementName() {
174        return ELEMENT;
175    }
176
177    @Override
178    public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
179        XmlStringBuilder xml = new XmlStringBuilder(this)
180                .optIntAttribute(ATTR_BEGIN, begin != null ? begin : -1)
181                .optIntAttribute(ATTR_END, end != null ? end : -1)
182                .attribute(ATTR_TYPE, type.toString())
183                .optAttribute(ATTR_ANCHOR, anchor)
184                .optAttribute(ATTR_URI, uri != null ? uri.toString() : null);
185
186        if (child == null) {
187            return xml.closeEmptyElement();
188        } else {
189            return xml.rightAngleBracket()
190                    .append(child.toXML())
191                    .closeElement(this);
192        }
193    }
194}