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