001/** 002 * 003 * Copyright 2014-2024 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 */ 017package org.jivesoftware.smack.packet; 018 019import java.util.ArrayList; 020import java.util.Collections; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Locale; 024import java.util.Map; 025 026import org.jivesoftware.smack.util.ExceptionUtil; 027import org.jivesoftware.smack.util.Objects; 028import org.jivesoftware.smack.util.PacketUtil; 029import org.jivesoftware.smack.util.XmlStringBuilder; 030 031public class AbstractError { 032 033 protected final String textNamespace; 034 protected final Map<String, String> descriptiveTexts; 035 protected final List<XmlElement> extensions; 036 037 038 protected AbstractError(Map<String, String> descriptiveTexts) { 039 this(descriptiveTexts, null); 040 } 041 042 protected AbstractError(Map<String, String> descriptiveTexts, List<XmlElement> extensions) { 043 this(descriptiveTexts, null, extensions); 044 } 045 046 protected AbstractError(Map<String, String> descriptiveTexts, String textNamespace, List<XmlElement> extensions) { 047 if (descriptiveTexts != null) { 048 this.descriptiveTexts = descriptiveTexts; 049 } else { 050 this.descriptiveTexts = Collections.emptyMap(); 051 } 052 this.textNamespace = textNamespace; 053 if (extensions != null) { 054 this.extensions = extensions; 055 } else { 056 this.extensions = Collections.emptyList(); 057 } 058 } 059 060 /** 061 * Get the descriptive text of this SASLFailure. 062 * <p> 063 * Returns the descriptive text of this SASLFailure in the system default language if possible. May return null. 064 * </p> 065 * 066 * @return the descriptive text or null. 067 */ 068 public String getDescriptiveText() { 069 if (descriptiveTexts.isEmpty()) 070 return null; 071 // attempt to obtain the text in the user's locale, the English text, or the "" default 072 Locale l = Locale.getDefault(); 073 String[] tags = new String[] { 074 l.getLanguage() + "-" + l.getCountry() + "-" + l.getVariant(), 075 l.getLanguage() + "-" + l.getCountry(), 076 l.getLanguage(), 077 "en", 078 "" 079 }; 080 for (String tag : tags) { 081 String descriptiveText = getDescriptiveText(tag); 082 if (descriptiveText != null) 083 return descriptiveText; 084 } 085 return descriptiveTexts.values().iterator().next(); 086 } 087 088 /** 089 * Get the descriptive test of this SASLFailure. 090 * <p> 091 * Returns the descriptive text of this SASLFailure in the given language. May return null if not available. 092 * </p> 093 * 094 * @param xmllang the language. 095 * @return the descriptive text or null. 096 */ 097 public String getDescriptiveText(String xmllang) { 098 Objects.requireNonNull(xmllang, "xmllang must not be null"); 099 return descriptiveTexts.get(xmllang); 100 } 101 102 /** 103 * Returns the first stanza extension that matches the specified element name and 104 * namespace, or <code>null</code> if it doesn't exist. 105 * 106 * @param elementName the XML element name of the stanza extension. 107 * @param namespace the XML element namespace of the stanza extension. 108 * @param <PE> type of the ExtensionElement. 109 * @return the extension, or <code>null</code> if it doesn't exist. 110 */ 111 @SuppressWarnings("TypeParameterUnusedInFormals") 112 public <PE extends XmlElement> PE getExtension(String elementName, String namespace) { 113 return PacketUtil.extensionElementFrom(extensions, elementName, namespace); 114 } 115 116 protected void addDescriptiveTextsAndExtensions(XmlStringBuilder xml) { 117 for (Map.Entry<String, String> entry : descriptiveTexts.entrySet()) { 118 String xmllang = entry.getKey(); 119 String text = entry.getValue(); 120 xml.halfOpenElement("text").xmlnsAttribute(textNamespace) 121 .optXmlLangAttribute(xmllang) 122 .rightAngleBracket(); 123 xml.escape(text); 124 xml.closeElement("text"); 125 } 126 xml.append(extensions); 127 } 128 129 public abstract static class Builder<B extends Builder<B>> { 130 protected String textNamespace; 131 protected Map<String, String> descriptiveTexts; 132 protected List<XmlElement> extensions; 133 134 public B setDescriptiveTexts(Map<String, String> descriptiveTexts) { 135 if (descriptiveTexts == null) { 136 this.descriptiveTexts = null; 137 return getThis(); 138 } 139 for (String key : descriptiveTexts.keySet()) { 140 if (key == null) { 141 throw new IllegalArgumentException("descriptiveTexts cannot contain null key"); 142 } 143 } 144 if (this.descriptiveTexts == null) { 145 this.descriptiveTexts = descriptiveTexts; 146 } 147 else { 148 this.descriptiveTexts.putAll(descriptiveTexts); 149 } 150 return getThis(); 151 } 152 153 public B setDescriptiveEnText(String descriptiveEnText) { 154 if (descriptiveTexts == null) { 155 descriptiveTexts = new HashMap<>(); 156 } 157 descriptiveTexts.put("en", descriptiveEnText); 158 return getThis(); 159 } 160 161 public B setDescriptiveEnText(String descriptiveEnText, Exception exception) { 162 StringBuilder sb = new StringBuilder(512); 163 sb.append(descriptiveEnText) 164 .append('\n'); 165 166 String stacktrace = ExceptionUtil.getStackTrace(exception); 167 sb.append(stacktrace); 168 169 return setDescriptiveEnText(sb.toString()); 170 } 171 172 public B setTextNamespace(String textNamespace) { 173 this.textNamespace = textNamespace; 174 return getThis(); 175 } 176 177 public B setExtensions(List<XmlElement> extensions) { 178 if (this.extensions == null) { 179 this.extensions = extensions; 180 } 181 else { 182 this.extensions.addAll(extensions); 183 } 184 return getThis(); 185 } 186 187 public B addExtension(XmlElement extension) { 188 if (extensions == null) { 189 extensions = new ArrayList<>(); 190 } 191 extensions.add(extension); 192 return getThis(); 193 } 194 195 protected abstract B getThis(); 196 } 197}