📄 jid.java
字号:
/**
* $RCSfile$
* $Revision: 3420 $
* $Date: 2006-02-08 18:28:28 -0800 (Wed, 08 Feb 2006) $
*
* Copyright 2004 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.xmpp.packet;
import org.jivesoftware.stringprep.IDNA;
import org.jivesoftware.stringprep.Stringprep;
import java.io.Serializable;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* An XMPP address (JID). A JID is made up of a node (generally a username), a domain,
* and a resource. The node and resource are optional; domain is required. In simple
* ABNF form:
*
* <ul><tt>jid = [ node "@" ] domain [ "/" resource ]</tt></ul>
*
* Some sample JID's:
* <ul>
* <li><tt>user@example.com</tt></li>
* <li><tt>user@example.com/home</tt></li>
* <li><tt>example.com</tt></li>
* </ul>
*
* Each allowable portion of a JID (node, domain, and resource) must not be more
* than 1023 bytes in length, resulting in a maximum total size (including the '@'
* and '/' separators) of 3071 bytes.
*
* @author Matt Tucker
*/
public class JID implements Comparable, Serializable {
// Stringprep operations are very expensive. Therefore, we cache node, domain and
// resource values that have already had stringprep applied so that we can check
// incoming values against the cache.
private static Map stringprepCache = Collections.synchronizedMap(new Cache(1000));
private String node;
private String domain;
private String resource;
private String cachedFullJID;
private String cachedBareJID;
/**
* Escapes the node portion of a JID according to "JID Escaping" (JEP-0106).
* Escaping replaces characters prohibited by node-prep with escape sequences,
* as follows:<p>
*
* <table border="1">
* <tr><td><b>Unescaped Character</b></td><td><b>Encoded Sequence</b></td></tr>
* <tr><td><space></td><td>\20</td></tr>
* <tr><td>"</td><td>\22</td></tr>
* <tr><td>&</td><td>\26</td></tr>
* <tr><td>'</td><td>\27</td></tr>
* <tr><td>/</td><td>\2f</td></tr>
* <tr><td>:</td><td>\3a</td></tr>
* <tr><td><</td><td>\3c</td></tr>
* <tr><td>></td><td>\3e</td></tr>
* <tr><td>@</td><td>\40</td></tr>
* <tr><td>\</td><td>\5c</td></tr>
* </table><p>
*
* This process is useful when the node comes from an external source that doesn't
* conform to nodeprep. For example, a username in LDAP may be "Joe Smith". Because
* the <space> character isn't a valid part of a node, the username should
* be escaped to "Joe\20Smith" before being made into a JID (e.g. "joe\20smith@example.com"
* after case-folding, etc. has been applied).<p>
*
* All node escaping and un-escaping must be performed manually at the appropriate
* time; the JID class will not escape or un-escape automatically.
*
* @param node the node.
* @return the escaped version of the node.
*/
public static String escapeNode(String node) {
if (node == null) {
return null;
}
StringBuilder buf = new StringBuilder(node.length() + 8);
for (int i=0, n=node.length(); i<n; i++) {
char c = node.charAt(i);
switch (c) {
case '"': buf.append("\\22"); break;
case '&': buf.append("\\26"); break;
case '\'': buf.append("\\27"); break;
case '/': buf.append("\\2f"); break;
case ':': buf.append("\\3a"); break;
case '<': buf.append("\\3c"); break;
case '>': buf.append("\\3e"); break;
case '@': buf.append("\\40"); break;
case '\\': buf.append("\\5c"); break;
default: {
if (Character.isWhitespace(c)) {
buf.append("\\20");
}
else {
buf.append(c);
}
}
}
}
return buf.toString();
}
/**
* Un-escapes the node portion of a JID according to "JID Escaping" (JEP-0106).<p>
* Escaping replaces characters prohibited by node-prep with escape sequences,
* as follows:<p>
*
* <table border="1">
* <tr><td><b>Unescaped Character</b></td><td><b>Encoded Sequence</b></td></tr>
* <tr><td><space></td><td>\20</td></tr>
* <tr><td>"</td><td>\22</td></tr>
* <tr><td>&</td><td>\26</td></tr>
* <tr><td>'</td><td>\27</td></tr>
* <tr><td>/</td><td>\2f</td></tr>
* <tr><td>:</td><td>\3a</td></tr>
* <tr><td><</td><td>\3c</td></tr>
* <tr><td>></td><td>\3e</td></tr>
* <tr><td>@</td><td>\40</td></tr>
* <tr><td>\</td><td>\5c</td></tr>
* </table><p>
*
* This process is useful when the node comes from an external source that doesn't
* conform to nodeprep. For example, a username in LDAP may be "Joe Smith". Because
* the <space> character isn't a valid part of a node, the username should
* be escaped to "Joe\20Smith" before being made into a JID (e.g. "joe\20smith@example.com"
* after case-folding, etc. has been applied).<p>
*
* All node escaping and un-escaping must be performed manually at the appropriate
* time; the JID class will not escape or un-escape automatically.
*
* @param node the escaped version of the node.
* @return the un-escaped version of the node.
*/
public static String unescapeNode(String node) {
if (node == null) {
return null;
}
char [] nodeChars = node.toCharArray();
StringBuilder buf = new StringBuilder(nodeChars.length);
for (int i=0, n=nodeChars.length; i<n; i++) {
compare: {
char c = node.charAt(i);
if (c == '\\' && i+2<n) {
char c2 = nodeChars[i+1];
char c3 = nodeChars[i+2];
if (c2 == '2') {
switch (c3) {
case '0': buf.append(' '); i+=2; break compare;
case '2': buf.append('"'); i+=2; break compare;
case '6': buf.append('&'); i+=2; break compare;
case '7': buf.append('\''); i+=2; break compare;
case 'f': buf.append('/'); i+=2; break compare;
}
}
else if (c2 == '3') {
switch (c3) {
case 'a': buf.append(':'); i+=2; break compare;
case 'c': buf.append('<'); i+=2; break compare;
case 'e': buf.append('>'); i+=2; break compare;
}
}
else if (c2 == '4') {
if (c3 == '0') {
buf.append("@");
i+=2;
break compare;
}
}
else if (c2 == '5') {
if (c3 == 'c') {
buf.append("\\");
i+=2;
break compare;
}
}
}
buf.append(c);
}
}
return buf.toString();
}
/**
* Constructs a JID from it's String representation.
*
* @param jid a valid JID.
* @throws IllegalArgumentException if the JID is not valid.
*/
public JID(String jid) {
if (jid == null) {
throw new NullPointerException("JID cannot be null");
}
String node = null;
String domain = null;
String resource = null;
int atIndex = jid.indexOf("@");
int slashIndex = jid.indexOf("/");
// Node
if (atIndex > 0) {
node = jid.substring(0, atIndex);
}
// Domain
if (atIndex + 1 > jid.length()) {
throw new IllegalArgumentException("JID with empty domain not valid");
}
if (atIndex < 0) {
if (slashIndex > 0) {
domain = jid.substring(0, slashIndex);
}
else {
domain = jid;
}
}
else {
if (slashIndex > 0) {
domain = jid.substring(atIndex + 1, slashIndex);
}
else {
domain = jid.substring(atIndex + 1);
}
}
// Resource
if (slashIndex + 1 > jid.length() || slashIndex < 0) {
resource = null;
}
else {
resource = jid.substring(slashIndex + 1);
}
init(node, domain,resource);
}
/**
* Constructs a JID given a node, domain, and resource.
*
* @param node the node.
* @param domain the domain, which must not be <tt>null</tt>.
* @param resource the resource.
* @throws IllegalArgumentException if the JID is not valid.
*/
public JID(String node, String domain, String resource) {
if (domain == null) {
throw new NullPointerException("Domain cannot be null");
}
init(node, domain, resource);
}
/**
* Constructs a new JID, bypassing all stringprep profiles. This
* is useful for constructing a JID object when it's already known
* that the String representation is well-formed.
*
* @param jid the JID.
* @param fake an extra param to create a different method signature.
* The value <tt>null</tt> should be passed in as this argument.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -