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