items;
/**
* Constructor
*/
public Argument() {
items = new ArrayList<>(1);
}
/**
* Append the given Argument to this Argument. All items
* from the source argument are copied into this destination
* argument.
*
* @param arg the Argument to append
* @return this
*/
public Argument append(Argument arg) {
items.addAll(arg.items);
return this;
}
/**
* Write out given string as an ASTRING, depending on the type
* of the characters inside the string. The string should
* contain only ASCII characters.
*
* XXX: Hmm .. this should really be called writeASCII()
*
* @param s String to write out
* @return this
*/
public Argument writeString(String s) {
items.add(new AString(ASCIIUtility.getBytes(s)));
return this;
}
/**
* Convert the given string into bytes in the specified
* charset, and write the bytes out as an ASTRING
*
* @param s String to write out
* @param charset the charset
* @return this
* @exception UnsupportedEncodingException for bad charset
*/
public Argument writeString(String s, String charset)
throws UnsupportedEncodingException {
if (charset == null) // convenience
writeString(s);
else
items.add(new AString(s.getBytes(charset)));
return this;
}
/**
* Convert the given string into bytes in the specified
* charset, and write the bytes out as an ASTRING
*
* @param s String to write out
* @param charset the charset
* @return this
* @since JavaMail 1.6.0
*/
public Argument writeString(String s, Charset charset) {
if (charset == null) // convenience
writeString(s);
else
items.add(new AString(s.getBytes(charset)));
return this;
}
/**
* Write out given string as an NSTRING, depending on the type
* of the characters inside the string. The string should
* contain only ASCII characters.
*
* @param s String to write out
* @return this
* @since JavaMail 1.5.1
*/
public Argument writeNString(String s) {
if (s == null)
items.add(new NString(null));
else
items.add(new NString(ASCIIUtility.getBytes(s)));
return this;
}
/**
* Convert the given string into bytes in the specified
* charset, and write the bytes out as an NSTRING
*
* @param s String to write out
* @param charset the charset
* @return this
* @exception UnsupportedEncodingException for bad charset
* @since JavaMail 1.5.1
*/
public Argument writeNString(String s, String charset)
throws UnsupportedEncodingException {
if (s == null)
items.add(new NString(null));
else if (charset == null) // convenience
writeString(s);
else
items.add(new NString(s.getBytes(charset)));
return this;
}
/**
* Convert the given string into bytes in the specified
* charset, and write the bytes out as an NSTRING
*
* @param s String to write out
* @param charset the charset
* @return this
* @since JavaMail 1.6.0
*/
public Argument writeNString(String s, Charset charset) {
if (s == null)
items.add(new NString(null));
else if (charset == null) // convenience
writeString(s);
else
items.add(new NString(s.getBytes(charset)));
return this;
}
/**
* Write out given byte[] as a Literal.
* @param b byte[] to write out
* @return this
*/
public Argument writeBytes(byte[] b) {
items.add(b);
return this;
}
/**
* Write out given ByteArrayOutputStream as a Literal.
* @param b ByteArrayOutputStream to be written out.
* @return this
*/
public Argument writeBytes(ByteArrayOutputStream b) {
items.add(b);
return this;
}
/**
* Write out given data as a literal.
* @param b Literal representing data to be written out.
* @return this
*/
public Argument writeBytes(Literal b) {
items.add(b);
return this;
}
/**
* Write out given string as an Atom. Note that an Atom can contain only
* certain US-ASCII characters. No validation is done on the characters
* in the string.
* @param s String
* @return this
*/
public Argument writeAtom(String s) {
items.add(new Atom(s));
return this;
}
/**
* Write out number.
* @param i number
* @return this
*/
public Argument writeNumber(int i) {
items.add(Integer.valueOf(i));
return this;
}
/**
* Write out number.
* @param i number
* @return this
*/
public Argument writeNumber(long i) {
items.add(Long.valueOf(i));
return this;
}
/**
* Write out as parenthesised list.
*
* @param c the Argument
* @return this
*/
public Argument writeArgument(Argument c) {
items.add(c);
return this;
}
/*
* Write out all the buffered items into the output stream.
*/
public void write(Protocol protocol)
throws IOException, ProtocolException {
int size = items != null ? items.size() : 0;
DataOutputStream os = (DataOutputStream)protocol.getOutputStream();
for (int i=0; i < size; i++) {
if (i > 0) // write delimiter if not the first item
os.write(' ');
Object o = items.get(i);
if (o instanceof Atom) {
os.writeBytes(((Atom)o).string);
} else if (o instanceof Number) {
os.writeBytes(((Number)o).toString());
} else if (o instanceof AString) {
astring(((AString)o).bytes, protocol);
} else if (o instanceof NString) {
nstring(((NString)o).bytes, protocol);
} else if (o instanceof byte[]) {
literal((byte[])o, protocol);
} else if (o instanceof ByteArrayOutputStream) {
literal((ByteArrayOutputStream)o, protocol);
} else if (o instanceof Literal) {
literal((Literal)o, protocol);
} else if (o instanceof Argument) {
os.write('('); // open parans
((Argument)o).write(protocol);
os.write(')'); // close parans
}
}
}
/**
* Write out given String as either an Atom, QuotedString or Literal
*/
private void astring(byte[] bytes, Protocol protocol)
throws IOException, ProtocolException {
nastring(bytes, protocol, false);
}
/**
* Write out given String as either NIL, QuotedString, or Literal.
*/
private void nstring(byte[] bytes, Protocol protocol)
throws IOException, ProtocolException {
if (bytes == null) {
DataOutputStream os = (DataOutputStream)protocol.getOutputStream();
os.writeBytes("NIL");
} else
nastring(bytes, protocol, true);
}
private void nastring(byte[] bytes, Protocol protocol, boolean doQuote)
throws IOException, ProtocolException {
DataOutputStream os = (DataOutputStream)protocol.getOutputStream();
int len = bytes.length;
// If length is greater than 1024 bytes, send as literal
if (len > 1024) {
literal(bytes, protocol);
return;
}
// if 0 length, send as quoted-string
boolean quote = len == 0 ? true : doQuote;
boolean escape = false;
boolean utf8 = protocol.supportsUtf8();
byte b;
for (int i = 0; i < len; i++) {
b = bytes[i];
if (b == '\0' || b == '\r' || b == '\n' ||
(!utf8 && ((b & 0xff) > 0177))) {
// NUL, CR or LF means the bytes need to be sent as literals
literal(bytes, protocol);
return;
}
if (b == '*' || b == '%' || b == '(' || b == ')' || b == '{' ||
b == '"' || b == '\\' ||
((b & 0xff) <= ' ') || ((b & 0xff) > 0177)) {
quote = true;
if (b == '"' || b == '\\') // need to escape these characters
escape = true;
}
}
/*
* Make sure the (case-independent) string "NIL" is always quoted,
* so as not to be confused with a real NIL (handled above in nstring).
* This is more than is necessary, but it's rare to begin with and
* this makes it safer than doing the test in nstring above in case
* some code calls writeString when it should call writeNString.
*/
if (!quote && bytes.length == 3 &&
(bytes[0] == 'N' || bytes[0] == 'n') &&
(bytes[1] == 'I' || bytes[1] == 'i') &&
(bytes[2] == 'L' || bytes[2] == 'l'))
quote = true;
if (quote) // start quote
os.write('"');
if (escape) {
// already quoted
for (int i = 0; i < len; i++) {
b = bytes[i];
if (b == '"' || b == '\\')
os.write('\\');
os.write(b);
}
} else
os.write(bytes);
if (quote) // end quote
os.write('"');
}
/**
* Write out given byte[] as a literal
*/
private void literal(byte[] b, Protocol protocol)
throws IOException, ProtocolException {
startLiteral(protocol, b.length).write(b);
}
/**
* Write out given ByteArrayOutputStream as a literal.
*/
private void literal(ByteArrayOutputStream b, Protocol protocol)
throws IOException, ProtocolException {
b.writeTo(startLiteral(protocol, b.size()));
}
/**
* Write out given Literal as a literal.
*/
private void literal(Literal b, Protocol protocol)
throws IOException, ProtocolException {
b.writeTo(startLiteral(protocol, b.size()));
}
private OutputStream startLiteral(Protocol protocol, int size)
throws IOException, ProtocolException {
DataOutputStream os = (DataOutputStream)protocol.getOutputStream();
boolean nonSync = protocol.supportsNonSyncLiterals();
os.write('{');
os.writeBytes(Integer.toString(size));
if (nonSync) // server supports non-sync literals
os.writeBytes("+}\r\n");
else
os.writeBytes("}\r\n");
os.flush();
// If we are using synchronized literals, wait for the server's
// continuation signal
if (!nonSync) {
for (; ;) {
Response r = protocol.readResponse();
if (r.isContinuation())
break;
if (r.isTagged())
throw new LiteralException(r);
// XXX - throw away untagged responses;
// violates IMAP spec, hope no servers do this
}
}
return os;
}
}
class Atom {
String string;
Atom(String s) {
string = s;
}
}
class AString {
byte[] bytes;
AString(byte[] b) {
bytes = b;
}
}
class NString {
byte[] bytes;
NString(byte[] b) {
bytes = b;
}
}